]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/type/RecordType.java
Added addFirst/After/Before + remove SCL functions for Ordered Sets
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / type / RecordType.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.type;
13
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.StringTokenizer;
18
19 import org.simantics.databoard.accessor.error.ReferenceException;
20 import org.simantics.databoard.accessor.reference.ChildReference;
21 import org.simantics.databoard.accessor.reference.IndexReference;
22 import org.simantics.databoard.accessor.reference.LabelReference;
23 import org.simantics.databoard.accessor.reference.NameReference;
24 import org.simantics.databoard.annotations.Referable;
25 import org.simantics.databoard.binding.error.DatatypeConstructionException;
26 import org.simantics.databoard.util.IdentityPair;
27
28 public @Referable class RecordType extends Datatype {
29         
30         public static final Datatype VOID_TYPE = new RecordType(false);
31         
32         public static final String KEY_REFERABLE = "referable"; // "false"/"true"
33         
34         // Key to identifier(s) index
35         public static final String KEY_IDENTIFIER = "identifier"; // "0" "0,1" "n[,m]"
36         
37         public static final Component[] NO_COMPONENTS = new Component[0];
38         
39     Component[] components = NO_COMPONENTS;
40     
41     /** Indices to identifiers of this record. This field is filled on request */
42     private transient int[] identifiersIndices;
43     
44     /** Identifier type */
45     private transient Datatype identifierType;
46     
47     public RecordType() {       
48         setReferable(false);
49     }
50     
51     public RecordType(boolean referable, Component...components) {
52         this.components = components;
53         setReferable(referable);
54     }
55     
56     public boolean isReferable() {
57         String str = metadata.get( KEY_REFERABLE );
58         return str!=null && str.equals( Boolean.TRUE.toString() );
59     }
60     
61     public void setReferable( boolean referable ) {
62         if ( !referable ) 
63         {
64                 metadata.remove(KEY_REFERABLE);  
65         } else {
66                 metadata.put(KEY_REFERABLE, Boolean.toString(referable));
67         }
68     }
69
70     @Override
71     protected void collectSubtypes(Set<Datatype> subtypes,
72                 Set<Datatype> recursiveSubtypes) {
73         if(!subtypes.add(this)) {
74                 recursiveSubtypes.add(this);
75                 return;
76         }
77         for(Component component : components)
78                 component.type.collectSubtypes(subtypes, recursiveSubtypes);
79     }
80     
81     public void setComponents(Component[] components) {
82         this.components = components;
83     }
84    
85     public void mergeRecord(RecordType src)
86     throws DatatypeConstructionException
87     {
88         int ci = src.getComponentCount();
89         for (int i=0; i<ci; i++) {
90                 Component sc = src.components[ i ];
91                 
92                 int li = getComponentIndex2( sc.name );
93                 if ( li<0 ) {
94                         addComponent(sc.name, sc.type);
95                 } else {
96                                 Component lc = components[ li ];
97                         if ( sc.type instanceof RecordType && lc.type instanceof RecordType ) {
98                                 ((RecordType)lc.type).mergeRecord( (RecordType) sc.type );
99                         } else if ( sc.type.equals( lc.type ) ) {} 
100                         else {
101                                 throw new DatatypeConstructionException("Cannot merge field \""+sc.name+"\" "+sc.type.getClass().getName()+" and "+lc.getClass().getName());
102                         }
103                 }
104                 
105         }
106     }
107     
108     public void addComponent(String name, Datatype type)
109     {
110         Component c = new Component(name, type);
111         if (components == null) {
112                 components = new Component[] { c };
113         } else {
114                 Component[] newComponents = new Component[ components.length +1 ];
115                 System.arraycopy(components, 0, newComponents, 0, components.length);
116                 newComponents[ components.length ] = c;
117                 components = newComponents;     
118         }       
119     }
120     
121     public void clear() {
122         components = new Component[0];
123         metadata.clear();
124     }
125     
126     public void removeComponent(String name) {
127         int index = getComponentIndex2(name);
128         if (index<0) throw new IllegalArgumentException();
129                 Component[] newComponents = new Component[ components.length -1 ];
130                 if (index>0) System.arraycopy(components, 0, newComponents, 0, index);
131                 if (index<newComponents.length) System.arraycopy(components, index+1, newComponents, index, newComponents.length - index);
132                 components = newComponents;     
133                 // xxx untested
134     }
135     
136     @Override
137     public int getComponentCount() {
138         return components.length;
139     }
140     
141     @Override
142     protected boolean deepEquals(Object obj, Set<IdentityPair<Datatype, Datatype>> compareHistory) {
143                 if ( this==obj ) return true;
144                 if ( !hasEqualMetadata(obj) ) return false;
145                 if (obj instanceof RecordType == false) return false;
146                 RecordType other = (RecordType) obj;
147                 
148                 if (components.length!= other.components.length) return false;
149                 // Verify names
150                 for (int i = 0; i<components.length; i++) {
151                         Component lc = components[i];
152                         Component rc = other.components[i];
153                         if (!lc.name.equals(rc.name)) return false;
154                         
155                 }
156
157                 // Verify types
158                 if (compareHistory==null) compareHistory = new HashSet<IdentityPair<Datatype, Datatype>>(1);
159
160                 IdentityPair<Datatype, Datatype> pair = new IdentityPair<Datatype, Datatype>(this, other);
161                 if (compareHistory.contains(pair)) return true;
162                 compareHistory.add(pair);
163                 
164                 for (int i = 0; i<components.length; i++) {
165                         Component lc = components[i];
166                         Component rc = other.components[i];
167                         if (!lc.type.deepEquals(rc.type, compareHistory)) return false;
168                 }
169                 return true;
170         }
171         
172         @Override
173         public int hashCode() {
174                 int hash = super.hashCode();            
175                 for (Component c : components) 
176                         hash = hash*13 + 7 * c.name.hashCode() /*+ 3*c.type.hashCode()*/;
177                 return hash;
178         }    
179         
180         @Override
181         public void accept(Visitor1 v, Object obj) {
182             v.visit(this, obj);        
183         }
184
185         @Override
186         public <T> T accept(Visitor<T> v) {
187             return v.visit(this);
188         }
189         
190         /**
191          * Return true if the record is a tuple.
192          * Tuple is a record with all components are named as a number, the index number of the field.
193          * Empty record is a tuple  
194          * 
195          * @return true if the record type is a tuple.
196          */
197         public boolean isTupleType() {
198                 if (components.length==0) return false;
199                 for (int i=0; i<getComponentCount(); i++) {
200                         if (!getComponent(i).name.equals(Integer.toString(i))) return false;
201                 }
202                 return true;
203         }
204
205     /**
206      * Get component type by index
207      * @param index index
208      * @return componenet type or <tt>null</tt> if index was invalid
209      */
210     @Override
211     public Datatype getComponentType(int index) {
212         if (index<0||index>=components.length) return null;
213         return components[index].type;
214     }
215     
216         @Override
217         public Datatype getComponentType(ChildReference path) throws IllegalArgumentException {
218                 if (path==null) return this;
219                 if (path instanceof IndexReference) {
220                         IndexReference ir = (IndexReference) path;
221                         return components[ir.index].type.getComponentType(path.childReference);
222                 }
223                 if (path instanceof NameReference) {
224                         NameReference nr = (NameReference) path;
225                         return getComponent( nr.name ).type.getComponentType(path.childReference);
226                 }
227                 if (path instanceof LabelReference) {
228                         LabelReference lr = (LabelReference) path;                      
229                         try {
230                                 Integer i = new Integer(lr.label);
231                                 return getComponent( i ).type.getComponentType(path.childReference);
232                         } catch (NumberFormatException nfe) {
233                                 return getComponent( lr.label ).type.getComponentType(path.childReference);
234                         }
235                 }
236                 throw new IllegalArgumentException();
237         }
238     
239         public boolean hasComponent(String fieldName) {
240         for (int i=0; i<components.length; i++)
241             if (components[i].name.equals(fieldName)) return true;
242         return false;
243         }
244         
245     /**
246      * Get component by name.
247      * 
248      * @param fieldName component name
249      * @return component index or <code>null</code> if one does not exist
250      */
251     public Integer getComponentIndex(String fieldName) {
252         for (int i=0; i<components.length; i++)
253             if (components[i].name.equals(fieldName)) return i;
254         return null;
255     }
256
257     /**
258      * Get component by name.
259      * 
260      * @param fieldName component name
261      * @return component index or -1 if one does not exist
262      */
263     public int getComponentIndex2(String fieldName) {
264         for (int i=0; i<components.length; i++)
265             if (components[i].name.equals(fieldName)) return i;
266         return -1;
267     }
268     
269     /**
270      * Get component Datatype by field name
271      * @param fieldName
272      * @return datatype or <code>null</code>
273      */
274     public Datatype getComponentType(String fieldName) {
275         int index = getComponentIndex2(fieldName);
276         if (index<0) return null;
277         return components[index].type;
278     }
279     
280     /**
281      * Get component by name.
282      * 
283      * @param fieldName component name
284      * @return component or <code>null</code> if one does not exist
285      */
286     public Component getComponent(String fieldName) {
287         for (Component c : components)
288             if (c.name.equals(fieldName)) return c;
289         return null;
290     }
291
292     /**
293      * Get component by index.
294      * 
295      * @param index component index
296      * @return component or <code>null</code> if one does not exist
297      */
298     public Component getComponent(int index) {
299         if (index<0||index>=components.length) return null;
300         return components[index];
301     }
302         
303         public Component[] getComponents() {
304                 return components;
305         }
306
307         /**
308          * Get an array of indices that describe which fields compose the identifier 
309          * of this record 
310          * 
311          * @return indices
312          */
313         public int[] getIdentifiers() {
314                 String ids = metadata.get( KEY_IDENTIFIER );
315                 if (ids == null) {
316                         identifiersIndices = new int[0];
317                 } else {
318                         // Parse
319                         StringTokenizer st = new StringTokenizer(ids, ",");
320                         
321                         int[] indices = new int[ st.countTokens() ];
322                         for (int i=0; i<indices.length; i++) {
323                                 String token = st.nextToken();
324                                 try {
325                                         indices[i] = Integer.valueOf(token);
326                                 } catch ( NumberFormatException nfe ) {
327                                         indices[i] = -1;
328                                 }
329                         }
330                         identifiersIndices = indices;
331                 }
332                 return identifiersIndices;
333         }
334         
335         /**
336          * Set which fields compose the identifier of this record 
337          * 
338          * @param indices
339          */
340         public void setIdentifiers(int...indices)
341         {
342                 if (indices.length==0) {
343                         metadata.remove( KEY_IDENTIFIER );
344                         return;
345                 }
346                 identifiersIndices = indices;
347                 StringBuilder sb = new StringBuilder();
348                 for (int i=0; i<indices.length; i++) {
349                         if (i>0) sb.append(',');
350                         sb.append( Integer.toString(indices[i]) );
351                 }
352                 
353                 String str = sb.toString();
354                 if ( str.isEmpty() ) {
355                         metadata.remove( KEY_IDENTIFIER ); 
356                 } else {
357                         metadata.put( KEY_IDENTIFIER, str );
358                 }
359         }
360
361         /**
362          * Set which fields compose the identifier of this record
363          * @param indices
364          */
365         public void setIdentifiers(List<Integer> indices)
366         {
367                 int[] indices2 = new int[indices.size()];
368                 for (int i=0; i<indices.size(); i++) indices2[i] = indices.get(i);
369                 setIdentifiers(indices2);
370         }
371         
372         public boolean isIdentifier( int fieldIndex )
373         {
374                 int[] ids = getIdentifiers();
375                 if (ids == null) return false;
376                 for (int index : ids)
377                 {
378                         if (index == fieldIndex) return true;
379                 }
380                 return false;
381         }
382         
383         /**
384          * Get a datatype that describes the identifier of this type.
385          * If no field has Identifier annotation, the result is null.
386          * If more than one field is an identifier the type is a record
387          * with all composing fields.
388          * 
389          * @return identifier type or null
390          */
391         public Datatype getIdentifierType() 
392         {               
393                 if ( identifierType != null ) return identifierType;
394                 
395                 int[] ids = getIdentifiers();
396                 if (ids.length==0) return null;
397                 
398                 if (ids.length==1) {
399                         identifierType = getComponentType(ids[0]);
400                 }
401                 
402                 RecordType rt = new RecordType();
403                 for (int i : ids) {
404                         Component c = getComponent(i);
405                         rt.addComponent( c.name, c.type );
406                 }
407                 identifierType = rt;            
408                 return identifierType;
409         }
410
411         @SuppressWarnings("unchecked")
412         public <T extends Datatype> T getChildType( ChildReference reference ) throws ReferenceException
413         {
414                 if (reference==null) return (T) this;
415                 
416                 if (reference instanceof LabelReference) {
417                         LabelReference lr = (LabelReference) reference;
418                         String fieldName = lr.label;
419                         int index = getComponentIndex2(fieldName);
420                         if (index<0) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
421                         return components[index].type.getChildType(reference.childReference);
422                 }               
423                 
424                 if (reference instanceof IndexReference) {
425                         IndexReference ref = (IndexReference) reference;
426                         int index = ref.getIndex();
427                         if ( index<0 || index>=components.length ) new ReferenceException("RecordType doesn't have field at index+"+index);
428                         return components[index].type.getChildType(reference.childReference);
429                 } 
430                 
431                 if (reference instanceof NameReference) {
432                         NameReference lr = (NameReference) reference;
433                         String fieldName = lr.name;
434                         int index = getComponentIndex2(fieldName);
435                         if (index<0) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
436                         return components[index].type.getChildType(reference.childReference);
437                 } 
438                 
439                 throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType");
440                 
441         }
442 }