diff --git a/src/org/atriasoft/aknot/internal/Log.java b/src/org/atriasoft/aknot/internal/Log.java index 7c05d77..18b9108 100644 --- a/src/org/atriasoft/aknot/internal/Log.java +++ b/src/org/atriasoft/aknot/internal/Log.java @@ -9,8 +9,8 @@ import org.atriasoft.reggol.LogLevel; import org.atriasoft.reggol.Logger; public class Log { + private static final String LIB_NAME = "aknot"; private static final boolean FORCE_ALL = false; - private static final String LIB_NAME = "exml"; private static final String LIB_NAME_DRAW = Logger.getDrawableName(Log.LIB_NAME); private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.CRITICAL); private static final boolean PRINT_DEBUG = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.DEBUG); diff --git a/src/org/atriasoft/aknot/model/IntrospectionModel.java b/src/org/atriasoft/aknot/model/IntrospectionModel.java index 7f2db2d..73e245e 100644 --- a/src/org/atriasoft/aknot/model/IntrospectionModel.java +++ b/src/org/atriasoft/aknot/model/IntrospectionModel.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import org.atriasoft.aknot.exception.AknotException; +import org.atriasoft.aknot.internal.Log; import org.atriasoft.aknot.pojo.IntrospectionProperty; public abstract class IntrospectionModel { @@ -27,7 +28,7 @@ public abstract class IntrospectionModel { this.names[0] = classType.getSimpleName(); } - public Object createObject(final Map properties, final Map> nodes) throws AknotException { + public Object createObject(final Map properties, final Map> nodes, final boolean attributeIndependent) throws AknotException { return null; } @@ -68,7 +69,11 @@ public abstract class IntrospectionModel { return null; } - public String getTreeNameOfSubNode(final String nodeName) throws AknotException { + public String getTokenBasicList() { + return STUPID_TOCKEN; + } + + public String getTreeNameOfSubNode(final String nodeName, final boolean attributeIndependent) throws AknotException { return null; } @@ -76,11 +81,11 @@ public abstract class IntrospectionModel { return null; } - public Class getTypeOfSubNode(final String nodeName) throws AknotException { + public Class getTypeOfSubNode(final String nodeName, final boolean attributeIndependent) throws AknotException { return null; } - public Class getTypeOfSubNodeList(final String nodeName) throws AknotException { + public Class getTypeOfSubNodeList(final String nodeName, final boolean attributeIndependent) throws AknotException { return null; } @@ -96,6 +101,106 @@ public abstract class IntrospectionModel { return null; } + /** + * This converter Number to the correct type ... + */ + public Object getValueConverted(final Object value, final Class classTypeLocal) throws AknotException { + // Note if the type is an Array<>() or a List<>() ==> we parse element by element ... then we need to keep the undertype... + Log.debug("======>>>>>>> convert : {} ==> {} ", value.getClass().getCanonicalName(), classTypeLocal.getCanonicalName()); + if (value.getClass() == Long.class) { + if (classTypeLocal == long.class || classTypeLocal == Long.class) { + return value; + } + final Long value2 = (Long) value; + if (classTypeLocal == byte.class || classTypeLocal == Byte.class) { + return value2.byteValue(); + } + if (classTypeLocal == short.class || classTypeLocal == Short.class) { + return value2.shortValue(); + } + if (classTypeLocal == float.class || classTypeLocal == Float.class) { + return value2.floatValue(); + } + if (classTypeLocal == double.class || classTypeLocal == Double.class) { + return value2.doubleValue(); + } + if (classTypeLocal == Integer.class || classTypeLocal == int.class) { + return value2.intValue(); + } + if (classTypeLocal == boolean.class || classTypeLocal == Boolean.class) { + return value2.equals(0); + } + } + if (value.getClass() == Double.class) { + if (classTypeLocal == double.class || classTypeLocal == Double.class) { + return value; + } + final Double value2 = (Double) value; + if (classTypeLocal == byte.class || classTypeLocal == Byte.class) { + return value2.byteValue(); + } + if (classTypeLocal == short.class || classTypeLocal == Short.class) { + return value2.shortValue(); + } + if (classTypeLocal == float.class || classTypeLocal == Float.class) { + return value2.floatValue(); + } + if (classTypeLocal == long.class || classTypeLocal == Long.class) { + return value2.longValue(); + } + if (classTypeLocal == Integer.class || classTypeLocal == int.class) { + return value2.intValue(); + } + if (classTypeLocal == boolean.class || classTypeLocal == Boolean.class) { + return !value2.equals(0); + } + } + if (value.getClass() == Boolean.class) { + if (classTypeLocal == boolean.class || classTypeLocal == Boolean.class) { + return value; + } + final Boolean value2 = (Boolean) value; + if (classTypeLocal == byte.class || classTypeLocal == Byte.class) { + if (value2) + return (byte) 1; + else + return (byte) 0; + } + if (classTypeLocal == short.class || classTypeLocal == Short.class) { + if (value2) + return (short) 1; + else + return (short) 0; + } + if (classTypeLocal == float.class || classTypeLocal == Float.class) { + if (value2) + return (float) 1; + else + return (float) 0; + } + if (classTypeLocal == long.class || classTypeLocal == Long.class) { + if (value2) + return (long) 1; + else + return (long) 0; + } + if (classTypeLocal == int.class || classTypeLocal == Integer.class) { + if (value2) + return (int) 1; + else + return (int) 0; + } + if (classTypeLocal == double.class || classTypeLocal == Double.class) { + if (value2) + return (double) 1; + else + return (double) 0; + } + } + Log.error("======>>>>>>> convert : {} ==> {} Can not do it !!!", value.getClass().getCanonicalName(), classTypeLocal.getCanonicalName()); + return value; + } + public Object getValueFromText(final String text) throws AknotException { return null; } diff --git a/src/org/atriasoft/aknot/pojo/IntrospectionModelArray.java b/src/org/atriasoft/aknot/pojo/IntrospectionModelArray.java index ea131cb..47ed1d7 100644 --- a/src/org/atriasoft/aknot/pojo/IntrospectionModelArray.java +++ b/src/org/atriasoft/aknot/pojo/IntrospectionModelArray.java @@ -25,7 +25,7 @@ public class IntrospectionModelArray extends IntrospectionModel { } @Override - public Object createObject(final Map properties, final Map> nodes) throws AknotException { + public Object createObject(final Map properties, final Map> nodes, final boolean attributeIndependent) throws AknotException { List tmp = null; if (this.nodeName == null) { tmp = nodes.get(STUPID_TOCKEN); diff --git a/src/org/atriasoft/aknot/pojo/IntrospectionModelBaseType.java b/src/org/atriasoft/aknot/pojo/IntrospectionModelBaseType.java index 86f474e..313c334 100644 --- a/src/org/atriasoft/aknot/pojo/IntrospectionModelBaseType.java +++ b/src/org/atriasoft/aknot/pojo/IntrospectionModelBaseType.java @@ -14,7 +14,7 @@ public class IntrospectionModelBaseType extends IntrospectionModel { } @Override - public Object createObject(final Map properties, final Map> nodes) throws AknotException { + public Object createObject(final Map properties, final Map> nodes, final boolean attributeIndependent) throws AknotException { throw new AknotException("Base type model can not have properties and nodes ... "); } diff --git a/src/org/atriasoft/aknot/pojo/IntrospectionModelComplex.java b/src/org/atriasoft/aknot/pojo/IntrospectionModelComplex.java new file mode 100644 index 0000000..fa3048c --- /dev/null +++ b/src/org/atriasoft/aknot/pojo/IntrospectionModelComplex.java @@ -0,0 +1,1156 @@ +package org.atriasoft.aknot.pojo; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.atriasoft.aknot.ReflectClass; +import org.atriasoft.aknot.ReflectTools; +import org.atriasoft.aknot.StringSerializer; +import org.atriasoft.aknot.exception.AknotException; +import org.atriasoft.aknot.internal.Log; +import org.atriasoft.aknot.model.ConstructorModel; +import org.atriasoft.aknot.model.IntrospectionModel; +import org.atriasoft.etk.Tools; +import org.atriasoft.etk.util.ArraysTools; + +public class IntrospectionModelComplex extends IntrospectionModel { + private final boolean isRecord; + // TODO Optimize this with external object for basic types.... + private final Method valueof; // used for the set Text if the object is an end point... + private final Method tostring; // used for the set Text if the object is an end point... + private final boolean isSubClass; // if true, the constructor must be called with a null first object. + private final Constructor constructorEmpty; + private final List constructors = new ArrayList<>(); + private List elements = new ArrayList<>(); + private List elementUnManaged = new ArrayList<>(); + + public IntrospectionModelComplex(final Class classType) throws AknotException { + super(classType); + try { + final String[] className = ReflectTools.getNames(classType, null); + if (className != null) { + this.names = className; + } + + if (classType.getNestHost() == classType) { + this.isSubClass = false; + } else if (!Modifier.isStatic(classType.getModifiers())) { + this.isSubClass = true; + } else { + this.isSubClass = false; + } + this.isRecord = Record.class.isAssignableFrom(classType); + if (classType.isPrimitive()) { + Log.critical("Detect primitive ==> impossible case !!! "); + } + final Boolean isDefaultManaged = ReflectTools.getIsDefaultManaged(classType, IntrospectionModel.DEFAULT_MANAGED); + // ------------------------------------------------------------------------ + // -- Parse constructor + // ------------------------------------------------------------------------ + Log.error("Introspect class: '" + classType.getCanonicalName() + "'"); + final Constructor[] constructors = this.classType.getConstructors(); + Log.warning(" 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; + } + // check if the constructor is rejected ! + final Boolean managedConstructor = ReflectTools.getIsManaged(elem, isDefaultManaged); + if (managedConstructor != null && !managedConstructor) { + continue; + } + if (elem.getParameterCount() == 0) { + emptyConstructorTmp = elem; + Log.error(" >>> " + elem.toGenericString()); + } else { + int offsetSubClass = 0; + if (this.isSubClass) { + offsetSubClass = 1; + } + if (elem.getParameterCount() == 1 && offsetSubClass == 1) { + emptyConstructorTmp = elem; + Log.error(" >>> " + elem.toGenericString()); + } else { + // Retrieve full description in constructor properties... + String[] namesBeans = ReflectTools.getNames(elem, null); + if (namesBeans == null) { + namesBeans = new String[elem.getParameterCount() - offsetSubClass]; + } else if (elem.getParameterCount() != namesBeans.length + offsetSubClass) { + throw new AknotException("Wrong number of parameter in constructor with ne number declared in the @XmlName (for bean name)"); + } + 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]; + + final Class[][] clazz = new Class[elem.getParameterCount() - offsetSubClass][]; + final String[][] names = new String[elem.getParameterCount() - offsetSubClass][]; + + final Parameter[] params = elem.getParameters(); + for (int iii = offsetSubClass; iii < params.length; 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); + 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) { + // TODO maybe do something id name is already set ??? + namesBeans[iii - offsetSubClass] = namesParam[0]; + } + } + // The constructor can not be managed if One of the element is mark as unmanaged... + boolean managedParameters = true; + for (int iii = 0; iii < isManageds.length; iii++) { + if (isManageds[iii] != null && !isManageds[iii]) { + managedParameters = false; + break; + } + } + if (!managedParameters) { + continue; + } + if (checkIfOneIsNull(namesBeans, 0)) { + Log.verbose(" - " + elem.toGenericString()); + Log.verbose(" ==> unmanaged (missing names description: " + Arrays.toString(namesBeans) + ")"); + } else { + // check default attributes in the global list ... (do it at the end to be sure the constructor is VALID ... + for (int iii = 0; iii < namesBeans.length; iii++) { + IntrospectionProperty prop = findElement(namesBeans[iii]); + if (prop == null) { + prop = new IntrospectionProperty(namesBeans[iii], clazz[iii], names[iii]); + this.elements.add(prop); + } else { + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); + if (curentType != clazz[iii][0] && curentSubType != clazz[iii][1]) { + throw new AknotException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + + " ==> new=" + clazz[iii][0] + " / " + clazz[iii][1] + " in " + elem.toGenericString()); + } + final String[] names1 = names[iii]; + if (names1 != null) { + final String[] curentValue = prop.getNames(); + if (curentValue != null) { + // TODO maybe set the value permissive if no change !!! like the others... + throw new AknotException("Set 'names' with a (already set!) " + elem.toGenericString()); + } + prop.setNames(names1); + } + } + // Set settable by the constructor + prop.setCanBeSetByConstructor(true); + final Boolean isAttribute = isAttributes[iii]; + if (isAttribute != null) { + final Boolean curentValue = prop.isAttribute(); + if (curentValue != null && curentValue != isAttribute) { + throw new AknotException("Set 'attribute' with a different value previous=" + curentValue + " ==> new=" + isAttribute + " in " + elem.toGenericString()); + } + prop.setAttribute(isAttribute); + } + final Boolean isCaseSensitive = isCaseSensitives[iii]; + if (isCaseSensitive != null) { + final Boolean curentValue = prop.isCaseSensitive(); + if (curentValue != null && curentValue != isCaseSensitive) { + throw new AknotException( + "Set 'caseSensitive' with a different value previous=" + curentValue + " ==> new=" + isCaseSensitive + " in " + elem.toGenericString()); + } + prop.setCaseSensitive(isCaseSensitive); + } + final Boolean isOptional = isOptionals[iii]; + if (isOptional != null) { + final Boolean curentValue = prop.isOptionnal(); + if (curentValue != null && curentValue != isOptional) { + throw new AknotException("Set 'optionnal' with a different value previous=" + curentValue + " ==> new=" + isOptional + " in " + elem.toGenericString()); + } + prop.setOptionnal(isOptional); + } + final Boolean isManaged = isManageds[iii]; + if (isManaged != null) { + final Boolean curentValue = prop.isManaged(); + if (curentValue != null && curentValue != isManaged) { + throw new AknotException("Set 'managed' with a different value previous=" + curentValue + " ==> new=" + isManaged + " in " + elem.toGenericString()); + } + prop.setManaged(isManaged); + } + } + this.constructors.add(new ConstructorModel(namesBeans, isAttributes, elem)); + } + } + } + } + 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 (final ConstructorModel elem : this.constructors) { + Log.verbose(" * " + elem.constructor().toGenericString()); + final StringBuilder tmpPrint = new StringBuilder(" ==> ("); + if (this.isSubClass) { + tmpPrint.append("null, "); + } + for (int iii = 0; iii < elem.values().length; iii++) { + if (iii != 0) { + tmpPrint.append(", "); + } + tmpPrint.append(elem.values()[iii]); + } + tmpPrint.append(")"); + Log.verbose(tmpPrint.toString()); + } + final List recordAllPossibleValues = new ArrayList<>(); + + if (this.isRecord) { + for (final ConstructorModel elem : this.constructors) { + for (int iii = 0; iii < elem.values().length; iii++) { + final String tmpp = elem.values()[iii]; + if (!recordAllPossibleValues.contains(tmpp)) { + recordAllPossibleValues.add(tmpp); + } + } + } + } + // ------------------------------------------------------------------------ + // -- Parse Field + // ------------------------------------------------------------------------ + final Field[] fields = this.classType.getFields(); + Log.verbose(" Fields: (" + fields.length + ")"); + for (final Field elem : fields) { + // we does not manage static field + if (Modifier.isStatic(elem.getModifiers())) { + continue; + } + // we does not manage private field + // NOTE: if the field is private I do not check the elements ==> the user want a private API !!! (maybe change ...) + if (!Modifier.isPublic(elem.getModifiers())) { + continue; + } + final String[] names = ReflectTools.getNames(elem, null); + 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 { + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); + if (curentType != types[0] && curentSubType != types[1]) { + throw new AknotException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + " ==> new=" + + types[0] + " / " + types[1] + " in " + elem.toGenericString()); + } + final String[] names1 = names; + if (names1 != null) { + final String[] curentValue = prop.getNames(); + if (curentValue != null) { + // TODO maybe set the value permissive if no change !!! like the others... + throw new AknotException("Set 'names' with a (already set!) " + elem.toGenericString()); + } + prop.setNames(names1); + } + } + + final String description = ReflectTools.getDescription(elem, null); + if (description != null) { + prop.setDescription(description); + } + final String listName = ReflectTools.getListName(elem, null); + if (listName != null) { + prop.setListName(listName); + } + final Boolean isSignal = ReflectTools.getIsSignal(elem, null); + if (isSignal != null) { + prop.setSignal(isSignal); + } + final Boolean isAttribute = ReflectTools.getIsAttribute(elem, null); + if (isAttribute != null) { + prop.setAttribute(isAttribute); + } + final Boolean isManaged = ReflectTools.getIsManaged(elem, null); + if (isManaged != null) { + prop.setManaged(isManaged); + } + final Boolean isOptionnal = ReflectTools.getIsOptional(elem, null); + if (isOptionnal != null) { + prop.setOptionnal(isOptionnal); + } + final Boolean isCaseSensitive = ReflectTools.getIsCaseSensitive(elem, null); + 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. + + 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); + } else { + prop.setSetter(modifier); + prop.setGetter(modifier); + } + Log.verbose(" - " + elem.toGenericString()); + } + + final List methods = ReflectClass.getFilterGenericFucntion(this.classType, recordAllPossibleValues, true, true, true); + + Log.verbose(" Methods: (" + methods.size() + ")"); + for (final Method elem : methods) { + Log.verbose(" - " + elem.toGenericString()); + } + + // Separate the methods and filer as: + // - XXX GetXxx(); & XXX != boolean + // - void setXxx(XXX elem); + // - [bB]oolean isXxx(); + // for records: + // - xxx(); + + final List methodsGet = ReflectClass.extractGetMethod(classType, methods, recordAllPossibleValues); + final List methodsSet = ReflectClass.extractSetMethod(classType, methods); + final List methodsIs = ReflectClass.extractIsMethod(classType, methods); + this.valueof = ReflectClass.extractValueOf(methods); + this.tostring = ReflectClass.extractToString(methods); + // associate methods by pair. + for (final Method method : methodsGet) { + final String name = Tools.decapitalizeFirst(this.isRecord ? method.getName() : method.getName().substring(3)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); + // generate getter and setter with field. + 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)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeReturnFunction(method)); + // generate getter and setter with field. + final IntrospectionPropertyMethodGetter modifier = new IntrospectionPropertyMethodGetter(method); + prop.setGetter(modifier); + } + for (final Method method : methodsSet) { + final String name = Tools.decapitalizeFirst(method.getName().substring(3)); + final IntrospectionProperty prop = updateForMethod(name, method, ReflectTools.getTypeParameterfunction(method)); + // generate getter and setter with field. + final IntrospectionPropertyMethodSetter modifier = new IntrospectionPropertyMethodSetter(method); + prop.setSetter(modifier); + } + } + this.ignoreUnknown = ReflectTools.getIsIgnoreUnknown(classType, IntrospectionModel.DEFAULT_IGNORE_UNBKNOWN); + + this.defaultNullValue = ReflectTools.getIsDefaultNullValue(classType, IntrospectionModel.DEFAULT_DEFAULT_NULL_VALUE); + + // Set only at the end ==> no need before... + final Boolean isDefaultAttribute = ReflectTools.getIsDefaultAttribute(classType, IntrospectionModel.DEFAULT_ATTRIBUTE); + for (final IntrospectionProperty prop : this.elements) { + if (prop.isAttribute() == null) { + prop.setAttribute(isDefaultAttribute); + } + } + for (final IntrospectionProperty prop : this.elements) { + if (prop.isManaged() == null) { + prop.setManaged(isDefaultManaged); + } + } + final Boolean isDefaultOptional = ReflectTools.getIsDefaultOptional(classType, IntrospectionModel.DEFAULT_OPTIONAL); + for (final IntrospectionProperty prop : this.elements) { + if (prop.isOptionnal() == null) { + prop.setOptionnal(isDefaultOptional); + } + } + final Boolean isDefaultCaseSensitive = ReflectTools.getIsDefaultCaseSensitive(classType, IntrospectionModel.DEFAULT_CASE_SENSITIVE); + for (final IntrospectionProperty prop : this.elements) { + if (prop.isCaseSensitive() == null) { + prop.setCaseSensitive(isDefaultCaseSensitive); + } + } + // set default name in the list: + for (final IntrospectionProperty prop : this.elements) { + if (prop.getNames() == null) { + prop.setNames(new String[] { prop.getBeanName() }); + } + } + + } catch (final Exception ex) { + ex.printStackTrace(); + throw new AknotException("Error in creating introspection data ... " + ex.getMessage()); + } + // Sort the parameters to generate all time the same XML.. + Collections.sort(this.elements, (a, b) -> a.getNames()[0].compareTo(b.getNames()[0])); + // separate managed and unmanaged to optimize performances... + this.elementUnManaged = this.elements.stream().filter(o -> !o.isManaged()).collect(Collectors.toList()); + this.elements = this.elements.stream().filter(o -> o.isManaged()).collect(Collectors.toList()); + //display(); + } + + @SuppressWarnings("unchecked") + private T[] autoCast(final Class clazz, final List data) { + 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); + } + return out; + //return data.stream().map(clazz::cast).toArray(new T[data.size()]);//java.lang.reflect.Array.newInstance((propMethode.getSubType(), tmpp.size())); + } + + private boolean checkIdenticalArray(final String[] valA, final String[] valB) { + if (valA == valB) { + return true; + } + if (valA.length != valB.length) { + return false; + } + for (int iii = 0; iii < valA.length; iii++) { + if (!valA[iii].equals(valB[iii])) { + return false; + } + } + return true; + } + + // private boolean checkIfOneIsNull(Boolean[] values, int offset) { + // for (int iii=offset; iii properties, final Map> nodes, final boolean attributeIndependent) throws AknotException { + Object tmp = null; + // STEP 1: try to create the object with provided parameter (if a constructor exist....) + if (!this.constructors.isEmpty()) { + Object[] inputs = null; + ConstructorModel betterModel = null; + int lastEmpty = Integer.MAX_VALUE; + // try to find the constructor that fit with parameters ... + for (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) { + final List tmppp = nodes.get(elem.values()[iii]); + if (tmppp != null) { + if (List.class == findBeanNodeDescription(elem.values()[iii], attributeIndependent).getType()) { + valueToInject = tmppp; + } else if (tmppp.size() >= 1) { + valueToInject = tmppp.get(0); + } + } + } + if (valueToInject == null) { + empty++; + inputsTmp[iii + offset] = null; + continue; + } + inputsTmp[iii + offset] = valueToInject; + } + if (lastEmpty >= empty && (inputs == null || inputs.length < inputsTmp.length)) { + inputs = inputsTmp; + betterModel = elem; + lastEmpty = empty; + } + } + if (betterModel != null) { + if (isDefaultNullValue() || lastEmpty == 0) { + final ConstructorModel elem = betterModel; + if (inputs != null) { + // here we find our constructor... + try { + tmp = switch (inputs.length) { + case 0 -> elem.constructor().newInstance(); + case 1 -> elem.constructor().newInstance(inputs[0]); + case 2 -> elem.constructor().newInstance(inputs[0], inputs[1]); + case 3 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2]); + case 4 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3]); + case 5 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4]); + case 6 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5]); + case 7 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6]); + case 8 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7]); + case 9 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8]); + case 10 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9]); + case 11 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10]); + case 12 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11]); + case 13 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12]); + case 14 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13]); + case 15 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14]); + case 16 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15]); + case 17 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15], inputs[16]); + case 18 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15], inputs[16], inputs[17]); + case 19 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15], inputs[16], inputs[17], inputs[18]); + case 20 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15], inputs[16], inputs[17], inputs[18], inputs[19]); + case 21 -> elem.constructor().newInstance(inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5], inputs[6], inputs[7], inputs[8], inputs[9], inputs[10], + inputs[11], inputs[12], inputs[13], inputs[14], inputs[15], inputs[16], inputs[17], inputs[18], inputs[19], inputs[20]); + default -> throw new AknotException("to much parameter in the constructor... " + inputs.length); + }; + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new AknotException("Error when creating the Object ..." + this.classType.getCanonicalName()); + } + // Remove all previously added parameters + for (int iii = 0; iii < elem.values().length; iii++) { + properties.remove(elem.values()[iii]); + nodes.remove(elem.values()[iii]); + } + } + } + } + } + // STEP 2: If we do not create the object ==> try with empty constructor... + if (tmp == null) { + if (this.constructorEmpty == null) { + throw new AknotException("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) { + final Object tmp2 = null; + tmp = this.constructorEmpty.newInstance(tmp2); + } else { + tmp = this.constructorEmpty.newInstance(); + } + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) { + e.printStackTrace(); + return null; + } + } + // STEP 3: set the rest if the parameters... + for (final Entry elem : properties.entrySet()) { + setValue(tmp, elem.getKey(), elem.getValue(), attributeIndependent); + } + for (final Entry> elem : nodes.entrySet()) { + setValue(tmp, elem.getKey(), elem.getValue(), attributeIndependent); + } + return tmp; + } + + public void display() { + Log.print("Class: {} nbProperty:{}", this.classType.getCanonicalName(), this.elements.size()); + Log.print("Managed:"); + for (final IntrospectionProperty prop : this.elements) { + Log.print(" * Property/node : {}", prop.getBeanName()); + Log.print(" names: {}", Arrays.toString(prop.getNames())); + Log.print(" list: {}", prop.getListName()); + //Log.print(" managed: {}", prop.isManaged()); + Log.print(" attribute: {}", prop.isAttribute()); + Log.print(" text: {}", prop.isText()); + Log.print(" case-sensitive: {}", prop.isCaseSensitive()); + Log.print(" optionnal: {}", prop.isOptionnal()); + Log.print(" constructor: {}", prop.isCanBeSetByConstructor()); + Log.print(" get/set: {} / {}", prop.canGetValue(), prop.canSetValue()); + Log.print(" type: {}", prop.getType().getCanonicalName()); + if (prop.getSubType() != null) { + Log.print(" sub-type: {}", prop.getSubType().getCanonicalName()); + } else { + Log.print(" sub-type: null"); + } + } + Log.info("Un-Managed:"); + for (final IntrospectionProperty prop : this.elementUnManaged) { + Log.print(" * Property/node : {}", prop.getBeanName()); + Log.print(" names: {}", Arrays.toString(prop.getNames())); + Log.print(" list: {}", prop.getListName()); + //Log.print(" managed: {}", prop.isManaged()); + Log.print(" attribute: {}", prop.isAttribute()); + Log.print(" text: {}", prop.isText()); + Log.print(" case-sensitive: {}", prop.isCaseSensitive()); + Log.print(" optionnal: {}", prop.isOptionnal()); + Log.print(" constructor: {}", prop.isCanBeSetByConstructor()); + Log.print(" get/set: {} / {}", prop.canGetValue(), prop.canSetValue()); + Log.print(" type: {}", prop.getType().getCanonicalName()); + if (prop.getSubType() != null) { + Log.print(" sub-type: {}", prop.getSubType().getCanonicalName()); + } else { + Log.print(" sub-type: null"); + } + } + } + + protected IntrospectionProperty findBeanNodeDescription(final String propertyBeanName, final boolean attributeIndependent) throws AknotException { + Log.debug("Find node description for element: {}", propertyBeanName); + for (final IntrospectionProperty prop : this.elements) { + Log.debug(" ==> {} => {}", prop.getBeanName(), prop.isAttribute()); + if (!attributeIndependent && prop.isAttribute()) { + continue; + } + if (prop.getBeanName().equals(propertyBeanName)) { + return prop; + } + } + return null; + } + + protected IntrospectionProperty findBeanPropertyDescription(final String propertyBeanName) throws AknotException { + for (final IntrospectionProperty prop : this.elements) { + if (!prop.isAttribute()) { + continue; + } + if (prop.getBeanName().equals(propertyBeanName)) { + return prop; + } + } + return null; + } + + protected IntrospectionProperty findElement(final String beanName) { + for (final IntrospectionProperty elem : this.elements) { + if (elem.getBeanName().equals(beanName)) { + return elem; + } + } + return null; + } + + protected IntrospectionProperty findNodeDescription(final String propertyName) throws AknotException { + for (final IntrospectionProperty prop : this.elements) { + if (prop.isAttribute()) { + continue; + } + if (prop.isCompatible(propertyName)) { + return prop; + } + } + return null; + } + + protected IntrospectionProperty findPropertyDescription(final String propertyName) throws AknotException { + for (final IntrospectionProperty prop : this.elements) { + if (!prop.isAttribute()) { + continue; + } + if (prop.isCompatible(propertyName)) { + return prop; + } + } + return null; + } + + @Override + public List getAttributes() { + final List out = new ArrayList<>(); + for (final IntrospectionProperty elem : this.elements) { + if (elem.isAttribute()) { + out.add(elem); + } + } + return out; + } + + @Override + public String getBeanName(final String nodeName) { + for (final IntrospectionProperty elem : this.elements) { + if (elem.isCompatible(nodeName)) { + return elem.getBeanName(); + } + } + 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() { + final List out = new ArrayList<>(); + for (final IntrospectionProperty prop : this.elements) { + if (prop.isAttribute()) { + continue; + } + out.add(prop.getNames()[0]); + } + return out; + } + + @Override + public List getNodes() { + final List out = new ArrayList<>(); + for (final IntrospectionProperty elem : this.elements) { + if (!elem.isAttribute()) { + out.add(elem); + } + } + return out; + } + + @Override + public List getSignals() { + final List out = new ArrayList<>(); + for (final IntrospectionProperty elem : this.elements) { + final Boolean signal = elem.isSignal(); + if (signal != null && signal) { + out.add(elem); + } + } + 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, final boolean attributeIndependent) throws AknotException { + Log.debug(" nodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName, attributeIndependent); + if (propMethode != null && propMethode.canSetValue()) { + Log.debug(" ==> find '" + propMethode.getNames()); + return propMethode.getListName(); + } + throw new AknotException("can not find the field '" + nodeBeanName + "'"); + } + + @Override + public Class getTypeOfProperty(final String nodeName) throws AknotException { + Log.debug("nodeType='" + nodeName + "'"); + final IntrospectionProperty propField = findPropertyDescription(nodeName); + if (propField != null && propField.canSetValue()) { + Log.debug(" ==> find '" + propField.getNames()); + return propField.getType(); + } + + throw new AknotException("can not find the field '" + nodeName + "' available: " + getNodeAvaillable()); + } + + /** + * Detect a subNode, and ask the type of the node at the parent Class + * @param nodeBeanName Name of the node (bean name access ==> not the XML name) + * @return Class of the node to create + */ + @Override + public Class getTypeOfSubNode(final String nodeBeanNames, final boolean attributeIndependent) throws AknotException { + final String[] elemstNames = nodeBeanNames.split("#"); + final String nodeBeanName = elemstNames[0]; + Log.debug("NodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName, attributeIndependent); + if (propMethode != null && propMethode.canSetValue()) { + Log.debug(" ==> find '" + propMethode.getNames()); + if (propMethode.hasFactory()) { + return propMethode.getCompatible(elemstNames[1]); + } else { + return propMethode.getType(); + } + } + throw new AknotException("can not find the field '" + nodeBeanName + "' available: " + getNodeAvaillable()); + } + + @Override + public Class getTypeOfSubNodeList(final String nodeBeanNames, final boolean attributeIndependent) throws AknotException { + final String[] elemstNames = nodeBeanNames.split("#"); + final String nodeBeanName = elemstNames[0]; + Log.debug(" nodeType='" + nodeBeanName + "'"); + final IntrospectionProperty propMethode = findBeanNodeDescription(nodeBeanName, attributeIndependent); + if (propMethode != null && propMethode.canSetValue()) { + Log.debug(" ==> find '" + propMethode.getNames()); + if (propMethode.hasFactory()) { + return propMethode.getCompatible(elemstNames[1]); + } else { + return propMethode.getSubType(); + } + } + throw new AknotException("can not find the field '" + nodeBeanName + "' available: " + getNodeAvaillable()); + } + + @Override + public Class getTypeOfSubProperty(final String nodeName) throws AknotException { + Log.debug(" nodeType='" + nodeName + "'"); + final IntrospectionProperty propField = findPropertyDescription(nodeName); + if (propField != null && propField.canSetValue()) { + Log.debug(" ==> find '" + propField.getNames()); + return propField.getSubType(); + } + throw new AknotException("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 + public Object getValue(final String propertyName, final String propertyValue) throws AknotException { + Log.debug(" propertyName='" + propertyName + "' propertyValue='" + propertyValue + "' "); + // by default use setter to set the property + final IntrospectionProperty propMethode = findNodeDescription(propertyName); + if (propMethode != null && propMethode.canSetValue()) { + Log.verbose(" ==> find '" + propMethode.getNames()); + return propMethode.createValue(propertyValue); + } + // try with direct field + final IntrospectionProperty propField = findPropertyDescription(propertyName); + if (propField != null && propField.canSetValue()) { + Log.verbose(" ==> find '" + propField.getNames()); + return propField.createValue(propertyValue); + } + throw new AknotException("can not find the field '" + propertyName + "'"); + } + + @Override + public Object getValueFromText(final String text) throws AknotException { + // Note if the type is an Array<>() or a List<>() ==> we parse element by element ... then we need to keep the undertype... + Class classTypeLocal = this.classType; + Log.debug("======>>>>>>> Get input type : " + this.classType.getCanonicalName()); + //Log.debug("======>>>>>>> Get input component type : " + this.classType.getComponentType().getCanonicalName()); + if (this.classType.isArray()) { + // generic array ... + classTypeLocal = this.classType.getComponentType(); + } else if (List.class == this.classType || Collection.class.isAssignableFrom(this.classType)) { + // a generic list .... + /*if (this.subClassType != null) { + classTypeLocal = this.subClassType; + }*/ + //Type[] tmpp = this.classType.getGenericInterfaces(); + //Class[] tmpp = this.classType.getNestMembers(); + //Class[] tmpp = this.classType.getClasses(); + //Class[] tmpp = this.classType.getDeclaredClasses(); + //Class[] tmpp = this.classType.getInterfaces(); + //Class tmpp = this.classType.getDeclaringClass(); + //TypeVariable[] tmpp = this.classType.getTypeParameters(); + /* + Class persistentClass =((ParameterizedType)classType.getGenericSuperclass()).getActualTypeArguments()[0]; + */ + //Type tmpp = classType.getGenericSuperclass(); + // Class tmpp = classType.getInterfaces(); + // Log.warning("======>>>>>>> Find List '" + tmpp + "'"); + // for (int iii = 0; iii < tmpp.length; iii++) { + // Log.warning(" - " + tmpp[iii]); + // } + } + Log.debug("======>>>>>>> subElement input type : " + classTypeLocal.getCanonicalName()); + + if (this.valueof == null) { + if (StringSerializer.contains(classTypeLocal)) { + throw new AknotException("function 'valueOf' for '" + classTypeLocal.getCanonicalName() + "' is not defined and not registered for specific type"); + } + return StringSerializer.valueOf(classTypeLocal, text); + } + try { + return this.valueof.invoke(null, text); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + if (Enum.class.isAssignableFrom(this.classType)) { + throw new AknotException("Error in call 'valueOf(String ...)' for enum '" + classTypeLocal.getCanonicalName() + "' ==> '" + text + "' ... availlable list: " + + Arrays.asList(this.classType.getEnumConstants())); + } + e.printStackTrace(); + throw new AknotException("Error in call 'valueOf(String ...)' for '" + classTypeLocal.getCanonicalName() + "' " + e.getMessage()); + } + } + + @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; + } + + private void setValue(final Object data, final String beanName, final Object value, final boolean attributeIndependent) throws AknotException { + Log.verbose(" Set value ='" + beanName + "' propertyValue='" + value + "' " + value.getClass().getCanonicalName()); + { + // by default use setter to set the property + final IntrospectionProperty propMethode = findBeanNodeDescription(beanName, attributeIndependent); + if (propMethode != null && propMethode.canSetValue()) { + Log.verbose(" ==> find '" + Arrays.toString(propMethode.getNames()) + " type=" + propMethode.getType() + " sub-type=" + propMethode.getSubType()); + if (propMethode.getType().isAssignableFrom(value.getClass())) { + propMethode.setExistingValue(data, value); + } else if (value instanceof List) { + @SuppressWarnings("unchecked") + final List tmpp = (List) value; + if (propMethode.getType().isArray()) { + if (propMethode.getType().componentType().isPrimitive()) { + 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()); + Log.verbose(" methode type: " + propMethode.getType().getCanonicalName()); + propMethode.setExistingValue(data, autoCast(propMethode.getType().componentType(), tmpp)); + } + } else if (tmpp.size() == 1) { + propMethode.setExistingValue(data, tmpp.get(0)); + } else { + // impossible case ... + } + } else if (propMethode.getType() == byte.class) { + final byte dataPrimitive = (Byte) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == short.class) { + final short dataPrimitive = (Short) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == int.class) { + final int dataPrimitive = (Integer) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == long.class) { + final long dataPrimitive = (Long) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == boolean.class) { + final boolean dataPrimitive = (Boolean) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == float.class) { + final float dataPrimitive = (Float) value; + propMethode.setExistingValue(data, dataPrimitive); + } else if (propMethode.getType() == double.class) { + final double dataPrimitive = (Double) value; + propMethode.setExistingValue(data, dataPrimitive); + } else { + + } + return; + } + } + // try with direct field + { + final IntrospectionProperty propField = findBeanPropertyDescription(beanName); + if (propField != null && propField.canSetValue()) { + Log.verbose(" ==> find '" + Arrays.toString(propField.getNames()) + " type=" + propField.getType() + " sub-type=" + propField.getSubType()); + if (propField.getType().isAssignableFrom(value.getClass())) { + propField.setExistingValue(data, value); + // Some specific case for primitives values + } else if (propField.getType() == byte.class) { + final byte dataPrimitive = (Byte) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == short.class) { + final short dataPrimitive = (Short) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == int.class) { + final int dataPrimitive = (Integer) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == long.class) { + final long dataPrimitive = (Long) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == float.class) { + final float dataPrimitive = (Float) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == double.class) { + final Double dataPrimitive = (Double) value; + propField.setExistingValue(data, dataPrimitive); + } else if (propField.getType() == boolean.class) { + final boolean dataPrimitive = (Boolean) value; + propField.setExistingValue(data, dataPrimitive); + } else { + @SuppressWarnings("unchecked") + final List tmpp = (List) value; + if (propField.getType().isArray()) { + if (propField.getType().componentType() == byte.class) { + final byte[] datas = ArraysTools.listByteToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == short.class) { + final short[] datas = ArraysTools.listShortToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == int.class) { + final int[] datas = ArraysTools.listIntegerToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == long.class) { + final long[] datas = ArraysTools.listLongToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == boolean.class) { + final boolean[] datas = ArraysTools.listBooleanToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == float.class) { + final float[] datas = ArraysTools.listFloatToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else if (propField.getType().componentType() == double.class) { + final double[] datas = ArraysTools.listDoubleToPrimitive(tmpp); + propField.setExistingValue(data, datas); + } else { + Log.verbose(" datas type: " + autoCast(propField.getType().componentType(), tmpp).getClass().getCanonicalName()); + Log.verbose(" methode type: " + propField.getType().getCanonicalName()); + propField.setExistingValue(data, autoCast(propField.getType().componentType(), tmpp)); + } + } else if (tmpp.size() == 1) { + propField.setExistingValue(data, tmpp.get(0)); + } else { + // impossible case ... + } + } + return; + } + } + throw new AknotException("can not find the field '" + beanName + "'"); + } + + @Override + public String toString(final Object data) throws AknotException { + if (this.tostring == null) { + if (StringSerializer.contains(this.classType)) { + throw new AknotException("function 'toString' for '" + this.classType.getCanonicalName() + "' is not defined and not registered for specific type"); + } + return StringSerializer.toString(data); + } + try { + return (String) this.tostring.invoke(data); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + if (Enum.class.isAssignableFrom(this.classType)) { + throw new AknotException( + "Error in call 'toString()' for '" + this.classType.getCanonicalName() + "' ==> '????' ... availlable list: " + Arrays.asList(this.classType.getEnumConstants())); + } + e.printStackTrace(); + throw new AknotException("Error in call 'toString()' for '" + this.classType.getCanonicalName() + "' " + e.getMessage()); + } + } + + private IntrospectionProperty updateForMethod(final String name, final Method method, final Class[] types) throws Exception { + IntrospectionProperty prop = findElement(name); + if (prop == null) { + final String[] names = ReflectTools.getNames(method, null); + prop = new IntrospectionProperty(name, types, names); + this.elements.add(prop); + } else { + final Class curentType = prop.getType(); + final Class curentSubType = prop.getSubType(); + if (curentType != types[0] && curentSubType != types[1]) { + throw new AknotException("Set 'return type' with a different value previous=" + curentType.getCanonicalName() + " / " + curentSubType.getCanonicalName() + " ==> new=" + types[0] + + " / " + types[1] + " in " + method.toGenericString()); + } + final String[] names = ReflectTools.getNames(method, null); + if (names != null) { + 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 AknotException("Set 'names' with a (already set!) " + method.toGenericString()); + } + prop.setNames(names); + } + } + final String description = ReflectTools.getDescription(method, null); + if (description != null) { + final String curentValue = prop.getListName(); + if (curentValue != null && curentValue != description) { + throw new AknotException("Set 'description' with a different value previous=" + curentValue + " ==> new=" + description + " in " + method.toGenericString()); + } + prop.setDescription(description); + } + final String listName = ReflectTools.getListName(method, null); + if (listName != null) { + final String curentValue = prop.getListName(); + if (curentValue != null && curentValue != listName) { + throw new AknotException("Set 'listNAme' with a different value previous=" + curentValue + " ==> new=" + listName + " in " + method.toGenericString()); + } + prop.setListName(listName); + } + final Boolean isAttribute = ReflectTools.getIsAttribute(method, null); + if (isAttribute != null) { + final Boolean curentValue = prop.isAttribute(); + if (curentValue != null && curentValue != isAttribute) { + throw new AknotException("Set 'attribute' with a different value previous=" + curentValue + " ==> new=" + isAttribute + " in " + method.toGenericString()); + } + prop.setAttribute(isAttribute); + } + final Boolean isManaged = ReflectTools.getIsManaged(method, null); + if (isManaged != null) { + final Boolean curentValue = prop.isManaged(); + if (curentValue != null && curentValue != isManaged) { + throw new AknotException("Set 'managed' with a different value previous=" + curentValue + " ==> new=" + isManaged + " in " + method.toGenericString()); + } + prop.setManaged(isManaged); + } + final Boolean isOptionnal = ReflectTools.getIsOptional(method, null); + if (isOptionnal != null) { + final Boolean curentValue = prop.isOptionnal(); + if (curentValue != null && curentValue != isOptionnal) { + throw new AknotException("Set 'optionnal' with a different value previous=" + curentValue + " ==> new=" + isOptionnal + " in " + method.toGenericString()); + } + prop.setOptionnal(isOptionnal); + } + final Boolean isCaseSensitive = ReflectTools.getIsCaseSensitive(method, null); + if (isCaseSensitive != null) { + final Boolean curentValue = prop.isCaseSensitive(); + if (curentValue != null && curentValue != isCaseSensitive) { + throw new AknotException("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 AknotException("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/aknot/pojo/IntrospectionModelFactory.java b/src/org/atriasoft/aknot/pojo/IntrospectionModelFactory.java new file mode 100644 index 0000000..07c2670 --- /dev/null +++ b/src/org/atriasoft/aknot/pojo/IntrospectionModelFactory.java @@ -0,0 +1,29 @@ +package org.atriasoft.aknot.pojo; + +import org.atriasoft.aknot.StringSerializer; +import org.atriasoft.aknot.exception.AknotException; +import org.atriasoft.aknot.model.IntrospectionModel; +import org.atriasoft.aknot.model.MapKey; + +public class IntrospectionModelFactory { + public static IntrospectionModel createModel(final MapKey modelType) throws AknotException { + if (StringSerializer.contains(modelType.type())) { + return new IntrospectionModelBaseType(modelType.type()); + } + return new IntrospectionModelComplex(modelType.type()); + } + + public static IntrospectionModel createModelArray(final String nodeName, final MapKey modelType) throws AknotException { + return new IntrospectionModelArray(nodeName, modelType.type()); + } + + public static IntrospectionModel createModelEnum(final MapKey modelType) throws AknotException { + return new IntrospectionModelComplex(modelType.type()); + } + + public static IntrospectionModel createModelList(final String nodeName, final MapKey modelType) throws AknotException { + return new IntrospectionModelList(nodeName, modelType.type()); + } + + private IntrospectionModelFactory() {} +} diff --git a/src/org/atriasoft/aknot/pojo/IntrospectionModelList.java b/src/org/atriasoft/aknot/pojo/IntrospectionModelList.java index 6c39b0c..a0004c8 100644 --- a/src/org/atriasoft/aknot/pojo/IntrospectionModelList.java +++ b/src/org/atriasoft/aknot/pojo/IntrospectionModelList.java @@ -17,11 +17,8 @@ public class IntrospectionModelList extends IntrospectionModel { } @Override - public Object createObject(final Map properties, final Map> nodes) throws AknotException { - if (this.nodeName == null) { - return nodes.get(STUPID_TOCKEN); - } - return nodes.get(this.nodeName); + public Object createObject(final Map properties, final Map> nodes, final boolean attributeIndependent) throws AknotException { + return nodes.get(this.getTokenBasicList()); } @Override @@ -32,6 +29,14 @@ public class IntrospectionModelList extends IntrospectionModel { return null; } + @Override + public String getTokenBasicList() { + if (this.nodeName == null) { + return STUPID_TOCKEN; + } + return this.nodeName; + } + @Override public Object getValue(final String propertyName, final String propertyValue) throws AknotException { return null;