880 lines
36 KiB
Java
880 lines
36 KiB
Java
package org.atriasoft.exml.builder;
|
|
|
|
import java.lang.annotation.Annotation;
|
|
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.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.atriasoft.eStringSerialize.StringSerializer;
|
|
import org.atriasoft.etk.util.ArraysTools;
|
|
import org.atriasoft.exml.annotation.XmlAttribute;
|
|
import org.atriasoft.exml.annotation.XmlCaseSensitive;
|
|
import org.atriasoft.exml.annotation.XmlDefaultAttibute;
|
|
import org.atriasoft.exml.annotation.XmlDefaultCaseSensitive;
|
|
import org.atriasoft.exml.annotation.XmlDefaultManaged;
|
|
import org.atriasoft.exml.annotation.XmlDefaultOptional;
|
|
import org.atriasoft.exml.annotation.XmlList;
|
|
import org.atriasoft.exml.annotation.XmlManaged;
|
|
import org.atriasoft.exml.annotation.XmlName;
|
|
import org.atriasoft.exml.annotation.XmlOptional;
|
|
import org.atriasoft.exml.exception.ExmlBuilderException;
|
|
import org.atriasoft.exml.internal.Log;
|
|
import org.atriasoft.exml.parser.Tools;
|
|
|
|
|
|
public class IntrospectionModelComplex extends IntrospectionModel {
|
|
|
|
// 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 List<IntrospectionProperty> nodes = new ArrayList<>();
|
|
|
|
private final List<IntrospectionProperty> attributes = new ArrayList<>();
|
|
|
|
|
|
@Override
|
|
public List<IntrospectionProperty> getNodes() {
|
|
return this.nodes;
|
|
}
|
|
@Override
|
|
public List<IntrospectionProperty> getAttributes() {
|
|
return this.attributes;
|
|
}
|
|
|
|
public IntrospectionModelComplex(final Class<?> classType) throws ExmlBuilderException {
|
|
super(classType);
|
|
try {
|
|
final Boolean isDefaultAttribute = getIsDefaultAttribute(classType, IntrospectionModel.DEFAULT_ATTRIBUTE);
|
|
final Boolean isDefaultManaged = getIsDefaultManaged(classType, IntrospectionModel.DEFAULT_MANAGED);
|
|
final Boolean isDefaultOptional = getIsDefaultOptional(classType, IntrospectionModel.DEFAULT_OPTIONAL);
|
|
final Boolean isDefaultCaseSensitive = getIsDefaultCaseSensitive(classType, IntrospectionModel.DEFAULT_CASE_SENSITIVE);
|
|
Log.verbose("Introspect class: '" + classType.getCanonicalName() + "'");
|
|
final Constructor<?>[] constructors = this.classType.getConstructors();
|
|
Log.verbose(" Constructors: (" + constructors.length + ")");
|
|
for (final Constructor<?> elem : constructors) {
|
|
Log.verbose(" - " + elem.toGenericString());
|
|
}
|
|
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
|
|
if (!Modifier.isPublic(elem.getModifiers())) {
|
|
continue;
|
|
}
|
|
final Boolean isAttribute = getIsAttribute(elem, isDefaultAttribute);
|
|
final Boolean isManaged = getIsManaged(elem, isDefaultManaged);
|
|
final Boolean isOptionnal = getIsOptional(elem, isDefaultOptional);
|
|
final String[] names = getNames(elem, Tools.decapitalizeFirst(elem.getName()));
|
|
final Boolean caseSensitive = getIsCaseSensitive(elem, isDefaultCaseSensitive);
|
|
final String listName = getListName(elem, null);
|
|
// TODO check if property does not already exist ...
|
|
if (isManaged) {
|
|
if (isAttribute) {
|
|
this.attributes.add(new IntrospectionPropertyField(elem, names, listName, caseSensitive, isOptionnal));
|
|
} else {
|
|
this.nodes.add(new IntrospectionPropertyField(elem, names, listName, caseSensitive, isOptionnal));
|
|
}
|
|
}
|
|
Log.verbose(" - " + elem.toGenericString());
|
|
}
|
|
final Method[] methodsTmp = this.classType.getMethods();
|
|
// filter getX setX isX
|
|
final List<Method> methods = List.of(methodsTmp).stream().filter(o -> {
|
|
if (o.getName().contentEquals("getClass")) {
|
|
return false;
|
|
}
|
|
// we does not manage private function
|
|
if (!Modifier.isPublic(o.getModifiers())) {
|
|
return false;
|
|
}
|
|
if (Modifier.isStatic(o.getModifiers())) {
|
|
if (o.getName().contentEquals("valueOf") && o.getParameterCount() == 1 && o.getParameters()[0].getType() == String.class) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (o.getName().startsWith("get")) {
|
|
if (o.getParameterCount() != 0 || o.getReturnType() == void.class || o.getReturnType() == Boolean.class || o.getReturnType() == boolean.class) {
|
|
return false;
|
|
}
|
|
// check name format
|
|
if (o.getName().length() == 3) {
|
|
return false;
|
|
}
|
|
if (o.getName().charAt(3) >= 'A' && o.getName().charAt(3) <= 'Z') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (o.getName().startsWith("set")) {
|
|
if (o.getReturnType() != void.class || o.getParameterCount() != 1) {
|
|
return false;
|
|
}
|
|
// check name format
|
|
if (o.getName().length() == 3) {
|
|
return false;
|
|
}
|
|
if (o.getName().charAt(3) >= 'A' && o.getName().charAt(3) <= 'Z') {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
if (o.getName().startsWith("is")) {
|
|
if (!(o.getReturnType() == Boolean.class || o.getReturnType() == boolean.class) && o.getParameterCount() != 0) {
|
|
return false;
|
|
}
|
|
// check name format
|
|
if (o.getName().length() == 2) {
|
|
return false;
|
|
}
|
|
if (o.getName().charAt(2) >= 'A' && o.getName().charAt(2) <= 'Z') {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
return false;
|
|
}).collect(Collectors.toList());
|
|
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();
|
|
|
|
List<Method> methodsGet;
|
|
List<Method> methodsSet;
|
|
List<Method> methodsIs;
|
|
if (!Enum.class.isAssignableFrom(classType)) {
|
|
methodsGet = methods.stream().filter(o -> o.getName().startsWith("get")).collect(Collectors.toList());
|
|
methodsSet = methods.stream().filter(o -> o.getName().startsWith("set")).collect(Collectors.toList());
|
|
methodsIs = methods.stream().filter(o -> o.getName().startsWith("is")).collect(Collectors.toList());
|
|
} else {
|
|
methodsGet = new ArrayList<>();
|
|
methodsSet = new ArrayList<>();
|
|
methodsIs = new ArrayList<>();
|
|
}
|
|
final List<Method> valueOfString = methods.stream().filter(o -> o.getName().startsWith("valueOf")).collect(Collectors.toList());
|
|
if (valueOfString.size() == 1) {
|
|
this.valueof = valueOfString.get(0);
|
|
} else {
|
|
// some specific model:
|
|
|
|
this.valueof = null;
|
|
}
|
|
// associate methods by pair.
|
|
final List<OrderData> elements = new ArrayList<>();
|
|
for (final Method method : methodsGet) {
|
|
final String name = method.getName().substring(3);
|
|
final OrderData tmp = new OrderData(name);
|
|
tmp.getter = method;
|
|
elements.add(tmp);
|
|
}
|
|
for (final Method method : methodsIs) {
|
|
final String name = method.getName().substring(2);
|
|
for (final OrderData elem : elements) {
|
|
if (elem.name.contentEquals(name)) {
|
|
Log.error("Can not have a setXXX and isXXX with the same name ... " + method.getName());
|
|
throw new Exception("lmkjlkjlk");
|
|
}
|
|
}
|
|
final OrderData tmp = new OrderData(name);
|
|
tmp.getter = method;
|
|
elements.add(tmp);
|
|
}
|
|
for (final Method method : methodsSet) {
|
|
final String name = method.getName().substring(3);
|
|
OrderData tmp = null;
|
|
for (final OrderData elem : elements) {
|
|
if (elem.name.contentEquals(name)) {
|
|
tmp = elem;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
Class<?> internalModelClass = null;
|
|
Class<?>[] tmppp = method.getParameterTypes();
|
|
if (tmppp.length > 0 && (List.class.isAssignableFrom(tmppp[0]))) {
|
|
Log.warning(" * " + method.getName());
|
|
Type[] empppe = method.getGenericParameterTypes();
|
|
if (empppe.length > 0) {
|
|
if (empppe[0] instanceof ParameterizedType plopppppp) {
|
|
Type[] realType = plopppppp.getActualTypeArguments();
|
|
if (realType.length > 0) {
|
|
Log.warning(" -->> " + realType[0]);
|
|
internalModelClass = Class.forName(realType[0].getTypeName());
|
|
}
|
|
}
|
|
}
|
|
for (int iii=0; iii<tmppp.length; iii++) {
|
|
Log.warning(" -- " + tmppp[iii].getCanonicalName());
|
|
}
|
|
}
|
|
*/
|
|
if (tmp == null) {
|
|
tmp = new OrderData(name);
|
|
tmp.setter = method;
|
|
elements.add(tmp);
|
|
} else {
|
|
tmp.setter = method;
|
|
}
|
|
}
|
|
// Add it in the engine
|
|
for (final OrderData elem : elements) {
|
|
Log.info("find methode : '" + elem.name + "' :");
|
|
if (elem.setter != null && elem.getter != null) {
|
|
Log.info(" setter: " + elem.setter.toGenericString());
|
|
Log.info(" getter: " + elem.getter.toGenericString());
|
|
final Boolean isAttributeSet = getIsAttribute(elem.setter, null);
|
|
final Boolean isAttributeGet = getIsAttribute(elem.getter, null);
|
|
if (isAttributeSet != null && isAttributeGet != null && isAttributeSet != isAttributeGet) {
|
|
throw new Exception("Can net set oposite information on getter and setter");
|
|
}
|
|
final Boolean isAttribute = isAttributeSet != null ? isAttributeSet : isAttributeGet != null ? isAttributeGet : isDefaultAttribute;
|
|
|
|
final Boolean isManagedSet = getIsManaged(elem.setter, null);
|
|
final Boolean isManagedGet = getIsManaged(elem.getter, null);
|
|
if (isManagedSet != null && isManagedGet != null && isManagedSet != isManagedGet) {
|
|
throw new Exception("Can net set oposite information on getter and setter");
|
|
}
|
|
final Boolean isManaged = isManagedSet != null ? isManagedSet : isManagedGet != null ? isManagedGet : isDefaultManaged;
|
|
|
|
final Boolean isOptionnalSet = getIsOptional(elem.setter, null);
|
|
final Boolean isOptionnalGet = getIsOptional(elem.getter, null);
|
|
if (isOptionnalSet != null && isOptionnalGet != null && isOptionnalSet != isOptionnalGet) {
|
|
throw new Exception("Can net set oposite information on getter and setter");
|
|
}
|
|
final Boolean isOptional = isOptionnalSet != null ? isOptionnalSet : isOptionnalGet != null ? isOptionnalGet : isDefaultOptional;
|
|
|
|
final Boolean caseSensitiveSet = getIsCaseSensitive(elem.setter, null);
|
|
final Boolean caseSensitiveGet = getIsCaseSensitive(elem.getter, null);
|
|
if (caseSensitiveSet != null && caseSensitiveGet != null && caseSensitiveSet != caseSensitiveGet) {
|
|
throw new Exception("Can net set oposite information on getter and setter");
|
|
}
|
|
|
|
final Boolean isCaseSensitive = caseSensitiveSet != null ? caseSensitiveSet : caseSensitiveGet != null ? caseSensitiveGet : isDefaultCaseSensitive;
|
|
|
|
final String[] namesSet = getNames(elem.setter, null);
|
|
final String[] namesGet = getNames(elem.getter, null);
|
|
if (namesSet != null && namesGet != null && namesSet.equals(namesGet)) {
|
|
throw new Exception("Can net set oposite information on getter and setter");
|
|
}
|
|
final String[] names = namesSet != null ? namesSet : namesGet != null ? namesGet : new String[] { Tools.decapitalizeFirst(elem.name) };
|
|
final String listNameSet = getListName(elem.setter, null);
|
|
final String listNameGet = getListName(elem.getter, null);
|
|
final String listName = listNameSet != null? listNameSet: listNameGet;
|
|
if (isAttribute) {
|
|
this.attributes.add(new IntrospectionPropertyMethod(elem.setter, elem.getter, names, listName, isCaseSensitive, isOptional));
|
|
} else {
|
|
this.nodes.add(new IntrospectionPropertyMethod(elem.setter, elem.getter, names, listName, isCaseSensitive, isOptional));
|
|
}
|
|
} else {
|
|
Boolean isManaged = null;
|
|
Boolean isOptionnal = null;
|
|
String[] names = null;
|
|
Boolean isCaseSensitive = null;
|
|
String listName = null;
|
|
Boolean isAttribute = isDefaultAttribute;
|
|
if (elem.setter != null) {
|
|
Log.info(" setter: " + elem.setter.toGenericString());
|
|
isAttribute = getIsAttribute(elem.setter, isDefaultAttribute);
|
|
isManaged = getIsManaged(elem.setter, isDefaultManaged);
|
|
isOptionnal = getIsOptional(elem.setter, isDefaultOptional);
|
|
names = getNames(elem.setter, Tools.decapitalizeFirst(elem.name));
|
|
isCaseSensitive = getIsCaseSensitive(elem.setter, isDefaultCaseSensitive);
|
|
listName= getListName(elem.setter, null);
|
|
} else {
|
|
Log.info(" setter: null");
|
|
}
|
|
if (elem.getter != null) {
|
|
Log.info(" getter: " + elem.getter.toGenericString());
|
|
isAttribute = getIsAttribute(elem.getter, isDefaultAttribute);
|
|
isManaged = getIsManaged(elem.getter, isDefaultManaged);
|
|
isOptionnal = getIsOptional(elem.getter, isDefaultOptional);
|
|
names = getNames(elem.getter, Tools.decapitalizeFirst(elem.name));
|
|
isCaseSensitive = getIsCaseSensitive(elem.getter, isDefaultCaseSensitive);
|
|
listName= getListName(elem.getter, null);
|
|
} else {
|
|
Log.info(" getter: null");
|
|
}
|
|
if (isManaged) {
|
|
if (isAttribute) {
|
|
this.attributes.add(new IntrospectionPropertyMethod(elem.setter, elem.getter, names, listName, isCaseSensitive, isOptionnal));
|
|
} else {
|
|
this.nodes.add(new IntrospectionPropertyMethod(elem.setter, elem.getter, names, listName, isCaseSensitive, isOptionnal));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
throw new ExmlBuilderException("Error in creating introspection data ... " + ex.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
Object createObject(final Map<String, Object> properties, final Map<String, List<Object>> nodes) throws ExmlBuilderException {
|
|
Object tmp;
|
|
try {
|
|
// pb here, can not create a primitive object with the correct elements... ==> must be generated with a siblist of elements
|
|
|
|
Constructor<?>[] constructors = this.classType.getConstructors();
|
|
Object tmp2 = null;
|
|
tmp = constructors[0].newInstance(tmp2);
|
|
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
return null;
|
|
}
|
|
for (Entry<String, Object> elem : properties.entrySet()) {
|
|
setValue(tmp, elem.getKey(), elem.getValue());
|
|
}
|
|
for (Entry<String, List<Object>> elem : nodes.entrySet()) {
|
|
setValue(tmp, elem.getKey(), elem.getValue());
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
|
|
@Override
|
|
protected List<String> getNodeAvaillable() {
|
|
List<String> out = new ArrayList<>();
|
|
for (final IntrospectionProperty prop : this.nodes) {
|
|
out.add(prop.getNames()[0]);
|
|
}
|
|
// for (final IntrospectionProperty prop : this.attributes) {
|
|
// out.add(prop.getNames()[0]);
|
|
// }
|
|
return out;
|
|
}
|
|
protected IntrospectionProperty findNodeDescription(final String propertyName) throws ExmlBuilderException {
|
|
for (final IntrospectionProperty prop : this.nodes) {
|
|
if (prop.isCompatible(propertyName)) {
|
|
return prop;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected IntrospectionProperty findPropertyDescription(final String propertyName) throws ExmlBuilderException {
|
|
for (final IntrospectionProperty prop : this.attributes) {
|
|
if (prop.isCompatible(propertyName)) {
|
|
return prop;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected Boolean getIsCaseSensitive(final Field element, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlCaseSensitive.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlCaseSensitive) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsCaseSensitive(final Method element, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlCaseSensitive.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlCaseSensitive) annotation[0]).value();
|
|
}
|
|
|
|
private Boolean getIsDefaultCaseSensitive(final Class<?> classType, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultCaseSensitive.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlDefaultCaseSensitive) annotation[0]).value();
|
|
}
|
|
|
|
private Boolean getIsDefaultAttribute(final Class<?> classType, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultAttibute.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlDefaultAttibute) annotation[0]).value();
|
|
}
|
|
|
|
private Boolean getIsDefaultManaged(final Class<?> classType, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultManaged.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlDefaultManaged) annotation[0]).value();
|
|
}
|
|
|
|
private Boolean getIsDefaultOptional(final Class<?> classType, final Boolean defaultValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = classType.getDeclaredAnnotationsByType(XmlDefaultOptional.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlDefaultOptional) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsAttribute(final Field element, final Boolean parentValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlAttribute.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlAttribute) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsAttribute(final Method element, final Boolean parentValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlAttribute.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlAttribute) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsManaged(final Field element, final Boolean parentValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlManaged.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlManaged) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsManaged(final Method element, final Boolean parentValue) throws ExmlBuilderException {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlManaged.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new ExmlBuilderException("Must not have more that ");
|
|
}
|
|
return ((XmlManaged) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsOptional(final Field element, final Boolean parentValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlOptional.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
return ((XmlOptional) annotation[0]).value();
|
|
}
|
|
|
|
protected Boolean getIsOptional(final Method element, final Boolean parentValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlOptional.class);
|
|
if (annotation.length == 0) {
|
|
return parentValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
return ((XmlOptional) annotation[0]).value();
|
|
}
|
|
|
|
protected String[] getNames(final Field element, final String defaultValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlName.class);
|
|
if (annotation.length == 0) {
|
|
if (defaultValue == null) {
|
|
return null;
|
|
}
|
|
return new String[] { defaultValue };
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
final String[] tmp = ((XmlName) annotation[0]).value();
|
|
if (tmp == null) {
|
|
throw new Exception("Set null value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
if (tmp.length == 0) {
|
|
throw new Exception("Set empty list value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
for (final String elem : tmp) {
|
|
if (elem == null) {
|
|
throw new Exception("Set null String in list of value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
if (elem.isEmpty()) {
|
|
throw new Exception("Set empty String in list of value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
protected String[] getNames(final Method element, final String defaultValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlName.class);
|
|
if (annotation.length == 0) {
|
|
if (defaultValue == null) {
|
|
return null;
|
|
}
|
|
return new String[] { defaultValue };
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
final String[] tmp = ((XmlName) annotation[0]).value();
|
|
if (tmp == null) {
|
|
throw new Exception("Set null value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
if (tmp.length == 0) {
|
|
throw new Exception("Set empty list value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
for (final String elem : tmp) {
|
|
if (elem == null) {
|
|
throw new Exception("Set null String in list of value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
if (elem.isEmpty()) {
|
|
throw new Exception("Set empty String in list of value in decorator @XmlName is not availlable on: " + element.toGenericString());
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
protected String getListName(final Field element, final String defaultValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlList.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
final String tmp = ((XmlList) annotation[0]).value();
|
|
if (tmp == null) {
|
|
throw new Exception("Set null value in decorator @XmlList is not availlable on: " + element.toGenericString());
|
|
}
|
|
return tmp;
|
|
}
|
|
protected String getListName(final Method element, final String defaultValue) throws Exception {
|
|
final Annotation[] annotation = element.getDeclaredAnnotationsByType(XmlList.class);
|
|
if (annotation.length == 0) {
|
|
return defaultValue;
|
|
}
|
|
if (annotation.length > 1) {
|
|
throw new Exception("Must not have more that ");
|
|
}
|
|
final String tmp = ((XmlList) annotation[0]).value();
|
|
if (tmp == null) {
|
|
throw new Exception("Set null value in decorator @XmlList is not availlable on: " + element.toGenericString());
|
|
}
|
|
return tmp;
|
|
}
|
|
@SuppressWarnings("unchecked")
|
|
private <T> T[] autoCast(final Class<T> clazz, final List<Object> data) {
|
|
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 void setValue(final Object data, final String name, final Object value) throws ExmlBuilderException {
|
|
Log.error(" Set value ='" + name + "' propertyValue='" + value + "' " + value.getClass().getCanonicalName());
|
|
// by default use setter to set the property
|
|
final IntrospectionProperty propMethode = findNodeDescription(name);
|
|
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")
|
|
List<Object> tmpp = (List<Object>)value;
|
|
if (propMethode.getType().isArray()) {
|
|
if (propMethode.getType().componentType() == byte.class) {
|
|
byte[] datas = ArraysTools.listByteToPrimitive(tmpp);
|
|
propMethode.setExistingValue(data, datas);
|
|
} else if (propMethode.getType().componentType() == short.class) {
|
|
short[] datas = ArraysTools.listShortToPrimitive(tmpp);
|
|
propMethode.setExistingValue(data, datas);
|
|
} else if (propMethode.getType().componentType() == int.class) {
|
|
int[] datas = ArraysTools.listIntegerToPrimitive(tmpp);
|
|
propMethode.setExistingValue(data, datas);
|
|
} else if (propMethode.getType().componentType() == long.class) {
|
|
long[] datas = ArraysTools.listLongToPrimitive(tmpp);
|
|
propMethode.setExistingValue(data, datas);
|
|
} else if (propMethode.getType().componentType() == boolean.class) {
|
|
boolean[] datas = ArraysTools.listBooleanToPrimitive(tmpp);
|
|
propMethode.setExistingValue(data, datas);
|
|
} 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) {
|
|
byte dataPrimitive = (Byte)value;
|
|
propMethode.setExistingValue(data, dataPrimitive);
|
|
} else if (propMethode.getType() == short.class) {
|
|
short dataPrimitive = (Short)value;
|
|
propMethode.setExistingValue(data, dataPrimitive);
|
|
} else if (propMethode.getType() == int.class) {
|
|
int dataPrimitive = (Integer)value;
|
|
propMethode.setExistingValue(data, dataPrimitive);
|
|
} else if (propMethode.getType() == long.class) {
|
|
long dataPrimitive = (Long)value;
|
|
propMethode.setExistingValue(data, dataPrimitive);
|
|
} else if (propMethode.getType() == boolean.class) {
|
|
boolean dataPrimitive = (Boolean)value;
|
|
propMethode.setExistingValue(data, dataPrimitive);
|
|
} else {
|
|
|
|
}
|
|
return;
|
|
}
|
|
// try with direct field
|
|
final IntrospectionProperty propField = findPropertyDescription(name);
|
|
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) {
|
|
byte dataPrimitive = (Byte)value;
|
|
propField.setExistingValue(data, dataPrimitive);
|
|
} else if (propField.getType() == short.class) {
|
|
short dataPrimitive = (Short)value;
|
|
propField.setExistingValue(data, dataPrimitive);
|
|
} else if (propField.getType() == int.class) {
|
|
int dataPrimitive = (Integer)value;
|
|
propField.setExistingValue(data, dataPrimitive);
|
|
} else if (propField.getType() == long.class) {
|
|
long dataPrimitive = (Long)value;
|
|
propField.setExistingValue(data, dataPrimitive);
|
|
} else if (propField.getType() == boolean.class) {
|
|
boolean dataPrimitive = (Boolean)value;
|
|
propField.setExistingValue(data, dataPrimitive);
|
|
} else {
|
|
@SuppressWarnings("unchecked")
|
|
List<Object> tmpp = (List<Object>)value;
|
|
if (propField.getType().isArray()) {
|
|
if (propField.getType().componentType() == byte.class) {
|
|
byte[] datas = ArraysTools.listByteToPrimitive(tmpp);
|
|
propField.setExistingValue(data, datas);
|
|
} else if (propField.getType().componentType() == short.class) {
|
|
short[] datas = ArraysTools.listShortToPrimitive(tmpp);
|
|
propField.setExistingValue(data, datas);
|
|
} else if (propField.getType().componentType() == int.class) {
|
|
int[] datas = ArraysTools.listIntegerToPrimitive(tmpp);
|
|
propField.setExistingValue(data, datas);
|
|
} else if (propField.getType().componentType() == long.class) {
|
|
long[] datas = ArraysTools.listLongToPrimitive(tmpp);
|
|
propField.setExistingValue(data, datas);
|
|
} else if (propField.getType().componentType() == boolean.class) {
|
|
boolean[] datas = ArraysTools.listBooleanToPrimitive(tmpp);
|
|
propField.setExistingValue(data, datas);
|
|
} else {
|
|
Log.verbose(" datas type: " + autoCast(propMethode.getType().componentType(), tmpp).getClass().getCanonicalName());
|
|
Log.verbose(" methode type: " + propMethode.getType().getCanonicalName());
|
|
propField.setExistingValue(data, autoCast(propMethode.getType().componentType(), tmpp));
|
|
}
|
|
} else if (tmpp.size() == 1) {
|
|
propField.setExistingValue(data, tmpp.get(0));
|
|
} else {
|
|
// impossible case ...
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
throw new ExmlBuilderException("can not find the field '" + name + "'");
|
|
|
|
}
|
|
|
|
/**
|
|
* Detect a subNode, and ask the type of the node at the parent Class
|
|
* @param nodeName Name of the node
|
|
* @return Class of the node to create
|
|
*/
|
|
@Override
|
|
public Class<?> getTypeOfSubNode(final Object data, final String nodeName) throws ExmlBuilderException {
|
|
Log.error(" nodeType='" + nodeName + "'");
|
|
// by default use setter to set the property
|
|
final IntrospectionProperty propMethode = findNodeDescription(nodeName);
|
|
if (propMethode != null && propMethode.canSetValue()) {
|
|
Log.error(" ==> find '" + propMethode.getNames());
|
|
return propMethode.getType();
|
|
}
|
|
// try with direct field
|
|
final IntrospectionProperty propField = findPropertyDescription(nodeName);
|
|
if (propField != null && propField.canSetValue()) {
|
|
Log.error(" ==> find '" + propField.getNames());
|
|
return propMethode.getType();
|
|
}
|
|
|
|
throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable());
|
|
}
|
|
@Override
|
|
public Class<?> getTypeOfSubNodeList(final Object data, final String nodeName) throws ExmlBuilderException {
|
|
Log.error(" nodeType='" + nodeName + "'");
|
|
// by default use setter to set the property
|
|
final IntrospectionProperty propMethode = findNodeDescription(nodeName);
|
|
if (propMethode != null && propMethode.canSetValue()) {
|
|
Log.error(" ==> find '" + propMethode.getNames());
|
|
return propMethode.getSubType();
|
|
}
|
|
// try with direct field
|
|
final IntrospectionProperty propField = findPropertyDescription(nodeName);
|
|
if (propField != null && propField.canSetValue()) {
|
|
Log.error(" ==> find '" + propField.getNames());
|
|
return propMethode.getSubType();
|
|
}
|
|
throw new ExmlBuilderException("can not find the field '" + nodeName + "' availlable: " + getNodeAvaillable());
|
|
}
|
|
@Override
|
|
public String getTreeNameOfSubNode(final Object data, final String nodeName) throws ExmlBuilderException {
|
|
Log.error(" nodeType='" + nodeName + "'");
|
|
// by default use setter to set the property
|
|
final IntrospectionProperty propMethode = findNodeDescription(nodeName);
|
|
if (propMethode != null && propMethode.canSetValue()) {
|
|
Log.error(" ==> find '" + propMethode.getNames());
|
|
return propMethode.getListName();
|
|
}
|
|
// try with direct field
|
|
final IntrospectionProperty propField = findPropertyDescription(nodeName);
|
|
if (propField != null && propField.canSetValue()) {
|
|
Log.error(" ==> find '" + propField.getNames());
|
|
return propMethode.getListName();
|
|
}
|
|
throw new ExmlBuilderException("can not find the field '" + nodeName + "'");
|
|
}
|
|
|
|
|
|
@Override
|
|
public Object getValueFromText(final String text) throws ExmlBuilderException {
|
|
// 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.warning("======>>>>>>> Get input type : " + this.classType.getCanonicalName());
|
|
//Log.warning("======>>>>>>> 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.warning("======>>>>>>> subElement input type : " + classTypeLocal.getCanonicalName());
|
|
|
|
if (this.valueof == null) {
|
|
if (StringSerializer.contains(classTypeLocal)) {
|
|
throw new ExmlBuilderException("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 ExmlBuilderException("Error in call 'valueOf(String ...)' for enum '" + classTypeLocal.getCanonicalName() + "' ==> '" + text + "' ... availlable list: " + Arrays.asList(this.classType.getEnumConstants()));
|
|
}
|
|
e.printStackTrace();
|
|
throw new ExmlBuilderException("Error in call 'valueOf(String ...)' for '" + classTypeLocal.getCanonicalName() + "' " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Object getValue(final String propertyName, final String propertyValue) throws ExmlBuilderException {
|
|
Log.error(" 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 ExmlBuilderException("can not find the field '" + propertyName + "'");
|
|
}
|
|
@Override
|
|
public boolean isEndPoint() {
|
|
return false;
|
|
}
|
|
@Override
|
|
public String toString(final Object data) {
|
|
return null;
|
|
}
|
|
@Override
|
|
public String[] toStringList(final Object data) {
|
|
// TODO Auto-generated method stub
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
class OrderData {
|
|
public Method getter = null;
|
|
public final String name;
|
|
public Method setter = null;
|
|
|
|
public OrderData(final String name) {
|
|
this.name = name;
|
|
}
|
|
}
|