/* 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.handlers;
import com.db4o.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.activation.*;
import com.db4o.internal.delete.*;
import com.db4o.internal.marshall.*;
import com.db4o.internal.metadata.*;
import com.db4o.marshall.*;
import com.db4o.reflect.*;
import com.db4o.typehandlers.*;
/**
* @exclude
*/
public class StandardReferenceTypeHandler implements FieldAwareTypeHandler, IndexableTypeHandler, ReadsObjectIds {
private static final int HASHCODE_FOR_NULL = 72483944;
private ClassMetadata _classMetadata;
public StandardReferenceTypeHandler(ClassMetadata classMetadata) {
classMetadata(classMetadata);
}
public StandardReferenceTypeHandler(){
}
public void defragment(final DefragmentContext context) {
traverseAllAspects(context, new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected int internalDeclaredAspectCount(ClassMetadata classMetadata) {
return context.readInt();
}
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if (!isNull) {
aspect.defragAspect(context);
}
}
@Override
public boolean accept(ClassAspect aspect) {
return aspect.isEnabledOn(context);
}
});
}
public void delete(DeleteContext context) throws Db4oIOException {
context.deleteObject();
}
public final void activateAspects(final UnmarshallingContext context) {
final BooleanByRef schemaUpdateDetected = new BooleanByRef();
ContextState savedState = context.saveState();
TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
public boolean accept(ClassAspect aspect) {
return aspect.isEnabledOn(context);
}
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if(aspect instanceof FieldMetadata){
FieldMetadata field = (FieldMetadata) aspect;
if(field.updating()){
schemaUpdateDetected.value = true;
}
// TODO: cant the aspect handle it itself?
// Probably no because old aspect versions might not be able
// to handle null...
if (isNull) {
if(field.getStoredType() == null || !field.getStoredType().isPrimitive()) {
field.set(context.persistentObject(), null);
}
return;
}
}
aspect.activate(context);
}
};
traverseAllAspects(context, command);
if(schemaUpdateDetected.value){
context.restoreState(savedState);
command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
FieldMetadata field = (FieldMetadata)aspect;
if (! isNull) {
field.attemptUpdate(context);
}
}
public boolean accept(ClassAspect aspect){
return aspect instanceof FieldMetadata;
}
};
traverseAllAspects(context, command);
}
}
public void activate(ReferenceActivationContext context) {
activateAspects((UnmarshallingContext) context);
}
public void write(WriteContext context, Object obj) {
marshallAspects(obj, (MarshallingContext)context);
}
public void marshallAspects(final Object obj, final MarshallingContext context) {
final Transaction trans = context.transaction();
final TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected int internalDeclaredAspectCount(ClassMetadata classMetadata) {
int aspectCount = classMetadata._aspects.length;
context.writeDeclaredAspectCount(aspectCount);
return aspectCount;
}
@Override
public boolean accept(ClassAspect aspect) {
return aspect.isEnabledOn(context);
}
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
Object marshalledObject = obj;
if(aspect instanceof FieldMetadata){
FieldMetadata field = (FieldMetadata) aspect;
marshalledObject = field.getOrCreate(trans, obj);
if(marshalledObject == null) {
context.isNull(currentSlot, true);
field.addIndexEntry(trans, context.objectID(), null);
return;
}
}
aspect.marshall(context, marshalledObject);
}
@Override
public void processAspectOnMissingClass(ClassAspect aspect, int currentSlot){
((MarshallingContext)context).isNull(currentSlot, true);
}
};
traverseAllAspects(context, command);
}
public PreparedComparison prepareComparison(Context context, final Object source) {
if(source == null){
return Null.INSTANCE;
}
if(source instanceof Integer){
int id = ((Integer)source).intValue();
return new PreparedComparisonImpl(id, null);
}
if(source instanceof TransactionContext){
TransactionContext tc = (TransactionContext)source;
Object obj = tc._object;
int id = idFor(obj, tc._transaction);
return new PreparedComparisonImpl(id, reflectClassFor(obj));
}
return platformComparisonFor(source);
}
@decaf.RemoveFirst(decaf.Platform.JDK11)
private PreparedComparison platformComparisonFor(final Object source) {
if(source == null) {
return new PreparedComparison() {
public int compareTo(Object obj) {
return obj == null ? 0 : -1;
}
};
}
//TODO: Move the comparable wrapping to a .Net specific StandardStructHandler
if (source instanceof Comparable) {
return new PreparedComparison(){
public int compareTo(Object obj) {
if(obj == null) {
return 1;
}
Comparable self = (Comparable) source;
return self.compareTo(obj);
}
};
}
throw new IllegalComparisonException();
}
private ReflectClass reflectClassFor(Object obj) {
return classMetadata().reflector().forObject(obj);
}
private int idFor(Object object, Transaction inTransaction) {
return stream().getID(inTransaction, object);
}
private ObjectContainerBase stream() {
return classMetadata().container();
}
public final static class PreparedComparisonImpl implements PreparedComparison {
private final int _id;
private final ReflectClass _claxx;
public PreparedComparisonImpl(int id, ReflectClass claxx) {
_id = id;
_claxx = claxx;
}
public int compareTo(Object obj) {
if(obj instanceof TransactionContext){
obj = ((TransactionContext)obj)._object;
}
if(obj == null){
return _id == 0 ? 0 : 1;
}
if(obj instanceof Integer){
int targetInt = ((Integer)obj).intValue();
return _id == targetInt ? 0 : (_id < targetInt ? - 1 : 1);
}
if(_claxx != null){
if(_claxx.isAssignableFrom(_claxx.reflector().forObject(obj))){
return 0;
}
}
throw new IllegalComparisonException();
}
}
public final void traverseAllAspects(MarshallingInfo context, TraverseAspectCommand command) {
ClassMetadata classMetadata = classMetadata();
assertClassMetadata(context.classMetadata());
classMetadata.traverseAllAspects(command);
}
protected MarshallingInfo ensureFieldList(MarshallingInfo context) {
return context;
}
private void assertClassMetadata(final ClassMetadata contextMetadata) {
// if (contextMetadata != classMetadata()) {
// throw new IllegalStateException("expecting '" + classMetadata() + "', got '" + contextMetadata + "'");
// }
}
public ClassMetadata classMetadata() {
return _classMetadata;
}
public void classMetadata(ClassMetadata classMetadata) {
_classMetadata = classMetadata;
}
public boolean equals(Object obj) {
if(! (obj instanceof StandardReferenceTypeHandler)){
return false;
}
StandardReferenceTypeHandler other = (StandardReferenceTypeHandler) obj;
if(classMetadata() == null){
return other.classMetadata() == null;
}
return classMetadata().equals(other.classMetadata());
}
public int hashCode() {
if(classMetadata() != null){
return classMetadata().hashCode();
}
return HASHCODE_FOR_NULL;
}
public TypeHandler4 unversionedTemplate() {
return new StandardReferenceTypeHandler(null);
}
public Object deepClone(Object context) {
TypeHandlerCloneContext typeHandlerCloneContext = (TypeHandlerCloneContext) context;
StandardReferenceTypeHandler cloned = (StandardReferenceTypeHandler) Reflection4.newInstance(this);
if(typeHandlerCloneContext.original instanceof StandardReferenceTypeHandler){
StandardReferenceTypeHandler original = (StandardReferenceTypeHandler) typeHandlerCloneContext.original;
cloned.classMetadata(original.classMetadata());
}else{
// New logic: ClassMetadata takes the responsibility in
// #correctHandlerVersion() to set the
// ClassMetadata directly on cloned handler.
// if(_classMetadata == null){
// throw new IllegalStateException();
// }
cloned.classMetadata(_classMetadata);
}
return cloned;
}
public void collectIDs(final CollectIdContext context, final Predicate4<ClassAspect> predicate) {
TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if(isNull) {
return;
}
if (predicate.match(aspect)) {
aspect.collectIDs(context);
} else {
aspect.incrementOffset(context, context);
}
}
};
traverseAllAspects(context, command);
}
public void cascadeActivation(ActivationContext context) {
assertClassMetadata(context.classMetadata());
context.cascadeActivationToTarget();
}
public TypeHandler4 readCandidateHandler(QueryingReadContext context) {
if (classMetadata().isArray()) {
return this;
}
return null;
}
public void collectIDs(final QueryingReadContext context) throws Db4oIOException {
if(collectIDsByTypehandlerAspect(context)){
return;
}
collectIDsByInstantiatingCollection(context);
}
private boolean collectIDsByTypehandlerAspect(QueryingReadContext context) throws Db4oIOException {
final BooleanByRef aspectFound = new BooleanByRef(false);
final CollectIdContext subContext = CollectIdContext.forID(context.transaction(), context.collector(), context.collectionID());
TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(subContext)) {
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if(isNull) {
return;
}
if(isCollectIdTypehandlerAspect(aspect)){
aspectFound.value = true;
aspect.collectIDs(subContext);
}else {
aspect.incrementOffset(subContext, subContext);
}
}
};
traverseAllAspects(subContext, command);
return aspectFound.value;
}
private boolean isCollectIdTypehandlerAspect(ClassAspect aspect){
if(! (aspect instanceof TypeHandlerAspect)){
return false;
}
TypeHandler4 typehandler = ((TypeHandlerAspect)aspect)._typeHandler;
return Handlers4.isCascading(typehandler);
}
private void collectIDsByInstantiatingCollection(final QueryingReadContext context) throws Db4oIOException {
int id = context.collectionID();
if (id == 0) {
return;
}
final Transaction transaction = context.transaction();
final ObjectContainerBase container = context.container();
Object obj = container.getByID(transaction, id);
if (obj == null) {
return;
}
// FIXME: [TA] review activation depth
int depth = DepthUtil.adjustDepthToBorders(2);
container.activate(transaction, obj, container.activationDepthProvider().activationDepth(depth, ActivationMode.ACTIVATE));
Platform4.forEachCollectionElement(obj, new Visitor4() {
public void visit(Object elem) {
context.add(elem);
}
});
}
public void readVirtualAttributes(final ObjectReferenceContext context){
TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if (!isNull) {
if(aspect instanceof VirtualFieldMetadata){
((VirtualFieldMetadata)aspect).readVirtualAttribute(context);
} else {
aspect.incrementOffset(context, context);
}
}
}
};
traverseAllAspects(context, command);
}
public void addFieldIndices(final ObjectIdContextImpl context) {
TraverseAspectCommand command = new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if(aspect instanceof FieldMetadata){
FieldMetadata field = (FieldMetadata)aspect;
if (isNull) {
field.addIndexEntry(context.transaction(), context.objectId(), null);
} else {
field.addFieldIndex(context);
}
}else{
aspect.incrementOffset(context.buffer(), context);
}
}
@Override
public boolean accept(ClassAspect aspect) {
return aspect.isEnabledOn(context);
}
};
traverseAllAspects(context, command);
}
public void deleteMembers(final DeleteContextImpl context, final boolean isUpdate) {
TraverseAspectCommand command=new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
protected void processAspect(ClassAspect aspect, int currentSlot, boolean isNull) {
if(isNull){
if(aspect instanceof FieldMetadata){
FieldMetadata field = (FieldMetadata)aspect;
field.removeIndexEntry(context.transaction(), context.objectId(), null);
}
return;
}
aspect.delete(context, isUpdate);
}
};
traverseAllAspects(context, command);
}
public boolean seekToField(final ObjectHeaderContext context, final ClassAspect aspect) {
final BooleanByRef found = new BooleanByRef(false);
TraverseAspectCommand command=new MarshallingInfoTraverseAspectCommand(ensureFieldList(context)) {
@Override
public boolean accept(ClassAspect aspect) {
return aspect.isEnabledOn(_marshallingInfo);
}
@Override
protected void processAspect(ClassAspect curField, int currentSlot, boolean isNull) {
if (curField == aspect) {
found.value = !isNull;
cancel();
return;
}
if(!isNull){
curField.incrementOffset(_marshallingInfo.buffer(), context);
}
}
};
traverseAllAspects(context, command);
return found.value;
}
public final Object indexEntryToObject(Context context, Object indexEntry){
if(indexEntry == null){
return null;
}
int id = ((Integer)indexEntry).intValue();
return ((ObjectContainerBase)context.objectContainer()).getByID2(context.transaction(), id);
}
public final void defragIndexEntry(DefragmentContextImpl context) {
context.copyID();
}
public final Object readIndexEntry(Context context, ByteArrayBuffer a_reader) {
return new Integer(a_reader.readInt());
}
public final Object readIndexEntryFromObjectSlot(MarshallerFamily mf, StatefulBuffer statefulBuffer) throws CorruptionException{
return readIndexEntry(statefulBuffer.transaction().context(), statefulBuffer);
}
public Object readIndexEntry(ObjectIdContext context) throws CorruptionException, Db4oIOException{
return new Integer(context.readInt());
}
public int linkLength() {
return Const4.ID_LENGTH;
}
public void writeIndexEntry(Context context, ByteArrayBuffer a_writer, Object a_object) {
if(a_object == null){
a_writer.writeInt(0);
return;
}
a_writer.writeInt(((Integer)a_object).intValue());
}
public TypeHandler4 delegateTypeHandler(Context context){
return classMetadata().delegateTypeHandler(context);
}
public ObjectID readObjectID(InternalReadContext context){
return ObjectID.read(context);
}
}