Package com.db4o.internal

Source Code of com.db4o.internal.ClassMetadata

/* Copyright (C) 2004 - 2006  db4objects Inc.  http://www.db4o.com

This file is part of the db4o open source object database.

db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
package com.db4o.internal;

import com.db4o.*;
import com.db4o.config.*;
import com.db4o.config.ObjectMarshaller;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.classindex.*;
import com.db4o.internal.diagnostic.*;
import com.db4o.internal.handlers.*;
import com.db4o.internal.marshall.*;
import com.db4o.internal.query.processor.*;
import com.db4o.internal.slots.*;
import com.db4o.query.*;
import com.db4o.reflect.*;
import com.db4o.reflect.generic.*;


/**
* @exclude
*/
public class ClassMetadata extends PersistentBase implements TypeHandler4, StoredClass {

    public ClassMetadata i_ancestor;

    Config4Class i_config;
    public int _metaClassID;
   
    public FieldMetadata[] i_fields;
   
    private final ClassIndexStrategy _index;
   
    protected String i_name;

    protected final ObjectContainerBase i_stream;

    byte[] i_nameBytes;
    private Buffer i_reader;

    private boolean _classIndexed;
   
    private ReflectClass _reflector;
    private boolean _isEnum;
    public boolean i_dontCallConstructors;
   
    private EventDispatcher _eventDispatcher;
   
    private boolean _internal;
   
    private boolean _unversioned;
   
  // for indexing purposes.
    // TODO: check race conditions, upon multiple calls against the same class
    private int i_lastID;
   
    private TernaryBool _canUpdateFast=TernaryBool.UNSPECIFIED;
   
    public final boolean canUpdateFast(){
      return _canUpdateFast.booleanValue(checkCanUpdateFast());
    }
   
    private final boolean checkCanUpdateFast() {
      if(i_ancestor != null && ! i_ancestor.canUpdateFast()){
        return false;
      }
    if(i_config != null && i_config.cascadeOnDelete() == TernaryBool.YES) {
      return false;
    }
    for(int i = 0; i < i_fields.length; ++i) {
      if(i_fields[i].hasIndex()) {
        return false;
      }
    }
    return true;
  }

  boolean isInternal() {
      return _internal;
    }

    private ClassIndexStrategy createIndexStrategy() {
    return new BTreeClassIndexStrategy(this);
  }

    ClassMetadata(ObjectContainerBase stream, ReflectClass reflector){
      i_stream = stream;
        _reflector = reflector;
        _index = createIndexStrategy();
        _classIndexed = true;
    }
   
    void activateFields(Transaction a_trans, Object a_object, int a_depth) {
        if(objectCanActivate(a_trans.stream(), a_object)){
            activateFields1(a_trans, a_object, a_depth);
        }
    }

    void activateFields1(Transaction a_trans, Object a_object, int a_depth) {
        for (int i = 0; i < i_fields.length; i++) {
            i_fields[i].cascadeActivation(a_trans, a_object, a_depth, true);
        }
        if (i_ancestor != null) {
            i_ancestor.activateFields1(a_trans, a_object, a_depth);
        }
    }

    public final void addFieldIndices(StatefulBuffer a_writer, Slot oldSlot) {
        if(hasIndex() || hasVirtualAttributes()){
            ObjectHeader oh = new ObjectHeader(i_stream, this, a_writer);
            oh._marshallerFamily._object.addFieldIndices(this, oh._headerAttributes, a_writer, oldSlot);
        }
    }
   
    void addMembers(ObjectContainerBase ocb) {
        bitTrue(Const4.CHECKED_CHANGES);
        if (installTranslator(ocb) || installMarshaller(ocb)) {
          return;
        }

        if (ocb.detectSchemaChanges()) {
            boolean dirty = isDirty();

            Collection4 members = new Collection4();

            if (null != i_fields) {
              members.addAll(i_fields);
              if(i_fields.length==1&&i_fields[0] instanceof TranslatedFieldMetadata) {
                setStateOK();
                return;
              }
            }
            if(generateVersionNumbers()) {
                if(! hasVersionField()) {
                    members.add(ocb.getVersionIndex());
                    dirty = true;
                }
            }
            if(generateUUIDs()) {
                if(! hasUUIDField()) {
                    members.add(ocb.getUUIDIndex());
                    dirty = true;
                }
            }
            dirty = collectReflectFields(ocb, members) | dirty;
            if (dirty) {
                i_stream.setDirtyInSystemTransaction(this);
                i_fields = new FieldMetadata[members.size()];
                members.toArray(i_fields);
                for (int i = 0; i < i_fields.length; i++) {
                    i_fields[i].setArrayPosition(i);
                }
            } else {
                if (members.size() == 0) {
                    i_fields = new FieldMetadata[0];
                }
            }
           
            DiagnosticProcessor dp = i_stream.i_handlers._diagnosticProcessor;
            if(dp.enabled()){
                dp.checkClassHasFields(this);
            }
           
        } else {
            if (i_fields == null) {
                i_fields = new FieldMetadata[0];
            }
        }
        setStateOK();
        ocb.callbacks().classOnRegistered(this);
    }

  private boolean collectReflectFields(ObjectContainerBase stream, Collection4 collectedFields) {
    boolean dirty=false;
    ReflectField[] fields = reflectFields();
    for (int i = 0; i < fields.length; i++) {
        if (storeField(fields[i])) {
            TypeHandler4 wrapper = stream.i_handlers.handlerForClass(stream, fields[i].getFieldType());
            if (wrapper == null) {
                continue;
            }
            FieldMetadata field = new FieldMetadata(this, fields[i], wrapper);

            boolean found = false;
            Iterator4 m = collectedFields.iterator();
            while (m.moveNext()) {
                if (((FieldMetadata)m.current()).equals(field)) {
                    found = true;
                    break;
                }
            }
            if (found) {
                continue;
            }

            // this has no effect on YapClients
            dirty = true;
            // we need a local dirty flag to tell us to reconstruct
            // i_fields

            collectedFields.add(field);
        }
    }
    return dirty;
  }
 
  private boolean installMarshaller(ObjectContainerBase ocb) {
    ObjectMarshaller om = getMarshaller();
      if (om == null) {
        return false;
      }
      installCustomFieldMetadata(ocb, new CustomMarshallerFieldMetadata(this, om));
      return true;
  }
 

    private boolean installTranslator(ObjectContainerBase ocb) {
      ObjectTranslator ot = getTranslator();
      if (ot == null) {
        return false;
      }
      if (isNewTranslator(ot)) {
        i_stream.setDirtyInSystemTransaction(this);
      }
        installCustomFieldMetadata(ocb, new TranslatedFieldMetadata(this, ot));
      return true;
    }

  private void installCustomFieldMetadata(ObjectContainerBase ocb, FieldMetadata customFieldMetadata) {
    int fieldCount = 1;
       
        boolean versions = generateVersionNumbers() && ! ancestorHasVersionField();
        boolean uuids = generateUUIDs()  && ! ancestorHasUUIDField();
       
        if(versions){
            fieldCount = 2;
        }
       
        if(uuids){
            fieldCount = 3;
        }
     
      i_fields = new FieldMetadata[fieldCount];
     
        i_fields[0] = customFieldMetadata;
       
       
        // Some explanation on the thoughts here:
       
        // Since i_fields for the translator are generated every time,
        // we want to make sure that the order of fields is consistent.
       
        // Therefore it's easier to implement with fixed index places in
        // the i_fields array:
       
        // [0] is the translator
        // [1] is the version
        // [2] is the UUID
       
        if(versions || uuids) {
           
            // We don't want to have a null field, so let's add the version
            // number, if we have a UUID, even if it's not needed.
           
            i_fields[1] = ocb.getVersionIndex();
        }
       
        if(uuids){
            i_fields[2] = ocb.getUUIDIndex();
        }
       
      setStateOK();
  }
   
