001    /**
002     * Copyright (C) 2009 Erik Putrycz <erik.putrycz@gmail.com>
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *         http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package net.ep.db4o.javassist;
018    
019    import java.lang.reflect.Constructor;
020    import java.lang.reflect.Modifier;
021    import java.util.ArrayList;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    
026    import javassist.ClassPool;
027    import javassist.LoaderClassPath;
028    import net.ep.db4o.activator.TransparentActivation;
029    
030    import com.db4o.internal.Platform4;
031    import com.db4o.reflect.ReflectArray;
032    import com.db4o.reflect.ReflectClass;
033    import com.db4o.reflect.Reflector;
034    import com.db4o.reflect.ReflectorConfiguration;
035    import com.db4o.reflect.jdk.ClassLoaderJdkLoader;
036    import com.db4o.reflect.jdk.JdkClass;
037    import com.db4o.reflect.jdk.JdkLoader;
038    import com.db4o.reflect.jdk.JdkReflector;
039    import com.db4o.ta.Activatable;
040    
041    @SuppressWarnings("unchecked")
042    public class JVSTReflector implements Reflector {
043    
044            private static final boolean debug = false;
045            
046    
047            private static final Map<Class, List<ClassData>> _classCache = new HashMap<Class, List<ClassData>>();
048            
049            public static class ClassData {
050                    ReflectClass _reflectClass;
051                    boolean ignoreNullActivator;
052                    boolean threadLocal;
053                    @Override
054                    public String toString() {
055                            return _reflectClass + " ignoreNullActivator:" + ignoreNullActivator + " threadLocal:" + threadLocal;
056                    }
057            }
058            
059            public static final <T> Class<? super T> getRealClass(Class<? super T> clazz) {
060                    if (isEnhanced(clazz)) {
061                            clazz = clazz.getSuperclass();
062                    }
063                    return clazz;
064            }
065    
066            public static final boolean hasPublicConstructor(Class clazz) {
067                    boolean hasPublic = true;
068                    for (Constructor constr : clazz.getDeclaredConstructors()) {
069                            if (Modifier.isPublic(constr.getModifiers()))
070                                    return true;
071                            hasPublic = false;
072                    }
073                    return hasPublic;
074            }
075    
076            public static final boolean isEnhanced(Class clazz) {
077                    if (clazz.equals(TransparentActivation.class))
078                            return false;
079                    return TransparentActivation.class.isAssignableFrom(clazz);
080            }
081    
082            public static final boolean shouldBeEnhanced(Class clazz) {
083                    return canBeEnhanced(clazz)
084                                    && hasPublicConstructor(clazz)
085                                    && (clazz.getPackage().getName() == null || !clazz.getPackage()
086                                                    .getName().startsWith("java")
087                                                    && !clazz.getPackage().getName().startsWith("com.db4o"));
088            }
089            
090            public static final boolean canBeEnhanced(Class clazz) {
091                    return !Modifier.isFinal(clazz.getModifiers())
092                                    && !clazz.isInterface()
093                                    && !clazz.isAnnotation()
094                                    && !clazz.isPrimitive()
095                                    && !clazz.isArray()
096                                    && !clazz.isAnonymousClass()
097                                    && !Activatable.class.isAssignableFrom(clazz);
098            }       
099            
100            public static String explainWhyNotEnhanced(Class clazz) {
101                    List<String> reasons = new ArrayList<String>();
102                    StringBuilder sb = new StringBuilder();
103                    if (Modifier.isFinal(clazz.getModifiers()))
104                            reasons.add("modifiers are final");
105                    if (clazz.isInterface())
106                            reasons.add("class is an interface");
107                    if (clazz.isAnnotation())
108                            reasons.add("class is an annotation");
109                    if (clazz.isPrimitive())
110                            reasons.add("the class represents a primitive type");
111                    if (clazz.isArray())
112                            reasons.add("the class represents an array");
113                    if (clazz.isAnonymousClass())
114                            reasons.add("the class represents an anonymous class");
115                    if (Activatable.class.isAssignableFrom(clazz))
116                            reasons.add("the class already implements " + Activatable.class.getName());
117    //              if (!hasPublicConstructor(clazz))
118    //                      reasons.add("no public constructors have been found");
119    //              if (clazz.getPackage().getName() == null)
120    //                      reasons.add("the package is empty");
121    //              if (clazz.getPackage()
122    //                                                      .getName().startsWith("java"))
123    //                      reasons.add("the package starts with 'java.xxxx'");
124    //              if (clazz.getPackage().getName().startsWith("com.db4o"))
125    //                      reasons.add("the class seems to be an internal db4o class (package starts with 'com.db4o')");
126                    boolean first = true;
127                    for (String reason:reasons) {
128                            if (!first)
129                                    sb.append("\n");
130                            sb.append(reason);
131                            first = false;
132                    }
133                    return sb.toString();
134            }
135    
136            /**
137             * Creates Class[] array from the ReflectClass[] array
138             * 
139             * @param claxx
140             *          ReflectClass array
141             * @return Class[] array
142             */
143            static Class[] toNative(ReflectClass[] claxx) {
144                    Class[] clazz = null;
145                    if (claxx != null) {
146                            clazz = new Class[claxx.length];
147                            for (int i = 0; i < claxx.length; i++) {
148                                    clazz[i] = JdkReflector.toNative(claxx[i]);
149                            }
150                    }
151                    return clazz;
152            }
153    
154            private ReflectArray _array;
155    
156            private final JdkLoader _classLoader;
157    
158            private ClassPool _classPool;
159    
160            private ReflectorConfiguration _config;
161    
162            private JdkReflector _jdkReflect;
163    
164            private Reflector _parent;
165    
166            private boolean _threadLocal;
167    
168            private PersistOnMethodPolicy _persistPolicy;
169    
170            private ActivateOnMethodPolicy _activationPolicy;
171    
172            private boolean _ignoreNullActivator;
173    
174            /**
175             * Constructor
176             * 
177             * @param classLoader
178             *          class loader
179             */
180            public JVSTReflector(ClassLoader classLoader) {
181                    this(new ClassLoaderJdkLoader(classLoader));
182            }
183            
184            /**
185             * Constructor
186             * 
187             * @param classLoader
188             *          class loader
189             */
190            public JVSTReflector(JdkLoader classLoader) {
191                    _classLoader = classLoader;
192                    _classPool = new ClassPool();
193                    _classPool.appendClassPath(new LoaderClassPath(Thread.currentThread()
194                                    .getContextClassLoader()));
195                    _jdkReflect = new JdkReflector(classLoader);
196                    // _classPool.appendSystemPath();
197            }
198    
199            private JVSTReflector(JdkLoader classLoader, ReflectorConfiguration config){
200                    _classLoader = classLoader;
201                    _config = config;
202            }
203    
204            /**
205             * ReflectArray factory
206             * 
207             * @return ReflectArray instance
208             */
209            public ReflectArray array() {
210                    if (_array == null) {
211                            _array = new JVSTArray(parent());
212                    }
213                    return _array;
214            }
215            
216            public ReflectorConfiguration configuration() {
217                    return _config;
218            }       
219    
220            public void configuration(ReflectorConfiguration config) {
221                    _config = config;
222                    _jdkReflect.configuration(config);
223            }
224    
225            private ReflectClass findInCache(Class clazz) {
226                    List<ClassData> clList = _classCache.get(clazz);
227                    if (clList == null)
228                            return null;
229                    if (debug)
230                            System.out.println(clazz.getName() + " - cache contains " + clList.size() + " items " + clList.toString());
231                    for (ClassData clData:clList) {
232                            if (clData.ignoreNullActivator == _ignoreNullActivator && clData.threadLocal == _threadLocal)
233                                    return clData._reflectClass;
234                    }
235                    return null;
236            }
237            
238            protected ReflectClass createClass(Class clazz) {                               
239                    ReflectClass res = findInCache(clazz);
240                    if (res != null)
241                            return res;
242                    if (shouldBeEnhanced(clazz)) {
243                            if (debug)
244                                    System.out.println("enhancing:" + clazz + " threadLocal:" + _threadLocal);
245                            res = enhanceClass(clazz);
246                    } else {
247                            res = new JdkClass(parent(), _jdkReflect, getRealClass(clazz));
248                            addToCache(clazz, res);
249                    }
250                    return res;
251            }
252    
253            private void addToCache(Class clazz, ReflectClass res) {
254                    ClassData clData = new ClassData();
255                    clData._reflectClass = res;
256                    clData.ignoreNullActivator = _ignoreNullActivator;
257                    clData.threadLocal = _threadLocal;
258                    List<ClassData> list = new ArrayList<ClassData>();
259                    list.add(clData);
260                    _classCache.put(clazz, list);
261            } 
262    
263            public JVSTClass enhanceClass(Class clazz) {
264                    ReflectClass res = findInCache(clazz);
265                    if (res != null && res instanceof JVSTClass)
266                            return (JVSTClass) res;
267                    JVSTClass jclazz = new JVSTClass(parent(), this, _classPool, clazz, _threadLocal, _ignoreNullActivator, _persistPolicy,_activationPolicy);
268                    addToCache(clazz, jclazz);
269                    return jclazz;
270            }
271            
272            /**
273             * Creates a copy of the object
274             * 
275             * @param obj
276             *          object to copy
277             * @return object copy
278             */
279            public Object deepClone(Object obj) {
280                    JVSTReflector clone = new JVSTReflector(_classLoader, _config);
281                    return clone;
282            }
283    
284            /**
285             * Returns ReflectClass for the specified class
286             * 
287             * @param clazz
288             *          class
289             * @return ReflectClass for the specified class
290             */
291            public ReflectClass forClass(Class clazz) {
292                    return createClass(clazz);
293            }
294    
295            /**
296             * Returns ReflectClass for the specified class name
297             * 
298             * @param className
299             *          class name
300             * @return ReflectClass for the specified class name
301             */
302            public ReflectClass forName(String className) {
303                    Class clazz = _classLoader.loadClass(className);
304                    if (clazz == null)
305                            return null;
306                    return createClass(clazz);
307            }
308    
309            /**
310             * Returns ReflectClass for the specified class object
311             * 
312             * @param a_object
313             *          class object
314             * @return ReflectClass for the specified class object
315             */
316            public ReflectClass forObject(Object a_object) {
317                    if (a_object == null)
318                            return null;
319                    return parent().forClass(a_object.getClass());
320            }
321            
322            public ActivateOnMethodPolicy getActivationPolicy() {
323                    return _activationPolicy;
324            }       
325            
326            public PersistOnMethodPolicy getPersistPolicy() {
327                    return _persistPolicy;
328            }
329    
330            /**
331             * Method stub. Returns false.
332             */
333            public boolean isCollection(ReflectClass candidate) {
334                    return false;
335            }
336    
337            /**
338             * Method stub. Returns false.
339             */
340            public boolean methodCallsSupported() {
341                    return true;
342            }
343    
344            Object nullValue(ReflectClass clazz) {
345                    return Platform4.nullValue(JdkReflector.toNative(clazz));
346            }
347    
348            private Reflector parent() {
349                    if(_parent == null){
350                            return this;
351                    }
352                    return _parent; 
353            }
354    
355            public void setActivationPolicy(ActivateOnMethodPolicy activationPolicy) {
356                    _activationPolicy = activationPolicy;
357            }
358    
359            /**
360             * Sets parent reflector
361             * 
362             * @param reflector
363             *          parent reflector
364             */
365            public void setParent(Reflector reflector) {
366                    _parent = reflector;
367            }
368    
369            public void setPersistPolicy(PersistOnMethodPolicy persistPolicy) {
370                    _persistPolicy = persistPolicy;
371            }
372    
373            public void setThreadLocal(boolean threadLocal) {
374                    _threadLocal = threadLocal;
375            }
376            
377            public void setIgnoreNullActivator(boolean ignore) {
378                    _ignoreNullActivator = ignore;
379            }
380    
381    }