1 2# Copyright (c) Xavier Figueroa 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# http://www.apache.org/licenses/LICENSE-2.0 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, 9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10# See the License for the specific language governing permissions and 11# limitations under the License. 12 13from __future__ import absolute_import 14from __future__ import print_function 15from __future__ import unicode_literals 16 17from rdflib import Literal 18 19from spdx import document 20 21 22class BaseWriter(object): 23 """ 24 Base class for all Writer classes. 25 Provide utility functions and stores shared fields. 26 - document: spdx.document class. Source of data to be written 27 - document_object: python dictionary representation of the entire spdx.document 28 """ 29 30 def __init__(self, document): 31 self.document = document 32 self.document_object = dict() 33 34 def license(self, license_field): 35 """ 36 Return a string representation of a license or spdx.utils special object 37 """ 38 if isinstance(license_field, (document.LicenseDisjunction, document.LicenseConjunction)): 39 return '({})'.format(license_field) 40 41 if isinstance(license_field, document.License): 42 license_str = license_field.identifier.__str__() 43 else: 44 license_str = license_field.__str__() 45 return license_str 46 47 def checksum(self, checksum_field): 48 """ 49 Return a dictionary representation of a spdx.checksum.Algorithm object 50 """ 51 checksum_object = dict() 52 checksum_object['algorithm'] = 'checksumAlgorithm_' + checksum_field.identifier.lower() 53 checksum_object['value'] = checksum_field.value 54 return checksum_object 55 56 def spdx_id(self, spdx_id_field): 57 return spdx_id_field.__str__().split('#')[-1] 58 59class CreationInfoWriter(BaseWriter): 60 """ 61 Represent spdx.creationinfo as json-serializable objects 62 """ 63 64 def __init__(self, document): 65 super(CreationInfoWriter, self).__init__(document) 66 67 def create_creation_info(self): 68 creation_info_object = dict() 69 creation_info = self.document.creation_info 70 creation_info_object['creators'] = list(map(str, creation_info.creators)) 71 creation_info_object['created'] = creation_info.created_iso_format 72 73 if creation_info.license_list_version: 74 creation_info_object['licenseListVersion'] = '{0}.{1}'.format(creation_info.license_list_version.major, creation_info.license_list_version.minor) 75 76 if creation_info.has_comment: 77 creation_info_object['comment'] = creation_info.comment 78 79 return creation_info_object 80 81class PackageWriter(BaseWriter): 82 """ 83 Represent spdx.package as python objects 84 """ 85 86 def __init__(self, document): 87 super(PackageWriter, self).__init__(document) 88 89 def package_verification_code(self, package): 90 """ 91 Represent the package verification code information as 92 as python dictionary 93 """ 94 95 package_verification_code_object = dict() 96 97 package_verification_code_object['value'] = package.verif_code 98 99 if package.verif_exc_files: 100 package_verification_code_object['excludedFilesNames'] = package.verif_exc_files 101 102 return package_verification_code_object 103 104 def create_package_info(self): 105 package_object = dict() 106 package = self.document.package 107 package_object['id'] = self.spdx_id(package.spdx_id) 108 package_object['name'] = package.name 109 package_object['downloadLocation'] = package.download_location.__str__() 110 package_object['packageVerificationCode'] = self.package_verification_code(package) 111 package_object['licenseConcluded'] = self.license(package.conc_lics) 112 package_object['licenseInfoFromFiles'] = list(map(self.license, package.licenses_from_files)) 113 package_object['licenseDeclared'] = self.license(package.license_declared) 114 package_object['copyrightText'] = package.cr_text.__str__() 115 116 if package.has_optional_field('version'): 117 package_object['versionInfo'] = package.version 118 119 if package.has_optional_field('summary'): 120 package_object['summary'] = package.summary 121 122 if package.has_optional_field('source_info'): 123 package_object['sourceInfo'] = package.source_info 124 125 if package.has_optional_field('file_name'): 126 package_object['packageFileName'] = package.file_name 127 128 if package.has_optional_field('supplier'): 129 package_object['supplier'] = package.supplier.__str__() 130 131 if package.has_optional_field('originator'): 132 package_object['originator'] = package.originator.__str__() 133 134 if package.has_optional_field('check_sum'): 135 package_object['checksums'] = [self.checksum(package.check_sum)] 136 package_object['sha1'] = package.check_sum.value 137 138 if package.has_optional_field('description'): 139 package_object['description'] = package.description 140 141 if package.has_optional_field('license_comment'): 142 package_object['licenseComments'] = package.license_comment 143 144 if package.has_optional_field('homepage'): 145 package_object['homepage'] = package.homepage.__str__() 146 147 return package_object 148 149class FileWriter(BaseWriter): 150 """ 151 Represent spdx.file as json-serializable objects 152 """ 153 154 def __init__(self, document): 155 super(FileWriter, self).__init__(document) 156 157 def create_artifact_info(self, file): 158 """ 159 Create the artifact json-serializable representation from a spdx.file.File object 160 """ 161 artifact_of_objects = [] 162 163 for i in range(len(file.artifact_of_project_name)): 164 artifact_of_object = dict() 165 artifact_of_object['name'] = file.artifact_of_project_name[i].__str__() 166 artifact_of_object['homePage'] = file.artifact_of_project_home[i].__str__() 167 artifact_of_object['projectUri'] = file.artifact_of_project_uri[i].__str__() 168 artifact_of_objects.append(artifact_of_object) 169 170 return artifact_of_objects 171 172 def create_file_info(self): 173 file_types = { 1: 'fileType_source', 2: 'fileType_binary', 3: 'fileType_archive', 4: 'fileType_other'} 174 file_objects = [] 175 files = self.document.files 176 177 for file in files: 178 file_object = dict() 179 180 file_object['name'] = file.name 181 file_object['id'] = self.spdx_id(file.spdx_id) 182 file_object['checksums'] = [self.checksum(file.chk_sum)] 183 file_object['licenseConcluded'] = self.license(file.conc_lics) 184 file_object['licenseInfoFromFiles'] = list(map(self.license, file.licenses_in_file)) 185 file_object['copyrightText'] = file.copyright.__str__() 186 file_object['sha1'] = file.chk_sum.value 187 188 if file.has_optional_field('comment'): 189 file_object['comment'] = file.comment 190 191 if file.has_optional_field('type'): 192 file_object['fileTypes'] = [file_types.get(file.type)] 193 194 if file.has_optional_field('license_comment'): 195 file_object['licenseComments'] = file.license_comment 196 197 if file.has_optional_field('notice'): 198 file_object['noticeText'] = file.notice 199 200 if file.contributors: 201 file_object['fileContributors'] = file.contributors.__str__() 202 203 if file.dependencies: 204 file_object['fileDependencies'] = file.dependencies 205 206 valid_artifacts = file.artifact_of_project_name and len(file.artifact_of_project_name) == len(file.artifact_of_project_home) and len(file.artifact_of_project_home) == len(file.artifact_of_project_uri) 207 208 if valid_artifacts: 209 file_object['artifactOf'] = self.create_artifact_info(file) 210 211 file_objects.append({"File": file_object}) 212 213 return file_objects 214 215class ReviewInfoWriter(BaseWriter): 216 """ 217 Represent spdx.review as json-serializable objects 218 """ 219 220 def __init__(self, document): 221 super(ReviewInfoWriter, self).__init__(document) 222 223 def create_review_info(self): 224 review_info_objects = [] 225 reviews = self.document.reviews 226 227 for review in reviews: 228 review_object = dict() 229 review_object['reviewer'] = review.reviewer.__str__() 230 review_object['reviewDate'] = review.review_date_iso_format 231 if review.has_comment: 232 review_object['comment'] = review.comment 233 234 review_info_objects.append(review_object) 235 236 return review_info_objects 237 238class AnnotationInfoWriter(BaseWriter): 239 """ 240 Represent spdx.annotation as json-serializable objects 241 """ 242 243 def __init__(self, document): 244 super(AnnotationInfoWriter, self).__init__(document) 245 246 def create_annotation_info(self): 247 """ 248 The way how tools-python manages its models makes difficult to classify 249 annotations (by document, files and packages) and some of them could end up omitted. 250 This method sets every annotation as part of the spdx document itself, 251 avoiding them to be omitted. 252 """ 253 annotation_objects = [] 254 255 for annotation in self.document.annotations: 256 annotation_object = dict() 257 annotation_object['id'] = self.spdx_id(annotation.spdx_id) 258 annotation_object['annotator'] = annotation.annotator.__str__() 259 annotation_object['annotationDate'] = annotation.annotation_date_iso_format 260 annotation_object['annotationType'] = annotation.annotation_type 261 annotation_object['comment'] = annotation.comment 262 263 annotation_objects.append(annotation_object) 264 265 return annotation_objects 266 267class SnippetWriter(BaseWriter): 268 """ 269 Represent spdx.annotation as json-serializable objects 270 """ 271 def __init__(self, document): 272 super(SnippetWriter, self).__init__(document) 273 274 def create_snippet_info(self): 275 snippet_info_objects = [] 276 snippets = self.document.snippet 277 278 for snippet in snippets: 279 snippet_object = dict() 280 snippet_object['id'] = self.spdx_id(snippet.spdx_id) 281 snippet_object['copyrightText'] = snippet.copyright 282 snippet_object['fileId'] = self.spdx_id(snippet.snip_from_file_spdxid) 283 snippet_object['licenseConcluded'] = self.license(snippet.conc_lics) 284 snippet_object['licenseInfoFromSnippet'] = list(map(self.license, snippet.licenses_in_snippet)) 285 286 if snippet.has_optional_field('name'): 287 snippet_object['name'] = snippet.name 288 289 if snippet.has_optional_field('comment'): 290 snippet_object['comment'] = snippet.comment 291 292 if snippet.has_optional_field('license_comment'): 293 snippet_object['licenseComments'] = snippet.license_comment 294 295 snippet_info_objects.append(snippet_object) 296 297 return snippet_info_objects 298 299class ExtractedLicenseWriter(BaseWriter): 300 """ 301 Represent spdx.document.ExtractedLicense as json-serializable objects 302 """ 303 304 def __init__(self, document): 305 super(ExtractedLicenseWriter, self).__init__(document) 306 307 def create_extracted_license(self): 308 extracted_license_objects = [] 309 extracted_licenses = self.document.extracted_licenses 310 311 for extracted_license in extracted_licenses: 312 extracted_license_object = dict() 313 314 if isinstance(extracted_license.identifier, Literal): 315 extracted_license_object['licenseId'] = extracted_license.identifier.toPython() 316 else: 317 extracted_license_object['licenseId'] = extracted_license.identifier 318 319 if isinstance(extracted_license.text, Literal): 320 extracted_license_object['extractedText'] = extracted_license.text.toPython() 321 else: 322 extracted_license_object['extractedText'] = extracted_license.text 323 324 if extracted_license.full_name: 325 if isinstance(extracted_license.full_name, Literal): 326 extracted_license_object['name'] = extracted_license.full_name.toPython() 327 else: 328 extracted_license_object['name'] = extracted_license.full_name 329 330 if extracted_license.cross_ref: 331 if isinstance(extracted_license.cross_ref, Literal): 332 extracted_license_object['seeAlso'] = extracted_license.cross_ref.toPython() 333 else: 334 extracted_license_object['seeAlso'] = extracted_license.cross_ref 335 336 if extracted_license.comment: 337 if isinstance(extracted_license.comment, Literal): 338 extracted_license_object['comment'] = extracted_license.comment.toPython() 339 else: 340 extracted_license_object['comment'] = extracted_license.comment 341 342 extracted_license_objects.append(extracted_license_object) 343 344 return extracted_license_objects 345 346class Writer(CreationInfoWriter, ReviewInfoWriter, FileWriter, PackageWriter, 347 AnnotationInfoWriter, SnippetWriter, ExtractedLicenseWriter): 348 """ 349 Wrapper for the other writers. 350 Represent a whole SPDX Document as json-serializable objects to then 351 be written as json or yaml files. 352 """ 353 354 def __init__(self, document): 355 super(Writer, self).__init__(document) 356 357 def create_ext_document_references(self): 358 """ 359 Create the External Document References json-serializable representation 360 """ 361 ext_document_references_field = self.document.ext_document_references 362 ext_document_reference_objects = [] 363 for ext_document_reference in ext_document_references_field: 364 ext_document_reference_object = dict() 365 ext_document_reference_object['externalDocumentId'] = ext_document_reference.external_document_id 366 ext_document_reference_object['spdxDocumentNamespace'] = ext_document_reference.spdx_document_uri 367 368 ext_document_reference_object['checksum'] = self.checksum(ext_document_reference.check_sum) 369 370 ext_document_reference_objects.append(ext_document_reference_object) 371 372 return ext_document_reference_objects 373 374 def create_document(self): 375 self.document_object = dict() 376 377 self.document_object['specVersion'] = self.document.version.__str__() 378 self.document_object['namespace'] = self.document.namespace.__str__() 379 self.document_object['creationInfo'] = self.create_creation_info() 380 self.document_object['dataLicense'] = self.license(self.document.data_license) 381 self.document_object['id'] = self.spdx_id(self.document.spdx_id) 382 self.document_object['name'] = self.document.name 383 384 package_info_object = self.create_package_info() 385 package_info_object['files'] = self.create_file_info() 386 387 self.document_object['documentDescribes'] = [{'Package': package_info_object}] 388 389 if self.document.has_comment: 390 self.document_object['comment'] = self.document.comment 391 392 if self.document.ext_document_references: 393 self.document_object['externalDocumentRefs'] = self.create_ext_document_references() 394 395 if self.document.extracted_licenses: 396 self.document_object['extractedLicenseInfos'] = self.create_extracted_license() 397 398 if self.document.reviews: 399 self.document_object['reviewers'] = self.create_review_info() 400 401 if self.document.snippet: 402 self.document_object['snippets'] = self.create_snippet_info() 403 404 if self.document.annotations: 405 self.document_object['annotations'] = self.create_annotation_info() 406 407 return {'Document' : self.document_object} 408