aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Zantow <kzantow@gmail.com>2022-10-10 12:36:03 -0400
committerKeith Zantow <kzantow@gmail.com>2022-10-10 12:36:03 -0400
commit0b2bd4be354bdd719c14a720af0ee2140b676dcb (patch)
tree89ea73f357d872d36baa447f55a99ca659a70e9d
parentab3e717feaf76784e3939592d98d788d19a4c022 (diff)
downloadspdx-tools-0b2bd4be354bdd719c14a720af0ee2140b676dcb.tar.gz
chore: support for 2.3 features to rdfloader
Signed-off-by: Keith Zantow <kzantow@gmail.com>
-rw-r--r--rdfloader/parser2v3/constants.go268
-rw-r--r--rdfloader/parser2v3/license_utils.go119
-rw-r--r--rdfloader/parser2v3/license_utils_test.go345
-rw-r--r--rdfloader/parser2v3/parse_annotation.go81
-rw-r--r--rdfloader/parser2v3/parse_annotation_test.go183
-rw-r--r--rdfloader/parser2v3/parse_creation_info.go58
-rw-r--r--rdfloader/parser2v3/parse_creation_info_test.go107
-rw-r--r--rdfloader/parser2v3/parse_file.go207
-rw-r--r--rdfloader/parser2v3/parse_file_test.go765
-rw-r--r--rdfloader/parser2v3/parse_license.go291
-rw-r--r--rdfloader/parser2v3/parse_license_test.go853
-rw-r--r--rdfloader/parser2v3/parse_other_license_info.go38
-rw-r--r--rdfloader/parser2v3/parse_other_license_info_test.go74
-rw-r--r--rdfloader/parser2v3/parse_package.go362
-rw-r--r--rdfloader/parser2v3/parse_package_test.go788
-rw-r--r--rdfloader/parser2v3/parse_relationship.go156
-rw-r--r--rdfloader/parser2v3/parse_relationship_test.go397
-rw-r--r--rdfloader/parser2v3/parse_review.go38
-rw-r--r--rdfloader/parser2v3/parse_review_test.go73
-rw-r--r--rdfloader/parser2v3/parse_snippet_info.go199
-rw-r--r--rdfloader/parser2v3/parse_snippet_info_test.go536
-rw-r--r--rdfloader/parser2v3/parse_spdx_document.go120
-rw-r--r--rdfloader/parser2v3/parse_spdx_document_test.go241
-rw-r--r--rdfloader/parser2v3/parser.go133
-rw-r--r--rdfloader/parser2v3/parser_test.go172
-rw-r--r--rdfloader/parser2v3/types.go127
-rw-r--r--rdfloader/parser2v3/utils.go155
-rw-r--r--rdfloader/parser2v3/utils_test.go290
-rw-r--r--rdfloader/rdfloader.go14
-rw-r--r--rdfloader/rdfloader_test.go84
30 files changed, 7274 insertions, 0 deletions
diff --git a/rdfloader/parser2v3/constants.go b/rdfloader/parser2v3/constants.go
new file mode 100644
index 0000000..44ca0d4
--- /dev/null
+++ b/rdfloader/parser2v3/constants.go
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import "github.com/spdx/gordf/rdfloader/parser"
+
+var (
+ // NAMESPACES
+ NS_SPDX = "http://spdx.org/rdf/terms#"
+ NS_RDFS = "http://www.w3.org/2000/01/rdf-schema#"
+ NS_RDF = parser.RDFNS
+ NS_PTR = "http://www.w3.org/2009/pointers#"
+ NS_DOAP = "http://usefulinc.com/ns/doap#"
+
+ // SPDX properties
+ SPDX_SPEC_VERSION = NS_SPDX + "specVersion"
+ SPDX_DATA_LICENSE = NS_SPDX + "dataLicense"
+ SPDX_NAME = NS_SPDX + "name"
+ SPDX_EXTERNAL_DOCUMENT_REF = NS_SPDX + "externalDocumentRef"
+ SPDX_LICENSE_LIST_VERSION = NS_SPDX + "licenseListVersion"
+ SPDX_CREATOR = NS_SPDX + "creator"
+ SPDX_CREATED = NS_SPDX + "created"
+ SPDX_REVIEWED = NS_SPDX + "reviewed"
+ SPDX_DESCRIBES_PACKAGE = NS_SPDX + "describesPackage"
+ SPDX_HAS_EXTRACTED_LICENSING_INFO = NS_SPDX + "hasExtractedLicensingInfo"
+ SPDX_RELATIONSHIP = NS_SPDX + "relationship"
+ SPDX_ANNOTATION = NS_SPDX + "annotation"
+ SPDX_COMMENT = NS_SPDX + "comment"
+ SPDX_CREATION_INFO = NS_SPDX + "creationInfo"
+ SPDX_CHECKSUM_ALGORITHM_SHA1 = NS_SPDX + "checksumAlgorithm_sha1"
+ SPDX_CHECKSUM_ALGORITHM_SHA256 = NS_SPDX + "checksumAlgorithm_sha256"
+ SPDX_CHECKSUM_ALGORITHM_MD5 = NS_SPDX + "checksumAlgorithm_md5"
+ SPDX_EXTERNAL_DOCUMENT_ID = NS_SPDX + "externalDocumentId"
+ SPDX_SPDX_DOCUMENT = NS_SPDX + "spdxDocument"
+ SPDX_SPDX_DOCUMENT_CAPITALIZED = NS_SPDX + "SpdxDocument"
+ SPDX_CHECKSUM = NS_SPDX + "checksum"
+ SPDX_CHECKSUM_CAPITALIZED = NS_SPDX + "Checksum"
+ SPDX_ANNOTATION_TYPE = NS_SPDX + "annotationType"
+ SPDX_ANNOTATION_TYPE_OTHER = NS_SPDX + "annotationType_other"
+ SPDX_ANNOTATION_TYPE_REVIEW = NS_SPDX + "annotationType_review"
+ SPDX_LICENSE_INFO_IN_FILE = NS_SPDX + "licenseInfoInFile"
+ SPDX_LICENSE_CONCLUDED = NS_SPDX + "licenseConcluded"
+ SPDX_LICENSE_COMMENTS = NS_SPDX + "licenseComments"
+ SPDX_COPYRIGHT_TEXT = NS_SPDX + "copyrightText"
+ SPDX_ARTIFACT_OF = NS_SPDX + "artifactOf"
+ SPDX_NOTICE_TEXT = NS_SPDX + "noticeText"
+ SPDX_FILE_CONTRIBUTOR = NS_SPDX + "fileContributor"
+ SPDX_FILE_DEPENDENCY = NS_SPDX + "fileDependency"
+ SPDX_FILE_TYPE = NS_SPDX + "fileType"
+ SPDX_FILE_NAME = NS_SPDX + "fileName"
+ SPDX_EXTRACTED_TEXT = NS_SPDX + "extractedText"
+ SPDX_LICENSE_ID = NS_SPDX + "licenseId"
+ SPDX_FILE = NS_SPDX + "File"
+ SPDX_PACKAGE = NS_SPDX + "Package"
+ SPDX_SPDX_ELEMENT = NS_SPDX + "SpdxElement"
+ SPDX_VERSION_INFO = NS_SPDX + "versionInfo"
+ SPDX_PACKAGE_FILE_NAME = NS_SPDX + "packageFileName"
+ SPDX_SUPPLIER = NS_SPDX + "supplier"
+ SPDX_ORIGINATOR = NS_SPDX + "originator"
+ SPDX_DOWNLOAD_LOCATION = NS_SPDX + "downloadLocation"
+ SPDX_FILES_ANALYZED = NS_SPDX + "filesAnalyzed"
+ SPDX_PACKAGE_VERIFICATION_CODE = NS_SPDX + "packageVerificationCode"
+ SPDX_SOURCE_INFO = NS_SPDX + "sourceInfo"
+ SPDX_LICENSE_INFO_FROM_FILES = NS_SPDX + "licenseInfoFromFiles"
+ SPDX_LICENSE_DECLARED = NS_SPDX + "licenseDeclared"
+ SPDX_SUMMARY = NS_SPDX + "summary"
+ SPDX_DESCRIPTION = NS_SPDX + "description"
+ SPDX_EXTERNAL_REF = NS_SPDX + "externalRef"
+ SPDX_HAS_FILE = NS_SPDX + "hasFile"
+ SPDX_PRIMARY_PACKAGE_PURPOSE = NS_SPDX + "primaryPackagePurpose"
+ SPDX_RELEASE_DATE = NS_SPDX + "releaseDate"
+ SPDX_BUILT_DATE = NS_SPDX + "builtDate"
+ SPDX_VALID_UNTIL_DATE = NS_SPDX + "validUntilDate"
+ SPDX_ATTRIBUTION_TEXT = NS_SPDX + "attributionText"
+ SPDX_PACKAGE_VERIFICATION_CODE_VALUE = NS_SPDX + "packageVerificationCodeValue"
+ SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE = NS_SPDX + "packageVerificationCodeExcludedFile"
+ SPDX_RELATED_SPDX_ELEMENT = NS_SPDX + "relatedSpdxElement"
+ SPDX_RELATIONSHIP_TYPE = NS_SPDX + "relationshipType"
+ SPDX_SNIPPET_FROM_FILE = NS_SPDX + "snippetFromFile"
+ SPDX_LICENSE_INFO_IN_SNIPPET = NS_SPDX + "licenseInfoInSnippet"
+ SPDX_RANGE = NS_SPDX + "range"
+ SPDX_REVIEWER = NS_SPDX + "reviewer"
+ SPDX_REVIEW_DATE = NS_SPDX + "reviewDate"
+ SPDX_SNIPPET = NS_SPDX + "Snippet"
+ SPDX_ALGORITHM = NS_SPDX + "algorithm"
+ SPDX_CHECKSUM_VALUE = NS_SPDX + "checksumValue"
+ SPDX_REFERENCE_CATEGORY = NS_SPDX + "referenceCategory"
+ SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER = NS_SPDX + "referenceCategory_packageManager"
+ SPDX_REFERENCE_CATEGORY_SECURITY = NS_SPDX + "referenceCategory_security"
+ SPDX_REFERENCE_CATEGORY_OTHER = NS_SPDX + "referenceCategory_other"
+
+ SPDX_REFERENCE_TYPE = NS_SPDX + "referenceType"
+ SPDX_REFERENCE_LOCATOR = NS_SPDX + "referenceLocator"
+ SPDX_ANNOTATION_DATE = NS_SPDX + "annotationDate"
+ SPDX_ANNOTATOR = NS_SPDX + "annotator"
+ SPDX_MEMBER = NS_SPDX + "member"
+ SPDX_DISJUNCTIVE_LICENSE_SET = NS_SPDX + "DisjunctiveLicenseSet"
+ SPDX_CONJUNCTIVE_LICENSE_SET = NS_SPDX + "ConjunctiveLicenseSet"
+ SPDX_EXTRACTED_LICENSING_INFO = NS_SPDX + "ExtractedLicensingInfo"
+ SPDX_SIMPLE_LICENSING_INFO = NS_SPDX + "SimpleLicensingInfo"
+ SPDX_NONE_CAPS = NS_SPDX + "NONE"
+ SPDX_NOASSERTION_CAPS = NS_SPDX + "NOASSERTION"
+ SPDX_NONE_SMALL = NS_SPDX + "none"
+ SPDX_NOASSERTION_SMALL = NS_SPDX + "noassertion"
+ SPDX_LICENSE = NS_SPDX + "License"
+ SPDX_LISTED_LICENSE = NS_SPDX + "ListedLicense"
+ SPDX_EXAMPLE = NS_SPDX + "example"
+ SPDX_IS_OSI_APPROVED = NS_SPDX + "isOsiApproved"
+ SPDX_STANDARD_LICENSE_TEMPLATE = NS_SPDX + "standardLicenseTemplate"
+ SPDX_IS_DEPRECATED_LICENSE_ID = NS_SPDX + "isDeprecatedLicenseId"
+ SPDX_IS_FSF_LIBRE = NS_SPDX + "isFsfLibre"
+ SPDX_LICENSE_TEXT = NS_SPDX + "licenseText"
+ SPDX_STANDARD_LICENSE_HEADER = NS_SPDX + "standardLicenseHeader"
+ SPDX_LICENSE_EXCEPTION_ID = NS_SPDX + "licenseExceptionId"
+ SPDX_LICENSE_EXCEPTION_TEXT = NS_SPDX + "licenseExceptionText"
+ SPDX_LICENSE_EXCEPTION = NS_SPDX + "licenseException"
+ SPDX_WITH_EXCEPTION_OPERATOR = NS_SPDX + "WithExceptionOperator"
+ SPDX_OR_LATER_OPERATOR = NS_SPDX + "OrLaterOperator"
+ SPDX_STANDARD_LICENSE_HEADER_TEMPLATE = NS_SPDX + "standardLicenseHeaderTemplate"
+
+ // RDFS properties
+ RDFS_COMMENT = NS_RDFS + "comment"
+ RDFS_SEE_ALSO = NS_RDFS + "seeAlso"
+
+ // RDF properties
+ RDF_TYPE = NS_RDF + "type"
+
+ // DOAP properties
+ DOAP_HOMEPAGE = NS_DOAP + "homepage"
+ DOAP_NAME = NS_DOAP + "name"
+
+ // PTR properties
+ PTR_START_END_POINTER = NS_PTR + "StartEndPointer"
+ PTR_START_POINTER = NS_PTR + "startPointer"
+ PTR_BYTE_OFFSET_POINTER = NS_PTR + "ByteOffsetPointer"
+ PTR_LINE_CHAR_POINTER = NS_PTR + "LineCharPointer"
+ PTR_REFERENCE = NS_PTR + "reference"
+ PTR_OFFSET = NS_PTR + "offset"
+ PTR_LINE_NUMBER = NS_PTR + "lineNumber"
+ PTR_END_POINTER = NS_PTR + "endPointer"
+
+ // prefixes
+ PREFIX_RELATIONSHIP_TYPE = "relationshipType_"
+)
+
+func AllRelationshipTypes() []string {
+ return []string{
+ "amendment", "ancestorOf", "buildDependencyOf", "buildToolOf",
+ "containedBy", "contains", "copyOf", "dataFile", "dataFileOf",
+ "dependencyManifestOf", "dependencyOf", "dependsOn", "descendantOf",
+ "describedBy", "describes", "devDependencyOf", "devToolOf",
+ "distributionArtifact", "documentation", "dynamicLink", "exampleOf",
+ "expandedFromArchive", "fileAdded", "fileDeleted", "fileModified",
+ "generatedFrom", "generates", "hasPrerequisite", "metafileOf",
+ "optionalComponentOf", "optionalDependencyOf", "other", "packageOf",
+ "patchApplied", "patchFor", "prerequisiteFor", "providedDependencyOf",
+ "runtimeDependencyOf", "staticLink", "testDependencyOf", "testOf",
+ "testToolOf", "testcaseOf", "variantOf",
+ }
+}
+
+func AllStandardLicenseIDS() []string {
+ return []string{
+ "0BSD", "389-exception", "AAL", "Abstyles", "Adobe-2006", "Adobe-Glyph",
+ "ADSL", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0", "Afmparse",
+ "AGPL-1.0-only", "AGPL-1.0-or-later", "AGPL-1.0", "AGPL-3.0-only",
+ "AGPL-3.0-or-later", "AGPL-3.0", "Aladdin", "AMDPLPA", "AML", "AMPAS",
+ "ANTLR-PD", "Apache-1.0", "Apache-1.1", "Apache-2.0", "APAFML", "APL-1.0",
+ "APSL-1.0", "APSL-1.1", "APSL-1.2", "APSL-2.0", "Artistic-1.0-cl8",
+ "Artistic-1.0-Perl", "Artistic-1.0", "Artistic-2.0", "",
+ "Autoconf-exception-2.0", "Autoconf-exception-3.0", "Bahyph", "Barr",
+ "Beerware", "Bison-exception-2.2", "BitTorrent-1.0", "BitTorrent-1.1",
+ "blessing", "BlueOak-1.0.0", "Bootloader-exception", "Borceux", "BSD-1-Clause",
+ "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-2-Clause-Patent",
+ "BSD-2-Clause-Views", "BSD-2-Clause", "BSD-3-Clause-Attribution",
+ "BSD-3-Clause-Clear", "BSD-3-Clause-LBNL",
+ "BSD-3-Clause-No-Nuclear-License-2014", "BSD-3-Clause-No-Nuclear-License",
+ "BSD-3-Clause-No-Nuclear-Warranty", "BSD-3-Clause-Open-MPI", "BSD-3-Clause",
+ "BSD-4-Clause-UC", "BSD-4-Clause", "BSD-Protection", "BSD-Source-Code",
+ "BSL-1.0", "bzip2-1.0.5", "bzip2-1.0.6", "CAL-1.0-Combined-Work-Exception",
+ "CAL-1.0", "Caldera", "CATOSL-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5",
+ "CC-BY-3.0-AT", "CC-BY-3.0", "CC-BY-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0",
+ "CC-BY-NC-2.5", "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0",
+ "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0-IGO", "CC-BY-NC-ND-3.0",
+ "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5",
+ "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0",
+ "CC-BY-ND-2.5", "CC-BY-ND-3.0", "CC-BY-ND-4.0", "CC-BY-SA-1.0", "CC-BY-SA-2.0",
+ "CC-BY-SA-2.5", "CC-BY-SA-3.0-AT", "CC-BY-SA-3.0", "CC-BY-SA-4.0", "CC-PDDC",
+ "CC0-1.0", "CDDL-1.0", "CDDL-1.1", "CDLA-Permissive-1.0", "CDLA-Sharing-1.0",
+ "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-2.1", "CECILL-B", "CECILL-C",
+ "CERN-OHL-1.1", "CERN-OHL-1.2", "CERN-OHL-P-2.0", "CERN-OHL-S-2.0",
+ "CERN-OHL-W-2.0", "ClArtistic", "Classpath-exception-2.0",
+ "CLISP-exception-2.0", "CNRI-Jython", "CNRI-Python-GPL-Compatible",
+ "CNRI-Python", "Condor-1.1", "copyleft-next-0.3.0", "copyleft-next-0.3.1",
+ "CPAL-1.0", "CPL-1.0", "CPOL-1.02", "Crossword", "CrystalStacker",
+ "CUA-OPL-1.0", "Cube", "curl", "D-FSL-1.0", "diffmark",
+ "DigiRule-FOSS-exception", "DOC", "Dotseqn", "DSDP", "dvipdfm", "ECL-1.0",
+ "ECL-2.0", "eCos-2.0", "eCos-exception-2.0", "EFL-1.0", "EFL-2.0", "eGenix",
+ "Entessa", "EPICS", "EPL-1.0", "EPL-2.0", "ErlPL-1.1", "etalab-2.0",
+ "EUDatagrid", "EUPL-1.0", "EUPL-1.1", "EUPL-1.2", "Eurosym", "Fair",
+ "Fawkes-Runtime-exception", "FLTK-exception", "Font-exception-2.0",
+ "Frameworx-1.0", "FreeImage", "freertos-exception-2.0", "FSFAP", "FSFUL",
+ "FSFULLR", "FTL", "GCC-exception-2.0", "GCC-exception-3.1",
+ "GFDL-1.1-invariants-only", "GFDL-1.1-invariants-or-later",
+ "GFDL-1.1-no-invariants-only", "GFDL-1.1-no-invariants-or-later",
+ "GFDL-1.1-only", "GFDL-1.1-or-later", "GFDL-1.1", "GFDL-1.2-invariants-only",
+ "GFDL-1.2-invariants-or-later", "GFDL-1.2-no-invariants-only",
+ "GFDL-1.2-no-invariants-or-later", "GFDL-1.2-only", "GFDL-1.2-or-later",
+ "GFDL-1.2", "GFDL-1.3-invariants-only", "GFDL-1.3-invariants-or-later",
+ "GFDL-1.3-no-invariants-only", "GFDL-1.3-no-invariants-or-later",
+ "GFDL-1.3-only", "GFDL-1.3-or-later", "GFDL-1.3", "Giftware", "GL2PS", "Glide",
+ "Glulxe", "GLWTPL", "gnu-javamail-exception", "gnuplot", "GPL-1.0+",
+ "GPL-1.0-only", "GPL-1.0-or-later", "GPL-1.0", "GPL-2.0+", "GPL-2.0-only",
+ "GPL-2.0-or-later", "GPL-2.0-with-autoconf-exception",
+ "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception",
+ "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-2.0",
+ "GPL-3.0+", "GPL-3.0-linking-exception", "GPL-3.0-linking-source-exception",
+ "GPL-3.0-only", "GPL-3.0-or-later", "GPL-3.0-with-autoconf-exception",
+ "GPL-3.0-with-GCC-exception", "GPL-3.0", "GPL-CC-1.0", "gSOAP-1.3b",
+ "HaskellReport", "Hippocratic-2.1", "HPND-sell-variant", "HPND",
+ "i2p-gpl-java-exception", "IBM-pibs", "ICU", "IJG", "ImageMagick", "iMatix",
+ "Imlib2", "Info-ZIP", "Intel-ACPI", "Intel", "Interbase-1.0", "IPA", "IPL-1.0",
+ "ISC", "JasPer-2.0", "JPNIC", "JSON", "LAL-1.2", "LAL-1.3", "Latex2e",
+ "Leptonica", "LGPL-2.0+", "LGPL-2.0-only", "LGPL-2.0-or-later", "LGPL-2.0",
+ "LGPL-2.1+", "LGPL-2.1-only", "LGPL-2.1-or-later", "LGPL-2.1", "LGPL-3.0+",
+ "LGPL-3.0-linking-exception", "LGPL-3.0-only", "LGPL-3.0-or-later", "LGPL-3.0",
+ "LGPLLR", "libpng-2.0", "Libpng", "libselinux-1.0", "libtiff",
+ "Libtool-exception", "licenses", "LiLiQ-P-1.1", "LiLiQ-R-1.1",
+ "LiLiQ-Rplus-1.1", "Linux-OpenIB", "Linux-syscall-note", "LLVM-exception",
+ "LPL-1.0", "LPL-1.02", "LPPL-1.0", "LPPL-1.1", "LPPL-1.2", "LPPL-1.3a",
+ "LPPL-1.3c", "LZMA-exception", "MakeIndex", "mif-exception", "MirOS", "MIT-0",
+ "MIT-advertising", "MIT-CMU", "MIT-enna", "MIT-feh", "MIT", "MITNFA",
+ "Motosoto", "mpich2", "MPL-1.0", "MPL-1.1", "MPL-2.0-no-copyleft-exception",
+ "MPL-2.0", "MS-PL", "MS-RL", "MTLL", "MulanPSL-1.0", "MulanPSL-2.0", "Multics",
+ "Mup", "NASA-1.3", "Naumen", "NBPL-1.0", "NCGL-UK-2.0", "NCSA", "Net-SNMP",
+ "NetCDF", "Newsletr", "NGPL", "NIST-PD-fallback", "NIST-PD", "NLOD-1.0",
+ "NLPL", "Nokia-Qt-exception-1.1", "Nokia", "NOSL", "Noweb", "NPL-1.0",
+ "NPL-1.1", "NPOSL-3.0", "NRL", "NTP-0", "NTP", "Nunit", "O-UDA-1.0",
+ "OCaml-LGPL-linking-exception", "OCCT-exception-1.0", "OCCT-PL", "OCLC-2.0",
+ "ODbL-1.0", "ODC-By-1.0", "OFL-1.0-no-RFN", "OFL-1.0-RFN", "OFL-1.0",
+ "OFL-1.1-no-RFN", "OFL-1.1-RFN", "OFL-1.1", "OGC-1.0", "OGL-Canada-2.0",
+ "OGL-UK-1.0", "OGL-UK-2.0", "OGL-UK-3.0", "OGTSL", "OLDAP-1.1", "OLDAP-1.2",
+ "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0.1", "OLDAP-2.0", "OLDAP-2.1",
+ "OLDAP-2.2.1", "OLDAP-2.2.2", "OLDAP-2.2", "OLDAP-2.3", "OLDAP-2.4",
+ "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OLDAP-2.8", "OML", "",
+ "OpenJDK-assembly-exception-1.0", "OpenSSL", "openvpn-openssl-exception",
+ "OPL-1.0", "OSET-PL-2.1", "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1",
+ "OSL-3.0", "Parity-6.0.0", "Parity-7.0.0", "PDDL-1.0", "PHP-3.0", "PHP-3.01",
+ "Plexus", "PolyForm-Noncommercial-1.0.0", "PolyForm-Small-Business-1.0.0",
+ "PostgreSQL", "PS-or-PDF-font-exception-20170817", "PSF-2.0", "psfrag",
+ "psutils", "Python-2.0", "Qhull", "QPL-1.0", "Qt-GPL-exception-1.0",
+ "Qt-LGPL-exception-1.1", "Qwt-exception-1.0", "Rdisc", "RHeCos-1.1", "RPL-1.1",
+ "RPL-1.5", "RPSL-1.0", "RSA-MD", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA",
+ "Sendmail-8.23", "Sendmail", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "SHL-0.5",
+ "SHL-0.51", "SHL-2.0", "SHL-2.1", "SimPL-2.0", "SISSL-1.2", "SISSL",
+ "Sleepycat", "SMLNJ", "SMPPL", "SNIA", "Spencer-86", "Spencer-94",
+ "Spencer-99", "SPL-1.0", "SSH-OpenSSH", "SSH-short", "SSPL-1.0",
+ "StandardML-NJ", "SugarCRM-1.1.3", "Swift-exception", "SWL", "TAPR-OHL-1.0",
+ "TCL", "TCP-wrappers", "TMate", "TORQUE-1.1", "TOSL", "TU-Berlin-1.0",
+ "TU-Berlin-2.0", "u-boot-exception-2.0", "UCL-1.0", "Unicode-DFS-2015",
+ "Unicode-DFS-2016", "Unicode-TOU", "Universal-FOSS-exception-1.0", "Unlicense",
+ "UPL-1.0", "Vim", "VOSTROM", "VSL-1.0", "W3C-19980720", "W3C-20150513", "W3C",
+ "Watcom-1.0", "Wsuipa", "WTFPL", "WxWindows-exception-3.1", "wxWindows", "X11",
+ "Xerox", "XFree86-1.1", "xinetd", "Xnet", "xpp", "XSkat", "YPL-1.0", "YPL-1.1",
+ "Zed", "Zend-2.0", "Zimbra-1.3", "Zimbra-1.4", "zlib-acknowledgement", "Zlib",
+ "ZPL-1.1", "ZPL-2.0", "ZPL-2.1",
+ }
+}
diff --git a/rdfloader/parser2v3/license_utils.go b/rdfloader/parser2v3/license_utils.go
new file mode 100644
index 0000000..11bb3c8
--- /dev/null
+++ b/rdfloader/parser2v3/license_utils.go
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+)
+
+/* util methods for licenses and checksums below:*/
+
+// Given the license URI, returns the name of the license defined
+// in the last part of the uri.
+// This function is susceptible to false-positives.
+func getLicenseStringFromURI(uri string) string {
+ licenseEnd := strings.TrimSpace(getLastPartOfURI(uri))
+ lower := strings.ToLower(licenseEnd)
+ if lower == "none" || lower == "noassertion" {
+ return strings.ToUpper(licenseEnd)
+ }
+ return licenseEnd
+}
+
+// returns the checksum algorithm and it's value
+// In the newer versions, these two strings will be bound to a single checksum struct
+// whose pointer will be returned.
+func (parser *rdfParser2_3) getChecksumFromNode(checksumNode *gordfParser.Node) (algorithm common.ChecksumAlgorithm, value string, err error) {
+ var checksumValue, checksumAlgorithm string
+ for _, checksumTriple := range parser.nodeToTriples(checksumNode) {
+ switch checksumTriple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_CHECKSUM_VALUE:
+ // cardinality: exactly 1
+ checksumValue = strings.TrimSpace(checksumTriple.Object.ID)
+ case SPDX_ALGORITHM:
+ // cardinality: exactly 1
+ checksumAlgorithm, err = getAlgorithmFromURI(checksumTriple.Object.ID)
+ if err != nil {
+ return
+ }
+ default:
+ err = fmt.Errorf("unknown predicate '%s' while parsing checksum node", checksumTriple.Predicate.ID)
+ return
+ }
+ }
+ return common.ChecksumAlgorithm(checksumAlgorithm), checksumValue, nil
+}
+
+func getAlgorithmFromURI(algorithmURI string) (checksumAlgorithm string, err error) {
+ fragment := getLastPartOfURI(algorithmURI)
+ if !strings.HasPrefix(fragment, "checksumAlgorithm_") {
+ return "", fmt.Errorf("checksum algorithm uri must begin with checksumAlgorithm_. found %s", fragment)
+ }
+ algorithm := strings.TrimPrefix(fragment, "checksumAlgorithm_")
+ algorithm = strings.ToLower(strings.TrimSpace(algorithm))
+ switch algorithm {
+ case "md2", "md4", "md5", "md6":
+ checksumAlgorithm = strings.ToUpper(algorithm)
+ case "sha1", "sha224", "sha256", "sha384", "sha512":
+ checksumAlgorithm = strings.ToUpper(algorithm)
+ default:
+ return "", fmt.Errorf("unknown checksum algorithm %s", algorithm)
+ }
+ return
+}
+
+// from a list of licenses, it returns a
+// list of string representation of those licenses.
+func mapLicensesToStrings(licences []AnyLicenseInfo) []string {
+ res := make([]string, len(licences), len(licences))
+ for i, lic := range licences {
+ res[i] = lic.ToLicenseString()
+ }
+ return res
+}
+
+/****** Type Functions ******/
+
+// TODO: should probably add brackets while linearizing a nested license.
+func (lic ConjunctiveLicenseSet) ToLicenseString() string {
+ return strings.Join(mapLicensesToStrings(lic.members), " AND ")
+}
+
+// TODO: should probably add brackets while linearizing a nested license.
+func (lic DisjunctiveLicenseSet) ToLicenseString() string {
+ return strings.Join(mapLicensesToStrings(lic.members), " OR ")
+}
+
+func (lic ExtractedLicensingInfo) ToLicenseString() string {
+ return lic.licenseID
+}
+
+func (operator OrLaterOperator) ToLicenseString() string {
+ return operator.member.ToLicenseString()
+}
+
+func (lic License) ToLicenseString() string {
+ return lic.licenseID
+}
+
+func (lic ListedLicense) ToLicenseString() string {
+ return lic.licenseID
+}
+
+func (lic WithExceptionOperator) ToLicenseString() string {
+ return lic.member.ToLicenseString()
+}
+
+func (lic SpecialLicense) ToLicenseString() string {
+ return string(lic.value)
+}
+
+func (lic SimpleLicensingInfo) ToLicenseString() string {
+ return lic.licenseID
+}
diff --git a/rdfloader/parser2v3/license_utils_test.go b/rdfloader/parser2v3/license_utils_test.go
new file mode 100644
index 0000000..0156b99
--- /dev/null
+++ b/rdfloader/parser2v3/license_utils_test.go
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "testing"
+)
+
+func Test_getLicenseStringFromURI(t *testing.T) {
+ // TestCase 1: NONE license
+ input := SPDX_NONE_CAPS
+ output := getLicenseStringFromURI(input)
+ expectedOutput := "NONE"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", expectedOutput, output)
+ }
+
+ // TestCase 2: NOASSERTION license
+ input = SPDX_NOASSERTION_SMALL
+ output = getLicenseStringFromURI(input)
+ expectedOutput = "NOASSERTION"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", expectedOutput, output)
+ }
+
+ // TestCase 3: Other license
+ input = NS_SPDX + "LicenseRef-1"
+ output = getLicenseStringFromURI(input)
+ expectedOutput = "LicenseRef-1"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", expectedOutput, output)
+ }
+}
+
+func Test_rdfParser2_3_getChecksumFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ // TestCase 1: invalid checksum algorithm
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha999"/>
+ </spdx:Checksum>
+ `)
+ checksumNode := parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getChecksumFromNode(checksumNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid checksum algorithm")
+ }
+
+ // TestCase 2: invalid predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+ <spdx:invalidPredicate />
+ </spdx:Checksum>
+ `)
+ checksumNode = parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getChecksumFromNode(checksumNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid predicate")
+ }
+
+ // TestCase 3: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+ </spdx:Checksum>
+ `)
+ checksumNode = parser.gordfParserObj.Triples[0].Subject
+ algorithm, value, err := parser.getChecksumFromNode(checksumNode)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if algorithm != "SHA1" {
+ t.Errorf("expected checksum algorithm to be sha1, found %s", algorithm)
+ }
+ expectedValue := "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
+ if value != expectedValue {
+ t.Errorf("expected checksumValue to be %s, found %s", expectedValue, value)
+ }
+}
+
+func Test_rdfParser2_3_getAlgorithmFromURI(t *testing.T) {
+ var algorithmURI string
+ var err error
+
+ // TestCase 1: checksumAlgorithm uri doesn't start with checksumAlgorithm_
+ algorithmURI = NS_SPDX + "sha1"
+ _, err = getAlgorithmFromURI(algorithmURI)
+ if err == nil {
+ t.Errorf("should've raised an error for algorithmURI that doesn't start with checksumAlgorithm_")
+ }
+
+ // TestCase 2: unknown checksum algorithm
+ algorithmURI = NS_SPDX + "checksumAlgorithm_sha999"
+ _, err = getAlgorithmFromURI(algorithmURI)
+ if err == nil {
+ t.Errorf("should've raised an error for invalid algorithm")
+ }
+
+ // TestCase 3: valid input
+ algorithmURI = NS_SPDX + "checksumAlgorithm_sha256"
+ algorithm, err := getAlgorithmFromURI(algorithmURI)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if algorithm != "SHA256" {
+ t.Errorf("expected: SHA256, found: %s", algorithm)
+ }
+}
+
+func Test_mapLicensesToStrings(t *testing.T) {
+ // nothing much to test here.
+ // just a dummy dry run.
+ licenses := []AnyLicenseInfo{
+ SpecialLicense{
+ value: NONE,
+ },
+ SpecialLicense{
+ value: NOASSERTION,
+ },
+ }
+ licenseStrings := mapLicensesToStrings(licenses)
+ expectedLicenseStrings := []string{"NONE", "NOASSERTION"}
+ if !reflect.DeepEqual(licenseStrings, expectedLicenseStrings) {
+ t.Errorf("expected: %+v\nfound %+v", expectedLicenseStrings, licenseStrings)
+ }
+}
+
+func TestConjunctiveLicenseSet_ToLicenseString(t *testing.T) {
+ var lic ConjunctiveLicenseSet
+ var output, expectedOutput string
+
+ // TestCase 1: no license in the set
+ lic = ConjunctiveLicenseSet{
+ members: nil,
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = ""
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 2: single license in the set
+ lic = ConjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 3: more than one license in the set.
+ lic = ConjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ SpecialLicense{value: NONE},
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION AND NONE"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 4: nested conjunctive license.
+ lic = ConjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ ConjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: "LicenseRef-1"},
+ SpecialLicense{value: NONE},
+ },
+ },
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION AND LicenseRef-1 AND NONE"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+}
+
+func TestDisjunctiveLicenseSet_ToLicenseString(t *testing.T) {
+ var lic DisjunctiveLicenseSet
+ var output, expectedOutput string
+
+ // TestCase 1: no license in the set
+ lic = DisjunctiveLicenseSet{
+ members: nil,
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = ""
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 2: single license in the set
+ lic = DisjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 3: more than one license in the set.
+ lic = DisjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ SpecialLicense{value: NONE},
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION OR NONE"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+
+ // TestCase 4: nested conjunctive license.
+ lic = DisjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: NOASSERTION},
+ DisjunctiveLicenseSet{
+ members: []AnyLicenseInfo{
+ SpecialLicense{value: "LicenseRef-1"},
+ SpecialLicense{value: NONE},
+ },
+ },
+ },
+ }
+ output = lic.ToLicenseString()
+ expectedOutput = "NOASSERTION OR LicenseRef-1 OR NONE"
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found %s", output, expectedOutput)
+ }
+}
+
+func TestExtractedLicensingInfo_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ extractedLicense := ExtractedLicensingInfo{
+ SimpleLicensingInfo: SimpleLicensingInfo{
+ licenseID: "license",
+ },
+ extractedText: "extracted Text",
+ }
+ expectedOutput := "license"
+ output := extractedLicense.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestOrLaterOperator_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ orLater := OrLaterOperator{
+ member: SimpleLicensingInfo{
+ licenseID: "license",
+ },
+ }
+ expectedOutput := "license"
+ output := orLater.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestLicense_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ license := License{
+ SimpleLicensingInfo: SimpleLicensingInfo{
+ licenseID: "license",
+ },
+ }
+ expectedOutput := "license"
+ output := license.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestListedLicense_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ ll := ListedLicense{License{
+ SimpleLicensingInfo: SimpleLicensingInfo{
+ licenseID: "license",
+ },
+ },
+ }
+ expectedOutput := "license"
+ output := ll.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestWithExceptionOperator_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ withException := WithExceptionOperator{
+ member: SimpleLicensingInfo{
+ licenseID: "license",
+ },
+ licenseException: LicenseException{},
+ }
+ expectedOutput := "license"
+ output := withException.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestSpecialLicense_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ specialLicense := SpecialLicense{
+ value: "license",
+ }
+ expectedOutput := "license"
+ output := specialLicense.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
+
+func TestSimpleLicensingInfo_ToLicenseString(t *testing.T) {
+ // nothing to test (just a dry run)
+ sli := SimpleLicensingInfo{
+ licenseID: "license",
+ }
+ expectedOutput := "license"
+ output := sli.ToLicenseString()
+ if output != expectedOutput {
+ t.Errorf("expected: %s, found: %s", expectedOutput, output)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_annotation.go b/rdfloader/parser2v3/parse_annotation.go
new file mode 100644
index 0000000..507158a
--- /dev/null
+++ b/rdfloader/parser2v3/parse_annotation.go
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "errors"
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// creates a new instance of annotation and sets the annotation attributes
+// associated with the given node.
+// The newly created annotation is appended to the doc.
+func (parser *rdfParser2_3) parseAnnotationFromNode(node *gordfParser.Node) (err error) {
+ ann := &v2_3.Annotation{}
+ for _, subTriple := range parser.nodeToTriples(node) {
+ switch subTriple.Predicate.ID {
+ case SPDX_ANNOTATOR:
+ // cardinality: exactly 1
+ err = setAnnotatorFromString(subTriple.Object.ID, ann)
+ case SPDX_ANNOTATION_DATE:
+ // cardinality: exactly 1
+ ann.AnnotationDate = subTriple.Object.ID
+ case RDFS_COMMENT:
+ // cardinality: exactly 1
+ ann.AnnotationComment = subTriple.Object.ID
+ case SPDX_ANNOTATION_TYPE:
+ // cardinality: exactly 1
+ err = setAnnotationType(subTriple.Object.ID, ann)
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ continue
+ default:
+ err = fmt.Errorf("unknown predicate %s while parsing annotation", subTriple.Predicate.ID)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return setAnnotationToParser(parser, ann)
+}
+
+func setAnnotationToParser(parser *rdfParser2_3, annotation *v2_3.Annotation) error {
+ if parser.doc == nil {
+ return errors.New("uninitialized spdx document")
+ }
+ if parser.doc.Annotations == nil {
+ parser.doc.Annotations = []*v2_3.Annotation{}
+ }
+ parser.doc.Annotations = append(parser.doc.Annotations, annotation)
+ return nil
+}
+
+// annotator is of type [Person|Organization|Tool]:String
+func setAnnotatorFromString(annotatorString string, ann *v2_3.Annotation) error {
+ subkey, subvalue, err := ExtractSubs(annotatorString, ":")
+ if err != nil {
+ return err
+ }
+ if subkey == "Person" || subkey == "Organization" || subkey == "Tool" {
+ ann.Annotator.AnnotatorType = subkey
+ ann.Annotator.Annotator = subvalue
+ return nil
+ }
+ return fmt.Errorf("unrecognized Annotator type %v while parsing annotation", subkey)
+}
+
+// it can be NS_SPDX+annotationType_[review|other]
+func setAnnotationType(annType string, ann *v2_3.Annotation) error {
+ switch annType {
+ case SPDX_ANNOTATION_TYPE_OTHER:
+ ann.AnnotationType = "OTHER"
+ case SPDX_ANNOTATION_TYPE_REVIEW:
+ ann.AnnotationType = "REVIEW"
+ default:
+ return fmt.Errorf("unknown annotation type %s", annType)
+ }
+ return nil
+}
diff --git a/rdfloader/parser2v3/parse_annotation_test.go b/rdfloader/parser2v3/parse_annotation_test.go
new file mode 100644
index 0000000..1ce0bd7
--- /dev/null
+++ b/rdfloader/parser2v3/parse_annotation_test.go
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func Test_setAnnotatorFromString(t *testing.T) {
+ // TestCase 1: Empty String must raise an error
+ ann := &v2_3.Annotation{}
+ input := ""
+ err := setAnnotatorFromString(input, ann)
+ if err == nil {
+ t.Error("should've raised an error for an empty string")
+ }
+
+ // TestCase 2: Invalid annotator type
+ ann = &v2_3.Annotation{}
+ input = "Company: some_company"
+ err = setAnnotatorFromString(input, ann)
+ if err == nil {
+ t.Errorf("should've raised an error for an unknown annotator type")
+ }
+
+ // TestCase 3: Valid annotator
+ ann = &v2_3.Annotation{}
+ input = "Person: Rishabh"
+ err = setAnnotatorFromString(input, ann)
+ if err != nil {
+ t.Errorf("unexpected error for a valid annotator")
+ }
+ if ann.Annotator.AnnotatorType != "Person" {
+ t.Errorf("wrnog annotator type: expected: %s, found: %s", "Person", ann.Annotator)
+ }
+ if ann.Annotator.Annotator != "Rishabh" {
+ t.Errorf("wrong annotator: expected: %s, found: %s", "Rishabh", ann.Annotator)
+ }
+}
+
+func Test_setAnnotationType(t *testing.T) {
+ ann := &v2_3.Annotation{}
+ // TestCase 1: invalid input (empty annotationType)
+ err := setAnnotationType("", ann)
+ if err == nil {
+ t.Errorf("expected an error for empty input")
+ }
+
+ // TestCase 2: invalid input (unknown annotation type)
+ err = setAnnotationType(NS_SPDX+"annotationType_unknown", ann)
+ if err == nil {
+ t.Errorf("expected an error for invalid annotationType")
+ }
+
+ // TestCase 3: valid input (annotationType_other)
+ err = setAnnotationType(SPDX_ANNOTATION_TYPE_OTHER, ann)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ann.AnnotationType != "OTHER" {
+ t.Errorf("expected: OTHER, found: %s", ann.AnnotationType)
+ }
+
+ // TestCase 4: valid input (annotationType_review)
+ err = setAnnotationType(SPDX_ANNOTATION_TYPE_REVIEW, ann)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ann.AnnotationType != "REVIEW" {
+ t.Errorf("expected: REVIEW, found: %s", ann.AnnotationType)
+ }
+}
+
+func Test_setAnnotationToParser(t *testing.T) {
+ // TestCase 1: doc is nil (must raise an error)
+ parser, _ := parserFromBodyContent(``)
+ parser.doc = nil
+ err := setAnnotationToParser(parser, &v2_3.Annotation{})
+ if err == nil {
+ t.Errorf("empty doc should've raised an error")
+ }
+
+ // TestCase 2: empty annotations should create a new annotations
+ // list and append the input to it.
+ parser, _ = parserFromBodyContent(``)
+ parser.doc.Annotations = nil
+ err = setAnnotationToParser(parser, &v2_3.Annotation{})
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if len(parser.doc.Annotations) != 1 {
+ t.Errorf("expected doc to have 1 annotation, found %d", len(parser.doc.Annotations))
+ }
+}
+
+func Test_rdfParser2_3_parseAnnotationFromNode(t *testing.T) {
+ // TestCase 1: invalid annotator must raise an error
+ parser, _ := parserFromBodyContent(`
+ <spdx:Annotation>
+ <spdx:annotationDate>2010-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>Document level annotation</rdfs:comment>
+ <spdx:annotator>Company: some company</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+ </spdx:Annotation>
+ `)
+ node := parser.gordfParserObj.Triples[0].Subject
+ err := parser.parseAnnotationFromNode(node)
+ if err == nil {
+ t.Errorf("wrong annotator type should've raised an error")
+ }
+
+ // TestCase 2: wrong annotation type should raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Annotation>
+ <spdx:annotationDate>2010-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>Document level annotation</rdfs:comment>
+ <spdx:annotator>Person: Jane Doe</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_unknown"/>
+ </spdx:Annotation>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseAnnotationFromNode(node)
+ if err == nil {
+ t.Errorf("wrong annotation type should've raised an error")
+ }
+
+ // TestCase 3: unknown predicate should also raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Annotation>
+ <spdx:annotationDate>2010-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>Document level annotation</rdfs:comment>
+ <spdx:annotator>Person: Jane Doe</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_unknown"/>
+ <spdx:unknownPredicate />
+ </spdx:Annotation>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseAnnotationFromNode(node)
+ if err == nil {
+ t.Errorf("unknown predicate must raise an error")
+ }
+
+ // TestCase 4: completely valid annotation
+ parser, _ = parserFromBodyContent(`
+ <spdx:Annotation>
+ <spdx:annotationDate>2010-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>Document level annotation</rdfs:comment>
+ <spdx:annotator>Person: Jane Doe</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+ </spdx:Annotation>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseAnnotationFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing valid a annotation")
+ }
+ if n := len(parser.doc.Annotations); n != 1 {
+ t.Errorf("document should've had only one annotation, found %d", n)
+ }
+ ann := parser.doc.Annotations[0]
+ // validating all the attributes of the annotations
+ expectedComment := "Document level annotation"
+ if ann.AnnotationComment != expectedComment {
+ t.Errorf(`expected: "%s", found "%s"`, expectedComment, ann.AnnotationComment)
+ }
+ expectedDate := "2010-01-29T18:30:22Z"
+ if expectedDate != ann.AnnotationDate {
+ t.Errorf(`expected: "%s", found "%s"`, expectedDate, ann.AnnotationDate)
+ }
+ expectedAnnotator := "Jane Doe"
+ if expectedAnnotator != ann.Annotator.Annotator {
+ t.Errorf(`expected: "%s", found "%s"`, expectedAnnotator, ann.Annotator)
+ }
+ if ann.Annotator.AnnotatorType != "Person" {
+ t.Errorf(`expected: "%s", found "%s"`, "Person", ann.Annotator.AnnotatorType)
+ }
+ expectedAnnotationType := "OTHER"
+ if expectedAnnotationType != ann.AnnotationType {
+ t.Errorf(`expected: "%s", found "%s"`, expectedAnnotationType, ann.AnnotationType)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_creation_info.go b/rdfloader/parser2v3/parse_creation_info.go
new file mode 100644
index 0000000..93a5628
--- /dev/null
+++ b/rdfloader/parser2v3/parse_creation_info.go
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// Cardinality: Mandatory, one.
+func (parser *rdfParser2_3) parseCreationInfoFromNode(ci *v2_3.CreationInfo, node *gordfParser.Node) error {
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case SPDX_LICENSE_LIST_VERSION: // 2.7
+ // cardinality: max 1
+ ci.LicenseListVersion = triple.Object.ID
+ case SPDX_CREATOR: // 2.8
+ // cardinality: min 1
+ err := setCreator(triple.Object.ID, ci)
+ if err != nil {
+ return err
+ }
+ case SPDX_CREATED: // 2.9
+ // cardinality: exactly 1
+ ci.Created = triple.Object.ID
+ case RDFS_COMMENT: // 2.10
+ ci.CreatorComment = triple.Object.ID
+ case RDF_TYPE:
+ continue
+ default:
+ return fmt.Errorf("unknown predicate %v while parsing a creation info", triple.Predicate)
+ }
+ }
+ return nil
+}
+
+func setCreator(creatorStr string, ci *v2_3.CreationInfo) error {
+ entityType, entity, err := ExtractSubs(creatorStr, ":")
+ if err != nil {
+ return fmt.Errorf("error setting creator of a creation info: %s", err)
+ }
+
+ creator := common.Creator{Creator: entity}
+
+ switch entityType {
+ case "Person", "Organization", "Tool":
+ creator.CreatorType = entityType
+ default:
+ return fmt.Errorf("unknown creatorType %v in a creation info", entityType)
+ }
+
+ ci.Creators = append(ci.Creators, creator)
+
+ return nil
+}
diff --git a/rdfloader/parser2v3/parse_creation_info_test.go b/rdfloader/parser2v3/parse_creation_info_test.go
new file mode 100644
index 0000000..354ac73
--- /dev/null
+++ b/rdfloader/parser2v3/parse_creation_info_test.go
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func Test_setCreator(t *testing.T) {
+ // TestCase 1: invalid creator (empty)
+ input := ""
+ err := setCreator(input, &v2_3.CreationInfo{})
+ if err == nil {
+ t.Errorf("shoud've raised an error due to invalid input")
+ }
+
+ // TestCase 2: invalid entity type
+ input = "Company: some company"
+ err = setCreator(input, &v2_3.CreationInfo{})
+ if err == nil {
+ t.Errorf("shoud've raised an error due to unknown entity type")
+ }
+
+ // TestCase 3: valid input
+ input = "Person: Jane Doe"
+ ci := &v2_3.CreationInfo{}
+ err = setCreator(input, ci)
+ if err != nil {
+ t.Errorf("error parsing a valid input: %v", err)
+ }
+ if len(ci.Creators) != 1 {
+ t.Errorf("creationInfo should've had 1 creatorPersons, found %d", len(ci.Creators))
+ }
+ expectedPerson := "Jane Doe"
+ if ci.Creators[0].Creator != expectedPerson {
+ t.Errorf("expected %s, found %s", expectedPerson, ci.Creators[0])
+ }
+}
+
+func Test_rdfParser2_3_parseCreationInfoFromNode(t *testing.T) {
+ // TestCase 1: invalid creator must raise an error
+ parser, _ := parserFromBodyContent(`
+ <spdx:CreationInfo>
+ <spdx:licenseListVersion>2.6</spdx:licenseListVersion>
+ <spdx:creator>Person Unknown</spdx:creator>
+ <spdx:created>2018-08-24T19:55:34Z</spdx:created>
+ </spdx:CreationInfo>
+ `)
+ ciNode := parser.gordfParserObj.Triples[0].Subject
+ err := parser.parseCreationInfoFromNode(&v2_3.CreationInfo{}, ciNode)
+ if err == nil {
+ t.Errorf("invalid creator must raise an error")
+ }
+
+ // TestCase 2: unknown predicate must also raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:CreationInfo>
+ <spdx:licenseListVersion>2.6</spdx:licenseListVersion>
+ <spdx:creator>Person: fossy (y)</spdx:creator>
+ <spdx:creator>Organization: </spdx:creator>
+ <spdx:creator>Tool: spdx2</spdx:creator>
+ <spdx:created>2018-08-24T19:55:34Z</spdx:created>
+ <spdx:unknownPredicate />
+ </spdx:CreationInfo>
+ `)
+ ciNode = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseCreationInfoFromNode(&v2_3.CreationInfo{}, ciNode)
+ if err == nil {
+ t.Errorf("unknown predicate must raise an error")
+ }
+
+ // TestCase 2: unknown predicate must also raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:CreationInfo>
+ <spdx:licenseListVersion>2.6</spdx:licenseListVersion>
+ <spdx:creator>Person: fossy</spdx:creator>
+ <spdx:created>2018-08-24T19:55:34Z</spdx:created>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:CreationInfo>
+ `)
+ ciNode = parser.gordfParserObj.Triples[0].Subject
+ ci := &v2_3.CreationInfo{}
+ err = parser.parseCreationInfoFromNode(ci, ciNode)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ci.LicenseListVersion != "2.6" {
+ t.Errorf(`expected %s, found %s`, "2.6", ci.LicenseListVersion)
+ }
+ n := len(ci.Creators)
+ if n != 1 {
+ t.Errorf("expected 1 creatorPersons, found %d", n)
+ }
+ if ci.Creators[0].Creator != "fossy" {
+ t.Errorf("expected %s, found %s", "fossy", ci.Creators[0].Creator)
+ }
+ expectedCreated := "2018-08-24T19:55:34Z"
+ if ci.Created != expectedCreated {
+ t.Errorf("expected %s, found %s", expectedCreated, ci.Created)
+ }
+ expectedComment := "comment"
+ if ci.CreatorComment != expectedComment {
+ t.Errorf("expected %s, found %s", expectedComment, ci.CreatorComment)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_file.go b/rdfloader/parser2v3/parse_file.go
new file mode 100644
index 0000000..a0bc6fd
--- /dev/null
+++ b/rdfloader/parser2v3/parse_file.go
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// returns a file instance and the error if any encountered.
+func (parser *rdfParser2_3) getFileFromNode(fileNode *gordfParser.Node) (file *v2_3.File, err error) {
+ file = &v2_3.File{}
+
+ currState := parser.cache[fileNode.ID]
+ if currState == nil {
+ // this is the first time we are seeing this node.
+ parser.cache[fileNode.ID] = &nodeState{
+ object: file,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this file node and we needn't parse it again.
+ return currState.object.(*v2_3.File), nil
+ }
+
+ // setting color to grey to indicate that we've started parsing this node.
+ parser.cache[fileNode.ID].Color = GREY
+
+ // setting color to black just before function returns to the caller to
+ // indicate that parsing current node is complete.
+ defer func() { parser.cache[fileNode.ID].Color = BLACK }()
+
+ err = setFileIdentifier(fileNode.ID, file) // 4.2
+ if err != nil {
+ return nil, err
+ }
+
+ if existingFile := parser.files[file.FileSPDXIdentifier]; existingFile != nil {
+ file = existingFile
+ }
+
+ for _, subTriple := range parser.nodeToTriples(fileNode) {
+ switch subTriple.Predicate.ID {
+ case SPDX_FILE_NAME: // 4.1
+ // cardinality: exactly 1
+ file.FileName = subTriple.Object.ID
+ case SPDX_NAME:
+ // cardinality: exactly 1
+ // TODO: check where it will be set in the golang-tools spdx-data-model
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ case SPDX_FILE_TYPE: // 4.3
+ // cardinality: min 0
+ fileType := ""
+ fileType, err = parser.getFileTypeFromUri(subTriple.Object.ID)
+ file.FileTypes = append(file.FileTypes, fileType)
+ case SPDX_CHECKSUM: // 4.4
+ // cardinality: min 1
+ err = parser.setFileChecksumFromNode(file, subTriple.Object)
+ case SPDX_LICENSE_CONCLUDED: // 4.5
+ // cardinality: (exactly 1 anyLicenseInfo) or (None) or (Noassertion)
+ anyLicense, err := parser.getAnyLicenseFromNode(subTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing licenseConcluded: %v", err)
+ }
+ file.LicenseConcluded = anyLicense.ToLicenseString()
+ case SPDX_LICENSE_INFO_IN_FILE: // 4.6
+ // cardinality: min 1
+ lic, err := parser.getAnyLicenseFromNode(subTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing licenseInfoInFile: %v", err)
+ }
+ file.LicenseInfoInFiles = append(file.LicenseInfoInFiles, lic.ToLicenseString())
+ case SPDX_LICENSE_COMMENTS: // 4.7
+ // cardinality: max 1
+ file.LicenseComments = subTriple.Object.ID
+ // TODO: allow copyright text to be of type NOASSERTION
+ case SPDX_COPYRIGHT_TEXT: // 4.8
+ // cardinality: exactly 1
+ file.FileCopyrightText = subTriple.Object.ID
+ case SPDX_LICENSE_INFO_FROM_FILES:
+ // TODO: implement it. It is not defined in the tools-golang model.
+ // deprecated artifactOf (see sections 4.9, 4.10, 4.11)
+ case SPDX_ARTIFACT_OF:
+ // cardinality: min 0
+ var artifactOf *v2_3.ArtifactOfProject
+ artifactOf, err = parser.getArtifactFromNode(subTriple.Object)
+ file.ArtifactOfProjects = append(file.ArtifactOfProjects, artifactOf)
+ case RDFS_COMMENT: // 4.12
+ // cardinality: max 1
+ file.FileComment = subTriple.Object.ID
+ case SPDX_NOTICE_TEXT: // 4.13
+ // cardinality: max 1
+ file.FileNotice = getNoticeTextFromNode(subTriple.Object)
+ case SPDX_FILE_CONTRIBUTOR: // 4.14
+ // cardinality: min 0
+ file.FileContributors = append(file.FileContributors, subTriple.Object.ID)
+ case SPDX_FILE_DEPENDENCY:
+ // cardinality: min 0
+ newFile, err := parser.getFileFromNode(subTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error setting a file dependency in a file: %v", err)
+ }
+ file.FileDependencies = append(file.FileDependencies, string(newFile.FileSPDXIdentifier))
+ case SPDX_ATTRIBUTION_TEXT:
+ // cardinality: min 0
+ file.FileAttributionTexts = append(file.FileAttributionTexts, subTriple.Object.ID)
+ case SPDX_ANNOTATION:
+ // cardinality: min 0
+ err = parser.parseAnnotationFromNode(subTriple.Object)
+ case SPDX_RELATIONSHIP:
+ // cardinality: min 0
+ err = parser.parseRelationship(subTriple)
+ default:
+ return nil, fmt.Errorf("unknown triple predicate id %s", subTriple.Predicate.ID)
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ parser.files[file.FileSPDXIdentifier] = file
+ return file, nil
+}
+
+func (parser *rdfParser2_3) setFileChecksumFromNode(file *v2_3.File, checksumNode *gordfParser.Node) error {
+ checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(checksumNode)
+ if err != nil {
+ return fmt.Errorf("error parsing checksumNode of a file: %v", err)
+ }
+ if file.Checksums == nil {
+ file.Checksums = []common.Checksum{}
+ }
+ switch checksumAlgorithm {
+ case common.MD5, common.SHA1, common.SHA256:
+ file.Checksums = append(file.Checksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue})
+ case "":
+ return fmt.Errorf("empty checksum algorithm and value")
+ default:
+ return fmt.Errorf("unknown checksumAlgorithm %s for a file", checksumAlgorithm)
+ }
+ return nil
+}
+
+func (parser *rdfParser2_3) getArtifactFromNode(node *gordfParser.Node) (*v2_3.ArtifactOfProject, error) {
+ artifactOf := &v2_3.ArtifactOfProject{}
+ // setting artifactOfProjectURI attribute (which is optional)
+ if node.NodeType == gordfParser.IRI {
+ artifactOf.URI = node.ID
+ }
+ // parsing rest triples and attributes of the artifact.
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ case DOAP_HOMEPAGE:
+ artifactOf.HomePage = triple.Object.ID
+ case DOAP_NAME:
+ artifactOf.Name = triple.Object.ID
+ default:
+ return nil, fmt.Errorf("error parsing artifactOf predicate %s", triple.Predicate.ID)
+ }
+ }
+ return artifactOf, nil
+}
+
+// TODO: check if the filetype is valid.
+func (parser *rdfParser2_3) getFileTypeFromUri(uri string) (string, error) {
+ // fileType is given as a uri. for example: http://spdx.org/rdf/terms#fileType_text
+ lastPart := getLastPartOfURI(uri)
+ if !strings.HasPrefix(lastPart, "fileType_") {
+ return "", fmt.Errorf("fileType Uri must begin with fileTYpe_. found: %s", lastPart)
+ }
+ return strings.TrimPrefix(lastPart, "fileType_"), nil
+}
+
+// populates parser.doc.Files by a list of files which are not
+// associated with a package by the hasFile attribute
+// assumes: all the packages are already parsed.
+func (parser *rdfParser2_3) setUnpackagedFiles() {
+ for fileID := range parser.files {
+ if !parser.assocWithPackage[fileID] {
+ parser.doc.Files = append(parser.doc.Files, parser.files[fileID])
+ }
+ }
+}
+
+func setFileIdentifier(idURI string, file *v2_3.File) (err error) {
+ idURI = strings.TrimSpace(idURI)
+ uriFragment := getLastPartOfURI(idURI)
+ file.FileSPDXIdentifier, err = ExtractElementID(uriFragment)
+ if err != nil {
+ return fmt.Errorf("error setting file identifier: %s", err)
+ }
+ return nil
+}
+
+func getNoticeTextFromNode(node *gordfParser.Node) string {
+ switch node.ID {
+ case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL:
+ return "NOASSERTION"
+ default:
+ return node.ID
+ }
+}
diff --git a/rdfloader/parser2v3/parse_file_test.go b/rdfloader/parser2v3/parse_file_test.go
new file mode 100644
index 0000000..3ea79bb
--- /dev/null
+++ b/rdfloader/parser2v3/parse_file_test.go
@@ -0,0 +1,765 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "bufio"
+ "strings"
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ rdfloader2 "github.com/spdx/gordf/rdfloader/xmlreader"
+ gordfWriter "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// content is the tags within the rdf:RDF tag
+// pads the content with the enclosing rdf:RDF tag
+func wrapIntoTemplate(content string) string {
+ header := `<rdf:RDF
+ xmlns:spdx="http://spdx.org/rdf/terms#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#"
+ xmlns:doap="http://usefulinc.com/ns/doap#"
+ xmlns:j.0="http://www.w3.org/2009/pointers#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">`
+ footer := `</rdf:RDF>`
+ return header + content + footer
+}
+
+func parserFromBodyContent(content string) (*rdfParser2_3, error) {
+ rdfContent := wrapIntoTemplate(content)
+ xmlreader := rdfloader2.XMLReaderFromFileObject(bufio.NewReader(strings.NewReader(rdfContent)))
+ rootBlock, err := xmlreader.Read()
+ if err != nil {
+ return nil, err
+ }
+ parser := gordfParser.New()
+ err = parser.Parse(rootBlock)
+ if err != nil {
+ return nil, err
+ }
+ nodeToTriples := gordfWriter.GetNodeToTriples(parser.Triples)
+ rdfParser := NewParser2_3(parser, nodeToTriples)
+ return rdfParser, err
+}
+
+func Test_rdfParser2_3_getArtifactFromNode(t *testing.T) {
+ // TestCase 1: artifactOf without project URI
+ rdfParser, err := parserFromBodyContent(
+ `<spdx:File>
+ <spdx:artifactOf>
+ <doap:Project>
+ <doap:homepage>http://www.openjena.org/</doap:homepage>
+ <doap:name>Jena</doap:name>
+ </doap:Project>
+ </spdx:artifactOf>
+ </spdx:File>`)
+ if err != nil {
+ t.Errorf("unexpected error while parsing a valid example: %v", err)
+ }
+ artifactOfNode := gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object
+ artifact, err := rdfParser.getArtifactFromNode(artifactOfNode)
+ if err != nil {
+ t.Errorf("error parsing a valid artifactOf node: %v", err)
+ }
+ if artifact.Name != "Jena" {
+ t.Errorf("expected name of artifact: %s, found: %s", "Jena", artifact.Name)
+ }
+ expectedHomePage := "http://www.openjena.org/"
+ if artifact.HomePage != expectedHomePage {
+ t.Errorf("wrong artifact homepage. Expected: %s, found: %s", expectedHomePage, artifact.HomePage)
+ }
+ if artifact.URI != "" {
+ t.Errorf("wrong artifact URI. Expected: %s, found: %s", "", artifact.URI)
+ }
+
+ // TestCase 2: artifactOf with a Project URI
+ rdfParser, err = parserFromBodyContent(
+ `<spdx:File>
+ <spdx:artifactOf>
+ <doap:Project rdf:about="http://subversion.apache.org/doap.rdf">
+ <doap:homepage>http://www.openjena.org/</doap:homepage>
+ <doap:name>Jena</doap:name>
+ </doap:Project>
+ </spdx:artifactOf>
+ </spdx:File>`)
+ if err != nil {
+ t.Errorf("unexpected error while parsing a valid example: %v", err)
+ }
+ artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object
+ artifact, err = rdfParser.getArtifactFromNode(artifactOfNode)
+ if err != nil {
+ t.Errorf("error parsing a valid artifactOf node: %v", err)
+ }
+ expectedURI := "http://subversion.apache.org/doap.rdf"
+ if artifact.URI != expectedURI {
+ t.Errorf("wrong artifact URI. Expected: %s, found: %s", expectedURI, artifact.URI)
+ }
+
+ // TestCase 3: artifactOf with unknown predicate
+ rdfParser, err = parserFromBodyContent(
+ `<spdx:File>
+ <spdx:artifactOf>
+ <doap:Project rdf:about="http://subversion.apache.org/doap.rdf">
+ <doap:homepage>http://www.openjena.org/</doap:homepage>
+ <doap:name>Jena</doap:name>
+ <doap:invalidTag rdf:ID="invalid"/>
+ </doap:Project>
+ </spdx:artifactOf>
+ </spdx:File>`)
+ if err != nil {
+ t.Errorf("unexpected error while parsing a valid example: %v", err)
+ }
+ artifactOfNode = gordfWriter.FilterTriples(rdfParser.gordfParserObj.Triples, nil, &SPDX_ARTIFACT_OF, nil)[0].Object
+ _, err = rdfParser.getArtifactFromNode(artifactOfNode)
+ if err == nil {
+ t.Errorf("must've raised an error for an invalid predicate")
+ }
+}
+
+func Test_rdfParser2_3_getFileTypeFromUri(t *testing.T) {
+ rdfParser, _ := parserFromBodyContent(``)
+
+ // TestCase 1: Valid fileType URI:
+ fileTypeURI := "http://spdx.org/rdf/terms#fileType_source"
+ fileType, err := rdfParser.getFileTypeFromUri(fileTypeURI)
+ if err != nil {
+ t.Errorf("error in a valid example: %v", err)
+ }
+ if fileType != "source" {
+ t.Errorf("wrong fileType. expected: %s, found: %s", "source", fileType)
+ }
+
+ // TestCase 2: Invalid fileType URI format.
+ fileTypeURI = "http://spdx.org/rdf/terms#source"
+ fileType, err = rdfParser.getFileTypeFromUri(fileTypeURI)
+ if err == nil {
+ t.Error("should've raised an error for invalid fileType")
+ }
+}
+
+func Test_rdfParser2_3_setUnpackagedFiles(t *testing.T) {
+ // unpackaged files are the files which are not associated with any package
+ // file associated with a package sets parser.assocWithPackage[fileID] to true.
+ rdfParser, _ := parserFromBodyContent(``)
+ file1 := &v2_3.File{FileSPDXIdentifier: common.ElementID("file1")}
+ file2 := &v2_3.File{FileSPDXIdentifier: common.ElementID("file2")}
+ file3 := &v2_3.File{FileSPDXIdentifier: common.ElementID("file3")}
+
+ // setting files to the document as if it were to be set when it was parsed using triples.
+ rdfParser.files[file1.FileSPDXIdentifier] = file1
+ rdfParser.files[file2.FileSPDXIdentifier] = file2
+ rdfParser.files[file3.FileSPDXIdentifier] = file3
+
+ // assuming file1 is associated with a package
+ rdfParser.assocWithPackage[file1.FileSPDXIdentifier] = true
+
+ rdfParser.setUnpackagedFiles()
+
+ // after setting unpackaged files, parser.doc.Files must've file2 and file3
+ if n := len(rdfParser.doc.Files); n != 2 {
+ t.Errorf("unpackage files should've had 2 files, found %d files", n)
+ }
+
+ // checking if the unpackagedFiles contain only file2 & file3.
+ for _, file := range rdfParser.doc.Files {
+ switch string(file.FileSPDXIdentifier) {
+ case "file2", "file3":
+ continue
+ default:
+ t.Errorf("unexpected file with id %s found in unpackaged files", file.FileSPDXIdentifier)
+ }
+ }
+}
+
+func Test_setFileIdentifier(t *testing.T) {
+ file := &v2_3.File{}
+
+ // TestCase 1: valid example
+ err := setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#SPDXRef-129", file)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if file.FileSPDXIdentifier != "129" {
+ t.Errorf("expected %s, found: %s", "129", file.FileSPDXIdentifier)
+ }
+
+ // TestCase 2: invalid example
+ err = setFileIdentifier("http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#129", file)
+ if err == nil {
+ t.Errorf("should've raised an error for an invalid example")
+ }
+}
+
+func Test_rdfParser2_3_setFileChecksumFromNode(t *testing.T) {
+ // TestCase 1: md5 checksum
+ parser, _ := parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md5" />
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file := &v2_3.File{}
+ err := parser.setFileChecksumFromNode(file, checksumNode)
+ if err != nil {
+ t.Errorf("error parsing a valid checksum node")
+ }
+ checksumValue := "d2356e0fe1c0b85285d83c6b2ad51b5f"
+ for _, checksum := range file.Checksums {
+ switch checksum.Algorithm {
+ case common.SHA1:
+ if checksum.Value != "" {
+ t.Errorf("incorrectly set sha1, should've been empty")
+ }
+ case common.SHA256:
+ if checksum.Value != "" {
+ t.Errorf("incorrectly set sha256, should've been empty")
+ }
+ case common.MD5:
+ if checksum.Value != checksumValue {
+ t.Errorf("wrong checksum value for md5. Expected: %s, found: %s", checksumValue, checksum.Value)
+ }
+ }
+ }
+
+ // TestCase 2: valid sha1 checksum
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1" />
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file = &v2_3.File{}
+ err = parser.setFileChecksumFromNode(file, checksumNode)
+ if err != nil {
+ t.Errorf("error parsing a valid checksum node")
+ }
+ for _, checksum := range file.Checksums {
+ switch checksum.Algorithm {
+ case common.SHA1:
+ if checksum.Value != checksumValue {
+ t.Errorf("wrong checksum value for sha1. Expected: %s, found: %s", checksumValue, checksum.Value)
+ }
+ case common.SHA256:
+ if checksum.Value != "" {
+ t.Errorf("incorrectly set sha256, should've been empty")
+ }
+ case common.MD5:
+ if checksum.Value != checksumValue {
+ t.Errorf("incorrectly set md5, should've been empty")
+ }
+ }
+ }
+
+ // TestCase 3: valid sha256 checksum
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha256" />
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file = &v2_3.File{}
+ err = parser.setFileChecksumFromNode(file, checksumNode)
+ if err != nil {
+ t.Errorf("error parsing a valid checksum node")
+ }
+ for _, checksum := range file.Checksums {
+ switch checksum.Algorithm {
+ case common.SHA1:
+ if checksum.Value != checksumValue {
+ t.Errorf("incorrectly set sha1, should've been empty")
+ }
+ case common.SHA256:
+ if checksum.Value != checksumValue {
+ t.Errorf("wrong checksum value for sha256. Expected: %s, found: %s", checksumValue, checksum.Value)
+ }
+ case common.MD5:
+ if checksum.Value != checksumValue {
+ t.Errorf("incorrectly set md5, should've been empty")
+ }
+ }
+ }
+
+ // TestCase 4: checksum node without one of the mandatory attributes
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file = &v2_3.File{}
+ err = parser.setFileChecksumFromNode(file, checksumNode)
+ if err == nil {
+ t.Errorf("should've raised an error parsing an invalid checksum node")
+ }
+
+ // TestCase 5: invalid checksum algorithm
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md43" />
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file = &v2_3.File{}
+ err = parser.setFileChecksumFromNode(file, checksumNode)
+ if err == nil {
+ t.Errorf("should've raised an error parsing an invalid checksum node")
+ }
+
+ // TestCase 6: valid checksum algorithm which is invalid for file (like md4, md6, sha384, etc.)
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha384" />
+ <spdx:checksumValue>d2356e0fe1c0b85285d83c6b2ad51b5f</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ checksumNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_CHECKSUM_CAPITALIZED)[0].Subject
+ file = &v2_3.File{}
+ err = parser.setFileChecksumFromNode(file, checksumNode)
+ if err == nil {
+ t.Errorf("should've raised an error parsing an invalid checksum algorithm for a file")
+ }
+}
+
+func Test_rdfParser2_3_getFileFromNode(t *testing.T) {
+ // TestCase 1: file with invalid id
+ parser, _ := parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gzspdx.rdf#item177"/>
+ `)
+ fileNode := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err := parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid file ID")
+ }
+
+ // TestCase 2: invalid fileType
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#source"/>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid fileType")
+ }
+
+ // TestCase 3: invalid file checksum
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha384" />
+ <spdx:checksumValue>0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd</spdx:checksumValue>
+ </spdx:Checksum>
+ </spdx:checksum>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid checksum")
+ }
+
+ // TestCase 4: invalid license concluded
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/rdf/terms#invalid_license" />
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid license Concluded")
+ }
+
+ // TestCase 5: invalid artifactOf attribute
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:artifactOf>
+ <doap:Project>
+ <doap:unknown_tag />
+ <doap:name>Jena</doap:name>
+ </doap:Project>
+ </spdx:artifactOf>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid artifactOf predicate")
+ }
+
+ // TestCase 6: invalid file dependency
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:fileDependency rdf:resource="http://spdx.org/spdxdocs/spdx-example#CommonsLangSrc"/>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid fileDependency")
+ }
+
+ // TestCase 7: invalid annotation with unknown predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:unknownAttribute />
+ </spdx:Annotation>
+ </spdx:annotation>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid annotation predicate")
+ }
+
+ // TestCase 8: invalid relationship
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#dynamicLink"/>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Errorf("should've raised an error stating invalid relationship Type")
+ }
+
+ // TestCase 8: unknown predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:unknown />
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Error("should've raised an error stating invalid predicate for a file")
+ }
+
+ // TestCase 9: invalid licenseInfoInFile.
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:licenseInfoInFile rdf:resource="http://spdx.org/licenses/DC0-1.0" />
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ _, err = parser.getFileFromNode(fileNode)
+ if err == nil {
+ t.Error("should've raised an error stating invalid licenseInfoInFile for a file")
+ }
+
+ // TestCase 10: Splitting of File definition into parents of different tags mustn't create new file objects.
+ fileDefinitions := []string{
+ `<spdx:Package rdf:about="#SPDXRef-Package1">
+ <spdx:hasFile>
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:fileName>time-1.9/ChangeLog</spdx:fileName>
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+ </spdx:File>
+ </spdx:hasFile>
+ </spdx:Package>`,
+ `<spdx:Package rdf:about="#SPDXRef-Package2">
+ <spdx:hasFile>
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:licenseInfoInFile rdf:resource="http://spdx.org/rdf/terms#NOASSERTION" />
+ </spdx:File>
+ </spdx:hasFile>
+ </spdx:Package>`,
+ }
+ parser, _ = parserFromBodyContent(strings.Join(fileDefinitions, ""))
+
+ var file *v2_3.File
+ packageTypeTriples := gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)
+ for _, typeTriple := range packageTypeTriples {
+ pkg, err := parser.getPackageFromNode(typeTriple.Subject)
+ if err != nil {
+ t.Errorf("unexpected error parsing a valid package: %v", err)
+ }
+ if n := len(pkg.Files); n != 1 {
+ t.Errorf("expected package to contain exactly 1 file. Found %d files", n)
+ }
+ for _, file = range pkg.Files {
+ }
+ }
+
+ // checking if all the attributes that spanned over a several tags are set in the same variable.
+ expectedFileName := "time-1.9/ChangeLog"
+ if file.FileName != expectedFileName {
+ t.Errorf("expected %s, found %s", expectedFileName, file.FileName)
+ }
+ expectedLicenseConcluded := "NOASSERTION"
+ if file.LicenseConcluded != expectedLicenseConcluded {
+ t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded)
+ }
+ expectedFileType := "source"
+ if file.FileTypes[0] != expectedFileType {
+ t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes)
+ }
+ expectedLicenseInfoInFile := "NOASSERTION"
+ if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile {
+ t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0])
+ }
+
+ // TestCase 12: checking if recursive dependencies are resolved.
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="#SPDXRef-ParentFile">
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+ <spdx:fileDependency>
+ <spdx:File rdf:about="#SPDXRef-ChildFile">
+ <spdx:fileDependency>
+ <spdx:File rdf:about="#SPDXRef-ParentFile">
+ <spdx:fileName>ParentFile</spdx:fileName>
+ </spdx:File>
+ </spdx:fileDependency>
+ </spdx:File>
+ </spdx:fileDependency>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ file, err = parser.getFileFromNode(fileNode)
+
+ // TestCase 11: all valid attribute and it's values.
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
+ <spdx:fileName>time-1.9/ChangeLog</spdx:fileName>
+ <spdx:name/>
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1" />
+ <spdx:checksumValue>0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd</spdx:checksumValue>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:licenseInfoInFile rdf:resource="http://spdx.org/rdf/terms#NOASSERTION" />
+ <spdx:licenseComments>no comments</spdx:licenseComments>
+ <spdx:copyrightText>from spdx file</spdx:copyrightText>
+ <spdx:artifactOf>
+ <doap:Project>
+ <doap:homepage>http://www.openjena.org/</doap:homepage>
+ <doap:name>Jena</doap:name>
+ </doap:Project>
+ </spdx:artifactOf>
+ <rdfs:comment>no comments</rdfs:comment>
+ <spdx:noticeText rdf:resource="http://spdx.org/rdf/terms#noassertion"/>
+ <spdx:fileContributor>Some Organization</spdx:fileContributor>
+ <spdx:fileDependency rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-CommonsLangSrc"/>
+ <spdx:attributionText>attribution text</spdx:attributionText>
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:annotationDate>2011-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>File level annotation copied from a spdx document</rdfs:comment>
+ <spdx:annotator>Person: File Commenter</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+ </spdx:Annotation>
+ </spdx:annotation>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_contains"/>
+ <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package"/>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ file, err = parser.getFileFromNode(fileNode)
+ if err != nil {
+ t.Errorf("unexpected error parsing a valid file: %v", err)
+ }
+
+ // checking each and every attribute of the obtained file.
+
+ expectedFileName = "time-1.9/ChangeLog"
+ if file.FileName != expectedFileName {
+ t.Errorf("expected %s, found %s", expectedFileName, file.FileName)
+ }
+
+ if len(file.FileTypes) != 1 {
+ t.Errorf("given file should have 1 fileType attribute. found %d", len(file.FileTypes))
+ }
+ expectedFileType = "source"
+ if file.FileTypes[0] != expectedFileType {
+ t.Errorf("expected %s, found %s", expectedFileType, file.FileTypes)
+ }
+
+ expectedChecksum := "0a3a0e1ab72b7c132f5021c538a7a3ea6d539bcd"
+
+ for _, checksum := range file.Checksums {
+ switch checksum.Algorithm {
+ case common.SHA1:
+ if checksum.Value != expectedChecksum {
+ t.Errorf("expected %s, found %s", expectedChecksum, checksum.Value)
+ }
+ }
+ }
+
+ expectedLicenseConcluded = "NOASSERTION"
+ if file.LicenseConcluded != expectedLicenseConcluded {
+ t.Errorf("expected %s, found %s", expectedLicenseConcluded, file.LicenseConcluded)
+ }
+
+ if len(file.LicenseInfoInFiles) != 1 {
+ t.Errorf("given file should have 1 licenseInfoInFile attribute. found %d", len(file.LicenseInfoInFiles))
+ }
+ expectedLicenseInfoInFile = "NOASSERTION"
+ if file.LicenseInfoInFiles[0] != expectedLicenseInfoInFile {
+ t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFiles[0])
+ }
+
+ expectedLicenseComments := "no comments"
+ if file.LicenseComments != expectedLicenseComments {
+ t.Errorf("expected %s, found %s", expectedLicenseComments, file.LicenseComments)
+ }
+
+ expectedCopyrightText := "from spdx file"
+ if file.FileCopyrightText != expectedCopyrightText {
+ t.Errorf("expected %s, found %s", expectedCopyrightText, file.FileCopyrightText)
+ }
+
+ if n := len(file.ArtifactOfProjects); n != 1 {
+ t.Errorf("given file should have 1 artifactOfProjects attribute. found %d", n)
+ }
+ artifactOf := file.ArtifactOfProjects[0]
+ expectedHomePage := "http://www.openjena.org/"
+ if artifactOf.HomePage != expectedHomePage {
+ t.Errorf("expected %s, found %s", expectedHomePage, artifactOf.HomePage)
+ }
+ if artifactOf.Name != "Jena" {
+ t.Errorf("expected %s, found %s", "Jena", artifactOf.Name)
+ }
+ if artifactOf.URI != "" {
+ t.Errorf("expected artifactOf uri to be empty, found %s", artifactOf.URI)
+ }
+
+ expectedFileComment := "no comments"
+ if file.FileComment != expectedFileComment {
+ t.Errorf("expected %s, found %s", expectedFileName, file.FileComment)
+ }
+
+ expectedNoticeText := "NOASSERTION"
+ if file.FileNotice != expectedNoticeText {
+ t.Errorf("expected %s, found %s", expectedNoticeText, file.FileNotice)
+ }
+
+ if n := len(file.FileContributors); n != 1 {
+ t.Errorf("given file should have 1 fileContributor. found %d", n)
+ }
+ expectedFileContributor := "Some Organization"
+ if file.FileContributors[0] != expectedFileContributor {
+ t.Errorf("expected %s, found %s", expectedFileContributor, file.FileContributors)
+ }
+
+ if n := len(file.FileDependencies); n != 1 {
+ t.Errorf("given file should have 1 fileDependencies. found %d", n)
+ }
+ expectedFileDependency := "CommonsLangSrc"
+ if file.FileDependencies[0] != expectedFileDependency {
+ t.Errorf("expected %s, found %s", expectedFileDependency, file.FileDependencies[0])
+ }
+
+ if n := len(file.FileAttributionTexts); n != 1 {
+ t.Errorf("given file should have 1 attributionText. found %d", n)
+ }
+ expectedAttributionText := "attribution text"
+ if file.FileAttributionTexts[0] != expectedAttributionText {
+ t.Errorf("expected %s, found %s", expectedAttributionText, file.FileAttributionTexts[0])
+ }
+
+ if n := len(parser.doc.Annotations); n != 1 {
+ t.Errorf("doc should've had 1 annotation. found %d", n)
+ }
+ ann := parser.doc.Annotations[0]
+ expectedAnnDate := "2011-01-29T18:30:22Z"
+ if ann.AnnotationDate != expectedAnnDate {
+ t.Errorf("expected %s, found %s", expectedAnnDate, ann.AnnotationDate)
+ }
+ expectedAnnComment := "File level annotation copied from a spdx document"
+ if ann.AnnotationComment != expectedAnnComment {
+ t.Errorf("expected %s, found %s", expectedAnnComment, ann.AnnotationComment)
+ }
+ expectedAnnotationType := "OTHER"
+ if ann.AnnotationType != expectedAnnotationType {
+ t.Errorf("expected %s, found %s", expectedAnnotationType, ann.AnnotationType)
+ }
+ expectedAnnotator := "File Commenter"
+ if ann.Annotator.Annotator != expectedAnnotator {
+ t.Errorf("expected %s, found %s", expectedAnnotator, ann.Annotator)
+ }
+ expectedAnnotatorType := "Person"
+ if ann.AnnotationType != expectedAnnotationType {
+ t.Errorf("expected %s, found %s", expectedAnnotatorType, ann.Annotator.AnnotatorType)
+ }
+
+ if n := len(parser.doc.Relationships); n != 1 {
+ t.Errorf("doc should've had 1 relation. found %d", n)
+ }
+ reln := parser.doc.Relationships[0]
+ expectedRefAEID := "item177"
+ if reln.RefA.DocumentRefID != "" {
+ t.Errorf("expected refA.DocumentRefID to be empty, found %s", reln.RefA.DocumentRefID)
+ }
+ if string(reln.RefA.ElementRefID) != expectedRefAEID {
+ t.Errorf("expected %s, found %s", expectedRefAEID, reln.RefA.ElementRefID)
+ }
+ expectedRefBEID := "Package"
+ if reln.RefB.DocumentRefID != "" {
+ t.Errorf("expected refB.DocumentRefID to be empty, found %s", reln.RefB.DocumentRefID)
+ }
+ if string(reln.RefB.ElementRefID) != expectedRefBEID {
+ t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID)
+ }
+ expectedRelationType := "contains"
+ if reln.Relationship != expectedRelationType {
+ t.Errorf("expected %s, found %s", expectedRefBEID, reln.RefB.ElementRefID)
+ }
+ if reln.RelationshipComment != "" {
+ t.Errorf("expected relationship comment to be empty, found %s", reln.RelationshipComment)
+ }
+}
+
+func Test_getNoticeTextFromNode(t *testing.T) {
+ // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION
+ output := getNoticeTextFromNode(&gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: SPDX_NOASSERTION_SMALL,
+ })
+ if strings.ToUpper(output) != "NOASSERTION" {
+ t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output))
+ }
+
+ // TestCase 2: SPDX_NOASSERTION_CAPS must return NOASSERTION
+ output = getNoticeTextFromNode(&gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: SPDX_NOASSERTION_CAPS,
+ })
+ if strings.ToUpper(output) != "NOASSERTION" {
+ t.Errorf("expected NOASSERTION, found %s", strings.ToUpper(output))
+ }
+
+ // TestCase 3: not a NOASSERTION must return the field verbatim
+ // TestCase 1: SPDX_NOASSERTION_SMALL must return NOASSERTION
+ output = getNoticeTextFromNode(&gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "text",
+ })
+ if output != "text" {
+ t.Errorf("expected text, found %s", output)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_license.go b/rdfloader/parser2v3/parse_license.go
new file mode 100644
index 0000000..b75ca05
--- /dev/null
+++ b/rdfloader/parser2v3/parse_license.go
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/gordf/rdfwriter"
+)
+
+// AnyLicense is a baseClass for all the licenses
+// All the types of licenses is a sub-type of AnyLicense,
+// either directly or indirectly.
+// This function acts as a mux for all the licenses. Based on the input, it
+// decides which type of license it is and passes control to that type of
+// license parser to parse the given input.
+func (parser *rdfParser2_3) getAnyLicenseFromNode(node *gordfParser.Node) (AnyLicenseInfo, error) {
+
+ currState := parser.cache[node.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[node.ID] = &nodeState{
+ object: nil, // not storing the object as we won't retrieve it later.
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this license node.
+ // We have a cyclic dependency!
+ return nil, errors.New("Couldn't parse license: found a cyclic dependency on " + node.ID)
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[node.ID].Color = GREY
+
+ // setting state color to black when we're done parsing this node.
+ defer func() { parser.cache[node.ID].Color = BLACK }()
+
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ if len(associatedTriples) == 0 {
+ // just a license uri string was found.
+ return parser.getSpecialLicenseFromNode(node)
+ }
+
+ // we have some attributes associated with the license node.
+ nodeType, err := getNodeTypeFromTriples(associatedTriples, node)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing license triple: %v", err)
+ }
+ switch nodeType {
+ case SPDX_DISJUNCTIVE_LICENSE_SET:
+ return parser.getDisjunctiveLicenseSetFromNode(node)
+ case SPDX_CONJUNCTIVE_LICENSE_SET:
+ return parser.getConjunctiveLicenseSetFromNode(node)
+ case SPDX_EXTRACTED_LICENSING_INFO:
+ return parser.getExtractedLicensingInfoFromNode(node)
+ case SPDX_LISTED_LICENSE, SPDX_LICENSE:
+ return parser.getLicenseFromNode(node)
+ case SPDX_WITH_EXCEPTION_OPERATOR:
+ return parser.getWithExceptionOperatorFromNode(node)
+ case SPDX_OR_LATER_OPERATOR:
+ return parser.getOrLaterOperatorFromNode(node)
+ case SPDX_SIMPLE_LICENSING_INFO:
+ return parser.getSimpleLicensingInfoFromNode(node)
+ }
+ return nil, fmt.Errorf("Unknown subTag (%s) found while parsing AnyLicense", nodeType)
+}
+
+func (parser *rdfParser2_3) getLicenseExceptionFromNode(node *gordfParser.Node) (exception LicenseException, err error) {
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ for _, triple := range associatedTriples {
+ value := triple.Object.ID
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_LICENSE_EXCEPTION_ID:
+ exception.licenseExceptionId = value
+ case SPDX_LICENSE_EXCEPTION_TEXT:
+ exception.licenseExceptionText = value
+ case RDFS_SEE_ALSO:
+ if !isUriValid(value) {
+ return exception, fmt.Errorf("invalid uri (%s) for seeAlso attribute of LicenseException", value)
+ }
+ exception.seeAlso = value
+ case SPDX_NAME:
+ exception.name = value
+ case SPDX_EXAMPLE:
+ exception.example = value
+ case RDFS_COMMENT:
+ exception.comment = value
+ default:
+ return exception, fmt.Errorf("invalid predicate(%s) for LicenseException", triple.Predicate)
+ }
+ }
+ return exception, nil
+}
+
+func (parser *rdfParser2_3) getSimpleLicensingInfoFromNode(node *gordfParser.Node) (SimpleLicensingInfo, error) {
+ simpleLicensingTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ return parser.getSimpleLicensingInfoFromTriples(simpleLicensingTriples)
+}
+
+func (parser *rdfParser2_3) getWithExceptionOperatorFromNode(node *gordfParser.Node) (operator WithExceptionOperator, err error) {
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ var memberFound bool
+ for _, triple := range associatedTriples {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_MEMBER:
+ if memberFound {
+ return operator,
+ fmt.Errorf("more than one member found in the WithExceptionOperator (expected only 1)")
+ }
+ memberFound = true
+ member, err := parser.getSimpleLicensingInfoFromNode(triple.Object)
+ if err != nil {
+ return operator, fmt.Errorf("error parsing member of a WithExceptionOperator: %v", err)
+ }
+ operator.member = member
+ case SPDX_LICENSE_EXCEPTION:
+ operator.licenseException, err = parser.getLicenseExceptionFromNode(triple.Object)
+ if err != nil {
+ return operator, fmt.Errorf("error parsing licenseException of WithExceptionOperator: %v", err)
+ }
+ default:
+ return operator, fmt.Errorf("unknown predicate (%s) for a WithExceptionOperator", triple.Predicate.ID)
+ }
+ }
+ return operator, nil
+}
+
+func (parser *rdfParser2_3) getOrLaterOperatorFromNode(node *gordfParser.Node) (operator OrLaterOperator, err error) {
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ n := len(associatedTriples)
+ if n != 2 {
+ return operator, fmt.Errorf("orLaterOperator must be associated with exactly one tag. found %v triples", n-1)
+ }
+ for _, triple := range associatedTriples {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_MEMBER:
+ operator.member, err = parser.getSimpleLicensingInfoFromNode(triple.Object)
+ if err != nil {
+ return operator, fmt.Errorf("error parsing simpleLicensingInfo of OrLaterOperator: %v", err)
+ }
+ default:
+ return operator, fmt.Errorf("unknown predicate %s", triple.Predicate.ID)
+ }
+ }
+ return operator, nil
+}
+
+// SpecialLicense is a type of license which is not defined in any of the
+// spdx documents, it is a type of license defined for the sake of brevity.
+// It can be [NONE|NOASSERTION|LicenseRef-<string>]
+func (parser *rdfParser2_3) getSpecialLicenseFromNode(node *gordfParser.Node) (lic SpecialLicense, err error) {
+ uri := strings.TrimSpace(node.ID)
+ switch uri {
+ case SPDX_NONE_CAPS, SPDX_NONE_SMALL:
+ return SpecialLicense{
+ value: NONE,
+ }, nil
+ case SPDX_NOASSERTION_SMALL, SPDX_NOASSERTION_CAPS:
+ return SpecialLicense{
+ value: NOASSERTION,
+ }, nil
+ }
+
+ // the license is neither NONE nor NOASSERTION
+ // checking if the license is among the standardLicenses
+ licenseAbbreviation := getLastPartOfURI(uri)
+ for _, stdLicense := range AllStandardLicenseIDS() {
+ if licenseAbbreviation == stdLicense {
+ return SpecialLicense{
+ value: SpecialLicenseValue(stdLicense),
+ }, nil
+ }
+ }
+ return lic, fmt.Errorf("found a custom license uri (%s) without any associated fields", uri)
+}
+
+func (parser *rdfParser2_3) getDisjunctiveLicenseSetFromNode(node *gordfParser.Node) (DisjunctiveLicenseSet, error) {
+ licenseSet := DisjunctiveLicenseSet{
+ members: []AnyLicenseInfo{},
+ }
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_MEMBER:
+ member, err := parser.getAnyLicenseFromNode(triple.Object)
+ if err != nil {
+ return licenseSet, fmt.Errorf("error parsing disjunctive license set: %v", err)
+ }
+ licenseSet.members = append(licenseSet.members, member)
+ }
+ }
+ return licenseSet, nil
+}
+
+func (parser *rdfParser2_3) getConjunctiveLicenseSetFromNode(node *gordfParser.Node) (ConjunctiveLicenseSet, error) {
+ licenseSet := ConjunctiveLicenseSet{
+ members: []AnyLicenseInfo{},
+ }
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_MEMBER:
+ member, err := parser.getAnyLicenseFromNode(triple.Object)
+ if err != nil {
+ return licenseSet, fmt.Errorf("error parsing conjunctive license set: %v", err)
+ }
+ licenseSet.members = append(licenseSet.members, member)
+ default:
+ return licenseSet, fmt.Errorf("unknown subTag for ConjunctiveLicenseSet: %s", triple.Predicate.ID)
+ }
+ }
+ return licenseSet, nil
+}
+
+func (parser *rdfParser2_3) getSimpleLicensingInfoFromTriples(triples []*gordfParser.Triple) (lic SimpleLicensingInfo, err error) {
+ for _, triple := range triples {
+ switch triple.Predicate.ID {
+ case RDFS_COMMENT:
+ lic.comment = triple.Object.ID
+ case SPDX_LICENSE_ID:
+ lic.licenseID = triple.Object.ID
+ case SPDX_NAME:
+ lic.name = triple.Object.ID
+ case RDFS_SEE_ALSO:
+ if !isUriValid(triple.Object.ID) {
+ return lic, fmt.Errorf("%s is not a valid uri for seeAlso attribute of a License", triple.Object.ID)
+ }
+ lic.seeAlso = append(lic.seeAlso, triple.Object.ID)
+ case SPDX_EXAMPLE:
+ lic.example = triple.Object.ID
+ case RDF_TYPE:
+ continue
+ default:
+ return lic, fmt.Errorf("unknown predicate(%s) for simple licensing info", triple.Predicate)
+ }
+ }
+ return lic, nil
+}
+
+func (parser *rdfParser2_3) getLicenseFromNode(node *gordfParser.Node) (lic License, err error) {
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ var restTriples []*gordfParser.Triple
+ for _, triple := range associatedTriples {
+ value := triple.Object.ID
+ switch triple.Predicate.ID {
+ case SPDX_IS_OSI_APPROVED:
+ lic.isOsiApproved, err = boolFromString(value)
+ if err != nil {
+ return lic, fmt.Errorf("error parsing isOsiApproved attribute of a License: %v", err)
+ }
+ case SPDX_LICENSE_TEXT:
+ lic.licenseText = value
+ case SPDX_STANDARD_LICENSE_HEADER:
+ lic.standardLicenseHeader = value
+ case SPDX_STANDARD_LICENSE_TEMPLATE:
+ lic.standardLicenseTemplate = value
+ case SPDX_STANDARD_LICENSE_HEADER_TEMPLATE:
+ lic.standardLicenseHeaderTemplate = value
+ case SPDX_IS_DEPRECATED_LICENSE_ID:
+ lic.isDeprecatedLicenseID, err = boolFromString(value)
+ if err != nil {
+ return lic, fmt.Errorf("error parsing isDeprecatedLicenseId attribute of a License: %v", err)
+ }
+ case SPDX_IS_FSF_LIBRE:
+ lic.isFsfLibre, err = boolFromString(value)
+ if err != nil {
+ return lic, fmt.Errorf("error parsing isFsfLibre attribute of a License: %v", err)
+ }
+ default:
+ restTriples = append(restTriples, triple)
+ }
+ }
+ lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples)
+ if err != nil {
+ return lic, fmt.Errorf("error setting simple licensing information of a License: %s", err)
+ }
+ return lic, nil
+}
diff --git a/rdfloader/parser2v3/parse_license_test.go b/rdfloader/parser2v3/parse_license_test.go
new file mode 100644
index 0000000..9ff2c88
--- /dev/null
+++ b/rdfloader/parser2v3/parse_license_test.go
@@ -0,0 +1,853 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+)
+
+func Test_rdfParser2_3_getAnyLicenseFromNode(t *testing.T) {
+ // since this function is a mux, we just have to make sure that with each
+ // type of input, it is able to redirect the request to an appropriate
+ // license getter.
+
+ // TestCase 1: input node is just a node string without any associated
+ // triple (either a NONE|NOASSERTION) because for other case,
+ // the license should've been associated with other triples
+ parser, _ := parserFromBodyContent(``)
+ inputNode := &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: NS_SPDX + "NONE",
+ }
+ lic, err := parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a SpecialLicense
+ switch lic.(type) {
+ case SpecialLicense:
+ default:
+ t.Errorf("expected license to be of type SpecialLicense, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 2: DisjunctiveLicenseSet:
+ parser, _ = parserFromBodyContent(`
+ <spdx:DisjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:DisjunctiveLicenseSet>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a DisjunctiveLicenseSet
+ switch lic.(type) {
+ case DisjunctiveLicenseSet:
+ default:
+ t.Errorf("expected license to be of type DisjunctiveLicenseSet, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 3: ConjunctiveLicenseSet:
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a ConjunctiveLicenseSet
+ switch lic.(type) {
+ case ConjunctiveLicenseSet:
+ default:
+ t.Errorf("expected license to be of type ConjunctiveLicenseSet, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 4: ExtractedLicensingInfo
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExtractedLicensingInfo rdf:about="http://spdx.dev/spdx.rdf#LicenseRef-Freeware">
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText><![CDATA[...]]></spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a ExtractedLicensingInfo
+ switch lic.(type) {
+ case ExtractedLicensingInfo:
+ default:
+ t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 4: ExtractedLicensingInfo
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExtractedLicensingInfo rdf:about="http://spdx.dev/spdx.rdf#LicenseRef-Freeware">
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText><![CDATA[...]]></spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a ExtractedLicensingInfo
+ switch lic.(type) {
+ case ExtractedLicensingInfo:
+ default:
+ t.Errorf("expected license to be of type ExtractedLicensingInfo, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 5: License
+ parser, _ = parserFromBodyContent(`
+ <spdx:License rdf:about="http://spdx.org/licenses/Apache-2.0">
+ <spdx:standardLicenseTemplate>&lt;&gt; Apache License Version 2.0, January 2004 http://www.apache.org/licenses/&lt;&gt;&lt;&gt; TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION&lt;&gt; &lt;&gt; Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. &lt;&gt; Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. &lt;&gt; Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. &lt;&gt; Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: &lt;&gt; You must give any other recipients of the Work or Derivative Works a copy of this License; and &lt;&gt; You must cause any modified files to carry prominent notices stating that You changed the files; and &lt;&gt; You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and &lt;&gt; If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. &lt;&gt; Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. &lt;&gt; Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. &lt;&gt; Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. &lt;&gt; Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. &lt;&gt; Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.&lt;&gt; END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright &lt;&gt; Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.&lt;&gt;</spdx:standardLicenseTemplate>
+ <rdfs:seeAlso>http://www.apache.org/licenses/LICENSE-2.0</rdfs:seeAlso>
+ <spdx:name>Apache License 2.0</spdx:name>
+ <spdx:licenseId>Apache-2.0</spdx:licenseId>
+ <spdx:isOsiApproved>true</spdx:isOsiApproved>
+ <rdfs:seeAlso>http://www.opensource.org/licenses/Apache-2.0</rdfs:seeAlso>
+ <spdx:licenseText>...</spdx:licenseText>
+ <spdx:standardLicenseHeader>...</spdx:standardLicenseHeader>
+ </spdx:License>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a License
+ switch lic.(type) {
+ case License:
+ default:
+ t.Errorf("expected license to be of type License, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 5: WithExceptionOperator
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:licenseException>
+ <spdx:LicenseException rdf:nodeID="A1">
+ <spdx:example></spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:comment></rdfs:comment>
+ </spdx:LicenseException>
+ </spdx:licenseException>
+ <spdx:member rdf:resource="http://spdx.org/licenses/GPL-2.0-or-later"/>
+ </spdx:WithExceptionOperator>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a WithExceptionOperator
+ switch lic.(type) {
+ case WithExceptionOperator:
+ default:
+ t.Errorf("expected license to be of type WithExceptionOperator, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 6: OrLaterOperator
+ parser, _ = parserFromBodyContent(`
+ <spdx:OrLaterOperator>
+ <spdx:member>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:member>
+ </spdx:OrLaterOperator>
+ `)
+ inputNode = parser.gordfParserObj.Triples[0].Subject
+ lic, err = parser.getAnyLicenseFromNode(inputNode)
+ if err != nil {
+ t.Errorf("error parsing a valid license input: %v", err)
+ }
+ // checking if the return type is a OrLaterOperator
+ switch lic.(type) {
+ case OrLaterOperator:
+ default:
+ t.Errorf("expected license to be of type OrLaterOperator, found %v", reflect.TypeOf(lic))
+ }
+
+ // TestCase 7: checking if an unknown license raises an error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:UnknownLicense>
+ <spdx:unknownTag />
+ </spdx:UnknownLicense>
+ `)
+ node := parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getAnyLicenseFromNode(node)
+ t.Log(err)
+ if err == nil {
+ t.Errorf("should've raised an error for invalid input")
+ }
+
+ // TestCase 8: cyclic dependent license must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet rdf:about="#SPDXRef-RecursiveLicense">
+ <spdx:member rdf:resource="http://spdx.org/licenses/GPL-2.0-or-later"/>
+ <spdx:member>
+ <spdx:ConjunctiveLicenseSet rdf:about="#SPDXRef-RecursiveLicense">
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ <spdx:member rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-RecursiveLicense"/>
+ </spdx:ConjunctiveLicenseSet>
+ </spdx:member>
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getAnyLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to cyclic dependent license. found %v", err)
+ }
+}
+
+func Test_rdfParser2_3_getConjunctiveLicenseSetFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ var licenseNode *gordfParser.Node
+ var license ConjunctiveLicenseSet
+
+ // TestCase 1: invalid license member
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Unknown"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid license member, found <nil>")
+ }
+
+ // TestCase 2: invalid predicate in the licenseSet.
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/CC0-1.0"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ <spdx:unknownTag />
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getConjunctiveLicenseSetFromNode(licenseNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid predicate found")
+ }
+
+ // TestCase 3: valid example.
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getConjunctiveLicenseSetFromNode(licenseNode)
+ if err != nil {
+ t.Errorf("unexpected error parsing licenseSet: %v", err)
+ }
+ nMembers := len(license.members)
+ if nMembers != 2 {
+ t.Errorf("expected licenseSet to have 2 members, found %d", nMembers)
+ }
+ licenseMembers := mapLicensesToStrings(license.members)
+ expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"}
+ sort.Strings(licenseMembers)
+ if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) {
+ t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers)
+ }
+}
+
+func Test_rdfParser2_3_getDisjunctiveLicenseSetFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ var licenseNode *gordfParser.Node
+ var license DisjunctiveLicenseSet
+
+ // TestCase 1: invalid license member
+ parser, _ = parserFromBodyContent(`
+ <spdx:DisjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Unknown"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:DisjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid license member, found <nil>")
+ }
+
+ // TestCase 2: invalid predicate in the licenseSet.
+ parser, _ = parserFromBodyContent(`
+ <spdx:DisjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Unknown"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ <spdx:unknownTag />
+ </spdx:DisjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode)
+ if err == nil {
+ t.Errorf("expected an error saying invalid predicate found")
+ }
+
+ // TestCase 3: valid example.
+ parser, _ = parserFromBodyContent(`
+ <spdx:DisjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:DisjunctiveLicenseSet>
+ `)
+ licenseNode = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getDisjunctiveLicenseSetFromNode(licenseNode)
+ if err != nil {
+ t.Errorf("unexpected error parsing licenseSet: %v", err)
+ }
+ nMembers := len(license.members)
+ if nMembers != 2 {
+ t.Errorf("expected licenseSet to have 2 members, found %d", nMembers)
+ }
+ licenseMembers := mapLicensesToStrings(license.members)
+ expectedLicenseMembers := []string{"LGPL-2.0", "Nokia"}
+ sort.Strings(licenseMembers)
+ if !reflect.DeepEqual(licenseMembers, expectedLicenseMembers) {
+ t.Errorf("expected %v, found %v", expectedLicenseMembers, licenseMembers)
+ }
+}
+
+func Test_rdfParser2_3_getLicenseExceptionFromNode(t *testing.T) {
+ var licenseException LicenseException
+ var err error
+ var node *gordfParser.Node
+ var parser *rdfParser2_3
+
+ // TestCase 1: invalid value for rdf:seeAlso
+ parser, _ = parserFromBodyContent(`
+ <spdx:LicenseException>
+ <rdfs:seeAlso>see-also</rdfs:seeAlso>
+ <spdx:example></spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:comment></rdfs:comment>
+ </spdx:LicenseException>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getLicenseExceptionFromNode(node)
+ if err == nil {
+ t.Errorf("should've raised an error due to invalid uri for rdfs:seeAlso")
+ }
+
+ // TestCase 2: invalid predicate for licenseException
+ // TestCase 1: invalid value for rdf:seeAlso
+ parser, _ = parserFromBodyContent(`
+ <spdx:LicenseException>
+ <spdx:example></spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:unknown></rdfs:unknown>
+ </spdx:LicenseException>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getLicenseExceptionFromNode(node)
+ if err == nil {
+ t.Errorf("should've raised an error due to invalid predicate")
+ }
+
+ // TestCase 3: everything valid
+ // TestCase 1: invalid value for rdf:seeAlso
+ parser, _ = parserFromBodyContent(`
+ <spdx:LicenseException>
+ <rdfs:seeAlso rdf:resource="http://www.opensource.org/licenses/GPL-3.0"/>
+ <spdx:example>no example</spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:comment>no comments</rdfs:comment>
+ <spdx:licenseExceptionText>text</spdx:licenseExceptionText>
+ <spdx:name>name</spdx:name>
+ </spdx:LicenseException>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ licenseException, err = parser.getLicenseExceptionFromNode(node)
+ if err != nil {
+ t.Fatalf("unexpected error while parsing a valid licenseException")
+ }
+ expectedCrossReference := "http://www.opensource.org/licenses/GPL-3.0"
+ if licenseException.seeAlso != expectedCrossReference {
+ t.Errorf("expected: %s, found: %s", expectedCrossReference, licenseException.seeAlso)
+ }
+ expectedExample := "no example"
+ if licenseException.example != expectedExample {
+ t.Errorf("expected: %s, got: %s", expectedExample, licenseException.example)
+ }
+ if licenseException.licenseExceptionId != "Libtool-exception" {
+ t.Errorf("expected: %s, got: %s", "Libtool-exception", licenseException.licenseExceptionId)
+ }
+ if licenseException.comment != "no comments" {
+ t.Errorf("expected: %s, got: %s", "no comments", licenseException.comment)
+ }
+ if licenseException.licenseExceptionText != "text" {
+ t.Errorf("expected: '%s', got: '%s'", "text", licenseException.licenseExceptionText)
+ }
+ if licenseException.name != "name" {
+ t.Errorf("expected: '%s', got: '%s'", "name", licenseException.name)
+ }
+}
+
+func Test_rdfParser2_3_getLicenseFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var license License
+ var err error
+
+ // TestCase 1: isOsiApproved is not a valid boolean
+ parser, _ = parserFromBodyContent(`
+ <spdx:License>
+ <spdx:isOsiApproved>no</spdx:isOsiApproved>
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected function to raise an error stating isOsiApproved should be a valid boolean type")
+ }
+
+ // TestCase 2: rdf:seeAlso not a valid uri must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:License>
+ <rdfs:seeAlso>uri</rdfs:seeAlso>
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected function to raise an error stating invalid uri for rdfs:seeAlso")
+ }
+
+ // TestCase 3: isDeprecatedLicenseId is not a valid boolean
+ parser, _ = parserFromBodyContent(`
+ <spdx:License>
+ <spdx:isDeprecatedLicenseId>yes</spdx:isDeprecatedLicenseId>
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected function to raise an error stating isDeprecatedLicenseId should be a valid boolean type")
+ }
+
+ // TestCase 4: isFsfLibre is not a valid boolean
+ parser, _ = parserFromBodyContent(`
+ <spdx:License>
+ <spdx:isFsfLibre>no</spdx:isFsfLibre>
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected function to raise an error stating isFsfLibre should be a valid boolean type")
+ }
+
+ // TestCase 5: invalid triple for License:
+ parser, _ = parserFromBodyContent(`
+ <spdx:License>
+ <spdx:unknown />
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("invalid predicate should've raised an error, got <nil>")
+ }
+
+ // TestCase 5: everything valid:
+ parser, _ = parserFromBodyContent(`
+ <spdx:License rdf:about="http://spdx.org/licenses/GPL-3.0-or-later">
+ <rdfs:seeAlso>http://www.opensource.org/licenses/GPL-3.0</rdfs:seeAlso>
+ <spdx:isOsiApproved>true</spdx:isOsiApproved>
+ <spdx:licenseText>GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007</spdx:licenseText>
+ <spdx:name>GNU General Public License v3.0 or later</spdx:name>
+ <spdx:standardLicenseHeaderTemplate>...</spdx:standardLicenseHeaderTemplate>
+ <spdx:licenseId>GPL-3.0-or-later</spdx:licenseId>
+ <rdfs:comment>This license was released: 29 June 2007</rdfs:comment>
+ <spdx:isFsfLibre>true</spdx:isFsfLibre>
+ <spdx:standardLicenseHeader>...</spdx:standardLicenseHeader>
+ <spdx:standardLicenseTemplate>....</spdx:standardLicenseTemplate>
+ </spdx:License>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ license, err = parser.getLicenseFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid input: %v", err)
+ }
+ expectedSeeAlso := "http://www.opensource.org/licenses/GPL-3.0"
+ if len(license.seeAlso) != 1 {
+ t.Fatalf("expected seeAlso to have 1 element, got %d", len(license.seeAlso))
+ }
+ if license.seeAlso[len(license.seeAlso)-1] != expectedSeeAlso {
+ t.Errorf("expected %s, got %s", expectedSeeAlso, license.seeAlso)
+ }
+ if license.isOsiApproved != true {
+ t.Errorf("expected %t, got %t", true, license.isOsiApproved)
+ }
+ expectedLicenseText := "GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007"
+ if license.licenseText != expectedLicenseText {
+ t.Errorf("expected %s, got %s", expectedSeeAlso, license.licenseText)
+ }
+ expectedName := "GNU General Public License v3.0 or later"
+ if license.name != expectedName {
+ t.Errorf("expected %s, got %s", expectedName, license.name)
+ }
+ expectedstdLicHeader := "..."
+ if license.standardLicenseHeader != expectedstdLicHeader {
+ t.Errorf("expected %s, got %s", expectedstdLicHeader, license.standardLicenseHeader)
+ }
+ expectedLicenseId := "GPL-3.0-or-later"
+ if expectedLicenseId != license.licenseID {
+ t.Errorf("expected %s, got %s", expectedLicenseId, license.licenseID)
+ }
+ expectedLicenseComment := "This license was released: 29 June 2007"
+ if expectedLicenseComment != license.comment {
+ t.Errorf("expected %s, got %s", expectedLicenseComment, license.comment)
+ }
+ expectedstdLicTemplate := "..."
+ if license.standardLicenseHeader != expectedstdLicTemplate {
+ t.Errorf("expected %s, got %s", expectedstdLicTemplate, license.standardLicenseTemplate)
+ }
+ expectedstdLicHeaderTemplate := "..."
+ if license.standardLicenseHeaderTemplate != expectedstdLicHeaderTemplate {
+ t.Errorf("expected %s, got %s", expectedstdLicHeaderTemplate, license.standardLicenseHeaderTemplate)
+ }
+ if license.isFsfLibre != true {
+ t.Errorf("expected %t, got %t", true, license.isFsfLibre)
+ }
+}
+
+func Test_rdfParser2_3_getOrLaterOperatorFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var err error
+
+ // TestCase 1: more than one member in the OrLaterOperator tag must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:OrLaterOperator>
+ <spdx:member>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:member>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ </spdx:OrLaterOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getOrLaterOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to more than one members, got <nil>")
+ }
+
+ // TestCase 2: Invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:OrLaterOperator>
+ <spdx:members>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:members>
+ </spdx:OrLaterOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getOrLaterOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to invalid predicate, got <nil>")
+ }
+
+ // TestCase 5: invalid member
+ parser, _ = parserFromBodyContent(`
+ <spdx:OrLaterOperator>
+ <spdx:member>
+ <spdx:SimpleLicensingInfo>
+ <spdx:invalidTag />
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:member>
+ </spdx:OrLaterOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getOrLaterOperatorFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error parsing invalid license member, got %v", err)
+ }
+
+ // TestCase 4: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx:OrLaterOperator>
+ <spdx:member>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:member>
+ </spdx:OrLaterOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getOrLaterOperatorFromNode(node)
+ if err != nil {
+ t.Errorf("unexpected error parsing a valid input: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_getSimpleLicensingInfoFromNode(t *testing.T) {
+ // nothing to test. The just provides an interface to call function that
+ // uses triples to render a SimpleLicensingInfo.
+ parser, _ := parserFromBodyContent(`
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ `)
+ node := parser.gordfParserObj.Triples[0].Subject
+ _, err := parser.getSimpleLicensingInfoFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid input: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_getSimpleLicensingInfoFromTriples(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ var license SimpleLicensingInfo
+
+ // TestCase 1: invalid rdf:seeAlso attribute
+ parser, _ = parserFromBodyContent(`
+ <spdx:SimpleLicensingInfo>
+ <rdfs:seeAlso>an invalid uri</rdfs:seeAlso>
+ </spdx:SimpleLicensingInfo>
+ `)
+ _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples)
+ if err == nil {
+ t.Error("expected an error reporting invalid uri for rdf:seeAlso, got <nil>")
+ }
+
+ // TestCase 2: invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:SimpleLicensingInfo>
+ <rdfs:invalidPredicate />
+ </spdx:SimpleLicensingInfo>
+ `)
+ _, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples)
+ if err == nil {
+ t.Error("expected an error reporting invalid predicate, got <nil>")
+ }
+
+ // TestCase 3: valid example
+ parser, _ = parserFromBodyContent(`
+ <spdx:SimpleLicensingInfo>
+ <rdfs:comment>comment</rdfs:comment>
+ <spdx:licenseId>lid</spdx:licenseId>
+ <spdx:name>name</spdx:name>
+ <rdfs:seeAlso>https://opensource.org/licenses/MPL-1.0</rdfs:seeAlso>
+ <spdx:example>example</spdx:example>
+ </spdx:SimpleLicensingInfo>
+ `)
+ license, err = parser.getSimpleLicensingInfoFromTriples(parser.gordfParserObj.Triples)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ expectedComment := "comment"
+ expectedLicenseId := "lid"
+ expectedName := "name"
+ expectedSeeAlso := "https://opensource.org/licenses/MPL-1.0"
+ expectedExample := "example"
+ if expectedComment != license.comment {
+ t.Errorf("expected %v, got %v", expectedComment, license.comment)
+ }
+ if expectedLicenseId != license.licenseID {
+ t.Errorf("expected %v, got %v", expectedLicenseId, license.licenseID)
+ }
+ if expectedName != license.name {
+ t.Errorf("expected %v, got %v", expectedName, license.name)
+ }
+ if len(license.seeAlso) != 1 {
+ t.Fatalf("expected seeAlso to have 1 element, found %d", len(license.seeAlso))
+ }
+ if license.seeAlso[0] != expectedSeeAlso {
+ t.Errorf("expected %v, got %v", expectedSeeAlso, license.seeAlso[0])
+ }
+ if license.example != expectedExample {
+ t.Errorf("expected %v, got %v", expectedExample, license.example)
+ }
+}
+
+func Test_rdfParser2_3_getSpecialLicenseFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var license SpecialLicense
+
+ // TestCase 1: NONE
+ parser, _ = parserFromBodyContent(``)
+ node = &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: NS_SPDX + "NONE",
+ }
+ license, err := parser.getSpecialLicenseFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid node: %v", err)
+ }
+ if license.value != "NONE" {
+ t.Errorf("expected %s, got %s", "NONE", license.value)
+ }
+
+ // TestCase 2: NOASSERTION
+ parser, _ = parserFromBodyContent(``)
+ node = &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: NS_SPDX + "NOASSERTION",
+ }
+ license, err = parser.getSpecialLicenseFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid node: %v", err)
+ }
+ if license.value != "NOASSERTION" {
+ t.Errorf("expected %s, got %s", "NOASSERTION", license.value)
+ }
+
+ // TestCase 4: undefined standard license
+ parser, _ = parserFromBodyContent(``)
+ node = &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "https://opensource.org/licenses/unknown",
+ }
+ _, err = parser.getSpecialLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error saying invalid license")
+ }
+
+ // TestCase 4: valid standard license
+ parser, _ = parserFromBodyContent(``)
+ node = &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "https://opensource.org/licenses/MPL-1.0",
+ }
+ license, err = parser.getSpecialLicenseFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid node: %v", err)
+ }
+ if license.value != "MPL-1.0" {
+ t.Errorf("expected %s, got %s", "MPL-1.0", license.value)
+ }
+}
+
+func Test_rdfParser2_3_getWithExceptionOperatorFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var err error
+
+ // TestCase 1: more than one member in the OrLaterOperator tag must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:member>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:member>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ </spdx:WithExceptionOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getWithExceptionOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to more than one members, got <nil>")
+ }
+
+ // TestCase 2: Invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:members>
+ <spdx:SimpleLicensingInfo>
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ </spdx:SimpleLicensingInfo>
+ </spdx:members>
+ </spdx:WithExceptionOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getWithExceptionOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to invalid predicate, got <nil>")
+ }
+
+ // TestCase 3: Invalid member
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:member>
+ <spdx:License rdf:about="http://spdx.org/licenses/GPL-2.0-or-later">
+ <spdx:unknownTag />
+ </spdx:License>
+ </spdx:member>
+ </spdx:WithExceptionOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getWithExceptionOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to error parsing a member, got <nil>")
+ }
+
+ // TestCase 4: Invalid licenseException
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:member>
+ <spdx:License rdf:about="http://spdx.org/licenses/GPL-2.0-or-later"/>
+ </spdx:member>
+ <spdx:licenseException>
+ <spdx:LicenseException>
+ <spdx:invalidTag />
+ <spdx:example>example</spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:LicenseException>
+ </spdx:licenseException>
+ </spdx:WithExceptionOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getWithExceptionOperatorFromNode(node)
+ if err == nil {
+ t.Error("expected an error due to invalid licenseException, got <nil>")
+ }
+
+ // TestCase 5: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx:WithExceptionOperator>
+ <spdx:member>
+ <spdx:License rdf:about="http://spdx.org/licenses/GPL-2.0-or-later"/>
+ </spdx:member>
+ <spdx:licenseException>
+ <spdx:LicenseException>
+ <spdx:example>example</spdx:example>
+ <spdx:licenseExceptionId>Libtool-exception</spdx:licenseExceptionId>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:LicenseException>
+ </spdx:licenseException>
+ </spdx:WithExceptionOperator>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getWithExceptionOperatorFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid input: %v", err)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_other_license_info.go b/rdfloader/parser2v3/parse_other_license_info.go
new file mode 100644
index 0000000..3e28979
--- /dev/null
+++ b/rdfloader/parser2v3/parse_other_license_info.go
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func (parser *rdfParser2_3) getExtractedLicensingInfoFromNode(node *gordfParser.Node) (lic ExtractedLicensingInfo, err error) {
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ var restTriples []*gordfParser.Triple
+ for _, triple := range associatedTriples {
+ switch triple.Predicate.ID {
+ case SPDX_EXTRACTED_TEXT:
+ lic.extractedText = triple.Object.ID
+ default:
+ restTriples = append(restTriples, triple)
+ }
+ }
+ lic.SimpleLicensingInfo, err = parser.getSimpleLicensingInfoFromTriples(restTriples)
+ if err != nil {
+ return lic, fmt.Errorf("error setting simple licensing information of extracted licensing info: %s", err)
+ }
+ return lic, nil
+}
+
+func (parser *rdfParser2_3) extractedLicenseToOtherLicense(extLicense ExtractedLicensingInfo) (othLic v2_3.OtherLicense) {
+ othLic.LicenseIdentifier = extLicense.licenseID
+ othLic.ExtractedText = extLicense.extractedText
+ othLic.LicenseComment = extLicense.comment
+ othLic.LicenseCrossReferences = extLicense.seeAlso
+ othLic.LicenseName = extLicense.name
+ return othLic
+}
diff --git a/rdfloader/parser2v3/parse_other_license_info_test.go b/rdfloader/parser2v3/parse_other_license_info_test.go
new file mode 100644
index 0000000..cb12181
--- /dev/null
+++ b/rdfloader/parser2v3/parse_other_license_info_test.go
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+)
+
+func Test_rdfParser2_3_getExtractedLicensingInfoFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ var node *gordfParser.Node
+
+ // TestCase 1: invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExtractedLicensingInfo rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#LicenseRef-Freeware">
+ <spdx:licenseID>LicenseRef-Freeware</spdx:licenseID>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText><![CDATA[Software classified as freeware is licensed at no cost and is either fully functional for an unlimited time; or has only basic functions enabled with a fully functional version available commercially or as shareware.[8] In contrast to free software, the author usually restricts one or more rights of the user, including the rights to use, copy, distribute, modify and make derivative works of the software or extract the source code.[1][2][9][10] The software license may impose various additional restrictions on the type of use, e.g. only for personal use, private use, individual use, non-profit use, non-commercial use, academic use, educational use, use in charity or humanitarian organizations, non-military use, use by public authorities or various other combinations of these type of restrictions.[11] For instance, the license may be "free for private, non-commercial use". The software license may also impose various other restrictions, such as restricted use over a network, restricted use on a server, restricted use in a combination with some types of other software or with some hardware devices, prohibited distribution over the Internet other than linking to author's website, restricted distribution without author's consent, restricted number of copies, etc.]]></spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getExtractedLicensingInfoFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error saying invalid predicate, got <nil>")
+ }
+
+ // TestCase 2: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExtractedLicensingInfo rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#LicenseRef-Freeware">
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText><![CDATA[Software classified as freeware is licensed at no cost and is either fully functional for an unlimited time; or has only basic functions enabled with a fully functional version available commercially or as shareware.[8] In contrast to free software, the author usually restricts one or more rights of the user, including the rights to use, copy, distribute, modify and make derivative works of the software or extract the source code.[1][2][9][10] The software license may impose various additional restrictions on the type of use, e.g. only for personal use, private use, individual use, non-profit use, non-commercial use, academic use, educational use, use in charity or humanitarian organizations, non-military use, use by public authorities or various other combinations of these type of restrictions.[11] For instance, the license may be "free for private, non-commercial use". The software license may also impose various other restrictions, such as restricted use over a network, restricted use on a server, restricted use in a combination with some types of other software or with some hardware devices, prohibited distribution over the Internet other than linking to author's website, restricted distribution without author's consent, restricted number of copies, etc.]]></spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getExtractedLicensingInfoFromNode(node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_extractedLicenseToOtherLicense(t *testing.T) {
+ // nothing to test for this function.
+ parser, _ := parserFromBodyContent(`
+ <spdx:ExtractedLicensingInfo rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#LicenseRef-Freeware">
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText><![CDATA[Software classified as freeware is licensed at no cost and is either fully functional for an unlimited time; or has only basic functions enabled with a fully functional version available commercially or as shareware.[8] In contrast to free software, the author usually restricts one or more rights of the user, including the rights to use, copy, distribute, modify and make derivative works of the software or extract the source code.[1][2][9][10] The software license may impose various additional restrictions on the type of use, e.g. only for personal use, private use, individual use, non-profit use, non-commercial use, academic use, educational use, use in charity or humanitarian organizations, non-military use, use by public authorities or various other combinations of these type of restrictions.[11] For instance, the license may be "free for private, non-commercial use". The software license may also impose various other restrictions, such as restricted use over a network, restricted use on a server, restricted use in a combination with some types of other software or with some hardware devices, prohibited distribution over the Internet other than linking to author's website, restricted distribution without author's consent, restricted number of copies, etc.]]></spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ `)
+ node := parser.gordfParserObj.Triples[0].Subject
+ extLicense, _ := parser.getExtractedLicensingInfoFromNode(node)
+ othLic := parser.extractedLicenseToOtherLicense(extLicense)
+
+ if othLic.LicenseIdentifier != extLicense.licenseID {
+ t.Errorf("expected %v, got %v", othLic.LicenseIdentifier, extLicense.licenseID)
+ }
+ if othLic.ExtractedText != extLicense.extractedText {
+ t.Errorf("expected %v, got %v", othLic.ExtractedText, extLicense.extractedText)
+ }
+ if othLic.LicenseComment != extLicense.comment {
+ t.Errorf("expected %v, got %v", othLic.LicenseComment, extLicense.comment)
+ }
+ if !reflect.DeepEqual(othLic.LicenseCrossReferences, extLicense.seeAlso) {
+ t.Errorf("expected %v, got %v", othLic.LicenseCrossReferences, extLicense.seeAlso)
+ }
+ if othLic.LicenseName != extLicense.name {
+ t.Errorf("expected %v, got %v", othLic.LicenseName, extLicense.name)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_package.go b/rdfloader/parser2v3/parse_package.go
new file mode 100644
index 0000000..f455fe9
--- /dev/null
+++ b/rdfloader/parser2v3/parse_package.go
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func (parser *rdfParser2_3) getPackageFromNode(packageNode *gordfParser.Node) (pkg *v2_3.Package, err error) {
+ pkg = &v2_3.Package{} // new package which will be returned
+
+ currState := parser.cache[packageNode.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[packageNode.ID] = &nodeState{
+ object: pkg,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this package node and we needn't parse it again.
+ return currState.object.(*v2_3.Package), nil
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[packageNode.ID].Color = GREY
+
+ // setting state color to black to indicate when we're done parsing this node.
+ defer func() { parser.cache[packageNode.ID].Color = BLACK }()
+
+ // setting the SPDXIdentifier for the package.
+ eId, err := ExtractElementID(getLastPartOfURI(packageNode.ID))
+ if err != nil {
+ return nil, fmt.Errorf("error extracting elementID of a package identifier: %v", err)
+ }
+ pkg.PackageSPDXIdentifier = eId // 3.2
+
+ // check if we already have a package initialized for this ID
+ existingPackageIndex := -1
+ for ii, existingPkg := range parser.doc.Packages {
+ if existingPkg != nil && existingPkg.PackageSPDXIdentifier == eId {
+ existingPackageIndex = ii
+ pkg = existingPkg
+ break
+ }
+ }
+
+ // iterate over all the triples associated with the provided package packageNode.
+ for _, subTriple := range parser.nodeToTriples(packageNode) {
+ switch subTriple.Predicate.ID {
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ continue
+ case SPDX_NAME: // 7.1
+ // cardinality: exactly 1
+ pkg.PackageName = subTriple.Object.ID
+ case SPDX_VERSION_INFO: // 7.3
+ // cardinality: max 1
+ pkg.PackageVersion = subTriple.Object.ID
+ case SPDX_PACKAGE_FILE_NAME: // 7.4
+ // cardinality: max 1
+ pkg.PackageFileName = subTriple.Object.ID
+ case SPDX_SUPPLIER: // 7.5
+ // cardinality: max 1
+ err = setPackageSupplier(pkg, subTriple.Object.ID)
+ case SPDX_ORIGINATOR: // 7.6
+ // cardinality: max 1
+ err = setPackageOriginator(pkg, subTriple.Object.ID)
+ case SPDX_DOWNLOAD_LOCATION: // 7.7
+ // cardinality: exactly 1
+ err = setDocumentLocationFromURI(pkg, subTriple.Object.ID)
+ case SPDX_FILES_ANALYZED: // 7.8
+ // cardinality: max 1
+ err = setFilesAnalyzed(pkg, subTriple.Object.ID)
+ case SPDX_PACKAGE_VERIFICATION_CODE: // 7.9
+ // cardinality: max 1
+ err = parser.setPackageVerificationCode(pkg, subTriple.Object)
+ case SPDX_CHECKSUM: // 7.10
+ // cardinality: min 0
+ err = parser.setPackageChecksum(pkg, subTriple.Object)
+ case DOAP_HOMEPAGE: // 7.11
+ // cardinality: max 1
+ // homepage must be a valid Uri
+ if !isUriValid(subTriple.Object.ID) {
+ return nil, fmt.Errorf("invalid uri %s while parsing doap_homepage in a package", subTriple.Object.ID)
+ }
+ pkg.PackageHomePage = subTriple.Object.ID
+ case SPDX_SOURCE_INFO: // 7.12
+ // cardinality: max 1
+ pkg.PackageSourceInfo = subTriple.Object.ID
+ case SPDX_LICENSE_CONCLUDED: // 7.13
+ // cardinality: exactly 1
+ anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object)
+ if err != nil {
+ return nil, err
+ }
+ pkg.PackageLicenseConcluded = anyLicenseInfo.ToLicenseString()
+ case SPDX_LICENSE_INFO_FROM_FILES: // 7.14
+ // cardinality: min 0
+ pkg.PackageLicenseInfoFromFiles = append(pkg.PackageLicenseInfoFromFiles, getLicenseStringFromURI(subTriple.Object.ID))
+ case SPDX_LICENSE_DECLARED: // 7.15
+ // cardinality: exactly 1
+ anyLicenseInfo, err := parser.getAnyLicenseFromNode(subTriple.Object)
+ if err != nil {
+ return nil, err
+ }
+ pkg.PackageLicenseDeclared = anyLicenseInfo.ToLicenseString()
+ case SPDX_LICENSE_COMMENTS: // 7.16
+ // cardinality: max 1
+ pkg.PackageLicenseComments = subTriple.Object.ID
+ case SPDX_COPYRIGHT_TEXT: // 7.17
+ // cardinality: exactly 1
+ pkg.PackageCopyrightText = subTriple.Object.ID
+ case SPDX_SUMMARY: // 7.18
+ // cardinality: max 1
+ pkg.PackageSummary = subTriple.Object.ID
+ case SPDX_DESCRIPTION: // 7.19
+ // cardinality: max 1
+ pkg.PackageDescription = subTriple.Object.ID
+ case RDFS_COMMENT: // 7.20
+ // cardinality: max 1
+ pkg.PackageComment = subTriple.Object.ID
+ case SPDX_EXTERNAL_REF: // 7.21
+ // cardinality: min 0
+ externalDocRef, err := parser.getPackageExternalRef(subTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing externalRef of a package: %v", err)
+ }
+ pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, externalDocRef)
+ case SPDX_HAS_FILE: // 7.22
+ // cardinality: min 0
+ file, err := parser.getFileFromNode(subTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error setting file inside a package: %v", err)
+ }
+ parser.setFileToPackage(pkg, file)
+ case SPDX_PRIMARY_PACKAGE_PURPOSE: // 7.24
+ // cardinality: exactly 1
+ pkg.PrimaryPackagePurpose = getPrimaryPackagePurpose(subTriple.Object.ID)
+ case SPDX_RELEASE_DATE: // 7.25
+ // cardinality: exactly 1
+ pkg.ReleaseDate = subTriple.Object.ID
+ case SPDX_BUILT_DATE: // 7.26
+ // cardinality: exactly 1
+ pkg.BuiltDate = subTriple.Object.ID
+ case SPDX_VALID_UNTIL_DATE: // 7.27
+ // cardinality: exactly 1
+ pkg.ValidUntilDate = subTriple.Object.ID
+ case SPDX_RELATIONSHIP:
+ // cardinality: min 0
+ err = parser.parseRelationship(subTriple)
+ case SPDX_ATTRIBUTION_TEXT:
+ // cardinality: min 0
+ pkg.PackageAttributionTexts = append(pkg.PackageAttributionTexts, subTriple.Object.ID)
+ case SPDX_ANNOTATION:
+ // cardinality: min 0
+ err = parser.parseAnnotationFromNode(subTriple.Object)
+ default:
+ return nil, fmt.Errorf("unknown predicate id %s while parsing a package", subTriple.Predicate.ID)
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if existingPackageIndex != -1 {
+ parser.doc.Packages[existingPackageIndex] = pkg
+ } else {
+ parser.doc.Packages = append(parser.doc.Packages, pkg)
+ }
+
+ return pkg, nil
+}
+
+// parses externalReference found in the package by the associated triple.
+func (parser *rdfParser2_3) getPackageExternalRef(node *gordfParser.Node) (externalDocRef *v2_3.PackageExternalReference, err error) {
+ externalDocRef = &v2_3.PackageExternalReference{}
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case SPDX_REFERENCE_CATEGORY:
+ // cardinality: exactly 1
+ switch triple.Object.ID {
+ case SPDX_REFERENCE_CATEGORY_SECURITY:
+ externalDocRef.Category = "SECURITY"
+ case SPDX_REFERENCE_CATEGORY_PACKAGE_MANAGER:
+ externalDocRef.Category = "PACKAGE-MANAGER"
+ case SPDX_REFERENCE_CATEGORY_OTHER:
+ externalDocRef.Category = "OTHER"
+ default:
+ return nil, fmt.Errorf("unknown packageManager uri %s", triple.Predicate.ID)
+ }
+ case RDF_TYPE:
+ continue
+ case SPDX_REFERENCE_TYPE:
+ // assumes: the reference type is associated with just the uri and
+ // other associated fields are ignored.
+ // other fields include:
+ // 1. contextualExample,
+ // 2. documentation and,
+ // 3. externalReferenceSite
+ externalDocRef.RefType = triple.Object.ID
+ case SPDX_REFERENCE_LOCATOR:
+ // cardinality: exactly 1
+ externalDocRef.Locator = triple.Object.ID
+ case RDFS_COMMENT:
+ // cardinality: max 1
+ externalDocRef.ExternalRefComment = triple.Object.ID
+ default:
+ return nil, fmt.Errorf("unknown package external reference predicate id %s", triple.Predicate.ID)
+ }
+ }
+ return
+}
+
+func getPrimaryPackagePurpose(purpose string) string {
+ value := strings.ReplaceAll(purpose, "packagePurpose_", "")
+ value = strings.ReplaceAll(value, "_", "-")
+ value = strings.ToUpper(value)
+ switch value {
+ case "APPLICATION", "FRAMEWORK", "LIBRARY", "CONTAINER", "OPERATING-SYSTEM", "DEVICE", "FIRMWARE", "SOURCE", "ARCHIVE", "FILE", "INSTALL", "OTHER":
+ return value
+ }
+ // invalid value
+ return ""
+}
+
+func (parser *rdfParser2_3) setPackageVerificationCode(pkg *v2_3.Package, node *gordfParser.Node) error {
+ if pkg.PackageVerificationCode == nil {
+ pkg.PackageVerificationCode = &common.PackageVerificationCode{}
+ }
+ for _, subTriple := range parser.nodeToTriples(node) {
+ switch subTriple.Predicate.ID {
+ case SPDX_PACKAGE_VERIFICATION_CODE_VALUE:
+ // cardinality: exactly 1
+ pkg.PackageVerificationCode.Value = subTriple.Object.ID
+ case SPDX_PACKAGE_VERIFICATION_CODE_EXCLUDED_FILE:
+ // cardinality: min 0
+ pkg.PackageVerificationCode.ExcludedFiles = append(pkg.PackageVerificationCode.ExcludedFiles, subTriple.Object.ID)
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ continue
+ default:
+ return fmt.Errorf("unparsed predicate %s", subTriple.Predicate.ID)
+ }
+ }
+ return nil
+}
+
+// appends the file to the package and also sets the assocWithPackage for the
+// file to indicate the file is associated with a package
+func (parser *rdfParser2_3) setFileToPackage(pkg *v2_3.Package, file *v2_3.File) {
+ if pkg.Files == nil {
+ pkg.Files = []*v2_3.File{}
+ }
+ pkg.Files = append(pkg.Files, file)
+ parser.assocWithPackage[file.FileSPDXIdentifier] = true
+}
+
+// given a supplierObject, sets the PackageSupplier attribute of the pkg.
+// Args:
+// value: [NOASSERTION | [Person | Organization]: string]
+func setPackageSupplier(pkg *v2_3.Package, value string) error {
+ value = strings.TrimSpace(value)
+ supplier := &common.Supplier{}
+ if strings.ToUpper(value) == "NOASSERTION" {
+ supplier.Supplier = "NOASSERTION"
+ pkg.PackageSupplier = supplier
+ return nil
+ }
+
+ subKey, subValue, err := ExtractSubs(value, ":")
+ if err != nil {
+ return fmt.Errorf("package supplier must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value)
+ }
+ switch subKey {
+ case "Person", "Organization":
+ supplier.Supplier = subValue
+ supplier.SupplierType = subKey
+ default:
+ return fmt.Errorf("unknown supplier %s", subKey)
+ }
+
+ pkg.PackageSupplier = supplier
+
+ return nil
+}
+
+// given a OriginatorObject, sets the PackageOriginator attribute of the pkg.
+// Args:
+// value: [NOASSERTION | [Person | Organization]: string]
+func setPackageOriginator(pkg *v2_3.Package, value string) error {
+ value = strings.TrimSpace(value)
+ originator := &common.Originator{}
+ if strings.ToUpper(value) == "NOASSERTION" {
+ originator.Originator = "NOASSERTION"
+ pkg.PackageOriginator = originator
+ return nil
+ }
+
+ subKey, subValue, err := ExtractSubs(value, ":")
+ if err != nil {
+ return fmt.Errorf("package Originator must be of the form NOASSERTION or [Person|Organization]: string. found: %s", value)
+ }
+ switch subKey {
+ case "Person", "Organization":
+ originator.Originator = subValue
+ originator.OriginatorType = subKey
+ default:
+ return fmt.Errorf("unknown Originator %s", subKey)
+ }
+
+ pkg.PackageOriginator = originator
+
+ return nil
+}
+
+// validates the uri and sets the location if it is valid
+func setDocumentLocationFromURI(pkg *v2_3.Package, locationURI string) error {
+ switch locationURI {
+ case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL:
+ pkg.PackageDownloadLocation = "NOASSERTION"
+ case SPDX_NONE_CAPS, SPDX_NONE_SMALL:
+ pkg.PackageDownloadLocation = "NONE"
+ default:
+ if !isUriValid(locationURI) {
+ return fmt.Errorf("%s is not a valid uri", locationURI)
+ }
+ pkg.PackageDownloadLocation = locationURI
+ }
+ return nil
+}
+
+// sets the FilesAnalyzed attribute to the given package
+// boolValue is a string of type "true" or "false"
+func setFilesAnalyzed(pkg *v2_3.Package, boolValue string) (err error) {
+ pkg.IsFilesAnalyzedTagPresent = true
+ pkg.FilesAnalyzed, err = boolFromString(boolValue)
+ return err
+}
+
+func (parser *rdfParser2_3) setPackageChecksum(pkg *v2_3.Package, node *gordfParser.Node) error {
+ checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(node)
+ if err != nil {
+ return fmt.Errorf("error getting checksum algorithm and value from %v", node)
+ }
+ if pkg.PackageChecksums == nil {
+ pkg.PackageChecksums = make([]common.Checksum, 0, 1)
+ }
+ switch checksumAlgorithm {
+ case common.MD5, common.SHA1, common.SHA256:
+ pkg.PackageChecksums = append(pkg.PackageChecksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue})
+ default:
+ return fmt.Errorf("unknown checksumAlgorithm %s while parsing a package", checksumAlgorithm)
+ }
+ return nil
+}
diff --git a/rdfloader/parser2v3/parse_package_test.go b/rdfloader/parser2v3/parse_package_test.go
new file mode 100644
index 0000000..cdaaced
--- /dev/null
+++ b/rdfloader/parser2v3/parse_package_test.go
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func Test_setPackageSupplier(t *testing.T) {
+ var err error
+
+ // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true
+ pkg := &v2_3.Package{}
+ err = setPackageSupplier(pkg, "NOASSERTION")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if pkg.PackageSupplier.Supplier != "NOASSERTION" {
+ t.Errorf("PackageSupplier must've been set to NOASSERTION")
+ }
+
+ // TestCase 2: lower-case noassertion must also set the
+ // PackageSupplierNOASSERTION to true.
+ pkg = &v2_3.Package{}
+ err = setPackageSupplier(pkg, "noassertion")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if pkg.PackageSupplier.Supplier != "NOASSERTION" {
+ t.Errorf("PackageSupplier must've been set to NOASSERTION")
+ }
+
+ // TestCase 3: invalid input without colon separator. must raise an error
+ pkg = &v2_3.Package{}
+ input := "string without colon separator"
+ err = setPackageSupplier(pkg, input)
+ if err == nil {
+ t.Errorf("invalid input \"%s\" didn't raise an error", input)
+ }
+
+ // TestCase 4: Valid Person
+ pkg = &v2_3.Package{}
+ personName := "Rishabh Bhatnagar"
+ input = "Person: " + personName
+ err = setPackageSupplier(pkg, input)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if pkg.PackageSupplier.Supplier != personName {
+ t.Errorf("PackageSupplierPerson should be %s. found %s", personName, pkg.PackageSupplier.Supplier)
+ }
+
+ // TestCase 5: Valid Organization
+ pkg = &v2_3.Package{}
+ orgName := "SPDX"
+ input = "Organization: " + orgName
+ err = setPackageSupplier(pkg, input)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if pkg.PackageSupplier.Supplier != orgName {
+ t.Errorf("PackageSupplierPerson should be %s. found %s", orgName, pkg.PackageSupplier.Supplier)
+ }
+
+ // TestCase 6: Invalid EntityType
+ pkg = &v2_3.Package{}
+ input = "InvalidEntity: entity"
+ err = setPackageSupplier(pkg, input)
+ if err == nil {
+ t.Errorf("invalid entity should've raised an error")
+ }
+}
+
+func Test_setPackageOriginator(t *testing.T) {
+ var err error
+
+ // TestCase 1: no assertion must set PackageSupplierNOASSERTION field to true
+ pkg := &v2_3.Package{}
+ err = setPackageOriginator(pkg, "NOASSERTION")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if pkg.PackageOriginator.Originator != "NOASSERTION" {
+ t.Errorf("PackageOriginator must've been set to NOASSERTION")
+ }
+
+ // TestCase 2: lower-case noassertion must also set the
+ // PackageOriginatorNOASSERTION to true.
+ pkg = &v2_3.Package{}
+ err = setPackageOriginator(pkg, "noassertion")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if pkg.PackageOriginator.Originator != "NOASSERTION" {
+ t.Errorf("PackageOriginator must've been set to NOASSERTION")
+ }
+
+ // TestCase 3: invalid input without colon separator. must raise an error
+ pkg = &v2_3.Package{}
+ input := "string without colon separator"
+ err = setPackageOriginator(pkg, input)
+ if err == nil {
+ t.Errorf("invalid input \"%s\" didn't raise an error", input)
+ }
+
+ // TestCase 4: Valid Person
+ pkg = &v2_3.Package{}
+ personName := "Rishabh Bhatnagar"
+ input = "Person: " + personName
+ err = setPackageOriginator(pkg, input)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if pkg.PackageOriginator.Originator != personName {
+ t.Errorf("PackageOriginatorPerson should be %s. found %s", personName, pkg.PackageOriginator.Originator)
+ }
+
+ // TestCase 5: Valid Organization
+ pkg = &v2_3.Package{}
+ orgName := "SPDX"
+ input = "Organization: " + orgName
+ err = setPackageOriginator(pkg, input)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if pkg.PackageOriginator.Originator != orgName {
+ t.Errorf("PackageOriginatorOrganization should be %s. found %s", orgName, pkg.PackageOriginator.Originator)
+ }
+
+ // TestCase 6: Invalid EntityType
+ pkg = &v2_3.Package{}
+ input = "InvalidEntity: entity"
+ err = setPackageOriginator(pkg, input)
+ if err == nil {
+ t.Errorf("invalid entity should've raised an error")
+ }
+}
+
+func Test_rdfParser2_3_setPackageVerificationCode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var pkg *v2_3.Package
+ var err error
+
+ // TestCase 1: invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx.PackageVerificationCode>
+ <spdx:invalidPredicate />
+ <spdx:packageVerificationCodeValue>cbceb8b5689b75a584efe35587b5d41bd48820ce</spdx:packageVerificationCodeValue>
+ <spdx:packageVerificationCodeExcludedFile>./package.spdx</spdx:packageVerificationCodeExcludedFile>
+ </spdx.PackageVerificationCode>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ pkg = &v2_3.Package{}
+ err = parser.setPackageVerificationCode(pkg, node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid predicate, got <nil>")
+ }
+
+ // TestCase 2: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx.PackageVerificationCode>
+ <spdx:packageVerificationCodeValue>cbceb8b5689b75a584efe35587b5d41bd48820ce</spdx:packageVerificationCodeValue>
+ <spdx:packageVerificationCodeExcludedFile>./package.spdx</spdx:packageVerificationCodeExcludedFile>
+ </spdx.PackageVerificationCode>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ pkg = &v2_3.Package{}
+ err = parser.setPackageVerificationCode(pkg, node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ expectedValue := "cbceb8b5689b75a584efe35587b5d41bd48820ce"
+ if pkg.PackageVerificationCode.Value != expectedValue {
+ t.Errorf("expected %v, got %v", expectedValue, pkg.PackageVerificationCode)
+ }
+ expectedExcludedFile := "./package.spdx"
+ if pkg.PackageVerificationCode.ExcludedFiles[0] != expectedExcludedFile {
+ t.Errorf("expected %v, got %v", expectedExcludedFile, pkg.PackageVerificationCode.ExcludedFiles)
+ }
+}
+
+func Test_rdfParser2_3_getPackageExternalRef(t *testing.T) {
+ var extRef *v2_3.PackageExternalReference
+ var err error
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+
+ // TestCase 1: invalid reference category
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_invalid"/>
+ </spdx:ExternalRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ extRef, err = parser.getPackageExternalRef(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid referenceCategory, got <nil>")
+ }
+
+ // TestCase 2: invalid predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalRef>
+ <spdx:unknownPredicate />
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_security"/>
+ </spdx:ExternalRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ extRef, err = parser.getPackageExternalRef(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid referenceCategory, got <nil>")
+ }
+
+ // TestCase 3: valid example (referenceCategory_security)
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_security"/>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:ExternalRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ extRef, err = parser.getPackageExternalRef(node)
+ if err != nil {
+ t.Fatalf("unexpected error parsing a valid example: %v", err)
+ }
+ expectedExtRef := &v2_3.PackageExternalReference{
+ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*",
+ RefType: "http://spdx.org/rdf/references/cpe23Type",
+ Category: "SECURITY",
+ ExternalRefComment: "comment",
+ }
+ if !reflect.DeepEqual(extRef, expectedExtRef) {
+ t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef)
+ }
+
+ // TestCase 4: valid example (referenceCategory_packageManager)
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_packageManager"/>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:ExternalRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ extRef, err = parser.getPackageExternalRef(node)
+ if err != nil {
+ t.Fatalf("unexpected error parsing a valid example: %v", err)
+ }
+ expectedExtRef = &v2_3.PackageExternalReference{
+ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*",
+ RefType: "http://spdx.org/rdf/references/cpe23Type",
+ Category: "PACKAGE-MANAGER",
+ ExternalRefComment: "comment",
+ }
+ if !reflect.DeepEqual(extRef, expectedExtRef) {
+ t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef)
+ }
+
+ // TestCase 5: valid example (referenceCategory_other)
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_other"/>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:ExternalRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ extRef, err = parser.getPackageExternalRef(node)
+ if err != nil {
+ t.Fatalf("unexpected error parsing a valid example: %v", err)
+ }
+ expectedExtRef = &v2_3.PackageExternalReference{
+ Locator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*",
+ RefType: "http://spdx.org/rdf/references/cpe23Type",
+ Category: "OTHER",
+ ExternalRefComment: "comment",
+ }
+ if !reflect.DeepEqual(extRef, expectedExtRef) {
+ t.Errorf("expected: \n%+v\ngot: \n%+v", expectedExtRef, extRef)
+ }
+}
+
+func Test_rdfParser2_3_getPrimaryPackagePurpose(t *testing.T) {
+ // TestCase 1: basic purpose
+ value := getPrimaryPackagePurpose("packagePurpose_container")
+ if value != "CONTAINER" {
+ t.Errorf("expected primary package purpose to be CONTAINER. got: '%s'", value)
+ }
+
+ // TestCase 2: purpose with underscore-to-dash
+ value = getPrimaryPackagePurpose("packagePurpose_operating_system")
+ if value != "OPERATING-SYSTEM" {
+ t.Errorf("expected primary package purpose to be OPERATING-SYSTEM. got: '%s'", value)
+ }
+
+ // TestCase 3: invalid purpose
+ value = getPrimaryPackagePurpose("packagePurpose_invalid")
+ if value != "" {
+ t.Errorf("expected invalid primary package purpose to be empty. got: '%s'", value)
+ }
+}
+
+func Test_rdfParser2_3_getPackageFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var err error
+
+ // TestCase 1: invalid elementId
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#upload2">
+ <spdx:name>time-1.9.tar.gz</spdx:name>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(missing SPDXRef- prefix), found %v", err)
+ }
+
+ // TestCase 2: Invalid License Concluded must raise an error:
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/licenses/IPL-3.0"/>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid license), found %v", err)
+ }
+
+ // TestCase 2: Invalid License Declared must raise an error:
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:licenseDeclared rdf:resource="http://spdx.org/licenses/IPL-3.0"/>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid license), found %v", err)
+ }
+
+ // TestCase 3: Invalid ExternalRef
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:externalRef>
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_invalid"/>
+ </spdx:ExternalRef>
+ </spdx:externalRef>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid externalRef), found %v", err)
+ }
+
+ // TestCase 4: invalid file must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:hasFile>
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#item8"/>
+ </spdx:hasFile>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid file), found %v", err)
+ }
+
+ // TestCase 5: invalid predicate must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:hasFiles>
+ <spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#item8"/>
+ </spdx:hasFiles>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid predicate), found %v", err)
+ }
+
+ // TestCase 6: invalid annotation must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:unknownAttribute />
+ </spdx:Annotation>
+ </spdx:annotation>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid annotation), found %v", err)
+ }
+
+ // TestCase 6: invalid homepage must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <doap:homepage>u r i</doap:homepage>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error(invalid homepage uri), found %v", err)
+ }
+
+ // TestCase 7: Package tag declared more than once should be parsed into a single object's definition
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:name>Test Package</spdx:name>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid package: %v", err)
+ }
+ yetAnotherPkgTriple := gordfParser.Triple{
+ Subject: node,
+ Predicate: &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: SPDX_PACKAGE_FILE_NAME,
+ },
+ Object: &gordfParser.Node{
+ NodeType: gordfParser.LITERAL,
+ ID: "packageFileName",
+ },
+ }
+ parser.nodeStringToTriples[node.String()] = append(parser.nodeStringToTriples[node.String()], &yetAnotherPkgTriple)
+ pkg, err := parser.getPackageFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid package: %v", err)
+ }
+ // validating if all the attributes that spanned over two tags are included in the parsed package.
+ expectedID := "upload2"
+ if string(pkg.PackageSPDXIdentifier) != expectedID {
+ t.Errorf("expected package id: %s, got %s", expectedID, pkg.PackageSPDXIdentifier)
+ }
+ expectedPkgFileName := "packageFileName"
+ if expectedPkgFileName != pkg.PackageFileName {
+ t.Errorf("expected package file name: %s, got %s", expectedPkgFileName, pkg.PackageFileName)
+ }
+ expectedName := "Test Package"
+ if pkg.PackageName != expectedName {
+ t.Errorf("expected package name: %s, got %s", expectedPkgFileName, pkg.PackageName)
+ }
+
+ // TestCase 8: Checking if packages can handle cyclic dependencies:
+ // Simulating a smallest possible cycle: package related to itself.
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:name>Test Package</spdx:name>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes" />
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:versionInfo>1.1.1</spdx:versionInfo>
+ </spdx:Package>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ pkg, err = parser.getPackageFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid package: %v", err)
+ }
+ // checking if both the attributes of the packages are set.
+ expectedVersionInfo := "1.1.1"
+ expectedPackageName := "Test Package"
+ if pkg.PackageVersion != expectedVersionInfo {
+ t.Errorf("Expected %s, found %s", expectedVersionInfo, pkg.PackageVersion)
+ }
+ if pkg.PackageName != expectedPackageName {
+ t.Errorf("Expected %s, found %s", expectedPackageName, pkg.PackageName)
+ }
+
+ // TestCase 9: everything valid
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:name>Test Package</spdx:name>
+ <spdx:versionInfo>1.1.1</spdx:versionInfo>
+ <spdx:packageFileName>time-1.9.tar.gz</spdx:packageFileName>
+ <spdx:supplier>Person: Jane Doe (jane.doe@example.com)</spdx:supplier>
+ <spdx:originator>Organization: SPDX</spdx:originator>
+ <spdx:downloadLocation rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:filesAnalyzed>true</spdx:filesAnalyzed>
+ <spdx:packageVerificationCode>
+ <spdx.PackageVerificationCode>
+ <spdx:packageVerificationCodeValue>cbceb8b5689b75a584efe35587b5d41bd48820ce</spdx:packageVerificationCodeValue>
+ <spdx:packageVerificationCodeExcludedFile>./package.spdx</spdx:packageVerificationCodeExcludedFile>
+ </spdx.PackageVerificationCode>
+ </spdx:packageVerificationCode>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1" />
+ <spdx:checksumValue>75068c26abbed3ad3980685bae21d7202d288317</spdx:checksumValue>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <doap:homepage>http://www.openjena.org/</doap:homepage>
+ <spdx:sourceInfo>uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.</spdx:sourceInfo>
+ <spdx:licenseConcluded>
+ <spdx:DisjunctiveLicenseSet>
+ <spdx:member rdf:resource="http://spdx.org/licenses/Nokia"/>
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ </spdx:DisjunctiveLicenseSet>
+ </spdx:licenseConcluded>
+ <spdx:licenseInfoFromFiles rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:licenseDeclared rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:licenseComments>Other versions available for a commercial license</spdx:licenseComments>
+ <spdx:copyrightText rdf:resource="http://spdx.org/rdf/terms#noassertion" />
+ <spdx:summary> Package for Testing </spdx:summary>
+ <spdx:description> Some tags are taken from other spdx autogenerated files </spdx:description>
+ <rdfs:comment>no comments</rdfs:comment>
+ <spdx:externalRef>
+ <spdx:ExternalRef>
+ <spdx:referenceLocator>cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*</spdx:referenceLocator>
+ <spdx:referenceType>
+ <spdx:ReferenceType rdf:about="http://spdx.org/rdf/references/cpe23Type"/>
+ </spdx:referenceType>
+ <spdx:referenceCategory rdf:resource="http://spdx.org/rdf/terms#referenceCategory_security"/>
+ </spdx:ExternalRef>
+ </spdx:externalRef>
+ <spdx:hasFile rdf:resource="http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#SPDXRef-129" />
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes" />
+ <spdx:relatedSpdxElement rdf:resource="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2" />
+ </spdx:Relationship>
+ </spdx:relationship>
+ <spdx:attributionText>attribution text</spdx:attributionText>
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:annotationDate>2011-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>Package level annotation</rdfs:comment>
+ <spdx:annotator>Person: Package Commenter</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+ </spdx:Annotation>
+ </spdx:annotation>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getPackageFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid package: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_setFileToPackage(t *testing.T) {
+ var pkg *v2_3.Package
+ var file *v2_3.File
+ var parser *rdfParser2_3
+
+ // TestCase 1: setting to a nil files attribute shouldn't panic.
+ parser, _ = parserFromBodyContent(``)
+ pkg = &v2_3.Package{}
+ file = &v2_3.File{}
+ parser.setFileToPackage(pkg, file)
+ if len(pkg.Files) != 1 {
+ t.Errorf("expected given package to have one file after setting, got %d", len(pkg.Files))
+ }
+ if parser.assocWithPackage[file.FileSPDXIdentifier] != true {
+ t.Errorf("given file should've been associated with a package, assocWithPackage is false")
+ }
+}
+
+func Test_rdfParser2_3_setPackageChecksum(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var pkg *v2_3.Package
+ var expectedChecksumValue string
+ var err error
+
+ // TestCase 1: invalid checksum algorithm
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha999"/>
+ </spdx:Checksum>
+ `)
+ pkg = &v2_3.Package{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setPackageChecksum(pkg, node)
+ if err == nil {
+ t.Error("expected an error due to invalid checksum node, got <nil>")
+ }
+
+ // TestCase 1: valid checksum algorithm which is invalid for package
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha384"/>
+ </spdx:Checksum>
+ `)
+ pkg = &v2_3.Package{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setPackageChecksum(pkg, node)
+ if err == nil {
+ t.Error("expected an error due to invalid checksum for package, got <nil>")
+ }
+
+ // TestCase 2: valid checksum (sha1)
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+ </spdx:Checksum>
+ `)
+ pkg = &v2_3.Package{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setPackageChecksum(pkg, node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
+
+ for _, checksum := range pkg.PackageChecksums {
+ switch checksum.Algorithm {
+ case common.SHA1:
+ if checksum.Value != expectedChecksumValue {
+ t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value)
+ }
+ }
+ }
+
+ // TestCase 3: valid checksum (sha256)
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha256"/>
+ </spdx:Checksum>
+ `)
+ pkg = &v2_3.Package{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setPackageChecksum(pkg, node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
+ for _, checksum := range pkg.PackageChecksums {
+ switch checksum.Algorithm {
+ case common.SHA256:
+ if checksum.Value != expectedChecksumValue {
+ t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value)
+ }
+ }
+ }
+
+ // TestCase 4: valid checksum (md5)
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum>
+ <spdx:checksumValue>2fd4e1c67a2d28fced849ee1bb76e7391b93eb12</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md5"/>
+ </spdx:Checksum>
+ `)
+ pkg = &v2_3.Package{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setPackageChecksum(pkg, node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ expectedChecksumValue = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
+ for _, checksum := range pkg.PackageChecksums {
+ switch checksum.Algorithm {
+ case common.MD5:
+ if checksum.Value != expectedChecksumValue {
+ t.Errorf("expected %v, got: %v", expectedChecksumValue, checksum.Value)
+ }
+ }
+ }
+}
+
+func Test_setDocumentLocationFromURI(t *testing.T) {
+ var pkg *v2_3.Package
+ var expectedDocumentLocation, gotDocumentLocation string
+ var inputURI string
+ var err error
+
+ // TestCase 1: NOASSERTION
+ inputURI = SPDX_NOASSERTION_SMALL
+ pkg = &v2_3.Package{}
+ err = setDocumentLocationFromURI(pkg, inputURI)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ expectedDocumentLocation = "NOASSERTION"
+ gotDocumentLocation = pkg.PackageDownloadLocation
+ if expectedDocumentLocation != gotDocumentLocation {
+ t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation)
+ }
+
+ // TestCase 2: NONE
+ inputURI = SPDX_NONE_CAPS
+ pkg = &v2_3.Package{}
+ err = setDocumentLocationFromURI(pkg, inputURI)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ expectedDocumentLocation = "NONE"
+ gotDocumentLocation = pkg.PackageDownloadLocation
+ if expectedDocumentLocation != gotDocumentLocation {
+ t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation)
+ }
+
+ // TestCase 3: valid uri
+ inputURI = "https://www.gnu.org/software/texinfo/"
+ pkg = &v2_3.Package{}
+ err = setDocumentLocationFromURI(pkg, inputURI)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ expectedDocumentLocation = "https://www.gnu.org/software/texinfo/"
+ gotDocumentLocation = pkg.PackageDownloadLocation
+ if expectedDocumentLocation != gotDocumentLocation {
+ t.Errorf("expected: %v, got: %v", expectedDocumentLocation, gotDocumentLocation)
+ }
+
+ // TestCase 3: invalid uri
+ inputURI = " "
+ pkg = &v2_3.Package{}
+ err = setDocumentLocationFromURI(pkg, inputURI)
+ if err == nil {
+ t.Fatalf("expected an error due to invalid uri, got %v", err)
+ }
+}
+
+func Test_setFilesAnalyzed(t *testing.T) {
+ var pkg *v2_3.Package
+ var err error
+
+ // TestCase 1: not a valid bool value:
+ pkg = &v2_3.Package{}
+ err = setFilesAnalyzed(pkg, "no")
+ if err == nil {
+ t.Errorf("expected an error due to invalid bool input, got %v", err)
+ }
+
+ // TestCase 2: valid input
+ pkg = &v2_3.Package{}
+ err = setFilesAnalyzed(pkg, "true")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if !pkg.IsFilesAnalyzedTagPresent {
+ t.Errorf("should've set IsFilesAnalyzedTagPresent, got: %t", pkg.IsFilesAnalyzedTagPresent)
+ }
+ if !pkg.FilesAnalyzed {
+ t.Errorf("expected: %t, got: %t", true, pkg.FilesAnalyzed)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_relationship.go b/rdfloader/parser2v3/parse_relationship.go
new file mode 100644
index 0000000..e182a45
--- /dev/null
+++ b/rdfloader/parser2v3/parse_relationship.go
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// parsing the relationship that exists in the rdf document.
+// Relationship is of type RefA relationType RefB.
+// parsing the relationship appends the relationship to the current document's
+// Relationships Slice.
+func (parser *rdfParser2_3) parseRelationship(triple *gordfParser.Triple) (err error) {
+ reln := v2_3.Relationship{}
+
+ reln.RefA, err = getReferenceFromURI(triple.Subject.ID)
+ if err != nil {
+ return err
+ }
+
+ currState := parser.cache[triple.Object.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[triple.Object.ID] = &nodeState{
+ object: reln,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this relationship node and we needn't parse it again.
+ return nil
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[triple.Object.ID].Color = GREY
+
+ // setting state color to black to indicate when we're done parsing this node.
+ defer func() { parser.cache[triple.Object.ID].Color = BLACK }()
+
+ for _, subTriple := range parser.nodeToTriples(triple.Object) {
+ switch subTriple.Predicate.ID {
+ case SPDX_RELATIONSHIP_TYPE:
+ // cardinality: exactly 1
+ reln.Relationship, err = getRelationshipTypeFromURI(subTriple.Object.ID)
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ continue
+ case SPDX_RELATED_SPDX_ELEMENT:
+ // cardinality: exactly 1
+ // assumes: spdx-element is a uri
+ reln.RefB, err = getReferenceFromURI(subTriple.Object.ID)
+ if err != nil {
+ return err
+ }
+
+ relatedSpdxElementTriples := parser.nodeToTriples(subTriple.Object)
+ if len(relatedSpdxElementTriples) == 0 {
+ continue
+ }
+
+ typeTriples := rdfwriter.FilterTriples(relatedSpdxElementTriples, &subTriple.Object.ID, &RDF_TYPE, nil)
+ if len(typeTriples) != 1 {
+ return fmt.Errorf("expected %s to have exactly one rdf:type triple. found %d triples", subTriple.Object, len(typeTriples))
+ }
+ err = parser.parseRelatedElementFromTriple(&reln, typeTriples[0])
+ if err != nil {
+ return err
+ }
+ case RDFS_COMMENT:
+ // cardinality: max 1
+ reln.RelationshipComment = subTriple.Object.ID
+ default:
+ return fmt.Errorf("unexpected predicate id: %s", subTriple.Predicate.ID)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ parser.doc.Relationships = append(parser.doc.Relationships, &reln)
+ return nil
+}
+
+func (parser *rdfParser2_3) parseRelatedElementFromTriple(reln *v2_3.Relationship, triple *gordfParser.Triple) error {
+ // iterate over relatedElement Type and check which SpdxElement it is.
+ var err error
+ switch triple.Object.ID {
+ case SPDX_FILE:
+ file, err := parser.getFileFromNode(triple.Subject)
+ if err != nil {
+ return fmt.Errorf("error setting a file: %v", err)
+ }
+ reln.RefB = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: file.FileSPDXIdentifier,
+ }
+
+ case SPDX_PACKAGE:
+ pkg, err := parser.getPackageFromNode(triple.Subject)
+ if err != nil {
+ return fmt.Errorf("error setting a package inside a relationship: %v", err)
+ }
+ reln.RefB = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: pkg.PackageSPDXIdentifier,
+ }
+
+ case SPDX_SPDX_ELEMENT:
+ // it shouldn't be associated with any other triple.
+ // it must be a uri reference.
+ reln.RefB, err = ExtractDocElementID(getLastPartOfURI(triple.Subject.ID))
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("undefined relatedElement %s found while parsing relationship", triple.Object.ID)
+ }
+ return nil
+}
+
+// references like RefA and RefB of any relationship
+func getReferenceFromURI(uri string) (common.DocElementID, error) {
+ fragment := getLastPartOfURI(uri)
+ switch strings.ToLower(strings.TrimSpace(fragment)) {
+ case "noassertion", "none":
+ return common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: common.ElementID(strings.ToUpper(fragment)),
+ }, nil
+ }
+ return ExtractDocElementID(fragment)
+}
+
+// note: relationshipType is case sensitive.
+func getRelationshipTypeFromURI(relnTypeURI string) (string, error) {
+ relnTypeURI = strings.TrimSpace(relnTypeURI)
+ lastPart := getLastPartOfURI(relnTypeURI)
+ if !strings.HasPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) {
+ return "", fmt.Errorf("relationshipType must start with %s. found %s", PREFIX_RELATIONSHIP_TYPE, lastPart)
+ }
+ lastPart = strings.TrimPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE)
+
+ lastPart = strings.TrimSpace(lastPart)
+ for _, validRelationshipType := range AllRelationshipTypes() {
+ if lastPart == validRelationshipType {
+ return lastPart, nil
+ }
+ }
+ return "", fmt.Errorf("unknown relationshipType: '%s'", lastPart)
+}
diff --git a/rdfloader/parser2v3/parse_relationship_test.go b/rdfloader/parser2v3/parse_relationship_test.go
new file mode 100644
index 0000000..9c4ddff
--- /dev/null
+++ b/rdfloader/parser2v3/parse_relationship_test.go
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func Test_getReferenceFromURI(t *testing.T) {
+ // TestCase 1: noassertion uri
+ ref, err := getReferenceFromURI(SPDX_NOASSERTION_CAPS)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ref.DocumentRefID != "" {
+ t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID)
+ }
+ if ref.ElementRefID != "NOASSERTION" {
+ t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NOASSERTION")
+ }
+
+ // TestCase 2: NONE uri
+ ref, err = getReferenceFromURI(SPDX_NONE_CAPS)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ref.DocumentRefID != "" {
+ t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID)
+ }
+ if ref.ElementRefID != "NONE" {
+ t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "NONE")
+ }
+
+ // TestCase 3: Valid URI
+ ref, err = getReferenceFromURI(NS_SPDX + "SPDXRef-item1")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if ref.DocumentRefID != "" {
+ t.Errorf("reference's documentRefID should've been empty, found %s", ref.DocumentRefID)
+ }
+ if ref.ElementRefID != "item1" {
+ t.Errorf("mismatching elementRefID. Found %s, expected %s", ref.ElementRefID, "item1")
+ }
+
+ // TestCase 3: Invalid URI
+ _, err = getReferenceFromURI(NS_SPDX + "item1")
+ if err == nil {
+ t.Errorf("should've raised an error for invalid input")
+ }
+}
+
+func Test_getRelationshipTypeFromURI(t *testing.T) {
+ // TestCase 1: valid relationshipType
+ relnType := "expandedFromArchive"
+ op, err := getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType)
+ if err != nil {
+ t.Errorf("error getting relationship type from a valid input")
+ }
+ if op != relnType {
+ t.Errorf("expected %s, found %s", relnType, op)
+ }
+
+ // TestCase2: invalid relationshipType
+ relnType = "invalidRelationship"
+ _, err = getRelationshipTypeFromURI(NS_SPDX + "relationshipType_" + relnType)
+ if err == nil {
+ t.Errorf("should've raised an error for an invalid input(%s)", relnType)
+ }
+}
+
+func Test_rdfParser2_3_parseRelatedElementFromTriple(t *testing.T) {
+ // TestCase 1: Package as a related element
+ parser, _ := parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln := &v2_3.Relationship{}
+ triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0]
+ err := parser.parseRelatedElementFromTriple(reln, triple)
+ if err != nil {
+ t.Errorf("error parsing a valid example")
+ }
+ expectedRefA := common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "",
+ }
+ if !reflect.DeepEqual(expectedRefA, reln.RefA) {
+ t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA)
+ }
+ expectedRefB := common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "Saxon",
+ }
+ if !reflect.DeepEqual(expectedRefB, reln.RefB) {
+ t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB)
+ }
+
+ // TestCase 3: invalid package as a relatedElement
+ parser, _ = parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln = &v2_3.Relationship{}
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_PACKAGE)[0]
+ err = parser.parseRelatedElementFromTriple(reln, triple)
+ if err == nil {
+ t.Errorf("expected an error due to invalid Package id, got %v", err)
+ }
+
+ // TestCase 4: valid File as a related element
+ parser, _ = parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln = &v2_3.Relationship{}
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0]
+ err = parser.parseRelatedElementFromTriple(reln, triple)
+ if err != nil {
+ t.Errorf("error parsing a valid example")
+ }
+ expectedRefA = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "",
+ }
+ if !reflect.DeepEqual(expectedRefA, reln.RefA) {
+ t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA)
+ }
+ expectedRefB = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "Saxon",
+ }
+ if !reflect.DeepEqual(expectedRefB, reln.RefB) {
+ t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB)
+ }
+
+ // TestCase 5: invalid File as a relatedElement
+ parser, _ = parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln = &v2_3.Relationship{}
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0]
+ err = parser.parseRelatedElementFromTriple(reln, triple)
+ if err == nil {
+ t.Errorf("expected an error while parsing an invalid File, got %v", err)
+ }
+
+ // TestCase 6: valid SpdxElement as a related element
+ parser, _ = parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:SpdxElement rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln = &v2_3.Relationship{}
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0]
+ err = parser.parseRelatedElementFromTriple(reln, triple)
+ if err != nil {
+ t.Errorf("error parsing a valid example")
+ }
+ expectedRefA = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "",
+ }
+ if !reflect.DeepEqual(expectedRefA, reln.RefA) {
+ t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA)
+ }
+ expectedRefB = common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "File",
+ }
+ if !reflect.DeepEqual(expectedRefB, reln.RefB) {
+ t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB)
+ }
+
+ // TestCase 7: invalid SpdxElement as a related element
+ parser, _ = parserFromBodyContent(`
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:SpdxElement rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-:File"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ `)
+ reln = &v2_3.Relationship{}
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_SPDX_ELEMENT)[0]
+ err = parser.parseRelatedElementFromTriple(reln, triple)
+ if err == nil {
+ t.Errorf("expected an error due to invalid documentId for SpdxElement, got %v", err)
+ }
+}
+
+func Test_rdfParser2_3_parseRelationship(t *testing.T) {
+ // TestCase 1: invalid RefA
+ parser, _ := parserFromBodyContent(`
+ <spdx:File>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err := parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("should've raised an error due to invalid RefA")
+ }
+
+ // TestCase 3: invalid RefB
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("should've raised an error due to invalid RefB")
+ }
+
+ // TestCase 3: more than one typeTriple for relatedElement
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ <spdx:relatedSpdxElement>
+ <spdx:File/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("should've raised an error due to more than one type triples")
+ }
+
+ // TestCase 4: undefined relatedSpdxElement
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Unknown rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("should've raised an error due to unknown relatedElement, got %v", err)
+ }
+
+ // TestCase 6: relatedElement associated with more than one type
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("expected an error due to invalid relatedElement, got %v", err)
+ }
+
+ // TestCase 5: unknown predicate inside a relationship
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relatedSpdxElement>
+ <spdx:Unknown rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ <spdx:unknownPredicate/>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err == nil {
+ t.Errorf("should've raised an error due to unknown predicate in a relationship")
+ }
+
+ // TestCase 8: Recursive relationships mustn't raise any error:
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship rdf:about="#SPDXRef-reln">
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon">
+ <spdx:relationship>
+ <spdx:Relationship rdf:about="#SPDXRef-reln">
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File"/>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:Package>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err != nil {
+ t.Errorf("error parsing a valid example")
+ }
+
+ // TestCase 7: completely valid example:
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon"/>
+ </spdx:relatedSpdxElement>
+ <rdfs:comment>comment</rdfs:comment>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err != nil {
+ t.Errorf("unexpected error parsing a valid relationship: %v", err)
+ }
+ // validating parsed attributes
+ if len(parser.doc.Relationships) != 1 {
+ t.Errorf("after parsing a valid relationship, doc should've had 1 relationship, found %d", len(parser.doc.Relationships))
+ }
+ reln := parser.doc.Relationships[0]
+ expectedRelnType := "describes"
+ if reln.Relationship != expectedRelnType {
+ t.Errorf("expected %s, found %s", expectedRelnType, reln.Relationship)
+ }
+ expectedRefA := common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "File",
+ }
+ if !reflect.DeepEqual(expectedRefA, reln.RefA) {
+ t.Errorf("expected %+v, found %+v", expectedRefA, reln.RefA)
+ }
+ expectedRefB := common.DocElementID{
+ DocumentRefID: "",
+ ElementRefID: "Saxon",
+ }
+ if !reflect.DeepEqual(expectedRefB, reln.RefB) {
+ t.Errorf("expected %+v, found %+v", expectedRefB, reln.RefB)
+ }
+ expectedComment := "comment"
+ if reln.RelationshipComment != expectedComment {
+ t.Errorf("expected %v, found %v", expectedComment, reln.RelationshipComment)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_review.go b/rdfloader/parser2v3/parse_review.go
new file mode 100644
index 0000000..c1c8b02
--- /dev/null
+++ b/rdfloader/parser2v3/parse_review.go
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func (parser *rdfParser2_3) setReviewFromNode(reviewedNode *gordfParser.Node) error {
+ review := v2_3.Review{}
+ for _, triple := range parser.nodeToTriples(reviewedNode) {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ continue
+ case RDFS_COMMENT:
+ // cardinality: max 1
+ review.ReviewComment = triple.Object.ID
+ case SPDX_REVIEW_DATE:
+ // cardinality: exactly 1
+ review.ReviewDate = triple.Object.ID
+ case SPDX_REVIEWER:
+ // cardinality: max 1
+ var err error
+ review.ReviewerType, review.Reviewer, err = ExtractSubs(triple.Object.ID, ":")
+ if err != nil {
+ return fmt.Errorf("error parsing reviewer: %v", err)
+ }
+ default:
+ return fmt.Errorf("unknown predicate %v for review triples", triple.Predicate)
+ }
+ }
+ parser.doc.Reviews = append(parser.doc.Reviews, &review)
+ return nil
+}
diff --git a/rdfloader/parser2v3/parse_review_test.go b/rdfloader/parser2v3/parse_review_test.go
new file mode 100644
index 0000000..18818aa
--- /dev/null
+++ b/rdfloader/parser2v3/parse_review_test.go
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+)
+
+func Test_rdfParser2_3_setReviewFromNode(t *testing.T) {
+ // TestCase 1: unknown predicate must raise an error
+ parser, _ := parserFromBodyContent(`
+ <spdx:Review>
+ <rdfs:comment>Another example reviewer.</rdfs:comment>
+ <spdx:reviewDate>2011-03-13T00:00:00Z</spdx:reviewDate>
+ <spdx:reviewer>Person: Suzanne Reviewer</spdx:reviewer>
+ <spdx:unknown />
+ </spdx:Review>
+ `)
+ reviewNode := parser.gordfParserObj.Triples[0].Subject
+ err := parser.setReviewFromNode(reviewNode)
+ if err == nil {
+ t.Errorf("unknown predicate should've elicit an error")
+ }
+
+ // TestCase 2: wrong reviewer format must raise an error
+ parser, _ = parserFromBodyContent(`
+ <spdx:Review>
+ <rdfs:comment>Another example reviewer.</rdfs:comment>
+ <spdx:reviewDate>2011-03-13T00:00:00Z</spdx:reviewDate>
+ <spdx:reviewer>Suzanne Reviewer</spdx:reviewer>
+ </spdx:Review>
+ `)
+ reviewNode = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setReviewFromNode(reviewNode)
+ if err == nil {
+ t.Errorf("incorrect should've elicit an error")
+ }
+
+ // TestCase 3: valid input
+ parser, _ = parserFromBodyContent(`
+ <spdx:Review>
+ <rdfs:comment>Another example reviewer.</rdfs:comment>
+ <spdx:reviewDate>2011-03-13T00:00:00Z</spdx:reviewDate>
+ <spdx:reviewer>Person: Suzanne</spdx:reviewer>
+ </spdx:Review>
+ `)
+ reviewNode = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setReviewFromNode(reviewNode)
+ if err != nil {
+ t.Errorf("error parsing a valid node")
+ }
+ n := len(parser.doc.Reviews)
+ if n != 1 {
+ t.Errorf("expected doc to have 1 review, found %d", n)
+ }
+ review := parser.doc.Reviews[0]
+ expectedComment := "Another example reviewer."
+ if review.ReviewComment != expectedComment {
+ t.Errorf("expected: %v, found: %s", expectedComment, review.ReviewComment)
+ }
+ expectedDate := "2011-03-13T00:00:00Z"
+ if review.ReviewDate != expectedDate {
+ t.Errorf("expected %s, found %s", expectedDate, review.ReviewDate)
+ }
+ expectedReviewer := "Suzanne"
+ if review.Reviewer != expectedReviewer {
+ t.Errorf("expected %s, found %s", expectedReviewer, review.Reviewer)
+ }
+ expectedReviewerType := "Person"
+ if review.ReviewerType != expectedReviewerType {
+ t.Errorf("expected %s, found %s", expectedReviewerType, review.ReviewerType)
+ }
+}
diff --git a/rdfloader/parser2v3/parse_snippet_info.go b/rdfloader/parser2v3/parse_snippet_info.go
new file mode 100644
index 0000000..90da873
--- /dev/null
+++ b/rdfloader/parser2v3/parse_snippet_info.go
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+ "strconv"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// Snippet Information
+// Cardinality: Optional, Many
+func (parser *rdfParser2_3) getSnippetInformationFromNode2_3(node *gordfParser.Node) (si *v2_3.Snippet, err error) {
+ si = &v2_3.Snippet{}
+
+ err = setSnippetID(node.ID, si)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, siTriple := range parser.nodeToTriples(node) {
+ switch siTriple.Predicate.ID {
+ case RDF_TYPE:
+ // cardinality: exactly 1
+ case SPDX_SNIPPET_FROM_FILE:
+ // cardinality: exactly 1
+ // file which is associated with the snippet
+ _, err := parser.getFileFromNode(siTriple.Object)
+ if err != nil {
+ return nil, err
+ }
+ docElemID, err := ExtractDocElementID(getLastPartOfURI(siTriple.Object.ID))
+ si.SnippetFromFileSPDXIdentifier = docElemID.ElementRefID
+ case SPDX_RANGE:
+ // cardinality: min 1
+ err = parser.setSnippetRangeFromNode(siTriple.Object, si)
+ if err != nil {
+ return nil, err
+ }
+ case SPDX_LICENSE_INFO_IN_SNIPPET:
+ // license info in snippet can be NONE, NOASSERTION or SimpleLicensingInfo
+ // using AnyLicenseInfo because it can redirect the request and
+ // can handle NONE & NOASSERTION
+ var anyLicense AnyLicenseInfo
+ anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing license info in snippet: %v", err)
+ }
+ si.LicenseInfoInSnippet = append(si.LicenseInfoInSnippet, anyLicense.ToLicenseString())
+ case SPDX_NAME:
+ si.SnippetName = siTriple.Object.ID
+ case SPDX_COPYRIGHT_TEXT:
+ si.SnippetCopyrightText = siTriple.Object.ID
+ case SPDX_LICENSE_COMMENTS:
+ si.SnippetLicenseComments = siTriple.Object.ID
+ case RDFS_COMMENT:
+ si.SnippetComment = siTriple.Object.ID
+ case SPDX_LICENSE_CONCLUDED:
+ var anyLicense AnyLicenseInfo
+ anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing license info in snippet: %v", err)
+ }
+ si.SnippetLicenseConcluded = anyLicense.ToLicenseString()
+ default:
+ return nil, fmt.Errorf("unknown predicate %v", siTriple.Predicate.ID)
+ }
+ }
+ return si, nil
+}
+
+// given is the id of the file, sets the snippet to the file in parser.
+func (parser *rdfParser2_3) setSnippetToFileWithID(snippet *v2_3.Snippet, fileID common.ElementID) error {
+ if parser.files[fileID] == nil {
+ return fmt.Errorf("snippet refers to an undefined file with ID: %s", fileID)
+ }
+
+ // initializing snippet of the files if it is not defined already
+ if parser.files[fileID].Snippets == nil {
+ parser.files[fileID].Snippets = map[common.ElementID]*v2_3.Snippet{}
+ }
+
+ // setting the snippet to the file.
+ parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet
+
+ return nil
+}
+
+func (parser *rdfParser2_3) setSnippetRangeFromNode(node *gordfParser.Node, si *v2_3.Snippet) error {
+ // for a range object, we can have only 3 associated triples:
+ // node -> RDF_TYPE -> Object
+ // node -> startPointer -> Object
+ // node -> endPointer -> Object
+ associatedTriples := parser.nodeToTriples(node)
+ if len(associatedTriples) != 3 {
+ return fmt.Errorf("range should be associated with exactly 3 triples, got %d", len(associatedTriples))
+ }
+
+ // Triple 1: Predicate=RDF_TYPE
+ typeTriple := rdfwriter.FilterTriples(associatedTriples, &node.ID, &RDF_TYPE, nil)
+ if len(typeTriple) != 1 {
+ // we had 3 associated triples. out of which 2 is start and end pointer,
+ // if we do not have the rdf:type triple as the third one,
+ // we have either extra or undefined predicate.
+ return fmt.Errorf("every object node must be associated with exactly one rdf:type triple, found: %d", len(typeTriple))
+ }
+
+ // getting start pointer
+ startPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_START_POINTER, nil)
+ if len(startPointerTriples) != 1 {
+ return fmt.Errorf("range object must be associated with exactly 1 startPointer, got %d", len(startPointerTriples))
+ }
+ startRangeType, start, err := parser.getPointerFromNode(startPointerTriples[0].Object, si)
+ if err != nil {
+ return fmt.Errorf("error parsing startPointer: %v", err)
+ }
+
+ // getting end pointer
+ endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil)
+ if len(startPointerTriples) != 1 {
+ return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples))
+ }
+ endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si)
+ if err != nil {
+ return fmt.Errorf("error parsing endPointer: %v", err)
+ }
+
+ // return error when start and end pointer type is not same.
+ if startRangeType != endRangeType {
+ return fmt.Errorf("start and end range type doesn't match")
+ }
+
+ si.Ranges = []common.SnippetRange{{
+ StartPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier},
+ EndPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier},
+ }}
+
+ if startRangeType == LINE_RANGE {
+ si.Ranges[0].StartPointer.LineNumber = start
+ si.Ranges[0].EndPointer.LineNumber = end
+ } else {
+ si.Ranges[0].StartPointer.Offset = start
+ si.Ranges[0].EndPointer.Offset = end
+ }
+ return nil
+}
+
+func (parser *rdfParser2_3) getPointerFromNode(node *gordfParser.Node, si *v2_3.Snippet) (rt RangeType, number int, err error) {
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case RDF_TYPE:
+ case PTR_REFERENCE:
+ err = parser.parseRangeReference(triple.Object, si)
+ case PTR_OFFSET:
+ number, err = strconv.Atoi(triple.Object.ID)
+ rt = BYTE_RANGE
+ case PTR_LINE_NUMBER:
+ number, err = strconv.Atoi(triple.Object.ID)
+ rt = LINE_RANGE
+ default:
+ err = fmt.Errorf("undefined predicate (%s) for a pointer", triple.Predicate)
+ }
+ if err != nil {
+ return
+ }
+ }
+ if rt == "" {
+ err = fmt.Errorf("range type not defined for a pointer")
+ }
+ return
+}
+
+func (parser *rdfParser2_3) parseRangeReference(node *gordfParser.Node, snippet *v2_3.Snippet) error {
+ // reference is supposed to be either a resource reference to an already
+ // defined or a new file. Unfortunately, I didn't find field where this can be set in the tools-golang data model.
+ // todo: set this reference to the snippet
+ associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
+ if len(associatedTriples) == 0 {
+ return nil
+ }
+ _, err := parser.getFileFromNode(node)
+ if err != nil {
+ return fmt.Errorf("error parsing a new file in a reference: %v", err)
+ }
+ return nil
+}
+
+func setSnippetID(uri string, si *v2_3.Snippet) (err error) {
+ fragment := getLastPartOfURI(uri)
+ si.SnippetSPDXIdentifier, err = ExtractElementID(fragment)
+ if err != nil {
+ return fmt.Errorf("error setting snippet identifier: %v", uri)
+ }
+ return nil
+}
diff --git a/rdfloader/parser2v3/parse_snippet_info_test.go b/rdfloader/parser2v3/parse_snippet_info_test.go
new file mode 100644
index 0000000..2147586
--- /dev/null
+++ b/rdfloader/parser2v3/parse_snippet_info_test.go
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func Test_rdfParser2_3_getSnippetInformationFromTriple2_3(t *testing.T) {
+ var err error
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+
+ // TestCase 1: invalid snippet id:
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#Snippet">
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid, got %v", err)
+ }
+
+ // TestCase 2: Invalid LicenseInfoInSnippet
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:licenseInfoInSnippet rdf:resource="http://spdx.org/licenses/Unknown"/>
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid licenseInfoInSnippet, got %v", err)
+ }
+
+ // TestCase 3: Invalid range.
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:range>
+ <spdx:StartEndPointer>
+ <spdx:unknownTag />
+ </spdx:StartEndPointer>
+ </spdx:range>
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid range, got %v", err)
+ }
+
+ // TestCase 3: invalid file in snippetFromFile
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:snippetFromFile>
+ <spdx:File rdf:resource="http://anupam-VirtualBox/spdx.rdf#item8" />
+ </spdx:snippetFromFile>
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid snippetFromFile, got %v", err)
+ }
+
+ // TestCase 4: unknown predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:unknownPredicate />
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid predicate, got %v", err)
+ }
+
+ // TestCase 5: invalid license concluded:
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/licenses/Unknown"/>
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid licenseConcluded, got %v", err)
+ }
+
+ // TestCase 6: everything valid:
+ parser, _ = parserFromBodyContent(`
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:snippetFromFile>
+ <spdx:File rdf:about="#SPDXRef-File" />
+ </spdx:snippetFromFile>
+ <spdx:range>
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>420</j.0:lineNumber>
+ </j.0:LineCharPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>310</j.0:lineNumber>
+ </j.0:LineCharPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ </spdx:range>
+ <spdx:licenseInfoInSnippet rdf:resource="http://spdx.org/rdf/terms#noassertion"/>
+ <spdx:name>snippet test</spdx:name>
+ <spdx:copyrightText>test</spdx:copyrightText>
+ <spdx:licenseComments>comments</spdx:licenseComments>
+ <rdfs:comment>comments</rdfs:comment>
+ <spdx:licenseConcluded rdf:resource="http://spdx.org/rdf/terms#noassertion"/>
+ </spdx:Snippet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getSnippetInformationFromNode2_3(node)
+ if err != nil {
+ t.Fatalf("error parsing a valid example: %v", err)
+ }
+}
+
+func Test_setSnippetID(t *testing.T) {
+ // TestCase 1: invalid input (empty)
+ err := setSnippetID("", &v2_3.Snippet{})
+ if err == nil {
+ t.Errorf("should've raised an error for empty input")
+ }
+
+ // TestCase 2: valid input
+ si := &v2_3.Snippet{}
+ err = setSnippetID("http://spdx.org/spdxdocs/spdx-example#SPDXRef-Snippet", si)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if si.SnippetSPDXIdentifier != "Snippet" {
+ t.Errorf("expected: %s, found: %s", "Snippet", si.SnippetSPDXIdentifier)
+ }
+}
+
+func Test_rdfParser2_3_parseRangeReference(t *testing.T) {
+ var err error
+ var node *gordfParser.Node
+ var parser *rdfParser2_3
+ var si *v2_3.Snippet
+
+ // TestCase 1: ResourceLiteral node without a new file shouldn't raise any error.
+ si = &v2_3.Snippet{}
+ parser, _ = parserFromBodyContent(``)
+ node = &gordfParser.Node{
+ NodeType: gordfParser.RESOURCELITERAL,
+ ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource",
+ }
+ err = parser.parseRangeReference(node, si)
+ if err != nil {
+ t.Errorf("error parsing a valid node: %v", err)
+ }
+
+ // TestCase 2: invalid file in the reference should raise an error
+ si = &v2_3.Snippet{}
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#DoapSource">
+ <spdx:fileName> test file </spdx:fileName>
+ </spdx:File>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseRangeReference(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to invalid file in the range reference, got %v", err)
+ }
+
+ // TestCase 3: A valid reference must set the file to the files map of the parser.
+ si = &v2_3.Snippet{}
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource">
+ <spdx:fileName> test file </spdx:fileName>
+ </spdx:File>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseRangeReference(node, si)
+ if err != nil {
+ t.Errorf("error parsing a valid input: %v", err)
+ }
+ if len(parser.files) != 1 {
+ t.Errorf("expected parser.files to have 1 file, found %d", len(parser.files))
+ }
+}
+
+func Test_rdfParser2_3_getPointerFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var si *v2_3.Snippet
+ var err error
+ var rt RangeType
+ var number int
+
+ // TestCase 1: invalid number in the offset field must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <j.0:startPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="#SPDXRef-DoapSource"/>
+ <j.0:offset>3-10</j.0:offset>
+ </j.0:LineCharPointer>
+ </j.0:startPointer>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getPointerFromNode(node, si)
+ if err == nil {
+ t.Errorf("should've raised an error parsing invalid offset, got %v", err)
+ }
+
+ // TestCase 2: invalid number in the lineNumber field must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="#SPDXRef-DoapSource"/>
+ <j.0:offset>3-10</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getPointerFromNode(node, si)
+ if err == nil {
+ t.Errorf("should've raised an error parsing invalid offset, got %v", err)
+ }
+
+ // TestCase 3: invalid predicate in the pointer field
+ parser, _ = parserFromBodyContent(`
+ <j.0:ByteOffsetPointer>
+ <spdx:invalidTag />
+ <j.0:reference rdf:resource="#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>3-10</j.0:lineNumber>
+ </j.0:ByteOffsetPointer>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getPointerFromNode(node, si)
+ if err == nil {
+ t.Errorf("should've raised an error parsing invalid predicate, got %v", err)
+ }
+
+ // TestCase 4: No range type defined must also raise an error
+ parser, _ = parserFromBodyContent(`
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="#SPDXRef-DoapSource"/>
+ </j.0:ByteOffsetPointer>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, _, err = parser.getPointerFromNode(node, si)
+ if err == nil {
+ t.Errorf("should've raised an error parsing invalid rangeType, got %v", err)
+ }
+
+ // TestCase 5: valid example
+ parser, _ = parserFromBodyContent(`
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ rt, number, err = parser.getPointerFromNode(node, si)
+ if err != nil {
+ t.Fatalf("unexpected error parsing a valid node: %v", err)
+ }
+ if rt != BYTE_RANGE {
+ t.Errorf("expected: %s, got: %s", BYTE_RANGE, rt)
+ }
+ if number != 310 {
+ t.Errorf("expected: %d, got: %d", 310, number)
+ }
+}
+
+func Test_rdfParser2_3_setSnippetRangeFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+ var si *v2_3.Snippet
+ var node *gordfParser.Node
+
+ // TestCase 1: range with less one pointer less must raise an error
+ // (end-pointer missing in the range)
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:LineCharPointer>
+ </j.0:startPointer>
+ </j.0:StartEndPointer>
+
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to missing end pointer, got %v", err)
+ }
+
+ // TestCase 2: triples with 0 or more than one type-triple
+ parser, _ = parserFromBodyContent(`
+
+ <j.0:StartEndPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ <j.0:startPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:startPointer>
+ </j.0:StartEndPointer>
+
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ dummyTriple := parser.gordfParserObj.Triples[0]
+ // resetting the node to be associated with 3 triples which will have
+ // rdf:type triple either thrice or 0 times.
+ parser.nodeStringToTriples[node.String()] = []*gordfParser.Triple{
+ dummyTriple, dummyTriple, dummyTriple,
+ }
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to invalid rdf:type triples, got %v", err)
+ }
+
+ // TestCase 3: triples with 0 startPointer
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ <j.0:endPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:LineCharPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to missing start pointer, got %v", err)
+ }
+
+ // TestCase 4: triples with 0 endPointer
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ <j.0:endPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:LineCharPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to missing end pointer, got %v", err)
+ }
+
+ // TestCase 5: error parsing start pointer must be propagated to the range
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>42.0</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to invalid start pointer, got %v", err)
+ }
+
+ // TestCase 6: error parsing end pointer must be propagated to the range
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>31+0</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to invalid end pointer, got %v", err)
+ }
+
+ // TestCase 7: mismatching start and end pointer must also raise an error.
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>310</j.0:lineNumber>
+ </j.0:LineCharPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err == nil {
+ t.Errorf("expected an error due to mismatching start and end pointers, got %v", err)
+ }
+
+ // TestCase 8: everything valid(byte_range):
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>420</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:ByteOffsetPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:offset>310</j.0:offset>
+ </j.0:ByteOffsetPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ // TestCase 9: everything valid(line_range):
+ parser, _ = parserFromBodyContent(`
+ <j.0:StartEndPointer>
+ <j.0:startPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>420</j.0:lineNumber>
+ </j.0:LineCharPointer>
+ </j.0:startPointer>
+ <j.0:endPointer>
+ <j.0:LineCharPointer>
+ <j.0:reference rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
+ <j.0:lineNumber>310</j.0:lineNumber>
+ </j.0:LineCharPointer>
+ </j.0:endPointer>
+ </j.0:StartEndPointer>
+ `)
+ si = &v2_3.Snippet{}
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.setSnippetRangeFromNode(node, si)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_setSnippetToFileWithID(t *testing.T) {
+ var parser *rdfParser2_3
+ var fileId common.ElementID
+ var si *v2_3.Snippet
+ var file *v2_3.File
+ var err error
+
+ // TestCase 1: file id which is not associated with any file must raise an error.
+ parser, _ = parserFromBodyContent("")
+ si = &v2_3.Snippet{}
+ err = parser.setSnippetToFileWithID(si, fileId)
+ if err == nil {
+ t.Errorf("expected an error saying undefined file")
+ }
+
+ // TestCase 2: file exists, but snippet of the file doesn't ( it mustn't raise any error )
+ fileId = common.ElementID("File1")
+ file = &v2_3.File{
+ FileSPDXIdentifier: fileId,
+ }
+ parser.files[fileId] = file
+ file.Snippets = nil // nil snippets
+ err = parser.setSnippetToFileWithID(si, fileId)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if len(file.Snippets) != 1 {
+ t.Errorf("expected file to have 1 snippet, got %d", len(file.Snippets))
+ }
+}
diff --git a/rdfloader/parser2v3/parse_spdx_document.go b/rdfloader/parser2v3/parse_spdx_document.go
new file mode 100644
index 0000000..abf89f9
--- /dev/null
+++ b/rdfloader/parser2v3/parse_spdx_document.go
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+func (parser *rdfParser2_3) parseSpdxDocumentNode(spdxDocNode *gordfParser.Node) (err error) {
+ // shorthand for document's creation info.
+ ci := parser.doc.CreationInfo
+
+ // parse the document header information (SPDXID and document namespace)
+ // the Subject.ID is of type baseURI#spdxID
+ baseUri, offset, err := ExtractSubs(spdxDocNode.ID, "#")
+ if err != nil {
+ return err
+ }
+ parser.doc.DocumentNamespace = baseUri // 2.5
+ parser.doc.SPDXIdentifier = common.ElementID(offset) // 2.3
+
+ // parse other associated triples.
+ for _, subTriple := range parser.nodeToTriples(spdxDocNode) {
+ objectValue := subTriple.Object.ID
+ switch subTriple.Predicate.ID {
+ case RDF_TYPE:
+ continue
+ case SPDX_SPEC_VERSION: // 2.1: specVersion
+ // cardinality: exactly 1
+ parser.doc.SPDXVersion = objectValue
+ case SPDX_DATA_LICENSE: // 2.2: dataLicense
+ // cardinality: exactly 1
+ dataLicense, err := parser.getAnyLicenseFromNode(subTriple.Object)
+ if err != nil {
+ return err
+ }
+ parser.doc.DataLicense = dataLicense.ToLicenseString()
+ case SPDX_NAME: // 2.4: DocumentName
+ // cardinality: exactly 1
+ parser.doc.DocumentName = objectValue
+ case SPDX_EXTERNAL_DOCUMENT_REF: // 2.6: externalDocumentReferences
+ // cardinality: min 0
+ var extRef v2_3.ExternalDocumentRef
+ extRef, err = parser.getExternalDocumentRefFromNode(subTriple.Object)
+ if err != nil {
+ return err
+ }
+ parser.doc.ExternalDocumentReferences = append(parser.doc.ExternalDocumentReferences, extRef)
+ case SPDX_CREATION_INFO: // 2.7 - 2.10:
+ // cardinality: exactly 1
+ err = parser.parseCreationInfoFromNode(ci, subTriple.Object)
+ case RDFS_COMMENT: // 2.11: Document Comment
+ // cardinality: max 1
+ parser.doc.DocumentComment = objectValue
+ case SPDX_REVIEWED: // reviewed:
+ // cardinality: min 0
+ err = parser.setReviewFromNode(subTriple.Object)
+ case SPDX_DESCRIBES_PACKAGE: // describes Package
+ // cardinality: min 0
+ var pkg *v2_3.Package
+ pkg, err = parser.getPackageFromNode(subTriple.Object)
+ if err != nil {
+ return err
+ }
+ parser.doc.Packages = append(parser.doc.Packages, pkg)
+ case SPDX_HAS_EXTRACTED_LICENSING_INFO: // hasExtractedLicensingInfo
+ // cardinality: min 0
+ extractedLicensingInfo, err := parser.getExtractedLicensingInfoFromNode(subTriple.Object)
+ if err != nil {
+ return fmt.Errorf("error setting extractedLicensingInfo in spdxDocument: %v", err)
+ }
+ othLicense := parser.extractedLicenseToOtherLicense(extractedLicensingInfo)
+ parser.doc.OtherLicenses = append(parser.doc.OtherLicenses, &othLicense)
+ case SPDX_RELATIONSHIP: // relationship
+ // cardinality: min 0
+ err = parser.parseRelationship(subTriple)
+ case SPDX_ANNOTATION: // annotations
+ // cardinality: min 0
+ err = parser.parseAnnotationFromNode(subTriple.Object)
+ default:
+ return fmt.Errorf("invalid predicate while parsing SpdxDocument: %v", subTriple.Predicate)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (parser *rdfParser2_3) getExternalDocumentRefFromNode(node *gordfParser.Node) (edr v2_3.ExternalDocumentRef, err error) {
+ for _, triple := range parser.nodeToTriples(node) {
+ switch triple.Predicate.ID {
+ case SPDX_EXTERNAL_DOCUMENT_ID:
+ // cardinality: exactly 1
+ edr.DocumentRefID = triple.Object.ID
+ case SPDX_SPDX_DOCUMENT:
+ // cardinality: exactly 1
+ // assumption: "spdxDocument" property of an external document
+ // reference is just a uri which doesn't follow a spdxDocument definition
+ edr.URI = triple.Object.ID
+ case SPDX_CHECKSUM:
+ // cardinality: exactly 1
+ alg, checksum, err := parser.getChecksumFromNode(triple.Object)
+ if err != nil {
+ return edr, err
+ }
+ edr.Checksum.Value = checksum
+ edr.Checksum.Algorithm = alg
+ case RDF_TYPE:
+ continue
+ default:
+ return edr, fmt.Errorf("unknown predicate ID (%s) while parsing externalDocumentReference", triple.Predicate.ID)
+ }
+ }
+ return edr, nil
+}
diff --git a/rdfloader/parser2v3/parse_spdx_document_test.go b/rdfloader/parser2v3/parse_spdx_document_test.go
new file mode 100644
index 0000000..f2c0f2f
--- /dev/null
+++ b/rdfloader/parser2v3/parse_spdx_document_test.go
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+)
+
+func Test_rdfParser2_3_getExternalDocumentRefFromNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var err error
+
+ // TestCase 1: invalid checksum
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalDocumentRef>
+ <spdx:externalDocumentId>DocumentRef-spdx-tool-1.2</spdx:externalDocumentId>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha999"/>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <spdx:spdxDocument rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"/>
+ </spdx:ExternalDocumentRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getExternalDocumentRefFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid checksum, found %v", err)
+ }
+
+ // TestCase 2: unknown predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalDocumentRef>
+ <spdx:unknownTag />
+ </spdx:ExternalDocumentRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getExternalDocumentRefFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid predicate, found %v", err)
+ }
+
+ // TestCase 3: valid example
+ parser, _ = parserFromBodyContent(`
+ <spdx:ExternalDocumentRef>
+ <spdx:externalDocumentId>DocumentRef-spdx-tool-1.2</spdx:externalDocumentId>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha256"/>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <spdx:spdxDocument rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"/>
+ </spdx:ExternalDocumentRef>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getExternalDocumentRefFromNode(node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_parseSpdxDocumentNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var node *gordfParser.Node
+ var err error
+
+ // TestCase 1: invalid spdx id of the document
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301/Document"/>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid document id, got %v", err)
+ }
+
+ // TestCase 2: erroneous dataLicense
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:dataLicense rdf:resource="http://spdx.org/rdf/terms#Unknown" />
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid dataLicense, got %v", err)
+ }
+
+ // TestCase 3: invalid external document ref
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:externalDocumentRef>
+ <spdx:ExternalDocumentRef>
+ <spdx:externalDocumentId>DocumentRef-spdx-tool-1.2</spdx:externalDocumentId>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha999"/>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <spdx:spdxDocument rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"/>
+ </spdx:ExternalDocumentRef>
+ </spdx:externalDocumentRef>
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err)
+ }
+
+ // TestCase 4: invalid package
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:describesPackage>
+ <spdx:Package rdf:about="http://www.spdx.org/spdxdocs/8f141b09-1138-4fc5-aecb-fc10d9ac1eed#SPDX-1"/>
+ </spdx:describesPackage>
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid externalDocumentRef, got %v", err)
+ }
+
+ // TestCase 5: error in extractedLicensingInfo
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:hasExtractedLicensingInfo>
+ <spdx:ExtractedLicensingInfo rdf:about="#LicenseRef-Freeware">
+ <spdx:invalidTag />
+ <spdx:licenseId>LicenseRef-Freeware</spdx:licenseId>
+ <spdx:name>freeware</spdx:name>
+ <spdx:extractedText>
+<![CDATA[Software classified as freeware is licensed at no cost and is either fully functional for an unlimited time; or has only basic functions enabled with a fully functional version available commercially or as shareware.[8] In contrast to free software, the author usually restricts one or more rights of the user, including the rights to use, copy, distribute, modify and make derivative works of the software or extract the source code.[1][2][9][10] The software license may impose various additional restrictions on the type of use, e.g. only for personal use, private use, individual use, non-profit use, non-commercial use, academic use, educational use, use in charity or humanitarian organizations, non-military use, use by public authorities or various other combinations of these type of restrictions.[11] For instance, the license may be "free for private, non-commercial use". The software license may also impose various other restrictions, such as restricted use over a network, restricted use on a server, restricted use in a combination with some types of other software or with some hardware devices, prohibited distribution over the Internet other than linking to author's website, restricted distribution without author's consent, restricted number of copies, etc.]]>
+ </spdx:extractedText>
+ </spdx:ExtractedLicensingInfo>
+ </spdx:hasExtractedLicensingInfo>
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err)
+ }
+
+ // TestCase 6: error in annotation
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:unknownAttribute />
+ </spdx:Annotation>
+ </spdx:annotation>
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to invalid extractedLicensingInfo, got %v", err)
+ }
+
+ // TestCase 7: invalid predicate
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:unknownTag />
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to unknown predicate, got %v", err)
+ }
+
+ // TestCase 7: everything valid
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document">
+ <spdx:specVersion>SPDX-2.1</spdx:specVersion>
+ <spdx:dataLicense rdf:resource="http://spdx.org/licenses/CC0-1.0" />
+ <spdx:name>/test/example</spdx:name>
+ <spdx:externalDocumentRef>
+ <spdx:ExternalDocumentRef>
+ <spdx:externalDocumentId>DocumentRef-spdx-tool-1.2</spdx:externalDocumentId>
+ <spdx:checksum>
+ <spdx:Checksum>
+ <spdx:checksumValue>d6a770ba38583ed4bb4525bd96e50461655d2759</spdx:checksumValue>
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+ </spdx:Checksum>
+ </spdx:checksum>
+ <spdx:spdxDocument rdf:resource="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"/>
+ </spdx:ExternalDocumentRef>
+ </spdx:externalDocumentRef>
+ <spdx:creationInfo>
+ <spdx:CreationInfo>
+ <spdx:licenseListVersion>2.6</spdx:licenseListVersion>
+ <spdx:creator>Person: spdx (y)</spdx:creator>
+ <spdx:creator>Organization: </spdx:creator>
+ <spdx:creator>Tool: spdx2</spdx:creator>
+ <spdx:created>2018-08-24T19:55:34Z</spdx:created>
+ </spdx:CreationInfo>
+ </spdx:creationInfo>
+ <rdfs:comment>test</rdfs:comment>
+ <spdx:reviewed>
+ <spdx:Review>
+ <rdfs:comment>Another example reviewer.</rdfs:comment>
+ <spdx:reviewDate>2011-03-13T00:00:00Z</spdx:reviewDate>
+ <spdx:reviewer>Person: Suzanne Reviewer</spdx:reviewer>
+ </spdx:Review>
+ </spdx:reviewed>
+ <spdx:describesPackage>
+ <spdx:Package rdf:about="#SPDXRef-1"/>
+ </spdx:describesPackage>
+ <spdx:hasExtractedLicensingInfo rdf:resource="http://spdx.org/licenses/CC0-1.0"/>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_containedBy"/>
+ <spdx:relatedSpdxElement rdf:resource="http://spdx.org/documents/spdx-toolsv2.1.7-SNAPSHOT#SPDXRef-1"/>
+ <rdfs:comment></rdfs:comment>
+ </spdx:Relationship>
+ </spdx:relationship>
+ <spdx:annotation>
+ <spdx:Annotation>
+ <spdx:annotationDate>2011-01-29T18:30:22Z</spdx:annotationDate>
+ <rdfs:comment>test annotation</rdfs:comment>
+ <spdx:annotator>Person: Rishabh Bhatnagar</spdx:annotator>
+ <spdx:annotationType rdf:resource="http://spdx.org/rdf/terms#annotationType_other"/>
+ </spdx:Annotation>
+ </spdx:annotation>
+ </spdx:SpdxDocument>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ err = parser.parseSpdxDocumentNode(node)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
diff --git a/rdfloader/parser2v3/parser.go b/rdfloader/parser2v3/parser.go
new file mode 100644
index 0000000..961af31
--- /dev/null
+++ b/rdfloader/parser2v3/parser.go
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "errors"
+ "fmt"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ gordfWriter "github.com/spdx/gordf/rdfwriter"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+// returns a new instance of rdfParser2_3 given the gordf object and nodeToTriples mapping
+func NewParser2_3(gordfParserObj *gordfParser.Parser, nodeToTriples map[string][]*gordfParser.Triple) *rdfParser2_3 {
+ parser := rdfParser2_3{
+ gordfParserObj: gordfParserObj,
+ nodeStringToTriples: nodeToTriples,
+ doc: &v2_3.Document{
+ ExternalDocumentReferences: []v2_3.ExternalDocumentRef{},
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{},
+ Files: []*v2_3.File{},
+ OtherLicenses: []*v2_3.OtherLicense{},
+ Relationships: []*v2_3.Relationship{},
+ Annotations: []*v2_3.Annotation{},
+ Reviews: []*v2_3.Review{},
+ },
+ files: map[common.ElementID]*v2_3.File{},
+ assocWithPackage: map[common.ElementID]bool{},
+ cache: map[string]*nodeState{},
+ }
+ return &parser
+}
+
+// main function which takes in a gordfParser and returns
+// a spdxDocument model or the error encountered while parsing it
+func LoadFromGoRDFParser(gordfParserObj *gordfParser.Parser) (*v2_3.Document, error) {
+ // nodeToTriples is a mapping from a node to list of triples.
+ // for every node in the set of subjects of all the triples,
+ // it provides a list of triples that are associated with that subject node.
+ nodeToTriples := gordfWriter.GetNodeToTriples(gordfParserObj.Triples)
+ parser := NewParser2_3(gordfParserObj, nodeToTriples)
+
+ spdxDocumentNode, err := parser.getSpdxDocNode()
+ if err != nil {
+ return nil, err
+ }
+
+ err = parser.parseSpdxDocumentNode(spdxDocumentNode)
+ if err != nil {
+ return nil, err
+ }
+
+ // parsing other root elements
+ for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) {
+ typeTriples := gordfWriter.FilterTriples(gordfParserObj.Triples, &rootNode.ID, &RDF_TYPE, nil)
+ if len(typeTriples) != 1 {
+ return nil, fmt.Errorf("every node must be associated with exactly 1 type Triple. found %d type triples", len(typeTriples))
+ }
+ switch typeTriples[0].Object.ID {
+ case SPDX_SPDX_DOCUMENT_CAPITALIZED:
+ continue // it is already parsed.
+ case SPDX_SNIPPET:
+ snippet, err := parser.getSnippetInformationFromNode2_3(typeTriples[0].Subject)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing a snippet: %v", err)
+ }
+ err = parser.setSnippetToFileWithID(snippet, snippet.SnippetFromFileSPDXIdentifier)
+ if err != nil {
+ return nil, err
+ }
+ // todo: check other root node attributes.
+ default:
+ continue
+ // because in rdf it is quite possible that the root node is an
+ // element that has been used in the some other element as a child
+ }
+ }
+
+ // parsing packages and files sets the files to a files variable which is
+ // associated with the parser and not the document. following method is
+ // necessary to transfer the files which are not set in the packages to the
+ // Files attribute of the document
+ // WARNING: do not relocate following function call. It must be at the end of the function
+ parser.setUnpackagedFiles()
+ return parser.doc, nil
+}
+
+// from the given parser object, returns the SpdxDocument Node defined in the root elements.
+// returns error if the document is associated with no SpdxDocument or
+// associated with more than one SpdxDocument node.
+func (parser *rdfParser2_3) getSpdxDocNode() (node *gordfParser.Node, err error) {
+ /* Possible Questions:
+ 1. why are you traversing the root nodes only? why not directly filter out
+ all the triples with rdf:type=spdx:SpdxDocument?
+ Ans: It is quite possible that the relatedElement or any other attribute
+ to have dependency of another SpdxDocument. In that case, that
+ element will reference the dependency using SpdxDocument tag which will
+ cause false positives when direct filtering is done.
+ */
+ // iterate over root nodes and find the node which has a property of rdf:type=spdx:SpdxDocument
+ var spdxDocNode *gordfParser.Node
+ for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) {
+ typeTriples := gordfWriter.FilterTriples(
+ parser.nodeToTriples(rootNode), // triples
+ &rootNode.ID, // Subject
+ &RDF_TYPE, // Predicate
+ nil, // Object
+ )
+
+ if typeTriples[0].Object.ID == SPDX_SPDX_DOCUMENT_CAPITALIZED {
+ // we found a SpdxDocument Node
+
+ // must be associated with exactly one rdf:type.
+ if len(typeTriples) != 1 {
+ return nil, fmt.Errorf("rootNode (%v) must be associated with exactly one"+
+ " triple of predicate rdf:type, found %d triples", rootNode, len(typeTriples))
+ }
+
+ // checking if we've already found a node and it is not same as the current one.
+ if spdxDocNode != nil && spdxDocNode.ID != typeTriples[0].Subject.ID {
+ return nil, fmt.Errorf("found more than one SpdxDocument Node (%v and %v)", spdxDocNode, typeTriples[0].Subject)
+ }
+ spdxDocNode = typeTriples[0].Subject
+ }
+ }
+ if spdxDocNode == nil {
+ return nil, errors.New("RDF files must be associated with a SpdxDocument tag. No tag found")
+ }
+ return spdxDocNode, nil
+}
diff --git a/rdfloader/parser2v3/parser_test.go b/rdfloader/parser2v3/parser_test.go
new file mode 100644
index 0000000..5aebe94
--- /dev/null
+++ b/rdfloader/parser2v3/parser_test.go
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "testing"
+)
+
+func TestNewParser2_3(t *testing.T) {
+ // testing if the attributes are initialised well and no top-level is left uninitialized.
+ // primarily, checking if all the maps are initialized because
+ // uninitialized slices are by default slices of length 0
+ p, _ := parserFromBodyContent(``)
+ parser := NewParser2_3(p.gordfParserObj, p.nodeStringToTriples)
+ if parser.files == nil {
+ t.Errorf("files should've been initialised, got %v", parser.files)
+ }
+ if parser.assocWithPackage == nil {
+ t.Errorf("assocWithPackage should've been initialised, got %v", parser.assocWithPackage)
+ }
+ if parser.doc.CreationInfo == nil {
+ t.Errorf("doc.CreationInfo should've been initialised, got %v", parser.doc.CreationInfo)
+ }
+ if parser.doc.Packages == nil {
+ t.Errorf("doc.Packages should've been initialised, got %v", parser.doc.Packages)
+ }
+ if parser.doc.Files == nil {
+ t.Errorf("doc.Files should've been initialised, got %v", parser.doc.Files)
+ }
+}
+
+func TestLoadFromGoRDFParser(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+
+ // TestCase 1: gordfparser without a SpdxDocument node triple:
+ parser, _ = parserFromBodyContent("")
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err == nil {
+ t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err)
+ }
+
+ // TestCase 2: invalid SpdxDocumentNode
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301/Document">
+ <spdx:invalidTag />
+ </spdx:SpdxDocument>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err == nil {
+ t.Errorf("expected an error because of absence of SpdxDocument node, got %v", err)
+ }
+
+ // TestCase 3: >1 type triples for subnode of a SpdxDocument:
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#Snippet"/>
+ <spdx:CreationInfo rdf:about="#Snippet"/>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err == nil {
+ t.Errorf("expected an error due to more than one type triples, got %v", err)
+ }
+
+ // TestCase 4: invalid snippet must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#Snippet"/>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err == nil {
+ t.Errorf("expected an error due to invalid Snippet, got %v", err)
+ }
+
+ // TestCase 5: invalid snippet not associated with any File must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet"/>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err == nil {
+ t.Errorf("expected an error due to invalid Snippet File, got %v", err)
+ }
+
+ // TestCase 6: other Tag alongwith the SpdxDocument node mustn't raise any error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:review/>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ // TestCase 5: everything valid:
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#SPDXRef-Snippet">
+ <spdx:name>from linux kernel</spdx:name>
+ <spdx:copyrightText>Copyright 2008-2010 John Smith</spdx:copyrightText>
+ <spdx:licenseComments>The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.</spdx:licenseComments>
+ <spdx:snippetFromFile>
+ <spdx:File rdf:about="#SPDXRef-DoapSource">
+ <spdx:copyrightText>Copyright 2010, 2011 Source Auditor Inc.</spdx:copyrightText>
+ <spdx:fileContributor>Open Logic Inc.</spdx:fileContributor>
+ <spdx:fileName>./src/org/spdx/parser/DOAPProject.java</spdx:fileName>
+ <spdx:fileContributor>Black Duck Software In.c</spdx:fileContributor>
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+ <spdx:licenseInfoInFile rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
+ </spdx:File>
+ </spdx:snippetFromFile>
+ </spdx:Snippet>
+ `)
+ _, err = LoadFromGoRDFParser(parser.gordfParserObj)
+ if err != nil {
+ t.Errorf("error parsing a valid example: %v", err)
+ }
+}
+
+func Test_rdfParser2_3_getSpdxDocNode(t *testing.T) {
+ var parser *rdfParser2_3
+ var err error
+
+ // TestCase 1: more than one association type for a single node.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#SPDXRef-Document"/>
+ `)
+ _, err = parser.getSpdxDocNode()
+ t.Log(err)
+ if err == nil {
+ t.Errorf("expected and error due to more than one type triples for the SpdxDocument Node, got %v", err)
+ }
+
+ // TestCase 2: must be associated with exactly one rdf:type.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document"/>
+ <spdx:Snippet rdf:about="#SPDXRef-Document"/>
+ <spdx:File rdf:about="#SPDXRef-DoapSource"/>
+ `)
+ _, err = parser.getSpdxDocNode()
+ t.Log(err)
+ if err == nil {
+ t.Errorf("rootNode must be associated with exactly one triple of predicate rdf:type, got %v", err)
+ }
+
+ // TestCase 3: two different spdx nodes found in a single document.
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document-1"/>
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document-2"/>
+ `)
+ _, err = parser.getSpdxDocNode()
+ if err == nil {
+ t.Errorf("expected and error due to more than one type SpdxDocument Node, got %v", err)
+ }
+
+ // TestCase 4: no spdx document
+ parser, _ = parserFromBodyContent(``)
+ _, err = parser.getSpdxDocNode()
+ if err == nil {
+ t.Errorf("expected and error due to no SpdxDocument Node, got %v", err)
+ }
+
+ // TestCase 5: valid spdxDocument node
+ parser, _ = parserFromBodyContent(`
+ <spdx:SpdxDocument rdf:about="#SPDXRef-Document-1"/>
+ `)
+ _, err = parser.getSpdxDocNode()
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
diff --git a/rdfloader/parser2v3/types.go b/rdfloader/parser2v3/types.go
new file mode 100644
index 0000000..9efd04e
--- /dev/null
+++ b/rdfloader/parser2v3/types.go
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+// copied from tvloader/parser2v3/types.go
+package parser2v3
+
+import (
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+ "github.com/spdx/tools-golang/spdx/v2_3"
+)
+
+type rdfParser2_3 struct {
+ // fields associated with gordf project which
+ // will be required by rdfloader
+ gordfParserObj *gordfParser.Parser
+ nodeStringToTriples map[string][]*gordfParser.Triple
+
+ // document into which data is being parsed
+ doc *v2_3.Document
+
+ // map of packages and files.
+ files map[common.ElementID]*v2_3.File
+ assocWithPackage map[common.ElementID]bool
+
+ // mapping of nodeStrings to parsed object to save double computation.
+ cache map[string]*nodeState
+}
+
+type Color int
+
+const (
+ GREY Color = iota // represents that the node is being visited
+ WHITE // unvisited node
+ BLACK // visited node
+)
+
+type nodeState struct {
+ // object will be pointer to the parsed or element being parsed.
+ object interface{}
+ // color of a state represents if the node is visited/unvisited/being-visited.
+ Color Color
+}
+
+type AnyLicenseInfo interface {
+ // ToLicenseString returns the representation of license about how it will
+ // be stored in the tools-golang data model
+ ToLicenseString() string
+}
+
+type SimpleLicensingInfo struct {
+ AnyLicenseInfo
+ comment string
+ licenseID string
+ name string
+ seeAlso []string
+ example string
+}
+
+type ExtractedLicensingInfo struct {
+ SimpleLicensingInfo
+ extractedText string
+}
+
+type OrLaterOperator struct {
+ AnyLicenseInfo
+ member SimpleLicensingInfo
+}
+
+type ConjunctiveLicenseSet struct {
+ AnyLicenseInfo
+ members []AnyLicenseInfo
+}
+
+type DisjunctiveLicenseSet struct {
+ AnyLicenseInfo
+ members []AnyLicenseInfo
+}
+
+type License struct {
+ SimpleLicensingInfo
+ isOsiApproved bool
+ licenseText string
+ standardLicenseHeader string
+ standardLicenseTemplate string
+ standardLicenseHeaderTemplate string
+ isDeprecatedLicenseID bool
+ isFsfLibre bool
+}
+
+type ListedLicense struct {
+ License
+}
+
+type LicenseException struct {
+ licenseExceptionId string
+ licenseExceptionText string
+ seeAlso string // must be a valid uri
+ name string
+ example string
+ comment string
+}
+
+type WithExceptionOperator struct {
+ AnyLicenseInfo
+ member SimpleLicensingInfo
+ licenseException LicenseException
+}
+
+// custom LicenseType to provide support for licences of
+// type Noassertion, None and customLicenses
+type SpecialLicense struct {
+ AnyLicenseInfo
+ value SpecialLicenseValue
+}
+
+type SpecialLicenseValue string
+
+const (
+ NONE SpecialLicenseValue = "NONE"
+ NOASSERTION SpecialLicenseValue = "NOASSERTION"
+)
+
+type RangeType string
+
+const (
+ BYTE_RANGE RangeType = "byteRange"
+ LINE_RANGE RangeType = "lineRange"
+)
diff --git a/rdfloader/parser2v3/utils.go b/rdfloader/parser2v3/utils.go
new file mode 100644
index 0000000..356471b
--- /dev/null
+++ b/rdfloader/parser2v3/utils.go
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/gordf/rdfwriter"
+ urilib "github.com/spdx/gordf/uri"
+ "github.com/spdx/tools-golang/spdx/common"
+)
+
+// a uri is of type baseURI#fragment or baseFragment/subFragment
+// returns fragment or subFragment when given as an input.
+func getLastPartOfURI(uri string) string {
+ if strings.Contains(uri, "#") {
+ parts := strings.Split(uri, "#")
+ return parts[len(parts)-1]
+ }
+ parts := strings.Split(uri, "/")
+ return parts[len(parts)-1]
+}
+
+func isUriValid(uri string) bool {
+ _, err := urilib.NewURIRef(uri)
+ return err == nil
+}
+
+func getNodeTypeFromTriples(triples []*gordfParser.Triple, node *gordfParser.Node) (string, error) {
+ if node == nil {
+ return "", errors.New("empty node passed to find node type")
+ }
+ typeTriples := rdfwriter.FilterTriples(triples, &node.ID, &RDF_TYPE, nil)
+ switch len(typeTriples) {
+ case 0:
+ return "", fmt.Errorf("node{%v} not associated with any type triple", node)
+ case 1:
+ return typeTriples[0].Object.ID, nil
+ default:
+ return "", fmt.Errorf("node{%v} is associated with more than one type triples", node)
+ }
+}
+
+func (parser *rdfParser2_3) nodeToTriples(node *gordfParser.Node) []*gordfParser.Triple {
+ if node == nil {
+ return []*gordfParser.Triple{}
+ }
+ return parser.nodeStringToTriples[node.String()]
+}
+
+// returns which boolean was given as an input
+// string(bool) is the only possible input for which it will not raise any error.
+func boolFromString(boolString string) (bool, error) {
+ switch strings.ToLower(boolString) {
+ case "true":
+ return true, nil
+ case "false":
+ return false, nil
+ default:
+ return false, fmt.Errorf("boolean string can be either true/false")
+ }
+}
+
+/* Function Below this line is taken from the tvloader/parser2v3/utils.go */
+
+// used to extract DocumentRef and SPDXRef values from an SPDX Identifier
+// which can point either to this document or to a different one
+func ExtractDocElementID(value string) (common.DocElementID, error) {
+ docRefID := ""
+ idStr := value
+
+ // check prefix to see if it's a DocumentRef ID
+ if strings.HasPrefix(idStr, "DocumentRef-") {
+ // extract the part that comes between "DocumentRef-" and ":"
+ strs := strings.Split(idStr, ":")
+ // should be exactly two, part before and part after
+ if len(strs) < 2 {
+ return common.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present")
+ }
+ if len(strs) > 2 {
+ return common.DocElementID{}, fmt.Errorf("more than one colon found")
+ }
+
+ // trim the prefix and confirm non-empty
+ docRefID = strings.TrimPrefix(strs[0], "DocumentRef-")
+ if docRefID == "" {
+ return common.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix")
+ }
+ // and use remainder for element ID parsing
+ idStr = strs[1]
+ }
+
+ // check prefix to confirm it's got the right prefix for element IDs
+ if !strings.HasPrefix(idStr, "SPDXRef-") {
+ return common.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier")
+ }
+
+ // make sure no colons are present
+ if strings.Contains(idStr, ":") {
+ // we know this means there was no DocumentRef- prefix, because
+ // we would have handled multiple colons above if it was
+ return common.DocElementID{}, fmt.Errorf("invalid colon in element identifier")
+ }
+
+ // trim the prefix and confirm non-empty
+ eltRefID := strings.TrimPrefix(idStr, "SPDXRef-")
+ if eltRefID == "" {
+ return common.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix")
+ }
+
+ // we're good
+ return common.DocElementID{DocumentRefID: docRefID, ElementRefID: common.ElementID(eltRefID)}, nil
+}
+
+// used to extract SPDXRef values only from an SPDX Identifier which can point
+// to this document only. Use extractDocElementID for parsing IDs that can
+// refer either to this document or a different one.
+func ExtractElementID(value string) (common.ElementID, error) {
+ // check prefix to confirm it's got the right prefix for element IDs
+ if !strings.HasPrefix(value, "SPDXRef-") {
+ return common.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier")
+ }
+
+ // make sure no colons are present
+ if strings.Contains(value, ":") {
+ return common.ElementID(""), fmt.Errorf("invalid colon in element identifier")
+ }
+
+ // trim the prefix and confirm non-empty
+ eltRefID := strings.TrimPrefix(value, "SPDXRef-")
+ if eltRefID == "" {
+ return common.ElementID(""), fmt.Errorf("element identifier has nothing after prefix")
+ }
+
+ // we're good
+ return common.ElementID(eltRefID), nil
+}
+
+// used to extract key / value from embedded substrings
+// returns subkey, subvalue, nil if no error, or "", "", error otherwise
+func ExtractSubs(value string, sep string) (string, string, error) {
+ // parse the value to see if it's a valid subvalue format
+ sp := strings.SplitN(value, sep, 2)
+ if len(sp) == 1 {
+ return "", "", fmt.Errorf("invalid subvalue format for %s (no %s found)", value, sep)
+ }
+
+ subkey := strings.TrimSpace(sp[0])
+ subvalue := strings.TrimSpace(sp[1])
+
+ return subkey, subvalue, nil
+}
diff --git a/rdfloader/parser2v3/utils_test.go b/rdfloader/parser2v3/utils_test.go
new file mode 100644
index 0000000..24aa4e9
--- /dev/null
+++ b/rdfloader/parser2v3/utils_test.go
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package parser2v3
+
+import (
+ "reflect"
+ "testing"
+
+ gordfParser "github.com/spdx/gordf/rdfloader/parser"
+ "github.com/spdx/tools-golang/spdx/common"
+)
+
+func Test_getLastPartOfURI(t *testing.T) {
+ // uri of type baseFragment#fragment
+ input := "baseFragment#fragment"
+ expectedOutput := "fragment"
+ output := getLastPartOfURI(input)
+ if output != expectedOutput {
+ t.Errorf("expected %s, found %s", expectedOutput, output)
+ }
+
+ // uri of type baseFragment/subFragment
+ input = "baseFragment/subFragment"
+ expectedOutput = "subFragment"
+ output = getLastPartOfURI(input)
+ if output != expectedOutput {
+ t.Errorf("expected %s, found %s", expectedOutput, output)
+ }
+
+ // neither of the case mustn't raise any error.
+ input = "www.github.com"
+ expectedOutput = input
+ output = getLastPartOfURI(input)
+ if output != expectedOutput {
+ t.Errorf("expected %s, found %s", expectedOutput, output)
+ }
+}
+
+func Test_isUriValid(t *testing.T) {
+ // TestCase 1: Valid Input URI
+ input := "https://www.github.com"
+ isValid := isUriValid(input)
+ if !isValid {
+ t.Errorf("valid input(%s) detected as invalid.", input)
+ }
+
+ // TestCase 2: Invalid Input URI
+ input = `http\:www.github.com`
+ isValid = isUriValid(input)
+ if isValid {
+ t.Errorf("invalid input(%s) detected as valid", input)
+ }
+}
+
+func Test_rdfParser2_3_nodeToTriples(t *testing.T) {
+ var parser *rdfParser2_3
+ var output, expectedOutput []*gordfParser.Triple
+
+ // TestCase 1: a nil node shouldn't raise any error or panic.
+ parser, _ = parserFromBodyContent(``)
+ output = parser.nodeToTriples(nil)
+ if output == nil {
+ t.Errorf("nil input should return an empty slice and not nil")
+ }
+ expectedOutput = []*gordfParser.Triple{}
+ if !reflect.DeepEqual(output, expectedOutput) {
+ t.Errorf("expected %+v, got %+v", expectedOutput, output)
+ }
+
+ // TestCase 2: node should be addressable based on the node content and not the pointer.
+ // It should allow new nodes same as the older ones to retrieve the associated triples.
+ parser, _ = parserFromBodyContent(`
+ <spdx:Checksum rdf:about="#checksum">
+ <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1" />
+ <spdx:checksumValue>75068c26abbed3ad3980685bae21d7202d288317</spdx:checksumValue>
+ </spdx:Checksum>
+ `)
+ newNode := &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#checksum",
+ }
+ output = parser.nodeToTriples(newNode)
+
+ // The output must have 3 triples:
+ // 1. newNode rdf:type Checksum
+ // 2. newNode spdx:algorithm http://spdx.org/rdf/terms#checksumAlgorithm_sha1
+ // 3. newNode spdx:checksumValue 75068c26abbed3ad3980685bae21d7202d288317
+ if len(output) != 3 {
+ t.Errorf("expected output to have 3 triples, got %d", len(output))
+ }
+}
+
+func Test_boolFromString(t *testing.T) {
+ // TestCase 1: Valid Input: "true"
+ // mustn't raise any error
+ input := "true"
+ val, err := boolFromString(input)
+ if err != nil {
+ t.Errorf("function raised an error for a valid input(%s): %s", input, err)
+ }
+ if val != true {
+ t.Errorf("invalid output. Expected %v, found %v", true, val)
+ }
+
+ // TestCase 2: Valid Input: "true"
+ // mustn't raise any error
+ input = "false"
+ val, err = boolFromString(input)
+ if err != nil {
+ t.Errorf("function raised an error for a valid input(%s): %s", input, err)
+ }
+ if val != false {
+ t.Errorf("invalid output. Expected %v, found %v", false, val)
+ }
+
+ // TestCase 3: invalid input: ""
+ // it must raise an error
+ input = ""
+ val, err = boolFromString(input)
+ if err == nil {
+ t.Errorf("invalid input should've raised an error")
+ }
+}
+
+func Test_getNodeTypeFromTriples(t *testing.T) {
+ var err error
+ var node *gordfParser.Node
+ var triples []*gordfParser.Triple
+ var nodeType, expectedNodeType string
+
+ // TestCase 1: nil node must raise an error because,
+ // nil nodes cannot be associated with any rdf:type attribute.
+ _, err = getNodeTypeFromTriples(triples, nil)
+ if err == nil {
+ t.Errorf("expected an error due to nil node, got %v", err)
+ }
+
+ // TestCase 2: none of the triples give information about the rdf:type of a node.
+ node = &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "N0",
+ }
+ _, err = getNodeTypeFromTriples(triples, node)
+ if err == nil {
+ t.Errorf("expected an error saying no rdf:type found, got %v", err)
+ }
+
+ // TestCase 3: node is associated with exactly one rdf:type triples
+ typeTriple := &gordfParser.Triple{
+ Subject: node,
+ Predicate: &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: RDF_TYPE,
+ },
+ Object: &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "http://spdx.org/rdf/terms#Checksum",
+ },
+ }
+ triples = append(triples, typeTriple)
+ expectedNodeType = "http://spdx.org/rdf/terms#Checksum"
+ nodeType, err = getNodeTypeFromTriples(triples, node)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if nodeType != expectedNodeType {
+ t.Errorf("expected: %v, got: %v", nodeType, expectedNodeType)
+ }
+
+ // TestCase 4: node associated with more than one rdf:type triples must raise an error.
+ typeTriple = &gordfParser.Triple{
+ Subject: node,
+ Predicate: &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: RDF_TYPE,
+ },
+ Object: &gordfParser.Node{
+ NodeType: gordfParser.IRI,
+ ID: "http://spdx.org/rdf/terms#Snippet",
+ },
+ }
+ triples = append(triples, typeTriple)
+ _, err = getNodeTypeFromTriples(triples, node)
+ if err == nil {
+ t.Errorf("expected an error saying more than one rdf:type found, got %v", err)
+ }
+}
+
+// following tests are copy pasted from tvloader/parser2v3/util_test.go
+
+func TestCanExtractDocumentAndElementRefsFromID(t *testing.T) {
+ // test with valid ID in this document
+ helperForExtractDocElementID(t, "SPDXRef-file1", false, "", "file1")
+ // test with valid ID in another document
+ helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file2", false, "doc2", "file2")
+ // test with invalid ID in this document
+ helperForExtractDocElementID(t, "a:SPDXRef-file1", true, "", "")
+ helperForExtractDocElementID(t, "file1", true, "", "")
+ helperForExtractDocElementID(t, "SPDXRef-", true, "", "")
+ helperForExtractDocElementID(t, "SPDXRef-file1:", true, "", "")
+ // test with invalid ID in another document
+ helperForExtractDocElementID(t, "DocumentRef-doc2", true, "", "")
+ helperForExtractDocElementID(t, "DocumentRef-doc2:", true, "", "")
+ helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-", true, "", "")
+ helperForExtractDocElementID(t, "DocumentRef-doc2:a", true, "", "")
+ helperForExtractDocElementID(t, "DocumentRef-:", true, "", "")
+ helperForExtractDocElementID(t, "DocumentRef-:SPDXRef-file1", true, "", "")
+ // test with invalid formats
+ helperForExtractDocElementID(t, "DocumentRef-doc2:SPDXRef-file1:file2", true, "", "")
+}
+
+func helperForExtractDocElementID(t *testing.T, tst string, wantErr bool, wantDoc string, wantElt string) {
+ deID, err := ExtractDocElementID(tst)
+ if err != nil && wantErr == false {
+ t.Errorf("testing %v: expected nil error, got %v", tst, err)
+ }
+ if err == nil && wantErr == true {
+ t.Errorf("testing %v: expected non-nil error, got nil", tst)
+ }
+ if deID.DocumentRefID != wantDoc {
+ if wantDoc == "" {
+ t.Errorf("testing %v: want empty string for DocumentRefID, got %v", tst, deID.DocumentRefID)
+ } else {
+ t.Errorf("testing %v: want %v for DocumentRefID, got %v", tst, wantDoc, deID.DocumentRefID)
+ }
+ }
+ if deID.ElementRefID != common.ElementID(wantElt) {
+ if wantElt == "" {
+ t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, deID.ElementRefID)
+ } else {
+ t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, deID.ElementRefID)
+ }
+ }
+}
+
+func TestCanExtractElementRefsOnlyFromID(t *testing.T) {
+ // test with valid ID in this document
+ helperForExtractElementID(t, "SPDXRef-file1", false, "file1")
+ // test with valid ID in another document
+ helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-file2", true, "")
+ // test with invalid ID in this document
+ helperForExtractElementID(t, "a:SPDXRef-file1", true, "")
+ helperForExtractElementID(t, "file1", true, "")
+ helperForExtractElementID(t, "SPDXRef-", true, "")
+ helperForExtractElementID(t, "SPDXRef-file1:", true, "")
+ // test with invalid ID in another document
+ helperForExtractElementID(t, "DocumentRef-doc2", true, "")
+ helperForExtractElementID(t, "DocumentRef-doc2:", true, "")
+ helperForExtractElementID(t, "DocumentRef-doc2:SPDXRef-", true, "")
+ helperForExtractElementID(t, "DocumentRef-doc2:a", true, "")
+ helperForExtractElementID(t, "DocumentRef-:", true, "")
+ helperForExtractElementID(t, "DocumentRef-:SPDXRef-file1", true, "")
+}
+
+func helperForExtractElementID(t *testing.T, tst string, wantErr bool, wantElt string) {
+ eID, err := ExtractElementID(tst)
+ if err != nil && wantErr == false {
+ t.Errorf("testing %v: expected nil error, got %v", tst, err)
+ }
+ if err == nil && wantErr == true {
+ t.Errorf("testing %v: expected non-nil error, got nil", tst)
+ }
+ if eID != common.ElementID(wantElt) {
+ if wantElt == "" {
+ t.Errorf("testing %v: want emptyString for ElementRefID, got %v", tst, eID)
+ } else {
+ t.Errorf("testing %v: want %v for ElementRefID, got %v", tst, wantElt, eID)
+ }
+ }
+}
+
+func TestCanExtractSubvalues(t *testing.T) {
+ subkey, subvalue, err := ExtractSubs("SHA1: abc123", ":")
+ if err != nil {
+ t.Errorf("got error when calling extractSubs: %v", err)
+ }
+ if subkey != "SHA1" {
+ t.Errorf("got %v for subkey", subkey)
+ }
+ if subvalue != "abc123" {
+ t.Errorf("got %v for subvalue", subvalue)
+ }
+}
+
+func TestReturnsErrorForInvalidSubvalueFormat(t *testing.T) {
+ _, _, err := ExtractSubs("blah", ":")
+ if err == nil {
+ t.Errorf("expected error when calling extractSubs for invalid format (0 colons), got nil")
+ }
+}
diff --git a/rdfloader/rdfloader.go b/rdfloader/rdfloader.go
index 838b0d7..4575799 100644
--- a/rdfloader/rdfloader.go
+++ b/rdfloader/rdfloader.go
@@ -7,7 +7,9 @@ import (
"github.com/spdx/gordf/rdfloader"
"github.com/spdx/tools-golang/rdfloader/parser2v2"
+ "github.com/spdx/tools-golang/rdfloader/parser2v3"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// Takes in a file Reader and returns the pertaining spdx document
@@ -21,3 +23,15 @@ func Load2_2(content io.Reader) (*v2_2.Document, error) {
doc, err := parser2v2.LoadFromGoRDFParser(rdfParserObj)
return doc, err
}
+
+// Takes in a file Reader and returns the pertaining spdx document
+// or the error if any is encountered while setting the doc.
+func Load2_3(content io.Reader) (*v2_3.Document, error) {
+ var rdfParserObj, err = rdfloader.LoadFromReaderObject(content)
+ if err != nil {
+ return nil, err
+ }
+
+ doc, err := parser2v3.LoadFromGoRDFParser(rdfParserObj)
+ return doc, err
+}
diff --git a/rdfloader/rdfloader_test.go b/rdfloader/rdfloader_test.go
index 74471c7..4f77bdf 100644
--- a/rdfloader/rdfloader_test.go
+++ b/rdfloader/rdfloader_test.go
@@ -32,3 +32,87 @@ func TestLoad2_2(t *testing.T) {
t.Errorf("expected an error due to no SpdxDocument Node in the document")
}
}
+
+func TestLoad2_3(t *testing.T) {
+ var reader io.Reader
+ var err error
+
+ // TestCase 1: invalid rdf/xml must raise an error
+ reader = strings.NewReader("")
+ _, err = Load2_3(reader)
+ if err == nil {
+ t.Errorf("expected an EOF error reading an empty file, got %v", err)
+ }
+
+ // TestCase 2: Valid rdf/xml but invalid spdx document must raise an error
+ reader = strings.NewReader(`
+ <rdf:RDF
+ xmlns:spdx="http://spdx.org/rdf/terms#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#">
+ </rdf:RDF>
+ `)
+ _, err = Load2_3(reader)
+ if err == nil {
+ t.Errorf("expected an error due to no SpdxDocument Node in the document")
+ }
+
+ // TestCase 3: New SPDX package elements
+ reader = strings.NewReader(`
+ <rdf:RDF
+ xmlns:spdx="http://spdx.org/rdf/terms#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#"
+ xmlns:doap="http://usefulinc.com/ns/doap#"
+ xmlns:j.0="http://www.w3.org/2009/pointers#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
+ <spdx:SpdxDocument rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT">
+ <spdx:specVersion>SPDX-2.0</spdx:specVersion>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon">
+ <spdx:name>Some-Package</spdx:name>
+ <spdx:primaryPackagePurpose rdf:resource="packagePurpose_container" />
+ <spdx:releaseDate>2021-10-15T02:38:00Z</spdx:releaseDate>
+ <spdx:builtDate>2021-09-15T02:38:00Z</spdx:builtDate>
+ <spdx:validUntilDate>2022-10-15T02:38:00Z</spdx:validUntilDate>
+ </spdx:Package>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:SpdxDocument>
+ </rdf:RDF>
+ `)
+
+ doc, err := Load2_3(reader)
+ if err != nil {
+ t.Errorf("expected valid SPDX document: %v", err)
+ }
+
+ if doc == nil {
+ t.Fatalf("expected valid SPDX document but got nil")
+ }
+
+ if len(doc.Packages) == 0 {
+ t.Errorf("expected packages but got none")
+ }
+
+ pkg := doc.Packages[0]
+ if pkg.PackageName != "Some-Package" {
+ t.Errorf("expected package nameof Some-Package but got: %s", pkg.PackageName)
+ }
+ if pkg.PrimaryPackagePurpose != "CONTAINER" {
+ t.Errorf("expected package primary purpose of CONTAINER but got: %s", pkg.PrimaryPackagePurpose)
+ }
+ if pkg.ReleaseDate != "2021-10-15T02:38:00Z" {
+ t.Errorf("expected release date of 2021-10-15T02:38:00Z but got: %s", pkg.ReleaseDate)
+ }
+ if pkg.BuiltDate != "2021-09-15T02:38:00Z" {
+ t.Errorf("expected built date of 2021-09-15T02:38:00Z but got: %s", pkg.BuiltDate)
+ }
+ if pkg.ValidUntilDate != "2022-10-15T02:38:00Z" {
+ t.Errorf("expected valid until date of 2022-10-15T02:38:00Z but got: %s", pkg.ValidUntilDate)
+ }
+}