]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryUnion.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryUnion.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.binary;
13
14 import gnu.trove.map.hash.TObjectIntHashMap;
15
16 import java.io.IOException;
17 import java.lang.ref.SoftReference;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Executor;
21
22 import org.simantics.databoard.accessor.Accessor;
23 import org.simantics.databoard.accessor.UnionAccessor;
24 import org.simantics.databoard.accessor.error.AccessorConstructionException;
25 import org.simantics.databoard.accessor.error.AccessorException;
26 import org.simantics.databoard.accessor.error.ReferenceException;
27 import org.simantics.databoard.accessor.event.Event;
28 import org.simantics.databoard.accessor.event.UnionValueAssigned;
29 import org.simantics.databoard.accessor.event.ValueAssigned;
30 import org.simantics.databoard.accessor.file.FileUnionAccessor;
31 import org.simantics.databoard.accessor.impl.AccessorParams;
32 import org.simantics.databoard.accessor.impl.ListenerEntry;
33 import org.simantics.databoard.accessor.interestset.InterestSet;
34 import org.simantics.databoard.accessor.interestset.UnionInterestSet;
35 import org.simantics.databoard.accessor.reference.ChildReference;
36 import org.simantics.databoard.accessor.reference.ComponentReference;
37 import org.simantics.databoard.accessor.reference.IndexReference;
38 import org.simantics.databoard.accessor.reference.LabelReference;
39 import org.simantics.databoard.accessor.reference.NameReference;
40 import org.simantics.databoard.adapter.AdaptException;
41 import org.simantics.databoard.binding.Binding;
42 import org.simantics.databoard.binding.UnionBinding;
43 import org.simantics.databoard.binding.error.BindingConstructionException;
44 import org.simantics.databoard.binding.error.BindingException;
45 import org.simantics.databoard.binding.mutable.MutableVariant;
46 import org.simantics.databoard.serialization.Serializer;
47 import org.simantics.databoard.serialization.SerializerConstructionException;
48 import org.simantics.databoard.type.Datatype;
49 import org.simantics.databoard.type.UnionType;
50 import org.simantics.databoard.util.binary.Blob;
51 import org.simantics.databoard.util.binary.Endian;
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 {
74                 assert b.isOpen();
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 {
85                         writeUnlock();
86                 }
87         }
88         
89         @SuppressWarnings("unchecked")
90         @Override
91         public <T extends Accessor> T getComponentAccessor()
92                         throws AccessorConstructionException {
93                 assert b.isOpen();
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 {
132                         readUnlock();
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 {
150                 assert b.isOpen();
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) {
166                         throw new AccessorException(e);
167                 } finally {
168                         readUnlock();
169                 }
170         }
171
172         @Override
173         public int getTag() throws AccessorException {
174                 assert b.isOpen();
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 {
182                         readUnlock();
183                 }
184         }
185
186         @Override
187         public void setComponentValue(int tag, Binding componentBinding,
188                         Object componentValue) throws AccessorException {
189                 assert b.isOpen();
190                 writeLock();
191                 try {
192                         setComponentValueNoflush(tag, componentBinding, componentValue);
193                         flush();
194                 } finally {
195                         writeUnlock();
196                 }
197         }
198
199         @SuppressWarnings("unchecked")
200         @Override
201         public <T extends Accessor> T getComponent(ChildReference reference)
202                         throws AccessorConstructionException {
203                 assert b.isOpen();
204                 readLock();
205                 try {
206                         if (reference==null) return (T) this;
207                         
208                         if (reference instanceof LabelReference) {
209                                 LabelReference lr = (LabelReference) reference;
210                                 Integer tag = type().getComponentIndex( lr.label );
211                                 
212                                 if (tag==null && lr.label.equals("uv")) {
213                                         Accessor result = getComponentAccessor();
214                                         if (reference.getChildReference() != null)
215                                                 result = result.getComponent(reference.getChildReference());
216                                         return (T) result;
217                                 } else if (tag==null) {
218                                         throw new ReferenceException("Tag \""+lr.label+"\" not found");
219                                 }
220                                 
221                                 if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");
222                                 Accessor result = getComponentAccessor();
223                                 if (reference.getChildReference() != null)
224                                         result = result.getComponent(reference.getChildReference());
225                                 return (T) result;                              
226                         }
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                         } 
234                         
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                         } 
244                         
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                         }
255                         
256                         throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType");
257                 } catch (AccessorException ae) {
258                         throw new AccessorConstructionException(ae);
259                 } finally {
260                         readUnlock();
261                 }
262         }
263
264         @Override
265         public void setComponentValueNoflush(int tag, Binding cb,
266                         Object cv) throws AccessorException {
267                 assert b.isOpen();
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) {
332                         throw new AccessorException(e);
333                 } finally {
334                         writeUnlock();
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;
375                 if (makeRollback) {
376                         try {
377                                 UnionType ut = type();
378                                 int tag = getTag();
379                                 Datatype ct = ut.getComponent(tag).type;
380                                 Binding cb = params.bindingScheme.getBinding(ct);
381                                 Object cv = getComponentValue(cb);
382                                 MutableVariant v = new MutableVariant(cb, cv);
383                                 rollback = new UnionValueAssigned(tag, v);
384                         } catch (BindingConstructionException e2) {
385                                 throw new AccessorException( e2 );
386                         }
387                 }
388                 
389                 if (e instanceof ValueAssigned) {
390                         ValueAssigned va = (ValueAssigned) e;
391                         setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
392                         return rollback;
393                 } else  
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;
402                 } else {
403                         throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type");
404                 }
405         }
406         
407 }
408