    private ObjectTranslator getTranslator() {
      return i_config == null
        ? null
        : i_config.getTranslator();
    }
   
    private ObjectMarshaller getMarshaller() {
      return i_config == null
    ? null
    : i_config.getMarshaller();
    }

  private boolean isNewTranslator(ObjectTranslator ot) {
    return !hasFields()
        || !ot.getClass().getName().equals(i_fields[0].getName());
  }

  private boolean hasFields() {
    return i_fields != null
        && i_fields.length > 0;
  }

    void addToIndex(LocalObjectContainer a_stream, Transaction a_trans, int a_id) {
        if (a_stream.maintainsIndices()) {
            addToIndex1(a_stream, a_trans, a_id);
        }
    }

    void addToIndex1(LocalObjectContainer a_stream, Transaction a_trans, int a_id) {
        if (i_ancestor != null) {
            i_ancestor.addToIndex1(a_stream, a_trans, a_id);
        }
        if (hasIndex()) {
            _index.add(a_trans, a_id);
        }
    }

    boolean allowsQueries() {
        return hasIndex();
    }

    public boolean canHold(ReflectClass claxx) {
        if (claxx == null) {
            return true;
        }
        if (_reflector != null) {
          if(classReflector().isCollection()){
                return true;
            }
            return classReflector().isAssignableFrom(claxx);
        }
        return false;
    }
   
    public void cascadeActivation(
        Transaction a_trans,
        Object a_object,
        int a_depth,
        boolean a_activate) {
        Config4Class config = configOrAncestorConfig();
        if (config != null) {
            if (a_activate) {
                a_depth = config.adjustActivationDepth(a_depth);
            }
        }
        if (a_depth > 0) {
            ObjectContainerBase stream = a_trans.stream();
            if (a_activate) {
                if(isValueType()){
                    activateFields(a_trans, a_object, a_depth - 1);
                }else{
                    stream.stillToActivate(a_object, a_depth - 1);
                }
            } else {
                stream.stillToDeactivate(a_object, a_depth - 1, false);
            }
        }
    }

    void checkChanges() {
        if (stateOK()) {
            if (!bitIsTrue(Const4.CHECKED_CHANGES)) {
                bitTrue(Const4.CHECKED_CHANGES);
                if (i_ancestor != null) {
                    i_ancestor.checkChanges();
                    // Ancestor first, so the object length calculates
                    // correctly
                }
                if (_reflector != null) {
                    addMembers(i_stream);
                    if (!i_stream.isClient()) {
                        write(i_stream.getSystemTransaction());
                    }
                }
            }
        }
    }
   
    public void checkType() {
        ReflectClass claxx = classReflector();
        if (claxx == null){
            return;
        }
        if (i_stream.i_handlers.ICLASS_INTERNAL.isAssignableFrom(claxx)) {
            _internal = true;
        }
        if (i_stream.i_handlers.ICLASS_UNVERSIONED.isAssignableFrom(claxx)) {
            _unversioned = true;
        }       
        if (i_stream.i_handlers.ICLASS_DB4OTYPEIMPL.isAssignableFrom(claxx)) {
          Db4oTypeImpl db4oTypeImpl = (Db4oTypeImpl) claxx.newInstance();
          _classIndexed = (db4oTypeImpl == null || db4oTypeImpl.hasClassIndex());
    } else if(i_config != null){
      _classIndexed = i_config.indexed();
    }
    }

    public void checkUpdateDepth(StatefulBuffer a_bytes) {
        int depth = a_bytes.getUpdateDepth();
        Config4Class config = configOrAncestorConfig();
        if (depth == Const4.UNSPECIFIED) {
            depth = checkUpdateDepthUnspecified(a_bytes.getStream());
            if (classReflector().isCollection()) {
                depth = adjustDepth(depth);
            }
        }
        if ((config != null && (config.cascadeOnDelete() == TernaryBool.YES || config.cascadeOnUpdate() == TernaryBool.YES))) {
            depth = adjustDepth(depth);
        }
        a_bytes.setUpdateDepth(depth - 1);
    }

  private int adjustDepth(int depth) {
    int depthBorder = reflector().collectionUpdateDepth(classReflector());
    if (depth>Integer.MIN_VALUE && depth < depthBorder) {
        depth = depthBorder;
    }
    return depth;
  }

    int checkUpdateDepthUnspecified(ObjectContainerBase a_stream) {
        int depth = a_stream.configImpl().updateDepth() + 1;
        if (i_config != null && i_config.updateDepth() != 0) {
            depth = i_config.updateDepth() + 1;
        }
        if (i_ancestor != null) {
            int ancestordepth = i_ancestor.checkUpdateDepthUnspecified(a_stream);
            if (ancestordepth > depth) {
                return ancestordepth;
            }
        }
        return depth;
    }

    public Object coerce(ReflectClass claxx, Object obj) {
        return canHold(claxx) ? obj : No4.INSTANCE;
    }

    public void collectConstraints(
        Transaction a_trans,
        QConObject a_parent,
        Object a_object,
        Visitor4 a_visitor) {
        if (i_fields != null) {
            for (int i = 0; i < i_fields.length; i++) {
                i_fields[i].collectConstraints(a_trans, a_parent, a_object, a_visitor);
            }
        }
        if (i_ancestor != null) {
            i_ancestor.collectConstraints(a_trans, a_parent, a_object, a_visitor);
        }
    }
   
    public final TreeInt collectFieldIDs(MarshallerFamily mf, ObjectHeaderAttributes attributes, TreeInt tree, StatefulBuffer a_bytes, String name) {
        return mf._object.collectFieldIDs(tree, this, attributes, a_bytes, name);
    }

    public final boolean configInstantiates(){
        return i_config != null && i_config.instantiates();
    }
   
    public Config4Class config() {
      return i_config;
    }

    public Config4Class configOrAncestorConfig() {
        if (i_config != null) {
            return i_config;
        }
        if (i_ancestor != null) {
            return i_ancestor.configOrAncestorConfig();
        }
        return null;
    }

    public void copyValue(Object a_from, Object a_to) {
        // do nothing
    }

    private boolean createConstructor(ObjectContainerBase a_stream, String a_name) {
       
        ReflectClass claxx;
        try {
          claxx = a_stream.reflector().forName(a_name);
        } catch (Throwable t) {
            claxx = null;
        }
       
        return createConstructor(a_stream,claxx , a_name, true);
    }

