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 }