/******************************************************************************* * 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
*
* If 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 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.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 );
}
}