/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.reflect.generic; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.internal.reflect.generic.*; import com.db4o.reflect.*; /** * db4o provides GenericReflector as a wrapper around specific * reflector (delegate). GenericReflector is set when an * ObjectContainer is opened. All subsequent reflector * calls are routed through this interface.<br><br> * An instance of GenericReflector can be obtained through * {@link com.db4o.ext.ExtObjectContainer#reflector()}.<br><br> * GenericReflector keeps list of known classes in memory. * When the GenericReflector is called, it first checks its list of * known classes. If the class cannot be found, the task is * transferred to the delegate reflector. If the delegate fails as * well, generic objects are created, which hold simulated * "field values" in an array of objects.<br><br> * Generic reflector makes possible the following usecases:<ul> * <li>running a db4o server without deploying application classes;</li> * <li>running db4o on Java dialects without reflection (J2ME CLDC, MIDP);</li> * <li>easier access to stored objects where classes or fields are not available;</li> * <li>running refactorings in the reflector;</li> * <li>building interfaces to db4o from any programming language.</li></ul> * <br><br> * One of the live usecases is ObjectManager, which uses GenericReflector * to read C# objects from Java. */ public class GenericReflector implements Reflector, DeepClone { private KnownClassesRepository _repository; /* default delegate Reflector is the JdkReflector */ private Reflector _delegate; private GenericArrayReflector _array; private Collection4 _collectionPredicates = new Collection4(); // todo: Why have this when there is already the _repository by name? Redundant private final Hashtable4 _classByClass = new Hashtable4(); private Transaction _trans; private ObjectContainerBase _stream; /** * Creates an instance of GenericReflector * @param trans transaction * @param delegateReflector delegate reflector, * providing specific reflector functionality. For example */ public GenericReflector(Transaction trans, Reflector delegateReflector){ _repository=new KnownClassesRepository(new GenericClassBuilder(this,delegateReflector)); setTransaction(trans); _delegate = delegateReflector; if(_delegate != null){ _delegate.setParent(this); } } public GenericReflector(Reflector delegateReflector) { this(null, delegateReflector); } /** * Creates a clone of provided object * @param obj object to copy * @return copy of the submitted object */ public Object deepClone(Object obj) { GenericReflector myClone = new GenericReflector(null, (Reflector)_delegate.deepClone(this)); myClone._collectionPredicates = (Collection4)_collectionPredicates.deepClone(myClone); // Interesting, adding the following messes things up. // Keep the code, since it may make sense to carry the // global reflectors into a running db4o session. // Iterator4 i = _classes.iterator(); // while(i.hasNext()){ // GenericClass clazz = (GenericClass)i.next(); // clazz = (GenericClass)clazz.deepClone(myClone); // myClone._classByName.put(clazz.getName(), clazz); // myClone._classes.add(clazz); // } return myClone; } ObjectContainerBase getStream(){ return _stream; } /** * If there is a transaction assosiated with the current refector. * @return true if there is a transaction assosiated with the current refector. */ public boolean hasTransaction(){ return _trans != null; } /** * Associated a transaction with the current reflector. * @param trans */ public void setTransaction(Transaction trans){ if(trans != null){ _trans = trans; _stream = trans.container(); } _repository.setTransaction(trans); } /** * @return generic reflect array instance. */ public ReflectArray array() { if(_array == null){ _array = new GenericArrayReflector(this); } return _array; } GenericClass ensureDelegate(ReflectClass clazz){ if(clazz == null){ return null; } GenericClass claxx = (GenericClass)_repository.lookupByName(clazz.getName()); if(claxx == null){ // We don't have to worry about the superclass, it can be null // because handling is delegated anyway claxx = genericClass(clazz); _repository.register(claxx); } return claxx; } private GenericClass genericClass(ReflectClass clazz) { GenericClass ret; String name = clazz.getName(); if(name.equals(ReflectPlatform.fullyQualifiedName(GenericArray.class))){ // special case, comparing name because can't compare class == class directly with ReflectClass ret = new GenericArrayClass(this, clazz, name, null); } else { ret = new GenericClass(this, clazz, name, null); } return ret; } /** * Returns a ReflectClass instance for the specified class * @param clazz class * @return a ReflectClass instance for the specified class * @see com.db4o.reflect.ReflectClass */ public ReflectClass forClass(Class clazz) { if(clazz == null){ return null; } ReflectClass claxx = (ReflectClass) _classByClass.get(clazz); if(claxx != null){ return claxx; } if (!clazz.isArray() && ReflectPlatform.isNamedClass(clazz)) { claxx = forName(ReflectPlatform.fullyQualifiedName(clazz)); if(claxx != null){ _classByClass.put(clazz, claxx); return claxx; } } claxx = _delegate.forClass(clazz); if(claxx == null){ return null; } claxx = ensureDelegate(claxx); _classByClass.put(clazz, claxx); return claxx; } /** * Returns a ReflectClass instance for the specified class name * @param className class name * @return a ReflectClass instance for the specified class name * @see com.db4o.reflect.ReflectClass */ public ReflectClass forName(final String className) { return withLock(new Closure4<ReflectClass>() { public ReflectClass run() { ReflectClass clazz = _repository.lookupByName(className); if(clazz != null){ return clazz; } clazz = _delegate.forName(className); if(clazz != null){ return ensureDelegate(clazz); } return _repository.forName(className); } }); } /** * Returns a ReflectClass instance for the specified class object * @param obj class object * @return a ReflectClass instance for the specified class object * @see com.db4o.reflect.ReflectClass */ public ReflectClass forObject(Object obj) { if (obj instanceof GenericObject){ return forGenericObject((GenericObject)obj); } if (obj instanceof GenericArray){ return ((GenericArray)obj)._clazz; } return _delegate.forObject(obj); } private ReflectClass forGenericObject(final GenericObject genericObject) { GenericClass claxx = genericObject._class; if(claxx == null){ throw new IllegalStateException(); } String name = claxx.getName(); if(name == null){ throw new IllegalStateException(); } GenericClass existingClass = (GenericClass) forName(name); if(existingClass == null){ _repository.register(claxx); return claxx; } // TODO: Using .equals() here would be more consistent with // the equals() method in GenericClass. if(existingClass != claxx){ throw new IllegalStateException(); } return claxx; } /** * Returns delegate reflector * @return delegate reflector */ public Reflector getDelegate(){ return _delegate; } /** * Determines if a candidate ReflectClass is a collection * @param candidate candidate ReflectClass * @return true if a candidate ReflectClass is a collection. */ public boolean isCollection(ReflectClass candidate) { //candidate = candidate.getDelegate(); Iterator4 i = _collectionPredicates.iterator(); while(i.moveNext()){ if (((ReflectClassPredicate)i.current()).match(candidate)) { return true; } } return _delegate.isCollection(candidate.getDelegate()); //TODO: will need knowledge for .NET collections here // possibility: call registercollection with strings } /** * Register a class as a collection * @param clazz class to be registered */ public void registerCollection(Class clazz) { registerCollection(classPredicate(clazz)); } /** * Register a predicate as a collection * @param predicate predicate to be registered */ public void registerCollection(ReflectClassPredicate predicate) { _collectionPredicates.add(predicate); } private ReflectClassPredicate classPredicate(Class clazz) { final ReflectClass collectionClass = forClass(clazz); ReflectClassPredicate predicate = new ReflectClassPredicate() { public boolean match(ReflectClass candidate) { return collectionClass.isAssignableFrom(candidate); } }; return predicate; } /** * Register a class * @param clazz class */ public void register(final GenericClass clazz) { withLock(new Closure4<Object>() { public Object run() { String name = clazz.getName(); if(_repository.lookupByName(name) == null){ _repository.register(clazz); } return null; } }); } /** * Returns an array of classes known to the reflector * @return an array of classes known to the reflector */ public ReflectClass[] knownClasses() { return withLock(new Closure4<ReflectClass[]>() { public ReflectClass[] run() { return new KnownClassesCollector(_stream, _repository).collect(); } }); } /** * Registers primitive class * @param id class id * @param name class name * @param converter class converter */ public void registerPrimitiveClass(final int id, final String name, final GenericConverter converter) { withLock(new Closure4<Object>() { public Object run() { GenericClass existing = (GenericClass)_repository.lookupByID(id); if (existing != null) { if (null != converter) { // existing.setSecondClass(); } else { existing.setConverter(null); } return null; } ReflectClass clazz = _delegate.forName(name); GenericClass claxx = null; if(clazz != null) { claxx = ensureDelegate(clazz); }else { claxx = new GenericClass(GenericReflector.this, null, name, null); register(claxx); claxx.initFields(new GenericField[] {new GenericField(null, null, true)}); claxx.setConverter(converter); } // claxx.setSecondClass(); claxx.setPrimitive(); _repository.register(id,claxx); return null; } }); } /** * method stub: generic reflector does not have a parent */ public void setParent(Reflector reflector) { // do nothing, the generic reflector does not have a parant } public void configuration(ReflectorConfiguration config) { if(_delegate != null) { _delegate.configuration(config); } } private <T> T withLock(Closure4<T> block) { if(_stream == null || _stream.isClosed()) { return block.run(); } return _stream.syncExec(block); } }