]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/java/JavaObject.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / java / JavaObject.java
1 /*******************************************************************************\r
2  *  Copyright (c) 2010 Association for Decentralized Information Management in\r
3  *  Industry THTH ry.\r
4  *  All rights reserved. This program and the accompanying materials\r
5  *  are made available under the terms of the Eclipse Public License v1.0\r
6  *  which accompanies this distribution, and is available at\r
7  *  http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  *  Contributors:\r
10  *      VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.accessor.java;
13
14 import java.io.IOException;\r
15 import java.util.Collection;\r
16 import java.util.LinkedList;\r
17 import java.util.List;\r
18 import java.util.concurrent.Executor;\r
19 import java.util.concurrent.locks.Lock;\r
20 \r
21 import org.simantics.databoard.accessor.Accessor;\r
22 import org.simantics.databoard.accessor.ParametrisedAccessor;\r
23 import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
24 import org.simantics.databoard.accessor.error.AccessorException;\r
25 import org.simantics.databoard.accessor.error.ReferenceException;\r
26 import org.simantics.databoard.accessor.event.Event;\r
27 import org.simantics.databoard.accessor.event.InvalidatedEvent;\r
28 import org.simantics.databoard.accessor.event.ValueAssigned;\r
29 import org.simantics.databoard.accessor.impl.AccessorParams;\r
30 import org.simantics.databoard.accessor.impl.ListenerEntry;\r
31 import org.simantics.databoard.accessor.interestset.ByteInterestSet;\r
32 import org.simantics.databoard.accessor.interestset.InterestSet;\r
33 import org.simantics.databoard.accessor.reference.ChildReference;\r
34 import org.simantics.databoard.adapter.AdaptException;\r
35 import org.simantics.databoard.adapter.AdapterConstructionException;\r
36 import org.simantics.databoard.binding.ArrayBinding;\r
37 import org.simantics.databoard.binding.Binding;\r
38 import org.simantics.databoard.binding.BooleanBinding;\r
39 import org.simantics.databoard.binding.ByteBinding;\r
40 import org.simantics.databoard.binding.DoubleBinding;\r
41 import org.simantics.databoard.binding.FloatBinding;\r
42 import org.simantics.databoard.binding.IntegerBinding;\r
43 import org.simantics.databoard.binding.LongBinding;\r
44 import org.simantics.databoard.binding.MapBinding;\r
45 import org.simantics.databoard.binding.OptionalBinding;\r
46 import org.simantics.databoard.binding.RecordBinding;\r
47 import org.simantics.databoard.binding.StringBinding;\r
48 import org.simantics.databoard.binding.UnionBinding;\r
49 import org.simantics.databoard.binding.VariantBinding;\r
50 import org.simantics.databoard.binding.error.BindingException;\r
51 import org.simantics.databoard.type.Datatype;\r
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>\r
60  * If a lock is not provided, operations cannot be performed simulataneously \r
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;\r
79         /** Key in parent, index for fields/arrays, key for maps, */\r
80         Object keyInParent = null;\r
81         /** Accessor params, propagated to children */\r
82         AccessorParams params;
83 \r
84         /**\r
85          * Create a new accessor to a Java Object.<p>\r
86          * \r
87          * Read and write locks may optionally be provided for locking mechanisms.\r
88          * ReadWriteLock can be provied or a signle MutualExclusion lock. \r
89          * \r
90          * @param parent parent, or <tt>null</tt>\r
91          * @param binding\r
92          * @param initialValue the java object\r
93          * @param params accessor params \r
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;\r
100                 this.params = params;\r
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         \r
116         @Override\r
117         public AccessorParams getParams() {\r
118                 return params;\r
119         }\r
120         
121         public Datatype type() {
122                 return binding.type();
123         }\r
124         \r
125         /**\r
126          * Get lock if available. \r
127          * \r
128          * @return lock or <tt>null</tt>\r
129          */\r
130         public Lock getReadLock() {\r
131                 return params.readLock;\r
132         }\r
133         \r
134         /**\r
135          * Get lock if available. \r
136          * \r
137          * @return lock or <tt>null</tt>\r
138          */\r
139         public Lock getWriteLock() {\r
140                 return params.writeLock;\r
141         }\r
142         
143 \r
144         /**\r
145          * Lock the lock if there is a lock.\r
146          */\r
147         protected void readLock() {\r
148                 if (params.readLock!=null) params.readLock.lock();\r
149         }\r
150         \r
151         /**\r
152          * Unlock the lock if one exists\r
153          */\r
154         protected void readUnlock() {\r
155                 if (params.readLock!=null) params.readLock.unlock();\r
156         }\r
157 \r
158         /**\r
159          * Lock the lock if there is a lock.\r
160          */\r
161         protected void writeLock() {\r
162                 if (params.writeLock!=null) params.writeLock.lock();\r
163         }\r
164         \r
165         /**\r
166          * Unlock the lock if one exists\r
167          */\r
168         protected void writeUnlock() {\r
169                 if (params.writeLock!=null) params.writeLock.unlock();\r
170         }\r
171         
172         @Override
173         public Object getValue(Binding binding) throws AccessorException {\r
174                 readLock();
175                 try {\r
176 //                      return params.adapterScheme.getAdapter(this.binding, binding, true, true).adapt(object);\r
177                         if (binding == this.binding) {\r
178                                 return binding.isImmutable() ? object : binding.clone(object);\r
179                         }\r
180                         return adapt(object, this.binding, binding);    
181                 } catch (AdaptException e) {
182                         throw new AccessorException(e);
183                 } catch (AdapterConstructionException e) {\r
184                         throw new AccessorException(e);\r
185                 } finally {\r
186                         readUnlock();\r
187                 }
188         }\r
189         \r
190         \r
191         @Override\r
192         public void getValue(Binding binding, Object obj) throws AccessorException {\r
193                 readLock();\r
194                 try {\r
195                         this.binding.readFrom(this.binding, object, obj);\r
196                 } catch (BindingException e) {\r
197                         throw new AccessorException(e);\r
198                 } finally {\r
199                         readLock();\r
200                 }\r
201         }\r
202         \r
203         @Override\r
204         public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
205                 try {\r
206                         Accessor a = getComponent(path);\r
207                         a.getValue(binding, obj);\r
208                         return true;\r
209                 } catch (ReferenceException re) {\r
210                         return false;\r
211                 } catch (AccessorConstructionException e) {\r
212                         throw new AccessorException(e);\r
213                 }\r
214         }       \r
215         \r
216         public Object getValue(ChildReference path, Binding binding) throws AccessorException {\r
217                 try {\r
218                         Accessor a = getComponent(path);\r
219                         return a.getValue(binding);\r
220                 } catch (ReferenceException re) {\r
221                         return null;\r
222                 } catch (AccessorConstructionException e) {\r
223                         throw new AccessorException(e);\r
224                 }\r
225         }\r
226         \r
227         public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
228                 try {\r
229                         Accessor a = getComponent(path);\r
230                         a.setValue(binding, obj);\r
231                         return true;\r
232                 } catch (ReferenceException re) {\r
233                         return false;\r
234                 } catch (AccessorConstructionException e) {\r
235                         throw new AccessorException(e);\r
236                 }\r
237         }\r
238         \r
239         Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {\r
240                 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);\r
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                 \r
274         public static JavaObject createAccessor(Accessor parent, Binding b, Object v, AccessorParams params) throws AccessorConstructionException {\r
275                 return createSubAccessor(parent, b, v, params);\r
276         }\r
277         \r
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) {\r
296                         return new JavaDouble(parent, (DoubleBinding)b, v, params);\r
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.\r
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 {\r
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 {\r
367                         writeUnlock();\r
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         }\r
381         \r
382         /**\r
383          * The Java Object was changed by means other than Accessor.\r
384          * ValueAssigned event is emited to listeners.   \r
385          */\r
386         public void notifyValueChanged() {\r
387                 // Notify\r
388                 ListenerEntry le = listeners;\r
389                 while (le!=null) {\r
390                         ByteInterestSet is = le.getInterestSet();\r
391                         if (is.inNotifications()) {\r
392                                 Event e = new ValueAssigned( binding, object );\r
393                                 emitEvent(le, e);\r
394                         }\r
395                         le = le.next;\r
396                 }               \r
397         }       \r
398 \r
399         protected void emitEvent(ListenerEntry le, Event e) {           \r
400                 e.reference = ChildReference.concatenate(le.path, e.reference);\r
401                 le.emitEvent(e);\r
402         }       \r
403 \r
404         protected void emitEvents(ListenerEntry le, Collection<Event> events) {\r
405                 for (Event e : events)\r
406                         e.reference = ChildReference.concatenate(le.path, e.reference);\r
407                 le.emitEvents(events);\r
408         }       \r
409         
410 }
411