/* 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.internal; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.metadata.*; import com.db4o.reflect.*; /** * @exclude */ public final class ClassMetadataRepository extends PersistentBase { private Collection4 _classes; private Hashtable4 _creating; private final Transaction _systemTransaction; private Hashtable4 _classMetadataByBytes; private Hashtable4 _classMetadataByClass; private Hashtable4 _classMetadataByName; private Hashtable4 _classMetadataByID; private int _classMetadataCreationDepth; private Queue4 _initClassMetadataOnUp; private final PendingClassInits _classInits; public ClassMetadataRepository(Transaction systemTransaction) { _systemTransaction = systemTransaction; _initClassMetadataOnUp = new NonblockingQueue(); _classInits = new PendingClassInits(_systemTransaction); } public void addClassMetadata(ClassMetadata clazz) { container().setDirtyInSystemTransaction(this); _classes.add(clazz); if(clazz.stateUnread()){ _classMetadataByBytes.put(clazz.i_nameBytes, clazz); }else{ _classMetadataByClass.put(clazz.classReflector(), clazz); } registerClassMetadataById(clazz); } private void registerClassMetadataById(ClassMetadata clazz) { if (clazz.getID() == 0) { clazz.write(_systemTransaction); } _classMetadataByID.put(clazz.getID(), clazz); } private byte[] asBytes(String str){ return container().stringIO().write(str); } public void attachQueryNode(final String fieldName, final Visitor4 visitor) { ClassMetadataIterator i = iterator(); while (i.moveNext()) { final ClassMetadata classMetadata = i.currentClass(); if(! classMetadata.isInternal()){ classMetadata.traverseAllAspects(new TraverseFieldCommand() { @Override protected void process(FieldMetadata field) { if(field.canAddToQuery(fieldName)){ visitor.visit(new Object[] {classMetadata, field}); } } }); } } } public void iterateTopLevelClasses(Visitor4 visitor){ ClassMetadataIterator i = iterator(); while (i.moveNext()) { final ClassMetadata classMetadata = i.currentClass(); if(! classMetadata.isInternal()){ if(classMetadata.getAncestor() == null){ visitor.visit(classMetadata); } } } } void checkChanges() { Iterator4 i = _classes.iterator(); while (i.moveNext()) { ((ClassMetadata)i.current()).checkChanges(); } } final boolean createClassMetadata(ClassMetadata clazz, ReflectClass reflectClazz) { boolean result = false; _classMetadataCreationDepth++; try { ReflectClass parentReflectClazz = reflectClazz.getSuperclass(); ClassMetadata parentClazz = null; if (parentReflectClazz != null && ! parentReflectClazz.equals(container()._handlers.ICLASS_OBJECT)) { parentClazz = produceClassMetadata(parentReflectClazz); } result = container().createClassMetadata(clazz, reflectClazz, parentClazz); } finally { _classMetadataCreationDepth--; } initClassMetadataOnUp(); return result; } private void ensureAllClassesRead() { boolean allClassesRead=false; while(!allClassesRead) { Collection4 unreadClasses=new Collection4(); int numClasses=_classes.size(); Iterator4 classIter = _classes.iterator(); while(classIter.moveNext()) { ClassMetadata clazz=(ClassMetadata)classIter.current(); if(clazz.stateUnread()) { unreadClasses.add(clazz); } } Iterator4 unreadIter=unreadClasses.iterator(); while(unreadIter.moveNext()) { ClassMetadata clazz=(ClassMetadata)unreadIter.current(); clazz = readClassMetadata(clazz,null); if(clazz.classReflector() == null){ clazz.forceRead(); } } allClassesRead=(_classes.size()==numClasses); } applyReadAs(); } boolean fieldExists(String field) { ClassMetadataIterator i = iterator(); while (i.moveNext()) { if (i.currentClass().fieldMetadataForName(field) != null) { return true; } } return false; } public Collection4 forInterface(ReflectClass claxx) { Collection4 col = new Collection4(); ClassMetadataIterator i = iterator(); while (i.moveNext()) { ClassMetadata clazz = i.currentClass(); ReflectClass candidate = clazz.classReflector(); if(! candidate.isInterface()){ if (claxx.isAssignableFrom(candidate)) { col.add(clazz); Iterator4 j = new Collection4(col).iterator(); while (j.moveNext()) { ClassMetadata existing = (ClassMetadata)j.current(); if(existing != clazz){ ClassMetadata higher = clazz.getHigherHierarchy(existing); if (higher != null) { if (higher == clazz) { col.remove(existing); }else{ col.remove(clazz); } } } } } } } return col; } public byte getIdentifier() { return Const4.YAPCLASSCOLLECTION; } ClassMetadata getActiveClassMetadata(ReflectClass reflectClazz) { return (ClassMetadata)_classMetadataByClass.get(reflectClazz); } ClassMetadata classMetadataForReflectClass(ReflectClass reflectClazz) { ClassMetadata cached = (ClassMetadata)_classMetadataByClass.get(reflectClazz); if (cached != null) { return cached; } ClassMetadata byName = (ClassMetadata) _classMetadataByName.get(reflectClazz.getName()); if(byName != null){ return byName; } return readClassMetadata(reflectClazz); } private ClassMetadata readClassMetadata(ReflectClass reflectClazz) { ClassMetadata clazz = (ClassMetadata)_classMetadataByBytes.remove(getNameBytes(reflectClazz.getName())); if (clazz == null) { return null; } return readClassMetadata(clazz, reflectClazz); } ClassMetadata produceClassMetadata(ReflectClass reflectClazz) { final ClassMetadata classMetadata = classMetadataForReflectClass(reflectClazz); if (classMetadata != null ) { return classMetadata; } final ClassMetadata classBeingCreated = (ClassMetadata)_creating.get(reflectClazz); if(classBeingCreated != null){ return classBeingCreated; } final ClassMetadata newClassMetadata = new ClassMetadata(container(), reflectClazz); _creating.put(reflectClazz, newClassMetadata); try { if(! createClassMetadata(newClassMetadata, reflectClazz)){ return null; } // ObjectContainerBase#createClassMetadata may add the ClassMetadata already, // so we have to check again if (!isRegistered(reflectClazz)) { addClassMetadata(newClassMetadata); _classInits.process(newClassMetadata); } else { registerClassMetadataById(newClassMetadata); if (newClassMetadata.aspectsAreNull()) { _classInits.process(newClassMetadata); } } container().setDirtyInSystemTransaction(this); } finally { _creating.remove(reflectClazz); } return newClassMetadata; } private boolean isRegistered(ReflectClass reflectClazz) { return _classMetadataByClass.get(reflectClazz) != null; } ClassMetadata classMetadataForId(int id) { final ClassMetadata classMetadata = (ClassMetadata)_classMetadataByID.get(id); if (null == classMetadata) { return null; } return readClassMetadata(classMetadata, null); } public int classMetadataIdForName(String name) { ClassMetadata classMetadata = (ClassMetadata)_classMetadataByBytes.get(getNameBytes(name)); if(classMetadata == null){ classMetadata = findInitializedClassByName(name); } if(classMetadata != null){ return classMetadata.getID(); } return 0; } public ClassMetadata getClassMetadata(String name) { byte[] nameBytes = getNameBytes(name); ClassMetadata classMetadata = (ClassMetadata)_classMetadataByBytes.get(nameBytes); if (classMetadata == null) { classMetadata = findInitializedClassByName(name); } if(classMetadata != null){ classMetadata = readClassMetadata(classMetadata, null); _classMetadataByBytes.remove(nameBytes); } return classMetadata; } private ClassMetadata findInitializedClassByName(String name){ ClassMetadata classMetadata = (ClassMetadata) _classMetadataByName.get(name); if(classMetadata != null){ return classMetadata; } ClassMetadataIterator i = iterator(); while (i.moveNext()) { classMetadata = (ClassMetadata)i.current(); if (name.equals(classMetadata.getName())) { _classMetadataByName.put(name, classMetadata); return classMetadata; } } return null; } public int getClassMetadataID(String name){ ClassMetadata clazz = (ClassMetadata)_classMetadataByBytes.get(getNameBytes(name)); if(clazz != null){ return clazz.getID(); } return 0; } byte[] getNameBytes(String name) { return asBytes(resolveAliasRuntimeName(name)); } private String resolveAliasRuntimeName(String name) { return container().configImpl().resolveAliasRuntimeName(name); } public void initOnUp(Transaction systemTrans) { _classMetadataCreationDepth++; systemTrans.container().showInternalClasses(true); try { Iterator4 i = _classes.iterator(); while (i.moveNext()) { ((ClassMetadata)i.current()).initOnUp(systemTrans); } } finally { systemTrans.container().showInternalClasses(false); _classMetadataCreationDepth--; } initClassMetadataOnUp(); } void initTables(int size) { _classes = new Collection4(); _classMetadataByBytes = new Hashtable4(size); if (size < 16) { size = 16; } _classMetadataByClass = new Hashtable4(size); _classMetadataByName = new Hashtable4(size); _classMetadataByID = new Hashtable4(size); _creating = new Hashtable4(1); } private void initClassMetadataOnUp() { if(_classMetadataCreationDepth != 0){ return; } ClassMetadata clazz = (ClassMetadata)_initClassMetadataOnUp.next(); while(clazz != null){ clazz.initOnUp(_systemTransaction); clazz = (ClassMetadata)_initClassMetadataOnUp.next(); } } public ClassMetadataIterator iterator(){ return new ClassMetadataIterator(this, new ArrayIterator4(_classes.toArray())); } private static class ClassIDIterator extends MappingIterator { public ClassIDIterator(Collection4 classes) { super(classes.iterator()); } protected Object map(Object current) { return new Integer(((ClassMetadata)current).getID()); } } public Iterator4 ids(){ return new ClassIDIterator(_classes); } public int ownLength() { return Const4.OBJECT_LENGTH + Const4.INT_LENGTH + (_classes.size() * Const4.ID_LENGTH); } void purge() { Iterator4 i = _classes.iterator(); while (i.moveNext()) { ((ClassMetadata)i.current()).purge(); } } public final void readThis(Transaction trans, ByteArrayBuffer buffer) { int classCount = buffer.readInt(); initTables(classCount); ObjectContainerBase container = container(); int[] ids = readMetadataIds(buffer, classCount); ByteArrayBuffer[] metadataSlots = container.readSlotBuffers(trans, ids); for (int i = 0; i < classCount; ++i) { ClassMetadata classMetadata = new ClassMetadata(container, null); classMetadata.setID(ids[i]); _classes.add(classMetadata); _classMetadataByID.put(ids[i], classMetadata); byte[] name = classMetadata.readName1(trans, metadataSlots[i]); if (name != null) { _classMetadataByBytes.put(name, classMetadata); } } applyReadAs(); } private int[] readMetadataIds(ByteArrayBuffer buffer, int classCount) { int[] ids = new int[classCount]; for (int i = 0; i < classCount; ++i) { ids[i] = buffer.readInt(); } return ids; } Hashtable4 classByBytes(){ return _classMetadataByBytes; } private void applyReadAs(){ final Hashtable4 readAs = container().configImpl().readAs(); Iterator4 i = readAs.iterator(); while(i.moveNext()){ Entry4 entry = (Entry4) i.current(); String dbName = (String)entry.key(); String useName = (String)entry.value(); byte[] dbbytes = getNameBytes(dbName); byte[] useBytes = getNameBytes(useName); if(classByBytes().get(useBytes) == null){ ClassMetadata clazz = (ClassMetadata)classByBytes().get(dbbytes); if(clazz != null){ clazz.i_nameBytes = useBytes; clazz.setConfig(configClass(dbName)); classByBytes().remove(dbbytes); classByBytes().put(useBytes, clazz); } } } } private Config4Class configClass(String name) { return container().configImpl().configClass(name); } public ClassMetadata readClassMetadata(ClassMetadata classMetadata, ReflectClass clazz) { if(classMetadata == null){ throw new ArgumentNullException(); } if (! classMetadata.stateUnread()) { return classMetadata; } _classMetadataCreationDepth++; try { classMetadata.resolveNameConfigAndReflector(this,clazz); ReflectClass claxx = classMetadata.classReflector(); if(claxx != null){ _classMetadataByClass.put(claxx, classMetadata); classMetadata.readThis(); classMetadata.checkChanges(); _initClassMetadataOnUp.add(classMetadata); } } finally { _classMetadataCreationDepth--; } initClassMetadataOnUp(); return classMetadata; } public void checkAllClassChanges(){ Iterator4 i = _classMetadataByID.keys(); while(i.moveNext()){ int classMetadataID = ((Integer)i.current()).intValue(); classMetadataForId(classMetadataID); } } public void refreshClasses() { ClassMetadataRepository rereader = new ClassMetadataRepository(_systemTransaction); rereader._id = _id; rereader.read(container().systemTransaction()); Iterator4 i = rereader._classes.iterator(); while (i.moveNext()) { ClassMetadata clazz = (ClassMetadata)i.current(); refreshClass(clazz); } i = _classes.iterator(); while (i.moveNext()) { ClassMetadata clazz = (ClassMetadata)i.current(); clazz.refresh(); } } private void refreshClass(ClassMetadata clazz) { if (_classMetadataByID.get(clazz.getID()) == null) { _classes.add(clazz); _classMetadataByID.put(clazz.getID(), clazz); refreshClassCache(clazz, null); } } public void refreshClassCache(ClassMetadata clazz, ReflectClass oldReflector) { if(clazz.stateUnread()){ _classMetadataByBytes.put(clazz.readName(_systemTransaction), clazz); }else{ if(oldReflector != null){ _classMetadataByClass.remove(oldReflector); } _classMetadataByClass.put(clazz.classReflector(), clazz); } } void reReadClassMetadata(ClassMetadata clazz){ if(clazz != null){ reReadClassMetadata(clazz._ancestor); clazz.readName(_systemTransaction); clazz.forceRead(); clazz.setStateClean(); clazz.bitFalse(Const4.CHECKED_CHANGES); clazz.bitFalse(Const4.READING); clazz.bitFalse(Const4.CONTINUE); clazz.bitFalse(Const4.DEAD); clazz.checkChanges(); } } public StoredClass[] storedClasses() { ensureAllClassesRead(); StoredClass[] sclasses = new StoredClass[_classes.size()]; _classes.toArray(sclasses); return sclasses; } public void writeAllClasses(){ Collection4 deadClasses = new Collection4(); StoredClass[] storedClasses = storedClasses(); for (int i = 0; i < storedClasses.length; i++) { ClassMetadata clazz = (ClassMetadata)storedClasses[i]; clazz.setStateDirty(); if(clazz.stateDead()){ deadClasses.add(clazz); clazz.setStateOK(); } } for (int i = 0; i < storedClasses.length; i++) { ClassMetadata clazz = (ClassMetadata)storedClasses[i]; clazz.write(_systemTransaction); } Iterator4 it = deadClasses.iterator(); while(it.moveNext()){ ((ClassMetadata)it.current()).setStateDead(); } } public void writeThis(Transaction trans, ByteArrayBuffer buffer) { buffer.writeInt(_classes.size()); Iterator4 i = _classes.iterator(); while (i.moveNext()) { buffer.writeIDOf(trans, i.current()); } } public String toString(){ String str = "Active:\n"; Iterator4 i = _classes.iterator(); while(i.moveNext()){ ClassMetadata clazz = (ClassMetadata)i.current(); str += clazz.getID() + " " + clazz + "\n"; } return str; } ObjectContainerBase container() { return _systemTransaction.container(); } public void setID(int id) { if (container().isClient()) { super.setID(id); return; } if(_id == 0) { systemData().classCollectionID(id); } super.setID(id); } private SystemData systemData() { return localSystemTransaction().localContainer().systemData(); } private LocalTransaction localSystemTransaction() { return ((LocalTransaction)_systemTransaction); } public void classMetadataNameResolved(ClassMetadata classMetadata, byte[] nameBytes) { _classMetadataByBytes.remove(nameBytes); _classMetadataByName.put(classMetadata.getName(), classMetadata); } }