Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion src/main/java/org/spdx/library/conversion/Spdx2to3Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nullable;

Expand All @@ -29,6 +30,7 @@
import org.spdx.core.IModelCopyManager;
import org.spdx.core.InvalidSPDXAnalysisException;
import org.spdx.library.ListedLicenses;
import org.spdx.library.SpdxModelFactory;
import org.spdx.library.model.v2.SpdxConstantsCompatV2;
import org.spdx.library.model.v2.SpdxCreatorInformation;
import org.spdx.library.model.v2.pointer.ByteOffsetPointer;
Expand Down Expand Up @@ -698,11 +700,58 @@ public SpdxDocument convertAndStore(org.spdx.library.model.v2.SpdxDocument fromD
}
).collect(Collectors.toList()));
for (org.spdx.library.model.v2.license.ExtractedLicenseInfo extractedLicense:fromDoc.getExtractedLicenseInfos()) {
convertAndStore(extractedLicense);
toDoc.getRootElements().add(convertAndStore(extractedLicense));
}
convertOrphanElements(fromDoc, toDoc);
return toDoc;
}

/**
* Converts any SPDX spec version 2 packages, files, snippets, listed licenses, and listed license
* exceptions belonging to fromDoc which are not reachable through documentDescribes, a relationship,
* or a license expression (i.e. have no edges connecting them to anything else in the document).
* Without this, such elements would be silently dropped from the conversion since they would never
* be visited by the relationship graph traversal performed elsewhere in this class.
* Any such elements found are converted, stored, and added to the toDoc's root elements.
* @param fromDoc SPDX spec version 2 document being converted from
* @param toDoc SPDX spec version 3 document being converted to
* @throws InvalidSPDXAnalysisException on any errors converting the orphan elements
*/
@SuppressWarnings("unchecked")
private void convertOrphanElements(org.spdx.library.model.v2.SpdxDocument fromDoc, SpdxDocument toDoc) throws InvalidSPDXAnalysisException {
String documentUri = fromDoc.getDocumentUri();
IModelStore fromModelStore = fromDoc.getModelStore();
for (String typeFilter : new String[] {SpdxConstantsCompatV2.CLASS_SPDX_PACKAGE,
Comment thread
goneall marked this conversation as resolved.
SpdxConstantsCompatV2.CLASS_SPDX_FILE, SpdxConstantsCompatV2.CLASS_SPDX_SNIPPET}) {
List<org.spdx.library.model.v2.SpdxElement> fromElements = ((Stream<org.spdx.library.model.v2.SpdxElement>)
SpdxModelFactory.getSpdxObjects(fromModelStore, copyManager, typeFilter, documentUri, null))
.collect(Collectors.toList());
for (org.spdx.library.model.v2.SpdxElement fromElement : fromElements) {
if (!alreadyCopied(fromElement.getObjectUri())) {
toDoc.getRootElements().add(convertAndStore(fromElement));
}
}
}
// Listed licenses and listed license exceptions are stored under the SPDX License List namespace
// rather than the document's namespace, so no object URI prefix filter is used to find them.
List<org.spdx.library.model.v2.license.SpdxListedLicense> fromListedLicenses = ((Stream<org.spdx.library.model.v2.license.SpdxListedLicense>)
SpdxModelFactory.getSpdxObjects(fromModelStore, copyManager, SpdxConstantsCompatV2.CLASS_SPDX_LISTED_LICENSE, null, null))
.collect(Collectors.toList());
for (org.spdx.library.model.v2.license.SpdxListedLicense fromListedLicense : fromListedLicenses) {
if (!alreadyCopied(fromListedLicense.getObjectUri())) {
toDoc.getRootElements().add(convertAndStore(fromListedLicense));
}
}
List<org.spdx.library.model.v2.license.ListedLicenseException> fromListedExceptions = ((Stream<org.spdx.library.model.v2.license.ListedLicenseException>)
SpdxModelFactory.getSpdxObjects(fromModelStore, copyManager, SpdxConstantsCompatV2.CLASS_SPDX_LISTED_LICENSE_EXCEPTION, null, null))
.collect(Collectors.toList());
for (org.spdx.library.model.v2.license.ListedLicenseException fromListedException : fromListedExceptions) {
if (!alreadyCopied(fromListedException.getObjectUri())) {
toDoc.getRootElements().add(convertAndStore(fromListedException));
}
}
}

