Repository: CycloneDX/cyclonedx-core-java
Pull request #579 (2024-12-18 14:09:20)

Add new Enum based on the spec for acknoledgement
This adds an Enum for acknowledgement for licenses, this is to avoid issues when parsing and serializing, so it complies with the spec.


Patch:
diff --git a/src/main/java/org/cyclonedx/model/License.java b/src/main/java/org/cyclonedx/model/License.java
index 3cdffd438..46dc30c73 100644
--- a/src/main/java/org/cyclonedx/model/License.java
+++ b/src/main/java/org/cyclonedx/model/License.java
@@ -18,6 +18,7 @@
  */
 package org.cyclonedx.model;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -30,6 +31,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import org.cyclonedx.Version;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.util.deserializer.PropertiesDeserializer;
 
 @SuppressWarnings("unused")
@@ -51,7 +53,7 @@ public class License extends ExtensibleElement {
     @JacksonXmlProperty(isAttribute = true, localName = "acknowledgement")
     @JsonProperty("acknowledgement")
     @VersionFilter(Version.VERSION_16)
-    private String acknowledgement;
+    private Acknowledgement acknowledgement;
 
     @VersionFilter(Version.VERSION_15)
     private Licensing licensing;
@@ -120,6 +122,13 @@ public class License extends ExtensibleElement {
         this.properties = properties;
     }
 
+    public void addProperty(Property property) {
+        if(this.properties == null) {
+            this.properties = new ArrayList<>();
+        }
+        this.properties.add(property);
+    }
+
     public AttachmentText getAttachmentText() {
         return attachmentText;
     }
@@ -128,11 +137,11 @@ public class License extends ExtensibleElement {
         this.attachmentText = attachmentText;
     }
 
-    public String getAcknowledgement() {
+    public Acknowledgement getAcknowledgement() {
         return acknowledgement;
     }
 
-    public void setAcknowledgement(final String acknowledgement) {
+    public void setAcknowledgement(final Acknowledgement acknowledgement) {
         this.acknowledgement = acknowledgement;
     }
 
diff --git a/src/main/java/org/cyclonedx/model/license/Acknowledgement.java b/src/main/java/org/cyclonedx/model/license/Acknowledgement.java
new file mode 100644
index 000000000..e317e0be9
--- /dev/null
+++ b/src/main/java/org/cyclonedx/model/license/Acknowledgement.java
@@ -0,0 +1,31 @@
+package org.cyclonedx.model.license;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.cyclonedx.model.ExternalReference.Type;
+
+public enum Acknowledgement
+{
+  @JsonProperty("declared")
+  DECLARED("declared"),
+  @JsonProperty("concluded")
+  CONCLUDED("concluded");
+
+  private final String name;
+
+  public String getValue() {
+    return this.name;
+  }
+
+  Acknowledgement(String name) {
+    this.name = name;
+  }
+
+  public static Acknowledgement fromString(String text) {
+    for (Acknowledgement t : Acknowledgement.values()) {
+      if (t.name.equals(text)) {
+        return t;
+      }
+    }
+    return null;
+  }
+}
diff --git a/src/main/java/org/cyclonedx/model/license/Expression.java b/src/main/java/org/cyclonedx/model/license/Expression.java
index 2a71aaba1..5ae298512 100644
--- a/src/main/java/org/cyclonedx/model/license/Expression.java
+++ b/src/main/java/org/cyclonedx/model/license/Expression.java
@@ -27,7 +27,7 @@ public class Expression
   @JacksonXmlProperty(isAttribute = true, localName = "acknowledgement")
   @JsonProperty("acknowledgement")
   @VersionFilter(Version.VERSION_16)
-  private String acknowledgement;
+  private Acknowledgement acknowledgement;
 
   @JacksonXmlText
   @JsonProperty("expression")
@@ -49,11 +49,11 @@ public class Expression
     this.bomRef = bomRef;
   }
 
-  public String getAcknowledgement() {
+  public Acknowledgement getAcknowledgement() {
     return acknowledgement;
   }
 
-  public void setAcknowledgement(final String acknowledgement) {
+  public void setAcknowledgement(final Acknowledgement acknowledgement) {
     this.acknowledgement = acknowledgement;
   }
 
diff --git a/src/main/java/org/cyclonedx/util/deserializer/ExpressionDeserializer.java b/src/main/java/org/cyclonedx/util/deserializer/ExpressionDeserializer.java
index 3588b4ae0..40010e43e 100644
--- a/src/main/java/org/cyclonedx/util/deserializer/ExpressionDeserializer.java
+++ b/src/main/java/org/cyclonedx/util/deserializer/ExpressionDeserializer.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonNode;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.model.license.Expression;
 
 public class ExpressionDeserializer
@@ -53,7 +54,7 @@ public class ExpressionDeserializer
     }
 
     if (node.has("acknowledgement")) {
-      expression.setAcknowledgement(node.get("acknowledgement").asText());
+      expression.setAcknowledgement(Acknowledgement.fromString(node.get("acknowledgement").asText()));
     }
 
     JsonNode textNode = node.get("expression");
diff --git a/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java b/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
index dc3e8797b..5438d9110 100644
--- a/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
+++ b/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
@@ -30,6 +30,7 @@ import org.cyclonedx.Version;
 import org.cyclonedx.model.License;
 import org.cyclonedx.model.LicenseChoice;
 import org.cyclonedx.model.Property;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.model.license.Expression;
 
 import static org.cyclonedx.util.serializer.SerializerUtils.shouldSerializeField;
@@ -129,7 +130,7 @@ public class LicenseChoiceSerializer
   private void serializeXmlAttributes(
       final ToXmlGenerator toXmlGenerator,
       final String bomRef,
-      final String acknowledgement,
+      final Acknowledgement acknowledgement,
       final Object object) throws IOException
   {
     toXmlGenerator.writeStartObject();
@@ -140,10 +141,10 @@ public class LicenseChoiceSerializer
       toXmlGenerator.writeString(bomRef);
       toXmlGenerator.setNextIsAttribute(false);
     }
-    if (StringUtils.isNotBlank(acknowledgement) && shouldSerializeField(object, version, "acknowledgement")) {
+    if (acknowledgement != null && shouldSerializeField(object, version, "acknowledgement")) {
       toXmlGenerator.setNextIsAttribute(true);
       toXmlGenerator.writeFieldName("acknowledgement");
-      toXmlGenerator.writeString(acknowledgement);
+      toXmlGenerator.writeString(acknowledgement.getValue());
       toXmlGenerator.setNextIsAttribute(false);
     }
   }
@@ -197,9 +198,8 @@ public class LicenseChoiceSerializer
     gen.writeStartArray();
     gen.writeStartObject();
     gen.writeStringField("expression", expression.getValue());
-    if (StringUtils.isNotBlank(expression.getAcknowledgement()) &&
-        shouldSerializeField(expression, version, "acknowledgement")) {
-      gen.writeStringField("acknowledgement", expression.getAcknowledgement());
+    if (expression.getAcknowledgement() != null && shouldSerializeField(expression, version, "acknowledgement")) {
+      gen.writeStringField("acknowledgement", expression.getAcknowledgement().getValue());
     }
     if (StringUtils.isNotBlank(expression.getBomRef()) && shouldSerializeField(expression, version, "bomRef")) {
       gen.writeStringField("bom-ref", expression.getBomRef());


Test patch:
diff --git a/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java b/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
index 6f62acf6b..aa8a2dca5 100644
--- a/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
@@ -70,6 +70,7 @@ import org.cyclonedx.model.formulation.trigger.Event;
 import org.cyclonedx.model.formulation.trigger.Trigger;
 import org.cyclonedx.model.formulation.workspace.Volume;
 import org.cyclonedx.model.formulation.workspace.Workspace;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.model.vulnerability.Vulnerability;
 import org.cyclonedx.model.vulnerability.Vulnerability.Analysis.Justification;
 import org.cyclonedx.model.vulnerability.Vulnerability.Analysis.State;
@@ -1010,7 +1011,7 @@ public class AbstractParserTest
     License license = lc.getLicenses().get(0);
     assertEquals("Apache-2.0", license.getId());
     assertEquals("my-license", license.getBomRef());
-    assertEquals("declared", license.getAcknowledgement());
+    assertEquals(Acknowledgement.DECLARED, license.getAcknowledgement());
   }
 
   Bom getXmlBom(String filename) throws ParseException, IOException {
diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
index 1cdccb4db..b534aa211 100644
--- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
@@ -59,6 +59,7 @@ import org.cyclonedx.model.component.modelCard.consideration.consumption.energy.
 import org.cyclonedx.model.definition.Level;
 import org.cyclonedx.model.definition.Requirement;
 import org.cyclonedx.model.definition.Standard;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.model.license.Expression;
 import org.junit.jupiter.api.Test;
 import java.io.File;
@@ -281,7 +282,7 @@ public class JsonParserTest
         License license = lc.getLicenses().get(0);
         assertEquals("Apache-2.0", license.getId());
         assertEquals("my-license", license.getBomRef());
-        assertEquals("declared", license.getAcknowledgement());
+        assertEquals(Acknowledgement.DECLARED, license.getAcknowledgement());
     }
 
     @Test
@@ -295,7 +296,7 @@ public class JsonParserTest
         Expression expression = lc.getExpression();
         assertEquals("EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", expression.getValue());
         assertEquals("my-license", expression.getBomRef());
-        assertEquals("declared", expression.getAcknowledgement());
+        assertEquals(Acknowledgement.DECLARED, expression.getAcknowledgement());
     }
 
     @Test
diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
index 997813e59..6e79b4f94 100644
--- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
@@ -60,6 +60,7 @@ import org.cyclonedx.model.component.modelCard.consideration.consumption.energy.
 import org.cyclonedx.model.definition.Level;
 import org.cyclonedx.model.definition.Requirement;
 import org.cyclonedx.model.definition.Standard;
+import org.cyclonedx.model.license.Acknowledgement;
 import org.cyclonedx.model.license.Expression;
 import org.junit.jupiter.api.Test;
 import java.io.File;
@@ -440,7 +441,7 @@ public class XmlParserTest
         Expression expression = lc.getExpression();
         assertEquals("EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", expression.getValue());
         assertEquals("my-license", expression.getBomRef());
-        assertEquals("declared", expression.getAcknowledgement());
+        assertEquals(Acknowledgement.DECLARED, expression.getAcknowledgement());
     }
 
     @Test