/*******************************************************************************
* Copyright (c) 2010 Association for Decentralized Information Management in
* Industry THTH ry.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.databoard.accessor.binary;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.accessor.Accessor;
import org.simantics.databoard.accessor.RecordAccessor;
import org.simantics.databoard.accessor.error.AccessorConstructionException;
import org.simantics.databoard.accessor.error.AccessorException;
import org.simantics.databoard.accessor.error.ReferenceException;
import org.simantics.databoard.accessor.event.Event;
import org.simantics.databoard.accessor.event.ValueAssigned;
import org.simantics.databoard.accessor.file.FileRecordAccessor;
import org.simantics.databoard.accessor.impl.AccessorParams;
import org.simantics.databoard.accessor.impl.ListenerEntry;
import org.simantics.databoard.accessor.interestset.InterestSet;
import org.simantics.databoard.accessor.interestset.RecordInterestSet;
import org.simantics.databoard.accessor.reference.ChildReference;
import org.simantics.databoard.accessor.reference.IndexReference;
import org.simantics.databoard.accessor.reference.LabelReference;
import org.simantics.databoard.accessor.reference.NameReference;
import org.simantics.databoard.adapter.AdaptException;
import org.simantics.databoard.binding.Binding;
import org.simantics.databoard.binding.RecordBinding;
import org.simantics.databoard.binding.error.BindingConstructionException;
import org.simantics.databoard.binding.error.BindingException;
import org.simantics.databoard.binding.mutable.MutableVariant;
import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
import org.simantics.databoard.serialization.Serializer;
import org.simantics.databoard.serialization.SerializerConstructionException;
import org.simantics.databoard.type.RecordType;
import org.simantics.databoard.util.binary.Blob;
import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
/**
* Accessor to a Binary Record.
* Get and set field operations scan the file.
* File operations are most efficient if field-specific subaccessors are acquired
* and used instead of {@link #setFieldValue(int, Binding, Object)} and
* {@link #getFieldValue(int, Binding)}.
*
* Note, To increase the random access performance of the record, create sub-accessors of
* its fields.
*
* @author Toni Kalajainen
*/
public class BinaryRecord extends BinaryObject implements RecordAccessor, FileRecordAccessor {
/** Accessors to children */
java.lang.ref.Reference[] children;
RecordBinding binding;
@SuppressWarnings("unchecked")
public BinaryRecord(BinaryObject parent, Blob blob, RecordType type, AccessorParams params) throws AccessorConstructionException {
super(parent, blob, type, params);
if (type.isReferable()) throw new AccessorConstructionException("Refereable record are not supported");
try {
binding = (RecordBinding) params.bindingScheme.getBinding(type);
} catch (BindingConstructionException e) {
throw new AccessorConstructionException(e);
}
int count = type.getComponentCount();
children = new java.lang.ref.Reference[count];
}
@Override
public RecordType type() {
return (RecordType) type;
}
@Override
public int count() {
return type().getComponentCount();
}
/**
* Get start position of a field
*
* @param fieldIndex
* @return
* @throws AccessorException
*/
long getStartPosition(int fieldIndex) throws AccessorException {
assert b.isOpen();
readLock();
try {
// sa, saIndex = getFloorExistingAccessor
BinaryObject sa = getExistingAccessor(fieldIndex);
if (sa!=null) return sa.b.getStartPositionInSourceBinary();
int saIndex = -1;
for (int i=fieldIndex; i>=0; i--) {
sa = getExistingAccessor(i);
if (sa!=null) {
saIndex = i;
break;
}
}
long pos = saIndex==-1 ? 0L : sa.b.getStartPositionInSourceBinary();
b.position(pos);
if (saIndex<0) saIndex = 0;
for (int i=saIndex; i T getFieldAccessor(String fieldName) throws AccessorConstructionException
{
int fieldIndex = type().getComponentIndex(fieldName);
if (fieldIndex<0) throw new AccessorConstructionException("Field "+fieldName+" does not exist");
return (T) getFieldAccessor(fieldIndex);
}
@SuppressWarnings("unchecked")
@Override
public T getFieldAccessor(int index) throws AccessorConstructionException
{
if (index<0 || index>=count()) throw new ReferenceException("Field index ("+index+") out of bounds ("+count()+")");
assert b.isOpen();
readLock();
try {
// Get existing or create new
java.lang.ref.Reference ref = children[index];
BinaryObject sa = (ref!=null)?(BinaryObject)ref.get():null;
if (sa==null) {
// Instantiate new accessor
Binding cb = binding.getComponentBinding(index);
Serializer cs = params.serializerScheme.getSerializer( cb );
long pos = getStartPosition(index);
b.position(pos);
cs.skip(b);
long len = b.position() - pos;
sa = createSubAccessor(cb.type(), pos, len, params);
children[index] = new SoftReference(sa);
// Add component interest sets
ListenerEntry le = listeners;
while (le!=null) {
RecordInterestSet is = le.getInterestSet();
InterestSet cis = is.getComponentInterest(index);
if (cis != null) {
try {
ChildReference childPath = ChildReference.concatenate( le.path, new IndexReference(index) );
sa.addListener(le.listener, cis, childPath, le.executor);
} catch (AccessorException e) {
throw new AccessorConstructionException(e);
}
}
le = le.next;
}
}
return (T) sa;
} catch (IOException e) {
throw new AccessorConstructionException(e);
} catch (AccessorException e) {
throw new AccessorConstructionException(e);
} catch (SerializerConstructionException e) {
throw new AccessorConstructionException(e);
} finally {
readUnlock();
}
}
/**
* Get existing sub accessor
* @param index
* @return sub-accessor or null
*/
BinaryObject getExistingAccessor(int index)
{
if (index<0 || index>=count()) throw new RuntimeException("Field index ("+index+") out of bounds ("+count()+")");
// Get existing or create new
java.lang.ref.Reference ref = children[index];
BinaryObject accessor = (ref!=null)?(BinaryObject)ref.get():null;
return accessor;
}
@Override
public Object getFieldValue(String fieldName, Binding fieldBinding)
throws AccessorException {
int fieldIndex = type().getComponentIndex(fieldName);
if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");
return getFieldValue(fieldIndex, fieldBinding);
}
@Override
public Object getFieldValue(int index, Binding fieldBinding)
throws AccessorException {
assert b.isOpen();
readLock();
try {
b.position( getStartPosition(index) );
List