    public boolean createConstructor(ObjectContainerBase a_stream, ReflectClass a_class, String a_name, boolean errMessages) {
       
        _reflector = a_class;
       
        _eventDispatcher = EventDispatcher.forClass(a_stream, a_class);
       
        if(! Deploy.csharp){
            if(a_class != null){
                _isEnum = Platform4.jdk().isEnum(reflector(), a_class);
            }
        }
       
        if(configInstantiates()){
            return true;
        }
       
        if(a_class != null){
            if(a_stream.i_handlers.ICLASS_TRANSIENTCLASS.isAssignableFrom(a_class)
              || Platform4.isTransient(a_class)) {
                a_class = null;
            }
        }
        if (a_class == null) {
            if(a_name == null || !Platform4.isDb4oClass(a_name)){
                if(errMessages){
                    a_stream.logMsg(23, a_name);
                }
            }
            setStateDead();
            return false;
        }
       
        if(a_stream.i_handlers.createConstructor(a_class, ! callConstructor())){
            return true;
        }
       
        setStateDead();
        if(errMessages){
            a_stream.logMsg(7, a_name);
        }
       
        if (a_stream.configImpl().exceptionsOnNotStorable()) {
            throw new ObjectNotStorableException(a_class);
        }

        return false;
       
    }

  public void deactivate(Transaction a_trans, Object a_object, int a_depth) {
        if(objectCanDeactivate(a_trans.stream(), a_object)){
            deactivate1(a_trans, a_object, a_depth);
            objectOnDeactivate(a_trans.stream(), a_object);
        }
    }

  private void objectOnDeactivate(ObjectContainerBase stream, Object obj) {
    stream.callbacks().objectOnDeactivate(obj);
    dispatchEvent(stream, obj, EventDispatcher.DEACTIVATE);
  }

  private boolean objectCanDeactivate(ObjectContainerBase stream, Object obj) {
    return stream.callbacks().objectCanDeactivate(obj)
      && dispatchEvent(stream, obj, EventDispatcher.CAN_DEACTIVATE);
  }

    void deactivate1(Transaction a_trans, Object a_object, int a_depth) {
       
        for (int i = 0; i < i_fields.length; i++) {
            i_fields[i].deactivate(a_trans, a_object, a_depth);
        }
        if (i_ancestor != null) {
            i_ancestor.deactivate1(a_trans, a_object, a_depth);
        }
    }

    final void delete(StatefulBuffer a_bytes, Object a_object) {
        ObjectHeader oh = new ObjectHeader(i_stream, this, a_bytes);
        delete1(oh._marshallerFamily, oh._headerAttributes, a_bytes, a_object);
    }

    private final void delete1(MarshallerFamily mf, ObjectHeaderAttributes attributes, StatefulBuffer a_bytes, Object a_object) {
        removeFromIndex(a_bytes.getTransaction(), a_bytes.getID());
        deleteMembers(mf, attributes, a_bytes, a_bytes.getTransaction().stream().i_handlers.arrayType(a_object), false);
    }

    public void deleteEmbedded(MarshallerFamily mf, StatefulBuffer a_bytes) {
        if (a_bytes.cascadeDeletes() > 0) {
            int id = a_bytes.readInt();
            if (id > 0) {
                deleteEmbedded1(mf, a_bytes, id);
            }
        } else {
            a_bytes.incrementOffset(linkLength());
        }
    }

    public void deleteEmbedded1(MarshallerFamily mf, StatefulBuffer a_bytes, int a_id) {
        if (a_bytes.cascadeDeletes() > 0) {
         
          ObjectContainerBase stream = a_bytes.getStream();
           
            // short-term reference to prevent WeakReference-gc to hit
            Object obj = stream.getByID2(a_bytes.getTransaction(), a_id);

            int cascade = a_bytes.cascadeDeletes() - 1;
            if (obj != null) {
                if (isCollection(obj)) {
                    cascade += reflector().collectionUpdateDepth(reflector().forObject(obj)) - 1;
                }
            }

            ObjectReference yo = stream.getYapObject(a_id);
            if (yo != null) {
                a_bytes.getStream().delete2(a_bytes.getTransaction(), yo, obj,cascade, false);
            }
        }
    }

    void deleteMembers(MarshallerFamily mf, ObjectHeaderAttributes attributes, StatefulBuffer a_bytes, int a_type, boolean isUpdate) {
        try{
          Config4Class config = configOrAncestorConfig();
          if (config != null && (config.cascadeOnDelete() == TernaryBool.YES)) {
              int preserveCascade = a_bytes.cascadeDeletes();
              if (classReflector().isCollection()) {
                  int newCascade =
                      preserveCascade + reflector().collectionUpdateDepth(classReflector()) - 3;
                  if (newCascade < 1) {
                      newCascade = 1;
                  }
                  a_bytes.setCascadeDeletes(newCascade);
              } else {
                  a_bytes.setCascadeDeletes(1);
              }
                mf._object.deleteMembers(this, attributes, a_bytes, a_type, isUpdate);
              a_bytes.setCascadeDeletes(preserveCascade);
          } else {
                mf._object.deleteMembers(this, attributes, a_bytes, a_type, isUpdate);
          }
        }catch(Exception e){
           
            // This a catch for changed class hierarchies.
            // It's quite ugly to catch all here but it does
            // help to heal migration from earlier db4o
            // versions.
           
            if(Debug.atHome){
                e.printStackTrace();
            }
        }
    }

    public final boolean dispatchEvent(ObjectContainerBase stream, Object obj, int message) {
      if(_eventDispatcher == null || ! stream.dispatchsEvents()){
        return true;
      }
        return _eventDispatcher.dispatch(stream, obj, message);
    }
   
    public final boolean isEqual(TypeHandler4 a_dataType) {
        return (this == a_dataType);
    }
   
    public final int fieldCount(){
        int count = i_fields.length;
       
        if(i_ancestor != null){
            count += i_ancestor.fieldCount();
        }
       
        return count;
    }
   
    private static class FieldMetadataIterator implements Iterator4 {
      private final ClassMetadata _initialClazz;
      private ClassMetadata _curClazz;
      private int _curIdx;
     
      public FieldMetadataIterator(ClassMetadata clazz) {
        _initialClazz=clazz;
        reset();
      }
     
    public Object current() {
      return _curClazz.i_fields[_curIdx];
    }

    public boolean moveNext() {
      if(_curClazz==null) {
        _curClazz=_initialClazz;
        _curIdx=0;
      } else {
        _curIdx++;
      }
      while(_curClazz!=null&&!indexInRange()) {
        _curClazz=_curClazz.i_ancestor;
        _curIdx=0;
      }
      return _curClazz!=null&&indexInRange();
    }

    public void reset() {
        _curClazz=null;
        _curIdx=-1;
    }

    private boolean indexInRange() {
      return _curIdx<_curClazz.i_fields.length;
    }
  }
   
  public Iterator4 fields() {
    return new FieldMetadataIterator(this);
  }

    // Scrolls offset in passed reader to the offset the passed field should
    // be read at.
    //
  // returns null if not successful or if the field value at this offset is null
    // returns MarshallerFamily from the object header if it is successful and
    //         if the value at this offset is not null
    public final MarshallerFamily findOffset(Buffer a_bytes, FieldMetadata a_field) {
        if (a_bytes == null) {
            return null;
        }
        a_bytes._offset = 0;
        ObjectHeader oh = new ObjectHeader(i_stream, this, a_bytes);
        boolean res = oh.objectMarshaller().findOffset(this, oh._headerAttributes, a_bytes, a_field);
        if(! res){
            return null;
        }
        return oh._marshallerFamily;
    }

    void forEachYapField(Visitor4 visitor) {
        if (i_fields != null) {
            for (int i = 0; i < i_fields.length; i++) {
                visitor.visit(i_fields[i]);
            }
        }
        if (i_ancestor != null) {
            i_ancestor.forEachYapField(visitor);
        }
    }
   
