3d527e03957509764e7daebf23ff27e01fd59999
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / AbstractVariable.java
1 package org.simantics.db.layer0.variable;
2
3 import gnu.trove.map.hash.THashMap;
4
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.simantics.databoard.Bindings;
12 import org.simantics.databoard.Databoard;
13 import org.simantics.databoard.binding.Binding;
14 import org.simantics.databoard.binding.StringBinding;
15 import org.simantics.databoard.binding.error.BindingConstructionException;
16 import org.simantics.databoard.binding.error.BindingException;
17 import org.simantics.databoard.binding.mutable.Variant;
18 import org.simantics.databoard.type.Datatype;
19 import org.simantics.databoard.util.URIStringUtils;
20 import org.simantics.datatypes.literal.GUID;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.Resource;
23 import org.simantics.db.WriteGraph;
24 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
25 import org.simantics.db.common.request.PossibleIndexRoot;
26 import org.simantics.db.common.request.PropertyMapOfResource;
27 import org.simantics.db.common.utils.NameUtils;
28 import org.simantics.db.exception.AdaptionException;
29 import org.simantics.db.exception.DatabaseException;
30 import org.simantics.db.layer0.exception.InvalidVariableException;
31 import org.simantics.db.layer0.exception.MissingVariableException;
32 import org.simantics.db.layer0.exception.MissingVariableValueException;
33 import org.simantics.db.layer0.request.PossibleResource;
34 import org.simantics.db.layer0.request.PropertyInfo;
35 import org.simantics.db.layer0.request.VariableRVIRequest;
36 import org.simantics.db.layer0.variable.RVI.GuidRVIPart;
37 import org.simantics.db.layer0.variable.RVI.RVIPart;
38 import org.simantics.db.layer0.variable.RVI.ResourceRVIPart;
39 import org.simantics.db.layer0.variable.RVI.StringRVIPart;
40 import org.simantics.db.layer0.variable.Variables.Role;
41 import org.simantics.layer0.Layer0;
42
43 /**
44  * Abstract implementation of Variable -interface.
45  * 
46  * @author Hannu Niemistö
47  */
48 public abstract class AbstractVariable implements Variable {
49
50     @SuppressWarnings("rawtypes")
51     final public VariableNode node;
52
53     public AbstractVariable(@SuppressWarnings("rawtypes") VariableNode node) {
54         this.node = node;
55     }
56
57     /**
58      * Returns a variable that is not one of the standard properties listed
59      * in <a href="https://www.simantics.org/wiki/index.php/Org.simantics.db.layer0.variable.Variable#Standard_required_properties">specification</a>.
60      */
61     protected abstract Variable getPossibleDomainProperty(ReadGraph graph, String name) throws DatabaseException;
62     public abstract Variable getPossibleExtraProperty(ReadGraph graph, String name) throws DatabaseException;
63     public abstract Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException;
64         
65     public abstract void collectExtraProperties(ReadGraph graph, Map<String, Variable> properties) throws DatabaseException;
66     public abstract Map<String, Variable> collectDomainProperties(ReadGraph graph, Map<String, Variable> map) throws DatabaseException;
67     public abstract Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException;
68     
69     public abstract <T> T getValue(ReadGraph graph) throws DatabaseException;
70     public abstract <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException;
71
72     public abstract void setValue(WriteGraph graph, Object value, Binding binding) throws DatabaseException;
73     public abstract String getName(ReadGraph graph) throws DatabaseException;
74     //public abstract Object getSerialized(ReadGraph graph) throws DatabaseException;
75     public abstract Variable getParent(ReadGraph graph) throws DatabaseException;
76     public abstract Role getRole(ReadGraph graph) throws DatabaseException;
77     public abstract Resource getRepresents(ReadGraph graph) throws DatabaseException;
78     
79     public abstract Set<String> getClassifications(ReadGraph graph) throws DatabaseException;
80      
81     @Override
82     public PropertyInfo getPropertyInfo(ReadGraph graph) throws DatabaseException {
83         throw new DatabaseException("PropertyInfo is not available");
84     }
85     
86     @Override
87     public Resource getIndexRoot(ReadGraph graph) throws DatabaseException {
88         Resource represents = getPossibleRepresents(graph);
89         if(represents != null) return graph.syncRequest(new PossibleIndexRoot(represents));
90         Variable parent = getParent(graph);
91         if(parent == null) return null;
92         return parent.getIndexRoot(graph);
93     }
94     
95     public void validate(ReadGraph graph) throws DatabaseException {
96     }
97
98     public String getIdentifier() {
99         return getClass().getSimpleName();
100     }
101     
102     
103     @Override
104     public Role getPossibleRole(ReadGraph graph) throws DatabaseException {
105         try {
106                 return getRole(graph);
107         } catch (DatabaseException e) {
108                 return null;
109         }
110     }
111     
112     protected Variable resolveChild(ReadGraph graph, Resource resource) throws DatabaseException {
113         String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
114         for(Variable child : browseChildren(graph)) {
115                 String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
116                 if(rName.equals(name)) return child;
117         }
118         throw new DatabaseException("Could not resolve child " + resource);
119     }
120     
121     protected Variable resolveChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
122         return StandardRVIResolver.resolveChildDefault(graph, this, part);
123     }
124     
125     protected Variable resolveProperty(ReadGraph graph, Resource resource) throws DatabaseException {
126         String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
127         for(Variable child : browseProperties(graph)) {
128                 String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
129                 if(rName.equals(name)) return child;
130         }
131         throw new DatabaseException("Could not resolve child " + resource);
132     }
133
134     protected Variable resolveProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
135         return StandardRVIResolver.resolvePropertyDefault(graph, this, part);
136     }
137
138     protected Variable resolvePossibleChild(ReadGraph graph, Resource resource) throws DatabaseException {
139         String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
140         for(Variable child : browseChildren(graph)) {
141             String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
142             if(rName.equals(name)) return child;
143         }
144         return null;
145     }
146     
147     protected Variable resolvePossibleChild(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
148                 Layer0 L0 = Layer0.getInstance(graph);
149         for(Variable child : browseChildren(graph)) {
150                 GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);
151                 if(id != null) {
152                         if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)
153                                 return child;
154                 }
155         }
156         return null;
157     }
158
159     protected Variable resolvePossibleProperty(ReadGraph graph, Resource resource) throws DatabaseException {
160         String rName = graph.getRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
161         for(Variable child : browseProperties(graph)) {
162             String name = child.getPossiblePropertyValue(graph, Variables.NAME, Bindings.STRING);
163             if(rName.equals(name)) return child;
164         }
165         return null;
166     }
167
168     protected Variable resolvePossibleProperty(ReadGraph graph, GuidRVIPart part) throws DatabaseException {
169                 Layer0 L0 = Layer0.getInstance(graph);
170         for(Variable child : browseProperties(graph)) {
171                 GUID id = child.getPossiblePropertyValue(graph, L0.identifier, GUID.BINDING);
172                 if(id != null) {
173                         if(id.mostSignificant == part.mostSignificant && id.leastSignificant == part.leastSignificant)
174                                 return child;
175                 }
176         }
177         return null;
178     }
179
180     public String getLabel(ReadGraph graph) throws DatabaseException { 
181         return getName(graph);
182     }
183     
184     public String getPossibleLabel(ReadGraph graph) throws DatabaseException {
185         Resource represents = getPossibleRepresents(graph);
186         if(represents == null) return null;
187                 return graph.getPossibleRelatedValue2(represents, graph.getService(Layer0.class).HasLabel, getParent(graph), Bindings.STRING);
188     }
189
190     public Resource getType(ReadGraph graph) throws DatabaseException {
191         
192         Resource resource = getPossibleRepresents(graph);
193         if(resource == null) {
194                 String uri = getPossiblePropertyValue(graph, "typeURI");
195                 if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());
196             throw new DatabaseException("No type for " + getURI(graph));
197         }
198         return graph.getSingleType(resource);
199         
200     }
201     
202     public RVIPart getRVIPart(ReadGraph graph) throws DatabaseException {
203         throw new UnsupportedOperationException();
204     }
205
206     @Override
207     public RVI getRVI(ReadGraph graph) throws DatabaseException {
208         Databoard databoard = graph.getService( Databoard.class );
209         Binding rviBinding = databoard.getBindingUnchecked( RVI.class );
210         if(Variables.isContext(graph, this)) {
211                 return RVI.empty( rviBinding );
212         } else {
213                 Variable parent = getParent(graph);
214                 if (parent == null)
215                         // TODO: consider using a more suitable exception here to better convey the situation.
216                         throw new MissingVariableException("no parent for variable " + this + " (URI=" + getPossibleURI(graph) + ")");
217                 RVI base = graph.syncRequest(new VariableRVIRequest(parent));
218                 RVIPart part = getRVIPart(graph);
219                 return new RVIBuilder(base).append(part).toRVI();
220         }
221     }
222     
223     protected Variable getDomainProperty(ReadGraph graph, String name) throws DatabaseException {
224         Variable property = getPossibleDomainProperty(graph, name);
225         if(property == null)
226             throw new MissingVariableException(getIdentifier() + ": Didn't find property " + name + ".");
227         return property;
228     }
229     
230     protected void addProperty(Map<String, Variable> properties, String name, Object value, Binding binding) {
231         if(value != null) {
232             properties.put(name, new ConstantPropertyVariable(this, name, value, binding));
233         }
234     }
235
236     protected Variable getNameVariable(ReadGraph graph) throws DatabaseException {
237         return new ConstantPropertyVariable(this, Variables.NAME, getName(graph), Bindings.STRING);
238     }
239
240     protected Variable getLabelVariable(ReadGraph graph) throws DatabaseException {
241         return new ConstantPropertyVariable(this, Variables.LABEL, getPossibleLabel(graph), Bindings.STRING);
242     }
243
244     final public Collection<Variable> browseProperties(ReadGraph graph) throws DatabaseException {
245         return getProperties(graph);
246     }
247
248     private Map<String, Variable> getPropertyMap(ReadGraph graph, String classification) throws DatabaseException {
249         return collectDomainProperties(graph, classification, null);
250     }
251
252     final static class PropertyMap extends THashMap<String,Variable> {
253         
254         final private Variable variable;
255         
256         PropertyMap(Variable variable) {
257                 this.variable = variable;
258         }
259
260         private Variable getTypeVariable() {
261                 
262                 return new AbstractConstantPropertyVariable(variable, Variables.TYPE, null) {
263
264                                 @SuppressWarnings("unchecked")
265                                 @Override
266                                 public <T> T getValue(ReadGraph graph) throws DatabaseException {
267                                         Resource represents = parent.getRepresents(graph);
268                                         if(represents == null) return null;
269                                         return (T)graph.getPossibleType(represents, Layer0.getInstance(graph).Entity);
270                                 }
271
272                                 @Override
273                                 public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
274                                         return getValue(graph);
275                                 }
276                         
277                 };
278         }
279
280         private Variable getURIVariable() {
281                 
282                 return new AbstractConstantPropertyVariable(variable, Variables.URI, null) {
283
284                                 @SuppressWarnings("unchecked")
285                                 @Override
286                                 public <T> T getValue(ReadGraph graph) throws DatabaseException {
287                                         return (T)variable.getURI(graph);
288                                 }
289
290                                 @Override
291                                 public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
292                                         return getValue(graph);
293                                 }
294                         
295                 };
296         }
297
298         public Variable get(Object key) {
299                 Variable result = super.get(key);
300                 if(result != null) return result;
301                 
302                 if(Variables.TYPE.equals(key)) return getTypeVariable();
303                 else if(Variables.URI.equals(key)) return getURIVariable();
304                 
305                         return variable;
306                 }
307         
308     }
309     
310     private Map<String, Variable> getPropertyMap(ReadGraph graph) throws DatabaseException {
311         PropertyMap properties = new PropertyMap(this);
312 //        Map<String, Variable> properties = new HashMap<String, Variable>();
313 //        try {
314 //            properties.put(Variables.NAME, getNameVariable(graph));
315 //        } catch (DatabaseException e) {
316 //            // A variable that has no name doesn't exist by definition.
317 //            // Therefore it can't have any properties.
318 //            return Collections.emptyMap();
319 //        }
320 //        try {
321 //            Variable labelVariable = getLabelVariable(graph);
322 //            if(labelVariable != null) properties.put(Variables.LABEL, getLabelVariable(graph));
323 //        } catch (DatabaseException e) {
324 //            // Label not absolutely mandatory.
325 //        }
326 //        addProperty(properties, Variables.TYPE, getPossibleType(graph), null);
327 //        addProperty(properties, Variables.URI, getPossibleURI(graph), Bindings.STRING);
328         //addProperty(properties, Variables.SERIALISED, getSerialized(graph), Bindings.STRING);
329         //addProperty(properties, Variables.PARENT, getParent(graph), null);
330 //        addProperty(properties, Variables.ROLE, getRole(graph), Bindings.STRING);
331 //        addProperty(properties, Variables.REPRESENTS, getPossibleRepresents(graph), null);
332         collectExtraProperties(graph, properties);
333         collectDomainProperties(graph, properties);
334         return properties;
335     }
336     
337     @Override
338     public Collection<Variable> getProperties(ReadGraph graph) throws DatabaseException {
339         return getPropertyMap(graph).values();
340     }
341     
342     public Collection<Variable> getProperties(ReadGraph graph, String classification) throws DatabaseException {
343         Map<String,Variable> propertyMap = getPropertyMap(graph, classification);
344         if(propertyMap == null) return Collections.emptyList();
345         else return propertyMap.values();
346     }
347
348     @Override
349     public Collection<Variable> getProperties(ReadGraph graph, Resource property) throws DatabaseException {
350         return getProperties(graph, uri(graph, property));
351     }
352
353     final public Collection<Variable> browseChildren(ReadGraph graph) throws DatabaseException {
354         return getChildren(graph);
355     }
356     
357     @Override
358     public Variable getPossibleProperty(ReadGraph graph, String name)
359             throws DatabaseException {
360         if(Variables.NAME.equals(name)) {
361                 return getNameVariable(graph);
362         }
363         if(Variables.LABEL.equals(name)) {
364                 return getLabelVariable(graph);
365         }
366         if(Variables.TYPE.equals(name)) {
367             Object value = getPossibleType(graph);
368             if(value != null)
369                 return new ConstantPropertyVariable(this, name, value, null);
370         }
371         if(Variables.URI.equals(name)) {
372             // TODO: getPossibleURI or getURI?
373             Object value = getURI(graph);
374             if(value != null)
375                 return new ConstantPropertyVariable(this, name, value, Bindings.STRING);
376         }
377 //        if(Variables.SERIALISED.equals(name)) {
378 //            Object value = getSerialized(graph);
379 //            if(value != null)
380 //                return new ConstantPropertyVariable(this, name, value, Bindings.STRING);
381 //        }
382         /*if(Variables.PARENT.equals(name)) {
383             Object value = getParent(graph);
384             if(value != null)
385                 return new ConstantPropertyVariable(this, name, value, null);
386         }*/
387 //        if(Variables.ROLE.equals(name)) {
388 //            Object value = getRole(graph);
389 //            if(value != null)
390 //                return new ConstantPropertyVariable(this, name, value, null);
391 //        }
392 //        if(Variables.REPRESENTS.equals(name)) {
393 //            Object value = getRepresents(graph);
394 //            if(value != null)
395 //                return new ConstantPropertyVariable(this, name, value, null);
396 //        }
397         Variable extra = getPossibleExtraProperty(graph, name);
398         if(extra != null) return extra;
399         return getPossibleDomainProperty(graph, name);
400     }
401     
402     @Override
403     public Variable getPossibleProperty(ReadGraph graph, Resource property) throws DatabaseException {
404         return getPossibleProperty(graph, name(graph, property));
405     }
406
407     @SuppressWarnings("unchecked")
408     protected <T> T checkNull(ReadGraph graph, Object value) throws DatabaseException {
409         if(value == null)
410             throw new MissingVariableValueException(getClass().getSimpleName() + ": Didn't find value for " + getPossibleURI(graph));
411         return (T)value;
412     }
413
414     private String name(ReadGraph graph, Resource property) throws DatabaseException {
415         return graph.getRelatedValue(property, Layer0.getInstance(graph).HasName, Bindings.STRING);
416     }
417     
418     private String uri(ReadGraph graph, Resource property) throws DatabaseException {
419         return graph.getURI(property);
420     }
421
422     @Override
423     public <T> T getPropertyValue(ReadGraph graph, String name) throws DatabaseException {
424         if(Variables.LABEL.equals(name)) return checkNull(graph, getLabel(graph));
425         Variable result = getDomainProperty(graph, name);
426         if(result != null) return result.getValue(graph);
427         if(Variables.NAME.equals(name)) return checkNull(graph, getName(graph));
428         if(Variables.TYPE.equals(name)) return checkNull(graph, getPossibleType(graph));
429         if(Variables.URI.equals(name)) return checkNull(graph, getURI(graph));
430 //      if(Variables.SERIALISED.equals(name)) return checkNull(graph, getSerialized(graph));
431 //      if(Variables.ROLE.equals(name)) return checkNull(graph, getRole(graph));
432 //      if(Variables.REPRESENTS.equals(name)) return checkNull(graph, getRepresents(graph));
433         Variable extra = getPossibleExtraProperty(graph, name);
434         if(extra != null) return extra.getValue(graph);
435         return null;
436     }    
437     
438     @Override
439     public <T> T getPropertyValue(ReadGraph graph, Resource property) throws DatabaseException {
440         return getPropertyValue(graph, name(graph, property));
441     }
442     
443     @SuppressWarnings("unchecked")
444     @Override
445     public <T> T getPossiblePropertyValue(ReadGraph graph, String name)
446             throws DatabaseException {
447         
448         Variable property = getPossibleDomainProperty(graph, name);
449         if(property != null) return property.getPossibleValue(graph);
450         
451         if(Variables.NAME.equals(name)) return (T)getName(graph);
452         if(Variables.LABEL.equals(name)) return (T)getLabel(graph);
453         if(Variables.TYPE.equals(name)) return (T)getPossibleType(graph);
454         if(Variables.URI.equals(name)) return (T)getURI(graph);
455 //        if(Variables.SERIALISED.equals(name)) return (T)getSerialized(graph);
456 //        if(Variables.ROLE.equals(name)) return (T)getRole(graph);
457 //        if(Variables.REPRESENTS.equals(name)) return (T)getRepresents(graph);
458         
459         Variable extra = getPossibleExtraProperty(graph, name);
460         if(extra != null) return extra.getPossibleValue(graph);
461         
462         return null;
463         
464     }
465
466     @Override
467     public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property) throws DatabaseException {
468         return getPossiblePropertyValue(graph, name(graph, property));
469     }
470
471     @SuppressWarnings("unchecked")
472     @Override
473     public <T> T getPropertyValue(ReadGraph graph, String name, Binding binding)
474             throws DatabaseException {
475         if(binding instanceof StringBinding) {
476             StringBinding sb = (StringBinding)binding;
477             try {
478                 if(Variables.NAME.equals(name)) return (T)sb.create((String)checkNull(graph, getName(graph)));
479                 if(Variables.LABEL.equals(name)) return (T)sb.create((String)checkNull(graph, getLabel(graph)));
480                 if(Variables.URI.equals(name)) return (T)sb.create((String)checkNull(graph, getURI(graph)));
481 //                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)checkNull(graph, getSerialized(graph)));
482             } catch(BindingException e) {
483                 throw new DatabaseException(e);
484             }
485         }
486         Variable property = getPossibleExtraProperty(graph, name);
487         if(property != null)
488             return property.getValue(graph, binding);
489         property = getPossibleDomainProperty(graph, name);
490         if(property == null)
491             throw new MissingVariableException("Didn't find property " + name + " for " + this + ".");
492         return property.getValue(graph, binding);
493     }
494
495     @Override
496     public <T> T getPropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {
497         return getPropertyValue(graph, name(graph, property), binding);
498     }
499
500     @SuppressWarnings("unchecked")
501     @Override
502     public <T> T getPossiblePropertyValue(ReadGraph graph, String name,
503             Binding binding) throws DatabaseException {
504         if(binding instanceof StringBinding) {
505             StringBinding sb = (StringBinding)binding;
506             try {
507                 if(Variables.NAME.equals(name)) return (T)sb.create((String)getName(graph));
508                 if(Variables.LABEL.equals(name)) return (T)sb.create((String)getLabel(graph));
509                 if(Variables.URI.equals(name)) return (T)sb.create((String)getURI(graph));
510 //                if(Variables.SERIALISED.equals(name)) return (T)sb.create((String)getSerialized(graph));
511             } catch(BindingException e) {
512                 throw new DatabaseException(e);
513             }
514         }
515         Variable property = getPossibleExtraProperty(graph, name);
516         if(property != null)
517             return property.getPossibleValue(graph, binding);
518         property = getPossibleDomainProperty(graph, name);
519         if(property == null)
520             return null;
521         return property.getPossibleValue(graph, binding);
522     }
523
524     @Override
525     public <T> T getPossiblePropertyValue(ReadGraph graph, Resource property, Binding binding) throws DatabaseException {
526         return getPossiblePropertyValue(graph, name(graph, property), binding);
527     }
528
529     @Override
530     public void setValue(WriteGraph graph, Object value) throws DatabaseException {
531         try {
532                         setValue(graph, value, Bindings.getBinding(value.getClass()));
533                 } catch (BindingConstructionException e) {
534                         throw new DatabaseException(e);
535                 }
536     }
537     
538     @Override
539     public void setPropertyValue(WriteGraph graph, String name, Object value) throws DatabaseException {
540         getProperty(graph, name).setValue(graph, value);
541     }
542
543     @Override
544     public void setPropertyValue(WriteGraph graph, Resource property, Object value) throws DatabaseException {
545         setPropertyValue(graph, name(graph, property), value);
546     }
547
548     @Override
549     public void setPropertyValue(WriteGraph graph, String name, Object value,
550             Binding binding) throws DatabaseException {
551         getProperty(graph, name).setValue(graph, value, binding);
552     }
553
554     @Override
555     public void setPropertyValue(WriteGraph graph, Resource property, Object value, Binding binding) throws DatabaseException {
556         setPropertyValue(graph, name(graph, property), value, binding);
557     }
558
559     @Override
560     public Variable getChild(ReadGraph graph, String name)
561             throws DatabaseException {
562         Variable child = getPossibleChild(graph, name);
563         if(child == null)
564             throw new MissingVariableException(getURI(graph) + ": didn't find child " + name + " for " + getIdentifier() + ".");
565         return child;
566     }    
567
568     @Override
569     public Variable getProperty(ReadGraph graph, String name)  throws DatabaseException {
570         Variable result = getPossibleProperty(graph, name);
571         if(result == null)
572             throw new MissingVariableException(getClass().getSimpleName() + ": Didn't find property " + name + " for " + getPossibleURI(graph) + ".");
573         return result;
574     }
575     
576     @Override
577     public Variable getProperty(ReadGraph graph, Resource property) throws DatabaseException {
578         return getProperty(graph, name(graph, property));
579     }
580
581     @Override
582     public Variable browse(ReadGraph graph, String suffix)
583             throws DatabaseException {
584         
585         if(suffix.isEmpty()) 
586             return this;        
587         switch(suffix.charAt(0)) {
588         case '.': {
589             Variable parent = getParent(graph); 
590             if(parent == null)
591                 throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
592             return parent.browse(graph, suffix.substring(1));
593         }
594         case '#': {
595             int segmentEnd = getSegmentEnd(suffix);
596             Variable property = getProperty(graph, 
597                     decodeString(suffix.substring(1, segmentEnd)));
598             if(property == null) 
599                 throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
600             return property.browse(graph, suffix.substring(segmentEnd));
601         }
602         case '/': {
603             int segmentEnd = getSegmentEnd(suffix);
604             Variable child = getChild(graph, 
605                     decodeString(suffix.substring(1, segmentEnd)));
606             if(child == null) 
607                 throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
608             return child.browse(graph, suffix.substring(segmentEnd));
609         }
610         default:
611             throw new MissingVariableException("Didn't find " + suffix + " for " + this + " (" + getPossibleURI(graph) + ").");
612         }
613         
614     }
615
616     private static int getSegmentEnd(String suffix) {
617         int pos;
618         for(pos=1;pos<suffix.length();++pos) {
619             char c = suffix.charAt(pos);
620             if(c == '/' || c == '#')
621                 break;
622         }
623         return pos;
624     }
625     
626     @Override
627     public Variable browsePossible(ReadGraph graph, String suffix)
628             throws DatabaseException {
629         if(suffix.isEmpty()) 
630             return this;        
631         switch(suffix.charAt(0)) {
632         case '.': {
633             Variable parent = getParent(graph); 
634             if(parent == null)
635                 return null;
636             return parent.browsePossible(graph, suffix.substring(1));
637         }
638         case '#': {
639             int segmentEnd = getSegmentEnd(suffix);
640             Variable property = getPossibleProperty(graph, 
641                     decodeString(suffix.substring(1, segmentEnd)));
642             if(property == null) 
643                 return null;
644             return property.browsePossible(graph, suffix.substring(segmentEnd));
645         }
646         case '/': {
647             int segmentEnd = getSegmentEnd(suffix);
648             Variable child = getPossibleChild(graph, 
649                     decodeString(suffix.substring(1, segmentEnd)));
650             if(child == null) 
651                 return null;
652             return child.browsePossible(graph, suffix.substring(segmentEnd));
653         }
654         default:
655             return null;
656         }
657     }
658
659     @Override
660     public Variable browse(ReadGraph graph, Resource config)
661             throws DatabaseException {
662         Variable variable = browsePossible(graph, config);
663         if(variable == null)
664             throw new MissingVariableException("Didn't find a variable related to " + 
665                     NameUtils.getSafeName(graph, config) + ".");
666         return variable;
667     }
668
669     @Override
670     public Variable browsePossible(ReadGraph graph, Resource config)
671             throws DatabaseException {
672         Layer0 l0 = Layer0.getInstance(graph);
673         String name = (String)graph.getPossibleRelatedValue(config, l0.HasName, Bindings.STRING);
674         if (name == null)
675             return null;
676         return getPossibleChild(graph, name);
677     }
678
679     @Override
680     public <T> T getInterface(ReadGraph graph, Class<T> clazz)
681             throws DatabaseException {
682         return null;
683     }
684
685     @Override
686     public String getURI(ReadGraph graph) throws DatabaseException {
687         validate(graph);
688         Variable parent = getParent(graph);
689         if (parent == null)
690             throw new InvalidVariableException(this + " has no URI");
691         return parent.getURI(graph) + getRole(graph).getIdentifier() + encodeString(getName(graph));
692     }
693
694     /**
695      * For debug messages.
696      * 
697      * @param graph
698      * @return
699      * @throws DatabaseException
700      */
701     public String getPossibleURI(ReadGraph graph) throws DatabaseException {
702         Variable parent = getParent(graph);
703         if (parent == null)
704             return null;
705         if (parent instanceof AbstractVariable) {
706             String parentUri = ((AbstractVariable) parent).getPossibleURI(graph);
707             if (parentUri == null)
708                 return null;
709             return parentUri + getRole(graph).getIdentifier() + encodeString(getName(graph));
710         }
711         return null;
712     }
713
714     public <T> T getPossibleValue(ReadGraph graph) throws DatabaseException {
715         try {
716             return getValue(graph);
717         } catch(DatabaseException e) {
718             return null;
719         }
720     }
721
722     @Override
723     public Variant getVariantValue(ReadGraph graph) throws DatabaseException {
724         Binding binding = getPossibleDefaultBinding(graph);
725         if(binding != null) {
726                 Object value = getValue(graph, binding);
727                 return new Variant(binding, value);
728         } else {
729 //              System.err.println("no data type for " + getURI(graph));
730                 // TODO: hackish, consider doing something else here?
731                 Object value = getValue(graph);
732                 try {
733                                 binding = Bindings.OBJECT.getContentBinding(value);
734                         } catch (BindingException e) {
735                                 throw new DatabaseException(e);
736                         }
737                 return new Variant(binding, value);
738         }
739     }
740     
741     public Variant getPossibleVariantValue(ReadGraph graph) throws DatabaseException {
742         Binding binding = getPossibleDefaultBinding(graph);
743         if(binding != null) {
744                 Object value = getPossibleValue(graph, binding);
745                 if(value == null) return null;
746                 return new Variant(binding, value);
747         } else {
748                 Object value = getPossibleValue(graph);
749                 if(value == null) return null;
750                 try {
751                         // TODO: hackish, consider doing something else here?
752                         binding = value != null ? Bindings.getBinding(value.getClass()) : null;
753                         return new Variant(binding, value);
754                 } catch (BindingConstructionException e) {
755                         return null;
756                 }
757         }
758     }
759
760     public <T> T getPossibleValue(ReadGraph graph, Binding binding) throws DatabaseException {
761         try {
762             return getValue(graph, binding);
763         } catch(MissingVariableValueException e) {
764             return null;
765         }
766     }
767
768     public <T> T adapt(ReadGraph graph, Class<T> clazz) throws DatabaseException {
769         throw new AdaptionException(this + " does not support adaption to " + clazz);
770     }
771     
772     @Override
773     public <T> T adaptPossible(ReadGraph graph, Class<T> clazz) throws DatabaseException {
774         try {
775                 return adapt(graph, clazz);
776         } catch (AdaptionException e) {
777                 return null;
778         }
779     }
780     
781     
782     public static String encodeString(String string) throws DatabaseException {
783         if (string == null || "".equals(string)) return string;
784         return URIStringUtils.escape(string);
785     }
786     
787     public static String decodeString(String string) throws DatabaseException {
788         return URIStringUtils.unescape(string);
789     }
790     
791
792         protected Variable getPossiblePropertyFromContext(ReadGraph graph, Resource context, String name) throws DatabaseException {
793
794                 Map<String, Resource> predicates = graph.syncRequest(new PropertyMapOfResource(context));
795                 Resource property = predicates.get(name);
796                 if(property == null) return null;
797                 Resource object = graph.getSingleObject(context, property);
798                 Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
799                                 ModelledVariablePropertyDescriptor.class, Variable.class);
800                 if(objectAdapter != null) return objectAdapter;
801                 return graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
802                                 ModelledVariablePropertyDescriptor.class, Variable.class);
803                 
804         }
805         
806         protected Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Resource context, Map<String, Variable> properties) throws DatabaseException {
807
808                 for(Map.Entry<String, Resource> entry : graph.syncRequest(new PropertyMapOfResource(context)).entrySet()) {
809                         String name = entry.getKey();
810                         Resource property = entry.getValue();
811                         Resource object = graph.getSingleObject(context, property);
812                         Variable objectAdapter = graph.getPossibleContextualAdapter(object, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
813                                         ModelledVariablePropertyDescriptor.class, Variable.class);
814                         if(objectAdapter != null) {
815                                 if(objectAdapter != null) {
816                                     if(properties == null) properties = new HashMap<String,Variable>();
817                                     properties.put(name, objectAdapter);
818                                 }
819                         } else {
820                                 Variable predicateAdapter = graph.getPossibleContextualAdapter(property, new ModelledVariablePropertyDescriptorImpl(this, context, property), 
821                                                 ModelledVariablePropertyDescriptor.class, Variable.class);
822                                 if(predicateAdapter != null) {
823                     if(properties == null) properties = new HashMap<String,Variable>();
824                                     properties.put(name, predicateAdapter);
825                                 }
826                         }
827
828                 }
829                 
830                 return properties;
831                 
832         }
833         
834         @Override
835         public Variable resolve(ReadGraph graph, RVIPart part) throws DatabaseException {
836                 if(part instanceof StringRVIPart) {
837                         StringRVIPart srp = (StringRVIPart)part;
838                         if(Role.CHILD.equals(srp.getRole())) return getChild(graph, srp.string);
839                         else if(Role.PROPERTY.equals(srp.getRole())) return getProperty(graph, srp.string);
840                 } else if(part instanceof ResourceRVIPart) {
841                         ResourceRVIPart rrp = (ResourceRVIPart)part;
842                         if(Role.CHILD.equals(rrp.getRole())) return resolveChild(graph, rrp.resource);
843                         else if(Role.PROPERTY.equals(rrp.getRole())) return resolveProperty(graph, rrp.resource);
844                 } else if(part instanceof GuidRVIPart) {
845                         GuidRVIPart grp = (GuidRVIPart)part;
846                         if(Role.CHILD.equals(grp.getRole())) return resolveChild(graph, grp);
847                         else if(Role.PROPERTY.equals(grp.getRole())) return resolveProperty(graph, grp);
848                 }
849                 throw new DatabaseException("Unrecognized RVIPart: " + part);
850         }
851
852         @Override
853         public Variable resolvePossible(ReadGraph graph, RVIPart part) throws DatabaseException {
854                 if(part instanceof StringRVIPart) {
855                         StringRVIPart srp = (StringRVIPart)part;
856                         if(Role.CHILD.equals(srp.getRole())) return getPossibleChild(graph, srp.string);
857                         else if(Role.PROPERTY.equals(srp.getRole())) return getPossibleProperty(graph, srp.string);
858                 } else if(part instanceof ResourceRVIPart) {
859                         ResourceRVIPart rrp = (ResourceRVIPart)part;
860                         if(Role.CHILD.equals(rrp.getRole())) return resolvePossibleChild(graph, rrp.resource);
861                         else if(Role.PROPERTY.equals(rrp.getRole())) return resolvePossibleProperty(graph, rrp.resource);
862                 } else if(part instanceof GuidRVIPart) {
863                         GuidRVIPart grp = (GuidRVIPart)part;
864                         if(Role.CHILD.equals(grp.getRole())) return resolvePossibleChild(graph, grp);
865                         else if(Role.PROPERTY.equals(grp.getRole())) return resolvePossibleProperty(graph, grp);
866                 }
867                 throw new DatabaseException("Unrecognized RVIPart: " + part);
868         }
869
870         @Override
871         public Datatype getDatatype(ReadGraph graph) throws DatabaseException {
872                 throw new DatabaseException("No data type.");
873         }
874
875         public Binding getDefaultBinding(ReadGraph graph) throws DatabaseException {
876                 Datatype type = getDatatype(graph);
877                 return Bindings.getBinding(type);
878         }
879         
880         public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {
881         try {
882             return getDefaultBinding(graph);
883         } catch(DatabaseException e) {
884             return null;
885         }
886         }
887
888         @Override
889         public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {
890         try {
891             return getDatatype(graph);
892         } catch(DatabaseException e) {
893             return null;
894         }
895         }
896         
897 //      public Binding getPossibleDefaultBinding(ReadGraph graph) throws DatabaseException {
898 //              
899 //              Datatype type = getPossibleDatatype(graph);
900 //              if(type == null) return null;
901 //              return Bindings.getBinding(type);
902 //
903 //      }
904 //
905 //      @Override
906 //      public Datatype getPossibleDatatype(ReadGraph graph) throws DatabaseException {
907 //              
908 //              Variant vt = getVariantValue(graph);
909 //              if(vt == null) return null;
910 //              Binding binding = vt.getBinding();
911 //              if(binding == null) return null;
912 //              return binding.type();
913 //
914 //      }
915
916         @Override
917         public Variable getPredicate(ReadGraph graph) throws DatabaseException {
918                 throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));
919         }
920
921         @Override
922         public Variable getPossiblePredicate(ReadGraph graph) throws DatabaseException {
923                 try {
924             return getPredicate(graph);
925         } catch(DatabaseException e) {
926             return null;
927         }
928         }
929         
930         @Override
931         public Resource getPredicateResource(ReadGraph graph) throws DatabaseException {
932                 Variable predicate = getPredicate(graph);
933                 if(predicate == null) throw new DatabaseException(getClass().getSimpleName() + ": No predicate property for " + getPossibleURI(graph));
934                 return predicate.getRepresents(graph);
935         }
936         
937         @Override
938         public Resource getPossiblePredicateResource(ReadGraph graph) throws DatabaseException {
939                 Variable predicate = getPossiblePredicate(graph);
940                 if(predicate == null) return null;
941                 else return predicate.getPossibleRepresents(graph);
942         }
943
944         @Override
945         public Resource getPossibleRepresents(ReadGraph graph) throws DatabaseException {
946         try {
947             return getRepresents(graph);
948         } catch(DatabaseException e) {
949             return null;
950         }
951         }
952
953         @Override
954         public Resource getPossibleType(ReadGraph graph) throws DatabaseException {
955         Resource resource = getPossibleRepresents(graph);
956         if(resource == null) {
957                 String uri = getPossiblePropertyValue(graph, "typeURI");
958                 if(uri != null) return graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());
959             return null;
960         }
961         return graph.getPossibleObject(resource, Layer0.getInstance(graph).InstanceOf);
962         }
963
964     public Resource getType(ReadGraph graph, Resource baseType) throws DatabaseException {
965         Resource resource = getPossibleRepresents(graph);
966         if(resource == null) {
967                 String uri = getPossiblePropertyValue(graph, "typeURI");
968                 if(uri != null) return graph.syncRequest(new org.simantics.db.common.primitiverequest.Resource(uri), TransientCacheAsyncListener.<Resource>instance());
969             throw new DatabaseException("No type for " + getURI(graph));
970         }
971         return graph.getSingleType(resource, baseType);
972     }
973
974         @Override
975         public Resource getPossibleType(ReadGraph graph, Resource baseType) throws DatabaseException {
976         Resource resource = getPossibleRepresents(graph);
977         if(resource == null) {
978                 String uri = getPossiblePropertyValue(graph, "typeURI");
979                 if(uri != null) {
980                         Resource type = graph.syncRequest(new PossibleResource(uri), TransientCacheAsyncListener.<Resource>instance());
981                         if(type == null) return null;
982                         if(graph.isInheritedFrom(type, baseType)) return type;
983                         else return null;
984                 }
985             return null;
986         }
987         return graph.getPossibleType(resource, baseType);
988         }
989
990     public Map<String, Variable> collectDomainProperties(ReadGraph graph, String classification, Map<String, Variable> map) throws DatabaseException {
991         Map<String,Variable> all = collectDomainProperties(graph, null);
992         for(Map.Entry<String, Variable> entry : all.entrySet()) {
993                 Set<String> classifications = entry.getValue().getClassifications(graph);
994                 if(classifications.contains(classification)) {
995                         if(map == null) map = new HashMap<String,Variable>();
996                         map.put(entry.getKey(), entry.getValue());
997                 }
998         }
999         return map;
1000     }
1001     
1002     @Override
1003     public RVI getPossibleRVI(ReadGraph graph) throws DatabaseException {
1004         try {
1005             return getRVI(graph);
1006         } catch(DatabaseException e) {
1007             return null;
1008         }
1009     }
1010
1011 }