spdx_tools.spdx.jsonschema.document_converter

 1# SPDX-FileCopyrightText: 2022 spdx contributors
 2#
 3# SPDX-License-Identifier: Apache-2.0
 4from beartype.typing import Any, Type
 5
 6from spdx_tools.spdx.document_utils import get_contained_spdx_element_ids
 7from spdx_tools.spdx.jsonschema.annotation_converter import AnnotationConverter
 8from spdx_tools.spdx.jsonschema.converter import TypedConverter
 9from spdx_tools.spdx.jsonschema.creation_info_converter import CreationInfoConverter
10from spdx_tools.spdx.jsonschema.document_properties import DocumentProperty
11from spdx_tools.spdx.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter
12from spdx_tools.spdx.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter
13from spdx_tools.spdx.jsonschema.file_converter import FileConverter
14from spdx_tools.spdx.jsonschema.json_property import JsonProperty
15from spdx_tools.spdx.jsonschema.package_converter import PackageConverter
16from spdx_tools.spdx.jsonschema.relationship_converter import RelationshipConverter
17from spdx_tools.spdx.jsonschema.snippet_converter import SnippetConverter
18from spdx_tools.spdx.model import Document
19
20
21class DocumentConverter(TypedConverter[Document]):
22    creation_info_converter: CreationInfoConverter
23    external_document_ref_converter: ExternalDocumentRefConverter
24    package_converter: PackageConverter
25    file_converter: FileConverter
26    snippet_converter: SnippetConverter
27    annotation_converter: AnnotationConverter
28    relationship_converter: RelationshipConverter
29    extracted_licensing_info_converter: ExtractedLicensingInfoConverter
30
31    def __init__(self):
32        self.external_document_ref_converter = ExternalDocumentRefConverter()
33        self.creation_info_converter = CreationInfoConverter()
34        self.package_converter = PackageConverter()
35        self.file_converter = FileConverter()
36        self.snippet_converter = SnippetConverter()
37        self.annotation_converter = AnnotationConverter()
38        self.relationship_converter = RelationshipConverter()
39        self.extracted_licensing_info_converter = ExtractedLicensingInfoConverter()
40
41    def get_json_type(self) -> Type[JsonProperty]:
42        return DocumentProperty
43
44    def get_data_model_type(self) -> Type[Document]:
45        return Document
46
47    def json_property_name(self, document_property: DocumentProperty) -> str:
48        if document_property == DocumentProperty.SPDX_ID:
49            return "SPDXID"
50        return super().json_property_name(document_property)
51
52    def _get_property_value(
53        self, document: Document, document_property: DocumentProperty, _document: Document = None
54    ) -> Any:
55        if document_property == DocumentProperty.SPDX_ID:
56            return document.creation_info.spdx_id
57        elif document_property == DocumentProperty.ANNOTATIONS:
58            # annotations referencing files, packages or snippets will be added to those elements directly
59            element_ids = get_contained_spdx_element_ids(document)
60            document_annotations = filter(
61                lambda annotation: annotation.spdx_id not in element_ids, document.annotations
62            )
63            return [self.annotation_converter.convert(annotation) for annotation in document_annotations] or None
64        elif document_property == DocumentProperty.COMMENT:
65            return document.creation_info.document_comment
66        elif document_property == DocumentProperty.CREATION_INFO:
67            return self.creation_info_converter.convert(document.creation_info)
68        elif document_property == DocumentProperty.DATA_LICENSE:
69            return document.creation_info.data_license
70        elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS:
71            return [
72                self.external_document_ref_converter.convert(external_document_ref)
73                for external_document_ref in document.creation_info.external_document_refs
74            ] or None
75        elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS:
76            return [
77                self.extracted_licensing_info_converter.convert(licensing_info)
78                for licensing_info in document.extracted_licensing_info
79            ] or None
80        elif document_property == DocumentProperty.NAME:
81            return document.creation_info.name
82        elif document_property == DocumentProperty.SPDX_VERSION:
83            return document.creation_info.spdx_version
84        elif document_property == DocumentProperty.DOCUMENT_NAMESPACE:
85            return document.creation_info.document_namespace
86        elif document_property == DocumentProperty.PACKAGES:
87            return [self.package_converter.convert(package, document) for package in document.packages] or None
88        elif document_property == DocumentProperty.FILES:
89            return [self.file_converter.convert(file, document) for file in document.files] or None
90        elif document_property == DocumentProperty.SNIPPETS:
91            return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] or None
92        elif document_property == DocumentProperty.RELATIONSHIPS:
93            return [
94                self.relationship_converter.convert(relationship) for relationship in document.relationships
95            ] or None
22class DocumentConverter(TypedConverter[Document]):
23    creation_info_converter: CreationInfoConverter
24    external_document_ref_converter: ExternalDocumentRefConverter
25    package_converter: PackageConverter
26    file_converter: FileConverter
27    snippet_converter: SnippetConverter
28    annotation_converter: AnnotationConverter
29    relationship_converter: RelationshipConverter
30    extracted_licensing_info_converter: ExtractedLicensingInfoConverter
31
32    def __init__(self):
33        self.external_document_ref_converter = ExternalDocumentRefConverter()
34        self.creation_info_converter = CreationInfoConverter()
35        self.package_converter = PackageConverter()
36        self.file_converter = FileConverter()
37        self.snippet_converter = SnippetConverter()
38        self.annotation_converter = AnnotationConverter()
39        self.relationship_converter = RelationshipConverter()
40        self.extracted_licensing_info_converter = ExtractedLicensingInfoConverter()
41
42    def get_json_type(self) -> Type[JsonProperty]:
43        return DocumentProperty
44
45    def get_data_model_type(self) -> Type[Document]:
46        return Document
47
48    def json_property_name(self, document_property: DocumentProperty) -> str:
49        if document_property == DocumentProperty.SPDX_ID:
50            return "SPDXID"
51        return super().json_property_name(document_property)
52
53    def _get_property_value(
54        self, document: Document, document_property: DocumentProperty, _document: Document = None
55    ) -> Any:
56        if document_property == DocumentProperty.SPDX_ID:
57            return document.creation_info.spdx_id
58        elif document_property == DocumentProperty.ANNOTATIONS:
59            # annotations referencing files, packages or snippets will be added to those elements directly
60            element_ids = get_contained_spdx_element_ids(document)
61            document_annotations = filter(
62                lambda annotation: annotation.spdx_id not in element_ids, document.annotations
63            )
64            return [self.annotation_converter.convert(annotation) for annotation in document_annotations] or None
65        elif document_property == DocumentProperty.COMMENT:
66            return document.creation_info.document_comment
67        elif document_property == DocumentProperty.CREATION_INFO:
68            return self.creation_info_converter.convert(document.creation_info)
69        elif document_property == DocumentProperty.DATA_LICENSE:
70            return document.creation_info.data_license
71        elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS:
72            return [
73                self.external_document_ref_converter.convert(external_document_ref)
74                for external_document_ref in document.creation_info.external_document_refs
75            ] or None
76        elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS:
77            return [
78                self.extracted_licensing_info_converter.convert(licensing_info)
79                for licensing_info in document.extracted_licensing_info
80            ] or None
81        elif document_property == DocumentProperty.NAME:
82            return document.creation_info.name
83        elif document_property == DocumentProperty.SPDX_VERSION:
84            return document.creation_info.spdx_version
85        elif document_property == DocumentProperty.DOCUMENT_NAMESPACE:
86            return document.creation_info.document_namespace
87        elif document_property == DocumentProperty.PACKAGES:
88            return [self.package_converter.convert(package, document) for package in document.packages] or None
89        elif document_property == DocumentProperty.FILES:
90            return [self.file_converter.convert(file, document) for file in document.files] or None
91        elif document_property == DocumentProperty.SNIPPETS:
92            return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] or None
93        elif document_property == DocumentProperty.RELATIONSHIPS:
94            return [
95                self.relationship_converter.convert(relationship) for relationship in document.relationships
96            ] or None

Base class for all converters between an instance of the tools-python data model and the corresponding dictionary representation, following the json schema. The generic type T is the type according to the data model. Each converter has several methods:

  • get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass. These methods are abstract in the base class and need to be implemented in subclasses.
  • json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case of exceptions like "SPDXID".
  • convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some cases, the full document is required (see below). The logic should be generic for all types.
  • requires_full_document: indicates whether the full document is required for conversion. Returns False by default, can be overridden as needed for specific types.
  • _get_property_value: Retrieves the value of a specific json property from the data model instance. In some cases, the full document is required.
def get_json_type(self) -> type[spdx_tools.spdx.jsonschema.json_property.JsonProperty]:
42    def get_json_type(self) -> Type[JsonProperty]:
43        return DocumentProperty
def get_data_model_type(self) -> type[spdx_tools.spdx.model.document.Document]:
45    def get_data_model_type(self) -> Type[Document]:
46        return Document
def json_property_name( self, document_property: spdx_tools.spdx.jsonschema.document_properties.DocumentProperty) -> str:
48    def json_property_name(self, document_property: DocumentProperty) -> str:
49        if document_property == DocumentProperty.SPDX_ID:
50            return "SPDXID"
51        return super().json_property_name(document_property)