package pluginbase.config.field; import pluginbase.config.annotation.FauxEnum; import pluginbase.config.annotation.IgnoreSuperFields; import org.jetbrains.annotations.NotNull; import pluginbase.config.annotation.SerializeWith; import pluginbase.config.serializers.SerializerSet; import pluginbase.config.util.PrimitivesUtil; import pluginbase.logging.Logging; import java.lang.reflect.Modifier; import java.util.*; public class FieldMapper { private static final Map<Class, FieldMap> compiledFieldMaps = new HashMap<Class, FieldMap>(); private final Class clazz; public static <T> T mapFields(T source, T destination) { FieldMap sourceMap = getFieldMap(source.getClass()); destination = mapFields(sourceMap, source, destination); return destination; } private static <T> T mapFields(FieldMap sourceMap, T source, T destination) { for (Field field : sourceMap) { if (field.hasChildFields()) { Object sourceChild = field.getValue(source); Object destinationChild = field.getValue(destination); if (destinationChild == null) { try { field.setValue(destination, sourceChild); } catch (PropertyVetoException e) { e.printStackTrace(); } } else { mapFields(field, sourceChild, destinationChild); } } else { try { field.setValue(destination, field.getValue(source)); } catch (PropertyVetoException e) { e.printStackTrace(); } } } return destination; } public static FieldMap getFieldMap(@NotNull Class clazz) { if (compiledFieldMaps.containsKey(clazz)) { return compiledFieldMaps.get(clazz); } FieldMapper fieldMapper = new FieldMapper(clazz); FieldMap fieldMap = new FieldMap(fieldMapper.mapFields()); compiledFieldMaps.put(clazz, fieldMap); return fieldMap; } private FieldMapper(@NotNull Class clazz) { this.clazz = clazz; } private Map<String, Field> mapFields() { Logging.finer("Mapping fields for %s", this); java.lang.reflect.Field[] allFields = collectAllFieldsForClass(clazz); Map<String, Field> resultMap = new LinkedHashMap<String, Field>(allFields.length); for (java.lang.reflect.Field field : allFields) { Class fieldType = PrimitivesUtil.switchForWrapper(field.getType()); Logging.finest("Mapping %s of type %s", field, fieldType); if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers())) { Field localField; if (Map.class.isAssignableFrom(fieldType) || Collection.class.isAssignableFrom(fieldType) || Enum.class.isAssignableFrom(fieldType) || field.getType().isAnnotationPresent(FauxEnum.class) || field.isAnnotationPresent(SerializeWith.class) || field.getType().isArray() || SerializerSet.defaultSet().hasSerializerForClass(fieldType) || VirtualField.class.isAssignableFrom(fieldType)) { localField = new Field(field); } else { if (fieldType.equals(clazz)) { throw new IllegalStateException("Mapping fields for " + clazz + " would result in infinite recursion due self containment."); } localField = new Field(field, getFieldMap(fieldType)); } resultMap.put(localField.getName().toLowerCase(), localField); } } return resultMap; } private java.lang.reflect.Field[] collectAllFieldsForClass(Class clazz) { java.lang.reflect.Field[] declaredFields = clazz.getDeclaredFields(); if (clazz.getAnnotation(IgnoreSuperFields.class) != null) { return declaredFields; } Class superClass = clazz.getSuperclass(); if (superClass != null) { java.lang.reflect.Field[] superFields = collectAllFieldsForClass(superClass); int length = declaredFields.length; declaredFields = Arrays.copyOf(declaredFields, declaredFields.length + superFields.length); System.arraycopy(superFields, 0, declaredFields, length, superFields.length); } return declaredFields; } @Override public String toString() { return "FieldMapper{" + "clazz=" + clazz + '}'; } }