/******************************************************************************* * 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.java; 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.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.event.ValueAssigned; import org.simantics.databoard.accessor.impl.AccessorParams; import org.simantics.databoard.accessor.impl.ListenerEntry; import org.simantics.databoard.accessor.interestset.ByteInterestSet; 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.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.BooleanBinding; import org.simantics.databoard.binding.ByteBinding; import org.simantics.databoard.binding.DoubleBinding; import org.simantics.databoard.binding.FloatBinding; import org.simantics.databoard.binding.IntegerBinding; import org.simantics.databoard.binding.LongBinding; import org.simantics.databoard.binding.MapBinding; import org.simantics.databoard.binding.OptionalBinding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.StringBinding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.type.Datatype; /** * Accessor to a Java Object. *
* The monitoring contract forbids modifications to the object outside this * accessor object. If you do modifications to the value using other mechanisms, * you must notify the listeners of the accessor with {@link #notifyValueChanged()}. *
* If a lock is not provided, operations cannot be performed simulataneously
* in multiple-threads.
*
* @author Toni Kalajainen
*
* Read and write locks may optionally be provided for locking mechanisms.
* ReadWriteLock can be provied or a signle MutualExclusion lock.
*
* @param parent parent, or null
* @param binding
* @param initialValue the java object
* @param params accessor params
*/
public JavaObject(Accessor parent, Binding binding, Object initialValue, AccessorParams params) {
if (binding==null) throw new IllegalArgumentException("null arg");
this.parent = parent;
this.binding = binding;
this.object = initialValue;
this.params = params;
}
/**
* Get the Java Object
*
* @return Object
*/
public Object getObject() {
return object;
}
public Binding getBinding() {
return binding;
}
@Override
public AccessorParams getParams() {
return params;
}
public Datatype type() {
return binding.type();
}
/**
* Get lock if available.
*
* @return lock or null
*/
public Lock getReadLock() {
return params.readLock;
}
/**
* Get lock if available.
*
* @return lock or null
*/
public Lock getWriteLock() {
return params.writeLock;
}
/**
* Lock the lock if there is a lock.
*/
protected void readLock() {
if (params.readLock!=null) params.readLock.lock();
}
/**
* Unlock the lock if one exists
*/
protected void readUnlock() {
if (params.readLock!=null) params.readLock.unlock();
}
/**
* Lock the lock if there is a lock.
*/
protected void writeLock() {
if (params.writeLock!=null) params.writeLock.lock();
}
/**
* Unlock the lock if one exists
*/
protected void writeUnlock() {
if (params.writeLock!=null) params.writeLock.unlock();
}
@Override
public Object getValue(Binding binding) throws AccessorException {
readLock();
try {
// return params.adapterScheme.getAdapter(this.binding, binding, true, true).adapt(object);
if (binding == this.binding) {
return binding.isImmutable() ? object : binding.clone(object);
}
return adapt(object, this.binding, binding);
} catch (AdaptException e) {
throw new AccessorException(e);
} catch (AdapterConstructionException e) {
throw new AccessorException(e);
} finally {
readUnlock();
}
}
@Override
public void getValue(Binding binding, Object obj) throws AccessorException {
readLock();
try {
this.binding.readFrom(this.binding, object, obj);
} catch (BindingException e) {
throw new AccessorException(e);
} finally {
readLock();
}
}
@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);
}
}
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);
}
}
Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
}
@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);
}
public static JavaObject createAccessor(Accessor parent, Binding b, Object v, AccessorParams params) throws AccessorConstructionException {
return createSubAccessor(parent, b, v, params);
}
public static JavaObject createSubAccessor(Accessor parent, Binding b, Object v, AccessorParams params)
throws AccessorConstructionException {
if (b instanceof BooleanBinding) {
return new JavaBoolean(parent, (BooleanBinding)b, v, params);
}
if (b instanceof ByteBinding) {
return new JavaByte(parent, (ByteBinding)b, v, params);
}
if (b instanceof IntegerBinding) {
return new JavaInteger(parent, (IntegerBinding)b, v, params);
}
if (b instanceof LongBinding) {
return new JavaLong(parent, (LongBinding)b, v, params);
}
if (b instanceof FloatBinding) {
return new JavaFloat(parent, (FloatBinding)b, v, params);
}
if (b instanceof DoubleBinding) {
return new JavaDouble(parent, (DoubleBinding)b, v, params);
}
if (b instanceof StringBinding) {
return new JavaString(parent, (StringBinding)b, v, params);
}
if (b instanceof UnionBinding) {
return new JavaUnion(parent, (UnionBinding)b, v, params);
}
if (b instanceof OptionalBinding) {
return new JavaOptional(parent, (OptionalBinding)b, v, params);
}
if (b instanceof VariantBinding) {
return new JavaVariant(parent, (VariantBinding)b, v, params);
}
if (b instanceof ArrayBinding) {
return new JavaArray(parent, (ArrayBinding)b, v, params);
}
if (b instanceof MapBinding) {
return new JavaMap(parent, (MapBinding)b, v, params);
}
if (b instanceof RecordBinding) {
return new JavaRecord(parent, (RecordBinding)b, v, params);
}
throw new AccessorConstructionException("Can not create accessor to "+b.type());
}
/**
* 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 in the path of the event.
* This is called within lock.
*
* @param cs
* @param makeRollback
* @return rollback-event
* @throws AccessorException
*/
abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
@Override
public void apply(List