2222
2323import difflib
2424from pathlib import Path
25+ from timeit import default_timer as timer
2526
2627from scanpipe import pipes
2728from scanpipe .models import CodebaseRelation
@@ -74,66 +75,93 @@ def get_best_checksum_matches(to_resource, matches):
7475 return matches
7576
7677
78+ def _resource_checksum_match (to_resource , from_resources , checksum_field ):
79+ checksum_value = getattr (to_resource , checksum_field )
80+ matches = from_resources .filter (** {checksum_field : checksum_value })
81+ for match in get_best_checksum_matches (to_resource , matches ):
82+ pipes .make_relationship (
83+ from_resource = match ,
84+ to_resource = to_resource ,
85+ relationship = CodebaseRelation .Relationship .IDENTICAL ,
86+ match_type = checksum_field ,
87+ )
88+
89+
7790def checksum_match (project , checksum_field , logger = None ):
7891 """Match using checksum."""
7992 project_files = project .codebaseresources .files ().no_status ()
8093 from_resources = project_files .from_codebase ().has_value (checksum_field )
8194 to_resources = (
8295 project_files .to_codebase ().has_value (checksum_field ).has_no_relation ()
8396 )
97+ resource_count = to_resources .count ()
8498
8599 if logger :
86- resource_count = to_resources .count ()
87100 logger (
88101 f"Matching { resource_count :,d} to/ resources using { checksum_field } "
89102 f"against from/ codebase"
90103 )
91104
92- for to_resource in to_resources :
93- checksum_value = getattr (to_resource , checksum_field )
94- matches = from_resources .filter (** {checksum_field : checksum_value })
95- for match in get_best_checksum_matches (to_resource , matches ):
96- pipes .make_relationship (
97- from_resource = match ,
98- to_resource = to_resource ,
99- relationship = CodebaseRelation .Relationship .IDENTICAL ,
100- match_type = checksum_field ,
101- )
105+ resource_iterator = to_resources .iterator (chunk_size = 2000 )
106+ last_percent = 0
107+ start_time = timer ()
108+ for resource_index , to_resource in enumerate (resource_iterator ):
109+ last_percent = pipes .log_progress (
110+ logger ,
111+ resource_index ,
112+ resource_count ,
113+ last_percent ,
114+ increment_percent = 10 ,
115+ start_time = start_time ,
116+ )
117+ _resource_checksum_match (to_resource , from_resources , checksum_field )
118+
119+
120+ def _resource_java_to_class_match (to_resource , from_resources ):
121+ qualified_class = get_extracted_subpath (to_resource .path )
122+
123+ if "$" in to_resource .name : # inner class
124+ path_parts = Path (qualified_class .lstrip ("/" )).parts
125+ parts_without_name = list (path_parts [:- 1 ])
126+ from_name = to_resource .name .split ("$" )[0 ] + ".java"
127+ qualified_java = "/" .join (parts_without_name + [from_name ])
128+ else :
129+ qualified_java = qualified_class .replace (".class" , ".java" )
130+
131+ matches = from_resources .filter (path__endswith = qualified_java )
132+ for match in matches :
133+ pipes .make_relationship (
134+ from_resource = match ,
135+ to_resource = to_resource ,
136+ relationship = CodebaseRelation .Relationship .COMPILED ,
137+ match_type = "java_to_class" ,
138+ )
102139
103140
104141def java_to_class_match (project , logger = None ):
105142 """Match a .java source to its compiled .class using fully qualified name."""
106- from_extension = ".java"
107- to_extension = ".class"
108-
109143 project_files = project .codebaseresources .files ().no_status ()
110144 from_resources = project_files .from_codebase ()
111145 to_resources = project_files .to_codebase ().has_no_relation ()
112146
113- to_resources_dot_class = to_resources .filter (name__endswith = to_extension )
147+ to_resources_dot_class = to_resources .filter (name__endswith = ".class" )
148+ resource_count = to_resources_dot_class .count ()
114149 if logger :
115- count = to_resources_dot_class .count ()
116- logger (f"Matching { count :,d} .class resources to .java" )
150+ logger (f"Matching { resource_count :,d} .class resources to .java" )
117151
118- for to_resource in to_resources_dot_class :
119- qualified_class = get_extracted_subpath (to_resource .path )
120-
121- if "$" in to_resource .name : # inner class
122- path_parts = Path (qualified_class .lstrip ("/" )).parts
123- parts_without_name = list (path_parts [:- 1 ])
124- from_name = to_resource .name .split ("$" )[0 ] + from_extension
125- qualified_java = "/" .join (parts_without_name + [from_name ])
126- else :
127- qualified_java = qualified_class .replace (to_extension , from_extension )
128-
129- matches = from_resources .filter (path__endswith = qualified_java )
130- for match in matches :
131- pipes .make_relationship (
132- from_resource = match ,
133- to_resource = to_resource ,
134- relationship = CodebaseRelation .Relationship .COMPILED ,
135- match_type = "java_to_class" ,
136- )
152+ resource_iterator = to_resources_dot_class .iterator (chunk_size = 2000 )
153+ last_percent = 0
154+ start_time = timer ()
155+ for resource_index , to_resource in enumerate (resource_iterator ):
156+ last_percent = pipes .log_progress (
157+ logger ,
158+ resource_index ,
159+ resource_count ,
160+ last_percent ,
161+ increment_percent = 10 ,
162+ start_time = start_time ,
163+ )
164+ _resource_java_to_class_match (to_resource , from_resources )
137165
138166
139167def get_diff_ratio (to_resource , from_resource ):
@@ -208,13 +236,35 @@ def path_match(project, logger=None):
208236
209237 resource_iterator = to_resources .iterator (chunk_size = 2000 )
210238 last_percent = 0
239+ start_time = timer ()
211240 for resource_index , to_resource in enumerate (resource_iterator ):
212241 last_percent = pipes .log_progress (
213- logger , resource_index , resource_count , last_percent , increment_percent = 5
242+ logger ,
243+ resource_index ,
244+ resource_count ,
245+ last_percent ,
246+ increment_percent = 10 ,
247+ start_time = start_time ,
214248 )
215249 _resource_path_match (to_resource , from_resources )
216250
217251
252+ def _resource_purldb_match (project , resource ):
253+ if results := purldb .match_by_sha1 (sha1 = resource .sha1 ):
254+ package_data = results [0 ]
255+ package_data .pop ("dependencies" )
256+ package = pipes .update_or_create_package (
257+ project = project ,
258+ package_data = package_data ,
259+ codebase_resource = resource ,
260+ )
261+ extracted_resources = project .codebaseresources .to_codebase ().filter (
262+ path__startswith = f"{ resource .path } -extract"
263+ )
264+ package .add_resources (extracted_resources )
265+ extracted_resources .update (status = "application-package" )
266+
267+
218268def purldb_match (project , extensions , logger = None ):
219269 to_resources = (
220270 project .codebaseresources .files ()
@@ -223,25 +273,24 @@ def purldb_match(project, extensions, logger=None):
223273 .has_value ("sha1" )
224274 .filter (extension__in = extensions )
225275 )
276+ resource_count = to_resources .count ()
226277
227278 if logger :
228- resource_count = to_resources .count ()
229279 extensions_str = ", " .join (extensions )
230280 logger (
231281 f"Matching { resource_count :,d} { extensions_str } resources against PurlDB"
232282 )
233283
234- for resource in to_resources :
235- if results := purldb .match_by_sha1 (sha1 = resource .sha1 ):
236- package_data = results [0 ]
237- package_data .pop ("dependencies" )
238- package = pipes .update_or_create_package (
239- project = project ,
240- package_data = package_data ,
241- codebase_resource = resource ,
242- )
243- extracted_resources = project .codebaseresources .to_codebase ().filter (
244- path__startswith = f"{ resource .path } -extract"
245- )
246- package .add_resources (extracted_resources )
247- extracted_resources .update (status = "application-package" )
284+ resource_iterator = to_resources .iterator (chunk_size = 2000 )
285+ last_percent = 0
286+ start_time = timer ()
287+ for resource_index , to_resource in enumerate (resource_iterator ):
288+ last_percent = pipes .log_progress (
289+ logger ,
290+ resource_index ,
291+ resource_count ,
292+ last_percent ,
293+ increment_percent = 10 ,
294+ start_time = start_time ,
295+ )
296+ _resource_purldb_match (project , to_resource )
0 commit comments