    public static ClassMetadata forObject(Transaction trans, Object obj, boolean allowCreation){
        ReflectClass reflectClass = trans.reflector().forObject(obj);
        if (reflectClass != null && reflectClass.getSuperclass() == null && obj != null) {
          throw new ObjectNotStorableException(obj.toString());
        }
        if(allowCreation){
          return trans.stream().produceYapClass(reflectClass);
        }
        return trans.stream().getYapClass(reflectClass);
    }
   
    public boolean generateUUIDs() {
        if(! generateVirtual()){
            return false;
        }
        boolean configValue = (i_config == null) ? false : i_config.generateUUIDs();
        return generate1(i_stream.config().generateUUIDs(), configValue);
    }

    private boolean generateVersionNumbers() {
        if(! generateVirtual()){
            return false;
        }
        boolean configValue = (i_config == null) ? false : i_config.generateVersionNumbers();
        return generate1(i_stream.config().generateVersionNumbers(), configValue);
    }
   
    private boolean generateVirtual(){
        if(_unversioned){
            return false;
        }
        if(_internal){
            return false;
        }
        return true;
    }
   
    private boolean generate1(ConfigScope globalConfig, boolean individualConfig) {
      return globalConfig.applyConfig(individualConfig);
    }


    ClassMetadata getAncestor() {
        return i_ancestor;
    }

    public Object getComparableObject(Object forObject) {
        if (i_config != null) {
            if (i_config.queryAttributeProvider() != null) {
                return i_config.queryAttributeProvider().attribute(forObject);
            }
        }
        return forObject;
    }

    public ClassMetadata getHigherHierarchy(ClassMetadata a_yapClass) {
        ClassMetadata yc = getHigherHierarchy1(a_yapClass);
        if (yc != null) {
            return yc;
        }
        return a_yapClass.getHigherHierarchy1(this);
    }

    private ClassMetadata getHigherHierarchy1(ClassMetadata a_yapClass) {
        if (a_yapClass == this) {
            return this;
        }
        if (i_ancestor != null) {
            return i_ancestor.getHigherHierarchy1(a_yapClass);
        }
        return null;
    }

    public ClassMetadata getHigherOrCommonHierarchy(ClassMetadata a_yapClass) {
        ClassMetadata yc = getHigherHierarchy1(a_yapClass);
        if (yc != null) {
            return yc;
        }
        if (i_ancestor != null) {
            yc = i_ancestor.getHigherOrCommonHierarchy(a_yapClass);
            if (yc != null) {
                return yc;
            }
        }
        return a_yapClass.getHigherHierarchy1(this);
    }

    public byte getIdentifier() {
        return Const4.YAPCLASS;
    }

    public long[] getIDs() {
        synchronized(i_stream.i_lock){
          if (! stateOK()) {
                return new long[0];
            }
          return getIDs(i_stream.getTransaction());
        }
    }

    public long[] getIDs(Transaction trans) {
        if (! stateOK()) {
            return new long[0];
        }       
        if (! hasIndex()) {
            return new long[0];
        }       
        return trans.stream().getIDsForClass(trans, this);
    }

    public boolean hasIndex() {
        return _classIndexed;
    }
   
    private boolean ancestorHasUUIDField(){
        if(i_ancestor == null) {
            return false;
        }
        return i_ancestor.hasUUIDField();
    }
   
    private boolean hasUUIDField() {
        if(ancestorHasUUIDField()){
            return true;
        }
        return Arrays4.containsInstanceOf(i_fields, UUIDFieldMetadata.class);
    }
   
    private boolean ancestorHasVersionField(){
        if(i_ancestor == null){
            return false;
        }
        return i_ancestor.hasVersionField();
    }
   
    private boolean hasVersionField() {
        if(ancestorHasVersionField()){
            return true;
        }
        return Arrays4.containsInstanceOf(i_fields, VersionFieldMetadata.class);
    }

    public ClassIndexStrategy index() {
      return _index;
    }   
   
    public int indexEntryCount(Transaction ta){
        if(!stateOK()){
            return 0;
        }
        return _index.entryCount(ta);
    }
   
    public Object indexEntryToObject(Transaction trans, Object indexEntry){
        if(indexEntry == null){
            return null;
        }
        int id = ((Integer)indexEntry).intValue();
        return getStream().getByID2(trans, id);
    }   

    public ReflectClass classReflector(){
        return _reflector;
    }

    public String getName() {
        if(i_name == null){
            if(_reflector != null){
                i_name = _reflector.getName();
            }
        }
        return i_name;
    }
   
    public StoredClass getParentStoredClass(){
        return getAncestor();
    }

    public StoredField[] getStoredFields(){
        synchronized(i_stream.i_lock){
          if(i_fields == null){
              return null;
          }
          StoredField[] fields = new StoredField[i_fields.length];
          System.arraycopy(i_fields, 0, fields, 0, i_fields.length);
          return fields;
        }
    }

    ObjectContainerBase getStream() {
        return i_stream;
    }

    public int getTypeID() {
        return Const4.TYPE_CLASS;
    }

    public ClassMetadata getYapClass(ObjectContainerBase a_stream) {
        return this;
    }

    public FieldMetadata getYapField(final String name) {
        final FieldMetadata[] yf = new FieldMetadata[1];
        forEachYapField(new Visitor4() {
            public void visit(Object obj) {
                if (name.equals(((FieldMetadata)obj).getName())) {
                    yf[0] = (FieldMetadata)obj;
                }
            }
        });
        return yf[0];

    }
   
    public boolean hasFixedLength(){
        return true;
    }

    public boolean hasField(ObjectContainerBase a_stream, String a_field) {
      if(classReflector().isCollection()){
            return true;
        }
        return getYapField(a_field) != null;
    }
   
    boolean hasVirtualAttributes(){
        if(_internal){
            return false;
        }
        return hasVersionField() || hasUUIDField();
    }

    public boolean holdsAnyClass() {
      return classReflector().isCollection();
    }

    void incrementFieldsOffset1(Buffer a_bytes) {
        int length = Debug.atHome ? readFieldCountSodaAtHome(a_bytes) : readFieldCount(a_bytes);
        for (int i = 0; i < length; i++) {
            i_fields[i].incrementOffset(a_bytes);
        }
    }

    public Object comparableObject(Transaction a_trans, Object a_object) {
        return a_object;
    }
   
    final boolean init( ObjectContainerBase a_stream, ClassMetadata a_ancestor,ReflectClass claxx) {
       
        if(DTrace.enabled){
            DTrace.YAPCLASS_INIT.log(getID());
        }
       
        i_ancestor = a_ancestor;
       
        Config4Impl config = a_stream.configImpl();
        String className = claxx.getName();   
    setConfig(config.configClass(className));
       
        if(! createConstructor(a_stream, claxx, className, false)){
            return false;
        }
       
        checkType();
        if (allowsQueries()) {
            _index.initialize(a_stream);
        }
        i_name = className;
        i_ancestor = a_ancestor;
        bitTrue(Const4.CHECKED_CHANGES);
       
        return true;
    }
   