/**
* Converts the externalDocRef to a NamespaceMap and store the NamespaceMap.
* The document information is also retained in the externalDocRefMap such that any subsequent
Expand Down
149 changes: 146 additions & 3 deletions src/test/java/org/spdx/library/conversion/Spdx2to3ConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Nullable;

Expand Down Expand Up @@ -606,9 +608,21 @@ public void testConvertAndStoreSpdxDocument() throws InvalidSPDXAnalysisExceptio
assertEquals(annotationComment, resultAnnotation.getStatement().get());

Element[] rootElements = result.getRootElements().toArray(new Element[result.getRootElements().size()]);
assertEquals(1, rootElements.length);
assertTrue(rootElements[0] instanceof SpdxPackage);
SpdxPackage resultPkg = (SpdxPackage)rootElements[0];
assertEquals(2, rootElements.length);
SpdxPackage resultPkg = null;
CustomLicense rootCustomLicense = null;
for (Element rootElement:rootElements) {
if (rootElement instanceof SpdxPackage) {
resultPkg = (SpdxPackage)rootElement;
} else if (rootElement instanceof CustomLicense) {
rootCustomLicense = (CustomLicense)rootElement;
} else {
fail("Unexpected root element type "+rootElement.getClass().getName());
}
}
assertTrue(Objects.nonNull(resultPkg));
assertTrue(Objects.nonNull(rootCustomLicense));
assertEquals(extractedLicName, rootCustomLicense.getName().get());
assertEquals(pkgName, resultPkg.getName().get());
assertEquals(pkgDownloadLocation, resultPkg.getDownloadLocation().get());
assertEquals(pkgCopyright, resultPkg.getCopyrightText().get());
Expand Down Expand Up @@ -671,6 +685,135 @@ public void testConvertAndStoreSpdxDocument() throws InvalidSPDXAnalysisExceptio
assertTrue(verify.isEmpty());
}

/**
* Test method for {@link org.spdx.library.conversion.Spdx2to3Converter#convertAndStore(org.spdx.library.model.v2.SpdxDocument)}.
* Verifies that a package with no relationships and not included in documentDescribes (i.e. no edges
* connecting it to anything else in the document) is still converted rather than silently dropped.
* @throws InvalidSPDXAnalysisException
*/
@Test
public void testConvertAndStoreSpdxDocumentOrphanPackage() throws InvalidSPDXAnalysisException {
String describedPkgName = "described package";
String orphanPkgName = "orphan package";

org.spdx.library.model.v2.SpdxDocument doc = new org.spdx.library.model.v2.SpdxDocument(fromModelStore, DOCUMENT_URI, copyManager, true);
doc.setCreationInfo(doc.createCreationInfo(Arrays.asList(new String[] {SpdxConstantsCompatV2.CREATOR_PREFIX_TOOL + "test"}),
"2010-01-29T18:30:22Z"));
org.spdx.library.model.v2.license.AnyLicenseInfo dataLicense =
LicenseInfoFactory.parseSPDXLicenseStringCompatV2("CC0-1.0", fromModelStore, DOCUMENT_URI, copyManager);
doc.setDataLicense(dataLicense);

org.spdx.library.model.v2.license.AnyLicenseInfo noAssertion =
new org.spdx.library.model.v2.license.SpdxNoAssertionLicense();
org.spdx.library.model.v2.SpdxPackage describedPkg = doc.createPackage(fromModelStore.getNextId(IdType.SpdxId),
describedPkgName, noAssertion, "copyright", noAssertion)
.setFilesAnalyzed(false)
.setDownloadLocation("NOASSERTION")
.build();
doc.setDocumentDescribes(Arrays.asList(new org.spdx.library.model.v2.SpdxItem[] {describedPkg}));

// orphan package - no relationships, not part of documentDescribes
org.spdx.library.model.v2.SpdxPackage orphanPkg = doc.createPackage(fromModelStore.getNextId(IdType.SpdxId),
orphanPkgName, noAssertion, "copyright", noAssertion)
.setFilesAnalyzed(false)
.setDownloadLocation("NOASSERTION")
.build();

Spdx2to3Converter converter = new Spdx2to3Converter(toModelStore, copyManager, defaultCreationInfo,
SpdxModelFactory.getLatestSpecVersion(), DEFAULT_PREFIX, true);
converter.convertAndStore(doc);

List<SpdxPackage> resultPackages = new ArrayList<>();
SpdxModelFactory.getSpdxObjects(toModelStore, copyManager, SpdxConstantsV3.SOFTWARE_SPDX_PACKAGE, DEFAULT_PREFIX, DEFAULT_PREFIX)
.forEach(pkg -> resultPackages.add((SpdxPackage)pkg));
assertEquals(2, resultPackages.size());
Set<String> resultPackageNames = new HashSet<>();
for (SpdxPackage pkg:resultPackages) {
resultPackageNames.add(pkg.getName().get());
}
assertTrue(resultPackageNames.contains(describedPkgName));
assertTrue(resultPackageNames.contains(orphanPkgName));
}

