diff --git a/pmml-model/src/main/java/org/jpmml/model/InvalidAttributeException.java b/pmml-model/src/main/java/org/jpmml/model/InvalidAttributeException.java
index 14b0a597..b9e9243a 100644
--- a/pmml-model/src/main/java/org/jpmml/model/InvalidAttributeException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/InvalidAttributeException.java
@@ -22,7 +22,7 @@ public InvalidAttributeException(PMMLObject object, Enum> value){
}
public InvalidAttributeException(PMMLObject object, Field field, Object value){
- super(formatMessage(XPathUtil.formatAttribute(field, value)), object);
+ super(formatMessage(XPathUtil.formatAttribute(object.getClass(), field, value)), object);
}
static
diff --git a/pmml-model/src/main/java/org/jpmml/model/InvalidElementListException.java b/pmml-model/src/main/java/org/jpmml/model/InvalidElementListException.java
index 2514a085..c7e0b96b 100644
--- a/pmml-model/src/main/java/org/jpmml/model/InvalidElementListException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/InvalidElementListException.java
@@ -9,6 +9,14 @@
public class InvalidElementListException extends InvalidMarkupException {
+ public InvalidElementListException(String message){
+ super(message);
+ }
+
+ public InvalidElementListException(String message, PMMLObject object){
+ super(message, object);
+ }
+
public InvalidElementListException(List extends PMMLObject> objects){
super("List of elements " + XPathUtil.formatElement((objects.get(0)).getClass()) + " is not valid", objects.get(0));
}
diff --git a/pmml-model/src/main/java/org/jpmml/model/MisplacedAttributeException.java b/pmml-model/src/main/java/org/jpmml/model/MisplacedAttributeException.java
index e1999599..dc5c485c 100644
--- a/pmml-model/src/main/java/org/jpmml/model/MisplacedAttributeException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/MisplacedAttributeException.java
@@ -14,7 +14,7 @@ public MisplacedAttributeException(PMMLObject object, Enum> value){
}
public MisplacedAttributeException(PMMLObject object, Field field, Object value){
- super(formatMessage(XPathUtil.formatAttribute(field, value)), object);
+ super(formatMessage(XPathUtil.formatAttribute(object.getClass(), field, value)), object);
}
static
diff --git a/pmml-model/src/main/java/org/jpmml/model/MisplacedElementListException.java b/pmml-model/src/main/java/org/jpmml/model/MisplacedElementListException.java
new file mode 100644
index 00000000..230db75c
--- /dev/null
+++ b/pmml-model/src/main/java/org/jpmml/model/MisplacedElementListException.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model;
+
+import java.util.List;
+
+import org.dmg.pmml.PMMLObject;
+
+public class MisplacedElementListException extends InvalidElementListException {
+
+ public MisplacedElementListException(List extends PMMLObject> objects){
+ super(formatMessage(XPathUtil.formatElement((objects.get(0)).getClass())), objects.get(0));
+ }
+
+ static
+ public String formatMessage(String xPath){
+ return "List of elements " + xPath + " is not permitted in this location";
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/MissingAttributeException.java b/pmml-model/src/main/java/org/jpmml/model/MissingAttributeException.java
index c6599ade..544838db 100644
--- a/pmml-model/src/main/java/org/jpmml/model/MissingAttributeException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/MissingAttributeException.java
@@ -18,7 +18,7 @@ public MissingAttributeException(String message, PMMLObject context){
}
public MissingAttributeException(PMMLObject object, Field field){
- super(formatMessage(XPathUtil.formatElementOrAttribute(field)), object);
+ super(formatMessage(XPathUtil.formatElementOrAttribute(object.getClass(), field)), object);
}
static
diff --git a/pmml-model/src/main/java/org/jpmml/model/MissingElementException.java b/pmml-model/src/main/java/org/jpmml/model/MissingElementException.java
index 3eac41c3..ab5f38e1 100644
--- a/pmml-model/src/main/java/org/jpmml/model/MissingElementException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/MissingElementException.java
@@ -18,7 +18,7 @@ public MissingElementException(String message, PMMLObject context){
}
public MissingElementException(PMMLObject object, Field field){
- super(formatMessage(XPathUtil.formatElementOrAttribute(field)), object);
+ super(formatMessage(XPathUtil.formatElementOrAttribute(object.getClass(), field)), object);
}
static
diff --git a/pmml-model/src/main/java/org/jpmml/model/PMMLOutputStream.java b/pmml-model/src/main/java/org/jpmml/model/PMMLOutputStream.java
new file mode 100644
index 00000000..79ec3250
--- /dev/null
+++ b/pmml-model/src/main/java/org/jpmml/model/PMMLOutputStream.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Objects;
+
+import org.dmg.pmml.Version;
+
+public class PMMLOutputStream extends FilterOutputStream {
+
+ private Version version = null;
+
+ private ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
+
+
+ public PMMLOutputStream(OutputStream os, Version version){
+ super(os);
+
+ this.version = Objects.requireNonNull(version);
+
+ if(!version.isStandard()){
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public void write(byte[] bytes) throws IOException {
+ this.write(bytes, 0, bytes.length);
+ }
+
+ @Override
+ public void write(byte[] bytes, int offset, int length) throws IOException {
+
+ if(this.buffer != null){
+
+ for(int i = offset, max = offset + length; i < max; i++){
+ this.write(bytes[i]);
+ }
+ } else
+
+ {
+ super.out.write(bytes, offset, length);
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+
+ if(this.buffer != null){
+ this.buffer.write(b);
+
+ if(b == '>'){
+ String string = this.buffer.toString("UTF-8");
+
+ if(string.endsWith("?>")){
+ super.out.write(string.getBytes("UTF-8"));
+
+ this.buffer.reset();
+ } else
+
+ if(string.endsWith(">")){
+ String updatedString = string.replace(Version.PMML_4_4.getNamespaceURI(), this.version.getNamespaceURI());
+
+ if(Objects.equals(string, updatedString)){
+ throw new IllegalStateException();
+ }
+
+ super.out.write(updatedString.getBytes("UTF-8"));
+
+ this.buffer = null;
+ } else
+
+ {
+ throw new IllegalStateException();
+ }
+ }
+ } else
+
+ {
+ super.out.write(b);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+
+ if(this.buffer != null){
+ throw new IllegalStateException();
+ }
+
+ super.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/UnsupportedAttributeException.java b/pmml-model/src/main/java/org/jpmml/model/UnsupportedAttributeException.java
index ed98f546..141d88f9 100644
--- a/pmml-model/src/main/java/org/jpmml/model/UnsupportedAttributeException.java
+++ b/pmml-model/src/main/java/org/jpmml/model/UnsupportedAttributeException.java
@@ -22,6 +22,6 @@ public UnsupportedAttributeException(PMMLObject object, Enum> value){
}
public UnsupportedAttributeException(PMMLObject object, Field field, Object value){
- super("Attribute with value " + XPathUtil.formatAttribute(field, value) + " is not supported", object);
+ super("Attribute with value " + XPathUtil.formatAttribute(object.getClass(), field, value) + " is not supported", object);
}
}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/annotations/Added.java b/pmml-model/src/main/java/org/jpmml/model/annotations/Added.java
index ad2b1281..d7f2ad69 100644
--- a/pmml-model/src/main/java/org/jpmml/model/annotations/Added.java
+++ b/pmml-model/src/main/java/org/jpmml/model/annotations/Added.java
@@ -24,4 +24,6 @@
public @interface Added {
Version value();
+
+ boolean removable() default false;
}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionChecker.java b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionChecker.java
index 5164ca5a..a164e8cd 100644
--- a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionChecker.java
+++ b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionChecker.java
@@ -14,11 +14,13 @@
import org.jpmml.model.MarkupException;
import org.jpmml.model.MisplacedAttributeException;
import org.jpmml.model.MisplacedElementException;
+import org.jpmml.model.MisplacedElementListException;
import org.jpmml.model.MissingAttributeException;
import org.jpmml.model.MissingElementException;
import org.jpmml.model.ReflectionUtil;
import org.jpmml.model.UnsupportedAttributeException;
import org.jpmml.model.UnsupportedElementException;
+import org.jpmml.model.UnsupportedElementListException;
import org.jpmml.model.annotations.Added;
import org.jpmml.model.annotations.Optional;
import org.jpmml.model.annotations.Removed;
@@ -60,7 +62,14 @@ public void handleAdded(PMMLObject object, AnnotatedElement element, Added added
} else
if(isElement(field)){
- report(new UnsupportedElementException((PMMLObject)value));
+
+ if(value instanceof List){
+ report(new UnsupportedElementListException((List extends PMMLObject>)value));
+ } else
+
+ {
+ report(new UnsupportedElementException((PMMLObject)value));
+ }
} else
{
@@ -90,7 +99,14 @@ public void handleRemoved(PMMLObject object, AnnotatedElement element, Removed r
} else
if(isElement(field)){
- report(new MisplacedElementException((PMMLObject)value));
+
+ if(value instanceof List){
+ report(new MisplacedElementListException((List extends PMMLObject>)value));
+ } else
+
+ {
+ report(new MisplacedElementException((PMMLObject)value));
+ }
} else
{
diff --git a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionDowngrader.java b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionDowngrader.java
new file mode 100644
index 00000000..10aa74d1
--- /dev/null
+++ b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionDowngrader.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model.visitors;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+import org.dmg.pmml.Apply;
+import org.dmg.pmml.MiningField;
+import org.dmg.pmml.PMML;
+import org.dmg.pmml.PMMLAttributes;
+import org.dmg.pmml.PMMLObject;
+import org.dmg.pmml.TargetValue;
+import org.dmg.pmml.Version;
+import org.dmg.pmml.VisitorAction;
+import org.dmg.pmml.time_series.TrendExpoSmooth;
+import org.jpmml.model.ReflectionUtil;
+import org.jpmml.model.UnsupportedAttributeException;
+import org.jpmml.model.UnsupportedElementException;
+import org.jpmml.model.annotations.Added;
+import org.jpmml.model.annotations.Optional;
+import org.jpmml.model.annotations.Removed;
+import org.jpmml.model.annotations.Required;
+import org.jpmml.model.filters.ExportFilter;
+
+/**
+ *
+ * A Visitor that downgrades a class model object from the latest PMML schema version to some earlier one.
+ *
+ *
+ * @see ExportFilter
+ */
+public class VersionDowngrader extends VersionInspector {
+
+ private Version version = null;
+
+
+ public VersionDowngrader(Version version){
+ this.version = Objects.requireNonNull(version);
+
+ if(!version.isStandard()){
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public void handleAdded(PMMLObject object, AnnotatedElement element, Added added){
+ Version version = added.value();
+
+ if(version.isStandard() && version.compareTo(this.version) > 0){
+
+ if(element instanceof Class){
+ // Ignored
+ } else
+
+ if(element instanceof Field){
+ Field field = (Field)element;
+
+ if(added.removable()){
+ ReflectionUtil.setFieldValue(field, object, null);
+ }
+ } else
+
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ @Override
+ public void handleRemoved(PMMLObject object, AnnotatedElement element, Removed removed){
+ }
+
+ @Override
+ public void handleOptional(PMMLObject object, AnnotatedElement element, Optional optional){
+ }
+
+ @Override
+ public void handleRequired(PMMLObject object, AnnotatedElement element, Required required){
+ }
+
+ @Override
+ public VisitorAction visit(Apply apply){
+ Object defaultValue = apply.getDefaultValue();
+
+ if(defaultValue != null){
+
+ if(this.version.compareTo(Version.PMML_4_1) == 0){
+ Object mapMissingTo = apply.getMapMissingTo();
+
+ if(mapMissingTo != null){
+ throw new UnsupportedAttributeException(apply, PMMLAttributes.APPLY_DEFAULTVALUE, defaultValue);
+ }
+
+ apply
+ .setDefaultValue(null)
+ .setMapMissingTo(defaultValue);
+ }
+ }
+
+ return super.visit(apply);
+ }
+
+ @Override
+ public VisitorAction visit(MiningField miningField){
+ MiningField.UsageType usageType = miningField.getUsageType();
+
+ switch(usageType){
+ case TARGET:
+ if(this.version.compareTo(Version.PMML_4_2) < 0){
+ miningField.setUsageType(MiningField.UsageType.PREDICTED);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return super.visit(miningField);
+ }
+
+ @Override
+ public VisitorAction visit(PMML pmml){
+ pmml.setVersion(this.version.getVersion());
+
+ return super.visit(pmml);
+ }
+
+ @Override
+ public VisitorAction visit(TargetValue targetValue){
+ String displayValue = targetValue.getDisplayValue();
+
+ if(displayValue != null){
+
+ if(this.version.compareTo(Version.PMML_3_2) <= 0){
+ throw new UnsupportedAttributeException(targetValue, PMMLAttributes.TARGETVALUE_DISPLAYVALUE, displayValue);
+ }
+ }
+
+ return super.visit(targetValue);
+ }
+
+ @Override
+ public VisitorAction visit(TrendExpoSmooth trendExpoSmooth){
+
+ if(this.version.compareTo(Version.PMML_4_0) == 0){
+ throw new UnsupportedElementException(trendExpoSmooth);
+ }
+
+ return super.visit(trendExpoSmooth);
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionInspector.java b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionInspector.java
index 6962805b..03127340 100644
--- a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionInspector.java
+++ b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionInspector.java
@@ -11,6 +11,7 @@
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlElements;
import jakarta.xml.bind.annotation.XmlEnumValue;
import org.dmg.pmml.Apply;
import org.dmg.pmml.PMMLAttributes;
@@ -96,6 +97,11 @@ public Class extends Annotation> annotationType(){
public Version value(){
return version;
}
+
+ @Override
+ public boolean removable(){
+ return false;
+ }
};
handleAdded(apply, PMMLAttributes.APPLY_FUNCTION, added);
@@ -174,7 +180,8 @@ protected boolean isEnumValue(Field field){
static
protected boolean isElement(Field field){
XmlElement xmlElement = field.getAnnotation(XmlElement.class);
+ XmlElements xmlElements = field.getAnnotation(XmlElements.class);
- return (xmlElement != null);
+ return (xmlElement != null) || (xmlElements != null);
}
}
\ No newline at end of file
diff --git a/pmml-model/src/main/java/org/jpmml/model/visitors/VersionStandardizer.java b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionStandardizer.java
new file mode 100644
index 00000000..ee917373
--- /dev/null
+++ b/pmml-model/src/main/java/org/jpmml/model/visitors/VersionStandardizer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model.visitors;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+
+import org.dmg.pmml.PMMLObject;
+import org.dmg.pmml.Version;
+import org.jpmml.model.ReflectionUtil;
+import org.jpmml.model.annotations.Added;
+import org.jpmml.model.annotations.Optional;
+import org.jpmml.model.annotations.Removed;
+import org.jpmml.model.annotations.Required;
+import org.jpmml.model.filters.ExportFilter;
+
+/**
+ *
+ * A Visitor that standardizes a class model object by reducing vendor extension markup.
+ *
+ *
+ * @see ExportFilter
+ */
+public class VersionStandardizer extends VersionInspector {
+
+ @Override
+ public void handleAdded(PMMLObject object, AnnotatedElement element, Added added){
+ Version version = added.value();
+
+ if(!version.isStandard()){
+
+ if(element instanceof Class){
+ // Ignored
+ } else
+
+ if(element instanceof Field){
+ Field field = (Field)element;
+
+ if(added.removable()){
+ ReflectionUtil.setFieldValue(field, object, null);
+ }
+ } else
+
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ @Override
+ public void handleRemoved(PMMLObject object, AnnotatedElement element, Removed removed){
+ }
+
+ @Override
+ public void handleOptional(PMMLObject object, AnnotatedElement element, Optional optional){
+ }
+
+ @Override
+ public void handleRequired(PMMLObject object, AnnotatedElement element, Required required){
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/main/schema/pmml.xjb b/pmml-model/src/main/schema/pmml.xjb
index 151886dc..ad33273e 100644
--- a/pmml-model/src/main/schema/pmml.xjb
+++ b/pmml-model/src/main/schema/pmml.xjb
@@ -91,7 +91,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.5.0")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
@@ -234,14 +234,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -341,14 +341,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
@@ -589,7 +589,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -606,7 +606,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -631,7 +631,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
@@ -872,7 +872,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -1144,7 +1144,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -1294,7 +1294,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -1323,7 +1323,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_3)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_3, removable = true)
@@ -1589,14 +1589,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -1719,14 +1719,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -1760,14 +1760,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -1856,7 +1856,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -2060,7 +2060,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_3)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_3, removable = true)
@@ -2159,7 +2159,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
@@ -2193,7 +2193,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
@@ -2302,7 +2302,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.3")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -2393,59 +2393,59 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_4)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_4, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -2536,7 +2536,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -2557,7 +2557,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -2568,7 +2568,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -2705,14 +2705,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -2739,7 +2739,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -2752,7 +2752,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -2972,7 +2972,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -3178,7 +3178,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -3406,14 +3406,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
@@ -3593,14 +3593,14 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -3671,7 +3671,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_1)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_1, removable = true)
@@ -3714,7 +3714,7 @@ Copyright (c) 2014 Villu Ruusmann
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.XPMML)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.XPMML, removable = true)
org.jpmml.model.annotations.Since("1.3.7")
- org.jpmml.model.annotations.Added(org.dmg.pmml.Version.PMML_4_0)
+ org.jpmml.model.annotations.Added(value = org.dmg.pmml.Version.PMML_4_0, removable = true)
diff --git a/pmml-model/src/test/java/org/jpmml/model/XPathUtilTest.java b/pmml-model/src/test/java/org/jpmml/model/XPathUtilTest.java
index 22d07ed3..2edde8dd 100644
--- a/pmml-model/src/test/java/org/jpmml/model/XPathUtilTest.java
+++ b/pmml-model/src/test/java/org/jpmml/model/XPathUtilTest.java
@@ -3,13 +3,22 @@
*/
package org.jpmml.model;
+import org.dmg.pmml.ComplexScoreDistribution;
import org.dmg.pmml.DataField;
import org.dmg.pmml.IntSparseArray;
import org.dmg.pmml.PMMLAttributes;
import org.dmg.pmml.PMMLElements;
+import org.dmg.pmml.ScoreDistribution;
+import org.dmg.pmml.ScoreProbability;
+import org.dmg.pmml.SimpleScoreDistribution;
+import org.dmg.pmml.tree.ComplexNode;
+import org.dmg.pmml.tree.LeafNode;
+import org.dmg.pmml.tree.Node;
+import org.dmg.pmml.tree.SimpleNode;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
public class XPathUtilTest {
@@ -41,11 +50,65 @@ public void formatDerivedField(){
assertEquals("DerivedField/", XPathUtil.formatElementOrAttribute(PMMLElements.DERIVEDFIELD_EXPRESSION));
}
+ @Test
+ public void formatComplexNode(){
+ assertEquals("Node", XPathUtil.formatElement(ComplexNode.class));
+
+ assertEquals("Node@score", XPathUtil.formatElementOrAttribute(org.dmg.pmml.tree.PMMLAttributes.COMPLEXNODE_SCORE));
+ assertEquals("Node/", XPathUtil.formatElementOrAttribute(org.dmg.pmml.tree.PMMLElements.COMPLEXNODE_PREDICATE));
+ }
+
+ @Test
+ public void formatSimpleNode(){
+ SimpleNode node = new LeafNode();
+
+ Class extends Node> nodeClazz = node.getClass();
+
+ assertEquals("Node", XPathUtil.formatElement(nodeClazz));
+
+ try {
+ XPathUtil.formatElementOrAttribute(ReflectionUtil.getField(nodeClazz, "score"));
+
+ fail();
+ } catch(IllegalArgumentException iae){
+ // Ignored
+ }
+
+ assertEquals("Node@score", XPathUtil.formatElementOrAttribute(nodeClazz, ReflectionUtil.getField(nodeClazz, "score")));
+ assertEquals("Node/", XPathUtil.formatElementOrAttribute(nodeClazz, ReflectionUtil.getField(nodeClazz, "predicate")));
+ }
+
@Test
public void formatPMML(){
assertEquals("PMML/", XPathUtil.formatElementOrAttribute(PMMLElements.PMML_MODELS));
}
+ @Test
+ public void formatComplexScoreDistribution(){
+ assertEquals("ScoreDistribution", XPathUtil.formatElement(ComplexScoreDistribution.class));
+
+ assertEquals("ScoreDistribution@value", XPathUtil.formatElementOrAttribute(PMMLAttributes.COMPLEXSCOREDISTRIBUTION_VALUE));
+ }
+
+ @Test
+ public void formatSimpleScoreDistribution(){
+ SimpleScoreDistribution scoreDistribution = new ScoreProbability();
+
+ Class extends ScoreDistribution> scoreDistributionClazz = scoreDistribution.getClass();
+
+ assertEquals("ScoreDistribution", XPathUtil.formatElement(scoreDistributionClazz));
+
+ try {
+ XPathUtil.formatElementOrAttribute(ReflectionUtil.getField(scoreDistributionClazz, "value"));
+
+ fail();
+ } catch(IllegalArgumentException iae){
+ // Ignored
+ }
+
+ assertEquals("ScoreDistribution@value", XPathUtil.formatElementOrAttribute(scoreDistributionClazz, ReflectionUtil.getField(scoreDistributionClazz, "value")));
+ }
+
@Test
public void formatSegment(){
assertEquals("Segment/", XPathUtil.formatElementOrAttribute(org.dmg.pmml.mining.PMMLElements.SEGMENT_MODEL));
diff --git a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionCheckerTest.java b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionCheckerTest.java
index 9e3be4b0..61374ff8 100644
--- a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionCheckerTest.java
+++ b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionCheckerTest.java
@@ -11,10 +11,15 @@
import java.util.stream.Collectors;
import org.dmg.pmml.Apply;
+import org.dmg.pmml.DataType;
+import org.dmg.pmml.DerivedField;
+import org.dmg.pmml.FieldRef;
+import org.dmg.pmml.Interval;
import org.dmg.pmml.LocalTransformations;
import org.dmg.pmml.MathContext;
import org.dmg.pmml.MiningFunction;
import org.dmg.pmml.MiningSchema;
+import org.dmg.pmml.OpType;
import org.dmg.pmml.Output;
import org.dmg.pmml.OutputField;
import org.dmg.pmml.PMMLFunctions;
@@ -26,6 +31,7 @@
import org.dmg.pmml.mining.Segmentation;
import org.jpmml.model.MarkupException;
import org.jpmml.model.MisplacedElementException;
+import org.jpmml.model.MisplacedElementListException;
import org.jpmml.model.MissingAttributeException;
import org.jpmml.model.MissingElementException;
import org.jpmml.model.UnsupportedAttributeException;
@@ -56,6 +62,23 @@ public void inspectApply(){
assertExceptions(apply, Version.PMML_4_4, Collections.singleton(UnsupportedAttributeException.class));
}
+ @Test
+ public void inspectDerivedField(){
+ Interval negativeInterval = new Interval(Interval.Closure.CLOSED_OPEN)
+ .setLeftMargin(-Double.MAX_VALUE)
+ .setRightMargin(0d);
+
+ Interval positiveInterval = new Interval(Interval.Closure.OPEN_CLOSED)
+ .setLeftMargin(0d)
+ .setRightMargin(Double.MAX_VALUE);
+
+ DerivedField derivedField = new DerivedField("double(x)", OpType.CONTINUOUS, DataType.DOUBLE, new FieldRef("x"))
+ .addIntervals(negativeInterval, positiveInterval);
+
+ assertExceptions(derivedField, Version.PMML_4_4, Collections.singleton(MisplacedElementListException.class));
+ assertExceptions(derivedField, Version.PMML_3_0, Collections.emptySet());
+ }
+
@Test
public void inspectMiningModel(){
Segmentation segmentation = new Segmentation()
diff --git a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionDowngraderTest.java b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionDowngraderTest.java
new file mode 100644
index 00000000..229a0032
--- /dev/null
+++ b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionDowngraderTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyrght (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model.visitors;
+
+import org.dmg.pmml.Apply;
+import org.dmg.pmml.ClusteringModelQuality;
+import org.dmg.pmml.Extension;
+import org.dmg.pmml.Header;
+import org.dmg.pmml.MiningField;
+import org.dmg.pmml.ModelExplanation;
+import org.dmg.pmml.PMML;
+import org.dmg.pmml.PMMLObject;
+import org.dmg.pmml.TargetValue;
+import org.dmg.pmml.Version;
+import org.dmg.pmml.clustering.ClusteringModel;
+import org.dmg.pmml.clustering.PMMLAttributes;
+import org.dmg.pmml.time_series.TrendExpoSmooth;
+import org.jpmml.model.ReflectionUtil;
+import org.jpmml.model.UnsupportedAttributeException;
+import org.jpmml.model.UnsupportedElementException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class VersionDowngraderTest {
+
+ @Test
+ public void downgradeApply(){
+ Apply apply = new Apply()
+ .setDefaultValue(0)
+ .setMapMissingTo(null);
+
+ apply = downgrade(apply, Version.PMML_4_2);
+
+ assertEquals(0, apply.getDefaultValue());
+ assertNull(apply.getMapMissingTo());
+
+ apply = downgrade(apply, Version.PMML_4_1);
+
+ assertNull(apply.getDefaultValue());
+ assertEquals(0, apply.getMapMissingTo());
+
+ apply = new Apply()
+ .setDefaultValue(0)
+ .setMapMissingTo(-999);
+
+ apply = downgrade(apply, Version.PMML_4_2);
+
+ assertEquals(0, apply.getDefaultValue());
+ assertEquals(-999, apply.getMapMissingTo());
+
+ try {
+ downgrade(apply, Version.PMML_4_1);
+
+ fail();
+ } catch(UnsupportedAttributeException uae){
+ // Ignored
+ }
+ }
+
+ @Test
+ public void downgradeMiningField(){
+ MiningField miningField = new MiningField()
+ .setUsageType(MiningField.UsageType.TARGET);
+
+ miningField = downgrade(miningField, Version.PMML_4_2);
+
+ assertEquals(MiningField.UsageType.TARGET, miningField.getUsageType());
+
+ miningField = downgrade(miningField, Version.PMML_4_1);
+
+ assertEquals(MiningField.UsageType.PREDICTED, miningField.getUsageType());
+ }
+
+ @Test
+ public void downgradePMML(){
+ Extension extension = new Extension();
+
+ ClusteringModelQuality clusteringModelQuality = new ClusteringModelQuality()
+ .addExtensions(extension);
+
+ ModelExplanation modelExplanation = new ModelExplanation()
+ .addClusteringModelQualities(clusteringModelQuality);
+
+ ClusteringModel clusteringModel = new ClusteringModel()
+ .setScorable(false)
+ .setModelExplanation(modelExplanation);
+
+ Header header = new Header()
+ .setModelVersion("1.0");
+
+ PMML pmml = new PMML()
+ .setHeader(header)
+ .addModels(clusteringModel);
+
+ pmml = downgrade(pmml, Version.PMML_4_4);
+
+ assertTrue(clusteringModelQuality.hasExtensions());
+
+ assertEquals("4.4", pmml.getVersion());
+
+ pmml = downgrade(pmml, Version.PMML_4_3);
+
+ assertFalse(clusteringModelQuality.hasExtensions());
+
+ assertNotNull(header.getModelVersion());
+
+ pmml = downgrade(pmml, Version.PMML_4_1);
+
+ assertNull(header.getModelVersion());
+
+ assertFalse(clusteringModel.isScorable());
+
+ pmml = downgrade(pmml, Version.PMML_4_0);
+
+ assertTrue(clusteringModel.isScorable());
+
+ assertNull(ReflectionUtil.getFieldValue(PMMLAttributes.CLUSTERINGMODEL_SCORABLE, clusteringModel));
+
+ assertNotNull(clusteringModel.getModelExplanation());
+
+ pmml = downgrade(pmml, Version.PMML_3_2);
+
+ assertNull(clusteringModel.getModelExplanation());
+
+ assertEquals("3.2", pmml.getVersion());
+ }
+
+ @Test
+ public void downgradeTargetValue(){
+ TargetValue targetValue = new TargetValue()
+ .setValue(1)
+ .setDisplayValue("one");
+
+ targetValue = downgrade(targetValue, Version.PMML_4_0);
+
+ assertEquals(1, targetValue.getValue());
+ assertEquals("one", targetValue.getDisplayValue());
+
+ try {
+ downgrade(targetValue, Version.PMML_3_2);
+
+ fail();
+ } catch(UnsupportedAttributeException uae){
+ // Ignored
+ }
+ }
+
+ @Test
+ public void downgradeTrendExpoSmooth(){
+ TrendExpoSmooth trendExpoSmooth = new TrendExpoSmooth();
+
+ trendExpoSmooth = downgrade(trendExpoSmooth, Version.PMML_4_1);
+
+ try {
+ downgrade(trendExpoSmooth, Version.PMML_4_0);
+ } catch(UnsupportedElementException uee){
+ // Ignored
+ }
+ }
+
+ static
+ private E downgrade(E object, Version version){
+ VersionDowngrader inspector = new VersionDowngrader(version);
+ inspector.applyTo(object);
+
+ return object;
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionInspectorTest.java b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionInspectorTest.java
new file mode 100644
index 00000000..9c52c7e8
--- /dev/null
+++ b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionInspectorTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model.visitors;
+
+import java.lang.reflect.Field;
+
+import org.dmg.pmml.PMMLAttributes;
+import org.dmg.pmml.PMMLElements;
+import org.dmg.pmml.ResultFeature;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class VersionInspectorTest {
+
+ @Test
+ public void isAttribute(){
+ assertTrue(VersionInspector.isAttribute(PMMLAttributes.OUTPUTFIELD_NAME));
+
+ assertFalse(VersionInspector.isAttribute(PMMLElements.OUTPUTFIELD_EXTENSIONS));
+ }
+
+ @Test
+ public void isEnumValue() throws NoSuchFieldException {
+ Field field = ResultFeature.class.getField("PREDICTED_VALUE");
+
+ assertTrue(VersionInspector.isEnumValue(field));
+
+ assertFalse(VersionInspector.isEnumValue(PMMLAttributes.OUTPUTFIELD_RESULTFEATURE));
+ }
+
+ @Test
+ public void isElement(){
+ assertFalse(VersionInspector.isElement(PMMLAttributes.OUTPUTFIELD_NAME));
+
+ assertTrue(VersionInspector.isElement(PMMLElements.OUTPUTFIELD_EXTENSIONS));
+ assertTrue(VersionInspector.isElement(PMMLElements.OUTPUTFIELD_EXPRESSION));
+ }
+}
\ No newline at end of file
diff --git a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionStandardizerTest.java b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionStandardizerTest.java
new file mode 100644
index 00000000..103070f3
--- /dev/null
+++ b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionStandardizerTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Villu Ruusmann
+ */
+package org.jpmml.model.visitors;
+
+import org.dmg.pmml.MathContext;
+import org.dmg.pmml.PMML;
+import org.dmg.pmml.tree.PMMLAttributes;
+import org.dmg.pmml.tree.TreeModel;
+import org.jpmml.model.ReflectionUtil;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class VersionStandardizerTest {
+
+ @Test
+ public void standardizePMML(){
+ PMML pmml = new PMML()
+ .setVersion("4.4")
+ .setBaseVersion("4.3");
+
+ TreeModel treeModel = new TreeModel()
+ .setMathContext(MathContext.FLOAT);
+
+ pmml.addModels(treeModel);
+
+ VersionStandardizer inspector = new VersionStandardizer();
+ inspector.applyTo(pmml);
+
+ assertNotNull(pmml.getVersion());
+ assertNull(pmml.getBaseVersion());
+
+ // The getter method is returning the default field value
+ assertEquals(MathContext.DOUBLE, treeModel.getMathContext());
+
+ assertNull(ReflectionUtil.getFieldValue(PMMLAttributes.TREEMODEL_MATHCONTEXT, treeModel));
+ }
+}
\ No newline at end of file
diff --git a/pmml-xjc/src/main/java/org/jpmml/xjc/CodeModelUtil.java b/pmml-xjc/src/main/java/org/jpmml/xjc/CodeModelUtil.java
index acda944e..2c53cf8e 100644
--- a/pmml-xjc/src/main/java/org/jpmml/xjc/CodeModelUtil.java
+++ b/pmml-xjc/src/main/java/org/jpmml/xjc/CodeModelUtil.java
@@ -3,9 +3,18 @@
*/
package org.jpmml.xjc;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.util.Collection;
import java.util.List;
+import com.sun.codemodel.JAnnotatable;
+import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JClass;
+import com.sun.codemodel.JFormatter;
+import com.sun.codemodel.JGenerable;
import com.sun.codemodel.JType;
public class CodeModelUtil {
@@ -24,4 +33,89 @@ public JType getElementType(JType collectionType){
return elementTypes.get(0);
}
+
+ static
+ public boolean hasAnnotation(Collection annotations, Class> clazz){
+ JAnnotationUse annotation = findAnnotation(annotations, clazz);
+
+ return (annotation != null);
+ }
+
+ static
+ public JAnnotationUse findAnnotation(Collection annotations, Class> clazz){
+ String fullName = clazz.getName();
+
+ for(JAnnotationUse annotation : annotations){
+ JClass type = annotation.getAnnotationClass();
+
+ if((type.fullName()).equals(fullName)){
+ return annotation;
+ }
+ }
+
+ return null;
+ }
+
+ static
+ public List getAnnotations(JAnnotatable annotatable){
+
+ try {
+ Class> clazz = annotatable.getClass();
+
+ Field annotationsField;
+
+ while(true){
+
+ try {
+ annotationsField = clazz.getDeclaredField("annotations");
+
+ break;
+ } catch(NoSuchFieldException nsfe){
+ clazz = clazz.getSuperclass();
+
+ if(clazz == null){
+ throw nsfe;
+ }
+ }
+ }
+
+ ensureAccessible(annotationsField);
+
+ return (List)annotationsField.get(annotatable);
+ } catch(ReflectiveOperationException roe){
+ throw new RuntimeException(roe);
+ }
+ }
+
+ static
+ public String stringValue(JGenerable generable){
+ String result;
+
+ try(StringWriter writer = new StringWriter()){
+ generable.generate(new JFormatter(writer));
+
+ result = writer.toString();
+ } catch(IOException ioe){
+ throw new RuntimeException(ioe);
+ }
+
+ if(result.length() >= 2 && (result.startsWith("\"") && result.endsWith("\""))){
+ result = result.substring(1, result.length() - 1);
+ } else
+
+ {
+ throw new RuntimeException();
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("deprecation")
+ static
+ void ensureAccessible(AccessibleObject accessibleObject){
+
+ if(!accessibleObject.isAccessible()){
+ accessibleObject.setAccessible(true);
+ }
+ }
}
\ No newline at end of file
diff --git a/pmml-xjc/src/main/java/org/jpmml/xjc/JacksonPlugin.java b/pmml-xjc/src/main/java/org/jpmml/xjc/JacksonPlugin.java
index c2d3c74f..3ef285d8 100644
--- a/pmml-xjc/src/main/java/org/jpmml/xjc/JacksonPlugin.java
+++ b/pmml-xjc/src/main/java/org/jpmml/xjc/JacksonPlugin.java
@@ -17,6 +17,7 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.sun.codemodel.JAnnotationArrayMember;
import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JAnnotationValue;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
@@ -38,6 +39,7 @@
import com.sun.tools.xjc.outline.EnumOutline;
import com.sun.tools.xjc.outline.FieldOutline;
import com.sun.tools.xjc.outline.Outline;
+import jakarta.xml.bind.annotation.XmlRootElement;
import org.xml.sax.ErrorHandler;
public class JacksonPlugin extends Plugin {
@@ -58,14 +60,18 @@ public boolean run(Outline outline, Options options, ErrorHandler errorHandler){
Collection extends ClassOutline> classOutlines = outline.getClasses();
for(ClassOutline classOutline : classOutlines){
- CClassInfo classInfo = classOutline.target;
JDefinedClass beanClazz = classOutline.implClass;
- if(classInfo.isElement()){
- QName elementName = classInfo.getElementName();
+ List beanClazzAnnotations = CodeModelUtil.getAnnotations(beanClazz);
+
+ JAnnotationUse xmlRootElement = CodeModelUtil.findAnnotation(beanClazzAnnotations, XmlRootElement.class);
+ if(xmlRootElement != null){
+ Map annotationMembers = xmlRootElement.getAnnotationMembers();
+
+ JAnnotationValue nameValue = annotationMembers.get("name");
JAnnotationUse jsonRootName = beanClazz.annotate(JsonRootName.class)
- .param("value", elementName.getLocalPart());
+ .param("value", CodeModelUtil.stringValue(nameValue));
}
FieldOutline[] fieldOutlines = classOutline.getDeclaredFields();
diff --git a/pmml-xjc/src/main/java/org/jpmml/xjc/PMMLPlugin.java b/pmml-xjc/src/main/java/org/jpmml/xjc/PMMLPlugin.java
index 21df1e5f..ed4b4fea 100644
--- a/pmml-xjc/src/main/java/org/jpmml/xjc/PMMLPlugin.java
+++ b/pmml-xjc/src/main/java/org/jpmml/xjc/PMMLPlugin.java
@@ -4,7 +4,6 @@
*/
package org.jpmml.xjc;
-import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -20,7 +19,6 @@
import javax.xml.namespace.QName;
-import com.sun.codemodel.JAnnotatable;
import com.sun.codemodel.JAnnotationArrayMember;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JAnnotationValue;
@@ -59,6 +57,7 @@
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElements;
import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.XmlValue;
import org.eclipse.persistence.oxm.annotations.XmlValueExtension;
import org.glassfish.jaxb.core.api.impl.NameConverter;
@@ -151,7 +150,7 @@ public int compare(CPropertyInfo left, CPropertyInfo right){
try {
Field pkgField = CClassInfoParent.Package.class.getDeclaredField("pkg");
- ensureAccessible(pkgField);
+ CodeModelUtil.ensureAccessible(pkgField);
JPackage subPackage = packageParent.pkg.subPackage(name);
@@ -166,7 +165,7 @@ public int compare(CPropertyInfo left, CPropertyInfo right){
try {
Field elementNameField = CClassInfo.class.getDeclaredField("elementName");
- ensureAccessible(elementNameField);
+ CodeModelUtil.ensureAccessible(elementNameField);
elementNameField.set(classInfo, new QName("http://www.dmg.org/PMML-4_4", name));
} catch(ReflectiveOperationException roe){
@@ -312,17 +311,40 @@ public boolean run(Outline outline, Options options, ErrorHandler errorHandler){
for(ClassOutline classOutline : classOutlines){
JDefinedClass beanClazz = classOutline.implClass;
- List beanClazzAnnotations = getAnnotations(beanClazz);
+ List beanClazzAnnotations = CodeModelUtil.getAnnotations(beanClazz);
- JAnnotationUse xmlAccessorType = findAnnotation(beanClazzAnnotations, XmlAccessorType.class);
+ JAnnotationUse xmlAccessorType = CodeModelUtil.findAnnotation(beanClazzAnnotations, XmlAccessorType.class);
if(xmlAccessorType != null){
beanClazzAnnotations.remove(xmlAccessorType);
}
- JAnnotationUse xmlRootElement = findAnnotation(beanClazzAnnotations, XmlRootElement.class);
+ JAnnotationUse xmlType = CodeModelUtil.findAnnotation(beanClazzAnnotations, XmlType.class);
+ if(xmlType != null){
+ beanClazzAnnotations.remove(xmlType);
+ beanClazzAnnotations.add(0, xmlType);
+ } else
+
+ {
+ throw new RuntimeException();
+ }
+
+ JAnnotationUse xmlRootElement = CodeModelUtil.findAnnotation(beanClazzAnnotations, XmlRootElement.class);
+ if(xmlRootElement == null){
+ String elementName = getElementName(beanClazz.name());
+
+ beanClazz.annotate(XmlRootElement.class)
+ .param("name", elementName)
+ .param("namespace", "http://www.dmg.org/PMML-4_4");
+ }
+
+ xmlRootElement = CodeModelUtil.findAnnotation(beanClazzAnnotations, XmlRootElement.class);
if(xmlRootElement != null){
beanClazzAnnotations.remove(xmlRootElement);
beanClazzAnnotations.add(0, xmlRootElement);
+ } else
+
+ {
+ throw new RuntimeException();
}
Map fieldVars = beanClazz.fields();
@@ -457,11 +479,11 @@ public boolean run(Outline outline, Options options, ErrorHandler errorHandler){
}
}
- List fieldVarAnnotations = getAnnotations(fieldVar);
+ List fieldVarAnnotations = CodeModelUtil.getAnnotations(fieldVar);
// XXX
if(("node").equals(name) || ("nodes").equals(name) || ("scoreDistributions").equals(name)){
- JAnnotationUse xmlElement = findAnnotation(fieldVarAnnotations, XmlElement.class);
+ JAnnotationUse xmlElement = CodeModelUtil.findAnnotation(fieldVarAnnotations, XmlElement.class);
fieldVarAnnotations.remove(xmlElement);
@@ -477,7 +499,7 @@ public boolean run(Outline outline, Options options, ErrorHandler errorHandler){
fieldVarAnnotations.add(0, xmlElements);
} // End if
- if(hasAnnotation(fieldVarAnnotations, XmlValue.class)){
+ if(CodeModelUtil.hasAnnotation(fieldVarAnnotations, XmlValue.class)){
fieldVar.annotate(XmlValueExtension.class);
}
}
@@ -639,6 +661,36 @@ public boolean run(Outline outline, Options options, ErrorHandler errorHandler){
return true;
}
+ static
+ private String getElementName(String name){
+
+ switch(name){
+ // baseline
+ case "CountTable":
+ return "COUNT-TABLE-TYPE";
+ // bayesian_network
+ case "ContinuousDistribution":
+ return name;
+ // bayesian_network
+ case "LognormalDistribution":
+ case "NormalDistribution":
+ case "TriangularDistribution":
+ case "UniformDistribution":
+ return name + "ForBN";
+ // support_vector_machne
+ case "LinearKernel":
+ case "PolynomialKernel":
+ case "RadialBasisKernel":
+ case "SigmoidKernel":
+ return name + "Type";
+ // time_series
+ case "TrendExpoSmooth":
+ return "Trend_ExpoSmooth";
+ default:
+ throw new IllegalArgumentException(name);
+ }
+ }
+
static
private FieldOutline getExtensionsField(ClassOutline classOutline){
Predicate predicate = new Predicate(){
@@ -690,65 +742,12 @@ public boolean test(FieldOutline fieldOutline){
.findFirst().orElse(null);
}
- static
- private boolean hasAnnotation(Collection annotations, Class> clazz){
- JAnnotationUse annotation = findAnnotation(annotations, clazz);
-
- return (annotation != null);
- }
-
- static
- private JAnnotationUse findAnnotation(Collection annotations, Class> clazz){
- String fullName = clazz.getName();
-
- for(JAnnotationUse annotation : annotations){
- JClass type = annotation.getAnnotationClass();
-
- if(checkType(type, fullName)){
- return annotation;
- }
- }
-
- return null;
- }
-
- static
- private List getAnnotations(JAnnotatable annotatable){
-
- try {
- Class> clazz = annotatable.getClass();
-
- Field annotationsField;
-
- while(true){
-
- try {
- annotationsField = clazz.getDeclaredField("annotations");
-
- break;
- } catch(NoSuchFieldException nsfe){
- clazz = clazz.getSuperclass();
-
- if(clazz == null){
- throw nsfe;
- }
- }
- }
-
- ensureAccessible(annotationsField);
-
- return (List)annotationsField.get(annotatable);
- } catch(ReflectiveOperationException roe){
- throw new RuntimeException(roe);
- }
- }
-
static
private void addValues(JAnnotationUse annotationUse, Map memberValues){
try {
Method addValueMethod = JAnnotationUse.class.getDeclaredMethod("addValue", String.class, JAnnotationValue.class);
- ensureAccessible(addValueMethod);
+ CodeModelUtil.ensureAccessible(addValueMethod);
Collection> entries = memberValues.entrySet();
for(Map.Entry entry : entries){
@@ -814,7 +813,7 @@ private JExpression constantExpr(JFieldVar fieldVar){
try {
Field ownerField = JFieldVar.class.getDeclaredField("owner");
- ensureAccessible(ownerField);
+ CodeModelUtil.ensureAccessible(ownerField);
owner = (JDefinedClass)ownerField.get(fieldVar);
} catch(ReflectiveOperationException roe){
@@ -864,7 +863,7 @@ private void addOverrideAnnotations(JDefinedClass beanClazz, String[][] typeMemb
if((name.startsWith("has") || name.startsWith("is") || name.startsWith("get") || name.startsWith("require")) && params.size() == 0){
- if(!hasAnnotation(method.annotations(), Override.class)){
+ if(!CodeModelUtil.hasAnnotation(method.annotations(), Override.class)){
method.annotate(Override.class);
}
} else
@@ -875,7 +874,7 @@ private void addOverrideAnnotations(JDefinedClass beanClazz, String[][] typeMemb
if(name.startsWith("set") && params.size() == 1){
- if(!hasAnnotation(method.annotations(), Override.class)){
+ if(!CodeModelUtil.hasAnnotation(method.annotations(), Override.class)){
method.annotate(Override.class);
}
} else
@@ -927,15 +926,6 @@ private boolean checkType(JType type, String fullName){
return (type.fullName()).equals(fullName);
}
- @SuppressWarnings("deprecation")
- static
- private void ensureAccessible(AccessibleObject accessibleObject){
-
- if(!accessibleObject.isAccessible()){
- accessibleObject.setAccessible(true);
- }
- }
-
static
private int parseVersion(String version){