]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/java/JavaObject.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / java / JavaObject.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
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
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.accessor.java;
13
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;
20
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;
52
53 /**
54  * Accessor to a Java Object.
55  * <p>
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()}.
59  * <p>
60  * If a lock is not provided, operations cannot be performed simulataneously 
61  * in multiple-threads. 
62  *
63  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
64  */
65 public abstract class JavaObject implements Accessor, ParametrisedAccessor {
66
67         /**  
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.  
71          */
72         Accessor parent;        
73         /** The Java object */
74         Object object;
75         /** Binding */
76         Binding binding;
77         /** Listeners */
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;
83
84         /**
85          * Create a new accessor to a Java Object.<p>
86          * 
87          * Read and write locks may optionally be provided for locking mechanisms.
88          * ReadWriteLock can be provied or a signle MutualExclusion lock. 
89          * 
90          * @param parent parent, or <tt>null</tt>
91          * @param binding
92          * @param initialValue the java object
93          * @param params accessor params 
94          */
95         public JavaObject(Accessor parent, Binding binding, Object initialValue, AccessorParams params) {
96                 if (binding==null) throw new IllegalArgumentException("null arg");
97                 this.parent = parent;
98                 this.binding = binding;
99                 this.object = initialValue;
100                 this.params = params;
101         }
102         
103         /**
104          * Get the Java Object
105          * 
106          * @return Object
107          */
108         public Object getObject() {
109                 return object;
110         }
111
112         public Binding getBinding() {
113                 return binding;
114         }       
115         
116         @Override
117         public AccessorParams getParams() {
118                 return params;
119         }
120         
121         public Datatype type() {
122                 return binding.type();
123         }
124         
125         /**
126          * Get lock if available. 
127          * 
128          * @return lock or <tt>null</tt>
129          */
130         public Lock getReadLock() {
131                 return params.readLock;
132         }
133         
134         /**
135          * Get lock if available. 
136          * 
137          * @return lock or <tt>null</tt>
138          */
139         public Lock getWriteLock() {
140                 return params.writeLock;
141         }
142         
143
144         /**
145          * Lock the lock if there is a lock.
146          */
147         protected void readLock() {
148                 if (params.readLock!=null) params.readLock.lock();
149         }
150         
151         /**
152          * Unlock the lock if one exists
153          */
154         protected void readUnlock() {
155                 if (params.readLock!=null) params.readLock.unlock();
156         }
157
158         /**
159          * Lock the lock if there is a lock.
160          */
161         protected void writeLock() {
162                 if (params.writeLock!=null) params.writeLock.lock();
163         }
164         
165         /**
166          * Unlock the lock if one exists
167          */
168         protected void writeUnlock() {
169                 if (params.writeLock!=null) params.writeLock.unlock();
170         }
171         
172         @Override
173         public Object getValue(Binding binding) throws AccessorException {
174                 readLock();
175                 try {
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);
179                         }
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);
185                 } finally {
186                         readUnlock();
187                 }
188         }
189         
190         
191         @Override
192         public void getValue(Binding binding, Object obj) throws AccessorException {
193                 readLock();
194                 try {
195                         this.binding.readFrom(this.binding, object, obj);
196                 } catch (BindingException e) {
197                         throw new AccessorException(e);
198                 } finally {
199                         readLock();
200                 }
201         }
202         
203         @Override
204         public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
205                 try {
206                         Accessor a = getComponent(path);
207                         a.getValue(binding, obj);
208                         return true;
209                 } catch (ReferenceException re) {
210                         return false;
211                 } catch (AccessorConstructionException e) {
212                         throw new AccessorException(e);
213                 }
214         }       
215         
216         public Object getValue(ChildReference path, Binding binding) throws AccessorException {
217                 try {
218                         Accessor a = getComponent(path);
219                         return a.getValue(binding);
220                 } catch (ReferenceException re) {
221                         return null;
222                 } catch (AccessorConstructionException e) {
223                         throw new AccessorException(e);
224                 }
225         }
226         
227         public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
228                 try {
229                         Accessor a = getComponent(path);
230                         a.setValue(binding, obj);
231                         return true;
232                 } catch (ReferenceException re) {
233                         return false;
234                 } catch (AccessorConstructionException e) {
235                         throw new AccessorException(e);
236                 }
237         }
238         
239         Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
240                 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
241         }       
242         
243         @Override
244         public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
245                 listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
246         }
247
248         protected ListenerEntry detachListener(Listener listener) throws AccessorException {
249                 ListenerEntry e = listeners;
250                 ListenerEntry p = null;
251                 while (e!=null) {
252                         // Found match
253                         if (e.listener == listener) {
254                                 // The match was the first entry of the linked list
255                                 if (p==null) {
256                                         listeners = e.next;
257                                         return e;
258                                 }
259                                 // Some other entry, unlink e
260                                 p.next = e.next;
261                                 return e;
262                         }
263                         p = e;
264                         e = e.next;
265                 }
266                 return null;            
267         }
268         
269         @Override
270         public void removeListener(Listener listener) throws AccessorException {
271                 detachListener(listener);
272         }
273                 
274         public static JavaObject createAccessor(Accessor parent, Binding b, Object v, AccessorParams params) throws AccessorConstructionException {
275                 return createSubAccessor(parent, b, v, params);
276         }
277         
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);
282                 }
283                 if (b instanceof ByteBinding) {
284                         return new JavaByte(parent, (ByteBinding)b, v, params);
285                 }
286                 if (b instanceof IntegerBinding) {
287                         return new JavaInteger(parent, (IntegerBinding)b, v, params);
288                 }
289                 if (b instanceof LongBinding) {
290                         return new JavaLong(parent, (LongBinding)b, v, params);
291                 }
292                 if (b instanceof FloatBinding) {
293                         return new JavaFloat(parent, (FloatBinding)b, v, params);
294                 }
295                 if (b instanceof DoubleBinding) {
296                         return new JavaDouble(parent, (DoubleBinding)b, v, params);
297                 }
298                 if (b instanceof StringBinding) {
299                         return new JavaString(parent, (StringBinding)b, v, params);
300                 }
301                 if (b instanceof UnionBinding) {
302                         return new JavaUnion(parent, (UnionBinding)b, v, params);
303                 }
304                 if (b instanceof OptionalBinding) {
305                         return new JavaOptional(parent, (OptionalBinding)b, v, params);
306                 }
307                 if (b instanceof VariantBinding) {
308                         return new JavaVariant(parent, (VariantBinding)b, v, params);
309                 }
310                 if (b instanceof ArrayBinding) {
311                         return new JavaArray(parent, (ArrayBinding)b, v, params);
312                 }
313                 if (b instanceof MapBinding) {
314                         return new JavaMap(parent, (MapBinding)b, v, params);
315                 }
316                 if (b instanceof RecordBinding) {
317                         return new JavaRecord(parent, (RecordBinding)b, v, params);
318                 }
319                 throw new AccessorConstructionException("Can not create accessor to "+b.type());
320         }
321         
322         /**
323          * Send notification that this accessor has been detached from the parent
324          */
325         void invalidatedNotification() {
326                 ListenerEntry le = listeners;
327                 while (le!=null) {                      
328                         InterestSet is = le.getInterestSet();
329                         if (is.inNotifications()) {
330                                 InvalidatedEvent e = new InvalidatedEvent();
331                                 emitEvent(le, e);
332                         }
333                         le = le.next;
334                 }               
335         }
336
337         /**
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.
341          * 
342          * @param cs
343          * @param makeRollback
344          * @return rollback-event
345          * @throws AccessorException
346          */
347         abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
348         
349         @Override
350         public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
351                 writeLock();
352                 try {
353                         boolean makeRollback = rollback != null;
354                         for (Event e : cs) {
355                                 // Accessor
356                                 JavaObject a = e.reference == null ? this : (JavaObject) getComponent(e.reference);
357                                 // Apply changes                                
358                                 Event rbe = a.applyLocal(e, makeRollback);
359                                 if (makeRollback) {
360                                         rbe.reference = e.reference;
361                                         rollback.addFirst( rbe );
362                                 }
363                         }
364                 } catch (AccessorConstructionException ae) {
365                         throw new AccessorException(ae);
366                 } finally {
367                         writeUnlock();
368                 }               
369         }
370         
371         @Override
372         public String toString() {
373                 try {
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()+")";
379                 }
380         }
381         
382         /**
383          * The Java Object was changed by means other than Accessor.
384          * ValueAssigned event is emited to listeners.   
385          */
386         public void notifyValueChanged() {
387                 // Notify
388                 ListenerEntry le = listeners;
389                 while (le!=null) {
390                         ByteInterestSet is = le.getInterestSet();
391                         if (is.inNotifications()) {
392                                 Event e = new ValueAssigned( binding, object );
393                                 emitEvent(le, e);
394                         }
395                         le = le.next;
396                 }               
397         }       
398
399         protected void emitEvent(ListenerEntry le, Event e) {           
400                 e.reference = ChildReference.concatenate(le.path, e.reference);
401                 le.emitEvent(e);
402         }       
403
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);
408         }       
409         
410 }
411