001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2013 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.messaging.jmf.reflect;
022    
023    import java.lang.reflect.Constructor;
024    import java.lang.reflect.Field;
025    import java.lang.reflect.InvocationTargetException;
026    import java.lang.reflect.Modifier;
027    import java.util.ArrayList;
028    import java.util.Collections;
029    import java.util.Comparator;
030    import java.util.List;
031    import java.util.concurrent.ConcurrentHashMap;
032    import java.util.concurrent.ConcurrentMap;
033    
034    /**
035     * @author Franck WOLFF
036     */
037    public class Reflection {
038            
039            protected static final int STATIC_TRANSIENT_MASK = Modifier.STATIC | Modifier.TRANSIENT;
040    
041            protected final ClassLoader classLoader;
042            protected final ConstructorFactory constructorFactory;
043            protected final Comparator<Field> lexicalFieldComparator;
044            protected final ConcurrentMap<Class<?>, List<Field>> serializableFieldsCache;
045            
046            public Reflection(ClassLoader classLoader) {
047                    this.classLoader = classLoader;
048                    
049                    this.constructorFactory = new SunConstructorFactory();
050                    
051                    this.lexicalFieldComparator = new Comparator<Field>() {
052                            public int compare(Field f1, Field f2) {
053                                    return f1.getName().compareTo(f2.getName());
054                            }
055                    };
056                    
057                    this.serializableFieldsCache = new ConcurrentHashMap<Class<?>, List<Field>>();
058            }
059            
060            public ClassLoader getClassLoader() {
061                    return (classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader());
062            }
063    
064            public Class<?> loadClass(String className) throws ClassNotFoundException {
065                    return getClassLoader().loadClass(className);
066            }
067            
068            public <T> T newInstance(Class<T> cls)
069                    throws InstantiationException, IllegalAccessException, IllegalArgumentException,
070                    InvocationTargetException, SecurityException, NoSuchMethodException {
071                    
072                    return findDefaultContructor(cls).newInstance();
073            }
074            
075            @SuppressWarnings("unchecked")
076            public <T> T newInstance(String className)
077                    throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException,
078                    InvocationTargetException, SecurityException, NoSuchMethodException {
079                    
080                    return newInstance((Class<T>)loadClass(className));
081            }
082            
083    
084            @SuppressWarnings("unchecked")
085            public <T> Constructor<T> findDefaultContructor(Class<T> cls) throws SecurityException, NoSuchMethodException {
086                    
087                    try {
088                            return cls.getConstructor();
089                    }
090                    catch (NoSuchMethodException e) {
091                            return (Constructor<T>)constructorFactory.newConstructorForSerialization(cls);
092                    }
093            }
094            
095            public List<Field> findSerializableFields(Class<?> cls) throws SecurityException {
096                    List<Field> serializableFields = serializableFieldsCache.get(cls);
097                    
098                    if (serializableFields == null) {
099                            List<Class<?>> hierarchy = new ArrayList<Class<?>>();
100                            for (Class<?> c = cls; c != null && c != Object.class; c = c.getSuperclass())
101                                    hierarchy.add(c);
102                            
103                            serializableFields = new ArrayList<Field>();
104                            for (int i = hierarchy.size() - 1; i >= 0; i--) {
105                                    Class<?> c = hierarchy.get(i);
106                                    serializableFields.addAll(findSerializableDeclaredFields(c));
107                            }
108                            serializableFields = Collections.unmodifiableList(serializableFields);
109                            List<Field> previous = serializableFieldsCache.putIfAbsent(cls, serializableFields);
110                            if (previous != null)
111                                    serializableFields = previous;
112                    }
113                    
114                    return serializableFields;
115            }
116    
117            protected List<Field> findSerializableDeclaredFields(Class<?> cls) throws SecurityException {
118                    
119                    if (!isRegularClass(cls))
120                            throw new IllegalArgumentException("Not a regular class: " + cls);
121    
122                    Field[] declaredFields = cls.getDeclaredFields();
123                    List<Field> serializableFields = new ArrayList<Field>(declaredFields.length);
124                    for (int i = 0; i < declaredFields.length; i++) {
125                            Field field = declaredFields[i];
126                            
127                            int modifiers = field.getModifiers();
128                            if ((modifiers & STATIC_TRANSIENT_MASK) == 0) {
129                                    declaredFields[i].setAccessible(true);
130                                    serializableFields.add(field);
131                            }
132                    }
133                    
134                    Collections.sort(serializableFields, lexicalFieldComparator);
135                    
136                    return serializableFields;
137            }
138            
139            public boolean isRegularClass(Class<?> cls) {
140                    return cls != Class.class && !cls.isAnnotation() && !cls.isArray() &&
141                            !cls.isEnum() && !cls.isInterface() && !cls.isPrimitive();
142            }
143    }
144