/**
* Test method for {@link org.spdx.library.conversion.Spdx2to3Converter#convertAndStore(org.spdx.library.model.v2.SpdxDocument)}.
* Verifies that extracted licensing info, listed licenses, and listed license exceptions stored in the
* document's model store but not referenced by any package, file, or snippet are still converted and
* added as root elements rather than silently dropped.
* @throws InvalidSPDXAnalysisException
*/
@Test
public void testConvertAndStoreSpdxDocumentOrphanLicenses() throws InvalidSPDXAnalysisException {
String pkgName = "package";
String extractedLicName = "Extracted License Name";
String extractedLicText = "Extracted license text";
String orphanLicenseId = "Apache-2.0";
String orphanExceptionId = "Classpath-exception-2.0";

org.spdx.library.model.v2.SpdxDocument doc = new org.spdx.library.model.v2.SpdxDocument(fromModelStore, DOCUMENT_URI, copyManager, true);
doc.setCreationInfo(doc.createCreationInfo(Arrays.asList(new String[] {SpdxConstantsCompatV2.CREATOR_PREFIX_TOOL + "test"}),
"2010-01-29T18:30:22Z"));
org.spdx.library.model.v2.license.AnyLicenseInfo dataLicense =
LicenseInfoFactory.parseSPDXLicenseStringCompatV2("CC0-1.0", fromModelStore, DOCUMENT_URI, copyManager);
doc.setDataLicense(dataLicense);

org.spdx.library.model.v2.license.AnyLicenseInfo noAssertion =
new org.spdx.library.model.v2.license.SpdxNoAssertionLicense();
org.spdx.library.model.v2.SpdxPackage pkg = doc.createPackage(fromModelStore.getNextId(IdType.SpdxId),
pkgName, noAssertion, "copyright", noAssertion)
.setFilesAnalyzed(false)
.setDownloadLocation("NOASSERTION")
.build();
doc.setDocumentDescribes(Arrays.asList(new org.spdx.library.model.v2.SpdxItem[] {pkg}));

// extracted license info stored at the document level but not referenced by the package
org.spdx.library.model.v2.license.ExtractedLicenseInfo extractedLicense =
new org.spdx.library.model.v2.license.ExtractedLicenseInfo(fromModelStore, DOCUMENT_URI,
fromModelStore.getNextId(IdType.LicenseRef), copyManager, true);
extractedLicense.setExtractedText(extractedLicText);
extractedLicense.setName(extractedLicName);
doc.setExtractedLicenseInfos(Arrays.asList(new org.spdx.library.model.v2.license.ExtractedLicenseInfo[] {extractedLicense}));

// listed license and listed license exception stored in the document's model store but not
// referenced by the package, a relationship, or documentDescribes
org.spdx.library.model.v2.license.SpdxListedLicense orphanLicense =
new org.spdx.library.model.v2.license.SpdxListedLicense(fromModelStore,
SpdxConstantsCompatV2.LISTED_LICENSE_NAMESPACE_PREFIX, orphanLicenseId, copyManager, true);
org.spdx.library.model.v2.license.ListedLicenseException orphanException =
new org.spdx.library.model.v2.license.ListedLicenseException(fromModelStore,
SpdxConstantsCompatV2.LISTED_LICENSE_NAMESPACE_PREFIX, orphanExceptionId, copyManager, true);

Spdx2to3Converter converter = new Spdx2to3Converter(toModelStore, copyManager, defaultCreationInfo,
SpdxModelFactory.getLatestSpecVersion(), DEFAULT_PREFIX, true);
SpdxDocument result = converter.convertAndStore(doc);

Element[] rootElements = result.getRootElements().toArray(new Element[result.getRootElements().size()]);
assertEquals(4, rootElements.length);
boolean foundPkg = false;
boolean foundCustomLicense = false;
boolean foundListedLicense = false;
boolean foundListedException = false;
for (Element rootElement:rootElements) {
if (rootElement instanceof SpdxPackage) {
foundPkg = true;
assertEquals(pkgName, ((SpdxPackage)rootElement).getName().get());
} else if (rootElement instanceof CustomLicense) {
foundCustomLicense = true;
assertEquals(extractedLicName, ((CustomLicense)rootElement).getName().get());
} else if (rootElement instanceof ListedLicense) {
foundListedLicense = true;
} else if (rootElement instanceof ListedLicenseException) {
foundListedException = true;
} else {
fail("Unexpected root element type "+rootElement.getClass().getName());
}
}
assertTrue(foundPkg);
assertTrue(foundCustomLicense);
assertTrue(foundListedLicense);
assertTrue(foundListedException);
}

@Test
public void testConvertAndStoreExternalDocRef() throws InvalidSPDXAnalysisException {
String externalDocumentId = SpdxConstantsCompatV2.EXTERNAL_DOC_REF_PRENUM + "external";
Expand Down
Loading