    final void initConfigOnUp(Transaction systemTrans) {
        Config4Class extendedConfig=Platform4.extendConfiguration(_reflector, i_stream.configure(), i_config);
      if(extendedConfig!=null) {
        i_config=extendedConfig;
      }
        if (i_config == null) {
            return;
        }
        if (! stateOK()) {
            return;
        }
       
        if (i_fields == null) {
            return;
        }
       
        for (int i = 0; i < i_fields.length; i++) {
            FieldMetadata curField = i_fields[i];
            String fieldName = curField.getName();
      if(!curField.hasConfig()&&extendedConfig!=null&&extendedConfig.configField(fieldName)!=null) {
              curField.initIndex(this,fieldName);
            }
      curField.initConfigOnUp(systemTrans);
        }
    }

    void initOnUp(Transaction systemTrans) {
        if (! stateOK()) {
            return;
        }
        initConfigOnUp(systemTrans);
        storeStaticFieldValues(systemTrans, false);
    }

  Object instantiate(ObjectReference ref, Object obj, MarshallerFamily mf, ObjectHeaderAttributes attributes, StatefulBuffer buffer, boolean addToIDTree) {
       
        // overridden in YapClassPrimitive
        // never called for primitive YapAny
   
    adjustInstantiationDepth(buffer);

    final ObjectContainerBase stream = buffer.getStream();
    final boolean instantiating = (obj == null);
    if (instantiating) {
      obj = instantiateObject(buffer, mf);
      if (obj == null) {
        return null;
     
           
      shareTransaction(obj, buffer.getTransaction());
      shareYapObject(obj, ref);
           
      ref.setObjectWeak(stream, obj);
      stream.referenceSystem().addExistingReferenceToObjectTree(ref);
    }
       
    if(addToIDTree){
      ref.addExistingReferenceToIdTree(stream);
    }
       
    // when there's a ObjectConstructor configured for a type
    // the type is marshalled through a lone virtual field
    // of type YapFieldTranslator which should take care of everything   
    //final boolean instantiatedByTranslator = instantiating && configInstantiates();
    final boolean doFields = buffer.getInstantiationDepth() > 0 || cascadeOnActivate();
    if (doFields && !activatingAlreadyActiveObject(instantiating, stream, ref) /* && !instantiatedByTranslator*/) {
      if(objectCanActivate(stream, obj)){
        ref.setStateClean();
        instantiateFields(ref, obj, mf, attributes, buffer);
        objectOnActivate(stream, obj);
      } else if (instantiating) {
        ref.setStateDeactivated();
      }
    } else {
      if (instantiating) {
                ref.setStateDeactivated();
            } else {
                if (buffer.getInstantiationDepth() > 1) {
                    activateFields(buffer.getTransaction(), obj, buffer.getInstantiationDepth() - 1);
                }
            }
        }
        return obj;
    }

  private boolean activatingAlreadyActiveObject(final boolean instantiating, final ObjectContainerBase stream, ObjectReference yapObject) {
    return !instantiating && !stream.i_refreshInsteadOfActivate && yapObject.isActive();
  }
 
  private Object instantiateObject(StatefulBuffer a_bytes, MarshallerFamily mf) {
    Object instance = null;
    if (configInstantiates()) {
      instance = instantiateFromConfig(a_bytes.getStream(), a_bytes, mf);             
    } else {
      instance = instantiateFromReflector(a_bytes.getStream());
    }
    return instance;
  }

  private Object instantiateFromReflector(ObjectContainerBase stream) {
    if (_reflector == null) {
        return null;
    }

    stream.instantiating(true);
    try {
        return _reflector.newInstance();
    } catch (NoSuchMethodError e) {
        stream.logMsg(7, classReflector().getName());
        return null;
    } catch (Exception e) {
        // TODO: be more helpful here
        return null;
    } finally {
      stream.instantiating(false);
    }
  }

  private Object instantiateFromConfig(ObjectContainerBase stream, StatefulBuffer a_bytes, MarshallerFamily mf) {
    int bytesOffset = a_bytes._offset;
    a_bytes.incrementOffset(Const4.INT_LENGTH);
    // Field length is always 1
    try {
        return i_config.instantiate(stream, i_fields[0].read(mf, a_bytes));                     
    } catch (Exception e) {
        Messages.logErr(stream.configImpl(), 6, classReflector().getName(), e);
        return null;
    } finally {
      a_bytes._offset = bytesOffset;
    }
  }

  private void adjustInstantiationDepth(StatefulBuffer a_bytes) {
    if (i_config != null) {
            a_bytes.setInstantiationDepth(
                i_config.adjustActivationDepth(a_bytes.getInstantiationDepth()));
        }
  }

  private boolean cascadeOnActivate() {
    return i_config != null && (i_config.cascadeOnActivate() == TernaryBool.YES);
  }

  private void shareYapObject(Object obj, ObjectReference yapObj) {
    if (obj instanceof Db4oTypeImpl) {
        ((Db4oTypeImpl)obj).setYapObject(yapObj);
    }
  }

  private void shareTransaction(Object obj, Transaction transaction) {
    if (obj instanceof TransactionAware) {
        ((TransactionAware)obj).setTrans(transaction);
    }
  }

  private void objectOnActivate(ObjectContainerBase stream, Object obj) {
    stream.callbacks().objectOnActivate(obj);
    dispatchEvent(stream, obj, EventDispatcher.ACTIVATE);
  }

  private boolean objectCanActivate(ObjectContainerBase stream, Object obj) {
    return stream.callbacks().objectCanActivate(obj)
      && dispatchEvent(stream, obj, EventDispatcher.CAN_ACTIVATE);
  }

    Object instantiateTransient(ObjectReference yapObject, Object obj, MarshallerFamily mf, ObjectHeaderAttributes attributes, StatefulBuffer buffer) {

        // overridden in YapClassPrimitive
        // never called for primitive YapAny

        Object instantiated = instantiateObject(buffer, mf);
        if (instantiated == null) {
          return null;
        }
        buffer.getStream().peeked(yapObject.getID(), instantiated);
        instantiateFields(yapObject, instantiated, mf, attributes, buffer);
        return instantiated;
    }

    void instantiateFields(ObjectReference a_yapObject, Object a_onObject, MarshallerFamily mf,ObjectHeaderAttributes attributes, StatefulBuffer a_bytes) {
        mf._object.instantiateFields(this, attributes, a_yapObject, a_onObject, a_bytes);
    }

    public boolean indexNullHandling() {
        return true;
    }

    public boolean isArray() {
        return classReflector().isCollection();
    }
   
  boolean isCollection(Object obj) {
    return reflector().forObject(obj).isCollection();
  }

    public boolean isDirty() {
        if (!stateOK()) {
            return false;
        }
        return super.isDirty();
    }
   
    boolean isEnum(){
        return _isEnum;
    }
   
    public boolean isPrimitive(){
        return false;
    }
   
    public TernaryBool isSecondClass(){
        return TernaryBool.NO;
    }

    /**
   * no any, primitive, array or other tricks. overriden in YapClassAny and
   * YapClassPrimitive
   */
    public boolean isStrongTyped() {
        return true;
    }
   
    boolean isValueType(){
        return Platform4.isValueType(classReflector());
    }
   
    public void calculateLengths(Transaction trans, ObjectHeaderAttributes header, boolean topLevel, Object obj, boolean withIndirection) {
        if(topLevel){
            header.addBaseLength(linkLength());
        }else{
            header.addPayLoadLength(linkLength());
        }
    }

    public String nameToWrite() {
        if(i_config != null && i_config.writeAs() != null){
            return i_config.writeAs();
        }
        if(i_name == null){
            return "";
        }
        return i_stream.configImpl().resolveAliasRuntimeName(i_name);
    }
   
    final boolean callConstructor() {
        i_dontCallConstructors = ! callConstructor1();
        return ! i_dontCallConstructors;
    }
   
    private final boolean callConstructor1() {
        TernaryBool res = callConstructorSpecialized();
        // FIXME: If specified, return yes?!?
        if(!res.unspecified()){
            return res == TernaryBool.YES;
        }
        return i_stream.configImpl().callConstructors().definiteYes();
    }
   
    private final TernaryBool callConstructorSpecialized(){
        if(i_config!= null){
            TernaryBool res = i_config.callConstructor();
            if(!res.unspecified()){
                return res;
            }
        }
        if(_isEnum){
            return TernaryBool.NO;
        }
        if(i_ancestor != null){
            return i_ancestor.callConstructorSpecialized();
        }
        return TernaryBool.UNSPECIFIED;
    }

    public int ownLength() {
        return MarshallerFamily.current()._class.marshalledLength(i_stream, this);
    }
   
  public ReflectClass primitiveClassReflector(){
    return null;
  }

    void purge() {
        _index.purge();
       
        // TODO: may want to add manual purge to Btree
        //       indexes here
    }

    public Object read(MarshallerFamily mf, StatefulBuffer a_bytes, boolean redirect) throws CorruptionException{
        try {
            int id = a_bytes.readInt();
            int depth = a_bytes.getInstantiationDepth() - 1;

            Transaction trans = a_bytes.getTransaction();
            ObjectContainerBase stream = trans.stream();

            if (a_bytes.getUpdateDepth() == Const4.TRANSIENT) {
                return stream.peekPersisted1(trans, id, depth);
            }
           
            if (isValueType()) {

                // for C# value types only:
                // they need to be instantiated fully before setting them
                // on the parent object because the set call modifies identity.
               
                // We also have to instantiate structs completely every time.
                if(depth < 1){
                    depth = 1;
                }
               
                // TODO: Do we want value types in the ID tree?
                // Shouldn't we treat them like strings and update
                // them every time ???

               
                ObjectReference yo = stream.getYapObject(id);
                if (yo != null) {
                    Object obj = yo.getObject();
                    if(obj == null){
                        stream.removeReference(yo);
                    }else{
                        yo.activate(trans, obj, depth, false);
                        return yo.getObject();
                    }
                }
               
                return new ObjectReference(id).read(
                    trans,
                    null,
                    null,
                    depth,
                    Const4.ADD_TO_ID_TREE, false);
            }

            Object ret = stream.getByID2(trans, id);

            if (ret instanceof Db4oTypeImpl) {
                depth = ((Db4oTypeImpl)ret).adjustReadDepth(depth);
            }

            // this is OK for primitive YapAnys. They will not be added
            // to the list, since they will not be found in the ID tree.
            stream.stillToActivate(ret, depth);

            return ret;

        } catch (Exception e) {
        }
        return null;
    }
   
    public Object readQuery(Transaction a_trans, MarshallerFamily mf, boolean withRedirection, Buffer a_reader, boolean a_toArray) throws CorruptionException{
        try {
            return a_trans.stream().getByID2(a_trans, a_reader.readInt());
        } catch (Exception e) {
            if (Debug.atHome) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public TypeHandler4 readArrayHandler(Transaction a_trans, MarshallerFamily mf, Buffer[] a_bytes) {
        if (isArray()) {
            return this;
        }
        return null;
    }

    public TypeHandler4 readArrayHandler1(Buffer[] a_bytes) {
        if(DTrace.enabled){
            if(a_bytes[0] instanceof StatefulBuffer){
                DTrace.READ_ARRAY_WRAPPER.log(((StatefulBuffer)a_bytes[0]).getID());
            }
        }
        if (isArray()) {
            if (Platform4.isCollectionTranslator(this.i_config)) {
                a_bytes[0].incrementOffset(Const4.INT_LENGTH);
                return new ArrayHandler(i_stream, null, false);
            }
            incrementFieldsOffset1(a_bytes[0]);
            if (i_ancestor != null) {
                return i_ancestor.readArrayHandler1(a_bytes);
            }
        }
        return null;
    }
   
    public QCandidate readSubCandidate(MarshallerFamily mf, Buffer reader, QCandidates candidates, boolean withIndirection) {
        int id = reader.readInt();
        if(id == 0){
            return null;
        }
        return new QCandidate(candidates, null, id, true);
    }

    public void readCandidates(MarshallerFamily mf, final Buffer a_bytes, final QCandidates a_candidates) {
        int id = 0;

        int offset = a_bytes._offset;
        try {
            id = a_bytes.readInt();
        } catch (Exception e) {
        }
        a_bytes._offset = offset;

        if (id != 0) {
            final Transaction trans = a_candidates.i_trans;
            Object obj = trans.stream().getByID1(trans, id);
            if (obj != null) {

                a_candidates.i_trans.stream().activate1(trans, obj, 2);
                Platform4.forEachCollectionElement(obj, new Visitor4() {
                    public void visit(Object elem) {
                        a_candidates.addByIdentity(new QCandidate(a_candidates, elem, (int)trans.stream().getID(elem), true));
                    }
                });
            }

        }
    }

    public final int readFieldCount(Buffer a_bytes) {
        int count = a_bytes.readInt();
        if (count > i_fields.length) {
            if (Debug.atHome) {
                System.out.println(
                    "YapClass.readFieldCount "
                        + getName()
                        + " count to high:"
                        + count
                        + " i_fields:"
                        + i_fields.length);
                new Exception().printStackTrace();
            }
            return i_fields.length;
        }   
        return count;
    }

    public int readFieldCountSodaAtHome(Buffer a_bytes) {
        if (Debug.atHome) {
            int count = a_bytes.readInt();
            if (count > i_fields.length) {
                return i_fields.length;
            }
            return count;
        }
        return 0;
    }

    public Object readIndexEntry(Buffer a_reader) {
        return new Integer(a_reader.readInt());
    }
   
    public Object readIndexEntry(MarshallerFamily mf, StatefulBuffer a_writer) throws CorruptionException{
        return readIndexEntry(a_writer);
    }

    byte[] readName(Transaction a_trans) {
        i_reader = a_trans.stream().readReaderByID(a_trans, getID());
        return readName1(a_trans, i_reader);
    }

    public final byte[] readName1(Transaction trans, Buffer reader) {
      if (reader == null)
        return null;
     
        i_reader = reader;
       
        try {
            ClassMarshaller marshaller = MarshallerFamily.current()._class;
      i_nameBytes = marshaller.readName(trans, reader);
            _metaClassID = marshaller.readMetaClassID(reader);

            setStateUnread();

            bitFalse(Const4.CHECKED_CHANGES);
            bitFalse(Const4.STATIC_FIELDS_STORED);

            return i_nameBytes;

        } catch (Throwable t) {
            setStateDead();
            if (Debug.atHome) {
                t.printStackTrace();
            }
        }
        return null;
    }
   
    void readVirtualAttributes(Transaction a_trans, ObjectReference a_yapObject) {
        int id = a_yapObject.getID();
        ObjectContainerBase stream = a_trans.stream();
        Buffer reader = stream.readReaderByID(a_trans, id);
        ObjectHeader oh = new ObjectHeader(stream, this, reader);
        oh.objectMarshaller().readVirtualAttributes(a_trans, this, a_yapObject, oh._headerAttributes, reader);
    }
   
  GenericReflector reflector() {
    return i_stream.reflector();
  }
   
    public void rename(String newName){
        if (!i_stream.isClient()) {
            int tempState = i_state;
            setStateOK();
            i_name = newName;
            setStateDirty();
            write(i_stream.getSystemTransaction());
            i_state = tempState;
        }else{
            Exceptions4.throwRuntimeException(58);
        }
    }

    void createConfigAndConstructor(
        Hashtable4 a_byteHashTable,
        ObjectContainerBase a_stream,
        ReflectClass a_class) {
        if (a_class == null) {
            if (i_nameBytes != null) {
              String name = a_stream.stringIO().read(i_nameBytes);
              i_name = a_stream.configImpl().resolveAliasStoredName(name);
            }
        } else {
            i_name = a_class.getName();
        }
        setConfig(i_stream.configImpl().configClass(i_name));
        if (a_class == null) {
            createConstructor(a_stream, i_name);
        } else {
            createConstructor(a_stream, a_class, i_name, true);
        }
        if (i_nameBytes != null) {
            a_byteHashTable.remove(i_nameBytes);
            i_nameBytes = null;
        }
    }

    boolean readThis() {
        if (stateUnread()) {
            setStateOK();
            setStateClean();
            forceRead();
            return true;
        }
        return false;
    }
   
    final void forceRead(){
        if(i_reader == null || bitIsTrue(Const4.READING)){
            return;
        }
       
        bitTrue(Const4.READING);
       
        MarshallerFamily.forConverterVersion(i_stream.converterVersion())._class.read(i_stream, this, i_reader);
      
        i_nameBytes = null;
        i_reader = null;
        bitFalse(Const4.READING);
   

    public boolean readArray(Object array, Buffer reader) {
        return false;
    }

    public void readThis(Transaction a_trans, Buffer a_reader) {
        throw Exceptions4.virtualException();
    }

    public void refresh() {
        if (!stateUnread()) {
            createConstructor(i_stream, i_name);
            bitFalse(Const4.CHECKED_CHANGES);
            checkChanges();
            if (i_fields != null) {
                for (int i = 0; i < i_fields.length; i++) {
                    i_fields[i].refresh();
                }
            }
        }
    }

    void removeFromIndex(Transaction ta, int id) {
        if (hasIndex()) {
            _index.remove(ta, id);
        }
        if (i_ancestor != null) {
            i_ancestor.removeFromIndex(ta, id);
        }
    }

    boolean renameField(String a_from, String a_to) {
        boolean renamed = false;
        for (int i = 0; i < i_fields.length; i++) {
            if (i_fields[i].getName().equals(a_to)) {
                i_stream.logMsg(9, "class:" + getName() + " field:" + a_to);
                return false;
            }
        }
        for (int i = 0; i < i_fields.length; i++) {
            if (i_fields[i].getName().equals(a_from)) {
                i_fields[i].setName(a_to);
                renamed = true;
            }
        }
        return renamed;
    }
   
    void setConfig(Config4Class config){
        // The configuration can be set by a ObjectClass#readAs setting
        // from YapClassCollection, right after reading the meta information
        // for the first time. In that case we never change the setting
        if(i_config == null){
            i_config = config;
        }
    }

    void setName(String a_name) {
        i_name = a_name;
    }

    private final void setStateDead() {
        bitTrue(Const4.DEAD);
        bitFalse(Const4.CONTINUE);
    }

    private final void setStateUnread() {
        bitFalse(Const4.DEAD);
        bitTrue(Const4.CONTINUE);
    }

    private final void setStateOK() {
        bitFalse(Const4.DEAD);
        bitFalse(Const4.CONTINUE);
    }
   
    boolean stateDead(){
        return bitIsTrue(Const4.DEAD);
    }

    private final boolean stateOK() {
        return bitIsFalse(Const4.CONTINUE)
            && bitIsFalse(Const4.DEAD)
            && bitIsFalse(Const4.READING);
    }
   
    final boolean stateOKAndAncestors(){
        if(! stateOK()  || i_fields == null){
            return false;
        }
        if(i_ancestor != null){
            return i_ancestor.stateOKAndAncestors();
        }
        return true;
    }

    boolean stateUnread() {
        return bitIsTrue(Const4.CONTINUE)
            && bitIsFalse(Const4.DEAD)
            && bitIsFalse(Const4.READING);
    }

    boolean storeField(ReflectField a_field) {
        if (a_field.isStatic()) {
            return false;
        }
        if (a_field.isTransient()) {
            Config4Class config = configOrAncestorConfig();
            if (config == null) {
                return false;
            }
            if (!config.storeTransientFields()) {
                return false;
            }
        }
        return Platform4.canSetAccessible() || a_field.isPublic();
    }
   
    public StoredField storedField(String a_name, Object a_type) {
        synchronized(i_stream.i_lock){
           
            ClassMetadata yc = i_stream.getYapClass(i_stream.configImpl().reflectorFor(a_type));
       
          if(i_fields != null){
              for (int i = 0; i < i_fields.length; i++) {
                  if(i_fields[i].getName().equals(a_name)){
                      if(yc == null || yc == i_fields[i].getFieldYapClass(i_stream)){
                          return (i_fields[i]);
                      }
                  }
                }
          }
       
        //TODO: implement field creation
       
          return null;
        }
    }

    void storeStaticFieldValues(Transaction trans, boolean force) {
        if (bitIsTrue(Const4.STATIC_FIELDS_STORED) && !force) {
          return;
        }
        bitTrue(Const4.STATIC_FIELDS_STORED);
       
        if (!shouldStoreStaticFields(trans)) {
          return;
        }
       
        final ObjectContainerBase stream = trans.stream();
        stream.showInternalClasses(true);
        try {
            StaticClass sc = queryStaticClass(trans);
            if (sc == null) {
              createStaticClass(trans);
            } else {
              updateStaticClass(trans, sc);
            }
        } finally {
            stream.showInternalClasses(false);
        }
    }

  private boolean shouldStoreStaticFields(Transaction trans) {
    return staticFieldValuesArePersisted()
              || Platform4.storeStaticFieldValues(trans.reflector(), classReflector());
  }

  private void updateStaticClass(final Transaction trans, final StaticClass sc) {
    final ObjectContainerBase stream = trans.stream();
    stream.activate1(trans, sc, 4);
   
    final StaticField[] existingFields = sc.fields;
    final Iterator4 staticFields = Iterators.map(
        staticReflectFields(),
        new Function4() {
          public Object apply(Object arg) {
            final ReflectField reflectField = (ReflectField)arg;
              StaticField existingField = fieldByName(existingFields, reflectField.getName());
              if (existingField != null) {
                updateExistingStaticField(trans, existingField, reflectField);
                  return existingField;
              }
              return toStaticField(reflectField);
          }
        });
    sc.fields = toStaticFieldArray(staticFields);
    if (!stream.isClient()) {
      setStaticClass(trans, sc);
    }
  }

  private void createStaticClass(Transaction trans) {
    if (trans.stream().isClient()) {
      return;
    }
    StaticClass sc = new StaticClass(i_name, toStaticFieldArray(staticReflectFieldsToStaticFields()));
    setStaticClass(trans, sc);
  }

  private Iterator4 staticReflectFieldsToStaticFields() {
    return Iterators.map(
      staticReflectFields(),
      new Function4() {
        public Object apply(Object arg) {
          return toStaticField((ReflectField) arg);
        }
      });
  }

  private StaticField toStaticField(final ReflectField reflectField) {
    return new StaticField(reflectField.getName(), staticReflectFieldValue(reflectField));
  }

  private Object staticReflectFieldValue(final ReflectField reflectField) {
    reflectField.setAccessible();
    return reflectField.get(null);
  }

  private void setStaticClass(Transaction trans, StaticClass sc) {
    // TODO: we should probably use a specific update depth here, 4?
    trans.stream().setInternal(trans, sc, true);
  }

  private StaticField[] toStaticFieldArray(Iterator4 iterator4) {
    return toStaticFieldArray(new Collection4(iterator4));
  }

  private StaticField[] toStaticFieldArray(Collection4 fields) {
    return (StaticField[]) fields.toArray(new StaticField[fields.size()]);
  }

  private Iterator4 staticReflectFields() {
    return Iterators.filter(reflectFields(), new Predicate4() {
      public boolean match(Object candidate) {
        return ((ReflectField)candidate).isStatic();
      }
    });
  }

  private ReflectField[] reflectFields() {
    return classReflector().getDeclaredFields();
  }

  private void updateExistingStaticField(Transaction trans, StaticField existingField, final ReflectField reflectField) {
    final ObjectContainerBase stream = trans.stream();
    final Object newValue = staticReflectFieldValue(reflectField);
   
    if (existingField.value != null
          && newValue != null
          && existingField.value.getClass() == newValue.getClass()) {
          long id = stream.getID1(existingField.value);
          if (id > 0) {
              if (existingField.value != newValue) {
                 
                  // This is the clue:
                  // Bind the current static member to it's old database identity,
                  // so constants and enums will work with '=='
                  stream.bind1(trans, newValue, id);
                 
                  // This may produce unwanted side effects if the static field object
                  // was modified in the current session. TODO:Add documentation case.
                 
                  stream.refresh(newValue, Integer.MAX_VALUE);
                 
                  existingField.value = newValue;
              }
              return;
          }
      }
   
    if(newValue == null){
            try{
                reflectField.set(null, existingField.value);
            }catch(Exception ex){
                // fail silently
              // TODO: why?
            }
          return;  
     }
   
    existingField.value = newValue;
  }

  private boolean staticFieldValuesArePersisted() {
    return (i_config != null && i_config.staticFieldValuesArePersisted());
  }

  private StaticField fieldByName(StaticField[] fields, final String fieldName) {
    for (int i = 0; i < fields.length; i++) {
        final StaticField field = fields[i];
      if (fieldName.equals(field.name)) {
        return field;
      }
    }
    return null;
  }

  private StaticClass queryStaticClass(Transaction trans) {
    Query q = trans.stream().query(trans);
    q.constrain(Const4.CLASS_STATICCLASS);
    q.descend("name").constrain(i_name);
    ObjectSet os = q.execute();
    return os.size() > 0
      ? (StaticClass)os.next()
      : null;
  }

    public boolean supportsIndex() {
        return true;
    }

    public String toString() {
      if(i_name!=null) {
        return i_name;
      }
        if(i_nameBytes==null){
            return "*CLASS NAME UNKNOWN*";
        }
      LatinStringIO stringIO =
        i_stream == null ?
            Const4.stringIO
            : i_stream.stringIO();
      return stringIO.read(i_nameBytes);
    }
   
    public boolean writeArray(Object array, Buffer reader) {
        return false;
    }

    public boolean writeObjectBegin() {
        if (!stateOK()) {
            return false;
        }
        return super.writeObjectBegin();
    }

    public void writeIndexEntry(Buffer a_writer, Object a_object) {
       
        if(a_object == null){
            a_writer.writeInt(0);
            return;
        }
       
        a_writer.writeInt(((Integer)a_object).intValue());
    }
   
    public Object writeNew(MarshallerFamily mf, Object a_object, boolean topLevel, StatefulBuffer a_bytes, boolean withIndirection, boolean restoreLinkOffset) {
        if (a_object == null) {
            a_bytes.writeInt(0);
            return new Integer(0);
        }

    int id = a_bytes.getStream().setInternal(
                    a_bytes.getTransaction(),
                    a_object,
                    a_bytes.getUpdateDepth(), true);
       
        a_bytes.writeInt(id);
    return new Integer(id);
    }

    public final void writeThis(Transaction trans, Buffer writer) {
        MarshallerFamily.current()._class.write(trans, this, writer);
    }

    // Comparison_______________________

    private ReflectClass i_compareTo;
   
  public void prepareComparison(Transaction a_trans, Object obj) {
      prepareComparison(obj);
  }

    public Comparable4 prepareComparison(Object obj) {
        if (obj != null) {
            if(obj instanceof Integer){
                i_lastID = ((Integer)obj).intValue();
            }else{
                i_lastID = (int)i_stream.getID(obj);
            }
            i_compareTo = reflector().forObject(obj);
        } else {
            i_lastID = 0;
            i_compareTo = null;
        }
        return this;
    }
   
    public Object current(){
        if(i_compareTo == null){
            return null;
        }
        return new Integer(i_lastID);
    }

    public int compareTo(Object a_obj) {
        if(a_obj instanceof Integer){
            return ((Integer)a_obj).intValue() - i_lastID;
        }
        if( (a_obj == null) && (i_compareTo == null)){
            return 0;
        }
        return -1;
    }
   
    public boolean isEqual(Object obj) {
        if (obj == null) {
            return i_compareTo == null;
        }
        return i_compareTo.isAssignableFrom(reflector().forObject(obj));
    }

    public boolean isGreater(Object obj) {
        return false;
    }

    public boolean isSmaller(Object obj) {
        return false;
    }

    public String toString(MarshallerFamily mf, StatefulBuffer writer, ObjectReference yapObject, int depth, int maxDepth)  {
        int length = readFieldCount(writer);
        String str = "";
        for (int i = 0; i < length; i++) {
            str += i_fields[i].toString(mf, writer);
        }
        if (i_ancestor != null) {
            str+= i_ancestor.toString(mf, writer, yapObject, depth, maxDepth);
        }
        return str;

    }

    public static void defragObject(ReaderPair readers) {
      ObjectHeader header=ObjectHeader.defrag(readers);
      header._marshallerFamily._object.defragFields(header.yapClass(),header,readers);
        if (Deploy.debug) {
            readers.readEnd();
        }
    } 

  public void defrag(MarshallerFamily mf, ReaderPair readers, boolean redirect) {
    if(hasIndex()) {
      readers.copyID();
    }
    else {
      readers.copyUnindexedID();
    }
    int restLength = (linkLength()-Const4.INT_LENGTH);
    readers.incrementOffset(restLength);
  }
 
  public void defragClass(ReaderPair readers, int classIndexID) throws CorruptionException {
    MarshallerFamily mf = MarshallerFamily.current();
    mf._class.defrag(this,i_stream.stringIO(), readers, classIndexID);
  }

    public static ClassMetadata readClass(ObjectContainerBase stream, Buffer reader) {
        ObjectHeader oh = new ObjectHeader(stream, reader);
        return oh.yapClass();
    }

  public boolean isAssignableFrom(ClassMetadata other) {
    return classReflector().isAssignableFrom(other.classReflector());
  }

  public final void defragIndexEntry(ReaderPair readers) {
    readers.copyID();
  }
}
TOP

Related Classes of com.db4o.internal.ClassMetadata

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.