/******************************************************************************* * 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; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executor; import org.simantics.databoard.Accessors; import org.simantics.databoard.accessor.binary.BinaryObject; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.event.Event; import org.simantics.databoard.accessor.event.InvalidatedEvent; import org.simantics.databoard.accessor.impl.ChangeSet; import org.simantics.databoard.accessor.interestset.InterestSet; import org.simantics.databoard.accessor.java.JavaObject; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.type.Datatype; /** * Accessor is an interface to access, modify and monitor a data container. * The actual storage format and location is implementation specific. * For instance, container could be: a bunch of bytes, a Java object, a file, * a folder with files, a network location, or direct memory of a simulation * experiment.

* * The structure is presentable with Databoard type system. The type can be * anything but a recursive structure.

* * The listening model allows placing of listener objects to accessors. * Each listener is associated with a InterestSet. It describes a sub-tree of * nodes to listen and aspects to monitor. Accessors emit Events on modifications * of structure and value. There is a reference in each event, and it is relative * to the accessor where the listener was placed. For instance, a listener, that is * interested in whole tree, is placed on a root accessor. A modification at a * leaf-node spawns an event with a reference path from the root to the leaf. * If the listener was placed directly on the leaf, there wouldn't be a path * in the evenr object.

* * Multi-thread-usage is implementation dependent, read the documentation.

* * Construction and destruction model is also implementation dependent. * A rule of thumb in life management of objects is that, the party that * constructs an object must destroy it. Both construction and destruction of * the container are also outside the scope of the Accessor interface.

* * However, as general implementation contract, the data container is disposed * as whole, individual nodes are not. While the container is alive, individual * nodes may be disposed using garbage collection mechanisms. Reference queue * mechanism is one implementatio strategy. More common strategy is usage of * weak references from parent to child.

* * @see Accessors * @see JavaObject Accessor to Java Object container * @see BinaryObject Accessor to a container in File or Memory * @see ArrayAccessor * @see BooleanAccessor * @see ByteAccessor * @see DoubleAccessor * @see FloatAccessor * @see IntegerAccessor * @see LongAccessor * @see MapAccessor * @see OptionalAccessor * @see RecordAccessor * @see StringAccessor * @see UnionAccessor * @see VariantAccessor * @author Toni Kalajainen (toni.kalajainen@iki.fi) */ public interface Accessor { /** * Get a snapshot of the object as a single data value.

* Accessor makes type adaption to users binding if possible.

* * @param binding * @return the value * @throws AccessorException */ Object getValue(Binding binding) throws AccessorException; /** * Read a copy of the accessor's object into the obj instance.

* Accessor makes type adaption to users binding if possible.

* * @param binding * @param obj object to read the value to * @throws AccessorException */ void getValue(Binding binding, Object obj) throws AccessorException; /** * Read value in path reference into an obj instance.

* * @param path component reference path or null to return _this_ * @param binding * @param obj * @return true if value existed, false if not * @throws AccessorException */ boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException; /** * Read value in path reference into an obj instance.

* * @param path component reference path or null to return _this_ accessor * @param binding * @param obj * @return the object or null if value doesn't exist * @throws AccessorException */ Object getValue(ChildReference path, Binding binding) throws AccessorException; /** * Set a complete new value to the data container. * Accessor makes type adaption to users binding if possible.

* * If the new value removes old map entries, array entries, optional value, or * changes union or variant value type, it will disengage any existing * sub-accessors and send {@link InvalidatedEvent}.

* * Writing the current value again may not emit an event. This is implementation * specific.

* * Write is durable, once a the method returns successfully the value has been * stored in the implmentation.

* * @param binding * @param newValue * @throws BindingException binding error * @throws UnsupportedOperationException cannot set a new value */ void setValue(Binding binding, Object newValue) throws AccessorException; /** * Set value to path reference. * * @param path component reference path or null to return _this_ * @param binding * @param obj * @return true if value existed in the accessor, false if not * @throws AccessorException */ boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException; /** * Open an accessor to a child. If one already exists, the existing is returned, * otherwise a new is created. Child accessors are often remembered with * weak reference.

* * InvalidatedEvent is thrown from the accessor if it is unlinked from the * parent hierarchy.

* * @param path component reference path or null to return _this_ accessor * @return accessor * @throws AccessorConstructionException */ T getComponent(ChildReference path) throws AccessorConstructionException; /** * Apply a change set in a single transaction operation. * * If rollback log is supplied, it is filled with reverse events. * * If the operation fails, rollback log can be applied to cancel changes. * * @param changeSet * @param rollback log to be filled with rollback events or null * @throws AccessorException failed to apply change set */ void apply(List changeSet, LinkedList rollback) throws AccessorException; /** * Get structural represtentation of the accessor presented in databoard's * type system. * * @return type description */ Datatype type(); /** * Place a listener to an accessor node. The listener will be notified for changes * in the node/node tree, depending on interest set.

* * When events are emited and in which thread processed is implementation * specific. It is also implementation specific, whether the object can be mutated * in the listener or whether it has to be done afterwards.

* * In many implementations there is a pluggable event handling strategy * EventEmitter. The default behaviour is to emit events as they are * spawned in the current thread.

* * There is a reference in each event instance that describes the path from * the accessor where listener was placed to the node to which the event * applies to.

* * Listener is attached to the object that holds the value at the time at * the of the adding. * For example, If a listener is attached to an array of element at index 3, * and a new value is inserted at position 2, the listener still monitors the same * container, which is now at index 4. The references of incoming the events * are modified to have the new index.

* * Also, if a new value is assigned to the parent of an object that is listened, * the listener keeps on monitoring the new value at the same reference. * This doesn't apply when a new value is set to a union of different tag, * to a variant with a new type, or value is removed from Optional type. * In these two cases the listener is invalidated.

* * See {@link ChangeSet} is an implementation that collects events. * * Executor argument determines the thread where the onEvents method is * handled. null argument denotes current thread. * * @param listener * @param interestSet * @param pathPrefix path to the accessor or null. This is used in the events the accessor produces * @param executor * @see ChangeSet collects events */ void addListener(Listener listener, InterestSet interestSet, ChildReference pathPrefix, Executor executor) throws AccessorException; /** * Remove a listener. If the listener is added multiple times, the last * one added is removed. * * @param listener * @throws AccessorException */ void removeListener(Listener listener) throws AccessorException; /** * Accessor listener. * * @author Toni Kalajainen */ public static interface Listener { /** * Notify the listener on a new event * * @param events */ void onEvents(Collection events); } }