1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.accessor.java;
14 import java.io.IOException;
15 import java.util.Collection;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.concurrent.Executor;
19 import java.util.concurrent.locks.Lock;
21 import org.simantics.databoard.accessor.Accessor;
22 import org.simantics.databoard.accessor.ParametrisedAccessor;
23 import org.simantics.databoard.accessor.error.AccessorConstructionException;
24 import org.simantics.databoard.accessor.error.AccessorException;
25 import org.simantics.databoard.accessor.error.ReferenceException;
26 import org.simantics.databoard.accessor.event.Event;
27 import org.simantics.databoard.accessor.event.InvalidatedEvent;
28 import org.simantics.databoard.accessor.event.ValueAssigned;
29 import org.simantics.databoard.accessor.impl.AccessorParams;
30 import org.simantics.databoard.accessor.impl.ListenerEntry;
31 import org.simantics.databoard.accessor.interestset.ByteInterestSet;
32 import org.simantics.databoard.accessor.interestset.InterestSet;
33 import org.simantics.databoard.accessor.reference.ChildReference;
34 import org.simantics.databoard.adapter.AdaptException;
35 import org.simantics.databoard.adapter.AdapterConstructionException;
36 import org.simantics.databoard.binding.ArrayBinding;
37 import org.simantics.databoard.binding.Binding;
38 import org.simantics.databoard.binding.BooleanBinding;
39 import org.simantics.databoard.binding.ByteBinding;
40 import org.simantics.databoard.binding.DoubleBinding;
41 import org.simantics.databoard.binding.FloatBinding;
42 import org.simantics.databoard.binding.IntegerBinding;
43 import org.simantics.databoard.binding.LongBinding;
44 import org.simantics.databoard.binding.MapBinding;
45 import org.simantics.databoard.binding.OptionalBinding;
46 import org.simantics.databoard.binding.RecordBinding;
47 import org.simantics.databoard.binding.StringBinding;
48 import org.simantics.databoard.binding.UnionBinding;
49 import org.simantics.databoard.binding.VariantBinding;
50 import org.simantics.databoard.binding.error.BindingException;
51 import org.simantics.databoard.type.Datatype;
54 * Accessor to a Java Object.
56 * The monitoring contract forbids modifications to the object outside this
57 * accessor object. If you do modifications to the value using other mechanisms,
58 * you must notify the listeners of the accessor with {@link #notifyValueChanged()}.
60 * If a lock is not provided, operations cannot be performed simulataneously
61 * in multiple-threads.
63 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
65 public abstract class JavaObject implements Accessor, ParametrisedAccessor {
68 * Strong Reference to the parent. It is needed to keep the parent path
69 * alive as long as its children. This is ensure the children are not
70 * instantiated more than once.
73 /** The Java object */
78 ListenerEntry listeners = null;
79 /** Key in parent, index for fields/arrays, key for maps, */
80 Object keyInParent = null;
81 /** Accessor params, propagated to children */
82 AccessorParams params;
85 * Create a new accessor to a Java Object.<p>
87 * Read and write locks may optionally be provided for locking mechanisms.
88 * ReadWriteLock can be provied or a signle MutualExclusion lock.
90 * @param parent parent, or <tt>null</tt>
92 * @param initialValue the java object
93 * @param params accessor params
95 public JavaObject(Accessor parent, Binding binding, Object initialValue, AccessorParams params) {
96 if (binding==null) throw new IllegalArgumentException("null arg");
98 this.binding = binding;
99 this.object = initialValue;
100 this.params = params;
104 * Get the Java Object
108 public Object getObject() {
112 public Binding getBinding() {
117 public AccessorParams getParams() {
121 public Datatype type() {
122 return binding.type();
126 * Get lock if available.
128 * @return lock or <tt>null</tt>
130 public Lock getReadLock() {
131 return params.readLock;
135 * Get lock if available.
137 * @return lock or <tt>null</tt>
139 public Lock getWriteLock() {
140 return params.writeLock;
145 * Lock the lock if there is a lock.
147 protected void readLock() {
148 if (params.readLock!=null) params.readLock.lock();
152 * Unlock the lock if one exists
154 protected void readUnlock() {
155 if (params.readLock!=null) params.readLock.unlock();
159 * Lock the lock if there is a lock.
161 protected void writeLock() {
162 if (params.writeLock!=null) params.writeLock.lock();
166 * Unlock the lock if one exists
168 protected void writeUnlock() {
169 if (params.writeLock!=null) params.writeLock.unlock();
173 public Object getValue(Binding binding) throws AccessorException {
176 // return params.adapterScheme.getAdapter(this.binding, binding, true, true).adapt(object);
177 if (binding == this.binding) {
178 return binding.isImmutable() ? object : binding.clone(object);
180 return adapt(object, this.binding, binding);
181 } catch (AdaptException e) {
182 throw new AccessorException(e);
183 } catch (AdapterConstructionException e) {
184 throw new AccessorException(e);
192 public void getValue(Binding binding, Object obj) throws AccessorException {
195 this.binding.readFrom(this.binding, object, obj);
196 } catch (BindingException e) {
197 throw new AccessorException(e);
204 public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
206 Accessor a = getComponent(path);
207 a.getValue(binding, obj);
209 } catch (ReferenceException re) {
211 } catch (AccessorConstructionException e) {
212 throw new AccessorException(e);
216 public Object getValue(ChildReference path, Binding binding) throws AccessorException {
218 Accessor a = getComponent(path);
219 return a.getValue(binding);
220 } catch (ReferenceException re) {
222 } catch (AccessorConstructionException e) {
223 throw new AccessorException(e);
227 public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
229 Accessor a = getComponent(path);
230 a.setValue(binding, obj);
232 } catch (ReferenceException re) {
234 } catch (AccessorConstructionException e) {
235 throw new AccessorException(e);
239 Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
240 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
244 public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
245 listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
248 protected ListenerEntry detachListener(Listener listener) throws AccessorException {
249 ListenerEntry e = listeners;
250 ListenerEntry p = null;
253 if (e.listener == listener) {
254 // The match was the first entry of the linked list
259 // Some other entry, unlink e
270 public void removeListener(Listener listener) throws AccessorException {
271 detachListener(listener);
274 public static JavaObject createAccessor(Accessor parent, Binding b, Object v, AccessorParams params) throws AccessorConstructionException {
275 return createSubAccessor(parent, b, v, params);
278 public static JavaObject createSubAccessor(Accessor parent, Binding b, Object v, AccessorParams params)
279 throws AccessorConstructionException {
280 if (b instanceof BooleanBinding) {
281 return new JavaBoolean(parent, (BooleanBinding)b, v, params);
283 if (b instanceof ByteBinding) {
284 return new JavaByte(parent, (ByteBinding)b, v, params);
286 if (b instanceof IntegerBinding) {
287 return new JavaInteger(parent, (IntegerBinding)b, v, params);
289 if (b instanceof LongBinding) {
290 return new JavaLong(parent, (LongBinding)b, v, params);
292 if (b instanceof FloatBinding) {
293 return new JavaFloat(parent, (FloatBinding)b, v, params);
295 if (b instanceof DoubleBinding) {
296 return new JavaDouble(parent, (DoubleBinding)b, v, params);
298 if (b instanceof StringBinding) {
299 return new JavaString(parent, (StringBinding)b, v, params);
301 if (b instanceof UnionBinding) {
302 return new JavaUnion(parent, (UnionBinding)b, v, params);
304 if (b instanceof OptionalBinding) {
305 return new JavaOptional(parent, (OptionalBinding)b, v, params);
307 if (b instanceof VariantBinding) {
308 return new JavaVariant(parent, (VariantBinding)b, v, params);
310 if (b instanceof ArrayBinding) {
311 return new JavaArray(parent, (ArrayBinding)b, v, params);
313 if (b instanceof MapBinding) {
314 return new JavaMap(parent, (MapBinding)b, v, params);
316 if (b instanceof RecordBinding) {
317 return new JavaRecord(parent, (RecordBinding)b, v, params);
319 throw new AccessorConstructionException("Can not create accessor to "+b.type());
323 * Send notification that this accessor has been detached from the parent
325 void invalidatedNotification() {
326 ListenerEntry le = listeners;
328 InterestSet is = le.getInterestSet();
329 if (is.inNotifications()) {
330 InvalidatedEvent e = new InvalidatedEvent();
338 * Apply a change set that has events for the particular accessor.
339 * There are no sub-accessor in the path of the event.
340 * This is called within lock.
343 * @param makeRollback
344 * @return rollback-event
345 * @throws AccessorException
347 abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
350 public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
353 boolean makeRollback = rollback != null;
356 JavaObject a = e.reference == null ? this : (JavaObject) getComponent(e.reference);
358 Event rbe = a.applyLocal(e, makeRollback);
360 rbe.reference = e.reference;
361 rollback.addFirst( rbe );
364 } catch (AccessorConstructionException ae) {
365 throw new AccessorException(ae);
372 public String toString() {
374 return "Java("+binding.printValueDefinition(object, true)+")";
375 } catch (IOException e) {
376 return "Java(error="+e.getMessage()+")";
377 } catch (BindingException e) {
378 return "Java(error="+e.getMessage()+")";
383 * The Java Object was changed by means other than Accessor.
384 * ValueAssigned event is emited to listeners.
386 public void notifyValueChanged() {
388 ListenerEntry le = listeners;
390 ByteInterestSet is = le.getInterestSet();
391 if (is.inNotifications()) {
392 Event e = new ValueAssigned( binding, object );
399 protected void emitEvent(ListenerEntry le, Event e) {
400 e.reference = ChildReference.concatenate(le.path, e.reference);
404 protected void emitEvents(ListenerEntry le, Collection<Event> events) {
405 for (Event e : events)
406 e.reference = ChildReference.concatenate(le.path, e.reference);
407 le.emitEvents(events);