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
021package org.granite.messaging.jmf.reflect;
022
023import java.lang.reflect.Constructor;
024import java.lang.reflect.Field;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Modifier;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.Comparator;
030import java.util.List;
031import java.util.concurrent.ConcurrentHashMap;
032import java.util.concurrent.ConcurrentMap;
033
034/**
035 * @author Franck WOLFF
036 */
037public 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