]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/ArrayBinding.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / ArrayBinding.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.binding;
13
14 import java.util.Collection;\r
15 import java.util.IdentityHashMap;\r
16 import java.util.Iterator;\r
17 import java.util.Set;\r
18 \r
19 import org.simantics.databoard.Bindings;\r
20 import org.simantics.databoard.accessor.reference.ChildReference;\r
21 import org.simantics.databoard.accessor.reference.IndexReference;\r
22 import org.simantics.databoard.accessor.reference.LabelReference;\r
23 import org.simantics.databoard.adapter.AdaptException;\r
24 import org.simantics.databoard.adapter.Adapter;\r
25 import org.simantics.databoard.adapter.AdapterConstructionException;\r
26 import org.simantics.databoard.binding.error.BindingException;\r
27 import org.simantics.databoard.binding.error.RuntimeBindingException;\r
28 import org.simantics.databoard.binding.impl.ArrayListBinding;\r
29 import org.simantics.databoard.binding.impl.BindingPrintContext;\r
30 import org.simantics.databoard.binding.impl.ByteArrayBinding;\r
31 import org.simantics.databoard.binding.impl.DoubleArrayBinding;\r
32 import org.simantics.databoard.binding.impl.FloatArrayBinding;\r
33 import org.simantics.databoard.binding.impl.IntArrayBinding;\r
34 import org.simantics.databoard.binding.impl.LinkedListBinding;\r
35 import org.simantics.databoard.binding.impl.LongArrayBinding;\r
36 import org.simantics.databoard.binding.impl.ObjectArrayBinding;\r
37 import org.simantics.databoard.type.ArrayType;\r
38 import org.simantics.databoard.util.IdentityPair;\r
39 import org.simantics.databoard.util.Range;\r
40
41 /**
42  * This is a binding of Array type and a Java Object.
43  * 
44  * @see ArrayType\r
45  * @see ArrayListBinding for Binding of java.util.ArrayList<?>\r
46  * @see LinkedListBinding for Binding of java.util.LinkedList<?>\r
47  * @see ObjectArrayBinding for Binding of Object[]\r
48  * @see IntArrayBinding for primitive array int[] binding\r
49  * @see ByteArrayBinding for primitive array byte[] binding\r
50  * @see LongArrayBinding for primitive array long[] binding\r
51  * @see DoubleArrayBinding for primitive array double[] binding\r
52  * @see FloatArrayBinding for primitive array float[] binding \r
53  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
54  */
55 public abstract class ArrayBinding extends Binding {
56
57     public Binding componentBinding;
58     
59         public ArrayBinding(ArrayType type, Binding componentBinding) {
60 //              if (!type.getComponentType().equals(componentBinding.type()))
61 //                      throw new IllegalArgumentException("Binding for "+type.componentType+" expected, got "+componentBinding.type());
62         this.componentBinding = componentBinding;
63         this.type = type;
64     }
65         \r
66         @Override\r
67         public Binding getComponentBinding(ChildReference path) {\r
68                 if (path==null) return this;            \r
69                 if (path instanceof org.simantics.databoard.accessor.reference.ComponentReference) {\r
70                 return componentBinding.getComponentBinding(path.childReference);\r
71                 }\r
72                 if (path instanceof IndexReference) {\r
73                         IndexReference ir = (IndexReference) path;                      \r
74                         if (ir.index == 0) { \r
75                         return componentBinding.getComponentBinding(path.childReference);\r
76                         }\r
77                 }\r
78                 if (path instanceof LabelReference) {\r
79                         LabelReference lr = (LabelReference) path;\r
80                         if (lr.label.equals("v")) { \r
81                         return componentBinding.getComponentBinding(path.childReference);\r
82                         }\r
83                 }\r
84                 throw new IllegalArgumentException();\r
85         }\r
86         
87         /**
88          * Returns true if array length can be modified. 
89          * 
90          * @return true if array length can be modified, false if not
91          */
92         @Override
93         public boolean isImmutable() {
94                 return super.isImmutable();
95         }\r
96         \r
97         /**\r
98          * Return true if the array's size can be adjusted and false if not\r
99          * \r
100          * @return true if array is resizable\r
101          */\r
102         public abstract boolean isResizable();\r
103         
104         @Override
105         public ArrayType type() {
106                 return (ArrayType) type;
107         }
108
109     public Binding getComponentBinding() {
110         return componentBinding;
111     }
112     
113     /**
114      * Create a new empty array
115      * @return array object
116      */
117     public abstract Object create();
118         
119     /**
120      * Create a new array with initial values copied or referred from a collection. 
121      * 
122      * @param collection
123      * @return array object
124      * @throws BindingException
125      */
126         public Object create(Collection<Object> collection) throws BindingException {
127                 return create(collection.size(), collection.iterator());
128         }
129     
130     /**
131      * Create new array instance with initial values possibly borrowed from an interator.
132      * <p>
133      * The implementation iterate the iterator before returning.
134      * 
135      * @param length array length
136      * @param values value iterator 
137      * @return new instance
138      */
139         public abstract Object create(int length, Iterator<Object> values)
140         throws BindingException;\r
141         \r
142         /**\r
143          * \r
144          * @param length\r
145          * @return array\r
146          * @throws BindingException\r
147          */\r
148         public Object create(int length) throws BindingException\r
149         {\r
150                 try {\r
151                         return create(length, new Iterator<Object>() {\r
152                                 @Override\r
153                                 public boolean hasNext() {\r
154                                         return true;\r
155                                 }\r
156         \r
157                                 @Override\r
158                                 public Object next() {\r
159                                         try {\r
160                                                 return componentBinding.createDefault();\r
161                                         } catch (BindingException e) {\r
162                                                 throw new RuntimeBindingException(e);\r
163                                         }\r
164                                 }\r
165         \r
166                                 @Override\r
167                                 public void remove() {\r
168                                 }});\r
169                 } catch (RuntimeBindingException e) {\r
170                         throw e.getCause();\r
171                 }\r
172         }
173                 
174         /**
175          * Create Array with initial values possibly borrowed from an java.lang.Array
176          * 
177          * @param array
178          * @return array of ArrayType
179          * @throws BindingException
180          */
181         public abstract Object create(Object[] array)
182         throws BindingException;
183         
184         public Object createUnchecked(Object...array)
185         throws RuntimeBindingException {
186                 try {
187                         return create(array);
188                 } catch (BindingException e) {
189                         throw new RuntimeBindingException(e);
190                 }
191         }\r
192 \r
193         @Override\r
194         public void readFrom(Binding srcBinding, Object src, Object dst)\r
195                         throws BindingException {\r
196                 \r
197                 try {\r
198                 \r
199                         // Src Binding\r
200                         ArrayBinding sb = (ArrayBinding) srcBinding;\r
201                         // Src Component Binding\r
202                         Binding scb = sb.getComponentBinding();\r
203                                 \r
204                         // Dst Component Binding\r
205                         Binding dcb = getComponentBinding();\r
206                                 \r
207                         int newLen = sb.size(src);\r
208                         int oldLen = size(dst);\r
209                         boolean cbImmutable = dcb.isImmutable();\r
210                         Adapter cloner = cbImmutable ? Bindings.adapterFactory.getAdapter(scb, dcb, false, true) : null;\r
211                                 \r
212                         // Set elements\r
213                         for (int i=0; i<newLen; i++) {\r
214                                 if (cbImmutable) {\r
215                                         // Create new instance\r
216                                         Object sc = sb.get(src, i);\r
217                                         Object dc = cloner.adapt(sc);\r
218                                         if (i<oldLen) set(dst, i, dc); else add(dst, dc); \r
219                                 } else \r
220                                 if (i<oldLen) {\r
221                                         // Read over old instance\r
222                                         Object dc = get(dst, i);\r
223                                         Object sc = sb.get(src, i);\r
224                                         dc = dcb.readFromTry(scb, sc, dc);\r
225                                         // Safety-writeback - clone/reference nature of get is unknown\r
226                                         set(dst, i, dc);\r
227                                 } else {\r
228                                         // New instance\r
229                                         Object sc = sb.get(src, i);\r
230                                         Object dc = dcb.createDefault();\r
231                                         dc = dcb.readFromTry(scb, sc, dc);\r
232                                         add(dst, dc);\r
233                                 }\r
234                         }\r
235                                 \r
236                         // Remove excess elements\r
237                         if (oldLen>newLen) {\r
238                                 setSize(dst, newLen);\r
239                         }\r
240                         \r
241                         if (!Bindings.equals(srcBinding, src, this, dst))\r
242                                 throw new BindingException("internal error");\r
243 \r
244                 } catch (AdaptException e) {\r
245                         throw new BindingException(e);\r
246                 } catch (AdapterConstructionException e) {\r
247                         throw new BindingException(e);\r
248                 }\r
249                 \r
250         }       \r
251 \r
252         
253         public void add(Object array, Object element) throws BindingException 
254         {
255                 int size = size(array);
256                 add(array, size, element);
257         }
258         public abstract void add(Object array, int index, Object element) throws BindingException, IndexOutOfBoundsException;
259         public void remove(Object array, int index) throws BindingException, IndexOutOfBoundsException
260         {
261                 remove(array, index, 1);
262         }
263         public abstract void remove(Object array, int index, int count) throws BindingException, IndexOutOfBoundsException;
264         public abstract Object get(Object array, int index) throws BindingException, IndexOutOfBoundsException;         
265         public abstract void getAll(Object array, Object[] result) throws BindingException;
266         public abstract void set(Object array, int index, Object value) throws BindingException;\r
267         public abstract void setSize(Object array, int newSize) throws BindingException;
268         
269         public abstract int size(Object array) throws BindingException;
270         
271         @Override
272         public void accept(Visitor1 v, Object obj) {
273             v.visit(this, obj);        
274         }
275
276         @Override
277         public <T> T accept(Visitor<T> v) {
278             return v.visit(this);
279         }
280         
281     /**
282      * Assert the instance is valid and follows restrictions set in data type.
283      * Assertions:
284      *   1. The Length of the array meets ArrayType range
285      *   2. Recursive assertion of each element
286      * 
287      * @param obj the instance
288      * @throws BindingException on invalid instance
289      */
290     public void assertInstaceIsValid(Object obj, Set<Object> validInstances)
291     throws BindingException 
292     {
293         ArrayType type = type();
294         // Assert The Length of the array meets ArrayType range
295         int length = size(obj);
296         Range range = type.getLength();
297         if (range!=null && !range.contains(length)) {
298                 throw new BindingException("Array length (="+length+")out of bounds. "+range+" was expected.");
299         }
300         // Recursive assertion of each element
301         for (int i=0; i<length; i++)
302         {
303                 Object component = get(obj, i);
304                 componentBinding.assertInstaceIsValid(component, validInstances);
305         }
306     }
307     
308     @Override
309     public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
310         int result = 1;
311         int len = size(value);
312         for (int i=0; i<len; i++) {
313                 Object element = get(value, i);
314                 result = 31*result + componentBinding.deepHashValue(element, hashedObjects);
315         }
316         return result;
317     }
318     
319     @Override
320     public int deepCompare(Object o1, Object o2,
321                 Set<IdentityPair<Object, Object>> compareHistory)
322                 throws BindingException {
323                 // Compare Lengths
324                 int l1 = size(o1);
325                 int l2 = size(o2);
326                 int dif = l1 - l2;
327                 if (dif!=0) return dif;
328                 // Compare elements
329                 Binding c = getComponentBinding();
330                 for (int i=0; i<l1; i++) {
331                         Object e1 = get(o1, i);
332                         Object e2 = get(o2, i);
333                         dif = c.deepCompare(e1, e2, compareHistory);
334                         if (dif!=0) return dif;
335                 }
336                 return 0;
337     }
338 \r
339     @Override\r
340         protected void toString(Object value, BindingPrintContext ctx) throws BindingException {\r
341                 Binding componentBinding = getComponentBinding();\r
342                 ctx.b.append('[');\r
343                 int size = size(value);\r
344                 boolean numberArray = componentBinding instanceof NumberBinding;\r
345                 boolean hasMany = size > 1;\r
346                 for(int i=0;i<size;++i) {\r
347                         if(i>0) {\r
348                                 ctx.b.append(", ");\r
349                                 if ( hasMany && !numberArray && !ctx.singleLine ) {\r
350                                         ctx.b.append("\n");\r
351                                 }\r
352                         }\r
353                         componentBinding.toString(get(value, i), ctx);\r
354                 }\r
355                 ctx.b.append(']');\r
356         }\r
357     \r
358     /**\r
359      * Get the number of component bindings\r
360      */\r
361     @Override\r
362     public int getComponentCount() {\r
363         return 1;\r
364     }\r
365     \r
366     @Override\r
367     public Binding getComponentBinding(int index) {\r
368         if (index!=0) throw new IllegalArgumentException();\r
369         return componentBinding;\r
370     }\r
371 \r
372     @Override\r
373     protected boolean deepEquals(Object obj,\r
374                 Set<IdentityPair<Binding, Binding>> compareHistory) {\r
375         return super.deepEquals( obj, compareHistory ) && componentBinding.equals(((ArrayBinding)obj).componentBinding, compareHistory);\r
376     }\r
377     \r
378     @Override\r
379     public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
380         return super.deepHashCode(hashedObjects) + 13 * componentBinding.hashCode(hashedObjects);\r
381     }\r
382 }