/* 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.marshall.*; import com.db4o.reflect.*; /** * @exclude * */ public class KnownClassesRepository { private final static Hashtable4 PRIMITIVES; static { PRIMITIVES=new Hashtable4(); for(Class primitive : Platform4.primitiveTypes()) { registerPrimitive(primitive); } } private static void registerPrimitive(Class primitive) { PRIMITIVES.put(ReflectPlatform.fullyQualifiedName(Platform4.nullableTypeFor(primitive)),primitive); } private ObjectContainerBase _stream; private Transaction _trans; private ReflectClassBuilder _builder; private final ListenerRegistry<ReflectClass> _listeners = ListenerRegistry.newInstance(); private final Hashtable4 _classByName = new Hashtable4(); private final Hashtable4 _classByID = new Hashtable4(); private Collection4 _pendingClasses = new Collection4(); private final Collection4 _classes = new Collection4(); public KnownClassesRepository(ReflectClassBuilder builder) { _builder=builder; } public void setTransaction(Transaction trans){ if(trans != null){ _trans = trans; _stream = trans.container(); } } public void register(ReflectClass clazz) { register(clazz.getName(), clazz); } public ReflectClass forID(int id) { synchronized(_stream.lock()) { if(_stream.handlers().isSystemHandler(id)) { return _stream.handlers().classForID(id); } return ensureClassAvailability(id); } } public ReflectClass forName(String className) { final ReflectClass clazz = lookupByName(className); if(clazz != null){ return clazz; } if(_stream == null) { return null; } synchronized(_stream.lock()) { if(_stream.classCollection() == null){ return null; } int classID = _stream.classMetadataIdForName(className); if(classID <= 0){ return null; } return initializeClass(classID, className); } } private ReflectClass initializeClass(int classID, String className) { ReflectClass newClazz = ensureClassInitialised(classID); _classByName.put(className, newClazz); return newClazz; } private void readAll(){ forEachClassId(new Procedure4<Integer>() { public void apply(Integer id) { ensureClassAvailability(id); }}); forEachClassId(new Procedure4<Integer>() { public void apply(Integer id) { ensureClassRead(id); }}); } private void forEachClassId(Procedure4<Integer> procedure) { for(Iterator4 ids=_stream.classCollection().ids();ids.moveNext();) { procedure.apply((Integer)ids.current()); } } private ReflectClass ensureClassAvailability (int id) { if(id == 0){ return null; } ReflectClass ret = (ReflectClass)_classByID.get(id); if(ret != null){ return ret; } ByteArrayBuffer classreader=_stream.readStatefulBufferById(_trans,id); ClassMarshaller marshaller = marshallerFamily()._class; RawClassSpec spec=marshaller.readSpec(_trans, classreader); String className = spec.name(); ret = lookupByName(className); if(ret != null){ _classByID.put(id, ret); _pendingClasses.add(new Integer(id)); return ret; } reportMissingClass(className); ret = _builder.createClass(className, ensureClassAvailability(spec.superClassID()),spec.numFields()); // step 1 only add to _classByID, keep the class out of _classByName and _classes _classByID.put(id, ret); _pendingClasses.add(new Integer(id)); return ret; } private void reportMissingClass(String className) { _stream.handlers().diagnosticProcessor().classMissed(className); } private void ensureClassRead(int id) { ReflectClass clazz = lookupByID(id); ByteArrayBuffer classreader=_stream.readStatefulBufferById(_trans,id); ClassMarshaller classMarshaller = marshallerFamily()._class; RawClassSpec classInfo=classMarshaller.readSpec(_trans, classreader); String className=classInfo.name(); // Having the class in the _classByName Map for now indicates // that the class is fully read. This is breakable if we start // returning GenericClass'es in other methods like forName // even if a native class has not been found if(lookupByName(className) != null){ return; } // step 2 add the class to _classByName and _classes to denote reading is completed register(className, clazz); int numFields=classInfo.numFields(); ReflectField[] fields=_builder.fieldArray(numFields); FieldMarshaller fieldMarshaller=marshallerFamily()._field; for (int i = 0; i < numFields; i++) { RawFieldSpec fieldInfo=fieldMarshaller.readSpec(_stream, classreader); String fieldName=fieldInfo.name(); ReflectClass fieldClass = reflectClassForFieldSpec(fieldInfo, _stream.reflector()); if (null == fieldClass && (fieldInfo.isField() && !fieldInfo.isVirtual())) { throw new IllegalStateException("Could not read field type for '" + className + "." + fieldName + "'"); } fields[i]=_builder.createField(clazz, fieldName, fieldClass, fieldInfo.isVirtual(), fieldInfo.isPrimitive(), fieldInfo.isArray(), fieldInfo.isNArray()); } _builder.initFields(clazz, fields); _listeners.notifyListeners(clazz); } private void register(String className, ReflectClass clazz) { if (lookupByName(className) != null) { throw new IllegalArgumentException(); } _classByName.put(className, clazz); _classes.add(clazz); } private ReflectClass reflectClassForFieldSpec(RawFieldSpec fieldInfo, Reflector reflector) { if (fieldInfo.isVirtualField()) { return virtualFieldByName(fieldInfo.name()).classReflector(reflector); } final int fieldTypeID = fieldInfo.fieldTypeID(); // need to take care of special handlers here switch (fieldTypeID){ case Handlers4.UNTYPED_ID: return objectClass(); case Handlers4.ANY_ARRAY_ID: return arrayClass(objectClass()); default: ReflectClass fieldClass=forID(fieldTypeID); if (null != fieldClass) { return normalizeFieldClass(fieldInfo, fieldClass); } break; } return null; } private ReflectClass normalizeFieldClass(RawFieldSpec fieldInfo, ReflectClass fieldClass) { // TODO: why the following line is necessary? ReflectClass theClass=_stream.reflector().forName(fieldClass.getName()); if(fieldInfo.isPrimitive()) { theClass = primitiveClass(theClass); } if(fieldInfo.isArray()) { theClass = arrayClass(theClass); } return theClass; } private ReflectClass objectClass() { return _stream.reflector().forClass(Object.class); } private VirtualFieldMetadata virtualFieldByName(final String fieldName) { return _stream.handlers().virtualFieldByName(fieldName); } private MarshallerFamily marshallerFamily() { return MarshallerFamily.forConverterVersion(_stream.converterVersion()); } private ReflectClass ensureClassInitialised (int id) { ReflectClass ret = ensureClassAvailability(id); while(_pendingClasses.size() > 0) { Collection4 pending = _pendingClasses; _pendingClasses = new Collection4(); Iterator4 i = pending.iterator(); while(i.moveNext()) { ensureClassRead(((Integer)i.current()).intValue()); } } return ret; } public Iterator4 classes() { readAll(); return _classes.iterator(); } public void register(int id,ReflectClass clazz) { _classByID.put(id, clazz); } public ReflectClass lookupByID(int id) { return (ReflectClass)_classByID.get(id); } public ReflectClass lookupByName(String name) { return (ReflectClass)_classByName.get(name); } private ReflectClass arrayClass(ReflectClass clazz) { Object proto=clazz.reflector().array().newInstance(clazz,0); return clazz.reflector().forObject(proto); } private ReflectClass primitiveClass(ReflectClass baseClass) { Class primitive=(Class) PRIMITIVES.get(baseClass.getName()); if(primitive!=null) { return baseClass.reflector().forClass(primitive); } return baseClass; } public void addListener(Listener4<ReflectClass> listener) { _listeners.register(listener); } public void removeListener(Listener4<ReflectClass> listener) { _listeners.remove(listener); } }