21
21
import warnings
22
22
from datetime import datetime
23
23
from enum import Enum
24
- from typing import Iterable , Optional , Set
24
+ from typing import Any , Iterable , Optional , Tuple , TypeVar
25
+
26
+ from sortedcontainers import SortedSet
25
27
26
28
from ..exception .model import (
27
29
InvalidLocaleTypeException ,
@@ -57,7 +59,44 @@ def sha1sum(filename: str) -> str:
57
59
return h .hexdigest ()
58
60
59
61
60
- class DataFlow (Enum ):
62
+ _T = TypeVar ('_T' )
63
+
64
+
65
+ class ComparableTuple (Tuple [Optional [_T ], ...]):
66
+ """
67
+ Allows comparison of tuples, allowing for None values.
68
+ """
69
+
70
+ def __lt__ (self , other : Any ) -> bool :
71
+ for s , o in zip (self , other ):
72
+ if s == o :
73
+ continue
74
+ if s is None :
75
+ return False
76
+ if o is None :
77
+ return True
78
+ if s < o :
79
+ return True
80
+ if s > o :
81
+ return False
82
+ return False
83
+
84
+ def __gt__ (self , other : Any ) -> bool :
85
+ for s , o in zip (self , other ):
86
+ if s == o :
87
+ continue
88
+ if s is None :
89
+ return True
90
+ if o is None :
91
+ return False
92
+ if s < o :
93
+ return False
94
+ if s > o :
95
+ return True
96
+ return False
97
+
98
+
99
+ class DataFlow (str , Enum ):
61
100
"""
62
101
This is our internal representation of the dataFlowType simple type within the CycloneDX standard.
63
102
@@ -132,7 +171,7 @@ def __repr__(self) -> str:
132
171
return f'<DataClassification flow={ self .flow } >'
133
172
134
173
135
- class Encoding (Enum ):
174
+ class Encoding (str , Enum ):
136
175
"""
137
176
This is our internal representation of the encoding simple type within the CycloneDX standard.
138
177
@@ -208,14 +247,20 @@ def __eq__(self, other: object) -> bool:
208
247
return hash (other ) == hash (self )
209
248
return False
210
249
250
+ def __lt__ (self , other : Any ) -> bool :
251
+ if isinstance (other , AttachedText ):
252
+ return ComparableTuple ((self .content_type , self .content , self .encoding )) < \
253
+ ComparableTuple ((other .content_type , other .content , other .encoding ))
254
+ return NotImplemented
255
+
211
256
def __hash__ (self ) -> int :
212
257
return hash ((self .content , self .content_type , self .encoding ))
213
258
214
259
def __repr__ (self ) -> str :
215
260
return f'<AttachedText content-type={ self .content_type } , encoding={ self .encoding } >'
216
261
217
262
218
- class HashAlgorithm (Enum ):
263
+ class HashAlgorithm (str , Enum ):
219
264
"""
220
265
This is our internal representation of the hashAlg simple type within the CycloneDX standard.
221
266
@@ -320,14 +365,19 @@ def __eq__(self, other: object) -> bool:
320
365
return hash (other ) == hash (self )
321
366
return False
322
367
368
+ def __lt__ (self , other : Any ) -> bool :
369
+ if isinstance (other , HashType ):
370
+ return ComparableTuple ((self .alg , self .content )) < ComparableTuple ((other .alg , other .content ))
371
+ return NotImplemented
372
+
323
373
def __hash__ (self ) -> int :
324
374
return hash ((self .alg , self .content ))
325
375
326
376
def __repr__ (self ) -> str :
327
377
return f'<HashType { self .alg .name } :{ self .content } >'
328
378
329
379
330
- class ExternalReferenceType (Enum ):
380
+ class ExternalReferenceType (str , Enum ):
331
381
"""
332
382
Enum object that defines the permissible 'types' for an External Reference according to the CycloneDX schema.
333
383
@@ -378,6 +428,11 @@ def __eq__(self, other: object) -> bool:
378
428
return hash (other ) == hash (self )
379
429
return False
380
430
431
+ def __lt__ (self , other : Any ) -> bool :
432
+ if isinstance (other , XsUri ):
433
+ return self ._uri < other ._uri
434
+ return NotImplemented
435
+
381
436
def __hash__ (self ) -> int :
382
437
return hash (self ._uri )
383
438
@@ -402,7 +457,7 @@ def __init__(self, *, reference_type: ExternalReferenceType, url: XsUri, comment
402
457
self .url = url
403
458
self .comment = comment
404
459
self .type = reference_type
405
- self .hashes = set (hashes or [])
460
+ self .hashes = SortedSet (hashes or [])
406
461
407
462
@property
408
463
def url (self ) -> XsUri :
@@ -450,7 +505,7 @@ def type(self, type_: ExternalReferenceType) -> None:
450
505
self ._type = type_
451
506
452
507
@property
453
- def hashes (self ) -> Set [HashType ]:
508
+ def hashes (self ) -> "SortedSet [HashType]" :
454
509
"""
455
510
The hashes of the external reference (if applicable).
456
511
@@ -461,13 +516,19 @@ def hashes(self) -> Set[HashType]:
461
516
462
517
@hashes .setter
463
518
def hashes (self , hashes : Iterable [HashType ]) -> None :
464
- self ._hashes = set (hashes )
519
+ self ._hashes = SortedSet (hashes )
465
520
466
521
def __eq__ (self , other : object ) -> bool :
467
522
if isinstance (other , ExternalReference ):
468
523
return hash (other ) == hash (self )
469
524
return False
470
525
526
+ def __lt__ (self , other : Any ) -> bool :
527
+ if isinstance (other , ExternalReference ):
528
+ return ComparableTuple ((self ._type , self ._url , self ._comment )) < \
529
+ ComparableTuple ((other ._type , other ._url , other ._comment ))
530
+ return NotImplemented
531
+
471
532
def __hash__ (self ) -> int :
472
533
return hash ((
473
534
self ._type , self ._url , self ._comment ,
@@ -566,6 +627,11 @@ def __eq__(self, other: object) -> bool:
566
627
return hash (other ) == hash (self )
567
628
return False
568
629
630
+ def __lt__ (self , other : Any ) -> bool :
631
+ if isinstance (other , License ):
632
+ return ComparableTuple ((self .id , self .name )) < ComparableTuple ((other .id , other .name ))
633
+ return NotImplemented
634
+
569
635
def __hash__ (self ) -> int :
570
636
return hash ((self .id , self .name , self .text , self .url ))
571
637
@@ -633,6 +699,11 @@ def __eq__(self, other: object) -> bool:
633
699
return hash (other ) == hash (self )
634
700
return False
635
701
702
+ def __lt__ (self , other : Any ) -> bool :
703
+ if isinstance (other , LicenseChoice ):
704
+ return ComparableTuple ((self .license , self .expression )) < ComparableTuple ((other .license , other .expression ))
705
+ return NotImplemented
706
+
636
707
def __hash__ (self ) -> int :
637
708
return hash ((self .license , self .expression ))
638
709
@@ -690,6 +761,11 @@ def __eq__(self, other: object) -> bool:
690
761
return hash (other ) == hash (self )
691
762
return False
692
763
764
+ def __lt__ (self , other : Any ) -> bool :
765
+ if isinstance (other , Property ):
766
+ return ComparableTuple ((self .name , self .value )) < ComparableTuple ((other .name , other .value ))
767
+ return NotImplemented
768
+
693
769
def __hash__ (self ) -> int :
694
770
return hash ((self .name , self .value ))
695
771
@@ -763,6 +839,12 @@ def __eq__(self, other: object) -> bool:
763
839
return hash (other ) == hash (self )
764
840
return False
765
841
842
+ def __lt__ (self , other : Any ) -> bool :
843
+ if isinstance (other , NoteText ):
844
+ return ComparableTuple ((self .content , self .content_type , self .encoding )) < \
845
+ ComparableTuple ((other .content , other .content_type , other .encoding ))
846
+ return NotImplemented
847
+
766
848
def __hash__ (self ) -> int :
767
849
return hash ((self .content , self .content_type , self .encoding ))
768
850
@@ -830,6 +912,11 @@ def __eq__(self, other: object) -> bool:
830
912
return hash (other ) == hash (self )
831
913
return False
832
914
915
+ def __lt__ (self , other : Any ) -> bool :
916
+ if isinstance (other , Note ):
917
+ return ComparableTuple ((self .locale , self .text )) < ComparableTuple ((other .locale , other .text ))
918
+ return NotImplemented
919
+
833
920
def __hash__ (self ) -> int :
834
921
return hash ((self .text , self .locale ))
835
922
@@ -902,11 +989,17 @@ def __eq__(self, other: object) -> bool:
902
989
return hash (other ) == hash (self )
903
990
return False
904
991
992
+ def __lt__ (self , other : Any ) -> bool :
993
+ if isinstance (other , OrganizationalContact ):
994
+ return ComparableTuple ((self .name , self .email , self .phone )) < \
995
+ ComparableTuple ((other .name , other .email , other .phone ))
996
+ return NotImplemented
997
+
905
998
def __hash__ (self ) -> int :
906
999
return hash ((self .name , self .phone , self .email ))
907
1000
908
1001
def __repr__ (self ) -> str :
909
- return f'<OrganizationalContact name={ self .name } >'
1002
+ return f'<OrganizationalContact name={ self .name } , email= { self . email } , phone= { self . phone } >'
910
1003
911
1004
912
1005
class OrganizationalEntity :
@@ -925,8 +1018,8 @@ def __init__(self, *, name: Optional[str] = None, urls: Optional[Iterable[XsUri]
925
1018
'One of name, urls or contacts must be supplied for an OrganizationalEntity - none supplied.'
926
1019
)
927
1020
self .name = name
928
- self .url = set (urls or [])
929
- self .contact = set (contacts or [])
1021
+ self .url = SortedSet (urls or [])
1022
+ self .contact = SortedSet (contacts or [])
930
1023
931
1024
@property
932
1025
def name (self ) -> Optional [str ]:
@@ -943,7 +1036,7 @@ def name(self, name: Optional[str]) -> None:
943
1036
self ._name = name
944
1037
945
1038
@property
946
- def url (self ) -> Set [XsUri ]:
1039
+ def url (self ) -> "SortedSet [XsUri]" :
947
1040
"""
948
1041
Get a list of URLs of the organization. Multiple URLs are allowed.
949
1042
@@ -954,10 +1047,10 @@ def url(self) -> Set[XsUri]:
954
1047
955
1048
@url .setter
956
1049
def url (self , urls : Iterable [XsUri ]) -> None :
957
- self ._url = set (urls )
1050
+ self ._url = SortedSet (urls )
958
1051
959
1052
@property
960
- def contact (self ) -> Set [OrganizationalContact ]:
1053
+ def contact (self ) -> "SortedSet [OrganizationalContact]" :
961
1054
"""
962
1055
Get a list of contact person at the organization. Multiple contacts are allowed.
963
1056
@@ -968,7 +1061,7 @@ def contact(self) -> Set[OrganizationalContact]:
968
1061
969
1062
@contact .setter
970
1063
def contact (self , contacts : Iterable [OrganizationalContact ]) -> None :
971
- self ._contact = set (contacts )
1064
+ self ._contact = SortedSet (contacts )
972
1065
973
1066
def __eq__ (self , other : object ) -> bool :
974
1067
if isinstance (other , OrganizationalEntity ):
@@ -998,8 +1091,8 @@ def __init__(self, *, vendor: Optional[str] = None, name: Optional[str] = None,
998
1091
self .vendor = vendor
999
1092
self .name = name
1000
1093
self .version = version
1001
- self .hashes = set (hashes or [])
1002
- self .external_references = set (external_references or [])
1094
+ self .hashes = SortedSet (hashes or [])
1095
+ self .external_references = SortedSet (external_references or [])
1003
1096
1004
1097
@property
1005
1098
def vendor (self ) -> Optional [str ]:
@@ -1044,7 +1137,7 @@ def version(self, version: Optional[str]) -> None:
1044
1137
self ._version = version
1045
1138
1046
1139
@property
1047
- def hashes (self ) -> Set [HashType ]:
1140
+ def hashes (self ) -> "SortedSet [HashType]" :
1048
1141
"""
1049
1142
The hashes of the tool (if applicable).
1050
1143
@@ -1055,10 +1148,10 @@ def hashes(self) -> Set[HashType]:
1055
1148
1056
1149
@hashes .setter
1057
1150
def hashes (self , hashes : Iterable [HashType ]) -> None :
1058
- self ._hashes = set (hashes )
1151
+ self ._hashes = SortedSet (hashes )
1059
1152
1060
1153
@property
1061
- def external_references (self ) -> Set [ExternalReference ]:
1154
+ def external_references (self ) -> "SortedSet [ExternalReference]" :
1062
1155
"""
1063
1156
External References provide a way to document systems, sites, and information that may be relevant but which
1064
1157
are not included with the BOM.
@@ -1070,13 +1163,19 @@ def external_references(self) -> Set[ExternalReference]:
1070
1163
1071
1164
@external_references .setter
1072
1165
def external_references (self , external_references : Iterable [ExternalReference ]) -> None :
1073
- self ._external_references = set (external_references )
1166
+ self ._external_references = SortedSet (external_references )
1074
1167
1075
1168
def __eq__ (self , other : object ) -> bool :
1076
1169
if isinstance (other , Tool ):
1077
1170
return hash (other ) == hash (self )
1078
1171
return False
1079
1172
1173
+ def __lt__ (self , other : Any ) -> bool :
1174
+ if isinstance (other , Tool ):
1175
+ return ComparableTuple ((self .vendor , self .name , self .version )) < \
1176
+ ComparableTuple ((other .vendor , other .name , other .version ))
1177
+ return NotImplemented
1178
+
1080
1179
def __hash__ (self ) -> int :
1081
1180
return hash ((self .vendor , self .name , self .version , tuple (self .hashes ), tuple (self .external_references )))
1082
1181
@@ -1150,6 +1249,12 @@ def __eq__(self, other: object) -> bool:
1150
1249
return hash (other ) == hash (self )
1151
1250
return False
1152
1251
1252
+ def __lt__ (self , other : Any ) -> bool :
1253
+ if isinstance (other , IdentifiableAction ):
1254
+ return ComparableTuple ((self .timestamp , self .name , self .email )) < \
1255
+ ComparableTuple ((other .timestamp , other .name , other .email ))
1256
+ return NotImplemented
1257
+
1153
1258
def __hash__ (self ) -> int :
1154
1259
return hash ((self .timestamp , self .name , self .email ))
1155
1260
@@ -1187,6 +1292,11 @@ def __eq__(self, other: object) -> bool:
1187
1292
return hash (other ) == hash (self )
1188
1293
return False
1189
1294
1295
+ def __lt__ (self , other : Any ) -> bool :
1296
+ if isinstance (other , Copyright ):
1297
+ return self .text < other .text
1298
+ return NotImplemented
1299
+
1190
1300
def __hash__ (self ) -> int :
1191
1301
return hash (self .text )
1192
1302
0 commit comments