diff --git a/src/org/atriasoft/exml/Exml.java b/src/org/atriasoft/exml/Exml.java index e85a411..bcf8657 100644 --- a/src/org/atriasoft/exml/Exml.java +++ b/src/org/atriasoft/exml/Exml.java @@ -99,7 +99,7 @@ public class Exml { } return null; } catch (final ExmlException ex) { - //ex.printStackTrace(); + ex.printStackTrace(); throw ex; } } @@ -203,7 +203,17 @@ public class Exml { * @return false : An error occured * @return true : Parsing is OK */ - + public static void store(final Uri uri, final Object root, final String rootNodeName) throws ExmlException, IOException { + StringBuilder builder = new StringBuilder(); + Exml.generate(root, rootNodeName, builder); + Uri.writeAll(uri, builder.toString()); + } + + public static void store(final Path path, final Object root, final String rootNodeName) throws ExmlException, IOException { + StringBuilder builder = new StringBuilder(); + Exml.generate(root, rootNodeName, builder); + Files.writeString(path, builder.toString()); + } /* * public boolean store( Uri _uri){ String createData; if (generate(createData) * == false) { Log.error("Error while creating the XML: " + _uri); return false; diff --git a/src/org/atriasoft/exml/annotation/XmlDefaultNullValue.java b/src/org/atriasoft/exml/annotation/XmlDefaultNullValue.java new file mode 100644 index 0000000..0443b0f --- /dev/null +++ b/src/org/atriasoft/exml/annotation/XmlDefaultNullValue.java @@ -0,0 +1,18 @@ +package org.atriasoft.exml.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExmlAnnotation +public @interface XmlDefaultNullValue { + /** + * Set the default value at true in the constructor if needed (set at 0 for basic type int, long ...). + * @return true if the element are by default at null. + */ + boolean value() default true; + +} diff --git a/src/org/atriasoft/exml/annotation/XmlIgnoreUnknow.java b/src/org/atriasoft/exml/annotation/XmlIgnoreUnknow.java new file mode 100644 index 0000000..e2fb52d --- /dev/null +++ b/src/org/atriasoft/exml/annotation/XmlIgnoreUnknow.java @@ -0,0 +1,20 @@ +package org.atriasoft.exml.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marker annotation that permit to set the default parsing as attributes. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExmlAnnotation +public @interface XmlIgnoreUnknow { + /** + * Set the element ignore unknown Nodes. + * @return true does not display unknown node. + */ + boolean value() default true; +} \ No newline at end of file diff --git a/src/org/atriasoft/exml/annotation/XmlManaged.java b/src/org/atriasoft/exml/annotation/XmlManaged.java index af233c0..8b3a8e0 100644 --- a/src/org/atriasoft/exml/annotation/XmlManaged.java +++ b/src/org/atriasoft/exml/annotation/XmlManaged.java @@ -9,7 +9,7 @@ import java.lang.annotation.Target; * Marker annotation that force the xml Parser to manage this element (used when the class is mark as @XmldefaultNotManaged). * */ -@Target({ ElementType.FIELD, ElementType.METHOD}) +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @ExmlAnnotation public @interface XmlManaged { diff --git a/src/org/atriasoft/exml/builder/BuilderIntrospection.java b/src/org/atriasoft/exml/builder/BuilderIntrospection.java index 78cbd7e..ca4d03d 100644 --- a/src/org/atriasoft/exml/builder/BuilderIntrospection.java +++ b/src/org/atriasoft/exml/builder/BuilderIntrospection.java @@ -7,7 +7,6 @@ import org.atriasoft.eStringSerialize.StringSerializer; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlException; -import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.atriasoft.exml.internal.Log; public class BuilderIntrospection implements Builder { @@ -55,6 +54,10 @@ public class BuilderIntrospection implements Builder { } typeClass = model.getClassType(); } else { + if (!introspectionObject.isSubNodeOrPropertyExist(nodeName) && model.isIgnoreUnknown()) { + Log.debug("Ignore node: '" + nodeName + "' Does not exist..."); + return null; + } typeClass = introspectionObject.getTypeOfSubNode(nodeName); listTreeName = introspectionObject.getTreeNameOfSubNode(nodeName); } @@ -106,6 +109,10 @@ public class BuilderIntrospection implements Builder { } String beanName = model.getBeanName(propertyName); if (beanName == null) { + if (model.isIgnoreUnknown()) { + Log.debug("Ignore node : '" + propertyName + "'"); + return; + } throw new ExmlAttributeDoesNotExist("The node '" + propertyName + "' Does not exist..."); } Class typeClass = model.getTypeOfProperty(propertyName); @@ -189,6 +196,7 @@ public class BuilderIntrospection implements Builder { @Override public void newElementFinished(final Object parent, final String tmpName, final Object element) throws ExmlException { + Log.debug("new element fionished : ==> " + tmpName); if (parent == null || element == null) { return; } diff --git a/src/org/atriasoft/exml/builder/IntrospectionModel.java b/src/org/atriasoft/exml/builder/IntrospectionModel.java index edf0314..7755cc9 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionModel.java +++ b/src/org/atriasoft/exml/builder/IntrospectionModel.java @@ -8,10 +8,15 @@ import org.atriasoft.exml.exception.ExmlBuilderException; public abstract class IntrospectionModel { protected static final Boolean DEFAULT_ATTRIBUTE = false; + protected static final Boolean DEFAULT_IGNORE_UNBKNOWN = false; + protected static final Boolean DEFAULT_DEFAULT_NULL_VALUE = false; protected static final Boolean DEFAULT_CASE_SENSITIVE = true; protected static final Boolean DEFAULT_MANAGED = true; protected static final Boolean DEFAULT_OPTIONAL = false; + protected boolean defaultNullValue = false; + protected boolean ignoreUnknown = false; + protected final Class classType; public List getNodes() { @@ -78,8 +83,14 @@ public abstract class IntrospectionModel { public boolean isEnum() { return this.classType.isEnum(); } - public String getBeanName(String nodeName) { + public String getBeanName(final String nodeName) { return nodeName; } + protected boolean isIgnoreUnknown() { + return this.ignoreUnknown; + } + protected boolean isDefaultNullValue() { + return this.defaultNullValue; + } } diff --git a/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java b/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java index 22cf294..6055d6f 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java +++ b/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java @@ -39,7 +39,8 @@ public class IntrospectionModelComplex extends IntrospectionModel { private final List constructors = new ArrayList<>(); private final List elements = new ArrayList<>(); - public String getBeanName(String nodeName) { + @Override + public String getBeanName(final String nodeName) { for (IntrospectionProperty elem : this.elements) { if (elem.isCompatible(nodeName)) { return elem.getBeanName(); @@ -483,6 +484,10 @@ public class IntrospectionModelComplex extends IntrospectionModel { prop.setSetter(modifier); } } + this.ignoreUnknown = ReflectTools.getIsIgnoreUnknown(classType, IntrospectionModel.DEFAULT_IGNORE_UNBKNOWN); + + this.defaultNullValue = ReflectTools.getIsDefaultNullValue(classType, IntrospectionModel.DEFAULT_DEFAULT_NULL_VALUE); + // Set only at the end ==> no need before... final Boolean isDefaultAttribute = ReflectTools.getIsDefaultAttribute(classType, IntrospectionModel.DEFAULT_ATTRIBUTE); for (IntrospectionProperty prop : this.elements) { @@ -540,6 +545,12 @@ public class IntrospectionModelComplex extends IntrospectionModel { Log.warning(" optionnal: " + prop.isOptionnal()); Log.warning(" constructor: " + prop.isCanBeSetByConstructor()); Log.warning(" get/set: " + prop.canGetValue() + " / " + prop.canSetValue()); + Log.warning(" type: " + prop.getType().getCanonicalName()); + if (prop.getSubType() != null) { + Log.warning(" sub-type: " + prop.getSubType().getCanonicalName()); + } else { + Log.warning(" sub-type: null"); + } } } @@ -629,67 +640,85 @@ public class IntrospectionModelComplex extends IntrospectionModel { Object tmp = null; // STEP 1: try to create the object with provided parameter (if a constructor exist....) if (!this.constructors.isEmpty()) { + Object[] inputs = null; + ConstructorModel betterModel = null; + int lastEmpty = Integer.MAX_VALUE; // try to find the constructor that fit with parameters ... for (ConstructorModel elem : this.constructors) { int offset = this.isSubClass?1:0; - Object[] inputs = new Object[elem.values().length+offset]; - inputs[0] = null; + Object[] inputsTmp = new Object[elem.values().length+offset]; + inputsTmp[0] = null; + int empty = 0; for (int iii=0; iii tmppp = nodes.get(elem.values()[iii]); - if (tmppp != null && tmppp.size() >=1) { - valueToInject = tmppp.get(0); + if (tmppp != null) { + if (List.class == findBeanNodeDescription(elem.values()[iii]).getType() ) { + valueToInject = tmppp; + } else if (tmppp.size() >=1) { + valueToInject = tmppp.get(0); + } } } if (valueToInject == null) { - inputs = null; - break; + empty++; + inputsTmp[iii+offset] = null; + continue; } - inputs[iii+offset] = valueToInject; + inputsTmp[iii+offset] = valueToInject; } - if (inputs != null) { - // here we find our constructor... - try { - switch(inputs.length) { - case 0: tmp = elem.constructor().newInstance(); break; - case 1: tmp = elem.constructor().newInstance(inputs[0]); break; - case 2: tmp = elem.constructor().newInstance(inputs[0],inputs[1]); break; - case 3: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2]); break; - case 4: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3]); break; - case 5: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4]); break; - case 6: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5]); break; - case 7: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6]); break; - case 8: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7]); break; - case 9: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8]); break; - case 10: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9]); break; - case 11: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10]); break; - case 12: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11]); break; - case 13: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12]); break; - case 14: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13]); break; - case 15: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14]); break; - case 16: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15]); break; - case 17: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16]); break; - case 18: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17]); break; - case 19: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18]); break; - case 20: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18],inputs[19]); break; - case 21: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18],inputs[19],inputs[20]); break; - default: - //tmp = elem.constructor().newInstance(new Object[] {inputs}); break; - throw new ExmlBuilderException("to much parameter in the constructor... " + inputs.length); + if (lastEmpty > empty) { + inputs = inputsTmp; + betterModel = elem; + lastEmpty = empty; + } + } + if (betterModel != null) { + if (isDefaultNullValue() || lastEmpty == 0) { + ConstructorModel elem = betterModel; + if (inputs != null) { + // here we find our constructor... + try { + switch(inputs.length) { + case 0: tmp = elem.constructor().newInstance(); break; + case 1: tmp = elem.constructor().newInstance(inputs[0]); break; + case 2: tmp = elem.constructor().newInstance(inputs[0],inputs[1]); break; + case 3: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2]); break; + case 4: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3]); break; + case 5: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4]); break; + case 6: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5]); break; + case 7: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6]); break; + case 8: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7]); break; + case 9: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8]); break; + case 10: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9]); break; + case 11: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10]); break; + case 12: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11]); break; + case 13: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12]); break; + case 14: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13]); break; + case 15: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14]); break; + case 16: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15]); break; + case 17: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16]); break; + case 18: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17]); break; + case 19: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18]); break; + case 20: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18],inputs[19]); break; + case 21: tmp = elem.constructor().newInstance(inputs[0],inputs[1],inputs[2],inputs[3],inputs[4],inputs[5],inputs[6],inputs[7],inputs[8],inputs[9],inputs[10],inputs[11],inputs[12],inputs[13],inputs[14],inputs[15],inputs[16],inputs[17],inputs[18],inputs[19],inputs[20]); break; + default: + //tmp = elem.constructor().newInstance(new Object[] {inputs}); break; + throw new ExmlBuilderException("to much parameter in the constructor... " + inputs.length); + } + //tmp = elem.constructor().newInstance(inputs); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new ExmlBuilderException("Error when creating the Object ..." + this.classType.getCanonicalName()); + } + // Remove all previously added parameters + for (int iii=0; iii find '" + Arrays.toString(propMethode.getNames()) + " type=" + propMethode.getType() + " sub-type=" + propMethode.getSubType() ); if (propMethode.getType().isAssignableFrom(value.getClass())) { @@ -823,7 +875,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } // try with direct field { - final IntrospectionProperty propField = findPropertyDescription(name); + final IntrospectionProperty propField = findBeanPropertyDescription(beanName); if (propField != null && propField.canSetValue()) { Log.verbose(" ==> find '" + Arrays.toString(propField.getNames()) + " type=" + propField.getType() + " sub-type=" + propField.getSubType() ); if (propField.getType().isAssignableFrom(value.getClass())) { @@ -889,49 +941,49 @@ public class IntrospectionModelComplex extends IntrospectionModel { return; } } - throw new ExmlBuilderException("can not find the field '" + name + "'"); + throw new ExmlBuilderException("can not find the field '" + beanName + "'"); } /** * Detect a subNode, and ask the type of the node at the parent Class - * @param nodeName Name of the node + * @param nodeBeanName Name of the node (bean name access ==> not the XML name) * @return Class of the node to create */ @Override - public Class getTypeOfSubNode(final String nodeName) throws ExmlBuilderException { - Log.error(" nodeType='" + nodeName + "'"); - final IntrospectionProperty propMethode = findNodeDescription(nodeName); + public Class getTypeOfSubNode(final String nodeBeanName) throws ExmlBuilderException { + Log.error(" nodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName); if (propMethode != null && propMethode.canSetValue()) { Log.error(" ==> find '" + propMethode.getNames()); return propMethode.getType(); } - throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' availlable: " + getNodeAvaillable()); } @Override - public Class getTypeOfSubNodeList(final String nodeName) throws ExmlBuilderException { - Log.error(" nodeType='" + nodeName + "'"); - final IntrospectionProperty propMethode = findNodeDescription(nodeName); + public Class getTypeOfSubNodeList(final String nodeBeanName) throws ExmlBuilderException { + Log.error(" nodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName); if (propMethode != null && propMethode.canSetValue()) { Log.error(" ==> find '" + propMethode.getNames()); return propMethode.getSubType(); } - throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' availlable: " + getNodeAvaillable()); } @Override - public String getTreeNameOfSubNode(final String nodeName) throws ExmlBuilderException { - Log.error(" nodeType='" + nodeName + "'"); - final IntrospectionProperty propMethode = findNodeDescription(nodeName); + public String getTreeNameOfSubNode(final String nodeBeanName) throws ExmlBuilderException { + Log.error(" nodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName); if (propMethode != null && propMethode.canSetValue()) { Log.error(" ==> find '" + propMethode.getNames()); return propMethode.getListName(); } - throw new ExmlBuilderException("can not find the field '" + nodeName + "'"); + throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "'"); } @Override public Class getTypeOfProperty(final String nodeName) throws ExmlBuilderException { - Log.error(" nodeType='" + nodeName + "'"); + Log.error("nodeType='" + nodeName + "'"); final IntrospectionProperty propField = findPropertyDescription(nodeName); if (propField != null && propField.canSetValue()) { Log.error(" ==> find '" + propField.getNames()); diff --git a/src/org/atriasoft/exml/builder/IntrospectionObject.java b/src/org/atriasoft/exml/builder/IntrospectionObject.java index 6d7292f..29be918 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionObject.java +++ b/src/org/atriasoft/exml/builder/IntrospectionObject.java @@ -30,48 +30,57 @@ public class IntrospectionObject { } public void putProperty(final String propertyName, final Object propertyValue) throws ExmlException { - final String beanName = modelInterface.getBeanName(propertyName); + final String beanName = this.modelInterface.getBeanName(propertyName); if (this.properties.containsKey(beanName)) { throw new ExmlBuilderException("Property have multiple values ==> impossible case; A Node must contain only 1 attibutes"); } this.properties.put(beanName, propertyValue); } + public boolean isSubNodeOrPropertyExist(final String nodeName) { + final String beanName = this.modelInterface.getBeanName(nodeName); + if (beanName == null) { + return false; + } + return true; + } + /** * Detect a subNode, and ask the type of the node at the parent Class * @param nodeName Name of the node * @return Class of the node to create */ public Class getTypeOfSubNode(final String nodeName) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } return this.modelInterface.getTypeOfSubNode(beanName); } public Class getTypeOfSubNodeSubType(final String nodeName) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } return this.modelInterface.getTypeOfSubNodeList(beanName); } public String getTreeNameOfSubNode(final String nodeName) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } return this.modelInterface.getTreeNameOfSubNode(beanName); } + public Class getTypeOfProperty(final String nodeName) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } return this.modelInterface.getTypeOfProperty(beanName); } public Class getTypeOfSubProperty(final String nodeName) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } @@ -87,7 +96,11 @@ public class IntrospectionObject { @SuppressWarnings("unchecked") public void addObject(final String nodeName, final Object value) throws ExmlException { - final String beanName = modelInterface.getBeanName(nodeName); + if (value == null) { + // specific case when a List is empty but define for a specific list ==> need no action + return; + } + final String beanName = this.modelInterface.getBeanName(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } diff --git a/src/org/atriasoft/exml/builder/IntrospectionProperty.java b/src/org/atriasoft/exml/builder/IntrospectionProperty.java index 2877803..a642f17 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionProperty.java +++ b/src/org/atriasoft/exml/builder/IntrospectionProperty.java @@ -1,6 +1,7 @@ package org.atriasoft.exml.builder; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.atriasoft.eStringSerialize.StringSerializer; @@ -111,17 +112,22 @@ public final class IntrospectionProperty { * @return true if the element is compatible, false otherwise */ public boolean isCompatible(final String name) { + Log.warning("Check compatible : '" + name + "' in " + Arrays.toString(this.names)); if (this.caseSensitive) { for (final String elem : this.names) { if (elem.contentEquals(name)) { + Log.warning(" - '" + elem + "' ==> true"); return true; } + Log.warning(" - '" + elem + "' == false"); } } else { for (final String elem : this.names) { if (elem.equalsIgnoreCase(name)) { + Log.warning(" - '" + elem + "' ==> true"); return true; } + Log.warning(" - '" + elem + "' == false"); } } return false; diff --git a/src/org/atriasoft/exml/internal/Log.java b/src/org/atriasoft/exml/internal/Log.java index 98691a9..e833beb 100644 --- a/src/org/atriasoft/exml/internal/Log.java +++ b/src/org/atriasoft/exml/internal/Log.java @@ -9,7 +9,7 @@ import io.scenarium.logger.LogLevel; import io.scenarium.logger.Logger; public class Log { - private static final boolean FORCE = true; + private static final boolean FORCE = false; private static final String LIB_NAME = "exml"; private static final String LIB_NAME_DRAW = Logger.getDrawableName(Log.LIB_NAME); private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.CRITICAL); diff --git a/src/org/atriasoft/exml/parser/ParseXml.java b/src/org/atriasoft/exml/parser/ParseXml.java index c2873db..cd04d46 100644 --- a/src/org/atriasoft/exml/parser/ParseXml.java +++ b/src/org/atriasoft/exml/parser/ParseXml.java @@ -1,7 +1,6 @@ package org.atriasoft.exml.parser; import org.atriasoft.exml.builder.Builder; -import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlException; import org.atriasoft.exml.exception.ExmlParserError; import org.atriasoft.exml.exception.ExmlParserErrorMulti; @@ -487,7 +486,6 @@ public class ParseXml { if (data.charAt(jjj) == '>') { pos.value = jjj; filePos.add(tmpPos); - Log.todo("End of node: '" + nameElement + "' ==> need add build result to parent and create object ..."); this.builder.endElement(parent); return true; } diff --git a/src/org/atriasoft/exml/reflect/ReflectTools.java b/src/org/atriasoft/exml/reflect/ReflectTools.java index 785d569..ef3c4b8 100644 --- a/src/org/atriasoft/exml/reflect/ReflectTools.java +++ b/src/org/atriasoft/exml/reflect/ReflectTools.java @@ -14,7 +14,9 @@ import org.atriasoft.exml.annotation.XmlCaseSensitive; import org.atriasoft.exml.annotation.XmlDefaultAttibute; import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive; import org.atriasoft.exml.annotation.XmlDefaultManaged; +import org.atriasoft.exml.annotation.XmlDefaultNullValue; import org.atriasoft.exml.annotation.XmlDefaultOptional; +import org.atriasoft.exml.annotation.XmlIgnoreUnknow; import org.atriasoft.exml.annotation.XmlList; import org.atriasoft.exml.annotation.XmlManaged; import org.atriasoft.exml.annotation.XmlName; @@ -82,7 +84,7 @@ public class ReflectTools { return new Class[] {type, subType}; } - public static Class[] getTypeParameterfunction(Constructor elem, int paramId) throws ClassNotFoundException { + public static Class[] getTypeParameterfunction(final Constructor elem, final int paramId) throws ClassNotFoundException { Class type = null; Class subType = null; @@ -90,12 +92,13 @@ public class ReflectTools { if (List.class.isAssignableFrom(type)) { Class internalModelClass = null; Type[] empppe = elem.getGenericParameterTypes(); - if (empppe.length > 0) { - if (empppe[0] instanceof ParameterizedType plopppppp) { + if (empppe.length > paramId) { + if (empppe[paramId] instanceof ParameterizedType plopppppp) { Type[] realType = plopppppp.getActualTypeArguments(); - if (realType.length > paramId) { - Log.warning(" -->> " + realType[paramId]); - internalModelClass = Class.forName(realType[paramId].getTypeName()); + //Log.info("ppplllppp: " + realType.length); + if (realType.length > 0) { + Log.warning(" -->> " + realType[0]); + internalModelClass = Class.forName(realType[0].getTypeName()); } } } @@ -157,6 +160,26 @@ public class ReflectTools { } return ((XmlDefaultAttibute) annotation[0]).value(); } + public static Boolean getIsIgnoreUnknown(final Class classType, final Boolean defaultValue) throws ExmlBuilderException { + final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlIgnoreUnknow.class); + if (annotation.length == 0) { + return defaultValue; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlIgnoreUnknow on " + classType.getClass().getCanonicalName()); + } + return ((XmlIgnoreUnknow) annotation[0]).value(); + } + public static Boolean getIsDefaultNullValue(final Class classType, final Boolean defaultValue) throws ExmlBuilderException { + final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultNullValue.class); + if (annotation.length == 0) { + return defaultValue; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlDefaultNullValue on " + classType.getClass().getCanonicalName()); + } + return ((XmlDefaultNullValue) annotation[0]).value(); + } public static Boolean getIsDefaultManaged(final Class classType, final Boolean defaultValue) throws ExmlBuilderException { final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultManaged.class); diff --git a/test/src/test/atriasoft/exml/ExmlLocal.java b/test/src/test/atriasoft/exml/ExmlLocal.java index e3177f1..35836fa 100644 --- a/test/src/test/atriasoft/exml/ExmlLocal.java +++ b/test/src/test/atriasoft/exml/ExmlLocal.java @@ -6,8 +6,9 @@ package test.atriasoft.exml; import org.atriasoft.exml.Exml; -import org.atriasoft.exml.exception.ExmlBuilderException; +import org.atriasoft.exml.exception.ExmlException; import org.atriasoft.exml.model.XmlNode; + import org.junit.jupiter.api.Assertions; class ExmlLocal { @@ -26,7 +27,7 @@ class ExmlLocal { Assertions.fail("Must have detected an error"); return; } - } catch (final ExmlBuilderException e) { + } catch (final ExmlException e) { if (errorPos == 1) { return; } diff --git a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorCaseSensitive.java b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorCaseSensitive.java index c92a108..8496346 100644 --- a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorCaseSensitive.java +++ b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorCaseSensitive.java @@ -12,9 +12,8 @@ import org.atriasoft.exml.annotation.XmlDefaultAttibute; import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive; import org.atriasoft.exml.annotation.XmlName; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; -import org.atriasoft.exml.exception.ExmlException; import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; -import org.atriasoft.exml.exception.ExmlParserErrorMulti; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorManaged.java b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorManaged.java index 7ba9fe2..ce2172a 100644 --- a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorManaged.java +++ b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorManaged.java @@ -7,10 +7,6 @@ package test.atriasoft.exml; import org.atriasoft.exml.Exml; -import org.atriasoft.exml.annotation.XmlManaged; -import org.atriasoft.exml.annotation.XmlCaseSensitive; -import org.atriasoft.exml.annotation.XmlDefaultAttibute; -import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive; import org.atriasoft.exml.annotation.XmlDefaultManaged; import org.atriasoft.exml.annotation.XmlManaged; import org.atriasoft.exml.annotation.XmlName; diff --git a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorNames.java b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorNames.java index bd7e846..29b85ee 100644 --- a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorNames.java +++ b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorNames.java @@ -9,7 +9,6 @@ package test.atriasoft.exml; import org.atriasoft.exml.Exml; import org.atriasoft.exml.annotation.XmlAttribute; import org.atriasoft.exml.annotation.XmlCaseSensitive; -import org.atriasoft.exml.annotation.XmlDefaultAttibute; import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive; import org.atriasoft.exml.annotation.XmlName; @@ -579,6 +578,55 @@ public class ExmlTestIntrospectionDecoratorNames { Assertions.assertEquals(-98885, root.getPFinalValueS()); Assertions.assertEquals(-8754, root.getPFinalValueT()); } + public class ChangingNames { + private String value1RataPlouf; + @XmlName("value2") + public String value2Ballet; + private String value3RataPlouf; + @XmlAttribute + @XmlName("value4") + public String value4Ballet; + + @XmlName("value1") + public String getValue1RataPlouf() { + return this.value1RataPlouf; + } + public void setValue1RataPlouf(final String value1RataPlouf) { + this.value1RataPlouf = value1RataPlouf; + } + @XmlAttribute + @XmlName("value3") + public String getValue3RataPlouf() { + return this.value3RataPlouf; + } + + public void setValue3RataPlouf(final String value3RataPlouf) { + this.value3RataPlouf = value3RataPlouf; + } + } + @Test + public void testChangingNames() { + ChangingNames elem = new ChangingNames(); + elem.value2Ballet = "55"; + elem.value4Ballet = "78"; + elem.setValue1RataPlouf("51"); + elem.setValue3RataPlouf("24"); + + StringBuilder builder = new StringBuilder(); + Assertions.assertDoesNotThrow(() -> Exml.generate(elem, ExmlTestIntrospectionObject.NODE_NAME, builder)); + String dataTest = builder.toString(); + Log.warning("data generated: " + builder.toString()); + Assertions.assertEquals("\n" + + " 51\n" + + " 55\n" + + "", dataTest); + + final ChangingNames root = Assertions.assertDoesNotThrow(() -> Exml.parseOne(dataTest, ChangingNames.class, ExmlTestIntrospectionObject.NODE_NAME)); + Assertions.assertEquals("55", root.value2Ballet); + Assertions.assertEquals("78", root.value4Ballet); + Assertions.assertEquals("51", root.getValue1RataPlouf()); + Assertions.assertEquals("24", root.getValue3RataPlouf()); + } } diff --git a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorOptionnal.java b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorOptionnal.java index 7443759..6545734 100644 --- a/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorOptionnal.java +++ b/test/src/test/atriasoft/exml/ExmlTestIntrospectionDecoratorOptionnal.java @@ -7,13 +7,10 @@ package test.atriasoft.exml; import org.atriasoft.exml.Exml; -import org.atriasoft.exml.annotation.XmlAttribute; -import org.atriasoft.exml.annotation.XmlCaseSensitive; -import org.atriasoft.exml.annotation.XmlDefaultAttibute; -import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive; import org.atriasoft.exml.annotation.XmlDefaultOptional; import org.atriasoft.exml.annotation.XmlName; import org.atriasoft.exml.annotation.XmlOptional; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/test/src/test/atriasoft/exml/ExmlTestIntrospectionGenerate.java b/test/src/test/atriasoft/exml/ExmlTestIntrospectionGenerate.java index e8efb75..e5eb100 100644 --- a/test/src/test/atriasoft/exml/ExmlTestIntrospectionGenerate.java +++ b/test/src/test/atriasoft/exml/ExmlTestIntrospectionGenerate.java @@ -6,7 +6,7 @@ package test.atriasoft.exml; import org.atriasoft.exml.Exml; -import org.atriasoft.exml.exception.ExmlBuilderException; +import org.atriasoft.exml.exception.ExmlException; import org.atriasoft.exml.exception.ExmlParserErrorMulti; import org.junit.jupiter.api.BeforeAll; @@ -24,7 +24,7 @@ public class ExmlTestIntrospectionGenerate { } @Test - public void test1() throws ExmlParserErrorMulti, ExmlBuilderException { + public void test1() throws ExmlParserErrorMulti, ExmlException { ClassPublicMemberOnly elem = new ClassPublicMemberOnly(); elem.memberArrayBoolean = new boolean[] {false, true}; elem.memberArrayBooleanClass = new Boolean[] {false, true, true}; @@ -54,7 +54,7 @@ public class ExmlTestIntrospectionGenerate { } @Test - public void test2() throws ExmlParserErrorMulti, ExmlBuilderException { + public void test2() throws ExmlParserErrorMulti, ExmlException { ClassPublicMethodOnly elem = new ClassPublicMethodOnly(); elem.setMemberArrayBoolean ( new boolean[] {false, true}); elem.setMemberArrayBooleanClass ( new Boolean[] {false, true, true}); @@ -85,7 +85,7 @@ public class ExmlTestIntrospectionGenerate { @Test - public void test3() throws ExmlParserErrorMulti, ExmlBuilderException { + public void test3() throws ExmlParserErrorMulti, ExmlException { ClassPublicMethodeNode elem = new ClassPublicMethodeNode(); elem.setMemberArrayBoolean ( new boolean[] {false, true}); elem.setMemberArrayBooleanClass ( new Boolean[] {false, true, true});