1414#
1515# SPDX-License-Identifier: Apache-2.0
1616#
17-
1817import hashlib
1918import re
2019import sys
2120import warnings
21+ from datetime import datetime
2222from enum import Enum
2323from typing import List , Optional , Union
2424
@@ -119,6 +119,17 @@ def classification(self) -> str:
119119 def classification (self , classification : str ) -> None :
120120 self ._classification = classification
121121
122+ def __eq__ (self , other : object ) -> bool :
123+ if isinstance (other , DataClassification ):
124+ return hash (other ) == hash (self )
125+ return False
126+
127+ def __hash__ (self ) -> int :
128+ return hash ((self .flow , self .classification ))
129+
130+ def __repr__ (self ) -> str :
131+ return f'<DataClassification flow={ self .flow } >'
132+
122133
123134class Encoding (Enum ):
124135 """
@@ -191,6 +202,17 @@ def content(self) -> str:
191202 def content (self , content : str ) -> None :
192203 self ._content = content
193204
205+ def __eq__ (self , other : object ) -> bool :
206+ if isinstance (other , AttachedText ):
207+ return hash (other ) == hash (self )
208+ return False
209+
210+ def __hash__ (self ) -> int :
211+ return hash ((self .content , self .content_type , self .encoding ))
212+
213+ def __repr__ (self ) -> str :
214+ return f'<AttachedText content-type={ self .content_type } , encoding={ self .encoding } >'
215+
194216
195217class HashAlgorithm (Enum ):
196218 """
@@ -270,8 +292,16 @@ def get_algorithm(self) -> HashAlgorithm:
270292 def get_hash_value (self ) -> str :
271293 return self ._content
272294
295+ def __eq__ (self , other : object ) -> bool :
296+ if isinstance (other , HashType ):
297+ return hash (other ) == hash (self )
298+ return False
299+
300+ def __hash__ (self ) -> int :
301+ return hash ((self ._alg , self ._content ))
302+
273303 def __repr__ (self ) -> str :
274- return f'<Hash { self ._alg .value } :{ self ._content } >'
304+ return f'<HashType { self ._alg .value } :{ self ._content } >'
275305
276306
277307class ExternalReferenceType (Enum ):
@@ -299,6 +329,17 @@ class ExternalReferenceType(Enum):
299329 VCS = 'vcs'
300330 WEBSITE = 'website'
301331
332+ # def __eq__(self, other: object) -> bool:
333+ # if isinstance(other, ExternalReferenceType):
334+ # return hash(other) == hash(self)
335+ # return False
336+ #
337+ # def __hash__(self) -> int:
338+ # return hash(self.value)
339+ #
340+ # def __repr__(self) -> str:
341+ # return f'<ExternalReferenceType name={self.name}, value={self.value}>'
342+
302343
303344class XsUri :
304345 """
@@ -322,9 +363,12 @@ def __init__(self, uri: str) -> None:
322363
323364 def __eq__ (self , other : object ) -> bool :
324365 if isinstance (other , XsUri ):
325- return str ( self ) == str ( other )
366+ return hash ( other ) == hash ( self )
326367 return False
327368
369+ def __hash__ (self ) -> int :
370+ return hash (self ._uri )
371+
328372 def __repr__ (self ) -> str :
329373 return self ._uri
330374
@@ -391,8 +435,19 @@ def get_url(self) -> str:
391435 """
392436 return self ._url
393437
438+ def __eq__ (self , other : object ) -> bool :
439+ if isinstance (other , ExternalReference ):
440+ return hash (other ) == hash (self )
441+ return False
442+
443+ def __hash__ (self ) -> int :
444+ return hash ((
445+ self ._type , self ._url , self ._comment ,
446+ tuple ([hash (hash_ ) for hash_ in set (sorted (self ._hashes , key = hash ))]) if self ._hashes else None
447+ ))
448+
394449 def __repr__ (self ) -> str :
395- return f'<ExternalReference { self ._type .name } , { self ._url } > { self . _hashes } '
450+ return f'<ExternalReference { self ._type .name } , { self ._url } >'
396451
397452
398453class License :
@@ -478,6 +533,17 @@ def url(self) -> Optional[XsUri]:
478533 def url (self , url : Optional [XsUri ]) -> None :
479534 self ._url = url
480535
536+ def __eq__ (self , other : object ) -> bool :
537+ if isinstance (other , License ):
538+ return hash (other ) == hash (self )
539+ return False
540+
541+ def __hash__ (self ) -> int :
542+ return hash ((self .id , self .name , self .text , self .url ))
543+
544+ def __repr__ (self ) -> str :
545+ return f'<License id={ self .id } , name={ self .name } >'
546+
481547
482548class LicenseChoice :
483549 """
@@ -534,6 +600,17 @@ def expression(self) -> Optional[str]:
534600 def expression (self , expression : Optional [str ]) -> None :
535601 self ._expression = expression
536602
603+ def __eq__ (self , other : object ) -> bool :
604+ if isinstance (other , LicenseChoice ):
605+ return hash (other ) == hash (self )
606+ return False
607+
608+ def __hash__ (self ) -> int :
609+ return hash ((self .license , self .expression ))
610+
611+ def __repr__ (self ) -> str :
612+ return f'<LicenseChoice license={ self .license } , expression={ self .expression } >'
613+
537614
538615class Property :
539616 """
@@ -568,6 +645,17 @@ def get_value(self) -> str:
568645 """
569646 return self ._value
570647
648+ def __eq__ (self , other : object ) -> bool :
649+ if isinstance (other , Property ):
650+ return hash (other ) == hash (self )
651+ return False
652+
653+ def __hash__ (self ) -> int :
654+ return hash ((self ._name , self ._value ))
655+
656+ def __repr__ (self ) -> str :
657+ return f'<Property name={ self ._name } >'
658+
571659
572660class NoteText :
573661 """
@@ -630,6 +718,17 @@ def encoding(self) -> Optional[Encoding]:
630718 def encoding (self , encoding : Optional [Encoding ]) -> None :
631719 self ._encoding = encoding
632720
721+ def __eq__ (self , other : object ) -> bool :
722+ if isinstance (other , NoteText ):
723+ return hash (other ) == hash (self )
724+ return False
725+
726+ def __hash__ (self ) -> int :
727+ return hash ((self .content , self .content_type , self .encoding ))
728+
729+ def __repr__ (self ) -> str :
730+ return f'<NoteText content_type={ self .content_type } , encoding={ self .encoding } >'
731+
633732
634733class Note :
635734 """
@@ -686,6 +785,17 @@ def locale(self, locale: Optional[str]) -> None:
686785 f"ISO-3166 (or higher) country code. according to ISO-639 format. Examples include: 'en', 'en-US'."
687786 )
688787
788+ def __eq__ (self , other : object ) -> bool :
789+ if isinstance (other , Note ):
790+ return hash (other ) == hash (self )
791+ return False
792+
793+ def __hash__ (self ) -> int :
794+ return hash ((hash (self .text ), self .locale ))
795+
796+ def __repr__ (self ) -> str :
797+ return f'<Note id={ id (self )} , locale={ self .locale } >'
798+
689799
690800class OrganizationalContact :
691801 """
@@ -735,6 +845,17 @@ def phone(self) -> Optional[str]:
735845 """
736846 return self ._phone
737847
848+ def __eq__ (self , other : object ) -> bool :
849+ if isinstance (other , OrganizationalContact ):
850+ return hash (other ) == hash (self )
851+ return False
852+
853+ def __hash__ (self ) -> int :
854+ return hash ((self .name , self .phone , self .email ))
855+
856+ def __repr__ (self ) -> str :
857+ return f'<OrganizationalContact name={ self .name } >'
858+
738859
739860class OrganizationalEntity :
740861 """
@@ -785,6 +906,21 @@ def contacts(self) -> Optional[List[OrganizationalContact]]:
785906 """
786907 return self ._contact
787908
909+ def __eq__ (self , other : object ) -> bool :
910+ if isinstance (other , OrganizationalEntity ):
911+ return hash (other ) == hash (self )
912+ return False
913+
914+ def __hash__ (self ) -> int :
915+ return hash ((
916+ self .name ,
917+ tuple ([hash (url ) for url in set (sorted (self .urls , key = hash ))]) if self .urls else None ,
918+ tuple ([hash (contact ) for contact in set (sorted (self .contacts , key = hash ))]) if self .contacts else None
919+ ))
920+
921+ def __repr__ (self ) -> str :
922+ return f'<OrganizationalEntity name={ self .name } >'
923+
788924
789925class Tool :
790926 """
@@ -876,8 +1012,131 @@ def get_version(self) -> Optional[str]:
8761012 """
8771013 return self ._version
8781014
1015+ def __eq__ (self , other : object ) -> bool :
1016+ if isinstance (other , Tool ):
1017+ return hash (other ) == hash (self )
1018+ return False
1019+
1020+ def __hash__ (self ) -> int :
1021+ return hash ((
1022+ self ._vendor , self ._name , self ._version ,
1023+ tuple ([hash (hash_ ) for hash_ in set (sorted (self ._hashes , key = hash ))]) if self ._hashes else None ,
1024+ tuple ([hash (ref ) for ref in
1025+ set (sorted (self ._external_references , key = hash ))]) if self ._external_references else None
1026+ ))
1027+
1028+ def __repr__ (self ) -> str :
1029+ return f'<Tool name={ self ._name } , version={ self ._version } , vendor={ self ._vendor } >'
1030+
1031+
1032+ class IdentifiableAction :
1033+ """
1034+ This is out internal representation of the `identifiableActionType` complex type.
1035+
1036+ .. note::
1037+ See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_identifiableActionType
1038+ """
1039+
1040+ def __init__ (self , timestamp : Optional [datetime ] = None , name : Optional [str ] = None ,
1041+ email : Optional [str ] = None ) -> None :
1042+ if not timestamp and not name and not email :
1043+ raise NoPropertiesProvidedException (
1044+ 'At least one of `timestamp`, `name` or `email` must be provided for an `IdentifiableAction`.'
1045+ )
1046+
1047+ self .timestamp = timestamp
1048+ self .name = name
1049+ self .email = email
1050+
1051+ @property
1052+ def timestamp (self ) -> Optional [datetime ]:
1053+ """
1054+ The timestamp in which the action occurred.
1055+
1056+ Returns:
1057+ `datetime` if set else `None`
1058+ """
1059+ return self ._timestamp
1060+
1061+ @timestamp .setter
1062+ def timestamp (self , timestamp : Optional [datetime ]) -> None :
1063+ self ._timestamp = timestamp
1064+
1065+ @property
1066+ def name (self ) -> Optional [str ]:
1067+ """
1068+ The name of the individual who performed the action.
1069+
1070+ Returns:
1071+ `str` if set else `None`
1072+ """
1073+ return self ._name
1074+
1075+ @name .setter
1076+ def name (self , name : Optional [str ]) -> None :
1077+ self ._name = name
1078+
1079+ @property
1080+ def email (self ) -> Optional [str ]:
1081+ """
1082+ The email address of the individual who performed the action.
1083+
1084+ Returns:
1085+ `str` if set else `None`
1086+ """
1087+ return self ._email
1088+
1089+ @email .setter
1090+ def email (self , email : Optional [str ]) -> None :
1091+ self ._email = email
1092+
1093+ def __eq__ (self , other : object ) -> bool :
1094+ if isinstance (other , IdentifiableAction ):
1095+ return hash (other ) == hash (self )
1096+ return False
1097+
1098+ def __hash__ (self ) -> int :
1099+ return hash ((hash (self .timestamp ), self .name , self .email ))
1100+
1101+ def __repr__ (self ) -> str :
1102+ return f'<IdentifiableAction name={ self .name } , email={ self .email } >'
1103+
1104+
1105+ class Copyright :
1106+ """
1107+ This is out internal representation of the `copyrightsType` complex type.
1108+
1109+ .. note::
1110+ See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_copyrightsType
1111+ """
1112+
1113+ def __init__ (self , text : str ) -> None :
1114+ self .text = text
1115+
1116+ @property
1117+ def text (self ) -> str :
1118+ """
1119+ Copyright statement.
1120+
1121+ Returns:
1122+ `str` if set else `None`
1123+ """
1124+ return self ._text
1125+
1126+ @text .setter
1127+ def text (self , text : str ) -> None :
1128+ self ._text = text
1129+
1130+ def __eq__ (self , other : object ) -> bool :
1131+ if isinstance (other , Copyright ):
1132+ return hash (other ) == hash (self )
1133+ return False
1134+
1135+ def __hash__ (self ) -> int :
1136+ return hash (self .text )
1137+
8791138 def __repr__ (self ) -> str :
880- return '<Tool {}:{}:{}>' . format ( self . _vendor , self . _name , self . _version )
1139+ return f'<Copyright text= { self . text } >'
8811140
8821141
8831142if sys .version_info >= (3 , 8 ):
0 commit comments