org.spdx.rdfparser.model.SpdxElement Maven / Gradle / Ivy
/**
* Copyright (c) 2015 Source Auditor Inc.
*
* 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.
*
*/
package org.spdx.rdfparser.model;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.jena.graph.Node;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spdx.rdfparser.IModelContainer;
import org.spdx.rdfparser.InvalidSPDXAnalysisException;
import org.spdx.rdfparser.RdfModelHelper;
import org.spdx.rdfparser.SpdxRdfConstants;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* An SpdxElement is any thing described in SPDX, either a document or an SpdxItem.
* SpdxElements can be related to other SpdxElements.
*
* All subclasses should override getType, equals and hashCode.
*
* If a subproperty is used for the name property name, getNamePropertyName should be overridden.
*
* If absolute URIs are required, getUri should be overriden.
*
* @author Gary O'Neall
*
*/
public class SpdxElement extends RdfModelObject {
static final Logger logger = LoggerFactory.getLogger(RdfModelObject.class);
protected Annotation[] annotations;
protected String comment;
protected String name;
protected Relationship[] relationships;
/**
* The ID is a special property for the SpdxElement. It used to create
* the unique URI for the item. The URI is the namespace of the modelContainer + id
*/
private String id;
public SpdxElement(IModelContainer modelContainer, Node node) throws InvalidSPDXAnalysisException {
super(modelContainer, node);
getPropertiesFromModel();
SpdxElementFactory.addToCreatedElements(modelContainer, node, this);
}
/* (non-Javadoc)
* @see org.spdx.rdfparser.model.RdfModelObject#getPropertiesFromModel()
*/
@Override
public void getPropertiesFromModel() throws InvalidSPDXAnalysisException {
SpdxElementFactory.addToCreatedElements(modelContainer, node, this);
this.annotations = findAnnotationPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_ANNOTATION);
this.comment = findSinglePropertyValue(SpdxRdfConstants.RDFS_NAMESPACE, SpdxRdfConstants.RDFS_PROP_COMMENT);
this.name = findSinglePropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, this.getNamePropertyName());
this.relationships = findRelationshipPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_RELATIONSHIP);
// ID
this.id = null;
if (this.resource.isURIResource()) {
if (this.resource.getURI().startsWith(modelContainer.getDocumentNamespace())) {
this.id = this.resource.getURI().substring(modelContainer.getDocumentNamespace().length());
} else {
// look for external document ID
String[] parts = this.resource.getURI().split("#");
if (parts.length == 2) {
String docId = this.modelContainer.documentNamespaceToId(parts[0]);
if (docId != null) {
this.id = docId + ":" + parts[1];
}
}
}
}
}
/**
* @param name Name of the element
* @param comment Optional comment on the element
* @param annotations Optional annotations for the element
* @param relationships Optional relationships with other elements
*/
public SpdxElement(String name, String comment, Annotation[] annotations,
Relationship[] relationships) {
this.name = name;
this.comment = comment;
if (annotations != null) {
this.annotations = annotations;
} else {
this.annotations = new Annotation[0];
}
if (relationships != null) {
this.relationships = relationships;
} else {
this.relationships = new Relationship[0];
}
}
@Override
public void populateModel() throws InvalidSPDXAnalysisException {
if (this.resource != null) {
if (this.name != null) {
setPropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, getNamePropertyName(), name);
}
if (this.comment != null) {
setPropertyValue(SpdxRdfConstants.RDFS_NAMESPACE, SpdxRdfConstants.RDFS_PROP_COMMENT, comment);
}
if (this.annotations != null) {
setPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_ANNOTATION, annotations);
}
if (this.relationships != null) {
setPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_RELATIONSHIP, relationships);
}
SpdxElementFactory.addToCreatedElements(modelContainer, node, this);
}
}
/* (non-Javadoc)
* @see org.spdx.rdfparser.model.IRdfModel#verify()
*/
@Override
public List verify() {
String localName = name;
List retval = Lists.newArrayList();
if (this.name == null) {
if (!(this instanceof SpdxSnippet)) {
// TODO: Move this down into the subclasses where they belong -
// a bit of a quick fix while working on SPDX 2.1 changes
retval.add("Missing required name for type "+this.getClass().getName());
}
localName = "UNKNOWN";
}
if (this.annotations != null) {
for (Annotation annotation:annotations) {
List annotationErrors = annotation.verify();
for (String annotationError:annotationErrors) {
retval.add("Annotation error for SPDX element "+localName+": "+annotationError);
}
}
}
return retval;
}
/**
* Add the name of the element to all strings in the list
* @param warnings
*/
protected void addNameToWarnings(List warnings) {
if (warnings == null) {
return;
}
String localName = this.name;
if (localName == null) {
localName = "[UNKNOWN]";
}
for (int i = 0; i < warnings.size(); i++) {
warnings.set(i, warnings.get(i)+" in "+localName);
}
}
/**
* @return the annotations
*/
public Annotation[] getAnnotations() {
if (model != null && this.refreshOnGet) {
try {
Annotation[] refresh = findAnnotationPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE,
SpdxRdfConstants.PROP_ANNOTATION);
if (refresh == null || !arraysEquivalent(refresh, this.annotations, true)) {
this.annotations = refresh;
}
} catch (InvalidSPDXAnalysisException e) {
logger.error("Invalid annotations in the model",e);
}
}
return annotations;
}
/**
* @param annotations the annotations to set
* @throws InvalidSPDXAnalysisException
*/
public void setAnnotations(Annotation[] annotations) throws InvalidSPDXAnalysisException {
if (annotations == null) {
this.annotations = new Annotation[0];
} else {
this.annotations = annotations;
}
setPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_ANNOTATION, annotations);
}
/**
* @return the comment
*/
public String getComment() {
if (this.resource != null && this.refreshOnGet) {
this.comment = findSinglePropertyValue(SpdxRdfConstants.RDFS_NAMESPACE, SpdxRdfConstants.RDFS_PROP_COMMENT);
}
return comment;
}
/**
* @param comment the comment to set
*/
public void setComment(String comment) {
this.comment = comment;
setPropertyValue(SpdxRdfConstants.RDFS_NAMESPACE, SpdxRdfConstants.RDFS_PROP_COMMENT, comment);
}
/**
* @return the name
*/
public String getName() {
if (this.resource != null && this.refreshOnGet) {
this.name = findSinglePropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, this.getNamePropertyName());
}
return name;
}
/**
* @return the property name used for the Name property. Override this function if using a subproperty of SPDX Name
*/
protected String getNamePropertyName() {
return SpdxRdfConstants.PROP_NAME;
}
/**
* Set the name
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
setPropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, getNamePropertyName(), name);
}
/**
* @return the relationships
*/
public Relationship[] getRelationships() {
if (model != null && this.refreshOnGet) {
try {
Relationship[] refresh = findRelationshipPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE,
SpdxRdfConstants.PROP_RELATIONSHIP);
if (refresh != null && !arraysEquivalent(refresh, this.relationships, true)) {
this.relationships = refresh;
}
} catch (InvalidSPDXAnalysisException e) {
logger.error("Invalid relationships in the model",e);
}
}
return relationships;
}
/**
* @param relationships the relationships to set
* @throws InvalidSPDXAnalysisException
*/
public void setRelationships(Relationship[] relationships) throws InvalidSPDXAnalysisException {
if (relationships == null) {
this.relationships = new Relationship[0];
} else {
this.relationships = relationships;
}
setPropertyValues(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_RELATIONSHIP, relationships);
}
/**
* The ID is a unique identify for the SPDX element. It is only required
* if this element is to be used outside of the RDF model containing the element.
* @return the id
*/
public String getId() {
// the ID from the resource URI.
if (this.resource != null) {
if (this.resource.isURIResource()) {
if (this.resource.getURI().startsWith(modelContainer.getDocumentNamespace())) {
this.id = this.resource.getURI().substring(modelContainer.getDocumentNamespace().length());
}
}
}
if (id == null) {
// Use the hash function to create a unique ID for this element
return String.valueOf(this.hashCode());
} else {
return id;
}
}
/**
* The ID is a unique identify for the SPDX element. It is only required
* if this element is to be used outside of the RDF model containing the element.
* @param id
* @throws InvalidSPDXAnalysisException
*/
public void setId(String id) throws InvalidSPDXAnalysisException {
if (this.modelContainer != null) {
if (this.resource != null) {
throw(new InvalidSPDXAnalysisException("Can not set a file ID for an SPDX element already in an RDF Model. You must create a new SPDX File with this ID."));
}
}
this.id = id;
}
/* (non-Javadoc)
* @see org.spdx.rdfparser.model.RdfModelObject#getUri(org.spdx.rdfparser.IModelContainer)
*/
@Override
public String getUri(IModelContainer modelContainer) throws InvalidSPDXAnalysisException {
if (this.node != null && this.node.isURI()) {
return this.node.getURI();
} else {
if (this.id == null || this.id.isEmpty()) {
this.id = modelContainer.getNextSpdxElementRef();
} else if (modelContainer.spdxElementRefExists(this.id)) {
throw(new InvalidSPDXAnalysisException("Duplicate ID: "+this.id));
} else {
modelContainer.addSpdxElementRef(id);
}
return modelContainer.getDocumentNamespace() + this.id;
}
}
/* (non-Javadoc)
* @see org.spdx.rdfparser.model.RdfModelObject#getType(org.apache.jena.rdf.model.Model)
*/
@Override
public Resource getType(Model model) {
return model.createResource(SpdxRdfConstants.SPDX_NAMESPACE + SpdxRdfConstants.CLASS_SPDX_ELEMENT);
}
protected Annotation[] cloneAnnotations() {
if (this.annotations == null) {
return null;
}
Annotation[] clonedAnnotations = new Annotation[this.annotations.length];
for (int i = 0; i < this.annotations.length; i++) {
clonedAnnotations[i] = this.annotations[i].clone();
}
return clonedAnnotations;
}
protected Relationship[] cloneRelationships(Map clonedElementIds) {
if (this.relationships == null) {
return null;
}
Relationship[] clonedRelationships =new Relationship[this.relationships.length];
for (int i = 0; i < this.relationships.length; i++) {
clonedRelationships[i] = this.relationships[i].clone(clonedElementIds);
}
return clonedRelationships;
}
@Override
public SpdxElement clone() {
//NOTE: We don't call super.clone since we must handle the special case avoiding infinite recursion
return clone(Maps.newHashMap());
}
/**
* Clones this element, but prevents infinite recursion by
* keeping track of all elements which have been cloned
* @param clonedElementIds element ID's fo all elements which have been cloned
* @return
*/
public SpdxElement clone(Map clonedElementIds) {
if (clonedElementIds.containsKey(this.getId())) {
return clonedElementIds.get(this.getId());
}
SpdxElement retval = new SpdxElement(this.name, this.comment, cloneAnnotations(),
null);
clonedElementIds.put(this.getId(), retval);
try {
retval.setRelationships(cloneRelationships(clonedElementIds));
} catch (InvalidSPDXAnalysisException e) {
logger.error("Unexected error setting relationships during clone",e);
}
return retval;
}
/* (non-Javadoc)
* @see org.spdx.rdfparser.model.RdfModelObject#equivalent(org.spdx.rdfparser.model.RdfModelObject)
*/
@Override
public boolean equivalent(IRdfModel o) {
return equivalent(o, true);
}
/**
* Test for equivalent
* @param o
* @param testRelationships If true, test relationships
* @return
*/
public boolean equivalent(IRdfModel o, boolean testRelationships) {
if (o == this) {
return true;
}
if (!(o instanceof SpdxElement)) {
return false;
}
SpdxElement comp = (SpdxElement)o;
if (testRelationships && !arraysEquivalent(comp.getRelationships(), this.getRelationships(), false)) {
return false;
}
return (Objects.equal(comp.getName(), this.getName()) &&
arraysEquivalent(comp.getAnnotations(), this.getAnnotations(), testRelationships) && RdfModelHelper.stringsEquivalent(comp.getComment(), this.getComment()));
}
@Override
public String toString() {
if (this.name == null) {
return super.toString();
} else {
return this.name;
}
}
/**
* @param describesRelationship
* @throws InvalidSPDXAnalysisException
*/
public void addRelationship(Relationship relationship) throws InvalidSPDXAnalysisException {
if (relationship != null) {
this.relationships = Arrays.copyOf(this.relationships, this.relationships.length + 1);
this.relationships[this.relationships.length-1] = relationship;
addPropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_RELATIONSHIP, relationship);
}
}
/**
* @param annotation
* @throws InvalidSPDXAnalysisException
*/
public void addAnnotation(Annotation annotation) throws InvalidSPDXAnalysisException {
if (annotation != null) {
this.annotations = Arrays.copyOf(this.annotations, this.annotations.length + 1);
this.annotations[this.annotations.length-1] = annotation;
addPropertyValue(SpdxRdfConstants.SPDX_NAMESPACE, SpdxRdfConstants.PROP_ANNOTATION, annotation);
}
}
/**
* Compares 2 arrays to see if the property values for the element RdfModelObjects are the same independent of
* order and considering nulls
* @param array1
* @param array2
* @param testRelationships If true, test relationships for an SpdxElement (used to manage infinite recursion on circular relationships)
* @return
*/
@Override
public boolean arraysEquivalent(IRdfModel[] array1, IRdfModel[] array2) {
return arraysEquivalent(array1, array2, true);
}
/**
* Compares 2 arrays to see if the property values for the element RdfModelObjects are the same independent of
* order and considering nulls
* @param array1
* @param array2
* @param testRelationships If true, test relationships for an SpdxElement (used to manage infinite recursion on circular relationships)
* @return
*/
public boolean arraysEquivalent(IRdfModel[] array1, IRdfModel[] array2, boolean testRelationships) {
if (array1 == null) {
return array2 == null;
}
if (array2 == null) {
return false;
}
if (array1.length != array2.length) {
return false;
}
Set foundIndexes = Sets.newHashSet();
for (int i = 0; i < array1.length; i++) {
boolean found = false;
for (int j = 0; j < array2.length; j++) {
if (!foundIndexes.contains(j)) {
if (array1[i] instanceof SpdxElement && array2[j] instanceof SpdxElement) {
if (equivalentConsideringNull((SpdxElement)array1[i],(SpdxElement)array2[j], testRelationships)) {
found = true;
foundIndexes.add(j);
break;
}
} else {
if (equivalentConsideringNull(array1[i],array2[j])) {
found = true;
foundIndexes.add(j);
break;
}
}
}
}
if (!found) {
return false;
}
}
return true;
}
/**
* Compares the properties of two RdfModelObjects considering possible null values
* @param o1
* @param o2
* @return
*/
@Override
public boolean equivalentConsideringNull(IRdfModel o1, IRdfModel o2) {
if (o1 instanceof SpdxElement && o2 instanceof SpdxElement) {
return this.equivalentConsideringNull((SpdxElement)o1, (SpdxElement)o2, true);
} else {
return super.equivalentConsideringNull(o1, o2);
}
}
/**
* Compares the properties of two RdfModelObjects considering possible null values
* @param o1
* @param o2
* @param testRelationships If true, test relationships for an SpdxElement (used to manage infinite recursion on circular relationships)
* @return
*/
public boolean equivalentConsideringNull(SpdxElement o1, SpdxElement o2, boolean testRelationship) {
if (o1 == null) {
return (o2 == null);
} else {
return o1.equivalent(o2, testRelationship);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy