/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.type; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.simantics.databoard.accessor.error.ReferenceException; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.IndexReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.accessor.reference.NameReference; import org.simantics.databoard.annotations.Referable; import org.simantics.databoard.binding.error.DatatypeConstructionException; import org.simantics.databoard.util.IdentityPair; public @Referable class RecordType extends Datatype { public static final Datatype VOID_TYPE = new RecordType(false); public static final String KEY_REFERABLE = "referable"; // "false"/"true" // Key to identifier(s) index public static final String KEY_IDENTIFIER = "identifier"; // "0" "0,1" "n[,m]" public static final Component[] NO_COMPONENTS = new Component[0]; Component[] components = NO_COMPONENTS; /** Indices to identifiers of this record. This field is filled on request */ private transient int[] identifiersIndices; /** Identifier type */ private transient Datatype identifierType; public RecordType() { setReferable(false); } public RecordType(boolean referable, Component...components) { this.components = components; setReferable(referable); } public boolean isReferable() { String str = metadata.get( KEY_REFERABLE ); return str!=null && str.equals( Boolean.TRUE.toString() ); } public void setReferable( boolean referable ) { if ( !referable ) { metadata.remove(KEY_REFERABLE); } else { metadata.put(KEY_REFERABLE, Boolean.toString(referable)); } } @Override protected void collectSubtypes(Set subtypes, Set recursiveSubtypes) { if(!subtypes.add(this)) { recursiveSubtypes.add(this); return; } for(Component component : components) component.type.collectSubtypes(subtypes, recursiveSubtypes); } public void setComponents(Component[] components) { this.components = components; } public void mergeRecord(RecordType src) throws DatatypeConstructionException { int ci = src.getComponentCount(); for (int i=0; i0) System.arraycopy(components, 0, newComponents, 0, index); if (index> compareHistory) { if ( this==obj ) return true; if ( !hasEqualMetadata(obj) ) return false; if (obj instanceof RecordType == false) return false; RecordType other = (RecordType) obj; if (components.length!= other.components.length) return false; // Verify names for (int i = 0; i>(1); IdentityPair pair = new IdentityPair(this, other); if (compareHistory.contains(pair)) return true; compareHistory.add(pair); for (int i = 0; i T accept(Visitor v) { return v.visit(this); } /** * Return true if the record is a tuple. * Tuple is a record with all components are named as a number, the index number of the field. * Empty record is a tuple * * @return true if the record type is a tuple. */ public boolean isTupleType() { if (components.length==0) return false; for (int i=0; inull if index was invalid */ @Override public Datatype getComponentType(int index) { if (index<0||index>=components.length) return null; return components[index].type; } @Override public Datatype getComponentType(ChildReference path) throws IllegalArgumentException { if (path==null) return this; if (path instanceof IndexReference) { IndexReference ir = (IndexReference) path; return components[ir.index].type.getComponentType(path.childReference); } if (path instanceof NameReference) { NameReference nr = (NameReference) path; return getComponent( nr.name ).type.getComponentType(path.childReference); } if (path instanceof LabelReference) { LabelReference lr = (LabelReference) path; try { Integer i = new Integer(lr.label); return getComponent( i ).type.getComponentType(path.childReference); } catch (NumberFormatException nfe) { return getComponent( lr.label ).type.getComponentType(path.childReference); } } throw new IllegalArgumentException(); } public boolean hasComponent(String fieldName) { for (int i=0; inull if one does not exist */ public Integer getComponentIndex(String fieldName) { for (int i=0; inull */ public Datatype getComponentType(String fieldName) { int index = getComponentIndex2(fieldName); if (index<0) return null; return components[index].type; } /** * Get component by name. * * @param fieldName component name * @return component or null if one does not exist */ public Component getComponent(String fieldName) { for (Component c : components) if (c.name.equals(fieldName)) return c; return null; } /** * Get component by index. * * @param index component index * @return component or null if one does not exist */ public Component getComponent(int index) { if (index<0||index>=components.length) return null; return components[index]; } public Component[] getComponents() { return components; } /** * Get an array of indices that describe which fields compose the identifier * of this record * * @return indices */ public int[] getIdentifiers() { String ids = metadata.get( KEY_IDENTIFIER ); if (ids == null) { identifiersIndices = new int[0]; } else { // Parse StringTokenizer st = new StringTokenizer(ids, ","); int[] indices = new int[ st.countTokens() ]; for (int i=0; i0) sb.append(','); sb.append( Integer.toString(indices[i]) ); } String str = sb.toString(); if ( str.isEmpty() ) { metadata.remove( KEY_IDENTIFIER ); } else { metadata.put( KEY_IDENTIFIER, str ); } } /** * Set which fields compose the identifier of this record * @param indices */ public void setIdentifiers(List indices) { int[] indices2 = new int[indices.size()]; for (int i=0; i T getChildType( ChildReference reference ) throws ReferenceException { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; String fieldName = lr.label; int index = getComponentIndex2(fieldName); if (index<0) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\""); return components[index].type.getChildType(reference.childReference); } if (reference instanceof IndexReference) { IndexReference ref = (IndexReference) reference; int index = ref.getIndex(); if ( index<0 || index>=components.length ) new ReferenceException("RecordType doesn't have field at index+"+index); return components[index].type.getChildType(reference.childReference); } if (reference instanceof NameReference) { NameReference lr = (NameReference) reference; String fieldName = lr.name; int index = getComponentIndex2(fieldName); if (index<0) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\""); return components[index].type.getChildType(reference.childReference); } throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType"); } }