/******************************************************************************* * 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.File; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.locks.Lock; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.CloseableAccessor; import org.simantics.databoard.accessor.ParametrisedAccessor; 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.InvalidatedEvent; import org.simantics.databoard.accessor.file.FileAccessor; 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.reference.ChildReference; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.adapter.AdapterConstructionException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.SerializerConstructionException; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.BooleanType; import org.simantics.databoard.type.ByteType; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.DoubleType; import org.simantics.databoard.type.FloatType; import org.simantics.databoard.type.IntegerType; import org.simantics.databoard.type.LongType; import org.simantics.databoard.type.MapType; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.StringType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.type.VariantType; import org.simantics.databoard.util.binary.BinaryFile; import org.simantics.databoard.util.binary.Blob; import org.simantics.databoard.util.binary.RandomAccessBinary; /** * BinaryObject is an accessor to a binary object, usually a random access file. * BinaryObject cannot handle files of recursive types. *
* The file can be opened once. It may not be modified by any other instance other than * accessor while accessors are beign used. You must not create more than one * instance of BinaryObjects for a file. *
*
*
* @see BinaryArray
* @see BinaryBoolean
* @see BinaryByte
* @see BinaryDouble
* @see BinaryFloat
* @see BinaryInteger
* @see BinaryLong
* @see BinaryMap
* @see BinaryOptional
* @see BinaryRecord
* @see BinaryString
* @see BinaryUnion
* @see BinaryVariant
* @author Toni Kalajainen null
if this is root */
protected Accessor parent;
/** Listeners */
protected ListenerEntry listeners = null;
/** Random access binary object */
protected Blob b;
/** Type */
protected Datatype type;
/** File, optional */
protected File file;
/** Accessor params */
protected AccessorParams params;
BinaryObject(Accessor parent, Blob blob, Datatype type, AccessorParams params) {
this.parent = parent;
this.b = blob;
this.type = type;
this.params = params;
if (parent!=null && parent instanceof BinaryObject) {
file = ((BinaryObject)parent).file();
} else {
RandomAccessBinary sourceBinary = b.getSource();
if (sourceBinary instanceof BinaryFile) {
BinaryFile bf = (BinaryFile) sourceBinary;
file = bf.file();
}
}
}
public Datatype type() {
return type;
}
public void flush() throws AccessorException {
try {
b.flush();
} catch (IOException e) {
throw new AccessorException(e);
}
}
@Override
public void reset() throws AccessorException {
try {
b.reset();
} catch (IOException e) {
throw new AccessorException(e);
}
}
public RandomAccessBinary getSource() {
RandomAccessBinary result = b;
while (result instanceof Blob) result = ((Blob)result).getParent();
return result;
}
/**
* Close the random access file beneath
*/
public void close() throws AccessorException {
writeLock();
try {
if (parent!=null) {
((FileAccessor) parent).close();
return;
}
// Root Object
if (b==null) return;
RandomAccessBinary rab = getSource();
rab.flush();
rab.close();
b = null;
} catch (IOException e) {
throw new AccessorException(e);
} finally {
writeUnlock();
}
}
public boolean isOpen() {
return b.isOpen();
}
/**
* Get file if the binary object is based on binary file.
*
* @return file or null
*/
public File file() {
return file;
}
public RandomAccessBinary getBinary() {
return b;
}
@Override
public AccessorParams getParams() {
return params;
}
@Override
public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
}
protected ListenerEntry detachListener(Listener listener) throws AccessorException {
ListenerEntry e = listeners;
ListenerEntry p = null;
while (e!=null) {
// Found match
if (e.listener == listener) {
// The match was the first entry of the linked list
if (p==null) {
listeners = e.next;
return e;
}
// Some other entry, unlink e
p.next = e.next;
return e;
}
p = e;
e = e.next;
}
return null;
}
@Override
public void removeListener(Listener listener) throws AccessorException {
detachListener(listener);
}
/**
* Write a new value and flush the buffer.
*
* @param binding
* @param newValue
*/
@Override
public void setValue(Binding binding, Object newValue)
throws AccessorException {
assert b.isOpen();
writeLock();
try {
setValueNoflush(binding, newValue);
b.flush();
} catch (IOException e) {
throw new AccessorException(e);
} finally {
writeUnlock();
}
}
/**
* Write a new value and don't flush the buffer
*
* @param binding
* @param newValue
* @throws AccessorException
*/
public abstract void setValueNoflush(Binding binding, Object newValue) throws AccessorException;
public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
try {
Accessor a = getComponent(path);
a.setValue(binding, obj);
return true;
} catch (ReferenceException re) {
return false;
} catch (AccessorConstructionException e) {
throw new AccessorException(e);
}
}
@Override
public Object getValue(Binding binding) throws AccessorException {
assert b.isOpen();
readLock();
try {
Serializer s = params.serializerScheme.getSerializer( binding );
b.position(0L);
return s.deserialize(b, null);
} catch (IOException e) {
throw new AccessorException(e);
} catch (SerializerConstructionException e) {
throw new AccessorException(e);
} finally {
readUnlock();
}
}
public void getValue(Binding binding, Object obj) throws AccessorException {
assert b.isOpen();
readLock();
try {
Serializer s = params.serializerScheme.getSerializer( binding );
b.position(0L);
s.deserializeTo(b, null, obj);
} catch (IOException e) {
throw new AccessorException(e);
} catch (SerializerConstructionException e) {
throw new AccessorException(e);
} finally {
readUnlock();
}
}
@Override
public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
try {
Accessor a = getComponent(path);
a.getValue(binding, obj);
return true;
} catch (ReferenceException re) {
return false;
} catch (AccessorConstructionException e) {
throw new AccessorException(e);
}
}
public Object getValue(ChildReference path, Binding binding) throws AccessorException {
try {
Accessor a = getComponent(path);
return a.getValue(binding);
} catch (ReferenceException re) {
return null;
} catch (AccessorConstructionException e) {
throw new AccessorException(e);
}
}
Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
}
/**
* Send notification that this accessor has been detached from the parent
*/
void invalidatedNotification() {
ListenerEntry le = listeners;
while (le!=null) {
InterestSet is = le.getInterestSet();
if (is.inNotifications()) {
InvalidatedEvent e = new InvalidatedEvent();
emitEvent(le, e);
}
le = le.next;
}
}
/**
* Apply a change set that has events for the particular accessor.
* There are no sub-accessor events. Does not flush buffer.
*
* @param cs
* @param makeRollback
* @return rollback-event
* @throws AccessorException
*/
abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
@Override
public void apply(List