Skip to content

Commit

Permalink
Support SPDX spec version 3 - BREAKING CHANGES (#72)
Browse files Browse the repository at this point in the history
* Updated for SPDX V3 changes

Signed-off-by: Gary O'Neall <[email protected]>

* Updated serializer to support multiple documents

Also updated to support API changes in core

Signed-off-by: Gary O'Neall <[email protected]>

* Add prefix to inflate API

Signed-off-by: Gary O'Neall <[email protected]>

* Fix issues in POM file

Signed-off-by: Gary O'Neall <[email protected]>

* Minor documentation fixes

Signed-off-by: Gary O'Neall <[email protected]>

* Renamed model version 3

Signed-off-by: Gary O'Neall <[email protected]>

* Implement new serialization interface

Signed-off-by: Gary O'Neall <[email protected]>

* Update to SPDX 3.0.1

Signed-off-by: Gary O'Neall <[email protected]>

* Move CompatibleModelStoreWrapper to V2 model

Signed-off-by: Gary O'Neall <[email protected]>

---------

Signed-off-by: Gary O'Neall <[email protected]>
  • Loading branch information
goneall authored Sep 5, 2024
1 parent 011fea6 commit 0a0560d
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 260 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.spdx</groupId>
<artifactId>spdx-jackson-store</artifactId>
<version>1.1.10-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spdx-jackson-store</name>
Expand Down Expand Up @@ -141,7 +141,7 @@ This store supports serializing and deserializing files in JSON, YAML and XML fo
<dependency>
<groupId>org.spdx</groupId>
<artifactId>java-spdx-library</artifactId>
<version>1.1.11</version>
<version>2.0.0-Alpha</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
Expand Down
136 changes: 71 additions & 65 deletions src/main/java/org/spdx/jacksonstore/JacksonDeSerializer.java

Large diffs are not rendered by default.

229 changes: 126 additions & 103 deletions src/main/java/org/spdx/jacksonstore/JacksonSerializer.java

Large diffs are not rendered by default.

137 changes: 113 additions & 24 deletions src/main/java/org/spdx/jacksonstore/MultiFormatStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,26 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import org.json.JSONObject;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spdx.library.InvalidSPDXAnalysisException;
import org.spdx.library.SpdxConstants;
import org.spdx.core.CoreModelObject;
import org.spdx.core.InvalidSPDXAnalysisException;
import org.spdx.core.TypedValue;
import org.spdx.library.ModelCopyManager;
import org.spdx.library.SpdxModelFactory;
import org.spdx.library.model.v2.SpdxConstantsCompatV2;
import org.spdx.library.model.v2.SpdxDocument;
import org.spdx.storage.IModelStore;
import org.spdx.storage.ISerializableModelStore;
import org.spdx.storage.simple.ExtendedSpdxStore;
Expand All @@ -37,7 +49,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
Expand Down Expand Up @@ -155,15 +167,27 @@ public synchronized void setVerbose(Verbose verbose) {
setMapper();
}


@Override
public synchronized void serialize(OutputStream stream) throws InvalidSPDXAnalysisException, IOException {
serialize(stream, null);
}

/* (non-Javadoc)
* @see org.spdx.storage.ISerializableModelStore#serialize(java.lang.String, java.io.OutputStream)
*/
@Override
public synchronized void serialize(String documentUri, OutputStream stream) throws InvalidSPDXAnalysisException, IOException {
public synchronized void serialize(OutputStream stream, @Nullable CoreModelObject modelObject) throws InvalidSPDXAnalysisException, IOException {
JacksonSerializer serializer = new JacksonSerializer(outputMapper, format, verbose, this);
ObjectNode output = serializer.docToJsonNode(documentUri);
JsonNode output;
if (Objects.nonNull(modelObject)) {
output = serializer.docToJsonNode(modelObject.getObjectUri().substring(0, modelObject.getObjectUri().indexOf('#')));
} else {
List<String> allDocuments = getAllItems(null, SpdxConstantsCompatV2.CLASS_SPDX_DOCUMENT)
.map(tv -> tv.getObjectUri().substring(0, tv.getObjectUri().indexOf('#')))
.collect(Collectors.toList());
output = allDocuments.size() == 1 ? serializer.docToJsonNode(allDocuments.get(0)) :
serializer.docsToJsonNode(allDocuments);
}
JsonGenerator jgen = null;
try {
switch (format) {
Expand Down Expand Up @@ -200,7 +224,7 @@ public synchronized void serialize(String documentUri, OutputStream stream) thro
public static String propertyNameToCollectionPropertyName(String propertyName) {
if (propertyName.endsWith("y")) {
return propertyName.substring(0, propertyName.length()-1) + "ies";
} else if (SpdxConstants.PROP_PACKAGE_LICENSE_INFO_FROM_FILES.equals(propertyName)) {
} else if (SpdxConstantsCompatV2.PROP_PACKAGE_LICENSE_INFO_FROM_FILES.getName().equals(propertyName)) {
return propertyName;
} else {
return propertyName + "s";
Expand All @@ -210,7 +234,7 @@ public static String propertyNameToCollectionPropertyName(String propertyName) {
public static String collectionPropertyNameToPropertyName(String collectionPropertyName) {
if (collectionPropertyName.endsWith("ies")) {
return collectionPropertyName.substring(0, collectionPropertyName.length()-3) + "y";
} else if (SpdxConstants.PROP_PACKAGE_LICENSE_INFO_FROM_FILES.equals(collectionPropertyName)) {
} else if (SpdxConstantsCompatV2.PROP_PACKAGE_LICENSE_INFO_FROM_FILES.getName().equals(collectionPropertyName)) {
return collectionPropertyName;
} else {
return collectionPropertyName.substring(0, collectionPropertyName.length()-1);
Expand All @@ -221,44 +245,109 @@ public static String collectionPropertyNameToPropertyName(String collectionPrope
* @see org.spdx.storage.ISerializableModelStore#deSerialize(java.io.InputStream, boolean)
*/
@Override
public synchronized String deSerialize(InputStream stream, boolean overwrite) throws InvalidSPDXAnalysisException, IOException {
public synchronized SpdxDocument deSerialize(InputStream stream, boolean overwrite) throws InvalidSPDXAnalysisException, IOException {
Objects.requireNonNull(stream, "Input stream must not be null");
if (this.verbose != Verbose.COMPACT) {
throw new InvalidSPDXAnalysisException("Only COMPACT verbose option is supported for deserialization");
}
JsonNode doc;
JsonNode root;
if (Format.XML.equals(format)) {
// Jackson XML mapper does not support deserializing collections or arrays. Use Json-In-Java to convert to JSON
JSONObject jo = XML.toJSONObject(new InputStreamReader(stream, "UTF-8"));
doc = inputMapper.readTree(jo.toString()).get("Document");
root = inputMapper.readTree(jo.toString()).get("Document");
} else {
doc = inputMapper.readTree(stream);
root = inputMapper.readTree(stream);
}
if (Objects.isNull(doc)) {
if (Objects.isNull(root)) {
throw new InvalidSPDXAnalysisException("Missing SPDX Document");
}
JsonNode namespaceNode = doc.get(SpdxConstants.PROP_DOCUMENT_NAMESPACE);
if (Objects.isNull(namespaceNode)) {
throw new InvalidSPDXAnalysisException("Missing document namespace");
List<String> documentNamespaces = new ArrayList<>();
if (root instanceof ArrayNode) {
for (JsonNode docNode:(ArrayNode)root) {
documentNamespaces.add(getNamespaceFromDoc(docNode));
}
} else {
documentNamespaces.add(getNamespaceFromDoc(root));
}
String documentNamespace = namespaceNode.asText();
if (Objects.isNull(documentNamespace) || documentNamespace.isEmpty()) {
throw new InvalidSPDXAnalysisException("Empty document namespace");

Set<String> existingDocNamespaces = this.getDocumentUris();
boolean existing = false;
for (String docNamespace:documentNamespaces) {
if (existingDocNamespaces.contains(docNamespace)) {
existing = true;
break;
}
}
if (this.getDocumentUris().contains(documentNamespace)) {
IModelStoreLock lock = this.enterCriticalSection(documentNamespace, false);
if (existing) {
IModelStoreLock lock = this.enterCriticalSection(false);
try {
if (!overwrite) {
throw new InvalidSPDXAnalysisException("Document namespace "+documentNamespace+" already exists.");
throw new InvalidSPDXAnalysisException("Document namespace(s) already exists.");
}
for (String docNamespace:documentNamespaces) {
if (existingDocNamespaces.contains(docNamespace)) {
this.clear(docNamespace);
}
}
this.clear(documentNamespace);
} finally {
this.leaveCriticalSection(lock);
}
}
JacksonDeSerializer deSerializer = new JacksonDeSerializer(this, format);
deSerializer.storeDocument(documentNamespace, doc);
String docNamespace;
if (root instanceof ArrayNode) {
for (JsonNode doc:(ArrayNode)root) {
deSerializer.storeDocument(getNamespaceFromDoc(doc), doc);
}
docNamespace = getNamespaceFromDoc((ArrayNode)root.get(0));
} else {
deSerializer.storeDocument(getNamespaceFromDoc(root), root);
docNamespace = getNamespaceFromDoc(root);
}
return (SpdxDocument)SpdxModelFactory.inflateModelObject(this, docNamespace + "#" + SpdxConstantsCompatV2.SPDX_DOCUMENT_ID,
SpdxConstantsCompatV2.CLASS_SPDX_DOCUMENT, new ModelCopyManager(),
SpdxConstantsCompatV2.SPEC_TWO_POINT_THREE_VERSION, false, docNamespace);
}

/**
* Get the document namespace from the JSON node representing the SPDX document
* @param docNode root of the SPDX document
* @throws InvalidSPDXAnalysisException on missing document namespace
*/
private String getNamespaceFromDoc(JsonNode docNode) throws InvalidSPDXAnalysisException {
JsonNode namespaceNode = docNode.get(SpdxConstantsCompatV2.PROP_DOCUMENT_NAMESPACE.getName());
if (Objects.isNull(namespaceNode)) {
throw new InvalidSPDXAnalysisException("Missing document namespace");
}
String documentNamespace = namespaceNode.asText();
if (Objects.isNull(documentNamespace) || documentNamespace.isEmpty()) {
throw new InvalidSPDXAnalysisException("Empty document namespace");
}
return documentNamespace;
}

/**
* @param documentNamespace
* @throws InvalidSPDXAnalysisException
*/
public void clear(String documentNamespace) throws InvalidSPDXAnalysisException {
List<TypedValue> valuesToDelete = this.getAllItems(documentNamespace, null).collect(Collectors.toList());
for (TypedValue valueToDelete:valuesToDelete) {
this.delete(valueToDelete.getObjectUri());
}
}

/**
* @return list of SPDX V2 document URI's in this model store
* @throws InvalidSPDXAnalysisException
*/
public Set<String> getDocumentUris() throws InvalidSPDXAnalysisException {
Set<String> retval = new HashSet<>();
this.getAllItems(null, null).forEach(tv -> {
if (tv.getObjectUri().contains("#")) {
retval.add(tv.getObjectUri().substring(0, tv.getObjectUri().indexOf('#')));
}
});
return retval;
}
}
32 changes: 16 additions & 16 deletions src/main/java/org/spdx/jacksonstore/PropertyComparator.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.Map;
import java.util.Objects;

import org.spdx.library.SpdxConstants;
import org.spdx.library.model.v2.SpdxConstantsCompatV2;

/**
* Comparator for property names for different SPDX class types
Expand All @@ -40,25 +40,25 @@
public class PropertyComparator implements Comparator<String> {

static final List<String> DOCUMENT_PROPERTY_ORDER = Arrays.asList(new String[] {
SpdxConstants.PROP_DOCUMENT_NAMESPACE,
SpdxConstants.PROP_SPDX_SPEC_VERSION,
SpdxConstants.PROP_SPDX_CREATION_INFO,
SpdxConstants.PROP_NAME,
SpdxConstants.PROP_SPDX_DATA_LICENSE,
SpdxConstants.RDFS_PROP_COMMENT,
SpdxConstants.PROP_SPDX_EXTERNAL_DOC_REF,
SpdxConstants.PROP_DOCUMENT_DESCRIBES,
SpdxConstants.PROP_DOCUMENT_PACKAGES,
SpdxConstants.PROP_DOCUMENT_FILES,
SpdxConstants.PROP_DOCUMENT_SNIPPETS,
SpdxConstants.PROP_SPDX_EXTRACTED_LICENSES,
SpdxConstants.PROP_ANNOTATION,
SpdxConstants.PROP_DOCUMENT_RELATIONSHIPS
SpdxConstantsCompatV2.PROP_DOCUMENT_NAMESPACE.getName(),
SpdxConstantsCompatV2.PROP_SPDX_SPEC_VERSION.getName(),
SpdxConstantsCompatV2.PROP_SPDX_CREATION_INFO.getName(),
SpdxConstantsCompatV2.PROP_NAME.getName(),
SpdxConstantsCompatV2.PROP_SPDX_DATA_LICENSE.getName(),
SpdxConstantsCompatV2.RDFS_PROP_COMMENT.getName(),
SpdxConstantsCompatV2.PROP_SPDX_EXTERNAL_DOC_REF.getName(),
SpdxConstantsCompatV2.PROP_DOCUMENT_DESCRIBES.getName(),
SpdxConstantsCompatV2.PROP_DOCUMENT_PACKAGES.getName(),
SpdxConstantsCompatV2.PROP_DOCUMENT_FILES.getName(),
SpdxConstantsCompatV2.PROP_DOCUMENT_SNIPPETS.getName(),
SpdxConstantsCompatV2.PROP_SPDX_EXTRACTED_LICENSES.getName(),
SpdxConstantsCompatV2.PROP_ANNOTATION.getName(),
SpdxConstantsCompatV2.PROP_DOCUMENT_RELATIONSHIPS.getName()
});
static final Map<String, List<String>> propertyOrderMap;
static {
HashMap<String, List<String>> hm = new HashMap<>();
hm.put(SpdxConstants.CLASS_SPDX_DOCUMENT, DOCUMENT_PROPERTY_ORDER);
hm.put(SpdxConstantsCompatV2.CLASS_SPDX_DOCUMENT, DOCUMENT_PROPERTY_ORDER);
propertyOrderMap = Collections.unmodifiableMap(hm);
}
private List<String> propertyOrder;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/spdx/jacksonstore/SpdxJsonLDContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import java.util.Optional;
import java.util.Set;

import org.spdx.library.InvalidSPDXAnalysisException;
import org.spdx.core.InvalidSPDXAnalysisException;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down
Loading

0 comments on commit 0a0560d

Please sign in to comment.