package com.constellio.model.utils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jdom2.Element; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import com.constellio.model.entities.EnumWithSmallCode; import com.constellio.model.utils.ParametrizedInstanceUtilsRuntimeException.CannotInstanciate; import com.constellio.model.utils.ParametrizedInstanceUtilsRuntimeException.NoSuchConstructor; import com.constellio.model.utils.ParametrizedInstanceUtilsRuntimeException.UnsupportedArgument; public class ParametrizedInstanceUtils { public <T extends Parametrized> T toObject(Element element, Class<T> type) { T parent; Class parentClass; try { parentClass = Class.forName(element.getAttribute("name").getValue()); } catch (ClassNotFoundException e) { throw new CannotInstanciate(type.getName(), e); } try { List<Object> parameters = new ArrayList<>(); List<Class<?>> parameterClasses = new ArrayList<>(); this.getConstructorParameter(element, parameters, parameterClasses); Constructor parentConstructor = this.getConstructor(parentClass, parameterClasses); if (parentConstructor == null) { throw new NoSuchConstructor(type, parameterClasses, null); } parent = (T) parentConstructor.newInstance(parameters.toArray()); } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new CannotInstanciate(type.getName(), e); } return parent; } protected void getConstructorParameter(Element root, List<Object> parameters, List<Class<?>> parameterClasses) { for (Element child : root.getChildren()) { parameters.add(toObject(child)); } for (Object parameter : parameters) { if (parameter == null) { parameterClasses.add(null); } else { parameterClasses.add(classConvert(parameter.getClass())); } } } private Constructor getConstructor(Class parentClass, List<Class<?>> parameter) { for (Constructor constructor : parentClass.getConstructors()) { boolean sameParameter = true; Class[] parameterClass = constructor.getParameterTypes(); for (int i = 0; i < parameterClass.length; i++) { if (parameter.get(i) != null && !parameter.get(i).equals(parameterClass[i])) { sameParameter = false; break; } } if (sameParameter) { return constructor; } } return null; } public Object toObject(Element element) { try { if (element == null || "null".equals(element.getAttribute("type").getValue())) { return null; } Class childClass = Class.forName(element.getAttribute("type").getValue()); if (List.class.isAssignableFrom(childClass)) { return getListObject(element, childClass); } else if (Map.class.isAssignableFrom(childClass)) { return getMapObject(element, childClass); } else { return getObject(element, childClass); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { throw new CannotInstanciate(element.getAttribute("type").getValue(), ex); } } protected Object getObject(Element element, Class childClass) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Object object; String value = element.getText(); if (childClass.isAssignableFrom(LocalDateTime.class)) { object = LocalDateTime.parse(value); } else if (childClass.isAssignableFrom(LocalDate.class)) { object = LocalDate.parse(value); } else if (EnumWithSmallCode.class.isAssignableFrom(childClass)) { // ENUM CHECK object = EnumWithSmallCodeUtils.toEnum(childClass, value); } else { object = childClass.getConstructor(String.class).newInstance(value); } return object; } private Object getMapObject(Element element, Class childClass) throws InstantiationException, IllegalAccessException { Map<Object, Object> object = (HashMap) childClass.newInstance(); for (Element entry : element.getChildren()) { Object key = this.toObject(entry.getChild("key")); Element valueElement = entry.getChild("value"); Object value = valueElement == null ? null : this.toObject(valueElement); object.put(key, value); } return object; } private Object getListObject(Element element, Class childClass) throws InstantiationException, IllegalAccessException { List<Object> object = (List) childClass.newInstance(); for (Element item : element.getChildren()) { object.add(this.toObject(item)); } return object; } public Element toElement(Parametrized parametrized, String elementTag) { Element root = new Element(elementTag); root.setAttribute("name", parametrized.getClass().getName()); Object[] parameters = parametrized.getInstanceParameters(); for (Object parameter : parameters) { if (parameter == null) { Element child = new Element("argument"); child.setAttribute("type", "null"); root.addContent(child); continue; } if (!isValid(parameter.getClass())) { throw new UnsupportedArgument(parameter.getClass()); } this.toElement(parameter, root, "argument"); } return root; } public void toElement(Object parameter, Element parent, String elementTag) { Element child = new Element(elementTag); child.setAttribute("type", parameter.getClass().getName()); if (List.class.isAssignableFrom(parameter.getClass())) { child.setAttribute("type", ArrayList.class.getName()); List<Object> listParameter = (List<Object>) parameter; for (Object item : listParameter) { this.toElement(item, child, "item"); } } else if (Map.class.isAssignableFrom(parameter.getClass())) { Map<Object, Object> mapParameter = (Map<Object, Object>) parameter; for (Object entryKey : mapParameter.keySet()) { Element entryElement = new Element("entry"); toElement(entryKey, entryElement, "key"); Object value = mapParameter.get(entryKey); if (value != null) { toElement(value, entryElement, "value"); } child.addContent(entryElement); } } else { if (!isValid(parameter.getClass())) { throw new UnsupportedArgument(parameter.getClass()); } if (EnumWithSmallCode.class.isAssignableFrom(parameter.getClass())) { child.setText("" + EnumWithSmallCodeUtils.toSmallCode((EnumWithSmallCode) parameter)); } else { child.setText("" + parameter.toString()); } } parent.addContent(child); } private Class classConvert(Class current) { Class newClass; if (List.class.isAssignableFrom(current)) { newClass = List.class; } else if (Map.class.isAssignableFrom(current)) { newClass = Map.class; } else { newClass = current; } return newClass; } private boolean isValid(Class clazz) { return Map.class.isAssignableFrom(clazz) || List.class.isAssignableFrom(clazz) || Integer.class.equals(clazz) || String.class.equals(clazz) || Double.class.equals(clazz) || Float.class.equals(clazz) || LocalDateTime.class.equals(clazz) || LocalDate.class.equals(clazz) || Boolean.class.equals(clazz) || Long.class.equals(clazz) || Enum.class.isAssignableFrom(clazz); } }