/* 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.*; import com.db4o.config.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.activation.*; import com.db4o.internal.btree.*; import com.db4o.internal.delete.*; import com.db4o.internal.handlers.*; import com.db4o.internal.handlers.array.*; import com.db4o.internal.marshall.*; import com.db4o.internal.query.processor.*; import com.db4o.internal.reflect.*; import com.db4o.marshall.*; import com.db4o.reflect.*; import com.db4o.reflect.generic.*; import com.db4o.typehandlers.*; /** * @exclude */ public class FieldMetadata extends ClassAspect implements StoredField { private ClassMetadata _containingClass; private String _name; protected boolean _isArray; private boolean _isNArray; private boolean _isPrimitive; private ReflectField _reflectField; private FieldMetadataState _state = FieldMetadataState.NOT_LOADED; private Config4Field _config; private Db4oTypeImpl _db4oType; private BTree _index; protected ClassMetadata _fieldType; protected int _fieldTypeID; static final FieldMetadata[] EMPTY_ARRAY = new FieldMetadata[0]; public FieldMetadata(ClassMetadata classMetadata) { _containingClass = classMetadata; } protected final Class translatorStoredClass(ObjectTranslator translator) { try { return translator.storedClass(); } catch (RuntimeException e) { throw new ReflectException(e); } } FieldMetadata(ClassMetadata containingClass, ReflectField field, ClassMetadata fieldType) { this(containingClass); init(field.getName()); _reflectField = field; _fieldType = fieldType; _fieldTypeID = fieldType.getID(); // TODO: beautify !!! possibly pull up isPrimitive to ReflectField boolean isPrimitive = field instanceof GenericField ? ((GenericField)field).isPrimitive() : false; configure(field.getFieldType(), isPrimitive); checkDb4oType(); setAvailable(); } protected void setAvailable() { _state = FieldMetadataState.AVAILABLE; } protected FieldMetadata(int fieldTypeID){ _fieldTypeID = fieldTypeID; } public FieldMetadata(ClassMetadata containingClass, String name, int fieldTypeID, boolean primitive, boolean isArray, boolean isNArray) { this(containingClass); init(name, fieldTypeID, primitive, isArray, isNArray); } protected FieldMetadata(ClassMetadata containingClass, String name) { this(containingClass); init(name); } public void addFieldIndex(ObjectIdContextImpl context) throws FieldIndexException { if (! hasIndex()) { incrementOffset(context, context); return; } try { addIndexEntry(context.transaction(), context.objectId(), readIndexEntry(context)); } catch (CorruptionException exc) { throw new FieldIndexException(exc,this); } } protected final void addIndexEntry(StatefulBuffer a_bytes, Object indexEntry) { addIndexEntry(a_bytes.transaction(), a_bytes.getID(), indexEntry); } public void addIndexEntry(Transaction trans, int parentID, Object indexEntry) { if (! hasIndex()) { return; } BTree index = getIndex(trans); index.add(trans, createFieldIndexKey(parentID, indexEntry)); } protected FieldIndexKey createFieldIndexKey(int parentID, Object indexEntry) { Object convertedIndexEntry = indexEntryFor(indexEntry); return new FieldIndexKeyImpl(parentID, convertedIndexEntry); } protected Object indexEntryFor(Object indexEntry) { return _reflectField.indexEntry(indexEntry); } public boolean canUseNullBitmap(){ return true; } public final Object readIndexEntry(ObjectIdContext context) throws CorruptionException, Db4oIOException { IndexableTypeHandler indexableTypeHandler = (IndexableTypeHandler) HandlerRegistry.correctHandlerVersion(context, getHandler()); return indexableTypeHandler.readIndexEntry(context); } public void removeIndexEntry(Transaction trans, int parentID, Object indexEntry){ if (! hasIndex()) { return; } BTree index = getIndex(trans); if(index == null){ return; } index.remove(trans, createFieldIndexKey(parentID, indexEntry)); } //TODO: Split into command query separation. public boolean alive() { if (_state == FieldMetadataState.AVAILABLE) { return true; } if (_state == FieldMetadataState.NOT_LOADED) { return load(); } return _state == FieldMetadataState.AVAILABLE; } private boolean load() { if (_fieldType == null) { // this may happen if the local ClassMetadataRepository // has not been updated from the server and presumably // in some refactoring cases. // We try to heal the problem by re-reading the class. // This could be dangerous, if the class type of a field // has been modified. // TODO: add class refactoring features _fieldType = detectFieldType(); checkFieldTypeID(); } checkCorrectTypeForField(); if(_fieldType == null || _reflectField == null){ _state = FieldMetadataState.UNAVAILABLE; _reflectField = null; return false; } if(updating()){ return false; } setAvailable(); checkDb4oType(); return true; } private boolean shouldStoreField() { return !_reflectField.isTransient() || (_containingClass != null && _containingClass.shouldStoreTransientFields()); } public boolean updating() { return _state == FieldMetadataState.UPDATING; } private void checkFieldTypeID() { int id = _fieldType != null ? _fieldType.getID() : 0; if (_fieldTypeID == 0) { _fieldTypeID = id; return; } if(id > 0 && id != _fieldTypeID){ // wrong type, refactoring, field should be turned off // TODO: it would be cool to log something here _fieldType = null; } } boolean canAddToQuery(String fieldName){ if(! alive()){ return false; } return fieldName.equals(getName()) && containingClass() != null && !containingClass().isInternal(); } private boolean canHold(ReflectClass type) { if (type == null) { throw new ArgumentNullException(); } final TypeHandler4 typeHandler = getHandler(); if (typeHandler instanceof QueryableTypeHandler) { if (((QueryableTypeHandler)typeHandler).descendsIntoMembers()) { return true; } } ReflectClass classReflector = fieldType().classReflector(); if (classReflector.isCollection()) { return true; } return classReflector.isAssignableFrom(type); } public GenericReflector reflector() { ObjectContainerBase container = container(); if (container == null) { return null; } return container.reflector(); } public Object coerce(ReflectClass valueClass, Object value) { if (value == null) { return _isPrimitive ? No4.INSTANCE : value; } if (valueClass == null) { throw new ArgumentNullException(); } if(getHandler() instanceof PrimitiveHandler){ return ((PrimitiveHandler)getHandler()).coerce(valueClass, value); } if(! canHold(valueClass)){ return No4.INSTANCE; } return value; } public final boolean canLoadByIndex() { return Handlers4.canLoadFieldByIndex(getHandler()); } public final void cascadeActivation(ActivationContext context) { if (! alive()) { return; } Object cascadeTo = cascadingTarget(context); if (cascadeTo == null) { return; } final ActivationContext cascadeContext = context.forObject(cascadeTo); ClassMetadata classMetadata = cascadeContext.classMetadata(); if(classMetadata == null){ return; } ensureObjectIsActive(cascadeContext); Handlers4.cascadeActivation(cascadeContext, classMetadata.typeHandler()); } private void ensureObjectIsActive(ActivationContext context) { if(!context.depth().mode().isActivate()){ return; } if(Handlers4.isValueType(getHandler())){ return; } ObjectContainerBase container = context.container(); ClassMetadata classMetadata = container.classMetadataForObject(context.targetObject()); if(classMetadata == null || !classMetadata.hasIdentity()){ return; } if(container.isActive(context.targetObject())){ return; } container.stillToActivate(context.descend()); } protected final Object cascadingTarget(ActivationContext context) { if (context.depth().mode().isDeactivate()) { if (null == _reflectField) { return null; } return fieldAccessor().get( _reflectField, context.targetObject()); } return getOrCreate(context.transaction(), context.targetObject()); } private void checkDb4oType() { if (_reflectField != null) { if (container()._handlers.ICLASS_DB4OTYPE.isAssignableFrom(_reflectField.getFieldType())) { _db4oType = HandlerRegistry.getDb4oType(_reflectField.getFieldType()); } } } void collectConstraints(Transaction trans, QConObject a_parent, Object a_template, Visitor4 a_visitor) { Object obj = getOn(trans, a_template); if (obj != null) { Collection4 objs = Platform4.flattenCollection(trans.container(), obj); Iterator4 j = objs.iterator(); while (j.moveNext()) { obj = j.current(); if (obj != null) { if (_isPrimitive && !_isArray) { Object nullValue = _reflectField.getFieldType().nullValue(); if (obj.equals(nullValue)) { return; } } if(Platform4.ignoreAsConstraint(obj)){ return; } if (!a_parent.hasObjectInParentPath(obj)) { QConObject constraint = new QConObject(trans, a_parent, qField(trans), obj); constraint.byExample(); a_visitor.visit(constraint); } } } } } public final void collectIDs(CollectIdContext context) throws FieldIndexException { if (! alive()) { incrementOffset(context.buffer(), context); return ; } final TypeHandler4 handler = HandlerRegistry.correctHandlerVersion(context, getHandler()); Handlers4.collectIdsInternal(context, handler, linkLength(context), true); } void configure(ReflectClass clazz, boolean isPrimitive) { _isArray = clazz.isArray(); if (_isArray) { ReflectArray reflectArray = reflector().array(); _isNArray = reflectArray.isNDimensional(clazz); _isPrimitive = reflectArray.getComponentType(clazz).isPrimitive(); } else { _isPrimitive = isPrimitive | clazz.isPrimitive(); } } protected final TypeHandler4 wrapHandlerToArrays(TypeHandler4 handler) { if(handler == null){ return null; } if (_isNArray) { return new MultidimensionalArrayHandler(handler, arraysUsePrimitiveClassReflector()); } if (_isArray) { return new ArrayHandler(handler, arraysUsePrimitiveClassReflector()); } return handler; } private boolean arraysUsePrimitiveClassReflector() { return _isPrimitive; } public void deactivate(ActivationContext context) { if (!alive() || !shouldStoreField()) { return; } boolean isEnumClass = _containingClass.isEnum(); if (_isPrimitive && !_isArray) { if (!isEnumClass) { Object nullValue = _reflectField.getFieldType().nullValue(); fieldAccessor().set(_reflectField, context.targetObject(), nullValue); } return; } if (context.depth().requiresActivation()) { cascadeActivation(context); } if (!isEnumClass) { fieldAccessor().set(_reflectField, context.targetObject(), null); } } private FieldAccessor fieldAccessor() { return _containingClass.fieldAccessor(); } public void delete(DeleteContextImpl context, boolean isUpdate) throws FieldIndexException { if (! checkAlive(context, context)) { return; } try { removeIndexEntry(context); if(isUpdate && ! isStruct()){ incrementOffset(context, context); return; } StatefulBuffer buffer = (StatefulBuffer) context.buffer(); final DeleteContextImpl childContext = new DeleteContextImpl(context, getStoredType(), _config); context.slotFormat().doWithSlotIndirection(buffer, getHandler(), new Closure4() { public Object run() { childContext.delete(getHandler()); return null; } }); } catch (CorruptionException exc) { throw new FieldIndexException(exc, this); } } private final void removeIndexEntry(DeleteContextImpl context) throws CorruptionException, Db4oIOException { if(! hasIndex()){ return; } int offset = context.offset(); Object obj = readIndexEntry(context); removeIndexEntry(context.transaction(), context.objectId(), obj); context.seek(offset); } public boolean equals(Object obj) { if (! (obj instanceof FieldMetadata)) { return false; } FieldMetadata other = (FieldMetadata) obj; other.alive(); alive(); return other._isPrimitive == _isPrimitive && other._fieldType == _fieldType && other._name.equals(_name); } public int hashCode() { return _name.hashCode(); } public final Object get(Object onObject) { return get(null, onObject); } public final Object get(Transaction trans, Object onObject) { if (_containingClass == null) { return null; } ObjectContainerBase container = container(); if (container == null) { return null; } synchronized (container.lock()) { // FIXME: The following is not really transactional. // This will work OK for normal C/S and for // single local mode but the transaction will // be wrong for MTOC. if(trans == null){ trans = container.transaction(); } container.checkClosed(); ObjectReference ref = trans.referenceForObject(onObject); if (ref == null) { return null; } int id = ref.getID(); if (id <= 0) { return null; } UnmarshallingContext context = new UnmarshallingContext(trans, ref, Const4.ADD_TO_ID_TREE, false); context.activationDepth(new LegacyActivationDepth(1)); return context.readFieldValue(this); } } public String getName() { return _name; } public final ClassMetadata fieldType() { // alive needs to be checked by all callers: Done return _fieldType; } public TypeHandler4 getHandler() { if (_fieldType == null) { return null; } // alive needs to be checked by all callers: Done return wrapHandlerToArrays(_fieldType.typeHandler()); } public int fieldTypeID(){ // alive needs to be checked by all callers: Done return _fieldTypeID; } public Object getOn(Transaction trans, Object onObject) { if (alive()) { return fieldAccessor().get( _reflectField, onObject); } return null; } /** * dirty hack for com.db4o.types some of them (BlobImpl) need to be set automatically * TODO: Derive from FieldMetadata for Db4oTypes */ public Object getOrCreate(Transaction trans, Object onObject) { if (!alive()) { return null; } Object obj = fieldAccessor().get( _reflectField, onObject); if (_db4oType != null && obj == null) { obj = _db4oType.createDefault(trans); fieldAccessor().set(_reflectField, onObject, obj); } return obj; } public final ClassMetadata containingClass() { // alive needs to be checked by all callers: Done return _containingClass; } public ReflectClass getStoredType() { if(_reflectField == null){ return null; } return Handlers4.baseType(_reflectField.getFieldType()); } public ObjectContainerBase container(){ if(_containingClass == null){ return null; } return _containingClass.container(); } public boolean hasConfig() { return _config!=null; } public boolean hasIndex() { return _index != null; } public final void init(String name) { _name = name; initConfiguration(name); } final void initConfiguration(String name) { final Config4Class containingClassConfig = _containingClass.config(); if (containingClassConfig == null) { return; } _config = containingClassConfig.configField(name); if (Debug4.configureAllFields) { if (_config == null) { _config = (Config4Field) containingClassConfig.objectField(_name); } } } public void init(String name, int fieldTypeID, boolean isPrimitive, boolean isArray, boolean isNArray) { _fieldTypeID = fieldTypeID; _isPrimitive = isPrimitive; _isArray = isArray; _isNArray = isNArray; init(name); loadFieldTypeById(); alive(); } private boolean _initialized=false; final void initConfigOnUp(Transaction trans) { if (_initialized) { return; } _initialized = true; if (_config != null) { _config.initOnUp(trans, this); } } public void activate(UnmarshallingContext context) { if(! checkAlive(context, context)) { return; } if(!shouldStoreField()) { incrementOffset(context, context); return; } Object toSet = read(context); informAboutTransaction(toSet, context.transaction()); set(context.persistentObject(), toSet); } public void attemptUpdate(UnmarshallingContext context) { if(! updating()){ incrementOffset(context, context); return; } int savedOffset = context.offset(); try{ Object toSet = context.read(getHandler()); if(toSet != null){ set(context.persistentObject(), toSet); } }catch(Exception ex){ // FIXME: COR-547 Diagnostics here please. context.buffer().seek(savedOffset); incrementOffset(context, context); } } private boolean checkAlive(AspectVersionContext context, HandlerVersionContext versionContext){ if(! checkEnabled(context, versionContext)){ return false; } boolean alive = alive(); if (!alive) { incrementOffset((ReadBuffer)context, versionContext); } return alive; } private void informAboutTransaction(Object obj, Transaction trans){ if (_db4oType != null && obj != null) { ((Db4oTypeImpl) obj).setTrans(trans); } } public boolean isArray() { return _isArray; } public int linkLength(HandlerVersionContext context) { alive(); return calculateLinkLength(context); } private int calculateLinkLength(HandlerVersionContext context){ return Handlers4.calculateLinkLength(HandlerRegistry.correctHandlerVersion(context, getHandler())); } public void loadFieldTypeById() { _fieldType = container().classMetadataForID(_fieldTypeID); } private ClassMetadata detectFieldType() { ReflectClass claxx = _containingClass.classReflector(); if (claxx == null) { return null; } _reflectField = claxx.getDeclaredField(_name); if (_reflectField == null) { return null; } ReflectClass fieldType = _reflectField.getFieldType(); if(fieldType == null) { return null; } return Handlers4.erasedFieldType(container(), fieldType); } protected TypeHandler4 typeHandlerForClass(ObjectContainerBase container, ReflectClass fieldType) { container.showInternalClasses(true); try{ return container.typeHandlerForClass(Handlers4.baseType(fieldType)); }finally{ container.showInternalClasses(false); } } private void checkCorrectTypeForField() { ClassMetadata currentFieldType = detectFieldType(); if (currentFieldType == null){ _reflectField = null; _state = FieldMetadataState.UNAVAILABLE; return; } if (currentFieldType == _fieldType && Handlers4.baseType(_reflectField.getFieldType()).isPrimitive() == _isPrimitive) { return; } // special case when migrating from type handler ids // to class metadata ids which caused // any interface metadata id to be mapped to UNTYPED_ID if (Handlers4.isUntyped(currentFieldType.typeHandler()) && Handlers4.isUntyped(_fieldType.typeHandler())) { return; } // FIXME: COR-547 Diagnostics here please. _state = FieldMetadataState.UPDATING; } private UpdateDepth adjustUpdateDepthForCascade(Object obj, UpdateDepth updateDepth) { return updateDepth.adjustUpdateDepthForCascade(_containingClass.isCollection(obj)); } private boolean cascadeOnUpdate(Config4Class parentClassConfiguration) { return ((parentClassConfiguration != null && (parentClassConfiguration.cascadeOnUpdate().definiteYes())) || (_config != null && (_config.cascadeOnUpdate().definiteYes()))); } public void marshall(MarshallingContext context, Object obj){ // alive needs to be checked by all callers: Done UpdateDepth updateDepth = context.updateDepth(); if (obj != null && cascadeOnUpdate(context.classConfiguration())) { context.updateDepth(adjustUpdateDepthForCascade(obj, updateDepth)); } context.writeObjectWithCurrentState(getHandler(), obj); context.updateDepth(updateDepth); if(hasIndex()){ context.addIndexEntry(this, obj); } } public boolean needsArrayAndPrimitiveInfo(){ return true; } public PreparedComparison prepareComparison(Context context, Object obj) { if (!alive()) { return null; } return Handlers4.prepareComparisonFor(getHandler(), context, obj); } public QField qField(Transaction a_trans) { int classMetadataID = 0; if(_containingClass != null){ classMetadataID = _containingClass.getID(); } return new QField(a_trans, _name, this, classMetadataID, _handle); } public Object read(ObjectIdContext context) { if(!canReadFromSlot((AspectVersionContext) context)) { incrementOffset(context, context); return null; } return context.read(getHandler()); } private boolean canReadFromSlot(AspectVersionContext context) { if(! isEnabledOn(context)){ return false; } if(alive()) { return true; } return _state != FieldMetadataState.NOT_LOADED; } void refresh() { ClassMetadata newFieldType = detectFieldType(); if (newFieldType != null && newFieldType.equals(_fieldType)) { return; } _reflectField = null; _state = FieldMetadataState.UNAVAILABLE; } // FIXME: needs test case public void rename(String newName) { ObjectContainerBase container = container(); if (! container.isClient()) { _name = newName; _containingClass.setStateDirty(); _containingClass.write(container.systemTransaction()); } else { Exceptions4.throwRuntimeException(58); } } public void set(Object onObject, Object obj){ // TODO: remove the following if and check callers if (null == _reflectField) return; fieldAccessor().set(_reflectField, onObject, obj); } void setName(String a_name) { _name = a_name; } boolean supportsIndex() { return alive() && (getHandler() instanceof Indexable4) && (! Handlers4.isUntyped(getHandler())); } public final void traverseValues(final Visitor4 userVisitor) { if(! alive()){ return; } traverseValues(container().transaction(), userVisitor); } public final void traverseValues(final Transaction transaction, final Visitor4 userVisitor) { if(! alive()){ return; } assertHasIndex(); ObjectContainerBase stream = transaction.container(); if(stream.isClient()){ Exceptions4.throwRuntimeException(Messages.CLIENT_SERVER_UNSUPPORTED); } synchronized(stream.lock()){ final Context context = transaction.context(); _index.traverseKeys(transaction, new Visitor4() { public void visit(Object obj) { FieldIndexKey key = (FieldIndexKey) obj; userVisitor.visit(((IndexableTypeHandler)getHandler()).indexEntryToObject(context, key.value())); } }); } } private void assertHasIndex() { if(! hasIndex()){ Exceptions4.throwRuntimeException(Messages.ONLY_FOR_INDEXED_FIELDS); } } public String toString() { StringBuffer sb = new StringBuffer(); if (_containingClass != null) { sb.append(_containingClass.getName()); sb.append("."); } sb.append(getName()); return sb.toString(); } private void initIndex(Transaction systemTrans) { initIndex(systemTrans, 0); } public void initIndex(Transaction systemTrans, final int id) { if(_index != null){ throw new IllegalStateException(); } if(systemTrans.container().isClient()){ return; } _index = newBTree(systemTrans, id); } protected final BTree newBTree(Transaction systemTrans, final int id) { ObjectContainerBase stream = systemTrans.container(); Indexable4 indexHandler = indexHandler(stream); if(indexHandler==null) { if(Debug4.atHome) { System.err.println("Could not create index for "+this+": No index handler found"); } return null; } return new BTree(systemTrans, id, new FieldIndexKeyHandler(indexHandler)); } protected Indexable4 indexHandler(ObjectContainerBase stream) { if(_reflectField ==null) { return null; } ReflectClass indexType = _reflectField.indexType(); TypeHandler4 classHandler = typeHandlerForClass(stream,indexType); if(! (classHandler instanceof Indexable4)){ return null; } return (Indexable4) classHandler; } /** @param trans */ public BTree getIndex(Transaction trans){ return _index; } public boolean isPrimitive() { return _isPrimitive; } public BTreeRange search(Transaction transaction, Object value) { assertHasIndex(); Object transActionalValue = Handlers4.wrapWithTransactionContext(transaction, value, getHandler()); BTreeNodeSearchResult lowerBound = searchLowerBound(transaction, transActionalValue); BTreeNodeSearchResult upperBound = searchUpperBound(transaction, transActionalValue); return lowerBound.createIncludingRange(upperBound); } private BTreeNodeSearchResult searchUpperBound(Transaction transaction, final Object value) { return searchBound(transaction, Integer.MAX_VALUE, value); } private BTreeNodeSearchResult searchLowerBound(Transaction transaction, final Object value) { return searchBound(transaction, 0, value); } private BTreeNodeSearchResult searchBound(Transaction transaction, int parentID, Object keyPart) { return getIndex(transaction).searchLeafByObject(transaction, createFieldIndexKey(parentID, keyPart), SearchTarget.LOWEST); } public boolean rebuildIndexForClass(LocalObjectContainer stream, ClassMetadata classMetadata) { // FIXME: BTree traversal over index here. long[] ids = classMetadata.getIDs(); for (int i = 0; i < ids.length; i++) { rebuildIndexForObject(stream, classMetadata, (int)ids[i]); } return ids.length > 0; } protected void rebuildIndexForObject(LocalObjectContainer stream, final ClassMetadata classMetadata, final int objectId) throws FieldIndexException { StatefulBuffer writer = stream.readStatefulBufferById(stream.systemTransaction(), objectId); if (writer != null) { rebuildIndexForWriter(stream, writer, objectId); } else { if(Deploy.debug){ throw new Db4oException("Unexpected null object for ID"); } } } protected void rebuildIndexForWriter(LocalObjectContainer stream, StatefulBuffer writer, final int objectId) { ObjectHeader oh = new ObjectHeader(stream, writer); Object obj = readIndexEntryForRebuild(writer, oh); addIndexEntry(stream.systemTransaction(), objectId, obj); } private final Object readIndexEntryForRebuild(StatefulBuffer writer, ObjectHeader oh) { ClassMetadata classMetadata = oh.classMetadata(); if(classMetadata == null){ return defaultValueForFieldType(); } ObjectIdContextImpl context = new ObjectIdContextImpl(writer.transaction(), writer, oh, writer.getID()); if(! classMetadata.seekToField(context, this)){ return defaultValueForFieldType(); } try { return readIndexEntry(context); } catch (CorruptionException exc) { throw new FieldIndexException(exc,this); } } private Object defaultValueForFieldType() { final TypeHandler4 handler = _fieldType.typeHandler(); return (handler instanceof PrimitiveHandler) ? ((PrimitiveHandler)handler).primitiveNull() : null; } public final void dropIndex(LocalTransaction systemTrans) { if(_index == null){ return; } ObjectContainerBase stream = systemTrans.container(); if (stream.configImpl().messageLevel() > Const4.NONE) { stream.message("dropping index " + toString()); } _index.free(systemTrans); stream.setDirtyInSystemTransaction(containingClass()); _index = null; } public void defragAspect(final DefragmentContext context) { if(!canDefragment()){ throw new IllegalStateException("Field '" + toString() + "' cannot be defragmented at this time."); } final TypeHandler4 correctTypeHandlerVersion = HandlerRegistry.correctHandlerVersion(context, getHandler(), _fieldType); context.slotFormat().doWithSlotIndirection(context, correctTypeHandlerVersion, new Closure4() { public Object run() { context.defragment(correctTypeHandlerVersion); return null; } }); } private boolean canDefragment() { if (alive() || updating() ) { return true; } if(_fieldType == null || getHandler() == null){ return false; } return ! _fieldType.stateDead(); } public void createIndex() { if(hasIndex()) { return; } LocalObjectContainer container= (LocalObjectContainer) container(); if (container.configImpl().messageLevel() > Const4.NONE) { container.message("creating index " + toString()); } initIndex(container.systemTransaction()); container.setDirtyInSystemTransaction(containingClass()); reindex(container); } private void reindex(LocalObjectContainer container) { ClassMetadata clazz = containingClass(); if (rebuildIndexForClass(container, clazz)) { container.systemTransaction().commit(); } } public AspectType aspectType() { return AspectType.FIELD; } public boolean canBeDisabled() { return true; } public void dropIndex() { dropIndex((LocalTransaction)container().systemTransaction()); } public boolean canUpdateFast() { if(hasIndex()){ return false; } if(isStruct()){ return false; } return true; } private boolean isStruct() { return _fieldType != null && _fieldType.isStruct(); } }