diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..f772715ae --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarCloud packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml deleted file mode 100644 index 74c342fe0..000000000 --- a/.github/workflows/maven.yml +++ /dev/null @@ -1,24 +0,0 @@ -# This workflow will build a Java project with Maven -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven - -name: Java CI with Maven - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Build with Maven - run: mvn -B package --file pom.xml diff --git a/README.md b/README.md index f8c9a589c..f8061459c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ Java library which implements the Java object model for SPDX and provides useful helper functions. +# Code quality badges + +| [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=java-spdx-library&metric=bugs)](https://sonarcloud.io/dashboard?id=java-spdx-library) | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=java-spdx-library&metric=security_rating)](https://sonarcloud.io/dashboard?id=java-spdx-library) | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=java-spdx-library&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=java-spdx-library) | [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=java-spdx-library&metric=sqale_index)](https://sonarcloud.io/dashboard?id=java-spdx-library) | + ## Storage Interface The Spdx-Java-Library allows for different implementations of SPDX object storage. The storage facility implements the org.spdx.storage.IModelStore interface. This is a low level Service Provider Interface (SPI). The ISerializableModelStore extends the IModelStore and supports serializing and de-serializing the store to an I/O Stream. This interface is currently used to implement JSON, XML, YAML, and RDF/XML formats. The default storage interface is an in-memory Map which should be sufficient for light weight usage of the library. diff --git a/pom.xml b/pom.xml index accc0d871..a4e365ac3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.spdx java-spdx-library - 1.0.5-SNAPSHOT + 1.0.6 jar java-spdx-library @@ -43,6 +43,9 @@ UTF-8 + https://sonarcloud.io + spdx + java-spdx-library diff --git a/src/main/java/org/spdx/library/ModelCopyManager.java b/src/main/java/org/spdx/library/ModelCopyManager.java index e514ca371..49596d69c 100644 --- a/src/main/java/org/spdx/library/ModelCopyManager.java +++ b/src/main/java/org/spdx/library/ModelCopyManager.java @@ -58,7 +58,7 @@ public class ModelCopyManager { * Create a ModelCopyManager with default options */ public ModelCopyManager() { - + // Required empty constructor } /** @@ -153,48 +153,86 @@ public void copy(IModelStore toStore, String toDocumentUri, String toId, IModelS List propertyNames = fromStore.getPropertyValueNames(fromDocumentUri, fromId); for (String propName:propertyNames) { if (fromStore.isCollectionProperty(fromDocumentUri, fromId, propName)) { - Iterator fromListIter = fromStore.listValues(fromDocumentUri, fromId, propName); - while (fromListIter.hasNext()) { - Object listItem = fromListIter.next(); - Object toStoreItem; - if (listItem instanceof IndividualUriValue) { - toStoreItem = new SimpleUriValue((IndividualUriValue)listItem); - } else if (listItem instanceof TypedValue) { - TypedValue listItemTv = (TypedValue)listItem; - if (toStore.equals(fromStore) && toDocumentUri.equals(fromDocumentUri)) { - toStoreItem = listItemTv; - } else { - toStoreItem = copy(toStore, toDocumentUri, fromStore, fromDocumentUri, - listItemTv.getId(), listItemTv.getType()); - } - } else { - toStoreItem = listItem; - } - toStore.addValueToCollection(toDocumentUri, toId, propName, toStoreItem); - } + copyCollectionProperty(toStore, toDocumentUri, toId, fromStore, fromDocumentUri, fromId, propName); } else { - Optional result = fromStore.getValue(fromDocumentUri, fromId, propName); - if (result.isPresent()) { - if (result.get() instanceof IndividualUriValue) { - toStore.setValue(toDocumentUri, toId, propName, new SimpleUriValue((IndividualUriValue)result.get())); - } else if (result.get() instanceof TypedValue) { - TypedValue tv = (TypedValue)result.get(); - if (fromStore.equals(toStore) && fromDocumentUri.equals(toDocumentUri)) { - toStore.setValue(toDocumentUri, toId, propName, tv); - } else { - toStore.setValue(toDocumentUri, toId, propName, - copy(toStore, toDocumentUri, fromStore, fromDocumentUri, - tv.getId(), tv.getType())); - } - } else { - toStore.setValue(toDocumentUri, toId, propName, result.get()); - } - } + copyIndividualProperty(toStore, toDocumentUri, toId, fromStore, fromDocumentUri, fromId, propName); } } } /** + * Copies an individual property value (non-collection property value) + * @param toStore Model Store to copy to + * @param toId Id to use in the copy + * @param toDocumentUri Target document URI + * @param fromStore Model Store containing the source item + * @param fromDocumentUri Document URI for the source item + * @param fromId ID source ID + * @param propName Name of the property + * @throws InvalidSPDXAnalysisException + */ + private void copyIndividualProperty(IModelStore toStore, String toDocumentUri, String toId, IModelStore fromStore, + String fromDocumentUri, String fromId, String propName) throws InvalidSPDXAnalysisException { + if (fromStore.isCollectionProperty(fromDocumentUri, fromId, propName)) { + throw new InvalidSPDXAnalysisException("Property "+propName+" is a collection type"); + } + Optional result = fromStore.getValue(fromDocumentUri, fromId, propName); + if (result.isPresent()) { + if (result.get() instanceof IndividualUriValue) { + toStore.setValue(toDocumentUri, toId, propName, new SimpleUriValue((IndividualUriValue)result.get())); + } else if (result.get() instanceof TypedValue) { + TypedValue tv = (TypedValue)result.get(); + if (fromStore.equals(toStore) && fromDocumentUri.equals(toDocumentUri)) { + toStore.setValue(toDocumentUri, toId, propName, tv); + } else { + toStore.setValue(toDocumentUri, toId, propName, + copy(toStore, toDocumentUri, fromStore, fromDocumentUri, + tv.getId(), tv.getType())); + } + } else { + toStore.setValue(toDocumentUri, toId, propName, result.get()); + } + } + } + + /** + * Copies a property which is is a collection + * @param toStore Model Store to copy to + * @param toId Id to use in the copy + * @param toDocumentUri Target document URI + * @param fromStore Model Store containing the source item + * @param fromDocumentUri Document URI for the source item + * @param fromId ID source ID + * @param propName Name of the property + * @throws InvalidSPDXAnalysisException + */ + private void copyCollectionProperty(IModelStore toStore, String toDocumentUri, String toId, IModelStore fromStore, + String fromDocumentUri, String fromId, String propName) throws InvalidSPDXAnalysisException { + if (!fromStore.isCollectionProperty(fromDocumentUri, fromId, propName)) { + throw new InvalidSPDXAnalysisException("Property "+propName+" is not a collection type"); + } + Iterator fromListIter = fromStore.listValues(fromDocumentUri, fromId, propName); + while (fromListIter.hasNext()) { + Object listItem = fromListIter.next(); + Object toStoreItem; + if (listItem instanceof IndividualUriValue) { + toStoreItem = new SimpleUriValue((IndividualUriValue)listItem); + } else if (listItem instanceof TypedValue) { + TypedValue listItemTv = (TypedValue)listItem; + if (toStore.equals(fromStore) && toDocumentUri.equals(fromDocumentUri)) { + toStoreItem = listItemTv; + } else { + toStoreItem = copy(toStore, toDocumentUri, fromStore, fromDocumentUri, + listItemTv.getId(), listItemTv.getType()); + } + } else { + toStoreItem = listItem; + } + toStore.addValueToCollection(toDocumentUri, toId, propName, toStoreItem); + } + } + + /** * Copy an item from one Model Object Store to another using the source ID for the target unless it is anonymous * @param toStore Model Store to copy to * @param toDocumentUri Target document URI diff --git a/src/main/java/org/spdx/library/SpdxConstants.java b/src/main/java/org/spdx/library/SpdxConstants.java index 54bfd838d..453c56cba 100644 --- a/src/main/java/org/spdx/library/SpdxConstants.java +++ b/src/main/java/org/spdx/library/SpdxConstants.java @@ -360,11 +360,14 @@ public class SpdxConstants { // Download Location Format private static final String SUPPORTED_DOWNLOAD_REPOS = "(git|hg|svn|bzr)"; - private static final String URL_PATTERN = "(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/|ssh:\\/\\/|git:\\/\\/|svn:\\/\\/|sftp:\\/\\/|ftp:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)"; + private static final String URL_PATTERN = "(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/|ssh:\\/\\/|git:\\/\\/|svn:\\/\\/|sftp:\\/\\/|ftp:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+){0,100}\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)"; private static final String GIT_PATTERN = "(git\\+git@[a-zA-Z0-9\\.]+:[a-zA-Z0-9]+)"; private static final String BAZAAR_PATTERN = "(bzr\\+lp:[a-zA-Z0-9\\.]+)"; public static final Pattern DOWNLOAD_LOCATION_PATTERN = Pattern.compile("^(NONE|NOASSERTION|(("+SUPPORTED_DOWNLOAD_REPOS+"\\+)?"+URL_PATTERN+")|"+GIT_PATTERN+"|"+BAZAAR_PATTERN+")$", Pattern.CASE_INSENSITIVE); + // License list version Format + + public static final Pattern LICENSE_LIST_VERSION_PATTERN = Pattern.compile("^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+"); // Standard value strings public static String NONE_VALUE = "NONE"; public static String NOASSERTION_VALUE = "NOASSERTION"; diff --git a/src/main/java/org/spdx/library/model/ModelObject.java b/src/main/java/org/spdx/library/model/ModelObject.java index 553c1eeb3..c4032b3d5 100644 --- a/src/main/java/org/spdx/library/model/ModelObject.java +++ b/src/main/java/org/spdx/library/model/ModelObject.java @@ -711,56 +711,17 @@ public boolean equivalent(ModelObject compare, boolean ignoreRelatedElements) th continue; } if (comparePropertyValueNames.contains(propertyName)) { - Optional myValue = this.getObjectPropertyValue(propertyName); - Optional compareValue = compare.getObjectPropertyValue(propertyName); - if (!myValue.isPresent()) { - if (compareValue.isPresent()) { - if (compareValue.get() instanceof ModelCollection) { - if (((ModelCollection)compareValue.get()).size() > 0) { - return false; - } - } else { - return false; - } - } - } else if (!compareValue.isPresent()) { - if (myValue.get() instanceof ModelCollection) { - if (((ModelCollection)myValue.get()).size() > 0) { - return false; - } - } else { - return false; - } - } else if (myValue.get() instanceof ModelCollection && compareValue.get() instanceof ModelCollection) { - List myList = ((ModelCollection)myValue.get()).toImmutableList(); - List compareList = ((ModelCollection)compareValue.get()).toImmutableList(); - if (!listsEquivalent(myList, compareList, ignoreRelatedElements)) { - return false; - } - } else if (myValue.get() instanceof List && compareValue.get() instanceof List) { - if (!listsEquivalent((List)myValue.get(), (List)compareValue.get(), ignoreRelatedElements)) { - return false; - } - } else if (myValue.get() instanceof IndividualUriValue && compareValue.get() instanceof IndividualUriValue) { - if (!Objects.equals(((IndividualUriValue)myValue.get()).getIndividualURI(), ((IndividualUriValue)compareValue.get()).getIndividualURI())) { - return false; - } - // Note: we must check the IndividualValue before the ModelObject types since the IndividualValue takes precedence - } else if (myValue.get() instanceof ModelObject && compareValue.get() instanceof ModelObject) { - if (!((ModelObject)myValue.get()).equivalent(((ModelObject)compareValue.get()), - SpdxConstants.PROP_RELATED_SPDX_ELEMENT.equals(propertyName) ? true : ignoreRelatedElements)) { - return false; - } - - } else if (!OptionalObjectsEquivalent(myValue, compareValue)) { // Present, not a list, and not a TypedValue - return false; + if (!propertyValuesEquivalent(propertyName, this.getObjectPropertyValue(propertyName), + compare.getObjectPropertyValue(propertyName), ignoreRelatedElements)) { + return false; } comparePropertyValueNames.remove(propertyName); } else { // No property value - if (this.getObjectPropertyValue(propertyName).isPresent()) { - if (this.getObjectPropertyValue(propertyName).get() instanceof ModelCollection) { - if (((ModelCollection)(this.getObjectPropertyValue(propertyName).get())).size() > 0) { + Optional propertyValue = this.getObjectPropertyValue(propertyName); + if (propertyValue.isPresent()) { + if (propertyValue.get() instanceof ModelCollection) { + if (((ModelCollection)(propertyValue.get())).size() > 0) { return false; } } else { @@ -778,6 +739,61 @@ public boolean equivalent(ModelObject compare, boolean ignoreRelatedElements) th } /** + * @param propertyName Name of the property + * @param valueA value to compare + * @param valueB value to compare + * @param ignoreRelatedElements if true, do not compare properties relatedSpdxElement - used to prevent infinite recursion + * @return true if the property values are equivalent + * @throws InvalidSPDXAnalysisException + */ + private boolean propertyValuesEquivalent(String propertyName, Optional valueA, + Optional valueB, boolean ignoreRelatedElements) throws InvalidSPDXAnalysisException { + if (!valueA.isPresent()) { + if (valueB.isPresent()) { + if (valueB.get() instanceof ModelCollection) { + if (((ModelCollection)valueB.get()).size() > 0) { + return false; + } + } else { + return false; + } + } + } else if (!valueB.isPresent()) { + if (valueA.get() instanceof ModelCollection) { + if (((ModelCollection)valueA.get()).size() > 0) { + return false; + } + } else { + return false; + } + } else if (valueA.get() instanceof ModelCollection && valueB.get() instanceof ModelCollection) { + List myList = ((ModelCollection)valueA.get()).toImmutableList(); + List compareList = ((ModelCollection)valueB.get()).toImmutableList(); + if (!listsEquivalent(myList, compareList, ignoreRelatedElements)) { + return false; + } + } else if (valueA.get() instanceof List && valueB.get() instanceof List) { + if (!listsEquivalent((List)valueA.get(), (List)valueB.get(), ignoreRelatedElements)) { + return false; + } + } else if (valueA.get() instanceof IndividualUriValue && valueB.get() instanceof IndividualUriValue) { + if (!Objects.equals(((IndividualUriValue)valueA.get()).getIndividualURI(), ((IndividualUriValue)valueB.get()).getIndividualURI())) { + return false; + } + // Note: we must check the IndividualValue before the ModelObject types since the IndividualValue takes precedence + } else if (valueA.get() instanceof ModelObject && valueB.get() instanceof ModelObject) { + if (!((ModelObject)valueA.get()).equivalent(((ModelObject)valueB.get()), + SpdxConstants.PROP_RELATED_SPDX_ELEMENT.equals(propertyName) ? true : ignoreRelatedElements)) { + return false; + } + + } else if (!OptionalObjectsEquivalent(valueA, valueB)) { // Present, not a list, and not a TypedValue + return false; + } + return true; + } + + /** * Compares 2 simple optional objects considering NONE and NOASSERTION values which are equivalent to their strings * @param valueA * @param valueB diff --git a/src/main/java/org/spdx/library/model/RelatedElementCollection.java b/src/main/java/org/spdx/library/model/RelatedElementCollection.java index 411489f97..408ffffad 100644 --- a/src/main/java/org/spdx/library/model/RelatedElementCollection.java +++ b/src/main/java/org/spdx/library/model/RelatedElementCollection.java @@ -177,8 +177,9 @@ public boolean remove(Object o) { if (rel instanceof Relationship) { Relationship relationship = (Relationship)rel; try { - if (relationship.getRelatedSpdxElement().isPresent() && - relationship.getRelatedSpdxElement().get().equals(o) && + Optional relatedElement = relationship.getRelatedSpdxElement(); + if (relatedElement.isPresent() && + relatedElement.get().equals(o) && relationship.getRelationshipType().equals(relationshipTypeFilter)) { return relationshipCollection.remove(relationship); } diff --git a/src/main/java/org/spdx/library/model/SpdxCreatorInformation.java b/src/main/java/org/spdx/library/model/SpdxCreatorInformation.java index 3be3c613c..63cb95cf0 100644 --- a/src/main/java/org/spdx/library/model/SpdxCreatorInformation.java +++ b/src/main/java/org/spdx/library/model/SpdxCreatorInformation.java @@ -23,6 +23,8 @@ import java.util.Objects; import java.util.Optional; +import javax.annotation.Nullable; + import org.spdx.library.InvalidSPDXAnalysisException; import org.spdx.library.ModelCopyManager; import org.spdx.library.SpdxConstants; @@ -249,9 +251,17 @@ protected List _verify(List verifiedIds) { * @param version * @return */ - private String verifyLicenseListVersion(String version) { + private @Nullable String verifyLicenseListVersion(String version) { // Currently, there is no rules for the format of a version - return null; + if (Objects.isNull(version)) { + return null; + } else { + if (SpdxConstants.LICENSE_LIST_VERSION_PATTERN.matcher(version).matches()) { + return null; + } else { + return "License list version does not match the pattern M.N"; + } + } } } diff --git a/src/main/java/org/spdx/library/model/SpdxDocument.java b/src/main/java/org/spdx/library/model/SpdxDocument.java index 1ae481ff0..5779a9220 100644 --- a/src/main/java/org/spdx/library/model/SpdxDocument.java +++ b/src/main/java/org/spdx/library/model/SpdxDocument.java @@ -243,7 +243,8 @@ protected List _verify(List verifiedIds) { List retval = super._verify(verifiedIds); // name try { - if (!getName().isPresent() || getName().get().isEmpty()) { + Optional name = getName(); + if (!name.isPresent() || name.get().isEmpty()) { retval.add("Missing required document name"); } } catch (InvalidSPDXAnalysisException e1) { diff --git a/src/main/java/org/spdx/library/model/SpdxNoAssertionElement.java b/src/main/java/org/spdx/library/model/SpdxNoAssertionElement.java index dd4b91291..6db337962 100644 --- a/src/main/java/org/spdx/library/model/SpdxNoAssertionElement.java +++ b/src/main/java/org/spdx/library/model/SpdxNoAssertionElement.java @@ -74,7 +74,7 @@ public boolean equals(Object o) { @Override public Optional getName() throws InvalidSPDXAnalysisException { - return Optional.of("NOASSERTION"); + return Optional.of(NOASSERTION_ELEMENT_ID); } @Override diff --git a/src/main/java/org/spdx/library/model/SpdxPackage.java b/src/main/java/org/spdx/library/model/SpdxPackage.java index e99b286cb..9dfb74d0a 100644 --- a/src/main/java/org/spdx/library/model/SpdxPackage.java +++ b/src/main/java/org/spdx/library/model/SpdxPackage.java @@ -520,29 +520,11 @@ protected List _verify(List verifiedIds) { retval.add("Invalid package declared license: "+e.getMessage()); } try { - if (getLicenseInfoFromFiles().size() == 0 && filesAnalyzed) { - retval.add("Missing required license information from files for "+pkgName); - } else { - boolean foundNonSimpleLic = false; - for (AnyLicenseInfo lic:getLicenseInfoFromFiles()) { - List verify = lic.verify(verifiedIds); - addNameToWarnings(verify); - retval.addAll(verify); - if (!(lic instanceof SimpleLicensingInfo || - lic instanceof SpdxNoAssertionLicense || - lic instanceof SpdxNoneLicense || - lic instanceof OrLaterOperator || - lic instanceof WithExceptionOperator)) { - foundNonSimpleLic = true; - } - } - if (foundNonSimpleLic) { - retval.add("license info from files contains complex licenses for "+pkgName); - } - } + verifyLicenseInfosInFiles(getLicenseInfoFromFiles(), filesAnalyzed, pkgName, verifiedIds, retval); } catch (InvalidSPDXAnalysisException e) { - retval.add("Invalid license infos from file: "+e.getMessage()); - } + retval.add("Invalid license infos from file: "+e.getMessage()); + } + // files depends on if the filesAnalyzed flag try { if (getFiles().size() == 0) { @@ -614,7 +596,31 @@ protected List _verify(List verifiedIds) { return retval; } - @Override + private void verifyLicenseInfosInFiles(Collection licenseInfoFromFiles, + boolean filesAnalyzed, String pkgName, List verifiedIds, List retval) { + if (licenseInfoFromFiles.size() == 0 && filesAnalyzed) { + retval.add("Missing required license information from files for "+pkgName); + } else { + boolean foundNonSimpleLic = false; + for (AnyLicenseInfo lic:licenseInfoFromFiles) { + List verify = lic.verify(verifiedIds); + addNameToWarnings(verify); + retval.addAll(verify); + if (!(lic instanceof SimpleLicensingInfo || + lic instanceof SpdxNoAssertionLicense || + lic instanceof SpdxNoneLicense || + lic instanceof OrLaterOperator || + lic instanceof WithExceptionOperator)) { + foundNonSimpleLic = true; + } + } + if (foundNonSimpleLic) { + retval.add("license info from files contains complex licenses for "+pkgName); + } + } + } + + @Override public int compareTo(SpdxPackage pkg) { // sort order is determined by the name and the version diff --git a/src/main/java/org/spdx/library/model/license/CrossRef.java b/src/main/java/org/spdx/library/model/license/CrossRef.java index 31bd16c83..219f5dfa2 100644 --- a/src/main/java/org/spdx/library/model/license/CrossRef.java +++ b/src/main/java/org/spdx/library/model/license/CrossRef.java @@ -315,7 +315,7 @@ public String toString(){ } catch (InvalidSPDXAnalysisException e) { timestamp = "N/A"; } - String crossRefDetails = String.format("{%s: %s,%s: %b,%s: %b,%s: %b,%s: %s,%s: %s}", + String crossRefDetails = String.format("{%s: %s,%s: %s,%s: %s,%s: %s,%s: %s,%s: %s}", SpdxConstants.PROP_CROSS_REF_URL, url, SpdxConstants.PROP_CROSS_REF_IS_VALID, isValid, SpdxConstants.PROP_CROSS_REF_IS_LIVE, isLive, @@ -361,6 +361,7 @@ public CrossRefBuilder(IModelStore modelStore, String documentUri, String id, this.documentUri = documentUri; this.id = id; this.url = url; + this.copyManager = copyManager; } public CrossRefBuilder setMatch(@Nullable String match) { diff --git a/src/main/java/org/spdx/library/model/license/LicenseExpressionParser.java b/src/main/java/org/spdx/library/model/license/LicenseExpressionParser.java index 08fb007a7..e5049c38f 100644 --- a/src/main/java/org/spdx/library/model/license/LicenseExpressionParser.java +++ b/src/main/java/org/spdx/library/model/license/LicenseExpressionParser.java @@ -177,8 +177,11 @@ private static AnyLicenseInfo parseLicenseExpression(String[] tokens, IModelStor } token = tokens[tokenIndex++]; LicenseException licenseException = null; + Optional exceptionId = Optional.empty(); if (LicenseInfoFactory.isSpdxListedExceptionId(token)) { - Optional exceptionId = LicenseInfoFactory.listedExceptionIdCaseSensitive(token); + exceptionId = LicenseInfoFactory.listedExceptionIdCaseSensitive(token); + } + if (exceptionId.isPresent()) { licenseException = LicenseInfoFactory.getListedExceptionById(exceptionId.get()); } else { licenseException = (LicenseException) SpdxModelFactory.createModelObject(store, @@ -260,9 +263,13 @@ private static AnyLicenseInfo parseSimpleLicenseToken(String token, IModelStore if (token.contains(":")) { // External License Ref return new ExternalExtractedLicenseInfo(store, documentUri, token, copyManager, true); - } else if (LicenseInfoFactory.isSpdxListedLicenseId(token)) { + } + Optional licenseId = Optional.empty(); + if (LicenseInfoFactory.isSpdxListedLicenseId(token)) { // listed license - Optional licenseId = LicenseInfoFactory.listedLicenseIdCaseSensitive(token); + licenseId = LicenseInfoFactory.listedLicenseIdCaseSensitive(token); + } + if (licenseId.isPresent()) { if (!store.exists(documentUri, licenseId.get())) { SpdxListedLicense listedLicense = LicenseInfoFactory.getListedLicenseById(licenseId.get()); if (Objects.nonNull(copyManager)) { diff --git a/src/main/java/org/spdx/library/model/license/ListedLicenses.java b/src/main/java/org/spdx/library/model/license/ListedLicenses.java index eaf2f6578..f906528ee 100644 --- a/src/main/java/org/spdx/library/model/license/ListedLicenses.java +++ b/src/main/java/org/spdx/library/model/license/ListedLicenses.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.locks.ReadWriteLock; @@ -49,7 +50,7 @@ public class ListedLicenses { Properties licenseProperties; boolean onlyUseLocalLicenses; private IListedLicenseStore licenseModelStore; - private static volatile ListedLicenses listedLicenses = null; + private static ListedLicenses listedLicenses = null; /** * Lock for any modifications to the underlying licenseModelStore */ @@ -126,17 +127,26 @@ private void initializeLicenseModelStore() { public static ListedLicenses getListedLicenses() { - if (listedLicenses == null) { - listedLicenseModificationLock.writeLock().lock(); - try { - if (listedLicenses == null) { - listedLicenses = new ListedLicenses(); - } - } finally { - listedLicenseModificationLock.writeLock().unlock(); - } - } - return listedLicenses; + + ListedLicenses retval = null; + listedLicenseModificationLock.readLock().lock(); + try { + retval = listedLicenses; + } finally { + listedLicenseModificationLock.readLock().unlock(); + } + if (Objects.isNull(retval)) { + listedLicenseModificationLock.writeLock().lock(); + try { + if (listedLicenses == null) { + listedLicenses = new ListedLicenses(); + } + retval = listedLicenses; + } finally { + listedLicenseModificationLock.writeLock().unlock(); + } + } + return retval; } /** diff --git a/src/main/java/org/spdx/storage/listedlicense/SpdxListedLicenseModelStore.java b/src/main/java/org/spdx/storage/listedlicense/SpdxListedLicenseModelStore.java index 445e40604..5c75a75e8 100644 --- a/src/main/java/org/spdx/storage/listedlicense/SpdxListedLicenseModelStore.java +++ b/src/main/java/org/spdx/storage/listedlicense/SpdxListedLicenseModelStore.java @@ -182,12 +182,6 @@ private void loadIds() throws InvalidSPDXAnalysisException { } catch (IOException e) { logger.warn("Unable to close JSON TOC reader"); } - } else if (tocStream != null) { - try { - tocStream.close(); - } catch (IOException e) { - logger.warn("Unable to close JSON TOC input stream"); - } } } } finally { @@ -643,7 +637,7 @@ public Iterator listValues(String documentUri, String id, String propert isLicenseId = true; } else if (exceptionIds.containsKey(id.toLowerCase())) { isExceptionId = true; - } else if (Objects.nonNull(crossRef)) { + } else { crossRef = crossRefs.get(id); } } finally { @@ -937,7 +931,7 @@ public int collectionSize(String documentUri, String id, String propertyName) th isLicenseId = true; } else if (exceptionIds.containsKey(id.toLowerCase())) { isExceptionId = true; - } else if (Objects.nonNull(crossRef)) { + } else { crossRef = crossRefs.get(id); } } finally { @@ -976,7 +970,7 @@ public boolean collectionContains(String documentUri, String id, String property isLicenseId = true; } else if (exceptionIds.containsKey(id.toLowerCase())) { isExceptionId = true; - } else if (Objects.nonNull(crossRef)) { + } else { crossRef = crossRefs.get(id); } } finally { diff --git a/src/main/java/org/spdx/utility/compare/CompareTemplateOutputHandler.java b/src/main/java/org/spdx/utility/compare/CompareTemplateOutputHandler.java index bcef07db1..555320a4b 100644 --- a/src/main/java/org/spdx/utility/compare/CompareTemplateOutputHandler.java +++ b/src/main/java/org/spdx/utility/compare/CompareTemplateOutputHandler.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -578,7 +579,9 @@ public void skipNextInstruction() { return; } ParseInstruction nextInst = parent.findFollowingInstruction(this); - nextInst.setSkip(true); + if (Objects.nonNull(nextInst)) { + nextInst.setSkip(true); + } } public boolean getSkip() { diff --git a/src/main/java/org/spdx/utility/compare/LicenseCompareHelper.java b/src/main/java/org/spdx/utility/compare/LicenseCompareHelper.java index a1b69cc94..ace6396ee 100644 --- a/src/main/java/org/spdx/utility/compare/LicenseCompareHelper.java +++ b/src/main/java/org/spdx/utility/compare/LicenseCompareHelper.java @@ -59,7 +59,7 @@ public class LicenseCompareHelper { static final Logger logger = LoggerFactory.getLogger(LicenseCompareHelper.class); - protected static final String TOKEN_SPLIT_REGEX = "(^|[^\\s\\.,?'();:\"/]+)((\\s|\\.|,|\\?|'|\"|\\(|\\)|;|:|/|$)+)"; + protected static final String TOKEN_SPLIT_REGEX = "(^|[^\\s\\.,?'();:\"/]{1,100})((\\s|\\.|,|\\?|'|\"|\\(|\\)|;|:|/|$){1,100})"; protected static final Pattern TOKEN_SPLIT_PATTERN = Pattern.compile(TOKEN_SPLIT_REGEX); protected static final Set PUNCTUATION = Collections.unmodifiableSet(new HashSet( @@ -129,11 +129,11 @@ public class LicenseCompareHelper { static final Pattern COPYRIGHT_HOLDERS_PATTERN = Pattern.compile("copyright holders", Pattern.CASE_INSENSITIVE); static final Pattern COPYRIGHT_OWNERS_PATTERN = Pattern.compile("copyright owners", Pattern.CASE_INSENSITIVE); static final Pattern COPYRIGHT_OWNER_PATTERN = Pattern.compile("copyright owner", Pattern.CASE_INSENSITIVE); - static final Pattern PER_CENT_PATTERN_LF = Pattern.compile("per\\s*\\n+\\s*cent", Pattern.CASE_INSENSITIVE); - static final Pattern COPYRIGHT_HOLDERS_PATTERN_LF = Pattern.compile("copyright\\s*\\n+\\s*holders", Pattern.CASE_INSENSITIVE); - static final Pattern COPYRIGHT_HOLDER_PATTERN_LF = Pattern.compile("copyright\\s*\\n+\\s*holder", Pattern.CASE_INSENSITIVE); - static final Pattern COPYRIGHT_OWNERS_PATTERN_LF = Pattern.compile("copyright\\s*\\n+\\s*owners", Pattern.CASE_INSENSITIVE); - static final Pattern COPYRIGHT_OWNER_PATTERN_LF = Pattern.compile("copyright\\s*\\n+\\s*owner", Pattern.CASE_INSENSITIVE); + static final Pattern PER_CENT_PATTERN_LF = Pattern.compile("per\\s{0,100}\\n{1,10}\\s{0,100}cent", Pattern.CASE_INSENSITIVE); + static final Pattern COPYRIGHT_HOLDERS_PATTERN_LF = Pattern.compile("copyright\\s{0,100}\\n{1,10}\\s{0,100}holders", Pattern.CASE_INSENSITIVE); + static final Pattern COPYRIGHT_HOLDER_PATTERN_LF = Pattern.compile("copyright\\s{0,100}\\n{1,10}\\s{0,100}holder", Pattern.CASE_INSENSITIVE); + static final Pattern COPYRIGHT_OWNERS_PATTERN_LF = Pattern.compile("copyright\\s{0,100}\\n{1,10}\\s{0,100}owners", Pattern.CASE_INSENSITIVE); + static final Pattern COPYRIGHT_OWNER_PATTERN_LF = Pattern.compile("copyright\\s{0,100}\\n{1,10}\\s{0,100}owner", Pattern.CASE_INSENSITIVE); static final Pattern COPYRIGHT_SYMBOL_PATTERN = Pattern.compile("\\(c\\)", Pattern.CASE_INSENSITIVE); static final String START_COMMENT_CHAR_PATTERN = "(//|/\\*|\\*|#|' |REM |