]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryUnion.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryUnion.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.binary;
13
14 import gnu.trove.map.hash.TObjectIntHashMap;\r
15 \r
16 import java.io.IOException;\r
17 import java.lang.ref.SoftReference;\r
18 import java.util.ArrayList;\r
19 import java.util.List;\r
20 import java.util.concurrent.Executor;\r
21 \r
22 import org.simantics.databoard.accessor.Accessor;\r
23 import org.simantics.databoard.accessor.UnionAccessor;\r
24 import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
25 import org.simantics.databoard.accessor.error.AccessorException;\r
26 import org.simantics.databoard.accessor.error.ReferenceException;\r
27 import org.simantics.databoard.accessor.event.Event;\r
28 import org.simantics.databoard.accessor.event.UnionValueAssigned;\r
29 import org.simantics.databoard.accessor.event.ValueAssigned;\r
30 import org.simantics.databoard.accessor.file.FileUnionAccessor;\r
31 import org.simantics.databoard.accessor.impl.AccessorParams;\r
32 import org.simantics.databoard.accessor.impl.ListenerEntry;\r
33 import org.simantics.databoard.accessor.interestset.InterestSet;\r
34 import org.simantics.databoard.accessor.interestset.UnionInterestSet;\r
35 import org.simantics.databoard.accessor.reference.ChildReference;\r
36 import org.simantics.databoard.accessor.reference.ComponentReference;\r
37 import org.simantics.databoard.accessor.reference.IndexReference;\r
38 import org.simantics.databoard.accessor.reference.LabelReference;\r
39 import org.simantics.databoard.accessor.reference.NameReference;\r
40 import org.simantics.databoard.adapter.AdaptException;\r
41 import org.simantics.databoard.binding.Binding;\r
42 import org.simantics.databoard.binding.UnionBinding;\r
43 import org.simantics.databoard.binding.error.BindingConstructionException;\r
44 import org.simantics.databoard.binding.error.BindingException;\r
45 import org.simantics.databoard.binding.mutable.MutableVariant;\r
46 import org.simantics.databoard.serialization.Serializer;\r
47 import org.simantics.databoard.serialization.SerializerConstructionException;\r
48 import org.simantics.databoard.type.Datatype;\r
49 import org.simantics.databoard.type.UnionType;\r
50 import org.simantics.databoard.util.binary.Blob;\r
51 import org.simantics.databoard.util.binary.Endian;\r
52
53 public class BinaryUnion extends BinaryObject implements UnionAccessor, FileUnionAccessor {
54
55         /** Accessor to childr */
56         SoftReference<BinaryObject> component;  
57         
58         public BinaryUnion(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
59                 super(parent, blob, type, params);
60         }
61         
62         public UnionType type() {
63                 return (UnionType) type;
64         }
65
66         @Override
67         public int count() {
68                 return type().getComponentCount();
69         }
70
71         @Override
72         public void setValueNoflush(Binding binding, Object newValue)
73                         throws AccessorException {\r
74                 assert b.isOpen();\r
75                 writeLock();
76                 try {
77                         UnionBinding ub = (UnionBinding) binding;
78                         int tag = ub.getTag(newValue);
79                         Binding cb = ub.getComponentBinding(tag);
80                         Object cv = ub.getValue(newValue);
81                         setComponentValueNoflush(tag, cb, cv);
82                 } catch (BindingException e) {
83                         throw new AccessorException(e);
84                 } finally {\r
85                         writeUnlock();\r
86                 }
87         }
88         
89         @SuppressWarnings("unchecked")
90         @Override
91         public <T extends Accessor> T getComponentAccessor()
92                         throws AccessorConstructionException {\r
93                 assert b.isOpen();\r
94                 readLock();
95                 try {
96                         // Get existing or create new
97                         BinaryObject sa = getExistingAccessor();
98
99                         if (sa==null) {
100                                 // Read Value                           
101                                 b.position(0L);
102                                 int tag = Endian.getUInt(b, count()-1);
103                                 int tagLen = (int) b.position();
104                                 Datatype ct = type().getComponent(tag).type;
105                                 
106                                 // Instantiate sub accessor. 
107                                 sa = createSubAccessor(ct, tagLen, b.length()-tagLen, params);
108                                 component = new SoftReference<BinaryObject>(sa);
109                                 
110                                 // Add listener to component, if it is in our interest set
111                                 ListenerEntry le = listeners;
112                                 while (le!=null) {                              
113                                         UnionInterestSet is = le.getInterestSet();
114                                         InterestSet cis = is.getComponentInterest(tag);
115                                         if (cis != null) {
116                                                 try {
117                                                         ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() );
118                                                         sa.addListener(le.listener, cis, childPath, le.executor);
119                                                 } catch (AccessorException e) {
120                                                         throw new AccessorConstructionException(e);
121                                                 }
122                                         }
123                                         le = le.next;
124                                 }                               
125
126                         }
127                         
128                         return (T) sa;
129                 } catch (IOException e) {
130                         throw new AccessorConstructionException( e );
131                 } finally {\r
132                         readUnlock();\r
133                 }
134         }
135         
136         /**
137          * Get existing sub-accessor
138          * 
139          * @return sub-accessor or <code>null</code>
140          */
141         BinaryObject getExistingAccessor() {
142                 SoftReference<BinaryObject> c = component;
143                 if (c==null) return null;
144                 return c.get();         
145         }       
146
147         @Override
148         public Object getComponentValue(Binding componentBinding)
149                         throws AccessorException {\r
150                 assert b.isOpen();\r
151                 readLock();
152                 try {
153                         b.position( 0L );
154                         int tag = Endian.getUInt(b, count()-1);
155                         Datatype ct = type().getComponent(tag).type; 
156                         if ( !ct.equals(componentBinding.type()) ) {
157                                 throw new AccessorException("Binding of "+ct+" expected.");
158                         }
159                         
160                         List<Object> ids = new ArrayList<Object>(0);
161                         Serializer s = params.serializerScheme.getSerializer( componentBinding );
162                         return s.deserialize(b, ids);
163                 } catch (IOException e) {
164                         throw new AccessorException(e);
165                 } catch (SerializerConstructionException e) {\r
166                         throw new AccessorException(e);\r
167                 } finally {\r
168                         readUnlock();\r
169                 }
170         }
171
172         @Override
173         public int getTag() throws AccessorException {\r
174                 assert b.isOpen();\r
175                 readLock();
176                 try {
177                         b.position(0L);
178                         return Endian.getUInt(b, count()-1);
179                 } catch (IOException e) {
180                         throw new AccessorException(e);
181                 } finally {\r
182                         readUnlock();\r
183                 }
184         }
185
186         @Override
187         public void setComponentValue(int tag, Binding componentBinding,
188                         Object componentValue) throws AccessorException {\r
189                 assert b.isOpen();\r
190                 writeLock();\r
191                 try {
192                         setComponentValueNoflush(tag, componentBinding, componentValue);
193                         flush();\r
194                 } finally {\r
195                         writeUnlock();\r
196                 }
197         }
198
199         @SuppressWarnings("unchecked")
200         @Override
201         public <T extends Accessor> T getComponent(ChildReference reference)
202                         throws AccessorConstructionException {\r
203                 assert b.isOpen();\r
204                 readLock();
205                 try {
206                         if (reference==null) return (T) this;\r
207                         \r
208                         if (reference instanceof LabelReference) {\r
209                                 LabelReference lr = (LabelReference) reference;\r
210                                 Integer tag = type().getComponentIndex( lr.label );\r
211                                 \r
212                                 if (tag==null && lr.label.equals("uv")) {\r
213                                         Accessor result = getComponentAccessor();\r
214                                         if (reference.getChildReference() != null)\r
215                                                 result = result.getComponent(reference.getChildReference());\r
216                                         return (T) result;\r
217                                 } else if (tag==null) {\r
218                                         throw new ReferenceException("Tag \""+lr.label+"\" not found");\r
219                                 }\r
220                                 \r
221                                 if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");\r
222                                 Accessor result = getComponentAccessor();\r
223                                 if (reference.getChildReference() != null)\r
224                                         result = result.getComponent(reference.getChildReference());\r
225                                 return (T) result;                              \r
226                         }\r
227                         
228                         if (reference instanceof ComponentReference) {
229                                 Accessor result = getComponentAccessor();
230                                 if (reference.getChildReference() != null)
231                                         result = result.getComponent(reference.getChildReference());
232                                 return (T) result;
233                         } \r
234                         \r
235                         if (reference instanceof IndexReference) {
236                                 IndexReference ir = (IndexReference) reference;
237                                 if (ir.index<0 || ir.index>=type().getComponentCount()) throw new ReferenceException("Tag index out of bounds");
238                                 if (ir.index != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(ir.index).name+")");
239                                 Accessor result = getComponentAccessor();
240                                 if (reference.getChildReference() != null)
241                                         result = result.getComponent(reference.getChildReference());
242                                 return (T) result;
243                         } \r
244                         \r
245                         if (reference instanceof NameReference) {
246                                 NameReference nr = (NameReference) reference;
247                                 Integer tag = type().getComponentIndex( nr.name );
248                                 if (tag==null) throw new ReferenceException("Tag \""+nr.name+"\" not found");
249                                 if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");
250                                 Accessor result = getComponentAccessor();
251                                 if (reference.getChildReference() != null)
252                                         result = result.getComponent(reference.getChildReference());
253                                 return (T) result;                              
254                         }\r
255                         
256                         throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType");
257                 } catch (AccessorException ae) {
258                         throw new AccessorConstructionException(ae);
259                 } finally {\r
260                         readUnlock();\r
261                 }
262         }
263
264         @Override
265         public void setComponentValueNoflush(int tag, Binding cb,
266                         Object cv) throws AccessorException {
267                 assert b.isOpen();\r
268                 writeLock();
269                 try {
270                         int oldTag = getTag();
271                         int newTag = tag;                       
272                         boolean hadSameTag = oldTag == newTag;
273                         
274                         // Write to component
275                         BinaryObject sa = getExistingAccessor();
276                         
277                         // Tag type changes, invalidate old accessor
278                         if (sa!=null && !hadSameTag) {
279                                 component = null;
280                                 sa.invalidatedNotification();
281                         }
282                         
283                         // Tag type remains the same
284                         if (sa!=null && hadSameTag) {
285                                 sa.setValue(cb, cv);
286                                 return;
287                         }
288                         
289                         // Write
290                         UnionType ut = type();
291                         Datatype ct = ut.getComponent(tag).type;
292                         if (!ct.equals(cb.type())) {
293                                 throw new AccessorException("Binding of "+ct+" expected.");
294                         }
295                         TObjectIntHashMap<Object> ids = new TObjectIntHashMap<Object>(0);
296                         int len = Endian.getUIntLength(count()-1);
297                         
298                         Serializer s = params.serializerScheme.getSerializer( cb );
299                         len += s.getSize(cv, ids);
300                         ids.clear();
301                         
302                         b.setLength(len);
303                         b.position(0L);
304                         Endian.putUInt(b, tag, count()-1);
305                         s.serialize(b, ids, cv);
306                         
307                         // Notify Listeners
308                         ListenerEntry le = listeners;
309                         while (le!=null) {                              
310                                 UnionInterestSet is = le.getInterestSet();
311                                 if (is.inNotificationsOf(tag)) {
312                                         MutableVariant newComponentValue = null;
313                                         if (is.inValuesOf(tag)) newComponentValue = new MutableVariant(cb, cb.isImmutable() ? cv : cb.clone(cv));
314                                         UnionValueAssigned e = new UnionValueAssigned(newTag, newComponentValue);
315                                         emitEvent(le, e);
316                                 }
317                                 
318                                 // Attach component listener
319 //                              InterestSet cis = is.getComponentInterest(newTag);
320 //                              if (cis!=null && getExistingAccessor()==null) {
321 //                                      sa = getComponentAccessor();
322 //                              }
323                                 
324                                 le = le.next;
325                         }
326                         
327                 } catch (IOException e) {
328                         throw new AccessorException(e);
329                 } catch (AdaptException e) {
330                         throw new AccessorException(e);
331                 } catch (SerializerConstructionException e) {\r
332                         throw new AccessorException(e);\r
333                 } finally {\r
334                         writeUnlock();\r
335                 }
336                 
337         }
338
339         @Override
340         public void addListener(Listener listener, InterestSet interestSet,
341                         ChildReference path, Executor executor) throws AccessorException {
342                 super.addListener(listener, interestSet, path, executor);
343                 UnionInterestSet is = (UnionInterestSet) interestSet;           
344                 if (is.componentInterests!=null) {
345                         int tag = getTag();
346                         InterestSet cis = is.componentInterests[tag];
347                         if (cis==null) return;
348                         BinaryObject sa = getExistingAccessor();
349                         if (sa==null) return;
350                         ChildReference childPath = ChildReference.concatenate(path, new IndexReference(tag) );
351                         sa.addListener(listener, cis, childPath, executor);
352                 }
353         }       
354         
355         @Override
356         public void removeListener(Listener listener) throws AccessorException {
357                 ListenerEntry e = detachListener(listener);
358                 if (e==null) return;
359                 UnionInterestSet is = (UnionInterestSet) e.interestSet;
360                 
361                 if (is.componentInterests!=null) {
362                         for (int i=0; i<is.componentInterests.length; i++) {
363                                 InterestSet cis = is.componentInterests[i];
364                                 if (cis==null) continue;
365                                 BinaryObject sa = getExistingAccessor();
366                                 if (sa==null) return;
367                                 sa.removeListener(listener);
368                         }
369                 }
370         }
371         
372         @Override
373         Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
374                 Event rollback = null;\r
375                 if (makeRollback) {\r
376                         try {\r
377                                 UnionType ut = type();\r
378                                 int tag = getTag();\r
379                                 Datatype ct = ut.getComponent(tag).type;\r
380                                 Binding cb = params.bindingScheme.getBinding(ct);\r
381                                 Object cv = getComponentValue(cb);\r
382                                 MutableVariant v = new MutableVariant(cb, cv);\r
383                                 rollback = new UnionValueAssigned(tag, v);\r
384                         } catch (BindingConstructionException e2) {\r
385                                 throw new AccessorException( e2 );\r
386                         }\r
387                 }\r
388                 \r
389                 if (e instanceof ValueAssigned) {\r
390                         ValueAssigned va = (ValueAssigned) e;\r
391                         setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());\r
392                         return rollback;\r
393                 } else  \r
394                 if (e instanceof UnionValueAssigned) {          
395                         UnionValueAssigned ua = (UnionValueAssigned) e;
396                         if (ua.tag<0 || ua.tag>=type().getComponentCount()) throw new AccessorException("Tag index ("+ua.tag+") out of bounds.");
397                         if (!ua.newValue.type().equals( type().getComponent(ua.tag).type ) )
398                                         throw new AccessorException("Cannot assign "+ua.newValue.type()+" to "+type().getComponent(ua.tag).type);
399         
400                         setComponentValueNoflush(ua.tag, ua.newValue.getBinding(), ua.newValue.getValue());             
401                         return rollback;\r
402                 } else {\r
403                         throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type");\r
404                 }
405         }
406         
407 }
408