001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.reflect.generic;
022:
023: import com.db4o.foundation.*;
024: import com.db4o.internal.*;
025: import com.db4o.reflect.*;
026:
027: /**
028: * db4o provides GenericReflector as a wrapper around specific
029: * reflector (delegate). GenericReflector is set when an
030: * ObjectContainer is opened. All subsequent reflector
031: * calls are routed through this interface.<br><br>
032: * An instance of GenericReflector can be obtained through
033: * {@link com.db4o.ext.ExtObjectContainer#reflector()}.<br><br>
034: * GenericReflector keeps list of known classes in memory.
035: * When the GenericReflector is called, it first checks its list of
036: * known classes. If the class cannot be found, the task is
037: * transferred to the delegate reflector. If the delegate fails as
038: * well, generic objects are created, which hold simulated
039: * "field values" in an array of objects.<br><br>
040: * Generic reflector makes possible the following usecases:<ul>
041: * <li>running a db4o server without deploying application classes;</li>
042: * <li>running db4o on Java dialects without reflection (J2ME CLDC, MIDP);</li>
043: * <li>easier access to stored objects where classes or fields are not available;</li>
044: * <li>running refactorings in the reflector;</li>
045: * <li>building interfaces to db4o from any programming language.</li></ul>
046: * <br><br>
047: * One of the live usecases is ObjectManager, which uses GenericReflector
048: * to read C# objects from Java.
049: */
050: public class GenericReflector implements Reflector, DeepClone {
051:
052: private KnownClassesRepository _repository;
053:
054: /* default delegate Reflector is the JdkReflector */
055: private Reflector _delegate;
056: private GenericArrayReflector _array;
057:
058: private Collection4 _collectionPredicates = new Collection4();
059: private Collection4 _collectionUpdateDepths = new Collection4();
060:
061: // todo: Why have this when there is already the _repository by name? Redundant
062: private final Hashtable4 _classByClass = new Hashtable4();
063:
064: private Transaction _trans;
065: private ObjectContainerBase _stream;
066:
067: /**
068: * Creates an instance of GenericReflector
069: * @param trans transaction
070: * @param delegateReflector delegate reflector,
071: * providing specific reflector functionality. For example
072: */
073: public GenericReflector(Transaction trans,
074: Reflector delegateReflector) {
075: _repository = new KnownClassesRepository(
076: new GenericClassBuilder(this , delegateReflector));
077: setTransaction(trans);
078: _delegate = delegateReflector;
079: if (_delegate != null) {
080: _delegate.setParent(this );
081: }
082: }
083:
084: /**
085: * Creates a clone of provided object
086: * @param obj object to copy
087: * @return copy of the submitted object
088: */
089: public Object deepClone(Object obj) {
090: GenericReflector myClone = new GenericReflector(null,
091: (Reflector) _delegate.deepClone(this ));
092: myClone._collectionPredicates = (Collection4) _collectionPredicates
093: .deepClone(myClone);
094: myClone._collectionUpdateDepths = (Collection4) _collectionUpdateDepths
095: .deepClone(myClone);
096:
097: // Interesting, adding the following messes things up.
098: // Keep the code, since it may make sense to carry the
099: // global reflectors into a running db4o session.
100:
101: // Iterator4 i = _classes.iterator();
102: // while(i.hasNext()){
103: // GenericClass clazz = (GenericClass)i.next();
104: // clazz = (GenericClass)clazz.deepClone(myClone);
105: // myClone._classByName.put(clazz.getName(), clazz);
106: // myClone._classes.add(clazz);
107: // }
108:
109: return myClone;
110: }
111:
112: ObjectContainerBase getStream() {
113: return _stream;
114: }
115:
116: /**
117: * If there is a transaction assosiated with the current refector.
118: * @return true if there is a transaction assosiated with the current refector.
119: */
120: public boolean hasTransaction() {
121: return _trans != null;
122: }
123:
124: /**
125: * Associated a transaction with the current reflector.
126: * @param trans
127: */
128: public void setTransaction(Transaction trans) {
129: if (trans != null) {
130: _trans = trans;
131: _stream = trans.container();
132: }
133: _repository.setTransaction(trans);
134: }
135:
136: /**
137: * @return generic reflect array instance.
138: */
139: public ReflectArray array() {
140: if (_array == null) {
141: _array = new GenericArrayReflector(this );
142: }
143: return _array;
144: }
145:
146: /**
147: * Determines collection update depth for the specified class
148: * @param candidate candidate class
149: * @return collection update depth for the specified class
150: */
151: public int collectionUpdateDepth(ReflectClass candidate) {
152: Iterator4 i = _collectionUpdateDepths.iterator();
153: while (i.moveNext()) {
154: CollectionUpdateDepthEntry entry = (CollectionUpdateDepthEntry) i
155: .current();
156: if (entry._predicate.match(candidate)) {
157: return entry._depth;
158: }
159: }
160: return 2;
161:
162: //TODO: will need knowledge for .NET collections here
163: }
164:
165: /**
166: * Defines if constructor calls are supported.
167: * @return true if constructor calls are supported.
168: * @see com.db4o.config.Configuration#callbacks(boolean)
169: */
170: public boolean constructorCallsSupported() {
171: return _delegate.constructorCallsSupported();
172: }
173:
174: GenericClass ensureDelegate(ReflectClass clazz) {
175: if (clazz == null) {
176: return null;
177: }
178: GenericClass claxx = (GenericClass) _repository
179: .lookupByName(clazz.getName());
180: if (claxx == null) {
181: // We don't have to worry about the superclass, it can be null
182: // because handling is delegated anyway
183: claxx = genericClass(clazz);
184: _repository.register(claxx);
185: }
186: return claxx;
187: }
188:
189: private GenericClass genericClass(ReflectClass clazz) {
190: GenericClass ret;
191: String name = clazz.getName();
192: if (name.equals(GenericArray.class.getName())) { // special case, comparing name because can't compare class == class directly with ReflectClass
193: ret = new GenericArrayClass(this , clazz, name, null);
194: } else {
195: ret = new GenericClass(this , clazz, name, null);
196: }
197: return ret;
198: }
199:
200: /**
201: * Returns a ReflectClass instance for the specified class
202: * @param clazz class
203: * @return a ReflectClass instance for the specified class
204: * @see com.db4o.reflect.ReflectClass
205: */
206: public ReflectClass forClass(Class clazz) {
207: if (clazz == null) {
208: return null;
209: }
210: ReflectClass claxx = (ReflectClass) _classByClass.get(clazz);
211: if (claxx != null) {
212: return claxx;
213: }
214: if (!clazz.isArray()) {
215: claxx = forName(ReflectPlatform.fullyQualifiedName(clazz));
216: if (claxx != null) {
217: _classByClass.put(clazz, claxx);
218: return claxx;
219: }
220: }
221: claxx = _delegate.forClass(clazz);
222: if (claxx == null) {
223: return null;
224: }
225: claxx = ensureDelegate(claxx);
226: _classByClass.put(clazz, claxx);
227: return claxx;
228: }
229:
230: /**
231: * Returns a ReflectClass instance for the specified class name
232: * @param className class name
233: * @return a ReflectClass instance for the specified class name
234: * @see com.db4o.reflect.ReflectClass
235: */
236: public ReflectClass forName(String className) {
237: ReflectClass clazz = _repository.lookupByName(className);
238: if (clazz != null) {
239: return clazz;
240: }
241: clazz = _delegate.forName(className);
242: if (clazz != null) {
243: return ensureDelegate(clazz);
244: }
245: return _repository.forName(className);
246: }
247:
248: /**
249: * Returns a ReflectClass instance for the specified class object
250: * @param obj class object
251: * @return a ReflectClass instance for the specified class object
252: * @see com.db4o.reflect.ReflectClass
253: */
254: public ReflectClass forObject(Object obj) {
255: if (obj instanceof GenericObject) {
256: return forGenericObject((GenericObject) obj);
257: }
258: if (obj instanceof GenericArray) {
259: return ((GenericArray) obj)._clazz;
260: }
261: return _delegate.forObject(obj);
262: }
263:
264: private ReflectClass forGenericObject(
265: final GenericObject genericObject) {
266: GenericClass claxx = genericObject._class;
267: if (claxx == null) {
268: throw new IllegalStateException();
269: }
270: String name = claxx.getName();
271: if (name == null) {
272: throw new IllegalStateException();
273: }
274: GenericClass existingClass = (GenericClass) forName(name);
275: if (existingClass == null) {
276: _repository.register(claxx);
277: return claxx;
278: }
279: // TODO: Using .equals() here would be more consistent with
280: // the equals() method in GenericClass.
281: if (existingClass != claxx) {
282:
283: throw new IllegalStateException();
284: }
285:
286: return claxx;
287: }
288:
289: /**
290: * Returns delegate reflector
291: * @return delegate reflector
292: */
293: public Reflector getDelegate() {
294: return _delegate;
295: }
296:
297: /**
298: * Determines if a candidate ReflectClass is a collection
299: * @param candidate candidate ReflectClass
300: * @return true if a candidate ReflectClass is a collection.
301: */
302: public boolean isCollection(ReflectClass candidate) {
303: //candidate = candidate.getDelegate();
304: Iterator4 i = _collectionPredicates.iterator();
305: while (i.moveNext()) {
306: if (((ReflectClassPredicate) i.current()).match(candidate)) {
307: return true;
308: }
309: }
310: return _delegate.isCollection(candidate.getDelegate());
311:
312: //TODO: will need knowledge for .NET collections here
313: // possibility: call registercollection with strings
314: }
315:
316: /**
317: * Register a class as a collection
318: * @param clazz class to be registered
319: */
320: public void registerCollection(Class clazz) {
321: registerCollection(classPredicate(clazz));
322: }
323:
324: /**
325: * Register a predicate as a collection
326: * @param predicate predicate to be registered
327: */
328: public void registerCollection(ReflectClassPredicate predicate) {
329: _collectionPredicates.add(predicate);
330: }
331:
332: private ReflectClassPredicate classPredicate(Class clazz) {
333: final ReflectClass collectionClass = forClass(clazz);
334: ReflectClassPredicate predicate = new ReflectClassPredicate() {
335: public boolean match(ReflectClass candidate) {
336: return collectionClass.isAssignableFrom(candidate);
337: }
338: };
339: return predicate;
340: }
341:
342: /**
343: * Register update depth for a collection class
344: * @param clazz class
345: * @param depth update depth
346: */
347: public void registerCollectionUpdateDepth(Class clazz, int depth) {
348: registerCollectionUpdateDepth(classPredicate(clazz), depth);
349: }
350:
351: /**
352: * Register update depth for a collection class
353: * @param predicate class predicate
354: * @param depth update depth
355: */
356: public void registerCollectionUpdateDepth(
357: ReflectClassPredicate predicate, int depth) {
358: _collectionUpdateDepths.add(new CollectionUpdateDepthEntry(
359: predicate, depth));
360: }
361:
362: /**
363: * Register a class
364: * @param clazz class
365: */
366: public void register(GenericClass clazz) {
367: String name = clazz.getName();
368: if (_repository.lookupByName(name) == null) {
369: _repository.register(clazz);
370: }
371: }
372:
373: /**
374: * Returns an array of classes known to the reflector
375: * @return an array of classes known to the reflector
376: */
377: public ReflectClass[] knownClasses() {
378: Collection4 classes = new Collection4();
379: collectKnownClasses(classes);
380: return (ReflectClass[]) classes
381: .toArray(new ReflectClass[classes.size()]);
382: }
383:
384: private void collectKnownClasses(Collection4 classes) {
385: Iterator4 i = _repository.classes();
386: while (i.moveNext()) {
387: GenericClass clazz = (GenericClass) i.current();
388: if (!_stream._handlers.ICLASS_INTERNAL
389: .isAssignableFrom(clazz)) {
390: if (!clazz.isSecondClass()) {
391: if (!clazz.isArray()) {
392: classes.add(clazz);
393: }
394: }
395: }
396: }
397: }
398:
399: /**
400: * Registers primitive class
401: * @param id class id
402: * @param name class name
403: * @param converter class converter
404: */
405: public void registerPrimitiveClass(int id, String name,
406: GenericConverter converter) {
407: GenericClass existing = (GenericClass) _repository
408: .lookupByID(id);
409: if (existing != null) {
410: if (null != converter) {
411: existing.setSecondClass();
412: } else {
413: existing.setConverter(null);
414: }
415: return;
416: }
417: ReflectClass clazz = _delegate.forName(name);
418:
419: GenericClass claxx = null;
420: if (clazz != null) {
421: claxx = ensureDelegate(clazz);
422: } else {
423: claxx = new GenericClass(this , null, name, null);
424: register(claxx);
425: claxx.initFields(new GenericField[] { new GenericField(
426: null, null, true) });
427: claxx.setConverter(converter);
428: }
429: claxx.setSecondClass();
430: claxx.setPrimitive();
431: _repository.register(id, claxx);
432: }
433:
434: /**
435: * method stub: generic reflector does not have a parent
436: */
437: public void setParent(Reflector reflector) {
438: // do nothing, the generic reflector does not have a parant
439: }
440:
441: }
|