From 6adc3b38976efbc3eab32590896965e753cf2f4a Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Sun, 24 Apr 2022 08:01:52 +0200 Subject: [PATCH] [DEV] add Text interface for injection (concept of pack text and factory Model --- .../eStringSerialize/StringSerializer.java | 199 ++++----- src/org/atriasoft/exml/Exml.java | 24 ++ .../atriasoft/exml/annotation/XmlFactory.java | 3 + .../atriasoft/exml/annotation/XmlText.java | 20 + src/org/atriasoft/exml/builder/Builder.java | 38 +- .../exml/builder/BuilderGeneric.java | 36 +- .../exml/builder/BuilderIntrospection.java | 133 ++++-- .../exml/builder/IntrospectionModel.java | 116 +++-- .../exml/builder/IntrospectionModelArray.java | 27 +- .../builder/IntrospectionModelComplex.java | 317 +++++++++----- .../exml/builder/IntrospectionObject.java | 15 +- .../exml/builder/IntrospectionProperty.java | 114 ++++- src/org/atriasoft/exml/parser/ParseXml.java | 137 ++++-- src/org/atriasoft/exml/parser/Tools.java | 404 +++++++++--------- .../atriasoft/exml/reflect/ReflectTools.java | 85 +++- 15 files changed, 1093 insertions(+), 575 deletions(-) create mode 100644 src/org/atriasoft/exml/annotation/XmlText.java diff --git a/src/org/atriasoft/eStringSerialize/StringSerializer.java b/src/org/atriasoft/eStringSerialize/StringSerializer.java index 88272ac..84e81ca 100644 --- a/src/org/atriasoft/eStringSerialize/StringSerializer.java +++ b/src/org/atriasoft/eStringSerialize/StringSerializer.java @@ -7,202 +7,192 @@ public class StringSerializer { private static final Map, Converter> VALUES_OF = new HashMap<>(); static { StringSerializer.VALUES_OF.put(byte.class, new Converter() { + @Override + public String toString(final Object data) { + return Byte.toString((Byte) data); + } + @Override public Object valueOf(final String value) { return Byte.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Byte.toString((Byte)data); - } }); StringSerializer.VALUES_OF.put(Byte.class, new Converter() { + @Override + public String toString(final Object data) { + return Byte.toString((Byte) data); + } + @Override public Object valueOf(final String value) { return Byte.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Byte.toString((Byte)data); - } }); StringSerializer.VALUES_OF.put(int.class, new Converter() { + @Override + public String toString(final Object data) { + return Integer.toString((Integer) data); + } + @Override public Object valueOf(final String value) { return Integer.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Integer.toString((Integer)data); - } }); StringSerializer.VALUES_OF.put(Integer.class, new Converter() { + @Override + public String toString(final Object data) { + return Integer.toString((Integer) data); + } + @Override public Object valueOf(final String value) { return Integer.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Integer.toString((Integer)data); - } }); StringSerializer.VALUES_OF.put(long.class, new Converter() { + @Override + public String toString(final Object data) { + return Long.toString((Long) data); + } + @Override public Object valueOf(final String value) { return Long.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Long.toString((Long)data); - } }); StringSerializer.VALUES_OF.put(Long.class, new Converter() { + @Override + public String toString(final Object data) { + return Long.toString((Long) data); + } + @Override public Object valueOf(final String value) { return Long.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Long.toString((Long)data); - } }); StringSerializer.VALUES_OF.put(short.class, new Converter() { + @Override + public String toString(final Object data) { + return Short.toString((Short) data); + } + @Override public Object valueOf(final String value) { return Short.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Short.toString((Short)data); - } }); StringSerializer.VALUES_OF.put(Short.class, new Converter() { + @Override + public String toString(final Object data) { + return Short.toString((Short) data); + } + @Override public Object valueOf(final String value) { return Short.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Short.toString((Short)data); - } }); StringSerializer.VALUES_OF.put(float.class, new Converter() { + @Override + public String toString(final Object data) { + return Float.toString((Float) data); + } + @Override public Object valueOf(final String value) { return Float.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Float.toString((Float)data); - } }); StringSerializer.VALUES_OF.put(Float.class, new Converter() { + @Override + public String toString(final Object data) { + return Float.toString((Float) data); + } + @Override public Object valueOf(final String value) { return Float.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Float.toString((Float)data); - } }); StringSerializer.VALUES_OF.put(double.class, new Converter() { + @Override + public String toString(final Object data) { + return Double.toString((Double) data); + } + @Override public Object valueOf(final String value) { return Double.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Double.toString((Double)data); - } }); StringSerializer.VALUES_OF.put(Double.class, new Converter() { + @Override + public String toString(final Object data) { + return Double.toString((Double) data); + } + @Override public Object valueOf(final String value) { return Double.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Double.toString((Double)data); - } }); StringSerializer.VALUES_OF.put(boolean.class, new Converter() { + @Override + public String toString(final Object data) { + return Boolean.toString((Boolean) data); + } + @Override public Object valueOf(final String value) { return Boolean.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Boolean.toString((Boolean)data); - } }); StringSerializer.VALUES_OF.put(Boolean.class, new Converter() { + @Override + public String toString(final Object data) { + return Boolean.toString((Boolean) data); + } + @Override public Object valueOf(final String value) { return Boolean.valueOf(value.trim()); } - - @Override - public String toString(final Object data) { - return Boolean.toString((Boolean)data); - } }); StringSerializer.VALUES_OF.put(String.class, new Converter() { + @Override + public String toString(final Object data) { + return (String) data; + } + @Override public Object valueOf(final String value) { return value; } - - @Override - public String toString(final Object data) { - return (String)data; - } }); } - + public static boolean contains(final Class clazz) { return StringSerializer.VALUES_OF.containsKey(clazz); } - /** - * Un-serialize a String in a specified Object - * @param value String to parse - * @return Data generated or Null - */ - public static Object valueOf(final Class clazz, final String value) { - if (value == null) { - return null; - } - Converter conv = StringSerializer.VALUES_OF.get(clazz); - return conv.valueOf(value); - } - public static String toString(final byte data) { - return Byte.toString(data); - } + public static String toString(final boolean data) { return Boolean.toString(data); } + + public static String toString(final byte data) { + return Byte.toString(data); + } + public static String toString(final int data) { return Integer.toString(data); } - public static String toString(final short data) { - return Short.toString(data); - } + public static String toString(final long data) { return Long.toString(data); } + /** * Serialize in a string the require data * @param data Object to serialize @@ -212,10 +202,27 @@ public class StringSerializer { if (data == null) { return null; } - Class clazz = data.getClass(); - Converter conv = StringSerializer.VALUES_OF.get(clazz); + final Class clazz = data.getClass(); + final Converter conv = StringSerializer.VALUES_OF.get(clazz); return conv.toString(data); } + public static String toString(final short data) { + return Short.toString(data); + } + + /** + * Un-serialize a String in a specified Object + * @param value String to parse + * @return Data generated or Null + */ + public static Object valueOf(final Class clazz, final String value) { + if (value == null) { + return null; + } + final Converter conv = StringSerializer.VALUES_OF.get(clazz); + return conv.valueOf(value); + } + private StringSerializer() {} } diff --git a/src/org/atriasoft/exml/Exml.java b/src/org/atriasoft/exml/Exml.java index 9321241..c721151 100644 --- a/src/org/atriasoft/exml/Exml.java +++ b/src/org/atriasoft/exml/Exml.java @@ -13,6 +13,7 @@ import java.nio.file.Files; import java.nio.file.Path; import org.atriasoft.etk.Uri; +import org.atriasoft.exml.annotation.XmlFactory.InterfaceXmlFactoryAccess; import org.atriasoft.exml.builder.Builder; import org.atriasoft.exml.builder.BuilderGeneric; import org.atriasoft.exml.builder.BuilderIntrospection; @@ -133,6 +134,29 @@ public class Exml { } } + public static Object[] parse(final String data, final InterfaceXmlFactoryAccess widgetXmlFactory) throws ExmlException, ExmlParserErrorMulti { + Builder builder; + try { + builder = new BuilderIntrospection(widgetXmlFactory); + final ParseXml parser = new ParseXml(builder); + final ParsingProperty property = new ParsingProperty(); + property.setDisplayError(true); + + final IntrospectionObject introspectionObject = (IntrospectionObject) parser.parse(data, property); + introspectionObject.generateTheObject(); + final Object listRet = introspectionObject.getData(); + /* + if (listRet != null && listRet.getClass().isArray() && listRet.getClass().componentType() == classType) { + return (T[]) listRet; + } + */ + return (Object[]) listRet; + } catch (final ExmlException ex) { + ex.printStackTrace(); + throw ex; + } + } + /** * Load the file that might contain the xml * diff --git a/src/org/atriasoft/exml/annotation/XmlFactory.java b/src/org/atriasoft/exml/annotation/XmlFactory.java index b909183..e44183e 100644 --- a/src/org/atriasoft/exml/annotation/XmlFactory.java +++ b/src/org/atriasoft/exml/annotation/XmlFactory.java @@ -4,6 +4,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Map; /** * Marker annotation that permit to set the default parsing as attributes. @@ -26,6 +27,8 @@ public @interface XmlFactory { * @return The xml string to inject (or null). */ String generateName(Object object); + + Map> getConversionMap(); } /** diff --git a/src/org/atriasoft/exml/annotation/XmlText.java b/src/org/atriasoft/exml/annotation/XmlText.java new file mode 100644 index 0000000..11fd004 --- /dev/null +++ b/src/org/atriasoft/exml/annotation/XmlText.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 set the Xml element seen as a property. + */ +@Target({ ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ExmlAnnotation +public @interface XmlText { + /** + * Set at true to set the element used as text data as a property of the Xml node + * @return property management. + */ + boolean value() default true; +} \ No newline at end of file diff --git a/src/org/atriasoft/exml/builder/Builder.java b/src/org/atriasoft/exml/builder/Builder.java index 5c7f478..63eeec8 100644 --- a/src/org/atriasoft/exml/builder/Builder.java +++ b/src/org/atriasoft/exml/builder/Builder.java @@ -10,6 +10,21 @@ import org.atriasoft.exml.exception.ExmlException; public interface Builder { + /** + * Detect the end of the element : {@code} + * @param element Element that is finished + * @throws ExmlBuilderException + */ + void endElement(Object element) throws ExmlException; + + /** + * This is called before a text adding, in some case the subData must not be parsed, then all the subData is pack as a single string without managing the XML properties. + * @param parent Parent node of the element that is created + * @return true if the data must be packed. + * @throws ExmlException + */ + boolean isPackText(Object parent) throws ExmlException; + /** * New comment added on this Element * @param element Element representing the node of the Comment is added @@ -36,6 +51,14 @@ public interface Builder { */ Object newElement(Object parent, String nodeName) throws ExmlException; + /** + * The sub-element is finish (creation done) + * @param parent Parent node of the element that is created + * @param tmpname Name of the node + * @param element Element builder that has been created + */ + void newElementFinished(Object parent, String tmpname, Object element) throws ExmlException; + /** * Add a property on the Element. * @param element Element representing the node of the property is added @@ -60,19 +83,4 @@ public interface Builder { */ void newText(Object parent, String text) throws ExmlException; - /** - * Detect the end of the element : {@code} - * @param element Element that is finished - * @throws ExmlBuilderException - */ - void endElement(Object element) throws ExmlException; - - /** - * The sub-element is finish (creation done) - * @param parent Parent node of the element that is created - * @param tmpname Name of the node - * @param element Element builder that has been created - */ - void newElementFinished(Object parent, String tmpname, Object element) throws ExmlException; - } diff --git a/src/org/atriasoft/exml/builder/BuilderGeneric.java b/src/org/atriasoft/exml/builder/BuilderGeneric.java index f37ae3a..a540c50 100644 --- a/src/org/atriasoft/exml/builder/BuilderGeneric.java +++ b/src/org/atriasoft/exml/builder/BuilderGeneric.java @@ -6,6 +6,7 @@ package org.atriasoft.exml.builder; import org.atriasoft.exml.exception.ExmlBuilderException; +import org.atriasoft.exml.exception.ExmlException; import org.atriasoft.exml.model.XmlAttribute; import org.atriasoft.exml.model.XmlAttributeList; import org.atriasoft.exml.model.XmlComment; @@ -15,9 +16,19 @@ import org.atriasoft.exml.model.XmlText; public class BuilderGeneric implements Builder { + @Override + public void endElement(final Object element) { + // Nothing to do... + } + + @Override + public boolean isPackText(final Object parent) throws ExmlException { + return false; + } + @Override public void newComment(final Object element, final String comment) throws ExmlBuilderException { - if (element instanceof XmlElement elem) { + if (element instanceof final XmlElement elem) { elem.append(new XmlComment(comment)); return; } @@ -26,7 +37,7 @@ public class BuilderGeneric implements Builder { @Override public Object newDeclaration(final Object parent, final String text) throws ExmlBuilderException { - if (parent instanceof XmlElement elem) { + if (parent instanceof final XmlElement elem) { final XmlDeclaration dec = new XmlDeclaration(text); elem.append(dec); return dec; @@ -36,7 +47,7 @@ public class BuilderGeneric implements Builder { @Override public Object newElement(final Object parent, final String nodeName) throws ExmlBuilderException { - if (parent instanceof XmlElement elem) { + if (parent instanceof final XmlElement elem) { final XmlElement eee = new XmlElement(nodeName); elem.append(eee); return eee; @@ -44,9 +55,14 @@ public class BuilderGeneric implements Builder { throw new ExmlBuilderException("can not add Element on something else than Element"); } + @Override + public void newElementFinished(final Object parent, final String tmpname, final Object element) { + // Nothing to do... + } + @Override public void newProperty(final Object element, final String propertyName, final String propertyValue) throws ExmlBuilderException { - if (element instanceof XmlAttributeList attr) { + if (element instanceof final XmlAttributeList attr) { attr.appendAttribute(new XmlAttribute(propertyName, propertyValue)); return; } @@ -60,21 +76,11 @@ public class BuilderGeneric implements Builder { @Override public void newText(final Object parent, final String text) throws ExmlBuilderException { - if (parent instanceof XmlElement attr) { + if (parent instanceof final XmlElement attr) { attr.append(new XmlText(text)); return; } throw new ExmlBuilderException("can not add Text on something else than Element or Declaration"); } - - @Override - public void endElement(Object element) { - // Nothing to do... - } - - @Override - public void newElementFinished(Object parent, String tmpname, Object element) { - // Nothing to do... - } } diff --git a/src/org/atriasoft/exml/builder/BuilderIntrospection.java b/src/org/atriasoft/exml/builder/BuilderIntrospection.java index df02157..20864c9 100644 --- a/src/org/atriasoft/exml/builder/BuilderIntrospection.java +++ b/src/org/atriasoft/exml/builder/BuilderIntrospection.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.atriasoft.eStringSerialize.StringSerializer; +import org.atriasoft.exml.annotation.XmlFactory.InterfaceXmlFactoryAccess; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlException; @@ -15,8 +16,19 @@ public class BuilderIntrospection implements Builder { // The root class (need to keep it if we use 2 time the builder, the root class is no more accessible). final Class rootClassType; final String rootNodeName; + final InterfaceXmlFactoryAccess factory; + + public BuilderIntrospection(final InterfaceXmlFactoryAccess factory) throws ExmlException { + this.factory = factory; + this.rootNodeName = null; + this.rootClassType = null; + for (final String it : this.factory.getConversionMap().keySet()) { + this.cacheModel.findOrCreate(ModelType.NORMAL, it, this.factory.getConversionMap().get(it)); + } + } public BuilderIntrospection(final ModelType model, final Class classType, final String rootNodeName) throws ExmlException { + this.factory = null; this.rootNodeName = rootNodeName; this.rootClassType = classType; this.cacheModel.findOrCreate(model, null, classType); @@ -24,6 +36,7 @@ public class BuilderIntrospection implements Builder { @Override public void endElement(final Object element) throws ExmlBuilderException { + Log.error("End of Element: {}", element); if (element == null) { return; } @@ -36,6 +49,19 @@ public class BuilderIntrospection implements Builder { } + @Override + public boolean isPackText(final Object parent) throws ExmlException { + if (parent == null) { + return false; + } + final IntrospectionObject introspectionObject = (IntrospectionObject) parent; + final IntrospectionModel model = introspectionObject.getModelIntrospection(); + if (model.hasTextModel()) { + return true; + } + return false; + } + @Override public void newComment(final Object element, final String comment) throws ExmlException {} @@ -52,11 +78,11 @@ public class BuilderIntrospection implements Builder { } Log.verbose("new element on NodeName=" + nodeName); final IntrospectionObject introspectionObject = (IntrospectionObject) parent; - IntrospectionModel model = introspectionObject.getModelIntrospection(); + final IntrospectionModel model = introspectionObject.getModelIntrospection(); Class typeClass = null; String listTreeName = null; if (model.isArray() || model.isList()) { - List nodesAvaillable = model.getNodeAvaillable(); + final List nodesAvaillable = model.getNodeAvaillable(); if (nodesAvaillable == null || nodesAvaillable.size() == 0) { throw new ExmlBuilderException("Model can not have subNode with name: '" + nodeName + "'"); } @@ -76,7 +102,7 @@ public class BuilderIntrospection implements Builder { if (typeClass != null) { // specific case for List ==> need to get the subType in introspection ... if (typeClass.isArray()) { - Class subTypeClass = typeClass.getComponentType(); + final Class subTypeClass = typeClass.getComponentType(); Log.verbose("Create array new 'SUB' class: '" + typeClass.getCanonicalName() + "' for node '" + nodeName + "'"); IntrospectionModel inferData = null; if (listTreeName == null) { @@ -89,7 +115,7 @@ public class BuilderIntrospection implements Builder { return new IntrospectionObject(inferData); } if (List.class.isAssignableFrom(typeClass)) { - Class subTypeClass = introspectionObject.getTypeOfSubNodeSubType(nodeName); + final Class subTypeClass = introspectionObject.getTypeOfSubNodeSubType(nodeName); Log.verbose("Create List new 'SUB' class: '" + typeClass.getCanonicalName() + "' for node '" + nodeName + "'"); IntrospectionModel inferData = null; if (listTreeName == null) { @@ -124,10 +150,10 @@ public class BuilderIntrospection implements Builder { if (introspectionParentObject.getModelIntrospection() == null) { if (tmpName.equals(this.rootNodeName)) { // this is the root node ... - Object tmpp = introspectionParentObject.getData(); + final Object tmpp = introspectionParentObject.getData(); if (tmpp instanceof List) { @SuppressWarnings("unchecked") - List elementsOut = (List) tmpp; + final List elementsOut = (List) tmpp; elementsOut.add(introspectionElementObject.getData()); } } @@ -142,11 +168,11 @@ public class BuilderIntrospection implements Builder { return; } final IntrospectionObject introspectionObject = (IntrospectionObject) element; - IntrospectionModel model = introspectionObject.getModelIntrospection(); + final IntrospectionModel model = introspectionObject.getModelIntrospection(); if (model.isArray() || model.isList()) { throw new ExmlBuilderException("Model (List/Array) can not have property with name '" + propertyName + "'"); } - String beanName = model.getBeanName(propertyName); + final String beanName = model.getBeanName(propertyName); if (beanName == null) { if (model.isIgnoreUnknown()) { Log.debug("Ignore node : '" + propertyName + "'"); @@ -154,36 +180,37 @@ public class BuilderIntrospection implements Builder { } throw new ExmlAttributeDoesNotExist("The node '" + propertyName + "' Does not exist..."); } - Class typeClass = model.getTypeOfProperty(propertyName); + // T O D O: check why we do not use the been name to get the type ..... !!!!!!!!!!! + final Class typeClass = model.getTypeOfProperty(propertyName); if (typeClass != null) { // specific case for List ==> need to get the subType in introspection ... if (typeClass.isPrimitive()) { - Object out = StringSerializer.valueOf(typeClass, propertyValue); + final Object out = StringSerializer.valueOf(typeClass, propertyValue); introspectionObject.putProperty(propertyName, out); } else if (typeClass.isArray()) { - String[] elems = propertyValue.split(";"); - Class subTypeClass = typeClass.getComponentType(); - IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); - List out = new ArrayList<>(); + final String[] elems = propertyValue.split(";"); + final Class subTypeClass = typeClass.getComponentType(); + final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); + final List out = new ArrayList<>(); for (int iii = 0; iii < elems.length; iii++) { - Object tmp = inferData.getValueFromText(elems[iii]); + final Object tmp = inferData.getValueFromText(elems[iii]); out.add(tmp); } introspectionObject.putProperty(propertyName, out); } else if (List.class.isAssignableFrom(typeClass)) { - String[] elems = propertyValue.split(";"); - Class subTypeClass = introspectionObject.getTypeOfSubProperty(propertyName); + final String[] elems = propertyValue.split(";"); + final Class subTypeClass = introspectionObject.getTypeOfSubProperty(propertyName); Log.verbose("Create List new 'SUB' class: '" + typeClass.getCanonicalName() + "' for property '" + propertyName + "'"); - IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); - List out = new ArrayList<>(); + final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); + final List out = new ArrayList<>(); for (int iii = 0; iii < elems.length; iii++) { - Object tmp = inferData.getValueFromText(elems[iii]); + final Object tmp = inferData.getValueFromText(elems[iii]); out.add(tmp); } introspectionObject.putProperty(propertyName, out); } else { final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, typeClass); - Object out = inferData.getValueFromText(propertyValue); + final Object out = inferData.getValueFromText(propertyValue); introspectionObject.putProperty(propertyName, out); } } @@ -201,21 +228,73 @@ public class BuilderIntrospection implements Builder { return; } final IntrospectionObject introspectionObject = (IntrospectionObject) parent; - IntrospectionModel model = introspectionObject.getModelIntrospection(); + final IntrospectionModel model = introspectionObject.getModelIntrospection(); if (model.isArray() || model.isList()) { - List nodesAvaillable = model.getNodeAvaillable(); + final List nodesAvaillable = model.getNodeAvaillable(); if (nodesAvaillable != null && nodesAvaillable.size() != 0) { throw new ExmlBuilderException("Model can not have direct text with model data= '" + text + "'"); } - Class arrayType = model.getClassType(); + final Class arrayType = model.getClassType(); final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, arrayType); // Create the data when object is ended created... - IntrospectionObject tmpp = new IntrospectionObject(inferData); + final IntrospectionObject tmpp = new IntrospectionObject(inferData); newText(tmpp, text); - Object dataLocal = tmpp.getData(); + final Object dataLocal = tmpp.getData(); introspectionObject.addObject(IntrospectionObject.STUPID_TOCKEN, dataLocal); return; } - introspectionObject.setText(text); + if (model.hasTextModel()) { + final String beanName = model.getTextBeanName(); + if (beanName == null) { + if (model.isIgnoreUnknown()) { + Log.debug("Ignore node : **TEXT**"); + return; + } + throw new ExmlAttributeDoesNotExist("The node **TEXT** Does not exist..."); + } + final Class typeClass = model.getTypeOfText(); + if (typeClass != null) { + // specific case for List ==> need to get the subType in introspection ... + if (typeClass.isPrimitive()) { + final Object out = StringSerializer.valueOf(typeClass, text); + introspectionObject.putProperty(beanName, out); + } else if (typeClass.isArray()) { + Log.error("Not managed !!! "); + /* + final String[] elems = propertyValue.split(";"); + final Class subTypeClass = typeClass.getComponentType(); + final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); + final List out = new ArrayList<>(); + for (int iii = 0; iii < elems.length; iii++) { + final Object tmp = inferData.getValueFromText(elems[iii]); + out.add(tmp); + } + introspectionObject.putProperty(propertyName, out); + */ + } else if (List.class.isAssignableFrom(typeClass)) { + Log.error("Not managed !!! "); + /* + final String[] elems = propertyValue.split(";"); + final Class subTypeClass = introspectionObject.getTypeOfSubProperty(propertyName); + Log.verbose("Create List new 'SUB' class: '" + typeClass.getCanonicalName() + "' for property '" + propertyName + "'"); + final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, subTypeClass); + final List out = new ArrayList<>(); + for (int iii = 0; iii < elems.length; iii++) { + final Object tmp = inferData.getValueFromText(elems[iii]); + out.add(tmp); + } + introspectionObject.putProperty(propertyName, out); + */ + } else { + //Log.error("Not managed !!! "); + final IntrospectionModel inferData = this.cacheModel.findOrCreate(ModelType.NORMAL, null, typeClass); + final Object out = inferData.getValueFromText(text); + introspectionObject.putProperty(IntrospectionObject.PUBLIC_TEXT_NAME, out); + } + } + + } else { + introspectionObject.setText(text); + } } } diff --git a/src/org/atriasoft/exml/builder/IntrospectionModel.java b/src/org/atriasoft/exml/builder/IntrospectionModel.java index 7755cc9..64c8049 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionModel.java +++ b/src/org/atriasoft/exml/builder/IntrospectionModel.java @@ -5,7 +5,6 @@ import java.util.Map; 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; @@ -13,21 +12,11 @@ public abstract class IntrospectionModel { 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() { - return null; - } - public List getAttributes() { - return null; - } - public Class getClassType() { - return this.classType; - } public IntrospectionModel(final Class classType) { this.classType = classType; @@ -37,60 +26,107 @@ public abstract class IntrospectionModel { return null; } + public List getAttributes() { + return null; + } + + public String getBeanName(final String nodeName) { + return nodeName; + } + + public String getBeanNameModel(final String nodeName) { + return getBeanName(nodeName); + } + + public Class getClassType() { + return this.classType; + } + public List getNodeAvaillable() { return null; } - public Object getValueFromText(final String text) throws ExmlBuilderException { + public List getNodes() { return null; } - - public Object getValue(final String propertyName, final String propertyValue) throws ExmlBuilderException { + + public String getTextBeanName() { + // TODO Auto-generated method stub + return null; + } + + public String getTreeNameOfSubNode(final String nodeName) throws ExmlBuilderException { + return null; + } + + public Class getTypeOfProperty(final String nodeName) throws ExmlBuilderException { return null; } public Class getTypeOfSubNode(final String nodeName) throws ExmlBuilderException { return null; } + public Class getTypeOfSubNodeList(final String nodeName) throws ExmlBuilderException { return null; } - public String getTreeNameOfSubNode(final String nodeName) throws ExmlBuilderException { - return null; - } - public Class getTypeOfProperty(final String nodeName) throws ExmlBuilderException { - return null; - } + public Class getTypeOfSubProperty(final String nodeName) throws ExmlBuilderException { return null; } - public boolean isObject(final String propertyName) { - return Object.class.isAssignableFrom(this.classType); + + protected Class getTypeOfText() { + return null; } - public boolean isRecord(final String propertyName) { - return Record.class.isAssignableFrom(this.classType); + + public Object getValue(final String propertyName, final String propertyValue) throws ExmlBuilderException { + return null; } - public boolean isNative() { + + public Object getValueFromText(final String text) throws ExmlBuilderException { + return null; + } + + /** + * This permit to know if an alement in the property is able to manage the Whole text under (this remove all parsing of xml inside the model...) Annotation @XmlText + * @return true if a parameter manage the text (otherwise the text is sended to the fromString() function. + */ + public boolean hasTextModel() { return false; } + public boolean isArray() { return false; } - public boolean isList() { - return false; - } - public abstract String toString(final Object data) throws ExmlBuilderException; - public boolean isEnum() { - return this.classType.isEnum(); - } - public String getBeanName(final String nodeName) { - return nodeName; - } - protected boolean isIgnoreUnknown() { - return this.ignoreUnknown; - } + protected boolean isDefaultNullValue() { return this.defaultNullValue; } + public boolean isEnum() { + return this.classType.isEnum(); + } + + protected boolean isIgnoreUnknown() { + return this.ignoreUnknown; + } + + public boolean isList() { + return false; + } + + public boolean isNative() { + return false; + } + + public boolean isObject(final String propertyName) { + return Object.class.isAssignableFrom(this.classType); + } + + public boolean isRecord(final String propertyName) { + return Record.class.isAssignableFrom(this.classType); + } + + public abstract String toString(final Object data) throws ExmlBuilderException; + } diff --git a/src/org/atriasoft/exml/builder/IntrospectionModelArray.java b/src/org/atriasoft/exml/builder/IntrospectionModelArray.java index 0d4a243..62800b4 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionModelArray.java +++ b/src/org/atriasoft/exml/builder/IntrospectionModelArray.java @@ -8,21 +8,21 @@ import java.util.Map; import org.atriasoft.etk.util.ArraysTools; import org.atriasoft.exml.exception.ExmlBuilderException; - public class IntrospectionModelArray extends IntrospectionModel { - final String nodeName; - - public IntrospectionModelArray(final String nodeName, final Class classType) { - super(classType); - this.nodeName = nodeName; - } - @SuppressWarnings("unchecked") public static T[] convertList(final Class classType, final List data) { final List rootList = (List) data; final T[] strarr = (T[]) Array.newInstance(classType, 0); return rootList.toArray(strarr); } + + final String nodeName; + + public IntrospectionModelArray(final String nodeName, final Class classType) { + super(classType); + this.nodeName = nodeName; + } + @Override public Object createObject(final Map properties, final Map> nodes) throws ExmlBuilderException { List tmp = null; @@ -48,14 +48,19 @@ public class IntrospectionModelArray extends IntrospectionModel { return null; } + @Override + public Object getValue(final String propertyName, final String propertyValue) throws ExmlBuilderException { + return null; + } + @Override public Object getValueFromText(final String text) throws ExmlBuilderException { return new Object[0]; } - + @Override - public Object getValue(final String propertyName, final String propertyValue) throws ExmlBuilderException { - return null; + public boolean hasTextModel() { + return false; } @Override diff --git a/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java b/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java index 92f7cc8..6f4bf56 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java +++ b/src/org/atriasoft/exml/builder/IntrospectionModelComplex.java @@ -54,26 +54,27 @@ public class IntrospectionModelComplex extends IntrospectionModel { // ------------------------------------------------------------------------ // -- Parse constructor // ------------------------------------------------------------------------ - Log.verbose("Introspect class: '" + classType.getCanonicalName() + "'"); + Log.error("Introspect class: '" + classType.getCanonicalName() + "'"); final Constructor[] constructors = this.classType.getConstructors(); Log.verbose(" Constructors: (" + constructors.length + ")"); Constructor emptyConstructorTmp = null; for (final Constructor elem : constructors) { + Log.error(" Constructor ??? : {}", elem.toGenericString()); // we does not manage private field if (!Modifier.isPublic(elem.getModifiers())) { continue; } if (elem.getParameterCount() == 0) { emptyConstructorTmp = elem; - Log.verbose(" >>> " + elem.toGenericString()); + Log.error(" >>> " + elem.toGenericString()); } else { int offsetSubClass = 0; if (this.isSubClass) { offsetSubClass = 1; } - if (elem.getParameterCount() == 1) { + if (elem.getParameterCount() == 1 && offsetSubClass == 1) { emptyConstructorTmp = elem; - Log.verbose(" >>> " + elem.toGenericString()); + Log.error(" >>> " + elem.toGenericString()); } else { // Retrieve full description in constructor properties... String[] namesBeans = ReflectTools.getNames(elem, null); @@ -82,23 +83,23 @@ public class IntrospectionModelComplex extends IntrospectionModel { } else if (elem.getParameterCount() != namesBeans.length + offsetSubClass) { throw new ExmlBuilderException("Wrong number of parameter in constructor with ne number declared in the @XmlName (for bean name)"); } - Boolean[] isAttributes = new Boolean[elem.getParameterCount() - offsetSubClass]; - Boolean[] isCaseSensitives = new Boolean[elem.getParameterCount() - offsetSubClass]; - Boolean[] isOptionals = new Boolean[elem.getParameterCount() - offsetSubClass]; - Boolean[] isManageds = new Boolean[elem.getParameterCount() - offsetSubClass]; + final Boolean[] isAttributes = new Boolean[elem.getParameterCount() - offsetSubClass]; + final Boolean[] isCaseSensitives = new Boolean[elem.getParameterCount() - offsetSubClass]; + final Boolean[] isOptionals = new Boolean[elem.getParameterCount() - offsetSubClass]; + final Boolean[] isManageds = new Boolean[elem.getParameterCount() - offsetSubClass]; - Class[][] clazz = new Class[elem.getParameterCount() - offsetSubClass][]; - String[][] names = new String[elem.getParameterCount() - offsetSubClass][]; + final Class[][] clazz = new Class[elem.getParameterCount() - offsetSubClass][]; + final String[][] names = new String[elem.getParameterCount() - offsetSubClass][]; - Parameter[] params = elem.getParameters(); + final Parameter[] params = elem.getParameters(); for (int iii = offsetSubClass; iii < params.length; iii++) { - Parameter paramElem = params[iii]; + final Parameter paramElem = params[iii]; isAttributes[iii - offsetSubClass] = ReflectTools.getIsAttribute(elem, paramElem, null); isCaseSensitives[iii - offsetSubClass] = ReflectTools.getIsCaseSensitive(elem, paramElem, null); isOptionals[iii - offsetSubClass] = ReflectTools.getIsOptional(elem, paramElem, null); isManageds[iii - offsetSubClass] = ReflectTools.getIsManaged(elem, paramElem, null); - String[] namesParam = ReflectTools.getNames(elem, paramElem, null); - Class[] types = ReflectTools.getTypeParameterfunction(elem, iii); + final String[] namesParam = ReflectTools.getNames(elem, paramElem, null); + final Class[] types = ReflectTools.getTypeParameterfunction(elem, iii); clazz[iii - offsetSubClass] = types; names[iii - offsetSubClass] = namesParam; if (namesParam != null && namesParam.length != 0) { @@ -117,15 +118,15 @@ public class IntrospectionModelComplex extends IntrospectionModel { prop = new IntrospectionProperty(namesBeans[iii], clazz[iii], names[iii]); this.elements.add(prop); } else { - Class curentType = prop.getType(); - Class curentSubType = prop.getSubType(); + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); if (curentType != clazz[iii][0] && curentSubType != clazz[iii][1]) { throw new ExmlBuilderException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + " ==> new=" + clazz[iii][0] + " / " + clazz[iii][1] + " in " + elem.toGenericString()); } - String[] names1 = names[iii]; + final String[] names1 = names[iii]; if (names1 != null) { - String[] curentValue = prop.getNames(); + final String[] curentValue = prop.getNames(); if (curentValue != null) { // TODO maybe set the value permissive if no change !!! like the others... throw new ExmlBuilderException("Set 'names' with a (already set!) " + elem.toGenericString()); @@ -137,7 +138,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { prop.setCanBeSetByConstructor(true); final Boolean isAttribute = isAttributes[iii]; if (isAttribute != null) { - Boolean curentValue = prop.isAttribute(); + final Boolean curentValue = prop.isAttribute(); if (curentValue != null && curentValue != isAttribute) { throw new ExmlBuilderException("Set 'attribute' with a different value previous=" + curentValue + " ==> new=" + isAttribute + " in " + elem.toGenericString()); } @@ -145,7 +146,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isCaseSensitive = isCaseSensitives[iii]; if (isCaseSensitive != null) { - Boolean curentValue = prop.isCaseSensitive(); + final Boolean curentValue = prop.isCaseSensitive(); if (curentValue != null && curentValue != isCaseSensitive) { throw new ExmlBuilderException( "Set 'caseSensitive' with a different value previous=" + curentValue + " ==> new=" + isCaseSensitive + " in " + elem.toGenericString()); @@ -154,7 +155,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isOptional = isOptionals[iii]; if (isOptional != null) { - Boolean curentValue = prop.isOptionnal(); + final Boolean curentValue = prop.isOptionnal(); if (curentValue != null && curentValue != isOptional) { throw new ExmlBuilderException("Set 'optionnal' with a different value previous=" + curentValue + " ==> new=" + isOptional + " in " + elem.toGenericString()); } @@ -162,7 +163,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isManaged = isManageds[iii]; if (isManaged != null) { - Boolean curentValue = prop.isManaged(); + final Boolean curentValue = prop.isManaged(); if (curentValue != null && curentValue != isManaged) { throw new ExmlBuilderException("Set 'managed' with a different value previous=" + curentValue + " ==> new=" + isManaged + " in " + elem.toGenericString()); } @@ -175,12 +176,12 @@ public class IntrospectionModelComplex extends IntrospectionModel { } } this.constructorEmpty = emptyConstructorTmp; - + Log.error(" ==> constructor = {}", this.constructorEmpty); // Order the constructor from the bigger number of element to the lowest... Collections.sort(this.constructors, (a, b) -> a.values().length - b.values().length); - for (ConstructorModel elem : this.constructors) { + for (final ConstructorModel elem : this.constructors) { Log.verbose(" * " + elem.constructor().toGenericString()); - StringBuilder tmpPrint = new StringBuilder(" ==> ("); + final StringBuilder tmpPrint = new StringBuilder(" ==> ("); if (this.isSubClass) { tmpPrint.append("null, "); } @@ -196,9 +197,9 @@ public class IntrospectionModelComplex extends IntrospectionModel { final List recordAllPossibleValues = new ArrayList<>(); final List recordAllPossibleValuesFiltered = new ArrayList<>(); if (this.isRecord) { - for (ConstructorModel elem : this.constructors) { + for (final ConstructorModel elem : this.constructors) { for (int iii = 0; iii < elem.values().length; iii++) { - String tmpp = elem.values()[iii]; + final String tmpp = elem.values()[iii]; if (!recordAllPossibleValues.contains(tmpp)) { recordAllPossibleValues.add(tmpp); } @@ -221,22 +222,22 @@ public class IntrospectionModelComplex extends IntrospectionModel { continue; } final String[] names = ReflectTools.getNames(elem, null); - Class[] types = ReflectTools.getTypeField(elem); + final Class[] types = ReflectTools.getTypeField(elem); IntrospectionProperty prop = findElement(elem.getName()); if (prop == null) { prop = new IntrospectionProperty(elem.getName(), types, names); this.elements.add(prop); } else { - Class curentType = prop.getType(); - Class curentSubType = prop.getSubType(); + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); if (curentType != types[0] && curentSubType != types[1]) { throw new ExmlBuilderException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + " ==> new=" + types[0] + " / " + types[1] + " in " + elem.toGenericString()); } - String[] names1 = names; + final String[] names1 = names; if (names1 != null) { - String[] curentValue = prop.getNames(); + final String[] curentValue = prop.getNames(); if (curentValue != null) { // TODO maybe set the value permissive if no change !!! like the others... throw new ExmlBuilderException("Set 'names' with a (already set!) " + elem.toGenericString()); @@ -265,9 +266,17 @@ public class IntrospectionModelComplex extends IntrospectionModel { if (isCaseSensitive != null) { prop.setCaseSensitive(isCaseSensitive); } + final Boolean isText = ReflectTools.getIsText(elem, null); + if (isText != null) { + prop.setTextMode(isText); + } + final Class factory = ReflectTools.getFactory(elem); + if (factory != null) { + prop.setFactory(factory); + } // generate getter and setter with field. - IntrospectionPropertyField modifier = new IntrospectionPropertyField(elem); + final IntrospectionPropertyField modifier = new IntrospectionPropertyField(elem); // if the field is final ==> we can not set the value... if (Modifier.isFinal(elem.getModifiers())) { prop.setGetter(modifier); @@ -397,24 +406,24 @@ public class IntrospectionModelComplex extends IntrospectionModel { // associate methods by pair. for (final Method method : methodsGet) { final String name = Tools.decapitalizeFirst(this.isRecord ? method.getName() : method.getName().substring(3)); - IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); // generate getter and setter with field. - IntrospectionPropertyMethodGetter modifier = new IntrospectionPropertyMethodGetter(method); + final IntrospectionPropertyMethodGetter modifier = new IntrospectionPropertyMethodGetter(method); prop.setGetter(modifier); } if (!this.isRecord) { for (final Method method : methodsIs) { final String name = Tools.decapitalizeFirst(method.getName().substring(2)); - IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); // generate getter and setter with field. - IntrospectionPropertyMethodGetter modifier = new IntrospectionPropertyMethodGetter(method); + final IntrospectionPropertyMethodGetter modifier = new IntrospectionPropertyMethodGetter(method); prop.setGetter(modifier); } for (final Method method : methodsSet) { final String name = Tools.decapitalizeFirst(method.getName().substring(3)); - IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeParameterfunction(method)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeParameterfunction(method)); // generate getter and setter with field. - IntrospectionPropertyMethodSetter modifier = new IntrospectionPropertyMethodSetter(method); + final IntrospectionPropertyMethodSetter modifier = new IntrospectionPropertyMethodSetter(method); prop.setSetter(modifier); } } @@ -424,37 +433,37 @@ public class IntrospectionModelComplex extends IntrospectionModel { // Set only at the end ==> no need before... final Boolean isDefaultAttribute = ReflectTools.getIsDefaultAttribute(classType, IntrospectionModel.DEFAULT_ATTRIBUTE); - for (IntrospectionProperty prop : this.elements) { + for (final IntrospectionProperty prop : this.elements) { if (prop.isAttribute() == null) { prop.setAttribute(isDefaultAttribute); } } final Boolean isDefaultManaged = ReflectTools.getIsDefaultManaged(classType, IntrospectionModel.DEFAULT_MANAGED); - for (IntrospectionProperty prop : this.elements) { + for (final IntrospectionProperty prop : this.elements) { if (prop.isManaged() == null) { prop.setManaged(isDefaultManaged); } } final Boolean isDefaultOptional = ReflectTools.getIsDefaultOptional(classType, IntrospectionModel.DEFAULT_OPTIONAL); - for (IntrospectionProperty prop : this.elements) { + for (final IntrospectionProperty prop : this.elements) { if (prop.isOptionnal() == null) { prop.setOptionnal(isDefaultOptional); } } final Boolean isDefaultCaseSensitive = ReflectTools.getIsDefaultCaseSensitive(classType, IntrospectionModel.DEFAULT_CASE_SENSITIVE); - for (IntrospectionProperty prop : this.elements) { + for (final IntrospectionProperty prop : this.elements) { if (prop.isCaseSensitive() == null) { prop.setCaseSensitive(isDefaultCaseSensitive); } } // set default name in the list: - for (IntrospectionProperty prop : this.elements) { + for (final IntrospectionProperty prop : this.elements) { if (prop.getNames() == null) { prop.setNames(new String[] { prop.getBeanName() }); } } - } catch (Exception ex) { + } catch (final Exception ex) { ex.printStackTrace(); throw new ExmlBuilderException("Error in creating introspection data ... " + ex.getMessage()); } @@ -462,21 +471,22 @@ public class IntrospectionModelComplex extends IntrospectionModel { // Sort the parameters to generate all time the same XML.. Collections.sort(this.elements, (a, b) -> a.getNames()[0].compareTo(b.getNames()[0])); - for (IntrospectionProperty prop : this.elements) { - Log.debug("Property/node : " + prop.getBeanName()); - Log.debug(" names: " + Arrays.toString(prop.getNames())); - Log.debug(" list: " + prop.getListName()); - Log.debug(" managed: " + prop.isManaged()); - Log.debug(" attribute: " + prop.isAttribute()); - Log.debug(" case-sensitive: " + prop.isCaseSensitive()); - Log.debug(" optionnal: " + prop.isOptionnal()); - Log.debug(" constructor: " + prop.isCanBeSetByConstructor()); - Log.debug(" get/set: " + prop.canGetValue() + " / " + prop.canSetValue()); - Log.debug(" type: " + prop.getType().getCanonicalName()); + for (final IntrospectionProperty prop : this.elements) { + Log.verbose("Property/node : " + prop.getBeanName()); + Log.verbose(" names: " + Arrays.toString(prop.getNames())); + Log.verbose(" list: " + prop.getListName()); + Log.verbose(" managed: " + prop.isManaged()); + Log.verbose(" attribute: " + prop.isAttribute()); + Log.verbose(" text: " + prop.isText()); + Log.verbose(" case-sensitive: " + prop.isCaseSensitive()); + Log.verbose(" optionnal: " + prop.isOptionnal()); + Log.verbose(" constructor: " + prop.isCanBeSetByConstructor()); + Log.verbose(" get/set: " + prop.canGetValue() + " / " + prop.canSetValue()); + Log.verbose(" type: " + prop.getType().getCanonicalName()); if (prop.getSubType() != null) { - Log.debug(" sub-type: " + prop.getSubType().getCanonicalName()); + Log.verbose(" sub-type: " + prop.getSubType().getCanonicalName()); } else { - Log.debug(" sub-type: null"); + Log.verbose(" sub-type: null"); } } @@ -484,7 +494,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { @SuppressWarnings("unchecked") private T[] autoCast(final Class clazz, final List data) { - T[] out = (T[]) java.lang.reflect.Array.newInstance(clazz, data.size());// T[data.size()]; + final T[] out = (T[]) java.lang.reflect.Array.newInstance(clazz, data.size());// T[data.size()]; for (int iii = 0; iii < data.size(); iii++) { out[iii] = (T) data.get(iii); @@ -534,15 +544,15 @@ public class IntrospectionModelComplex extends IntrospectionModel { 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[] inputsTmp = new Object[elem.values().length + offset]; + for (final ConstructorModel elem : this.constructors) { + final int offset = this.isSubClass ? 1 : 0; + final Object[] inputsTmp = new Object[elem.values().length + offset]; inputsTmp[0] = null; int empty = 0; for (int iii = 0; iii < elem.values().length; iii++) { Object valueToInject = properties.get(elem.values()[iii]); if (valueToInject == null) { - List tmppp = nodes.get(elem.values()[iii]); + final List tmppp = nodes.get(elem.values()[iii]); if (tmppp != null) { if (List.class == findBeanNodeDescription(elem.values()[iii]).getType()) { valueToInject = tmppp; @@ -566,7 +576,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } if (betterModel != null) { if (isDefaultNullValue() || lastEmpty == 0) { - ConstructorModel elem = betterModel; + final ConstructorModel elem = betterModel; if (inputs != null) { // here we find our constructor... try { @@ -625,23 +635,24 @@ public class IntrospectionModelComplex extends IntrospectionModel { throw new ExmlBuilderException("No constructor accessible for class: " + this.classType.getCanonicalName()); } try { + Log.error("create class : {} with subClass={}", this.classType.getCanonicalName(), this.isSubClass); + Log.error(" ==> constructor = {}", this.constructorEmpty); if (this.isSubClass) { - Object tmp2 = null; + final Object tmp2 = null; tmp = this.constructorEmpty.newInstance(tmp2); } else { tmp = this.constructorEmpty.newInstance(); } } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) { - // TODO Auto-generated catch block e.printStackTrace(); return null; } } // STEP 3: set the rest if the parameters... - for (Entry elem : properties.entrySet()) { + for (final Entry elem : properties.entrySet()) { setValue(tmp, elem.getKey(), elem.getValue()); } - for (Entry> elem : nodes.entrySet()) { + for (final Entry> elem : nodes.entrySet()) { setValue(tmp, elem.getKey(), elem.getValue()); } return tmp; @@ -672,7 +683,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } protected IntrospectionProperty findElement(final String beanName) { - for (IntrospectionProperty elem : this.elements) { + for (final IntrospectionProperty elem : this.elements) { if (elem.getBeanName().equals(beanName)) { return elem; } @@ -706,8 +717,8 @@ public class IntrospectionModelComplex extends IntrospectionModel { @Override public List getAttributes() { - List out = new ArrayList<>(); - for (IntrospectionProperty elem : this.elements) { + final List out = new ArrayList<>(); + for (final IntrospectionProperty elem : this.elements) { if (elem.isAttribute()) { out.add(elem); } @@ -717,7 +728,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { @Override public String getBeanName(final String nodeName) { - for (IntrospectionProperty elem : this.elements) { + for (final IntrospectionProperty elem : this.elements) { if (elem.isCompatible(nodeName)) { return elem.getBeanName(); } @@ -725,9 +736,22 @@ public class IntrospectionModelComplex extends IntrospectionModel { return null; } + @Override + public String getBeanNameModel(final String nodeName) { + for (final IntrospectionProperty elem : this.elements) { + if (elem.isCompatible(nodeName)) { + if (elem.hasFactory()) { + return elem.getBeanName() + "#" + nodeName; + } + return elem.getBeanName(); + } + } + return null; + } + @Override public List getNodeAvaillable() { - List out = new ArrayList<>(); + final List out = new ArrayList<>(); for (final IntrospectionProperty prop : this.elements) { if (prop.isAttribute()) { continue; @@ -739,8 +763,8 @@ public class IntrospectionModelComplex extends IntrospectionModel { @Override public List getNodes() { - List out = new ArrayList<>(); - for (IntrospectionProperty elem : this.elements) { + final List out = new ArrayList<>(); + for (final IntrospectionProperty elem : this.elements) { if (!elem.isAttribute()) { out.add(elem); } @@ -748,6 +772,17 @@ public class IntrospectionModelComplex extends IntrospectionModel { return out; } + @Override + public String getTextBeanName() { + for (final IntrospectionProperty prop : this.elements) { + final Boolean isText = prop.isText(); + if (isText != null && isText) { + return prop.getBeanName(); + } + } + return null; + } + @Override public String getTreeNameOfSubNode(final String nodeBeanName) throws ExmlBuilderException { Log.debug(" nodeType='" + nodeBeanName + "'"); @@ -768,7 +803,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { return propField.getType(); } - throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeName + "' available: " + getNodeAvaillable()); } /** @@ -777,25 +812,37 @@ public class IntrospectionModelComplex extends IntrospectionModel { * @return Class of the node to create */ @Override - public Class getTypeOfSubNode(final String nodeBeanName) throws ExmlBuilderException { + public Class getTypeOfSubNode(final String nodeBeanNames) throws ExmlBuilderException { + final String[] elemstNames = nodeBeanNames.split("#"); + final String nodeBeanName = elemstNames[0]; Log.debug(" nodeType='" + nodeBeanName + "'"); final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName); if (propMethode != null && propMethode.canSetValue()) { Log.debug(" ==> find '" + propMethode.getNames()); - return propMethode.getType(); + if (propMethode.hasFactory()) { + return propMethode.getCompatible(elemstNames[1]); + } else { + return propMethode.getType(); + } } - throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' available: " + getNodeAvaillable()); } @Override - public Class getTypeOfSubNodeList(final String nodeBeanName) throws ExmlBuilderException { + public Class getTypeOfSubNodeList(final String nodeBeanNames) throws ExmlBuilderException { + final String[] elemstNames = nodeBeanNames.split("#"); + final String nodeBeanName = elemstNames[0]; Log.debug(" nodeType='" + nodeBeanName + "'"); final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName); if (propMethode != null && propMethode.canSetValue()) { Log.debug(" ==> find '" + propMethode.getNames()); - return propMethode.getSubType(); + if (propMethode.hasFactory()) { + return propMethode.getCompatible(elemstNames[1]); + } else { + return propMethode.getSubType(); + } } - throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeBeanName + "' available: " + getNodeAvaillable()); } @Override @@ -806,7 +853,18 @@ public class IntrospectionModelComplex extends IntrospectionModel { Log.debug(" ==> find '" + propField.getNames()); return propField.getSubType(); } - throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable()); + throw new ExmlBuilderException("can not find the field '" + nodeName + "' available: " + getNodeAvaillable()); + } + + @Override + public Class getTypeOfText() { + for (final IntrospectionProperty prop : this.elements) { + final Boolean isText = prop.isText(); + if (isText != null && isText) { + return prop.getType(); + } + } + return null; } @Override @@ -878,6 +936,19 @@ public class IntrospectionModelComplex extends IntrospectionModel { } } + @Override + public boolean hasTextModel() { + Log.warning("in {}", this.classType.getCanonicalName()); + for (final IntrospectionProperty prop : this.elements) { + Log.warning(" check {}, manage={} cas setValue={} isText={}", prop.getBeanName(), prop.isManaged(), prop.canSetValue(), prop.isText()); + final Boolean isText = prop.isText(); + if (isText != null && isText) { + return true; + } + } + return false; + } + @Override public boolean isNative() { return false; @@ -894,10 +965,10 @@ public class IntrospectionModelComplex extends IntrospectionModel { propMethode.setExistingValue(data, value); } else if (value instanceof List) { @SuppressWarnings("unchecked") - List tmpp = (List) value; + final List tmpp = (List) value; if (propMethode.getType().isArray()) { if (propMethode.getType().componentType().isPrimitive()) { - Object newData = ArraysTools.listToPrimitiveAuto(propMethode.getType().componentType(), tmpp); + final Object newData = ArraysTools.listToPrimitiveAuto(propMethode.getType().componentType(), tmpp); propMethode.setExistingValue(data, newData); } else { Log.verbose(" datas type: " + autoCast(propMethode.getType().componentType(), tmpp).getClass().getCanonicalName()); @@ -910,25 +981,25 @@ public class IntrospectionModelComplex extends IntrospectionModel { // impossible case ... } } else if (propMethode.getType() == byte.class) { - byte dataPrimitive = (Byte) value; + final byte dataPrimitive = (Byte) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == short.class) { - short dataPrimitive = (Short) value; + final short dataPrimitive = (Short) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == int.class) { - int dataPrimitive = (Integer) value; + final int dataPrimitive = (Integer) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == long.class) { - long dataPrimitive = (Long) value; + final long dataPrimitive = (Long) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == boolean.class) { - boolean dataPrimitive = (Boolean) value; + final boolean dataPrimitive = (Boolean) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == float.class) { - float dataPrimitive = (Float) value; + final float dataPrimitive = (Float) value; propMethode.setExistingValue(data, dataPrimitive); } else if (propMethode.getType() == double.class) { - double dataPrimitive = (Double) value; + final double dataPrimitive = (Double) value; propMethode.setExistingValue(data, dataPrimitive); } else { @@ -945,50 +1016,50 @@ public class IntrospectionModelComplex extends IntrospectionModel { propField.setExistingValue(data, value); // Some specific case for primitives values } else if (propField.getType() == byte.class) { - byte dataPrimitive = (Byte) value; + final byte dataPrimitive = (Byte) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == short.class) { - short dataPrimitive = (Short) value; + final short dataPrimitive = (Short) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == int.class) { - int dataPrimitive = (Integer) value; + final int dataPrimitive = (Integer) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == long.class) { - long dataPrimitive = (Long) value; + final long dataPrimitive = (Long) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == float.class) { - float dataPrimitive = (Float) value; + final float dataPrimitive = (Float) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == double.class) { - Double dataPrimitive = (Double) value; + final Double dataPrimitive = (Double) value; propField.setExistingValue(data, dataPrimitive); } else if (propField.getType() == boolean.class) { - boolean dataPrimitive = (Boolean) value; + final boolean dataPrimitive = (Boolean) value; propField.setExistingValue(data, dataPrimitive); } else { @SuppressWarnings("unchecked") - List tmpp = (List) value; + final List tmpp = (List) value; if (propField.getType().isArray()) { if (propField.getType().componentType() == byte.class) { - byte[] datas = ArraysTools.listByteToPrimitive(tmpp); + final byte[] datas = ArraysTools.listByteToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == short.class) { - short[] datas = ArraysTools.listShortToPrimitive(tmpp); + final short[] datas = ArraysTools.listShortToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == int.class) { - int[] datas = ArraysTools.listIntegerToPrimitive(tmpp); + final int[] datas = ArraysTools.listIntegerToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == long.class) { - long[] datas = ArraysTools.listLongToPrimitive(tmpp); + final long[] datas = ArraysTools.listLongToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == boolean.class) { - boolean[] datas = ArraysTools.listBooleanToPrimitive(tmpp); + final boolean[] datas = ArraysTools.listBooleanToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == float.class) { - float[] datas = ArraysTools.listFloatToPrimitive(tmpp); + final float[] datas = ArraysTools.listFloatToPrimitive(tmpp); propField.setExistingValue(data, datas); } else if (propField.getType().componentType() == double.class) { - double[] datas = ArraysTools.listDoubleToPrimitive(tmpp); + final double[] datas = ArraysTools.listDoubleToPrimitive(tmpp); propField.setExistingValue(data, datas); } else { Log.verbose(" datas type: " + autoCast(propField.getType().componentType(), tmpp).getClass().getCanonicalName()); @@ -1030,19 +1101,19 @@ public class IntrospectionModelComplex extends IntrospectionModel { private IntrospectionProperty updateForMethod(final String name, final Method method, final Class[] types) throws Exception { IntrospectionProperty prop = findElement(name); if (prop == null) { - String[] names = ReflectTools.getNames(method, null); + final String[] names = ReflectTools.getNames(method, null); prop = new IntrospectionProperty(name, types, names); this.elements.add(prop); } else { - Class curentType = prop.getType(); - Class curentSubType = prop.getSubType(); + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); if (curentType != types[0] && curentSubType != types[1]) { throw new ExmlBuilderException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + " ==> new=" + types[0] + " / " + types[1] + " in " + method.toGenericString()); } - String[] names = ReflectTools.getNames(method, null); + final String[] names = ReflectTools.getNames(method, null); if (names != null) { - String[] curentValue = prop.getNames(); + final String[] curentValue = prop.getNames(); if (curentValue != null && !checkIdenticalArray(curentValue, names)) { // TODO maybe set the value permissive if no change !!! like the others... throw new ExmlBuilderException("Set 'names' with a (already set!) " + method.toGenericString()); @@ -1052,7 +1123,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final String listName = ReflectTools.getListName(method, null); if (listName != null) { - String curentValue = prop.getListName(); + final String curentValue = prop.getListName(); if (curentValue != null && curentValue != listName) { throw new ExmlBuilderException("Set 'listNAme' with a different value previous=" + curentValue + " ==> new=" + listName + " in " + method.toGenericString()); } @@ -1060,7 +1131,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isAttribute = ReflectTools.getIsAttribute(method, null); if (isAttribute != null) { - Boolean curentValue = prop.isAttribute(); + final Boolean curentValue = prop.isAttribute(); if (curentValue != null && curentValue != isAttribute) { throw new ExmlBuilderException("Set 'attribute' with a different value previous=" + curentValue + " ==> new=" + isAttribute + " in " + method.toGenericString()); } @@ -1068,7 +1139,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isManaged = ReflectTools.getIsManaged(method, null); if (isManaged != null) { - Boolean curentValue = prop.isManaged(); + final Boolean curentValue = prop.isManaged(); if (curentValue != null && curentValue != isManaged) { throw new ExmlBuilderException("Set 'managed' with a different value previous=" + curentValue + " ==> new=" + isManaged + " in " + method.toGenericString()); } @@ -1076,7 +1147,7 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isOptionnal = ReflectTools.getIsOptional(method, null); if (isOptionnal != null) { - Boolean curentValue = prop.isOptionnal(); + final Boolean curentValue = prop.isOptionnal(); if (curentValue != null && curentValue != isOptionnal) { throw new ExmlBuilderException("Set 'optionnal' with a different value previous=" + curentValue + " ==> new=" + isOptionnal + " in " + method.toGenericString()); } @@ -1084,12 +1155,24 @@ public class IntrospectionModelComplex extends IntrospectionModel { } final Boolean isCaseSensitive = ReflectTools.getIsCaseSensitive(method, null); if (isCaseSensitive != null) { - Boolean curentValue = prop.isCaseSensitive(); + final Boolean curentValue = prop.isCaseSensitive(); if (curentValue != null && curentValue != isCaseSensitive) { throw new ExmlBuilderException("Set 'case sensitive' with a different value previous=" + curentValue + " ==> new=" + isCaseSensitive + " in " + method.toGenericString()); } prop.setCaseSensitive(isCaseSensitive); } + final Boolean isText = ReflectTools.getIsText(method, null); + if (isText != null) { + final Boolean curentValue = prop.isText(); + if (curentValue != null && curentValue != isText) { + throw new ExmlBuilderException("Set 'case sensitive' with a different value previous=" + curentValue + " ==> new=" + isText + " in " + method.toGenericString()); + } + prop.setTextMode(isText); + } + final Class factory = ReflectTools.getFactory(method); + if (factory != null) { + prop.setFactory(factory); + } return prop; } } diff --git a/src/org/atriasoft/exml/builder/IntrospectionObject.java b/src/org/atriasoft/exml/builder/IntrospectionObject.java index 75e3620..9a069ae 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionObject.java +++ b/src/org/atriasoft/exml/builder/IntrospectionObject.java @@ -11,6 +11,7 @@ import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.atriasoft.exml.internal.Log; public class IntrospectionObject { + public static final String PUBLIC_TEXT_NAME = "##<< ** TEXT-ZONE ** >>##"; public static final String STUPID_TOCKEN = "___aé\"'__-è==**ù!^$:;,;AZEARTYUIOPMLKJHGFDSQW>XCVBN?"; // can not exist .... private final IntrospectionModel modelInterface; private Object data = null; @@ -63,7 +64,7 @@ public class IntrospectionObject { Log.error("this is a big problem ..."); Log.error("this is a big problem ..."); } else if (List.class.isAssignableFrom(value.getClass())) { - List nodeIn = (List) value; + final List nodeIn = (List) value; node.addAll(nodeIn); } else { node.add(value); @@ -112,7 +113,8 @@ public class IntrospectionObject { * @return Class of the node to create */ public Class getTypeOfSubNode(final String nodeName) throws ExmlException { - final String beanName = this.modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanNameModel(nodeName); + if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } @@ -120,7 +122,7 @@ public class IntrospectionObject { } public Class getTypeOfSubNodeSubType(final String nodeName) throws ExmlException { - final String beanName = this.modelInterface.getBeanName(nodeName); + final String beanName = this.modelInterface.getBeanNameModel(nodeName); if (beanName == null) { throw new ExmlNodeDoesNotExist("The node '" + nodeName + "' Does not exist..."); } @@ -144,7 +146,12 @@ public class IntrospectionObject { } public void putProperty(final String propertyName, final Object propertyValue) throws ExmlException { - final String beanName = this.modelInterface.getBeanName(propertyName); + String beanName = null; + if (propertyName == PUBLIC_TEXT_NAME) { + beanName = this.modelInterface.getTextBeanName(); + } else { + 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"); } diff --git a/src/org/atriasoft/exml/builder/IntrospectionProperty.java b/src/org/atriasoft/exml/builder/IntrospectionProperty.java index 00281fd..b05643e 100644 --- a/src/org/atriasoft/exml/builder/IntrospectionProperty.java +++ b/src/org/atriasoft/exml/builder/IntrospectionProperty.java @@ -1,10 +1,13 @@ package org.atriasoft.exml.builder; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; import org.atriasoft.eStringSerialize.StringSerializer; +import org.atriasoft.exml.annotation.XmlFactory.InterfaceXmlFactoryAccess; import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.internal.Log; @@ -17,6 +20,7 @@ public final class IntrospectionProperty { private Boolean optionnal = null; // Attribute or Node (set at null while not define by a function attribute, parameter ...) private Boolean attribute = null; + private Boolean textMode = null; // name of the field or the function before renaming... private final String beanName; // names that can take the Node or the aatibute @@ -24,14 +28,14 @@ public final class IntrospectionProperty { // if organized in sublist (!= null) then the subNode have this value private String listName = null; private boolean canBeSetByConstructor = false; + private Class factory = null; + private InterfaceXmlFactoryAccess factoryCreated = null; private final Class type; private final Class subType; - // can get the property, if null not gettable ... ==> TODO need to remove this property ??? // First function call // second field access IntrospectionPropertyGetter getter = null; - // can get the property, if null not settable (otherwise use the constructor???) // First constructor call // second function call @@ -71,12 +75,12 @@ public final class IntrospectionProperty { } else if ((this.type != List.class) || !StringSerializer.contains(this.subType)) { throw new ExmlBuilderException("Can not parse the specific element ... need to introspect and find the 'xxx valueOf(String data);'"); } - ArrayList out = new ArrayList<>(); - for (String elem : value.split(";")) { + final ArrayList out = new ArrayList<>(); + for (final String elem : value.split(";")) { out.add(StringSerializer.valueOf(this.subType, elem)); } return out; - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new ExmlBuilderException("Error in parsing the property value ... " + e.getMessage()); @@ -87,6 +91,58 @@ public final class IntrospectionProperty { return this.beanName; } + public Class getCompatible(final String name) { + final InterfaceXmlFactoryAccess factoryGenerator = getFactory(); + if (factoryGenerator != null) { + if (this.caseSensitive) { + for (final Entry> elem : factoryGenerator.getConversionMap().entrySet()) { + if (elem.getKey().contentEquals(name)) { + return elem.getValue(); + } + } + } else { + for (final Entry> elem : factoryGenerator.getConversionMap().entrySet()) { + if (elem.getKey().equalsIgnoreCase(name)) { + return elem.getValue(); + } + } + } + return null; + } + if (this.caseSensitive) { + for (final String elem : this.names) { + if (elem.contentEquals(name)) { + Log.verbose(" - '{}' ==> true", elem); + return getType(); + } + Log.verbose(" - '{}' == false", elem); + } + } else { + for (final String elem : this.names) { + if (elem.equalsIgnoreCase(name)) { + Log.verbose(" - '{}' ==> true", elem); + return getType(); + } + Log.verbose(" - '{}' == false", elem); + } + } + return null; + } + + public InterfaceXmlFactoryAccess getFactory() { + if (this.factoryCreated == null && this.factory != null) { + try { + this.factoryCreated = (InterfaceXmlFactoryAccess) this.factory.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + this.factoryCreated = null; + this.factory = null; + } + } + return this.factoryCreated; + } + public String getListName() { return this.listName; } @@ -128,6 +184,10 @@ public final class IntrospectionProperty { throw new ExmlBuilderException("Property: " + this.names + " have no getter"); } + public boolean hasFactory() { + return this.factory != null; + } + public Boolean isAttribute() { return this.attribute; } @@ -146,22 +206,44 @@ public final class IntrospectionProperty { * @return true if the element is compatible, false otherwise */ public boolean isCompatible(final String name) { - Log.verbose("Check compatible : '" + name + "' in " + Arrays.toString(this.names)); + Log.verbose("Check compatible : '{}' in {}", name, Arrays.toString(this.names)); + final InterfaceXmlFactoryAccess factoryGenerator = getFactory(); + if (factoryGenerator != null) { + Log.verbose(" ===> Detect factory !!!!"); + if (this.caseSensitive) { + for (final Entry> elem : factoryGenerator.getConversionMap().entrySet()) { + if (elem.getKey().contentEquals(name)) { + Log.verbose(" + '{}' ==> true", elem.getKey()); + return true; + } + Log.verbose(" + '{}' == false", elem.getKey()); + } + } else { + for (final Entry> elem : factoryGenerator.getConversionMap().entrySet()) { + if (elem.getKey().equalsIgnoreCase(name)) { + Log.verbose(" + '{}' ==> true", elem.getKey()); + return true; + } + Log.verbose(" + '{}' == false", elem.getKey()); + } + } + return false; + } if (this.caseSensitive) { for (final String elem : this.names) { if (elem.contentEquals(name)) { - Log.verbose(" - '" + elem + "' ==> true"); + Log.verbose(" - '{}' ==> true", elem); return true; } - Log.verbose(" - '" + elem + "' == false"); + Log.verbose(" - '{}' == false", elem); } } else { for (final String elem : this.names) { if (elem.equalsIgnoreCase(name)) { - Log.verbose(" - '" + elem + "' ==> true"); + Log.verbose(" - '{}' ==> true", elem); return true; } - Log.verbose(" - '" + elem + "' == false"); + Log.verbose(" - '{}' == false", elem); } } return false; @@ -175,6 +257,10 @@ public final class IntrospectionProperty { return this.optionnal; } + public Boolean isText() { + return this.textMode; + } + public void setAttribute(final Boolean attribute) { this.attribute = attribute; } @@ -201,6 +287,10 @@ public final class IntrospectionProperty { throw new ExmlBuilderException("Property: " + this.names + " have no setter"); } + public void setFactory(final Class factory) { + this.factory = factory; + } + public void setGetter(final IntrospectionPropertyGetter getter) { this.getter = getter; } @@ -225,4 +315,8 @@ public final class IntrospectionProperty { this.setter = setter; } + public void setTextMode(final Boolean isText) { + this.textMode = isText; + } + } \ No newline at end of file diff --git a/src/org/atriasoft/exml/parser/ParseXml.java b/src/org/atriasoft/exml/parser/ParseXml.java index cd04d46..55fe993 100644 --- a/src/org/atriasoft/exml/parser/ParseXml.java +++ b/src/org/atriasoft/exml/parser/ParseXml.java @@ -170,7 +170,7 @@ public class ParseXml { continue; } if (data.charAt(iii) == '>' || data.charAt(iii) == '<') { - // an error occured : + // an error occured : parsingProperty.createError(new ExmlParserError(Tools.extractLine(data, pos.value), filePos, " find '>' or '<' instead of '?>'")); return false; } @@ -264,30 +264,110 @@ public class ParseXml { return false; } - protected boolean iParseText(final Object parent, final String data, final PositionParsing pos, final FilePos filePos, final ParsingProperty parsingProperty) throws ExmlException { - Log.verbose("start parse : 'text'"); - // search end of the comment : - for (int iii = pos.value; iii < data.length(); iii++) { - Tools.drawElementParsed(data.charAt(iii), filePos); - if (filePos.check(data.charAt(iii))) { + protected int iParseFindEndComment(final String data, final int position) { + for (int iii = position; iii + 2 < data.length(); iii++) { + Log.verbose("iParseFindEndComment : '{}{}{}'", data.charAt(iii), data.charAt(iii + 1), data.charAt(iii + 2)); + if (data.charAt(iii) == '-' && data.charAt(iii + 1) == '-' && data.charAt(iii + 2) == '>') { + return iii + 2; + } + } + return data.length(); + } + + protected int iParseFindEndDeclaration(final String data, final int position) { + for (int iii = position; iii + 1 < data.length(); iii++) { + Log.verbose("iParseFindEndDeclaration : '{}{}'", data.charAt(iii), data.charAt(iii + 1)); + if (data.charAt(iii) == '?' && data.charAt(iii + 1) == '>') { + return iii + 1; + } + } + return data.length(); + } + + protected int iParseFindEndNode(final String data, final int position) { + Log.verbose("iParseFindEndNode : {}", data.charAt(position)); + if (position + 3 < data.length() && data.charAt(position) == '!' && data.charAt(position + 1) == '-' && data.charAt(position + 2) == '-') { + // fond comment + return iParseFindEndComment(data, position + 3); + } + if (position < data.length() && data.charAt(position) == '?') { + // simple declaration Node + return iParseFindEndDeclaration(data, position + 1); + } + int newPos = iParseFindEndSingleNode(data, position); + // check <..../> + if (data.charAt(newPos - 1) == '/') { + return newPos; + } + // normal Node + newPos = iParseFindTextPackEnd(data, newPos) + 1; + if (data.charAt(newPos) != '<' || data.charAt(newPos + 1) != '/') { + Log.error("iParseFindEndNode : {}{} ==> wrong end node !!! ", data.charAt(newPos), data.charAt(newPos + 1)); + } + newPos = iParseFindEndSingleNode(data, newPos + 1); + return newPos; + } + + protected int iParseFindEndSingleNode(final String data, final int position) { + for (int iii = position; iii < data.length(); iii++) { + Log.verbose("iParseFindEndSingleNode : '{}'", data.charAt(iii)); + if (data.charAt(iii) == '>') { + return iii; + } + } + return data.length(); + } + + protected int iParseFindTextPackEnd(final String data, final int position) { + for (int iii = position; iii < data.length(); iii++) { + Log.verbose("iParseFindTextPackEnd : {}", data.charAt(iii)); + if (data.charAt(iii) == '<' && data.charAt(iii + 1) == '/') { + Log.verbose("iParseFindTextPackEnd ==> find end of text !!!"); + return iii - 1; + } + if (data.charAt(iii) == '<') { + Log.verbose("iParseFindTextPackEnd ==> find new node !!!"); + final int endNode = iParseFindEndNode(data, iii + 1); + iii = endNode; continue; } - if (data.charAt(iii) == '>' || data.charAt(iii) == '<') { - // search whitespace : - int newEnd = iii; - for (int jjj = iii - 1; jjj > pos.value; --jjj) { - if (!Tools.isWhiteChar(data.charAt(jjj))) { - break; - } - newEnd = jjj; + } + return data.length(); + } + + protected boolean iParseText(final Object parent, final String data, final PositionParsing pos, final FilePos filePos, final ParsingProperty parsingProperty) throws ExmlException { + Log.verbose("start parse : 'text'"); + if (this.builder.isPackText(parent)) { + final int endOfText = iParseFindTextPackEnd(data, pos.value); + final String valueText = data.substring(pos.value, endOfText + 1); + Log.error("find text '{}' ==> new Pos={}", valueText, data.charAt(endOfText + 1)); + pos.value = endOfText; + this.builder.newText(parent, valueText); + return true; + } else { + // search end of the comment : + for (int iii = pos.value; iii < data.length(); iii++) { + Tools.drawElementParsed(data.charAt(iii), filePos); + if (filePos.check(data.charAt(iii))) { + continue; + } + if (data.charAt(iii) == '>' || data.charAt(iii) == '<') { + // search whitespace : + int newEnd = iii; + for (int jjj = iii - 1; jjj > pos.value; --jjj) { + if (!Tools.isWhiteChar(data.charAt(jjj))) { + break; + } + newEnd = jjj; + } + // find end of value: + String valueText = data.substring(pos.value, newEnd); + Log.verbose("find text '" + valueText + "'"); + pos.value = iii - 1; + valueText = Tools.replaceSpecialChar(valueText); + this.builder.newText(parent, valueText); + return true; } - // find end of value: - String valueText = data.substring(pos.value, newEnd); - Log.verbose("find text '" + valueText + "'"); - pos.value = iii - 1; - valueText = Tools.replaceSpecialChar(valueText); - this.builder.newText(parent, valueText); - return true; } } parsingProperty.createError(new ExmlParserError(Tools.extractLine(data, pos.value), filePos, "Text got end of file without finding end node")); @@ -322,9 +402,10 @@ public class ParseXml { * @param mainNode if true, this is the first root node * @return true parsing is done OK * @return false An error appear in the parsing - * @throws ExmlException + * @throws ExmlException */ - protected boolean subParseElement(final Object parent, final String nameElement, final String data, final PositionParsing pos, final FilePos filePos, final ParsingProperty parsingProperty) throws ExmlException { + protected boolean subParseElement(final Object parent, final String nameElement, final String data, final PositionParsing pos, final FilePos filePos, final ParsingProperty parsingProperty) + throws ExmlException { //EXMLPARSEELEMENT(" start subParse ... " << pos << " " << filePos); for (int iii = pos.value; iii < data.length(); iii++) { filePos.check(data.charAt(iii)); @@ -477,7 +558,7 @@ public class ParseXml { return false; } // find end of node : - // find > element ... + // find > element ... for (int jjj = endPosName + 1; jjj < data.length(); jjj++) { Tools.drawElementParsed(data.charAt(jjj), filePos); if (tmpPos.check(data.charAt(jjj))) { @@ -529,9 +610,9 @@ public class ParseXml { // find text: Object element = null; //try { - element = this.builder.newElement(parent, tmpname); + element = this.builder.newElement(parent, tmpname); //} catch (final Exception e) { - // TODO Auto-generated catch block + // TODO Auto-generated catch block // e.printStackTrace(); //} pos.value = endPosName + 1; @@ -546,7 +627,7 @@ public class ParseXml { continue; } filePos.add(tmpPos); - // here we have an error : + // here we have an error : parsingProperty.createError(new ExmlParserError(Tools.extractLine(data, pos.value), filePos, "Find an ununderstanding element : '" + data.charAt(iii + white + 1) + "'")); return false; } diff --git a/src/org/atriasoft/exml/parser/Tools.java b/src/org/atriasoft/exml/parser/Tools.java index 14cf823..b96027b 100644 --- a/src/org/atriasoft/exml/parser/Tools.java +++ b/src/org/atriasoft/exml/parser/Tools.java @@ -72,20 +72,20 @@ public class Tools { } public static String createPosPointer(final String line, final int pos) { - String out = ""; + final StringBuilder out = new StringBuilder(); int iii; for (iii = 0; iii < pos && iii < line.length(); iii++) { if (line.charAt(iii) == '\t') { - out += "\t"; + out.append("\t"); } else { - out += " "; + out.append(" "); } } for (; iii < pos; iii++) { - out += " "; + out.append(" "); } - out += "^"; - return out; + out.append("^"); + return out.toString(); } // based on this: https://stackoverflow.com/questions/4052840/most-efficient-way-to-make-the-first-character-of-a-string-lower-case @@ -105,11 +105,11 @@ public class Tools { */ public static void drawElementParsed(final Character val, final FilePos filePos) { // if (val == '\n') { - // Log.debug(filePos + " parse '\\n'"); + // Log.error(filePos + " parse '\\n'"); // } else if (val == '\t') { - // Log.debug(filePos + " parse '\\t'"); + // Log.error(filePos + " parse '\\t'"); // } else { - // Log.debug(filePos + " parse '" + val + "'"); + // Log.error(filePos + " parse '" + val + "'"); // } } @@ -162,16 +162,6 @@ public class Tools { } return out; } - public static String toString(final Boolean[] data) { - StringBuilder out = new StringBuilder(); - for (int iii=0; iii getFactory(final Field element) throws ExmlBuilderException { + final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlFactory.class); + if (annotation.length == 0) { + return null; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlFactory on " + element.getClass().getCanonicalName()); + } + return ((XmlFactory) annotation[0]).value(); + } + + public static Class getFactory(final Method element) throws ExmlBuilderException { + final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlFactory.class); + if (annotation.length == 0) { + return null; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlFactory on " + element.getClass().getCanonicalName()); + } + return ((XmlFactory) annotation[0]).value(); + } + public static Boolean getIsAttribute(final Constructor constructor, final Parameter element, final Boolean parentValue) throws ExmlBuilderException { final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlAttribute.class); if (annotation.length == 0) { @@ -223,6 +247,39 @@ public class ReflectTools { return ((XmlOptional) annotation[0]).value(); } + public static Boolean getIsText(final Constructor constructor, final Parameter element, final Boolean parentValue) throws ExmlBuilderException { + final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlText.class); + if (annotation.length == 0) { + return parentValue; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlText on " + constructor.getClass().getCanonicalName()); + } + return ((XmlText) annotation[0]).value(); + } + + public static Boolean getIsText(final Field element, final Boolean parentValue) throws ExmlBuilderException { + final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlText.class); + if (annotation.length == 0) { + return parentValue; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlText on " + element.getClass().getCanonicalName()); + } + return ((XmlText) annotation[0]).value(); + } + + public static Boolean getIsText(final Method element, final Boolean parentValue) throws ExmlBuilderException { + final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlText.class); + if (annotation.length == 0) { + return parentValue; + } + if (annotation.length > 1) { + throw new ExmlBuilderException("Must not have more than 1 element @XmlText on " + element.getClass().getCanonicalName()); + } + return ((XmlText) annotation[0]).value(); + } + public static String getListName(final Field element, final String defaultValue) throws Exception { final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlList.class); if (annotation.length == 0) { @@ -370,15 +427,15 @@ public class ReflectTools { } public static Class[] getTypeField(final Field fieldDescription) { - Class type = fieldDescription.getType(); + final Class type = fieldDescription.getType(); Class subType = null; - Type empppe = fieldDescription.getGenericType(); - if (empppe instanceof ParameterizedType plopppppp) { - Type[] realType = plopppppp.getActualTypeArguments(); + final Type empppe = fieldDescription.getGenericType(); + if (empppe instanceof final ParameterizedType plopppppp) { + final Type[] realType = plopppppp.getActualTypeArguments(); if (realType.length > 0) { try { subType = Class.forName(realType[0].getTypeName()); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } @@ -394,10 +451,10 @@ public class ReflectTools { type = elem.getParameters()[paramId].getType(); if (List.class.isAssignableFrom(type)) { Class internalModelClass = null; - Type[] empppe = elem.getGenericParameterTypes(); + final Type[] empppe = elem.getGenericParameterTypes(); if (empppe.length > paramId) { - if (empppe[paramId] instanceof ParameterizedType plopppppp) { - Type[] realType = plopppppp.getActualTypeArguments(); + if (empppe[paramId] instanceof final ParameterizedType plopppppp) { + final Type[] realType = plopppppp.getActualTypeArguments(); //Log.info("ppplllppp: " + realType.length); if (realType.length > 0) { Log.verbose(" -->> " + realType[0]); @@ -417,10 +474,10 @@ public class ReflectTools { type = setter.getParameters()[0].getType(); if (List.class.isAssignableFrom(type)) { Class internalModelClass = null; - Type[] empppe = setter.getGenericParameterTypes(); + final Type[] empppe = setter.getGenericParameterTypes(); if (empppe.length > 0) { - if (empppe[0] instanceof ParameterizedType plopppppp) { - Type[] realType = plopppppp.getActualTypeArguments(); + if (empppe[0] instanceof final ParameterizedType plopppppp) { + final Type[] realType = plopppppp.getActualTypeArguments(); if (realType.length > 0) { Log.verbose(" -->> " + realType[0]); internalModelClass = Class.forName(realType[0].getTypeName()); @@ -437,9 +494,9 @@ public class ReflectTools { Class subType = null; type = getter.getReturnType(); if (!Enum.class.isAssignableFrom(type)) { - Type empppe = getter.getGenericReturnType(); - if (empppe instanceof ParameterizedType plopppppp) { - Type[] realType = plopppppp.getActualTypeArguments(); + final Type empppe = getter.getGenericReturnType(); + if (empppe instanceof final ParameterizedType plopppppp) { + final Type[] realType = plopppppp.getActualTypeArguments(); if (realType.length > 0) { subType = Class.forName(realType[0].getTypeName()); }