/******************************************************************************* * 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.accessor.reference; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.simantics.databoard.Bindings; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.annotations.Optional; import org.simantics.databoard.annotations.Union; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.MapType; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.URIUtil; /** * Path is a single or multi-level reference to a child node in the tree * representation of data value.

* * ComponentReference[] is a path from a node to a child or decendent node * in the tree representation of the container.

* * Reference has three serializable formats: Binary, URL compatible Path. * * @author Toni Kalajainen */ @Union({ IndexReference.class, KeyReference.class, NameReference.class, ComponentReference.class, LabelReference.class }) public abstract class ChildReference implements Cloneable { /** * Create a concatenation of two references. Prefix part is cloned, * suffix is linked. * * @param pathToBeCloned prefix path, or null * @param ref suffix path, or null * @return path */ public static ChildReference concatenate(ChildReference pathToBeCloned, ChildReference ref) { if (pathToBeCloned==null) return ref; ChildReference result = pathToBeCloned.clone(); if (ref==null) return result; result.tail().setChildReference(ref); return result; } /** * Creates a compilation of individual a references into a one refence. * * @param refs * @return reference or null if there are no elements */ public static ChildReference compile(ChildReference ... refs) { if (refs.length==0) return null; ChildReference first = refs[0].clone(); ChildReference r = first; for (int i=1; inull if there are no elements */ public static ChildReference compile(Collection refs) { if (refs.isEmpty()) return null; Iterator itr = refs.iterator(); ChildReference first = itr.next().clone(); ChildReference r = first; for (; itr.hasNext(); ) { ChildReference next = itr.next().clone(); r.setChildReference( next ); r = next; while(r.childReference!=null) r = r.childReference; } return first; } /** * Get reference path from AccessorReference path. * Path Notation * * @param path * @return reference path or null if there is no path */ public static ChildReference parsePath(String path) { StringTokenizer st = new StringTokenizer(path, "/", false); if (!st.hasMoreTokens()) return null; ChildReference first = createSingleReference( st.nextToken() ); ChildReference ref = first; while (st.hasMoreTokens()) { ref.childReference = createSingleReference(st.nextToken()); ref = ref.childReference; } return first; } /** * Attempt to convert value reference to type reference. * * @param vref * @param type * @return type reference or null * @throws IllegalArgumentException if conversion fails. */ public static ChildReference toTypeReference(ChildReference vref, Datatype type) throws IllegalArgumentException { if (vref==null) return null; if (type instanceof ArrayType) { if (vref instanceof IndexReference || vref instanceof LabelReference) { ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(0) ); return new ComponentReference( tail ); } } if (type instanceof MapType) { if (vref instanceof KeyReference) { ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(1) ); return new IndexReference( 1, tail ); } } if (type instanceof OptionalType) { if (vref instanceof ComponentReference) { ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(0) ); return new ComponentReference( tail ); } if (vref instanceof LabelReference) { LabelReference lr = (LabelReference) vref; if (lr.label.equals("v")) { ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(0) ); return new ComponentReference( tail ); } } } if (type instanceof RecordType) { RecordType rt = (RecordType) type; if (vref instanceof IndexReference) { IndexReference ir = (IndexReference) vref; ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(ir.index) ); return new IndexReference( ir.index, tail ); } if (vref instanceof NameReference) { NameReference ir = (NameReference) vref; ChildReference tail = toTypeReference( vref.childReference, rt.getComponentType(ir.name) ); return new NameReference( ir.name, tail ); } if (vref instanceof LabelReference) { LabelReference ir = (LabelReference) vref; ChildReference tail = toTypeReference( vref.childReference, rt.getComponentType(ir.label) ); return new NameReference( ir.label, tail ); } } if (type instanceof UnionType) { UnionType ut = (UnionType) type; if (vref instanceof IndexReference) { IndexReference ir = (IndexReference) vref; ChildReference tail = toTypeReference( vref.childReference, type.getComponentType(ir.index) ); return new IndexReference( ir.index, tail ); } if (vref instanceof NameReference) { NameReference ir = (NameReference) vref; ChildReference tail = toTypeReference( vref.childReference, ut.getComponentType(ir.name) ); return new NameReference( ir.name, tail ); } if (vref instanceof LabelReference) { LabelReference ir = (LabelReference) vref; ChildReference tail = toTypeReference( vref.childReference, ut.getComponentType(ir.label) ); return new NameReference( ir.label, tail ); } } throw new IllegalArgumentException(); } public static ChildReference parseBinary(byte[] binaryRef) throws IOException { Binding binding = Bindings.getBindingUnchecked(ChildReference.class); ChildReference result; result = (ChildReference) Bindings.getSerializerUnchecked( binding ).deserialize(binaryRef); return result; } public final static Pattern INDEX_PATTERN = Pattern.compile("i-(\\d*)"); public final static Pattern MAP_PATTERN = Pattern.compile("k-(\\p{ASCII}*)"); public final static Pattern NAME_PATTERN = Pattern.compile("n-(\\p{ASCII}*)"); public @Optional ChildReference childReference; protected ChildReference() {} protected ChildReference(ChildReference childReference) { this.childReference = childReference; } public ChildReference getChildReference() { return childReference; } public boolean hasChildReference() { return childReference != null; } public void setChildReference(ChildReference childReference) { this.childReference = childReference; } public int getDepth() { int result = 1; ChildReference r = this; while ( r.childReference != null ) { r = r.childReference; result++; } return result; } public String toPath() { return toPath(true); } /** * Converts the reference path into string representation. * * @param labelReference if true return label references. * @return path string representation */ public String toPath(boolean labelReference) { if (childReference == null) return toString(); StringBuilder sb = new StringBuilder(); ChildReference ref = this; while (ref!=null) { if (sb.length() > 0) sb.append("/"); sb.append( ref.toString(labelReference) ); ref = ref.getChildReference(); } return sb.toString(); } /** * Convert the reference into its string representation * * @return reference string representation */ public String toString() { return toString(true); } /** * Convert the reference into string representation.

* * If labelReference is true, the string representation is * more user readable but has weaker typing. It serializes into * instances of LabelReference. * * For instance Record Field Reference is "n-Children", but label reference "Children". * * Some references cannot be converted into LabelReference. * E.g. string representation of FieldNameReference("i-5") is ambiguous with ArrayIndexReference(5). * * @param labelReference if true returns * @return string representation */ public abstract String toString(boolean labelReference); public abstract ChildReference clone(); public ChildReference tail() { ChildReference result = this; while (result.childReference!=null) result = result.childReference; return result; } /** * Create accessor reference from string representation. * This doesn't parse path separators. * * @see https://www.simantics.org/wiki/index.php/Databoard_Specification#Accessor_Reference * * @param ref * @return */ static ChildReference createSingleReference(String ref) { Matcher m; m = INDEX_PATTERN.matcher( ref ); if (m.matches()) { return new IndexReference( Integer.parseInt( m.group(1) ) ); } m = MAP_PATTERN.matcher( ref ); if (m.matches()) { String keyStr = m.group(1); MutableVariant key; try { key = (MutableVariant) Bindings.adapt(keyStr, Bindings.STRING, Bindings.MUTABLE_VARIANT); } catch (AdaptException e) { throw new IllegalArgumentException("Not string variant "+ref, e); } return new KeyReference(key); } m = NAME_PATTERN.matcher( ref ); if (m.matches()) { String encoded = m.group(1); String name = URIUtil.decodeURI(encoded); return new NameReference( name ); } if (ref.equals("v")) { return new ComponentReference(); } String text = URIUtil.decodeURI( ref ); return new LabelReference( text ); } }