/* 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.array;
import com.db4o.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.delete.*;
import com.db4o.internal.handlers.*;
import com.db4o.internal.marshall.*;
import com.db4o.marshall.*;
import com.db4o.reflect.*;
import com.db4o.typehandlers.*;
/**
* This is the latest version, the one that should be used.
* @exclude
*/
public class ArrayHandler implements CascadingTypeHandler, Comparable4, ValueTypeHandler, VariableLengthTypeHandler, VersionedTypeHandler, QueryableTypeHandler{
private TypeHandler4 _handler;
private boolean _usePrimitiveClassReflector;
protected final ArrayVersionHelper _versionHelper;
public ArrayHandler(){
_versionHelper = createVersionHelper();
}
public ArrayHandler(TypeHandler4 handler, boolean usePrimitiveClassReflector) {
this();
_handler = handler;
_usePrimitiveClassReflector = usePrimitiveClassReflector;
}
protected ArrayVersionHelper createVersionHelper() {
return new ArrayVersionHelper();
}
protected ReflectArray arrayReflector(ObjectContainerBase container){
return container.reflector().array();
}
public Iterator4 allElements(ObjectContainerBase container, Object a_object) {
return allElements(arrayReflector(container), a_object);
}
public static Iterator4 allElements(final ReflectArray reflectArray, final Object array) {
return new ReflectArrayIterator(reflectArray, array);
}
public final void cascadeActivation(ActivationContext context){
if (! Handlers4.isCascading(_handler)) {
return;
}
ObjectContainerBase container = context.container();
Iterator4 all = allElements(container, context.targetObject());
while (all.moveNext()) {
context.cascadeActivationToChild(all.current());
}
}
ObjectContainerBase container(Transaction trans) {
return trans.container();
}
public void collectIDs(final QueryingReadContext context) {
final TypeHandler4 handler = HandlerRegistry.correctHandlerVersion(context, _handler);
forEachElement(context, new Runnable() {
public void run() {
context.readId(handler);
}
});
}
protected ArrayInfo forEachElement(final AbstractBufferContext context, final Runnable elementRunnable){
final ArrayInfo info = newArrayInfo();
withContent(context, new Runnable() {
public void run() {
if (context.buffer() == null) {
return;
}
if (Deploy.debug) {
Debug4.readBegin(context, identifier());
}
if(isUntypedByteArray(context)) {
return;
}
readInfo(context.transaction(), context, info);
int elementCount = info.elementCount();
elementCount -= reducedCountForNullBitMap(info, context);
for (int i = 0; i < elementCount; i++) {
elementRunnable.run();
}
}
});
return info;
}
protected void withContent(AbstractBufferContext context, Runnable runnable){
runnable.run();
}
private int reducedCountForNullBitMap(ArrayInfo info, final ReadBuffer context) {
if (! hasNullBitmap(info)) {
return 0;
}
return reducedCountForNullBitMap(info.elementCount(), readNullBitmap(context, info.elementCount()));
}
private int reducedCountForNullBitMap(int count, BitMap4 bitMap) {
int nullCount = 0;
for (int i = 0; i < count; i++) {
if(bitMap.isTrue(i)){
nullCount++;
}
}
return nullCount;
}
public void delete(final DeleteContext context) throws Db4oIOException {
if (! cascadeDelete(context)) {
return;
}
forEachElement((AbstractBufferContext)context, new Runnable() {
public void run() {
_handler.delete(context);
}
});
}
private boolean cascadeDelete(DeleteContext context) {
// FIXME: ValueType could reference objects, shouldn't they be deleted too?
return context.cascadeDelete() && Handlers4.isCascading(_handler);
}
// FIXME: This code has not been called in any test case when the
// new ArrayMarshaller was written.
// Apparently it only frees slots.
// For now the code simply returns without freeing.
/** @param classPrimitive */
public final void deletePrimitiveEmbedded(
StatefulBuffer buffer,
PrimitiveTypeMetadata classPrimitive) {
buffer.readInt(); //int address = a_bytes.readInt();
buffer.readInt(); //int length = a_bytes.readInt();
}
public boolean equals(Object obj) {
if (! (obj instanceof ArrayHandler)) {
return false;
}
ArrayHandler other = (ArrayHandler) obj;
if (other.identifier() != identifier()) {
return false;
}
if(_handler == null){
return other._handler == null;
}
return _handler.equals(other._handler) && _usePrimitiveClassReflector == other._usePrimitiveClassReflector;
}
public int hashCode() {
if(_handler == null){
return HASHCODE_FOR_NULL;
}
int hc = _handler.hashCode() >> 7;
return _usePrimitiveClassReflector ? hc : - hc;
}
protected boolean handleAsByteArray(Object obj) {
if(Deploy.csharp){
return obj.getClass() == byte[].class;
}
return obj instanceof byte[];
}
public byte identifier() {
return Const4.YAPARRAY;
}
public ReflectClass primitiveClassReflector(Reflector reflector) {
return Handlers4.primitiveClassReflector(_handler, reflector);
}
protected Object readCreate(Transaction trans, ReadBuffer buffer, ArrayInfo info) {
readInfo(trans, buffer, info);
ReflectClass clazz = newInstanceReflectClass(trans.reflector(), info);
if(clazz == null){
return null;
}
return newInstance(arrayReflector(container(trans)), info, clazz);
}
protected final Object newInstance(ReflectArray arrayReflector, ArrayInfo info, ReflectClass clazz) {
return arrayReflector.newInstance(clazz, info);
}
protected final ReflectClass newInstanceReflectClass(Reflector reflector, ArrayInfo info){
if(_usePrimitiveClassReflector){
return primitiveClassReflector(reflector);
}
return info.reflectClass();
}
public TypeHandler4 readCandidateHandler(QueryingReadContext context) {
return this;
}
protected void readInfo(Transaction trans, ReadBuffer buffer, ArrayInfo info){
int classID = buffer.readInt();
if (isPreVersion0Format(classID)) {
throw new UnsupportedOldFormatException();
} else {
_versionHelper.readTypeInfo(trans, buffer, info, classID);
reflectClassFromElementsEntry(container(trans), info, classID);
readDimensions(info, buffer);
}
if(Debug4.exceedsMaximumArrayEntries(info.elementCount(), _usePrimitiveClassReflector)){
info.elementCount(0);
}
}
protected void readDimensions(ArrayInfo info, ReadBuffer buffer) {
info.elementCount(buffer.readInt());
}
protected boolean isPreVersion0Format(int elementCount) {
return _versionHelper.isPreVersion0Format(elementCount);
}
private void reflectClassFromElementsEntry(ObjectContainerBase container, ArrayInfo info, int classID) {
info.reflectClass(_versionHelper.reflectClassFromElementsEntry(container, info, classID));
}
protected final ReflectClass classReflector(Reflector reflector, ClassMetadata classMetadata, boolean isPrimitive){
return _versionHelper.classReflector(reflector, classMetadata, isPrimitive);
}
public static Iterator4 iterator(ReflectClass claxx, Object obj) {
ReflectArray reflectArray = claxx.reflector().array();
if (reflectArray.isNDimensional(claxx)) {
return MultidimensionalArrayHandler.allElementsMultidimensional(reflectArray, obj);
}
return ArrayHandler.allElements(reflectArray, obj);
}
protected boolean useJavaHandling() {
return _versionHelper.useJavaHandling();
}
protected int classIDFromInfo(ObjectContainerBase container, ArrayInfo info){
return _versionHelper.classIDFromInfo(container, info);
}
private final int marshalledClassID(ObjectContainerBase container, ArrayInfo info){
return classIdToMarshalledClassId(classIDFromInfo(container, info), info.primitive());
}
public final int classIdToMarshalledClassId(int classID, boolean primitive){
return _versionHelper.classIdToMarshalledClassId(classID, primitive);
}
protected final boolean isPrimitive(Reflector reflector, ReflectClass claxx, ClassMetadata classMetadata) {
return _versionHelper.isPrimitive(reflector, claxx, classMetadata);
}
private ReflectClass componentType(ObjectContainerBase container, Object obj){
return arrayReflector(container).getComponentType(container.reflector().forObject(obj));
}
public void defragment(DefragmentContext context) {
if(context.classMetadata().hasIdentity()){
defragmentSlot(context);
}else{
context.incrementOffset(linkLength());
}
}
public final void defragmentSlot(DefragmentContext context) {
if (Deploy.debug) {
Debug4.readBegin(context, Const4.YAPARRAY);
}
if(isUntypedByteArray(context)) {
return;
}
int classIdOffset = context.targetBuffer().offset();
ArrayInfo info = newArrayInfo();
readInfo(context.transaction(), context, info);
defragmentWriteMappedClassId(context, info, classIdOffset);
int elementCount = info.elementCount();
if(hasNullBitmap(info)){
BitMap4 bitMap = readNullBitmap(context, elementCount);
elementCount -= reducedCountForNullBitMap(elementCount, bitMap);
}
TypeHandler4 correctTypeHandlerVersion = correctHandlerVersion(context, _handler, info);
for (int i = 0; i < elementCount; i++) {
context.defragment(correctTypeHandlerVersion);
}
if (Deploy.debug) {
Debug4.readEnd(context);
}
}
private TypeHandler4 correctHandlerVersion(DefragmentContext context, TypeHandler4 handler, ArrayInfo info) {
ClassMetadata classMetadata = classMetadata(context, info);
return HandlerRegistry.correctHandlerVersion(context, handler, classMetadata);
}
private ClassMetadata classMetadata(DefragmentContext context, ArrayInfo info) {
int classMetadataId = classIDFromInfo(container(context), info);
return container(context).classMetadataForID(classMetadataId);
}
private void defragmentWriteMappedClassId(DefragmentContext context, ArrayInfo info, int classIdOffset) {
ByteArrayBuffer targetBuffer = context.targetBuffer();
int currentOffset = targetBuffer.offset();
targetBuffer.seek(classIdOffset);
int classID = classIDFromInfo(container(context), info);
int mappedID = context.mappedID(classID);
final int marshalledMappedId = classIdToMarshalledClassId(mappedID, info.primitive());
targetBuffer.writeInt(marshalledMappedId);
targetBuffer.seek(currentOffset);
}
private boolean isUntypedByteArray(BufferContext context) {
return Handlers4.isUntyped(_handler) && handleAsByteArray(context);
}
protected boolean handleAsByteArray(BufferContext context){
int offset = context.offset();
ArrayInfo info = newArrayInfo();
readInfo(context.transaction(), context, info);
boolean isByteArray = context.transaction().reflector().forClass(byte.class).equals(info.reflectClass());
context.seek(offset);
return isByteArray;
}
public Object read(ReadContext context) {
if (Deploy.debug) {
Debug4.readBegin(context, identifier());
}
ArrayInfo info = newArrayInfo();
Object array = readCreate(context.transaction(), context, info);
readElements(context, info, array);
if (Deploy.debug) {
Debug4.readEnd(context);
}
return array;
}
protected void readElements(ReadContext context, ArrayInfo info, Object array) {
readInto(context, info, array);
}
protected ArrayInfo newArrayInfo() {
return new ArrayInfo();
}
protected final void readInto (ReadContext context, ArrayInfo info, Object array) {
if (array == null){
return;
}
if(handleAsByteArray(array)){
context.readBytes((byte[])array); // byte[] performance optimisation
return;
}
if (hasNullBitmap(info)) {
BitMap4 nullBitMap = readNullBitmap(context, info.elementCount());
for (int i = 0; i < info.elementCount(); i++) {
Object obj = nullBitMap.isTrue(i) ? null :context.readObject(_handler);
arrayReflector(container(context)).set(array, i, obj);
}
} else {
for (int i = 0; i < info.elementCount(); i++) {
arrayReflector(container(context)).set(array, i, context.readObject(_handler));
}
}
}
protected BitMap4 readNullBitmap(ReadBuffer context, int length) {
return context.readBitMap(length);
}
protected final boolean hasNullBitmap(ArrayInfo info) {
return _versionHelper.hasNullBitmap(info);
}
public void write(WriteContext context, Object obj) {
if (Deploy.debug) {
Debug4.writeBegin(context, identifier());
}
ArrayInfo info = newArrayInfo();
analyze(container(context), obj, info);
writeInfo(context, info);
writeElements(context, obj, info);
if (Deploy.debug) {
Debug4.writeEnd(context);
}
}
protected void writeElements(WriteContext context, Object obj, ArrayInfo info) {
if(handleAsByteArray(obj)){
context.writeBytes((byte[])obj); // byte[] performance optimisation
}else{
if (hasNullBitmap(info)) {
BitMap4 nullItems = nullItemsMap(arrayReflector(container(context)), obj);
writeNullBitmap(context, nullItems);
for (int i = 0; i < info.elementCount(); i++) {
if (!nullItems.isTrue(i)) {
context.writeObject(_handler, arrayReflector(container(context)).get(obj, i));
}
}
} else {
for (int i = 0; i < info.elementCount(); i++) {
context.writeObject(_handler, arrayReflector(container(context)).get(obj, i));
}
}
}
}
protected void writeInfo(WriteContext context, ArrayInfo info) {
writeHeader(context, info);
writeDimensions(context, info);
}
private void writeHeader(WriteContext context, ArrayInfo info) {
context.writeInt(marshalledClassID(container(context), info));
_versionHelper.writeTypeInfo(context, info);
}
protected void writeDimensions(WriteContext context, ArrayInfo info) {
context.writeInt(info.elementCount());
}
protected final void analyze(ObjectContainerBase container, Object obj, ArrayInfo info) {
// TODO: Move as much analysis as possible to ReflectArray#analyze()
arrayReflector(container).analyze(obj, info);
ReflectClass claxx = componentType(container, obj);
ClassMetadata classMetadata = container.produceClassMetadata(claxx);
boolean primitive = isPrimitive(container.reflector(), claxx, classMetadata);
if(primitive){
claxx = classMetadata.classReflector();
}
info.primitive(primitive);
info.reflectClass(claxx);
analyzeDimensions(container, obj, info);
}
protected void analyzeDimensions(ObjectContainerBase container, Object obj, ArrayInfo info){
info.elementCount(arrayReflector(container).getLength(obj));
}
private void writeNullBitmap(WriteBuffer context, BitMap4 bitMap) {
context.writeBytes(bitMap.bytes());
}
protected BitMap4 nullItemsMap(ReflectArray reflector, Object array) {
int arrayLength = reflector.getLength(array);
BitMap4 nullBitMap = new BitMap4(arrayLength);
for (int i = 0; i < arrayLength; i++) {
if (reflector.get(array, i) == null) {
nullBitMap.set(i, true);
}
}
return nullBitMap;
}
ObjectContainerBase container(Context context) {
return context.transaction().container();
}
public PreparedComparison prepareComparison(Context context, Object obj) {
return new PreparedArrayContainsComparison(context, this, _handler, obj);
}
public int linkLength() {
return Const4.INDIRECTION_LENGTH;
}
public TypeHandler4 unversionedTemplate() {
return new ArrayHandler();
}
public Object deepClone(Object context) {
TypeHandlerCloneContext typeHandlerCloneContext = (TypeHandlerCloneContext) context;
ArrayHandler original = (ArrayHandler) typeHandlerCloneContext.original;
ArrayHandler cloned = (ArrayHandler) Reflection4.newInstance(this);
cloned._usePrimitiveClassReflector = original._usePrimitiveClassReflector;
cloned._handler = typeHandlerCloneContext.correctHandlerVersion(original.delegateTypeHandler());
return cloned;
}
public TypeHandler4 delegateTypeHandler() {
return _handler;
}
private static final int HASHCODE_FOR_NULL = 9141078;
@Override
public String toString() {
return "ArrayHandler(isPrimitive=" + _usePrimitiveClassReflector + ", handler=" + _handler + ")";
}
public boolean descendsIntoMembers() {
return true;
}
}