package org.simantics.db.layer0.variable; import java.util.Arrays; import org.simantics.databoard.Bindings; import org.simantics.databoard.Databoard; import org.simantics.databoard.annotations.Union; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.util.Bean; import org.simantics.databoard.util.URIStringUtils; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.AdaptionException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.exception.ValidationException; import org.simantics.db.layer0.variable.Variables.Role; import org.simantics.db.service.SerialisationSupport; import org.simantics.layer0.Layer0; /** * Relative Value Indicator is a parent-child reference inside variable-tree * address space. * * @See RVIBuilder * @author toni.kalajainen */ public class RVI extends Bean { private static final RVIPart[] NONE = {}; public RVIPart[] parts; public RVI() { super(); } public RVI(Binding rviBinding) { super(rviBinding); } public static RVI empty(Binding rviBinding) { RVI result = new RVI(rviBinding); result.parts = NONE; return result; } public boolean isEmpty() { return parts.length == 0; } public Variable resolve(ReadGraph graph, Variable base) throws DatabaseException { for(RVIPart part : parts) { base = base.resolve(graph, part); } return base; } public Variable resolvePossible(ReadGraph graph, Variable base) throws DatabaseException { for(RVIPart part : parts) { base = base.resolvePossible(graph, part); if (base == null) return null; } return base; } @Override public int hashCode() { return Arrays.hashCode(parts); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RVI other = (RVI) obj; return Arrays.equals(parts, other.parts); } /** * Returns a string representation of the all the parts of this RVI for * visualization purposes. This representation of the RVI does not withstand * moving the RVI-referenced resource between model composites unlike the * databoard serialization of this class. * *
* Implementation takes the RVI part of a Variable resolved using this RVI
* and the specified Variable as a base for the resolution.
*
* @param graph transaction handle
* @param base base resource to use for resolving the variable
* @return this RVI as a String
* @throws DatabaseException
*/
public String asString(ReadGraph graph, Variable base) throws DatabaseException {
String baseURI = base.getURI(graph);
Variable resolved = resolve(graph, base);
return resolved.getURI(graph).substring(baseURI.length());
}
public UniqueReadnull
if resolution of the string form fails because of the
* database contents.
*
* @param graph transaction handle
* @param base base resource to use for resolving the variable
* @return this RVI as a String
* @throws DatabaseException
*/
public String asPossibleString(ReadGraph graph, Variable base) throws DatabaseException {
try {
return asString(graph, base);
} catch (DatabaseException e) {
return null;
}
}
/**
* Print as persistent string
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (RVIPart p : parts) {
if (p instanceof ResourceRVIPart) {
sb.append(p.toString());
} else if (p instanceof StringRVIPart) {
sb.append(((StringRVIPart) p).toEscapedString());
} else if (p instanceof GuidRVIPart) {
sb.append(((GuidRVIPart) p).toEscapedString());
}
}
return sb.toString();
}
/**
* Resolves this RVI into a string with a best effort method that tries to
* resolve the RVI parts into variables for as long as it can. After
* resolution is finished the remaining RVI parts are simply concatenated
* into the result string as strings without resolving them into variables.
* This is different from {@link #asString(ReadGraph, Variable)} or
* {@link #asPossibleString(ReadGraph, Variable)} in that it doesn't demand
* that the RVI resolves completely. Still at least the first RVI part must
* resolve, otherwise null
is returned, since this means that
* the variable no longer exists at all.
*
* @param graph
* database read handle
* @param base
* the base of resolution for the RVI
* @return The RVI of the referenced entity as a string or null
* if zero parts of this RVI can be resolved, i.e. the resources
* referenced by it no longer exist.
* @throws DatabaseException
*/
public String toPossibleString(ReadGraph graph, Variable base) throws DatabaseException {
String baseURI = base.getURI(graph);
Variable resolved = null;
int i = 0;
for (; i < parts.length; ++i) {
RVIPart p = parts[i];
base = base.resolvePossible(graph, p);
if (base == null)
break;
resolved = base;
}
if (resolved == null)
return null;
StringBuilder sb = new StringBuilder();
String resolvedURI = resolved.getURI(graph);
sb.append(resolvedURI, baseURI.length(), resolvedURI.length());
if (i < parts.length) {
// The tail didn't resolve into a Variable synchronously
// so let's just concatenate the rest by hand into the
// result string.
Layer0 L0 = Layer0.getInstance(graph);
for (; i < parts.length; ++i) {
RVIPart p = parts[i];
if (p instanceof ResourceRVIPart) {
Resource r = ((ResourceRVIPart) p).resource;
String str = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);
if (str == null)
return null;
sb.append(p.getRole().getIdentifier()).append(URIStringUtils.escape(str));
} else if (p instanceof StringRVIPart) {
sb.append(p.getRole().getIdentifier()).append(URIStringUtils.escape(((StringRVIPart) p).string));
} else if (p instanceof GuidRVIPart) {
sb.append(p.getRole().getIdentifier()).append(((GuidRVIPart)p).mostSignificant).append(":").append(((GuidRVIPart)p).leastSignificant);
}
}
}
return sb.toString();
}
/**
* Print for USER. Not the real RVI
*
* @param graph
* @return
* @throws AdaptionException
* @throws ValidationException
* @throws ServiceException
*/
public String toString(ReadGraph graph) throws DatabaseException {
StringBuilder sb = new StringBuilder();
for (RVIPart p : parts) {
if (p instanceof ResourceRVIPart) {
Resource r = ((ResourceRVIPart) p).resource;
String str = NameUtils.getSafeName(graph, r);
sb.append(p.getRole().getIdentifier()).append(str);
} else if (p instanceof StringRVIPart) {
String str = p.toString();
sb.append(str);
} else if (p instanceof GuidRVIPart) {
String str = p.toString();
sb.append(str);
}
}
return sb.toString();
}
// Enumeration | ResourceRVIPart | StringRVIPart | GuidRVIPart
@Union({ResourceRVIPart.class, StringRVIPart.class, GuidRVIPart.class})
public static interface RVIPart { public Role getRole(); }
public static class ResourceRVIPart implements RVIPart {
public Role role;
public Resource resource;
@Override
public Role getRole() {
return role;
}
public ResourceRVIPart() {}
public ResourceRVIPart(Role role, Resource resource) {
if (resource == null)
throw new NullPointerException("null resource");
this.role = role;
this.resource = resource;
}
@Override
public String toString() {
return role.getIdentifier()+"r"+Long.toString( resource.getResourceId() );
}
@Override
public int hashCode() {
return resource.hashCode() * 31 + role.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ResourceRVIPart other = (ResourceRVIPart) obj;
return role == other.role && resource.equals(other.resource);
}
}
public static class StringRVIPart implements RVIPart {
public Role role;
public String string;
@Override
public Role getRole() {
return role;
}
public StringRVIPart() {}
public StringRVIPart(Role role, String string) {
if (string == null)
throw new NullPointerException("null string");
this.role = role;
this.string = string;
}
@Override
public String toString() {
return role.getIdentifier()+string;
}
public String toEscapedString() {
return role.getIdentifier()+URIStringUtils.escape(string);
}
@Override
public int hashCode() {
return string.hashCode() * 31 + role.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StringRVIPart other = (StringRVIPart) obj;
return role == other.role && string.equals(other.string);
}
}
public static class GuidRVIPart implements RVIPart {
public Role role;
public long mostSignificant;
public long leastSignificant;
public transient Resource resource;
@Override
public Role getRole() {
return role;
}
public GuidRVIPart() {}
public GuidRVIPart(Role role, Resource resource, long mostSignificant, long leastSignificant) {
this.role = role;
this.resource = resource;
this.mostSignificant = mostSignificant;
this.leastSignificant = leastSignificant;
}
@Override
public String toString() {
return role.getIdentifier()+mostSignificant+":"+leastSignificant;
}
public String toEscapedString() {
String rid = resource != null ? Long.toString( resource.getResourceId() ) : "0";
return role.getIdentifier()+mostSignificant+":"+leastSignificant+":"+rid;
}
@Override
public int hashCode() {
return (longHashCode(leastSignificant) * 51 + longHashCode(mostSignificant)) * 31 + role.hashCode();
}
/*
* TODO: remove this when switched into Java 1.8
*/
private static int longHashCode(long value) {
return (int)(value ^ (value >>> 32));
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GuidRVIPart other = (GuidRVIPart) obj;
return role == other.role && leastSignificant == other.leastSignificant && mostSignificant == other.mostSignificant;
}
}
public static RVI fromResourceFormat( RequestProcessor proc, String str ) {
SerialisationSupport support = proc.getService(SerialisationSupport.class);
if (support == null) throw new RuntimeException("No serialization support in Session");
Databoard databoard = proc.getService(Databoard.class);
if (databoard == null) throw new RuntimeException("No databoard support in Session");
Binding rviBinding = databoard.getBindingUnchecked( RVI.class );
RVIBuilder rb = new RVIBuilder( rviBinding );
int pos = 0, len = str.length();
while (pos