001package arcommon.xml;
002
003import java.lang.reflect.Field;
004import java.util.HashMap;
005import java.util.Map;
006
007import arcommon.ReflectionUtil;
008import arcommon.persistens.AbstractSettingWithFrameDialogAndTableSettings;
009import arcommon.persistens.AbstractWindowSettings;
010import arcommon.persistens.DialogSettings;
011import arcommon.persistens.FrameSettings;
012import arcommon.persistens.MyJTableSettings;
013import arcommon.persistens.PlatformDependentString;
014
015public class XmlDeserializerMapper {
016    public static enum LogLevel {ERROR, WARNING, INFO, IGNORE};
017    
018    public static LogLevel classMissingLogLevel = LogLevel.WARNING;
019    public static LogLevel classMappedLogLevel = LogLevel.WARNING;
020    public static LogLevel fieldMissingLogLevel = LogLevel.WARNING;
021    public static LogLevel fieldMappedLogLevel = LogLevel.WARNING;
022    public static LogLevel fieldTypeMismatchLogLevel = LogLevel.ERROR;
023
024    private static HashMap<String, Class<?>> classMapping = new HashMap<String, Class<?>>();
025    private static HashMap<Class<?>, HashMap<String, Field>> fieldRenameMapping = new HashMap<Class<?>, HashMap<String, Field>>();
026    
027    static {
028        addClassMapping("util.persistens.AbstractFrameDialogSettings", AbstractWindowSettings.class); // TODO Remove when all settings have been converted.
029        addClassMapping("util.ui.MyJTableSettings", MyJTableSettings.class); // TODO Remove when all settings have been converted.
030        
031        addClassMapping("util.persistens.AbstractSettingWithFrameDialogAndTableSettings", AbstractSettingWithFrameDialogAndTableSettings.class); // TODO Remove when all settings have been converted.
032        addClassMapping("util.persistens.AbstractWindowSettings", AbstractWindowSettings.class); // TODO Remove when all settings have been converted.
033        addClassMapping("util.persistens.DialogSettings", DialogSettings.class); // TODO Remove when all settings have been converted.
034        addClassMapping("util.persistens.FrameSettings", FrameSettings.class); // TODO Remove when all settings have been converted.
035        addClassMapping("util.persistens.MyJTableSettings", MyJTableSettings.class); // TODO Remove when all settings have been converted.
036        addClassMapping("util.persistens.PlatformDependentString", PlatformDependentString.class); // TODO Remove when all settings have been converted.
037    }
038
039    public static void addClassMapping(String oldClassName, Class<?> newClass) {
040        if (classMapping.containsKey(oldClassName)) {
041            throw new RuntimeException("The \"" + oldClassName + "\" class is already map to " + classMapping.get(oldClassName).getName());
042        }
043        if (oldClassName.equals(newClass.getName())) {
044            throw new RuntimeException("The \"" + oldClassName + "\" class can't map to it self " + newClass.getName());
045        }
046        classMapping.put(oldClassName, newClass);
047    }
048    
049    public static void addFieldRenameMapping(Class<?> clazz, String oldFieldName, String newFieldName) {
050        HashMap<String, Field> fieldRenameClassMap = fieldRenameMapping.get(clazz);
051        if (fieldRenameClassMap == null) {
052            fieldRenameClassMap = new HashMap<String, Field>();
053            fieldRenameMapping.put(clazz, fieldRenameClassMap);
054        }
055        
056        if (fieldRenameClassMap.containsKey(oldFieldName)) {
057            throw new RuntimeException("The \"" + oldFieldName + "\" field in \"" + clazz.getName() + "\" class is already map to " + fieldRenameClassMap.get(oldFieldName).getName());
058        }
059        if (oldFieldName.equals(newFieldName)) {
060            throw new RuntimeException("The \"" + oldFieldName + "\" field can't map to it self " + newFieldName);
061        }
062        
063        Field field = ReflectionUtil.findField(clazz, newFieldName, true);
064        fieldRenameClassMap.put(oldFieldName, field);
065    }
066    
067    private XmlDeserializer xmlDeserializer;
068    
069    private HashMap<String, Class<?>> classCacheMap;
070    private Map<Class<?>, Map<String, Field>> fieldCacheMap;
071    
072    public XmlDeserializerMapper(XmlDeserializer pxmlDeserializer) {
073        xmlDeserializer = pxmlDeserializer;
074        classCacheMap = new HashMap<String, Class<?>>();
075        fieldCacheMap = new HashMap<Class<?>, Map<String, Field>>();
076    }
077    
078    public Class<?> loadClassWithLogLevel(String className) {
079        Class<?> clazz = classCacheMap.get(className);
080        if (clazz != null) {
081            return clazz;
082        }
083        
084        String classNameNoArray = className;
085        int dimensions = 0;
086        while (classNameNoArray.endsWith("[]")) {
087            classNameNoArray = classNameNoArray.substring(0, classNameNoArray.length() - 2);
088            dimensions++;
089        }
090        
091        clazz = classMapping.get(classNameNoArray);
092        if (clazz != null) {
093            if (dimensions > 0) {
094                String dimensionsString = "";
095                for (int i = 0; i < dimensions; i++) {
096                    dimensionsString += "[]";
097                }
098                String mappedToClassName = clazz.getName() + dimensionsString;
099                clazz = ReflectionUtil.classNameInJavaSyntaxToClass(mappedToClassName, true);
100            }
101                
102            switch (classMappedLogLevel) {
103                case ERROR :
104                    throw new RuntimeException("The class \"" + className + "\" was mapped to \"" + clazz.getName() + "\"");
105//              break;
106                case WARNING :
107                    System.err.println("WARNING: The class \"" + className + "\" was mapped to \"" + clazz.getName() + "\". Note: This warning will only be given once per deserialization.");
108                break;
109                case INFO :
110                    System.out.println("Info: The class \"" + className + "\" was mapped to \"" + clazz.getName() + "\". Note: This info will only be given once per deserialization.");
111                break;
112                case IGNORE :
113                    ;
114                break;
115                default :
116                    throw new RuntimeException("Internal error " + classMappedLogLevel);
117            }
118
119            classCacheMap.put(className, clazz);
120            return clazz;
121        }
122        
123        clazz = ReflectionUtil.classNameInJavaSyntaxToClass(className, false);
124        classCacheMap.put(className, clazz);
125        
126        if (clazz == null) {
127            if (xmlDeserializer.getResultObject() == null) {
128                throw new RuntimeException("The top level class \"" + className + "\" was not found. Object can't be deserialized.");
129            }
130            switch (classMissingLogLevel) {
131                case ERROR :
132                    throw new RuntimeException("The class \"" + className + "\" was not found");
133//              break;
134                case WARNING :
135                    System.err.println("WARNING: The class \"" + className + "\" was not found. All values in the object will not be loaded.");
136                break;
137                case INFO :
138                    System.out.println("Info: The class \"" + className + "\" was not found. All values in the object will not be loaded.");
139                break;
140                case IGNORE :
141                    ;
142                break;
143                default :
144                    throw new RuntimeException("Internal error " + classMissingLogLevel);
145            }
146        }
147        
148        return clazz;
149    }
150    
151    public Field findField(Class<?> clazz, String name) {
152        Map<String, Field> map = fieldCacheMap.get(clazz);
153        if (map == null) {
154            map = new HashMap<String, Field>();
155            fieldCacheMap.put(clazz, map);
156        }
157        
158        Field field = map.get(name);
159        if (field != null) {
160            return field;
161        }
162        
163        HashMap<String, Field> fieldRenameClassMap = fieldRenameMapping.get(clazz);
164        if (fieldRenameClassMap != null) {
165            field = fieldRenameClassMap.get(name);
166            if (field != null) {
167                switch (fieldMappedLogLevel) {
168                    case ERROR :
169                        throw new RuntimeException("The \"" + name + "\" field in \"" + clazz.getName() + "\" class was mapped to \"" + field.getName() + "\"");
170//                  break;
171                    case WARNING :
172                        System.err.println("WARNING: The \"" + name + "\" field in \"" + clazz.getName() + "\" class was mapped to \"" + field.getName() + "\". Note: This warning will only be given once per deserialization.");
173                    break;
174                    case INFO :
175                        System.out.println("Info: The \"" + name + "\" field in \"" + clazz.getName() + "\" class was mapped to \"" + field.getName() + "\". Note: This warning will only be given once per deserialization.");
176                    break;
177                    case IGNORE :
178                        ;
179                    break;
180                    default :
181                        throw new RuntimeException("Internal error " + fieldMappedLogLevel);
182                }
183
184                map.put(name, field);
185                return field;
186            }
187        }
188        
189        field = ReflectionUtil.findField(clazz, name, false);
190        map.put(name, field);
191
192        if (field == null) {
193            switch (fieldMissingLogLevel) {
194                case ERROR :
195                    throw new RuntimeException("Field \"" + name + "\" was not found in class \"" + clazz.toString() + "\"");
196    //          break;
197                case WARNING :
198                    System.err.println("WARNING: Field \"" + name + "\" was not found in class \"" + clazz.toString() + "\". The field value will not be loaded.");
199                break;
200                case INFO :
201                    System.out.println("Info: Field \"" + name + "\" was not found in class \"" + clazz.toString() + "\". The field value will not be loaded.");
202                break;
203                case IGNORE :
204                    ;
205                break;
206                default :
207                    throw new RuntimeException("Internal error " + fieldMissingLogLevel);
208            }
209        }
210        
211        return field;
212    }
213}