]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/AbstractVariable.java
PropertyMap.get does not return null if there is no mapping for the key
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / AbstractVariable.java
index dc88b0a7ad51b0e52cd5de5dabe91f535a282f41..d9366b132aa2a41e471bf0cd2ea7d1c9b19cef96 100644 (file)
-package org.simantics.db.layer0.variable;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Databoard;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.StringBinding;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.util.URIStringUtils;\r
-import org.simantics.datatypes.literal.GUID;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;\r
-import org.simantics.db.common.request.PossibleIndexRoot;\r
-import org.simantics.db.common.request.PropertyMapOfResource;\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.layer0.exception.InvalidVariableException;\r
-import org.simantics.db.layer0.exception.MissingVariableException;\r
-import org.simantics.db.layer0.exception.MissingVariableValueException;\r
-import org.simantics.db.layer0.request.PossibleResource;\r
-import org.simantics.db.layer0.request.PropertyInfo;\r
-import org.simantics.db.layer0.request.VariableRVIRequest;\r
-import org.simantics.db.layer0.variable.RVI.GuidRVIPart;\r
-import org.simantics.db.layer0.variable.RVI.RVIPart;\r
-import org.simantics.db.layer0.variable.RVI.ResourceRVIPart;\r
-import org.simantics.db.layer0.variable.RVI.StringRVIPart;\r
-import org.simantics.db.layer0.variable.Variables.Role;\r
-import org.simantics.layer0.Layer0;\r
-\r
-/**\r
- * Abstract implementation of Variable -interface.\r
- * \r
- * @author Hannu Niemistö\r
- */\r
-public abstract class AbstractVariable implements Variable {\r
-\r
-    @SuppressWarnings("rawtypes")\r
-    final public VariableNode node;\r
-\r
-    public AbstractVariable(@SuppressWarnings("rawtypes") VariableNode node) {\r
-        this.node = node;\r
-    }\r
-\r
-    /**\r
-     * Returns a variable that is not one of the standard properties listed\r
-     * in <a href="https://www.simantics.org/wiki/index.php/Org.simantics.db.layer0.variable.Variable#Standard_required_properties">specification</a>.\r
-     */\r
-    protected abstract Variable getPossibleDomainProperty(ReadGraph graph, String name) throws DatabaseException;\r
-    public abstract Variable getPossibleExtraProperty(ReadGraph graph, String name) throws DatabaseException;\r
-    public abstract Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException;\r
-        \r
-    public abstract void collectExtraProperties(ReadGraph graph, Map<String, Variable> properties) throws DatabaseException;\r
-    public abstract Map<String, Variable> collectDomainProperties(ReadGraph graph, Map<String, Variable> map) throws DatabaseException;\r
-    public abstract Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException;\r
-    \r
-    public abstract <T> T getValue(ReadGraph graph) throws DatabaseException;\r
-    public abstract <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException;\r
-\r
-    public abstract void setValue(WriteGraph graph, Object value, Binding binding) throws DatabaseException;\r
-    public abstract String getName(ReadGraph graph) throws DatabaseException;\r
-    //public abstract Object getSerialized(ReadGraph graph) throws DatabaseException;\r
-    public abstract Variable getParent(ReadGraph graph) throws DatabaseException;\r
-    public abstract Role getRole(ReadGraph graph) throws DatabaseException;\r
-    public abstract Resource getRepresents(ReadGraph graph) throws DatabaseException;\r
-    \r
-    public abstract Set<String> getClassifications(ReadGraph graph) throws DatabaseException;\r
-     \r
-    @Override\r
-    public PropertyInfo getPropertyInfo(ReadGraph graph) throws DatabaseException {\r
-       throw new DatabaseException("PropertyInfo is not available");\r
-    }\r
-    \r
-    @Override\r
-    public Resource getIndexRoot(ReadGraph graph) throws DatabaseException {\r
-       Resource represents = getPossibleRepresents(graph);\r
-       if(represents != null) return graph.syncRequest(new PossibleIndexRoot(represents));\r
-       Variable parent = getParent(graph);\r
-       if(parent == null) return null;\r
-       return parent.getIndexRoot(graph);\r
-    }\r
-    \r
-    public void validate(ReadGraph graph) throws DatabaseException {\r
-    }\r
-\r
-    public String getIdentifier() {\r
-       return getClass().getSimpleName();\r
-    }\r
-    \r
-    \r
-    @Override\r
-    public Role getPossibleRole(ReadGraph graph) throws DatabaseException {\r
-       try {\r
-               return getRole(graph);\r
-       } catch (DatabaseException e) {\r
-               return null;\r
-       }\r
-    }\r
-    \r
-    protected Variable resolveChild(ReadGraph graph, Resource resource) throws DatabaseException {\r
-       String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);\r
-       for(Variable child : browseChildren(graph)) {\r
-               String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);\r
-               if(rName.equals(name)) return child;\r
-       }\r
-       throw new DatabaseException("Could not resolve child " + resource);\r
-    }\r
-    \r
-    protected Variable resolveChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {\r
-       return StandardRVIResolver.resolveChildDefault(graph, this, part);\r
-    }\r
-    \r
-    protected Variable resolveProperty(ReadGraph graph, Resource resource) throws DatabaseException {\r
-       String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);\r
-       for(Variable child : browseProperties(graph)) {\r
-               String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);\r
-               if(rName.equals(name)) return child;\r
-       }\r
-       throw new DatabaseException("Could not resolve child " + resource);\r
-    }\r
-\r
-    protected Variable resolveProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {\r
-       return StandardRVIResolver.resolvePropertyDefault(graph, this, part);\r
-    }\r
-\r
-    protected Variable resolvePossibleChild(ReadGraph graph, Resource resource) throws DatabaseException {\r
-        String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);\r
-        for(Variable child : browseChildren(graph)) {\r
-            String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);\r
-            if(rName.equals(name)) return child;\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    protected Variable resolvePossibleChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-       for(Variable child : browseChildren(graph)) {\r
-               GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);\r
-               if(id != null) {\r
-                       if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)\r
-                               return child;\r
-               }\r
-       }\r
-       return null;\r
-    }\r
-\r
-    protected Variable resolvePossibleProperty(ReadGraph graph, Resource resource) throws DatabaseException {\r
-        String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);\r
-        for(Variable child : browseProperties(graph)) {\r
-            String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);\r
-            if(rName.equals(name)) return child;\r
-        }\r
-        return null;\r
-    }\r
-\r
-    protected Variable resolvePossibleProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-       for(Variable child : browseProperties(graph)) {\r
-               GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);\r
-               if(id != null) {\r
-                       if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)\r
-                               return child;\r
-               }\r
-       }\r
-       return null;\r
-    }\r
-\r
-    public String getLabel(ReadGraph graph) throws DatabaseException { \r
-        return getName(graph);\r
-    }\r
-    \r
-    public String getPossibleLabel(ReadGraph graph) throws DatabaseException {\r
-       Resource represents = getPossibleRepresents(graph);\r
-       if(represents == null) return null;\r
-               return graph.getPossibleRelatedValue2(represents, graph.getService(Layer0.class).HasLabel, getParent(graph), Bindings.STRING);\r
-    }\r
-\r
-    public Resource getType(ReadGraph graph) throws DatabaseException {\r
-       \r
-        Resource resource = getPossibleRepresents(graph);\r
-        if(resource == null) {\r
-               String uri = getPossiblePropertyValue(graph, "typeURI");\r
-               if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());\r
-            throw new DatabaseException("No type for " + getURI(graph));\r
-        }\r
-        return graph.getSingleType(resource);\r
-        \r
-    }\r
-    \r
-    public RVIPart getRVIPart(ReadGraph graph) throws DatabaseException {\r
-        throw new UnsupportedOperationException();\r
-    }\r
-\r
-    @Override\r
-    public RVI getRVI(ReadGraph graph) throws DatabaseException {\r
-       Databoard databoard = graph.getService( Databoard.class );\r
-       Binding rviBinding = databoard.getBindingUnchecked( RVI.class );\r
-       if(Variables.isContext(graph, this)) {\r
-               return RVI.empty( rviBinding );\r
-       } else {\r
-               Variable parent = getParent(graph);\r
-               if (parent == null)\r
-                       // TODO: consider using a more suitable exception here to better convey the situation.\r
-                       throw new MissingVariableException("no parent for variable " + this + " (URI=" + getPossibleURI(graph) + ")");\r
-               RVI base = graph.syncRequest(new VariableRVIRequest(parent));\r
-               RVIPart part = getRVIPart(graph);\r
-               return new RVIBuilder(base).append(part).toRVI();\r
-       }\r
-    }\r
-    \r
-    protected Variable getDomainProperty(ReadGraph graph, String name) throws DatabaseException {\r
-        Variable property = getPossibleDomainProperty(graph, name);\r
-        if(property == null)\r
-            throw new MissingVariableException(getIdentifier() + ": Didn't find property " + name + ".");\r
-        return property;\r
-    }\r
-    \r
-    protected void addProperty(Map<String, Variable> properties, String name, Object value, Binding binding) {\r
-        if(value != null) {\r
-            properties.put(name, new ConstantPropertyVariable(this, name, value, binding));\r
-        }\r
-    }\r
-\r
-    protected Variable getNameVariable(ReadGraph graph) throws DatabaseException {\r
-       return new ConstantPropertyVariable(this, Variables.NAME, getName(graph), Bindings.STRING);\r
-    }\r
-\r
-    protected Variable getLabelVariable(ReadGraph graph) throws DatabaseException {\r
-       return new ConstantPropertyVariable(this, Variables.LABEL, getPossibleLabel(graph), Bindings.STRING);\r
-    }\r
-\r
-    final public Collection<Variable> browseProperties(ReadGraph graph) throws DatabaseException {\r
-       return getProperties(graph);\r
-    }\r
-\r
-    private Map<String, Variable> getPropertyMap(ReadGraph graph, String classification) throws DatabaseException {\r
-        return collectDomainProperties(graph, classification, null);\r
-    }\r
-\r
-    final static class PropertyMap extends THashMap<String,Variable> {\r
-       \r
-       final private Variable variable;\r
-       \r
-       PropertyMap(Variable variable) {\r
-               this.variable = variable;\r
-       }\r
-\r
-       private Variable getTypeVariable() {\r
-               \r
-               return new AbstractConstantPropertyVariable(variable, Variables.TYPE, null) {\r
-\r
-                               @SuppressWarnings("unchecked")\r
-                               @Override\r
-                               public <T> T getValue(ReadGraph graph) throws DatabaseException {\r
-                                       Resource represents = parent.getRepresents(graph);\r
-                                       if(represents == null) return null;\r
-                                       return (T)graph.getPossibleType(represents, Layer0.getInstance(graph).Entity);\r
-                               }\r
-\r
-                               @Override\r
-                               public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {\r
-                                       return getValue(graph);\r
-                               }\r
-                       \r
-               };\r
-       }\r
-\r
-       private Variable getURIVariable() {\r
-               \r
-               return new AbstractConstantPropertyVariable(variable, Variables.URI, null) {\r
-\r
-                               @SuppressWarnings("unchecked")\r
-                               @Override\r
-                               public <T> T getValue(ReadGraph graph) throws DatabaseException {\r
-                                       return (T)variable.getURI(graph);\r
-                               }\r
-\r
-                               @Override\r
-                               public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {\r
-                                       return getValue(graph);\r
-                               }\r
-                       \r
-               };\r
-       }\r
-\r
-       public Variable get(Object key) {\r
-               Variable result = super.get(key);\r
-               if(result != null) return result;\r
-               \r
-               if(Variables.TYPE.equals(key)) return getTypeVariable();\r
-               else if(Variables.URI.equals(key)) return getURIVariable();\r
-               \r
-                       return variable;\r
-               }\r
-       \r
-    }\r
-    \r
-    private Map<String, Variable> getPropertyMap(ReadGraph graph) throws DatabaseException {\r
-       PropertyMap properties = new PropertyMap(this);\r
-//        Map<String, Variable> properties = new HashMap<String, Variable>();\r
-//        try {\r
-//            properties.put(Variables.NAME, getNameVariable(graph));\r
-//        } catch (DatabaseException e) {\r
-//            // A variable that has no name doesn't exist by definition.\r
-//            // Therefore it can't have any properties.\r
-//            return Collections.emptyMap();\r
-//        }\r
-//        try {\r
-//            Variable labelVariable = getLabelVariable(graph);\r
-//            if(labelVariable != null) properties.put(Variables.LABEL, getLabelVariable(graph));\r
-//        } catch (DatabaseException e) {\r
-//            // Label not absolutely mandatory.\r
-//        }\r
-//        addProperty(properties, Variables.TYPE, getPossibleType(graph), null);\r
-//        addProperty(properties, Variables.URI, getPossibleURI(graph), Bindings.STRING);\r
-        //addProperty(properties, Variables.SERIALISED, getSerialized(graph), Bindings.STRING);\r
-        //addProperty(properties, Variables.PARENT, getParent(graph), null);\r
-//        addProperty(properties, Variables.ROLE, getRole(graph), Bindings.STRING);\r
-//        addProperty(properties, Variables.REPRESENTS, getPossibleRepresents(graph), null);\r
-        collectExtraProperties(graph, properties);\r
-        collectDomainProperties(graph, properties);\r
-        return properties;\r
-    }\r
-    \r
-    @Override\r
-    public Collection<Variable> getProperties(ReadGraph graph) throws DatabaseException {\r
-       return getPropertyMap(graph).values();\r
-    }\r
-    \r
-    public Collection<Variable> getProperties(ReadGraph graph, String classification) throws DatabaseException {\r
-       Map<String,Variable> propertyMap = getPropertyMap(graph, classification);\r
-       if(propertyMap == null) return Collections.emptyList();\r
-       else return propertyMap.values();\r
-    }\r
-\r
-    @Override\r
-    public Collection<Variable> getProperties(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return getProperties(graph, uri(graph, property));\r
-    }\r
-\r
-    final public Collection<Variable> browseChildren(ReadGraph graph) throws DatabaseException {\r
-       return getChildren(graph);\r
-    }\r
-    \r
-    @Override\r
-    public Variable getPossibleProperty(ReadGraph graph, String name)\r
-            throws DatabaseException {\r
-        if(Variables.NAME.equals(name)) {\r
-               return getNameVariable(graph);\r
-        }\r
-        if(Variables.LABEL.equals(name)) {\r
-               return getLabelVariable(graph);\r
-        }\r
-        if(Variables.TYPE.equals(name)) {\r
-            Object value = getPossibleType(graph);\r
-            if(value != null)\r
-                return new ConstantPropertyVariable(this, name, value, null);\r
-        }\r
-        if(Variables.URI.equals(name)) {\r
-            // TODO: getPossibleURI or getURI?\r
-            Object value = getURI(graph);\r
-            if(value != null)\r
-                return new ConstantPropertyVariable(this, name, value, Bindings.STRING);\r
-        }\r
-//        if(Variables.SERIALISED.equals(name)) {\r
-//            Object value = getSerialized(graph);\r
-//            if(value != null)\r
-//                return new ConstantPropertyVariable(this, name, value, Bindings.STRING);\r
-//        }\r
-        /*if(Variables.PARENT.equals(name)) {\r
-            Object value = getParent(graph);\r
-            if(value != null)\r
-                return new ConstantPropertyVariable(this, name, value, null);\r
-        }*/\r
-//        if(Variables.ROLE.equals(name)) {\r
-//            Object value = getRole(graph);\r
-//            if(value != null)\r
-//                return new ConstantPropertyVariable(this, name, value, null);\r
-//        }\r
-//        if(Variables.REPRESENTS.equals(name)) {\r
-//            Object value = getRepresents(graph);\r
-//            if(value != null)\r
-//                return new ConstantPropertyVariable(this, name, value, null);\r
-//        }\r
-        Variable extra = getPossibleExtraProperty(graph, name);\r
-        if(extra != null) return extra;\r
-        return getPossibleDomainProperty(graph, name);\r
-    }\r
-    \r
-    @Override\r
-    public Variable getPossibleProperty(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return getPossibleProperty(graph, name(graph, property));\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    protected <T> T checkNull(ReadGraph graph, Object value) throws DatabaseException {\r
-        if(value == null)\r
-            throw new MissingVariableValueException(getClass().getSimpleName() + ": Didn't find value for " + getPossibleURI(graph));\r
-        return (T)value;\r
-    }\r
-\r
-    private String name(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return graph.getRelatedValue(property, Layer0.getInstance(graph).HasName, Bindings.STRING);\r
-    }\r
-    \r
-    private String uri(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return graph.getURI(property);\r
-    }\r
-\r
-    @Override\r
-    public <T> T getPropertyValue(ReadGraph graph, String name) throws DatabaseException {\r
-       if(Variables.LABEL.equals(name)) return checkNull(graph, getLabel(graph));\r
-       Variable result = getDomainProperty(graph, name);\r
-       if(result != null) return result.getValue(graph);\r
-       if(Variables.NAME.equals(name)) return checkNull(graph, getName(graph));\r
-       if(Variables.TYPE.equals(name)) return checkNull(graph, getPossibleType(graph));\r
-       if(Variables.URI.equals(name)) return checkNull(graph, getURI(graph));\r
-//     if(Variables.SERIALISED.equals(name)) return checkNull(graph, getSerialized(graph));\r
-//     if(Variables.ROLE.equals(name)) return checkNull(graph, getRole(graph));\r
-//     if(Variables.REPRESENTS.equals(name)) return checkNull(graph, getRepresents(graph));\r
-       Variable extra = getPossibleExtraProperty(graph, name);\r
-       if(extra != null) return extra.getValue(graph);\r
-       return null;\r
-    }    \r
-    \r
-    @Override\r
-    public <T> T getPropertyValue(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return getPropertyValue(graph, name(graph, property));\r
-    }\r
-    \r
-    @SuppressWarnings("unchecked")\r
-    @Override\r
-    public <T> T getPossiblePropertyValue(ReadGraph graph, String name)\r
-            throws DatabaseException {\r
-       \r
-        Variable property = getPossibleDomainProperty(graph, name);\r
-        if(property != null) return property.getPossibleValue(graph);\r
-       \r
-        if(Variables.NAME.equals(name)) return (T)getName(graph);\r
-        if(Variables.LABEL.equals(name)) return (T)getLabel(graph);\r
-        if(Variables.TYPE.equals(name)) return (T)getPossibleType(graph);\r
-        if(Variables.URI.equals(name)) return (T)getURI(graph);\r
-//        if(Variables.SERIALISED.equals(name)) return (T)getSerialized(graph);\r
-//        if(Variables.ROLE.equals(name)) return (T)getRole(graph);\r
-//        if(Variables.REPRESENTS.equals(name)) return (T)getRepresents(graph);\r
-        \r
-        Variable extra = getPossibleExtraProperty(graph, name);\r
-        if(extra != null) return extra.getPossibleValue(graph);\r
-        \r
-        return null;\r
-        \r
-    }\r
-\r
-    @Override\r
-    public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return getPossiblePropertyValue(graph, name(graph, property));\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    @Override\r
-    public <T> T getPropertyValue(ReadGraph graph, String name, Binding binding)\r
-            throws DatabaseException {\r
-        if(binding instanceof StringBinding) {\r
-            StringBinding sb = (StringBinding)binding;\r
-            try {\r
-                if(Variables.NAME.equals(name)) return (T)sb.create((String)checkNull(graph, getName(graph)));\r
-                if(Variables.LABEL.equals(name)) return (T)sb.create((String)checkNull(graph, getLabel(graph)));\r
-                if(Variables.URI.equals(name)) return (T)sb.create((String)checkNull(graph, getURI(graph)));\r
-//                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)checkNull(graph, getSerialized(graph)));\r
-            } catch(BindingException e) {\r
-                throw new DatabaseException(e);\r
-            }\r
-        }\r
-        Variable property = getPossibleExtraProperty(graph, name);\r
-        if(property != null)\r
-            return property.getValue(graph, binding);\r
-        property = getPossibleDomainProperty(graph, name);\r
-        if(property == null)\r
-            throw new MissingVariableException("Didn't find property " + name + " for " + this + ".");\r
-        return property.getValue(graph, binding);\r
-    }\r
-\r
-    @Override\r
-    public <T> T getPropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {\r
-       return getPropertyValue(graph, name(graph, property), binding);\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    @Override\r
-    public <T> T getPossiblePropertyValue(ReadGraph graph, String name,\r
-            Binding binding) throws DatabaseException {\r
-        if(binding instanceof StringBinding) {\r
-            StringBinding sb = (StringBinding)binding;\r
-            try {\r
-                if(Variables.NAME.equals(name)) return (T)sb.create((String)getName(graph));\r
-                if(Variables.LABEL.equals(name)) return (T)sb.create((String)getLabel(graph));\r
-                if(Variables.URI.equals(name)) return (T)sb.create((String)getURI(graph));\r
-//                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)getSerialized(graph));\r
-            } catch(BindingException e) {\r
-                throw new DatabaseException(e);\r
-            }\r
-        }\r
-        Variable property = getPossibleExtraProperty(graph, name);\r
-        if(property != null)\r
-            return property.getPossibleValue(graph, binding);\r
-        property = getPossibleDomainProperty(graph, name);\r
-        if(property == null)\r
-            return null;\r
-        return property.getPossibleValue(graph, binding);\r
-    }\r
-\r
-    @Override\r
-    public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {\r
-       return getPossiblePropertyValue(graph, name(graph, property), binding);\r
-    }\r
-\r
-    @Override\r
-    public void setValue(WriteGraph graph, Object value) throws DatabaseException {\r
-       try {\r
-                       setValue(graph, value, Bindings.getBinding(value.getClass()));\r
-               } catch (BindingConstructionException e) {\r
-                       throw new DatabaseException(e);\r
-               }\r
-    }\r
-    \r
-    @Override\r
-    public void setPropertyValue(WriteGraph graph, String name, Object value) throws DatabaseException {\r
-        getProperty(graph, name).setValue(graph, value);\r
-    }\r
-\r
-    @Override\r
-    public void setPropertyValue(WriteGraph graph, Resource property, Object value) throws DatabaseException {\r
-       setPropertyValue(graph, name(graph, property), value);\r
-    }\r
-\r
-    @Override\r
-    public void setPropertyValue(WriteGraph graph, String name, Object value,\r
-            Binding binding) throws DatabaseException {\r
-        getProperty(graph, name).setValue(graph, value, binding);\r
-    }\r
-\r
-    @Override\r
-    public void setPropertyValue(WriteGraph graph, Resource property, Object value, Binding binding) throws DatabaseException {\r
-       setPropertyValue(graph, name(graph, property), value, binding);\r
-    }\r
-\r
-    @Override\r
-    public Variable getChild(ReadGraph graph, String name)\r
-            throws DatabaseException {\r
-        Variable child = getPossibleChild(graph, name);\r
-        if(child == null)\r
-            throw new MissingVariableException(getURI(graph) + ": didn't find child " + name + " for " + getIdentifier() + ".");\r
-        return child;\r
-    }    \r
-\r
-    @Override\r
-    public Variable getProperty(ReadGraph graph, String name)  throws DatabaseException {\r
-        Variable result = getPossibleProperty(graph, name);\r
-        if(result == null)\r
-            throw new MissingVariableException(getClass().getSimpleName() + ": Didn't find property " + name + " for " + getPossibleURI(graph) + ".");\r
-        return result;\r
-    }\r
-    \r
-    @Override\r
-    public Variable getProperty(ReadGraph graph, Resource property) throws DatabaseException {\r
-       return getProperty(graph, name(graph, property));\r
-    }\r
-\r
-    @Override\r
-    public Variable browse(ReadGraph graph, String suffix)\r
-            throws DatabaseException {\r
-       \r
-        if(suffix.isEmpty()) \r
-            return this;        \r
-        switch(suffix.charAt(0)) {\r
-        case '.': {\r
-            Variable parent = getParent(graph); \r
-            if(parent == null)\r
-                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");\r
-            return parent.browse(graph, suffix.substring(1));\r
-        }\r
-        case '#': {\r
-            int segmentEnd = getSegmentEnd(suffix);\r
-            Variable property = getProperty(graph, \r
-                    decodeString(suffix.substring(1, segmentEnd)));\r
-            if(property == null) \r
-                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");\r
-            return property.browse(graph, suffix.substring(segmentEnd));\r
-        }\r
-        case '/': {\r
-            int segmentEnd = getSegmentEnd(suffix);\r
-            Variable child = getChild(graph, \r
-                    decodeString(suffix.substring(1, segmentEnd)));\r
-            if(child == null) \r
-                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");\r
-            return child.browse(graph, suffix.substring(segmentEnd));\r
-        }\r
-        default:\r
-            throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");\r
-        }\r
-       \r
-    }\r
-\r
-    private static int getSegmentEnd(String suffix) {\r
-        int pos;\r
-        for(pos=1;pos<suffix.length();++pos) {\r
-            char c = suffix.charAt(pos);\r
-            if(c == '/' || c == '#')\r
-                break;\r
-        }\r
-        return pos;\r
-    }\r
-    \r
-    @Override\r
-    public Variable browsePossible(ReadGraph graph, String suffix)\r
-            throws DatabaseException {\r
-        if(suffix.isEmpty()) \r
-            return this;        \r
-        switch(suffix.charAt(0)) {\r
-        case '.': {\r
-            Variable parent = getParent(graph); \r
-            if(parent == null)\r
-                return null;\r
-            return parent.browsePossible(graph, suffix.substring(1));\r
-        }\r
-        case '#': {\r
-            int segmentEnd = getSegmentEnd(suffix);\r
-            Variable property = getPossibleProperty(graph, \r
-                    decodeString(suffix.substring(1, segmentEnd)));\r
-            if(property == null) \r
-                return null;\r
-            return property.browsePossible(graph, suffix.substring(segmentEnd));\r
-        }\r
-        case '/': {\r
-            int segmentEnd = getSegmentEnd(suffix);\r
-            Variable child = getPossibleChild(graph, \r
-                    decodeString(suffix.substring(1, segmentEnd)));\r
-            if(child == null) \r
-                return null;\r
-            return child.browsePossible(graph, suffix.substring(segmentEnd));\r
-        }\r
-        default:\r
-            return null;\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public Variable browse(ReadGraph graph, Resource config)\r
-            throws DatabaseException {\r
-        Variable variable = browsePossible(graph, config);\r
-        if(variable == null)\r
-            throw new MissingVariableException("Didn't find a variable related to " + \r
-                    NameUtils.getSafeName(graph, config) + ".");\r
-        return variable;\r
-    }\r
-\r
-    @Override\r
-    public Variable browsePossible(ReadGraph graph, Resource config)\r
-            throws DatabaseException {\r
-        Layer0 l0 = Layer0.getInstance(graph);\r
-        String name = (String)graph.getPossibleRelatedValue(config, l0.HasName, Bindings.STRING);\r
-        if (name == null)\r
-            return null;\r
-        return getPossibleChild(graph, name);\r
-    }\r
-\r
-    @Override\r
-    public <T> T getInterface(ReadGraph graph, Class<T> clazz)\r
-            throws DatabaseException {\r
-        return null;\r
-    }\r
-\r
-    @Override\r
-    public String getURI(ReadGraph graph) throws DatabaseException {\r
-       validate(graph);\r
-        Variable parent = getParent(graph);\r
-        if (parent == null)\r
-            throw new InvalidVariableException(this + " has no URI");\r
-        return parent.getURI(graph) + getRole(graph).getIdentifier() + encodeString(getName(graph));\r
-    }\r
-\r
-    /**\r
-     * For debug messages.\r
-     * \r
-     * @param graph\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public String getPossibleURI(ReadGraph graph) throws DatabaseException {\r
-        Variable parent = getParent(graph);\r
-        if (parent == null)\r
-            return null;\r
-        if (parent instanceof AbstractVariable) {\r
-            String parentUri = ((AbstractVariable) parent).getPossibleURI(graph);\r
-            if (parentUri == null)\r
-                return null;\r
-            return parentUri + getRole(graph).getIdentifier() + encodeString(getName(graph));\r
-        }\r
-        return null;\r
-    }\r
-\r
-    public <T> T getPossibleValue(ReadGraph graph) throws DatabaseException {\r
-        try {\r
-            return getValue(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public Variant getVariantValue(ReadGraph graph) throws DatabaseException {\r
-       Binding binding = getPossibleDefaultBinding(graph);\r
-        if(binding != null) {\r
-               Object value = getValue(graph, binding);\r
-               return new Variant(binding, value);\r
-        } else {\r
-//             System.err.println("no data type for " + getURI(graph));\r
-               // TODO: hackish, consider doing something else here?\r
-               Object value = getValue(graph);\r
-               try {\r
-                               binding = Bindings.OBJECT.getContentBinding(value);\r
-                       } catch (BindingException e) {\r
-                               throw new DatabaseException(e);\r
-                       }\r
-               return new Variant(binding, value);\r
-        }\r
-    }\r
-    \r
-    public Variant getPossibleVariantValue(ReadGraph graph) throws DatabaseException {\r
-       Binding binding = getPossibleDefaultBinding(graph);\r
-        if(binding != null) {\r
-               Object value = getPossibleValue(graph, binding);\r
-               if(value == null) return null;\r
-               return new Variant(binding, value);\r
-        } else {\r
-               Object value = getPossibleValue(graph);\r
-               if(value == null) return null;\r
-               try {\r
-                       // TODO: hackish, consider doing something else here?\r
-                       binding = value != null ? Bindings.getBinding(value.getClass()) : null;\r
-                       return new Variant(binding, value);\r
-               } catch (BindingConstructionException e) {\r
-                       return null;\r
-               }\r
-        }\r
-    }\r
-\r
-    public <T> T getPossibleValue(ReadGraph graph, Binding binding) throws DatabaseException {\r
-        try {\r
-            return getValue(graph, binding);\r
-        } catch(MissingVariableValueException e) {\r
-            return null;\r
-        }\r
-    }\r
-\r
-    public <T> T adapt(ReadGraph graph, Class<T> clazz) throws DatabaseException {\r
-        throw new AdaptionException(this + " does not support adaption to " + clazz);\r
-    }\r
-    \r
-    @Override\r
-    public <T> T adaptPossible(ReadGraph graph, Class<T> clazz) throws DatabaseException {\r
-       try {\r
-               return adapt(graph, clazz);\r
-       } catch (AdaptionException e) {\r
-               return null;\r
-       }\r
-    }\r
-    \r
-    \r
-    public static String encodeString(String string) throws DatabaseException {\r
-       if (string == null || "".equals(string)) return string;\r
-        return URIStringUtils.escape(string);\r
-    }\r
-    \r
-    public static String decodeString(String string) throws DatabaseException {\r
-        return URIStringUtils.unescape(string);\r
-    }\r
-    \r
-\r
-       protected Variable getPossiblePropertyFromContext(ReadGraph graph, Resource context, String name) throws DatabaseException {\r
-\r
-               Map<String, Resource> predicates = graph.syncRequest(new PropertyMapOfResource(context));\r
-               Resource property = predicates.get(name);\r
-               if(property == null) return null;\r
-               Resource object = graph.getSingleObject(context, property);\r
-               Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), \r
-                               ModelledVariablePropertyDescriptor.class, Variable.class);\r
-               if(objectAdapter != null) return objectAdapter;\r
-               return graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), \r
-                               ModelledVariablePropertyDescriptor.class, Variable.class);\r
-               \r
-       }\r
-       \r
-       protected Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Resource context, Map<String, Variable> properties) throws DatabaseException {\r
-\r
-               for(Map.Entry<String, Resource> entry : graph.syncRequest(new PropertyMapOfResource(context)).entrySet()) {\r
-                       String name = entry.getKey();\r
-                       Resource property = entry.getValue();\r
-                       Resource object = graph.getSingleObject(context, property);\r
-                       Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), \r
-                                       ModelledVariablePropertyDescriptor.class, Variable.class);\r
-                       if(objectAdapter != null) {\r
-                               if(objectAdapter != null) {\r
-                                   if(properties == null) properties = new HashMap<String,Variable>();\r
-                                   properties.put(name, objectAdapter);\r
-                               }\r
-                       } else {\r
-                               Variable predicateAdapter = graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), \r
-                                               ModelledVariablePropertyDescriptor.class, Variable.class);\r
-                               if(predicateAdapter != null) {\r
-                    if(properties == null) properties = new HashMap<String,Variable>();\r
-                                   properties.put(name, predicateAdapter);\r
-                               }\r
-                       }\r
-\r
-               }\r
-               \r
-               return properties;\r
-               \r
-       }\r
-       \r
-       @Override\r
-       public Variable resolve(ReadGraph graph, RVIPart part) throws DatabaseException {\r
-               if(part instanceof StringRVIPart) {\r
-                       StringRVIPart srp = (StringRVIPart)part;\r
-                       if(Role.CHILD.equals(srp.getRole())) return getChild(graph, srp.string);\r
-                       else if(Role.PROPERTY.equals(srp.getRole())) return getProperty(graph, srp.string);\r
-               } else if(part instanceof ResourceRVIPart) {\r
-                       ResourceRVIPart rrp = (ResourceRVIPart)part;\r
-                       if(Role.CHILD.equals(rrp.getRole())) return resolveChild(graph, rrp.resource);\r
-                       else if(Role.PROPERTY.equals(rrp.getRole())) return resolveProperty(graph, rrp.resource);\r
-               } else if(part instanceof GuidRVIPart) {\r
-                       GuidRVIPart grp = (GuidRVIPart)part;\r
-                       if(Role.CHILD.equals(grp.getRole())) return resolveChild(graph, grp);\r
-                       else if(Role.PROPERTY.equals(grp.getRole())) return resolveProperty(graph, grp);\r
-               }\r
-               throw new DatabaseException("Unrecognized RVIPart: " + part);\r
-       }\r
-\r
-       @Override\r
-       public Variable resolvePossible(ReadGraph graph, RVIPart part) throws DatabaseException {\r
-               if(part instanceof StringRVIPart) {\r
-                       StringRVIPart srp = (StringRVIPart)part;\r
-                       if(Role.CHILD.equals(srp.getRole())) return getPossibleChild(graph, srp.string);\r
-                       else if(Role.PROPERTY.equals(srp.getRole())) return getPossibleProperty(graph, srp.string);\r
-               } else if(part instanceof ResourceRVIPart) {\r
-                       ResourceRVIPart rrp = (ResourceRVIPart)part;\r
-                       if(Role.CHILD.equals(rrp.getRole())) return resolvePossibleChild(graph, rrp.resource);\r
-                       else if(Role.PROPERTY.equals(rrp.getRole())) return resolvePossibleProperty(graph, rrp.resource);\r
-               } else if(part instanceof GuidRVIPart) {\r
-                       GuidRVIPart grp = (GuidRVIPart)part;\r
-                       if(Role.CHILD.equals(grp.getRole())) return resolvePossibleChild(graph, grp);\r
-                       else if(Role.PROPERTY.equals(grp.getRole())) return resolvePossibleProperty(graph, grp);\r
-               }\r
-               throw new DatabaseException("Unrecognized RVIPart: " + part);\r
-       }\r
-\r
-       @Override\r
-       public Datatype getDatatype(ReadGraph graph) throws DatabaseException {\r
-               throw new DatabaseException("No data type.");\r
-       }\r
-\r
-       public Binding getDefaultBinding(ReadGraph graph) throws DatabaseException {\r
-               Datatype type = getDatatype(graph);\r
-               return Bindings.getBinding(type);\r
-       }\r
-       \r
-       public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {\r
-        try {\r
-            return getDefaultBinding(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-       }\r
-\r
-       @Override\r
-       public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {\r
-        try {\r
-            return getDatatype(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-       }\r
-       \r
-//     public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {\r
-//             \r
-//             Datatype type = getPossibleDatatype(graph);\r
-//             if(type == null) return null;\r
-//             return Bindings.getBinding(type);\r
-//\r
-//     }\r
-//\r
-//     @Override\r
-//     public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {\r
-//             \r
-//             Variant vt = getVariantValue(graph);\r
-//             if(vt == null) return null;\r
-//             Binding binding = vt.getBinding();\r
-//             if(binding == null) return null;\r
-//             return binding.type();\r
-//\r
-//     }\r
-\r
-       @Override\r
-       public Variable getPredicate(ReadGraph graph) throws DatabaseException {\r
-               throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));\r
-       }\r
-\r
-       @Override\r
-       public Variable getPossiblePredicate(ReadGraph graph) throws DatabaseException {\r
-               try {\r
-            return getPredicate(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-       }\r
-       \r
-       @Override\r
-       public Resource getPredicateResource(ReadGraph graph) throws DatabaseException {\r
-               Variable predicate = getPredicate(graph);\r
-               if(predicate == null) throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));\r
-               return predicate.getRepresents(graph);\r
-       }\r
-       \r
-       @Override\r
-       public Resource getPossiblePredicateResource(ReadGraph graph) throws DatabaseException {\r
-               Variable predicate = getPossiblePredicate(graph);\r
-               if(predicate == null) return null;\r
-               else return predicate.getPossibleRepresents(graph);\r
-       }\r
-\r
-       @Override\r
-       public Resource getPossibleRepresents(ReadGraph graph) throws DatabaseException {\r
-        try {\r
-            return getRepresents(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-       }\r
-\r
-       @Override\r
-       public Resource getPossibleType(ReadGraph graph) throws DatabaseException {\r
-        Resource resource = getPossibleRepresents(graph);\r
-        if(resource == null) {\r
-               String uri = getPossiblePropertyValue(graph, "typeURI");\r
-               if(uri != null) return graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());\r
-            return null;\r
-        }\r
-        return graph.getPossibleObject(resource, Layer0.getInstance(graph).InstanceOf);\r
-       }\r
-\r
-    public Resource getType(ReadGraph graph, Resource baseType) throws DatabaseException {\r
-        Resource resource = getPossibleRepresents(graph);\r
-        if(resource == null) {\r
-               String uri = getPossiblePropertyValue(graph, "typeURI");\r
-               if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());\r
-            throw new DatabaseException("No type for " + getURI(graph));\r
-        }\r
-        return graph.getSingleType(resource, baseType);\r
-    }\r
-\r
-       @Override\r
-       public Resource getPossibleType(ReadGraph graph, Resource baseType) throws DatabaseException {\r
-        Resource resource = getPossibleRepresents(graph);\r
-        if(resource == null) {\r
-               String uri = getPossiblePropertyValue(graph, "typeURI");\r
-               if(uri != null) {\r
-                       Resource type = graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());\r
-                       if(type == null) return null;\r
-                       if(graph.isInheritedFrom(type, baseType)) return type;\r
-                       else return null;\r
-               }\r
-            return null;\r
-        }\r
-        return graph.getPossibleType(resource, baseType);\r
-       }\r
-\r
-    public Map<String, Variable> collectDomainProperties(ReadGraph graph, String classification, Map<String, Variable> map) throws DatabaseException {\r
-       Map<String,Variable> all = collectDomainProperties(graph, null);\r
-       for(Map.Entry<String, Variable> entry : all.entrySet()) {\r
-               Set<String> classifications = entry.getValue().getClassifications(graph);\r
-               if(classifications.contains(classification)) {\r
-                       if(map == null) map = new HashMap<String,Variable>();\r
-                       map.put(entry.getKey(), entry.getValue());\r
-               }\r
-       }\r
-       return map;\r
-    }\r
-    \r
-    @Override\r
-    public RVI getPossibleRVI(ReadGraph graph) throws DatabaseException {\r
-        try {\r
-            return getRVI(graph);\r
-        } catch(DatabaseException e) {\r
-            return null;\r
-        }\r
-    }\r
-\r
-}\r
+package org.simantics.db.layer0.variable;
+
+import gnu.trove.map.hash.THashMap;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Databoard;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.StringBinding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.URIStringUtils;
+import org.simantics.datatypes.literal.GUID;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
+import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.PropertyMapOfResource;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.AdaptionException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.exception.InvalidVariableException;
+import org.simantics.db.layer0.exception.MissingVariableException;
+import org.simantics.db.layer0.exception.MissingVariableValueException;
+import org.simantics.db.layer0.request.PossibleResource;
+import org.simantics.db.layer0.request.PropertyInfo;
+import org.simantics.db.layer0.request.VariableRVIRequest;
+import org.simantics.db.layer0.variable.RVI.GuidRVIPart;
+import org.simantics.db.layer0.variable.RVI.RVIPart;
+import org.simantics.db.layer0.variable.RVI.ResourceRVIPart;
+import org.simantics.db.layer0.variable.RVI.StringRVIPart;
+import org.simantics.db.layer0.variable.Variables.Role;
+import org.simantics.layer0.Layer0;
+
+/**
+ * Abstract implementation of Variable -interface.
+ * 
+ * @author Hannu Niemist&ouml;
+ */
+public abstract class AbstractVariable implements Variable {
+
+    @SuppressWarnings("rawtypes")
+    final public VariableNode node;
+
+    public AbstractVariable(@SuppressWarnings("rawtypes") VariableNode node) {
+        this.node = node;
+    }
+
+    /**
+     * Returns a variable that is not one of the standard properties listed
+     * in <a href="https://www.simantics.org/wiki/index.php/Org.simantics.db.layer0.variable.Variable#Standard_required_properties">specification</a>.
+     */
+    protected abstract Variable getPossibleDomainProperty(ReadGraph graph, String name) throws DatabaseException;
+    public abstract Variable getPossibleExtraProperty(ReadGraph graph, String name) throws DatabaseException;
+    public abstract Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException;
+        
+    public abstract void collectExtraProperties(ReadGraph graph, Map<String, Variable> properties) throws DatabaseException;
+    public abstract Map<String, Variable> collectDomainProperties(ReadGraph graph, Map<String, Variable> map) throws DatabaseException;
+    public abstract Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException;
+    
+    public abstract <T> T getValue(ReadGraph graph) throws DatabaseException;
+    public abstract <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException;
+
+    public abstract void setValue(WriteGraph graph, Object value, Binding binding) throws DatabaseException;
+    public abstract String getName(ReadGraph graph) throws DatabaseException;
+    //public abstract Object getSerialized(ReadGraph graph) throws DatabaseException;
+    public abstract Variable getParent(ReadGraph graph) throws DatabaseException;
+    public abstract Role getRole(ReadGraph graph) throws DatabaseException;
+    public abstract Resource getRepresents(ReadGraph graph) throws DatabaseException;
+    
+    public abstract Set<String> getClassifications(ReadGraph graph) throws DatabaseException;
+     
+    @Override
+    public PropertyInfo getPropertyInfo(ReadGraph graph) throws DatabaseException {
+       throw new DatabaseException("PropertyInfo is not available");
+    }
+    
+    @Override
+    public Resource getIndexRoot(ReadGraph graph) throws DatabaseException {
+       Resource represents = getPossibleRepresents(graph);
+       if(represents != null) return graph.syncRequest(new PossibleIndexRoot(represents));
+       Variable parent = getParent(graph);
+       if(parent == null) return null;
+       return parent.getIndexRoot(graph);
+    }
+    
+    public void validate(ReadGraph graph) throws DatabaseException {
+    }
+
+    public String getIdentifier() {
+       return getClass().getSimpleName();
+    }
+    
+    
+    @Override
+    public Role getPossibleRole(ReadGraph graph) throws DatabaseException {
+       try {
+               return getRole(graph);
+       } catch (DatabaseException e) {
+               return null;
+       }
+    }
+    
+    protected Variable resolveChild(ReadGraph graph, Resource resource) throws DatabaseException {
+       String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
+       for(Variable child : browseChildren(graph)) {
+               String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
+               if(rName.equals(name)) return child;
+       }
+       throw new DatabaseException("Could not resolve child " + resource);
+    }
+    
+    protected Variable resolveChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
+       return StandardRVIResolver.resolveChildDefault(graph, this, part);
+    }
+    
+    protected Variable resolveProperty(ReadGraph graph, Resource resource) throws DatabaseException {
+       String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
+       for(Variable child : browseProperties(graph)) {
+               String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
+               if(rName.equals(name)) return child;
+       }
+       throw new DatabaseException("Could not resolve child " + resource);
+    }
+
+    protected Variable resolveProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
+       return StandardRVIResolver.resolvePropertyDefault(graph, this, part);
+    }
+
+    protected Variable resolvePossibleChild(ReadGraph graph, Resource resource) throws DatabaseException {
+        String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
+        for(Variable child : browseChildren(graph)) {
+            String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
+            if(rName.equals(name)) return child;
+        }
+        return null;
+    }
+    
+    protected Variable resolvePossibleChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+       for(Variable child : browseChildren(graph)) {
+               GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);
+               if(id != null) {
+                       if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)
+                               return child;
+               }
+       }
+       return null;
+    }
+
+    protected Variable resolvePossibleProperty(ReadGraph graph, Resource resource) throws DatabaseException {
+        String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
+        for(Variable child : browseProperties(graph)) {
+            String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
+            if(rName.equals(name)) return child;
+        }
+        return null;
+    }
+
+    protected Variable resolvePossibleProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+       for(Variable child : browseProperties(graph)) {
+               GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);
+               if(id != null) {
+                       if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)
+                               return child;
+               }
+       }
+       return null;
+    }
+
+    public String getLabel(ReadGraph graph) throws DatabaseException { 
+        return getName(graph);
+    }
+    
+    public String getPossibleLabel(ReadGraph graph) throws DatabaseException {
+       Resource represents = getPossibleRepresents(graph);
+       if(represents == null) return null;
+               return graph.getPossibleRelatedValue2(represents, graph.getService(Layer0.class).HasLabel, getParent(graph), Bindings.STRING);
+    }
+
+    public Resource getType(ReadGraph graph) throws DatabaseException {
+       
+        Resource resource = getPossibleRepresents(graph);
+        if(resource == null) {
+               String uri = getPossiblePropertyValue(graph, "typeURI");
+               if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());
+            throw new DatabaseException("No type for " + getURI(graph));
+        }
+        return graph.getSingleType(resource);
+        
+    }
+    
+    public RVIPart getRVIPart(ReadGraph graph) throws DatabaseException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public RVI getRVI(ReadGraph graph) throws DatabaseException {
+       Databoard databoard = graph.getService( Databoard.class );
+       Binding rviBinding = databoard.getBindingUnchecked( RVI.class );
+       if(Variables.isContext(graph, this)) {
+               return RVI.empty( rviBinding );
+       } else {
+               Variable parent = getParent(graph);
+               if (parent == null)
+                       // TODO: consider using a more suitable exception here to better convey the situation.
+                       throw new MissingVariableException("no parent for variable " + this + " (URI=" + getPossibleURI(graph) + ")");
+               RVI base = graph.syncRequest(new VariableRVIRequest(parent));
+               RVIPart part = getRVIPart(graph);
+               return new RVIBuilder(base).append(part).toRVI();
+       }
+    }
+    
+    protected Variable getDomainProperty(ReadGraph graph, String name) throws DatabaseException {
+        Variable property = getPossibleDomainProperty(graph, name);
+        if(property == null)
+            throw new MissingVariableException(getIdentifier() + ": Didn't find property " + name + ".");
+        return property;
+    }
+    
+    protected void addProperty(Map<String, Variable> properties, String name, Object value, Binding binding) {
+        if(value != null) {
+            properties.put(name, new ConstantPropertyVariable(this, name, value, binding));
+        }
+    }
+
+    protected Variable getNameVariable(ReadGraph graph) throws DatabaseException {
+       return new ConstantPropertyVariable(this, Variables.NAME, getName(graph), Bindings.STRING);
+    }
+
+    protected Variable getLabelVariable(ReadGraph graph) throws DatabaseException {
+       return new ConstantPropertyVariable(this, Variables.LABEL, getPossibleLabel(graph), Bindings.STRING);
+    }
+
+    final public Collection<Variable> browseProperties(ReadGraph graph) throws DatabaseException {
+       return getProperties(graph);
+    }
+
+    private Map<String, Variable> getPropertyMap(ReadGraph graph, String classification) throws DatabaseException {
+        return collectDomainProperties(graph, classification, null);
+    }
+
+    final static class PropertyMap extends THashMap<String,Variable> {
+       
+       final private Variable variable;
+       
+       PropertyMap(Variable variable) {
+               this.variable = variable;
+       }
+
+       private Variable getTypeVariable() {
+               
+               return new AbstractConstantPropertyVariable(variable, Variables.TYPE, null) {
+
+                               @SuppressWarnings("unchecked")
+                               @Override
+                               public <T> T getValue(ReadGraph graph) throws DatabaseException {
+                                       Resource represents = parent.getRepresents(graph);
+                                       if(represents == null) return null;
+                                       return (T)graph.getPossibleType(represents, Layer0.getInstance(graph).Entity);
+                               }
+
+                               @Override
+                               public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
+                                       return getValue(graph);
+                               }
+                       
+               };
+       }
+
+       private Variable getURIVariable() {
+               
+               return new AbstractConstantPropertyVariable(variable, Variables.URI, null) {
+
+                               @SuppressWarnings("unchecked")
+                               @Override
+                               public <T> T getValue(ReadGraph graph) throws DatabaseException {
+                                       return (T)variable.getURI(graph);
+                               }
+
+                               @Override
+                               public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
+                                       return getValue(graph);
+                               }
+                       
+               };
+       }
+
+       public Variable get(Object key) {
+               Variable result = super.get(key);
+               if(result != null) return result;
+               
+               if(Variables.TYPE.equals(key)) return getTypeVariable();
+               else if(Variables.URI.equals(key)) return getURIVariable();
+               
+                       return null;
+               }
+       
+    }
+    
+    private Map<String, Variable> getPropertyMap(ReadGraph graph) throws DatabaseException {
+       PropertyMap properties = new PropertyMap(this);
+//        Map<String, Variable> properties = new HashMap<String, Variable>();
+//        try {
+//            properties.put(Variables.NAME, getNameVariable(graph));
+//        } catch (DatabaseException e) {
+//            // A variable that has no name doesn't exist by definition.
+//            // Therefore it can't have any properties.
+//            return Collections.emptyMap();
+//        }
+//        try {
+//            Variable labelVariable = getLabelVariable(graph);
+//            if(labelVariable != null) properties.put(Variables.LABEL, getLabelVariable(graph));
+//        } catch (DatabaseException e) {
+//            // Label not absolutely mandatory.
+//        }
+//        addProperty(properties, Variables.TYPE, getPossibleType(graph), null);
+//        addProperty(properties, Variables.URI, getPossibleURI(graph), Bindings.STRING);
+        //addProperty(properties, Variables.SERIALISED, getSerialized(graph), Bindings.STRING);
+        //addProperty(properties, Variables.PARENT, getParent(graph), null);
+//        addProperty(properties, Variables.ROLE, getRole(graph), Bindings.STRING);
+//        addProperty(properties, Variables.REPRESENTS, getPossibleRepresents(graph), null);
+        collectExtraProperties(graph, properties);
+        collectDomainProperties(graph, properties);
+        return properties;
+    }
+    
+    @Override
+    public Collection<Variable> getProperties(ReadGraph graph) throws DatabaseException {
+       return getPropertyMap(graph).values();
+    }
+    
+    public Collection<Variable> getProperties(ReadGraph graph, String classification) throws DatabaseException {
+       Map<String,Variable> propertyMap = getPropertyMap(graph, classification);
+       if(propertyMap == null) return Collections.emptyList();
+       else return propertyMap.values();
+    }
+
+    @Override
+    public Collection<Variable> getProperties(ReadGraph graph, Resource property) throws DatabaseException {
+       return getProperties(graph, uri(graph, property));
+    }
+
+    final public Collection<Variable> browseChildren(ReadGraph graph) throws DatabaseException {
+       return getChildren(graph);
+    }
+    
+    @Override
+    public Variable getPossibleProperty(ReadGraph graph, String name)
+            throws DatabaseException {
+        if(Variables.NAME.equals(name)) {
+               return getNameVariable(graph);
+        }
+        if(Variables.LABEL.equals(name)) {
+               return getLabelVariable(graph);
+        }
+        if(Variables.TYPE.equals(name)) {
+            Object value = getPossibleType(graph);
+            if(value != null)
+                return new ConstantPropertyVariable(this, name, value, null);
+        }
+        if(Variables.URI.equals(name)) {
+            // TODO: getPossibleURI or getURI?
+            Object value = getURI(graph);
+            if(value != null)
+                return new ConstantPropertyVariable(this, name, value, Bindings.STRING);
+        }
+//        if(Variables.SERIALISED.equals(name)) {
+//            Object value = getSerialized(graph);
+//            if(value != null)
+//                return new ConstantPropertyVariable(this, name, value, Bindings.STRING);
+//        }
+        /*if(Variables.PARENT.equals(name)) {
+            Object value = getParent(graph);
+            if(value != null)
+                return new ConstantPropertyVariable(this, name, value, null);
+        }*/
+//        if(Variables.ROLE.equals(name)) {
+//            Object value = getRole(graph);
+//            if(value != null)
+//                return new ConstantPropertyVariable(this, name, value, null);
+//        }
+//        if(Variables.REPRESENTS.equals(name)) {
+//            Object value = getRepresents(graph);
+//            if(value != null)
+//                return new ConstantPropertyVariable(this, name, value, null);
+//        }
+        Variable extra = getPossibleExtraProperty(graph, name);
+        if(extra != null) return extra;
+        return getPossibleDomainProperty(graph, name);
+    }
+    
+    @Override
+    public Variable getPossibleProperty(ReadGraph graph, Resource property) throws DatabaseException {
+       return getPossibleProperty(graph, name(graph, property));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T checkNull(ReadGraph graph, Object value) throws DatabaseException {
+        if(value == null)
+            throw new MissingVariableValueException(getClass().getSimpleName() + ": Didn't find value for " + getPossibleURI(graph));
+        return (T)value;
+    }
+
+    private String name(ReadGraph graph, Resource property) throws DatabaseException {
+       return graph.getRelatedValue(property, Layer0.getInstance(graph).HasName, Bindings.STRING);
+    }
+    
+    private String uri(ReadGraph graph, Resource property) throws DatabaseException {
+       return graph.getURI(property);
+    }
+
+    @Override
+    public <T> T getPropertyValue(ReadGraph graph, String name) throws DatabaseException {
+       if(Variables.LABEL.equals(name)) return checkNull(graph, getLabel(graph));
+       Variable result = getDomainProperty(graph, name);
+       if(result != null) return result.getValue(graph);
+       if(Variables.NAME.equals(name)) return checkNull(graph, getName(graph));
+       if(Variables.TYPE.equals(name)) return checkNull(graph, getPossibleType(graph));
+       if(Variables.URI.equals(name)) return checkNull(graph, getURI(graph));
+//     if(Variables.SERIALISED.equals(name)) return checkNull(graph, getSerialized(graph));
+//     if(Variables.ROLE.equals(name)) return checkNull(graph, getRole(graph));
+//     if(Variables.REPRESENTS.equals(name)) return checkNull(graph, getRepresents(graph));
+       Variable extra = getPossibleExtraProperty(graph, name);
+       if(extra != null) return extra.getValue(graph);
+       return null;
+    }    
+    
+    @Override
+    public <T> T getPropertyValue(ReadGraph graph, Resource property) throws DatabaseException {
+       return getPropertyValue(graph, name(graph, property));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getPossiblePropertyValue(ReadGraph graph, String name)
+            throws DatabaseException {
+       
+        Variable property = getPossibleDomainProperty(graph, name);
+        if(property != null) return property.getPossibleValue(graph);
+       
+        if(Variables.NAME.equals(name)) return (T)getName(graph);
+        if(Variables.LABEL.equals(name)) return (T)getLabel(graph);
+        if(Variables.TYPE.equals(name)) return (T)getPossibleType(graph);
+        if(Variables.URI.equals(name)) return (T)getURI(graph);
+//        if(Variables.SERIALISED.equals(name)) return (T)getSerialized(graph);
+//        if(Variables.ROLE.equals(name)) return (T)getRole(graph);
+//        if(Variables.REPRESENTS.equals(name)) return (T)getRepresents(graph);
+        
+        Variable extra = getPossibleExtraProperty(graph, name);
+        if(extra != null) return extra.getPossibleValue(graph);
+        
+        return null;
+        
+    }
+
+    @Override
+    public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property) throws DatabaseException {
+       return getPossiblePropertyValue(graph, name(graph, property));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getPropertyValue(ReadGraph graph, String name, Binding binding)
+            throws DatabaseException {
+        if(binding instanceof StringBinding) {
+            StringBinding sb = (StringBinding)binding;
+            try {
+                if(Variables.NAME.equals(name)) return (T)sb.create((String)checkNull(graph, getName(graph)));
+                if(Variables.LABEL.equals(name)) return (T)sb.create((String)checkNull(graph, getLabel(graph)));
+                if(Variables.URI.equals(name)) return (T)sb.create((String)checkNull(graph, getURI(graph)));
+//                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)checkNull(graph, getSerialized(graph)));
+            } catch(BindingException e) {
+                throw new DatabaseException(e);
+            }
+        }
+        Variable property = getPossibleExtraProperty(graph, name);
+        if(property != null)
+            return property.getValue(graph, binding);
+        property = getPossibleDomainProperty(graph, name);
+        if(property == null)
+            throw new MissingVariableException("Didn't find property " + name + " for " + this + ".");
+        return property.getValue(graph, binding);
+    }
+
+    @Override
+    public <T> T getPropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {
+       return getPropertyValue(graph, name(graph, property), binding);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getPossiblePropertyValue(ReadGraph graph, String name,
+            Binding binding) throws DatabaseException {
+        if(binding instanceof StringBinding) {
+            StringBinding sb = (StringBinding)binding;
+            try {
+                if(Variables.NAME.equals(name)) return (T)sb.create((String)getName(graph));
+                if(Variables.LABEL.equals(name)) return (T)sb.create((String)getLabel(graph));
+                if(Variables.URI.equals(name)) return (T)sb.create((String)getURI(graph));
+//                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)getSerialized(graph));
+            } catch(BindingException e) {
+                throw new DatabaseException(e);
+            }
+        }
+        Variable property = getPossibleExtraProperty(graph, name);
+        if(property != null)
+            return property.getPossibleValue(graph, binding);
+        property = getPossibleDomainProperty(graph, name);
+        if(property == null)
+            return null;
+        return property.getPossibleValue(graph, binding);
+    }
+
+    @Override
+    public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {
+       return getPossiblePropertyValue(graph, name(graph, property), binding);
+    }
+
+    @Override
+    public void setValue(WriteGraph graph, Object value) throws DatabaseException {
+       try {
+                       setValue(graph, value, Bindings.getBinding(value.getClass()));
+               } catch (BindingConstructionException e) {
+                       throw new DatabaseException(e);
+               }
+    }
+    
+    @Override
+    public void setPropertyValue(WriteGraph graph, String name, Object value) throws DatabaseException {
+        getProperty(graph, name).setValue(graph, value);
+    }
+
+    @Override
+    public void setPropertyValue(WriteGraph graph, Resource property, Object value) throws DatabaseException {
+       setPropertyValue(graph, name(graph, property), value);
+    }
+
+    @Override
+    public void setPropertyValue(WriteGraph graph, String name, Object value,
+            Binding binding) throws DatabaseException {
+        getProperty(graph, name).setValue(graph, value, binding);
+    }
+
+    @Override
+    public void setPropertyValue(WriteGraph graph, Resource property, Object value, Binding binding) throws DatabaseException {
+       setPropertyValue(graph, name(graph, property), value, binding);
+    }
+
+    @Override
+    public Variable getChild(ReadGraph graph, String name)
+            throws DatabaseException {
+        Variable child = getPossibleChild(graph, name);
+        if(child == null)
+            throw new MissingVariableException(getURI(graph) + ": didn't find child " + name + " for " + getIdentifier() + ".");
+        return child;
+    }    
+
+    @Override
+    public Variable getProperty(ReadGraph graph, String name)  throws DatabaseException {
+        Variable result = getPossibleProperty(graph, name);
+        if(result == null)
+            throw new MissingVariableException(getClass().getSimpleName() + ": Didn't find property " + name + " for " + getPossibleURI(graph) + ".");
+        return result;
+    }
+    
+    @Override
+    public Variable getProperty(ReadGraph graph, Resource property) throws DatabaseException {
+       return getProperty(graph, name(graph, property));
+    }
+
+    @Override
+    public Variable browse(ReadGraph graph, String suffix)
+            throws DatabaseException {
+       
+        if(suffix.isEmpty()) 
+            return this;        
+        switch(suffix.charAt(0)) {
+        case '.': {
+            Variable parent = getParent(graph); 
+            if(parent == null)
+                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
+            return parent.browse(graph, suffix.substring(1));
+        }
+        case '#': {
+            int segmentEnd = getSegmentEnd(suffix);
+            Variable property = getProperty(graph, 
+                    decodeString(suffix.substring(1, segmentEnd)));
+            if(property == null) 
+                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
+            return property.browse(graph, suffix.substring(segmentEnd));
+        }
+        case '/': {
+            int segmentEnd = getSegmentEnd(suffix);
+            Variable child = getChild(graph, 
+                    decodeString(suffix.substring(1, segmentEnd)));
+            if(child == null) 
+                throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
+            return child.browse(graph, suffix.substring(segmentEnd));
+        }
+        default:
+            throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
+        }
+       
+    }
+
+    private static int getSegmentEnd(String suffix) {
+        int pos;
+        for(pos=1;pos<suffix.length();++pos) {
+            char c = suffix.charAt(pos);
+            if(c == '/' || c == '#')
+                break;
+        }
+        return pos;
+    }
+    
+    @Override
+    public Variable browsePossible(ReadGraph graph, String suffix)
+            throws DatabaseException {
+        if(suffix.isEmpty()) 
+            return this;        
+        switch(suffix.charAt(0)) {
+        case '.': {
+            Variable parent = getParent(graph); 
+            if(parent == null)
+                return null;
+            return parent.browsePossible(graph, suffix.substring(1));
+        }
+        case '#': {
+            int segmentEnd = getSegmentEnd(suffix);
+            Variable property = getPossibleProperty(graph, 
+                    decodeString(suffix.substring(1, segmentEnd)));
+            if(property == null) 
+                return null;
+            return property.browsePossible(graph, suffix.substring(segmentEnd));
+        }
+        case '/': {
+            int segmentEnd = getSegmentEnd(suffix);
+            Variable child = getPossibleChild(graph, 
+                    decodeString(suffix.substring(1, segmentEnd)));
+            if(child == null) 
+                return null;
+            return child.browsePossible(graph, suffix.substring(segmentEnd));
+        }
+        default:
+            return null;
+        }
+    }
+
+    @Override
+    public Variable browse(ReadGraph graph, Resource config)
+            throws DatabaseException {
+        Variable variable = browsePossible(graph, config);
+        if(variable == null)
+            throw new MissingVariableException("Didn't find a variable related to " + 
+                    NameUtils.getSafeName(graph, config) + ".");
+        return variable;
+    }
+
+    @Override
+    public Variable browsePossible(ReadGraph graph, Resource config)
+            throws DatabaseException {
+        Layer0 l0 = Layer0.getInstance(graph);
+        String name = (String)graph.getPossibleRelatedValue(config, l0.HasName, Bindings.STRING);
+        if (name == null)
+            return null;
+        return getPossibleChild(graph, name);
+    }
+
+    @Override
+    public <T> T getInterface(ReadGraph graph, Class<T> clazz)
+            throws DatabaseException {
+        return null;
+    }
+
+    @Override
+    public String getURI(ReadGraph graph) throws DatabaseException {
+       validate(graph);
+        Variable parent = getParent(graph);
+        if (parent == null)
+            throw new InvalidVariableException(this + " has no URI");
+        return parent.getURI(graph) + getRole(graph).getIdentifier() + encodeString(getName(graph));
+    }
+
+    /**
+     * For debug messages.
+     * 
+     * @param graph
+     * @return
+     * @throws DatabaseException
+     */
+    public String getPossibleURI(ReadGraph graph) throws DatabaseException {
+        Variable parent = getParent(graph);
+        if (parent == null)
+            return null;
+        if (parent instanceof AbstractVariable) {
+            String parentUri = ((AbstractVariable) parent).getPossibleURI(graph);
+            if (parentUri == null)
+                return null;
+            return parentUri + getRole(graph).getIdentifier() + encodeString(getName(graph));
+        }
+        return null;
+    }
+
+    public <T> T getPossibleValue(ReadGraph graph) throws DatabaseException {
+        try {
+            return getValue(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public Variant getVariantValue(ReadGraph graph) throws DatabaseException {
+       Binding binding = getPossibleDefaultBinding(graph);
+        if(binding != null) {
+               Object value = getValue(graph, binding);
+               return new Variant(binding, value);
+        } else {
+//             System.err.println("no data type for " + getURI(graph));
+               // TODO: hackish, consider doing something else here?
+               Object value = getValue(graph);
+               try {
+                               binding = Bindings.OBJECT.getContentBinding(value);
+                       } catch (BindingException e) {
+                               throw new DatabaseException(e);
+                       }
+               return new Variant(binding, value);
+        }
+    }
+    
+    public Variant getPossibleVariantValue(ReadGraph graph) throws DatabaseException {
+       Binding binding = getPossibleDefaultBinding(graph);
+        if(binding != null) {
+               Object value = getPossibleValue(graph, binding);
+               if(value == null) return null;
+               return new Variant(binding, value);
+        } else {
+               Object value = getPossibleValue(graph);
+               if(value == null) return null;
+               try {
+                       // TODO: hackish, consider doing something else here?
+                       binding = value != null ? Bindings.getBinding(value.getClass()) : null;
+                       return new Variant(binding, value);
+               } catch (BindingConstructionException e) {
+                       return null;
+               }
+        }
+    }
+
+    public <T> T getPossibleValue(ReadGraph graph, Binding binding) throws DatabaseException {
+        try {
+            return getValue(graph, binding);
+        } catch(MissingVariableValueException e) {
+            return null;
+        }
+    }
+
+    public <T> T adapt(ReadGraph graph, Class<T> clazz) throws DatabaseException {
+        throw new AdaptionException(this + " does not support adaption to " + clazz);
+    }
+    
+    @Override
+    public <T> T adaptPossible(ReadGraph graph, Class<T> clazz) throws DatabaseException {
+       try {
+               return adapt(graph, clazz);
+       } catch (AdaptionException e) {
+               return null;
+       }
+    }
+    
+    
+    public static String encodeString(String string) throws DatabaseException {
+       if (string == null || "".equals(string)) return string;
+        return URIStringUtils.escape(string);
+    }
+    
+    public static String decodeString(String string) throws DatabaseException {
+        return URIStringUtils.unescape(string);
+    }
+    
+
+       protected Variable getPossiblePropertyFromContext(ReadGraph graph, Resource context, String name) throws DatabaseException {
+
+               Map<String, Resource> predicates = graph.syncRequest(new PropertyMapOfResource(context));
+               Resource property = predicates.get(name);
+               if(property == null) return null;
+               Resource object = graph.getSingleObject(context, property);
+               Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
+                               ModelledVariablePropertyDescriptor.class, Variable.class);
+               if(objectAdapter != null) return objectAdapter;
+               return graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
+                               ModelledVariablePropertyDescriptor.class, Variable.class);
+               
+       }
+       
+       protected Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Resource context, Map<String, Variable> properties) throws DatabaseException {
+
+               for(Map.Entry<String, Resource> entry : graph.syncRequest(new PropertyMapOfResource(context)).entrySet()) {
+                       String name = entry.getKey();
+                       Resource property = entry.getValue();
+                       Resource object = graph.getSingleObject(context, property);
+                       Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
+                                       ModelledVariablePropertyDescriptor.class, Variable.class);
+                       if(objectAdapter != null) {
+                               if(objectAdapter != null) {
+                                   if(properties == null) properties = new HashMap<String,Variable>();
+                                   properties.put(name, objectAdapter);
+                               }
+                       } else {
+                               Variable predicateAdapter = graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
+                                               ModelledVariablePropertyDescriptor.class, Variable.class);
+                               if(predicateAdapter != null) {
+                    if(properties == null) properties = new HashMap<String,Variable>();
+                                   properties.put(name, predicateAdapter);
+                               }
+                       }
+
+               }
+               
+               return properties;
+               
+       }
+       
+       @Override
+       public Variable resolve(ReadGraph graph, RVIPart part) throws DatabaseException {
+               if(part instanceof StringRVIPart) {
+                       StringRVIPart srp = (StringRVIPart)part;
+                       if(Role.CHILD.equals(srp.getRole())) return getChild(graph, srp.string);
+                       else if(Role.PROPERTY.equals(srp.getRole())) return getProperty(graph, srp.string);
+               } else if(part instanceof ResourceRVIPart) {
+                       ResourceRVIPart rrp = (ResourceRVIPart)part;
+                       if(Role.CHILD.equals(rrp.getRole())) return resolveChild(graph, rrp.resource);
+                       else if(Role.PROPERTY.equals(rrp.getRole())) return resolveProperty(graph, rrp.resource);
+               } else if(part instanceof GuidRVIPart) {
+                       GuidRVIPart grp = (GuidRVIPart)part;
+                       if(Role.CHILD.equals(grp.getRole())) return resolveChild(graph, grp);
+                       else if(Role.PROPERTY.equals(grp.getRole())) return resolveProperty(graph, grp);
+               }
+               throw new DatabaseException("Unrecognized RVIPart: " + part);
+       }
+
+       @Override
+       public Variable resolvePossible(ReadGraph graph, RVIPart part) throws DatabaseException {
+               if(part instanceof StringRVIPart) {
+                       StringRVIPart srp = (StringRVIPart)part;
+                       if(Role.CHILD.equals(srp.getRole())) return getPossibleChild(graph, srp.string);
+                       else if(Role.PROPERTY.equals(srp.getRole())) return getPossibleProperty(graph, srp.string);
+               } else if(part instanceof ResourceRVIPart) {
+                       ResourceRVIPart rrp = (ResourceRVIPart)part;
+                       if(Role.CHILD.equals(rrp.getRole())) return resolvePossibleChild(graph, rrp.resource);
+                       else if(Role.PROPERTY.equals(rrp.getRole())) return resolvePossibleProperty(graph, rrp.resource);
+               } else if(part instanceof GuidRVIPart) {
+                       GuidRVIPart grp = (GuidRVIPart)part;
+                       if(Role.CHILD.equals(grp.getRole())) return resolvePossibleChild(graph, grp);
+                       else if(Role.PROPERTY.equals(grp.getRole())) return resolvePossibleProperty(graph, grp);
+               }
+               throw new DatabaseException("Unrecognized RVIPart: " + part);
+       }
+
+       @Override
+       public Datatype getDatatype(ReadGraph graph) throws DatabaseException {
+               throw new DatabaseException("No data type.");
+       }
+
+       public Binding getDefaultBinding(ReadGraph graph) throws DatabaseException {
+               Datatype type = getDatatype(graph);
+               return Bindings.getBinding(type);
+       }
+       
+       public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {
+        try {
+            return getDefaultBinding(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+       }
+
+       @Override
+       public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {
+        try {
+            return getDatatype(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+       }
+       
+//     public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {
+//             
+//             Datatype type = getPossibleDatatype(graph);
+//             if(type == null) return null;
+//             return Bindings.getBinding(type);
+//
+//     }
+//
+//     @Override
+//     public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {
+//             
+//             Variant vt = getVariantValue(graph);
+//             if(vt == null) return null;
+//             Binding binding = vt.getBinding();
+//             if(binding == null) return null;
+//             return binding.type();
+//
+//     }
+
+       @Override
+       public Variable getPredicate(ReadGraph graph) throws DatabaseException {
+               throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));
+       }
+
+       @Override
+       public Variable getPossiblePredicate(ReadGraph graph) throws DatabaseException {
+               try {
+            return getPredicate(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+       }
+       
+       @Override
+       public Resource getPredicateResource(ReadGraph graph) throws DatabaseException {
+               Variable predicate = getPredicate(graph);
+               if(predicate == null) throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));
+               return predicate.getRepresents(graph);
+       }
+       
+       @Override
+       public Resource getPossiblePredicateResource(ReadGraph graph) throws DatabaseException {
+               Variable predicate = getPossiblePredicate(graph);
+               if(predicate == null) return null;
+               else return predicate.getPossibleRepresents(graph);
+       }
+
+       @Override
+       public Resource getPossibleRepresents(ReadGraph graph) throws DatabaseException {
+        try {
+            return getRepresents(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+       }
+
+       @Override
+       public Resource getPossibleType(ReadGraph graph) throws DatabaseException {
+        Resource resource = getPossibleRepresents(graph);
+        if(resource == null) {
+               String uri = getPossiblePropertyValue(graph, "typeURI");
+               if(uri != null) return graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());
+            return null;
+        }
+        return graph.getPossibleObject(resource, Layer0.getInstance(graph).InstanceOf);
+       }
+
+    public Resource getType(ReadGraph graph, Resource baseType) throws DatabaseException {
+        Resource resource = getPossibleRepresents(graph);
+        if(resource == null) {
+               String uri = getPossiblePropertyValue(graph, "typeURI");
+               if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());
+            throw new DatabaseException("No type for " + getURI(graph));
+        }
+        return graph.getSingleType(resource, baseType);
+    }
+
+       @Override
+       public Resource getPossibleType(ReadGraph graph, Resource baseType) throws DatabaseException {
+        Resource resource = getPossibleRepresents(graph);
+        if(resource == null) {
+               String uri = getPossiblePropertyValue(graph, "typeURI");
+               if(uri != null) {
+                       Resource type = graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());
+                       if(type == null) return null;
+                       if(graph.isInheritedFrom(type, baseType)) return type;
+                       else return null;
+               }
+            return null;
+        }
+        return graph.getPossibleType(resource, baseType);
+       }
+
+    public Map<String, Variable> collectDomainProperties(ReadGraph graph, String classification, Map<String, Variable> map) throws DatabaseException {
+       Map<String,Variable> all = collectDomainProperties(graph, null);
+       for(Map.Entry<String, Variable> entry : all.entrySet()) {
+               Set<String> classifications = entry.getValue().getClassifications(graph);
+               if(classifications.contains(classification)) {
+                       if(map == null) map = new HashMap<String,Variable>();
+                       map.put(entry.getKey(), entry.getValue());
+               }
+       }
+       return map;
+    }
+    
+    @Override
+    public RVI getPossibleRVI(ReadGraph graph) throws DatabaseException {
+        try {
+            return getRVI(graph);
+        } catch(DatabaseException e) {
+            return null;
+        }
+    }
+
+}