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 index e32623dc..229a0032 100644 --- a/pmml-model/src/test/java/org/jpmml/model/visitors/VersionDowngraderTest.java +++ b/pmml-model/src/test/java/org/jpmml/model/visitors/VersionDowngraderTest.java @@ -18,6 +18,7 @@ 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; @@ -159,7 +160,7 @@ public void downgradeTrendExpoSmooth(){ try { downgrade(trendExpoSmooth, Version.PMML_4_0); - } catch(IllegalArgumentException iae){ + } catch(UnsupportedElementException uee){ // Ignored } } 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 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 141ca4e8..a2efbbfb 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){ @@ -831,7 +830,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){ @@ -881,7 +880,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 @@ -892,7 +891,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 @@ -944,15 +943,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){