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