]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/function/All.java
Issues-view menu improvements & Variable-based issue context resolution
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / function / All.java
1 package org.simantics.db.layer0.function;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.concurrent.atomic.AtomicReference;
10
11 import org.simantics.databoard.Bindings;
12 import org.simantics.databoard.accessor.reference.ChildReference;
13 import org.simantics.databoard.accessor.reference.IndexReference;
14 import org.simantics.databoard.adapter.AdaptException;
15 import org.simantics.databoard.binding.Binding;
16 import org.simantics.databoard.binding.error.BindingConstructionException;
17 import org.simantics.databoard.binding.error.BindingException;
18 import org.simantics.databoard.binding.mutable.Variant;
19 import org.simantics.databoard.type.ArrayType;
20 import org.simantics.databoard.type.Datatype;
21 import org.simantics.databoard.type.NumberType;
22 import org.simantics.databoard.util.Range;
23 import org.simantics.db.Issue;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.Statement;
27 import org.simantics.db.WriteGraph;
28 import org.simantics.db.common.issue.StandardIssue;
29 import org.simantics.db.common.primitiverequest.PossibleRelatedValueImplied2;
30 import org.simantics.db.common.primitiverequest.PossibleResource;
31 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
32 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
33 import org.simantics.db.common.request.IsEnumeratedValue;
34 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
35 import org.simantics.db.common.utils.CommonDBUtils;
36 import org.simantics.db.common.utils.Functions;
37 import org.simantics.db.common.utils.ListUtils;
38 import org.simantics.db.common.utils.Logger;
39 import org.simantics.db.common.utils.NameUtils;
40 import org.simantics.db.common.validation.L0Validations;
41 import org.simantics.db.exception.DatabaseException;
42 import org.simantics.db.exception.DoesNotContainValueException;
43 import org.simantics.db.exception.NoSingleResultException;
44 import org.simantics.db.layer0.exception.MissingVariableValueException;
45 import org.simantics.db.layer0.exception.PendingVariableException;
46 import org.simantics.db.layer0.exception.VariableException;
47 import org.simantics.db.layer0.request.PossibleURI;
48 import org.simantics.db.layer0.request.PropertyInfo;
49 import org.simantics.db.layer0.request.PropertyInfoRequest;
50 import org.simantics.db.layer0.request.UnescapedAssertedPropertyMapOfResource;
51 import org.simantics.db.layer0.request.UnescapedPropertyMapOfResource;
52 import org.simantics.db.layer0.scl.CompileResourceValueRequest;
53 import org.simantics.db.layer0.scl.CompileValueRequest;
54 import org.simantics.db.layer0.util.Layer0Utils;
55 import org.simantics.db.layer0.util.PrimitiveValueParser;
56 import org.simantics.db.layer0.variable.AbstractVariable;
57 import org.simantics.db.layer0.variable.ChildVariableMapRequest;
58 import org.simantics.db.layer0.variable.ExternalSetValue;
59 import org.simantics.db.layer0.variable.PropertyVariableMapRequest;
60 import org.simantics.db.layer0.variable.StandardAssertedGraphPropertyVariable;
61 import org.simantics.db.layer0.variable.StandardComposedProperty;
62 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
63 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
64 import org.simantics.db.layer0.variable.SubliteralPropertyVariable;
65 import org.simantics.db.layer0.variable.SubliteralPropertyVariableDeprecated;
66 import org.simantics.db.layer0.variable.ValueAccessor;
67 import org.simantics.db.layer0.variable.Variable;
68 import org.simantics.db.layer0.variable.VariableBuilder;
69 import org.simantics.db.layer0.variable.VariableMap;
70 import org.simantics.db.layer0.variable.VariableMapImpl;
71 import org.simantics.db.layer0.variable.VariableNode;
72 import org.simantics.db.layer0.variable.VariableNodeReadRunnable;
73 import org.simantics.db.layer0.variable.VariableUtils;
74 import org.simantics.db.layer0.variable.Variables;
75 import org.simantics.db.layer0.variable.Variables.NodeStructure;
76 import org.simantics.db.service.ClusteringSupport;
77 import org.simantics.db.service.UndoRedoSupport;
78 import org.simantics.issues.ontology.IssueResource;
79 import org.simantics.layer0.Layer0;
80 import org.simantics.scl.reflection.annotations.SCLValue;
81 import org.simantics.scl.runtime.function.Function4;
82 import org.simantics.scl.runtime.function.FunctionImpl1;
83 import org.simantics.simulator.variable.exceptions.NodeManagerException;
84 import org.simantics.utils.Development;
85 import org.simantics.utils.datastructures.Pair;
86 import org.simantics.utils.strings.StringInputValidator;
87
88 import gnu.trove.map.hash.THashMap;
89 import gnu.trove.set.hash.THashSet;
90
91 public class All {
92
93         public static Object standardGetValue1(ReadGraph graph, Variable context) throws DatabaseException {
94
95                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
96
97         // First from node
98         if(variable.node != null) {
99             Variant value = Variables.requestNodeValue(graph, variable.node);
100             if(Variables.PENDING_NODE_VALUE == value) throw new PendingVariableException("");
101             return value.getValue();
102         }
103                 
104                 try {
105
106             if(variable.property.hasEnumerationRange) {
107                         Resource object = variable.getRepresents(graph);
108                                 if(graph.sync(new IsEnumeratedValue(object))) {
109                             Layer0 L0 = Layer0.getInstance(graph);
110                                         if(graph.isInstanceOf(object, L0.Literal)) {
111                                                 return graph.getValue(object);
112                                         } else {
113                                                 String label = graph.getPossibleRelatedValue2(variable.getRepresents(graph), L0.HasLabel, Bindings.STRING);
114                                                 if(label == null) label = graph.getPossibleRelatedValue(variable.getRepresents(graph), L0.HasName, Bindings.STRING);
115                                                 if(label == null) label = "<no label>";
116                                                 return label;
117                                         }
118                                 }
119             }
120                         
121             if (variable.isAsserted()) {
122                                 if (variable.parentResource != null) {
123                                         Map<String, Pair<PropertyInfo, Resource>> assertions = graph.syncRequest(
124                                                         new UnescapedAssertedPropertyMapOfResource(variable.parentResource),
125                                                         TransientCacheAsyncListener.instance());
126
127                                         // NOTE: This optimization assumes the property
128                                         // variable's representation is the asserted object.
129                                         Resource object = variable.getPossibleRepresents(graph);
130                                         if (object != null) {
131                                                 return graph.getValue2(object, variable);
132                                         } else {
133                                                 for (Pair<PropertyInfo, Resource> assertion : assertions.values()) {
134                                                         if (assertion.first.predicate.equals(variable.property.predicate)) {
135                                                                 return graph.getValue2(assertion.second, variable);
136                                                         }
137                                                 }
138                                         }
139                                 }
140             }
141                 
142                         return graph.getValue2(variable.getRepresents(graph), variable);
143                         
144                 } catch (NoSingleResultException e) {
145                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
146                 } catch (DoesNotContainValueException e) {
147                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
148                 } catch (DatabaseException e) {
149                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
150                 }
151
152         }
153         
154         public static Object standardGetValue2(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {           
155                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
156
157         // First from node
158         if(variable.node != null) {
159             try {
160                 Variant value = Variables.requestNodeValue(graph, variable.node, binding);
161                 if(Variables.PENDING_NODE_VALUE == value) throw new PendingVariableException("");
162                 if(value == null) throw new MissingVariableValueException(variable.getPossibleURI(graph));
163                 return value.getValue(binding);
164             } catch (AdaptException e) {
165                 throw new DatabaseException(e);
166             }
167         }
168                 
169         try {
170                         
171                 if(variable.property.hasEnumerationRange) {
172                 Resource object = variable.getRepresents(graph);
173                 if(graph.sync(new IsEnumeratedValue(object))) {
174                         Layer0 L0 = Layer0.getInstance(graph);
175                         if(graph.isInstanceOf(object, L0.Literal)) {
176                                 return graph.getValue(object, binding);
177                         } else {
178                                 return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel, binding);
179                         }
180                 }
181             }
182                         
183                 if (variable.isAsserted()) {
184                         if (variable.parentResource != null) {
185                                         Map<String, Pair<PropertyInfo, Resource>> assertions = graph.syncRequest(
186                                                         new UnescapedAssertedPropertyMapOfResource(variable.parentResource),
187                                                         TransientCacheAsyncListener.instance());
188
189                                         // NOTE: This optimization assumes the property
190                                         // variable's representation is the asserted object.
191                                         Resource object = variable.getPossibleRepresents(graph);
192                                         if (object != null) {
193                                                 return graph.getValue2(object, variable, binding);
194                                         } else {
195                                                 for (Pair<PropertyInfo, Resource> assertion : assertions.values()) {
196                                                         if (assertion.first.predicate.equals(variable.property.predicate)) {
197                                                                 return graph.getValue2(assertion.second, variable, binding);
198                                                         }
199                                                 }
200                                         }
201                         }
202                 }
203                         
204                         return graph.getValue2(variable.getRepresents(graph), context, binding);
205                         
206                 } catch (NoSingleResultException e) {
207                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
208                 } catch (DoesNotContainValueException e) {
209                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
210                 } catch (DatabaseException e) {
211                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
212                 }
213
214         }
215
216         public static void standardSetValue2(WriteGraph graph, Variable context, final Object value) throws DatabaseException {
217                 
218             if(context instanceof StandardGraphPropertyVariable) {
219
220                 final StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; 
221                 
222                 // First from node
223             if(variable.node != null) {
224
225                 final Binding binding = Layer0Utils.getDefaultBinding(graph, variable);
226
227                 final AtomicReference<Object> oldValueRef = new AtomicReference<Object>();
228                 try {
229                     variable.node.support.manager.getRealm().syncExec(new Runnable() {
230                         @Override
231                         public void run() {
232                             try {
233                                 oldValueRef.set(getNodeValue(variable, binding));
234                                 setNodeValue(variable, value, binding);
235                             } catch (NodeManagerException e) {
236                                 throw new RuntimeException(e);
237                             } catch (BindingException e) {
238                                     throw new RuntimeException(e);
239                                 }
240                         }
241                     });
242                 } catch(RuntimeException e) {
243                     if(e.getCause() instanceof NodeManagerException || e.getCause() instanceof BindingException)
244                         throw new DatabaseException(e.getCause());
245                     else
246                         throw e;
247                 } catch (InterruptedException e) {
248                     throw new DatabaseException(e);
249                 }
250
251                 ExternalSetValue ext = new ExternalSetValue(variable.node.support.manager, variable.node.node,
252                         oldValueRef.get(), value, binding);
253                 graph.getService(UndoRedoSupport.class).addExternalOperation(graph, ext);
254
255                 return;
256             }
257                 
258             }
259             
260                 Function4<WriteGraph, Variable, Object, Object, String> modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
261                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
262                 try {
263                         modifier.apply(graph, context, value, Bindings.getBinding(value.getClass()));
264                 } catch (BindingConstructionException e) {
265                         throw new DatabaseException(e);
266                 }
267
268         }
269
270         public static void standardSetValue3(final WriteGraph graph, Variable context, final Object value, final Binding binding) throws DatabaseException {
271
272         // First from node
273         if(context instanceof StandardGraphPropertyVariable) {
274
275             final StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; 
276             
277             // First from node
278             if(variable.node != null) {
279                 
280                 try {
281                     
282                     variable.node.support.manager.getRealm().syncExec(new Runnable() {
283
284                         @Override
285                         public void run() {
286                             try {
287                                 Object oldValue = getNodeValue(variable, binding);
288                                 setNodeValue(variable, value, binding);
289                                 ExternalSetValue ext = new ExternalSetValue(variable.node.support.manager, variable.node.node, oldValue, value, binding);
290                                 graph.getService(UndoRedoSupport.class).addExternalOperation(graph, ext);
291                             } catch (NodeManagerException | BindingException e) {
292                                 Logger.defaultLogError(e);
293                             }
294                         }
295
296                         
297                     });
298                     
299                     return;
300                     
301                 } catch (InterruptedException e) {
302                     throw new DatabaseException(e);
303                 }
304                 
305             }
306             
307         }
308             
309                 Function4<WriteGraph, Variable, Object, Object, String> modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
310                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
311                 modifier.apply(graph, context, value, binding);
312
313         }
314
315         public static Datatype getDatatypeFromValue(ReadGraph graph, Variable context) throws DatabaseException {
316                 if (context instanceof AbstractVariable) {
317                         Binding defaultBinding = ((AbstractVariable)context).getPossibleDefaultBinding(graph);
318                         if (defaultBinding != null)
319                                 return defaultBinding.type();
320                 }
321                         
322         Variant value = context.getVariantValue(graph);
323         if (value.getBinding() == null)
324                 throw new DatabaseException("No value binding for " + context.getURI(graph));
325         
326         return value.getBinding().type();
327         }
328
329     @SuppressWarnings("rawtypes")
330     private static class DatatypeGetter implements VariableNodeReadRunnable {
331         final VariableNode node;
332         Datatype type;
333         Exception exception;
334
335         public DatatypeGetter(VariableNode node) {
336             this.node = node;
337         }
338
339         @SuppressWarnings("unchecked")
340         @Override
341         public void run() {
342             try {
343                 type = node.support.manager.getDatatype(node.node);
344             } catch (NodeManagerException e) {
345                 exception = e;
346             }
347         }
348         @Override
349         public String toString() {
350             return "DatatypeGetter(" + node.node + ")";
351         }
352     }
353
354     public static Datatype standardGetDatatype(ReadGraph graph, Variable context) throws DatabaseException {
355         if (context instanceof AbstractVariable) {
356                 final AbstractVariable variable = (AbstractVariable)context;
357                 if (variable.node != null) {
358                         try {
359                                 DatatypeGetter request = new DatatypeGetter(variable.node);
360                                 
361                                         variable.node.support.manager.getRealm().syncExec(request);
362                                         
363                                         if (request.exception != null)
364                                                 throw new DatabaseException(request.exception);
365                                         
366                                         return request.type;
367                                 } catch (InterruptedException e) {
368                                 }
369                 }
370         }
371         
372         return getDatatypeFromValue(graph, context);
373         }
374
375 //      @SCLValue(type = "ValueAccessor")
376 //      public static ValueAccessor standardValueAccessor = new ValueAccessor() {
377 //
378 //              @Override
379 //              public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
380 //                      return standardGetValue(graph, (StandardGraphPropertyVariable)context);
381 //              }
382 //
383 //              @Override
384 //              public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
385 //                      return standardGetValue(graph, context, binding);
386 //              }
387 //
388 //              @Override
389 //              public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
390 //                      standardSetValue(graph, context, value);
391 //              }
392 //
393 //              @Override
394 //              public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
395 //                      standardSetValue(graph, context, value, binding);
396 //              }
397 //
398 //              @Override
399 //              public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
400 //                      return standardGetDatatype(graph, context);
401 //              }
402 //              
403 //      };
404         
405         @SCLValue(type = "ValueAccessor")
406         public static ValueAccessor standardValueAccessor = new ValueAccessor() {
407
408                 @Override
409                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
410                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
411                         if(accessor != null) return accessor.getValue(graph, context);
412                         else 
413                                 return standardGetValue1(graph, context);
414                 }
415
416                 @Override
417                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
418                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
419                         if(accessor != null) return accessor.getValue(graph, context, binding);
420                         else 
421                                 return standardGetValue2(graph, context, binding);
422                 }
423
424                 @Override
425                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
426                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
427                         if(accessor != null) accessor.setValue(graph, context, value);
428                         else 
429                                 standardSetValue2(graph, context, value);
430                 }
431
432                 @Override
433                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
434                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
435                         if(accessor != null) accessor.setValue(graph, context, value, binding);
436                         else 
437                                 standardSetValue3(graph, context, value, binding);
438                 }
439
440                 @Override
441                 public Datatype getDatatype(ReadGraph graph, Variable context)
442                                 throws DatabaseException {
443                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
444                         if(accessor != null) return accessor.getDatatype(graph, context);
445                         else 
446                                 return standardGetDatatype(graph, context);
447                 }
448                 
449         };
450
451         public static Variable getStandardChildDomainPropertyVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
452         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
453         PropertyInfo graphProperty = getPossiblePropertyInfoFromContext(graph, variable, variable.resource, name);
454         return getStandardChildDomainPropertyVariable(graph, context, graphProperty, name);
455     }
456
457     public static Resource getPossiblePropertyResource(ReadGraph graph, AbstractVariable parent, Object node) throws DatabaseException {
458         if(parent != null && parent.node != null && parent.node.node != null && parent.node.support != null) {
459             String propertyURI = getPossiblePropertyURI(parent, node);
460             if(propertyURI != null)
461                 return graph.getPossibleResource(propertyURI);
462         }
463         return null;
464     }
465
466         public static Variable getStandardChildDomainPropertyVariable(ReadGraph graph, Variable context, PropertyInfo graphProperty, String name) throws DatabaseException {
467         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
468         Object propertyNode = getPossibleNodeProperty(graph, variable, name, true);
469         if(graphProperty != null && graphProperty.builder != null)
470             return buildPropertyVariable(graph, variable, variable.resource, graphProperty, propertyNode);
471         if(propertyNode != null) {
472             // Fallback: try to ask property resource uri from NodeManager
473             return createStandardGraphPropertyVariable(graph, variable, propertyNode);
474         }
475         // Final fallback: check types corresponding to
476         // node classification(s) and look for asserted
477         // properties from the URIs specified.
478         if (variable.node != null) {
479             try {
480                 @SuppressWarnings("unchecked")
481                 Set<String> classifications = variable.node.support.manager.getClassifications(variable.node.node);
482                 if (!classifications.isEmpty()) {
483                     for (String uri : classifications) {
484                         Resource type = graph.syncRequest(
485                                 new PossibleResource(uri),
486                                 TransientCacheAsyncListener.instance());
487                         if (type == null)
488                             continue;
489                         Map<String, Pair<PropertyInfo, Resource>> pm = graph.syncRequest(
490                                 new UnescapedAssertedPropertyMapOfResource(type),
491                                 TransientCacheAsyncListener.instance());
492                         Pair<PropertyInfo, Resource> pi = pm.get(name);
493                         if (pi != null) {
494                             return new StandardAssertedGraphPropertyVariable(graph, context, null, type, pi.first.predicate, pi.second);
495                         }
496                     }
497                 }
498             } catch(NodeManagerException e) {
499                 throw new DatabaseException(e);
500             }
501         }
502         return null;
503     }
504
505     public static Map<String, Variable> getStandardChildDomainPropertyVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
506         // Get properties with null identification
507         return getStandardChildDomainPropertyVariables(graph, context, null, map);
508     }
509
510     public static Map<String, Variable> getStandardChildDomainPropertyVariables(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
511         
512         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
513         
514         Collection<Object> nodeProperties = getPossibleNodeProperties(graph, variable);
515         if(!nodeProperties.isEmpty()) {
516
517             // Get variables for properties read from the graph
518             Map<String,PropertyInfo> graphProperties = collectPropertyInfosFromContext(graph, variable, variable.resource);
519             
520             Set<String> used = new THashSet<String>(nodeProperties.size());
521             
522             map = ensureVariableMap(map, graphProperties.size() + nodeProperties.size());
523             
524             // Process NodeManager property nodes
525             for(Object nodeProperty : nodeProperties) {
526                 String name = getNodeName(variable, nodeProperty);
527                 used.add(name);
528                 
529                 PropertyInfo graphProperty = graphProperties.get(name); 
530                 if(graphProperty != null && graphProperty.builder != null) {
531                     if (classification != null && !graphProperty.hasClassification(classification)) continue;
532                     
533                     // Combine with identically named graph property
534                     map.put(name, buildPropertyVariable(graph, variable, variable.resource, graphProperty, nodeProperty));
535                     continue;
536                 }
537                 
538                 map.put(name, createStandardGraphPropertyVariable(graph, variable, nodeProperty));
539             }
540             
541             // Process graph properties
542             for(PropertyInfo info : graphProperties.values()) {
543                 String name = info.name;
544                 if(used != null && used.contains(name)) continue;
545                 if (classification != null && !info.hasClassification(classification)) continue;
546                 if (info.builder != null) {
547                     map.put(name, buildPropertyVariable(graph, variable, variable.resource, info, null));
548                 }
549             }
550             return map;
551                 
552         } else {
553
554                 if(variable.resource == null) return map;
555
556                 // Only graph properties
557                 Collection<Resource> predicates = graph.getPredicates(variable.resource);
558                 if(predicates.isEmpty()) return map;
559                 
560                 map = ensureVariableMap(map, predicates.size());
561                 
562             // Process graph properties
563             for(Resource predicate : predicates) {
564                 
565                         PropertyInfo info = graph.isImmutable(predicate) ?
566                                         graph.syncRequest(new PropertyInfoRequest(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
567                                                 graph.syncRequest(new PropertyInfoRequest(predicate));
568
569                         if(!info.isHasProperty) continue;
570                                         
571                 if (classification != null && !info.hasClassification(classification)) continue;
572                 if (info.builder != null) {
573                     map.put(info.name, buildPropertyVariable(graph, variable, variable.resource, info, null));
574                 }
575                 
576             }
577             
578             return map;
579                 
580         }
581         
582      }
583         
584     @SCLValue(type = "VariableMap")
585         public static VariableMap standardChildDomainProperties = new VariableMapImpl() {
586         
587                 @Override
588                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
589                 return getStandardChildDomainPropertyVariable(graph, context, name);
590                 }
591
592                 @Override
593                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
594                     return getStandardChildDomainPropertyVariables(graph, context, map);
595                 }
596                 
597         };
598         
599         public static Variable getStandardPropertyDomainPropertyVariableFromValue(ReadGraph graph, Variable context, String name) throws DatabaseException {
600
601                 if(context instanceof StandardGraphPropertyVariable) {
602                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
603                 Resource literal = variable.getPossibleRepresents(graph);
604                 Object propertyNode = getPossibleNodeProperty(graph, variable, name, false);
605
606                 if(literal != null) {
607                         Variable result = getPossiblePropertyFromContext(graph, variable, literal, name, propertyNode);
608                         if(result != null) return result;
609                 }
610                 
611                 Variable result = getPossibleSubliteralPropertyFromContext(graph, variable, name);
612                 if(result != null) return result;
613                 result = getPossiblePropertyFromContext(graph, variable, variable.property.predicate, name, propertyNode);
614                 if (result != null) return result;
615                 
616                 // Get possible property from NodeManager
617                 if (propertyNode != null)
618                         return createStandardGraphPropertyVariable(graph, variable, propertyNode);
619                 return null;
620                 } else if (context instanceof StandardGraphChildVariable) {
621                         return standardChildDomainProperties.getVariable(graph, context, name);
622                 } else {
623                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
624                 }                       
625                 
626         }
627         
628         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
629
630                 if(context instanceof StandardGraphPropertyVariable) {
631                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
632                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, map);
633                         if (variable.parentResource != null) {
634                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
635                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, map);
636                                 map=collectSubliteralProperties(graph, variable, map);
637                         }
638
639                         // Get properties from VariableNode
640                         map = getStandardNodePropertyVariables(graph, context, map);
641                         return map;
642                 } else if (context instanceof StandardGraphChildVariable) {
643                         return standardChildDomainProperties.getVariables(graph, context, map);
644                 } else {
645                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
646                 }
647                 
648         }
649         
650         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
651
652                 if(context instanceof StandardGraphPropertyVariable) {
653                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
654                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, classification, map);
655                         if (variable.parentResource != null) {
656                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
657                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, classification, map);
658                         }
659                         
660                         // Get properties from VariableNode
661                         map = getStandardNodePropertyVariables(graph, context, map);
662                         return map;
663                 } else if (context instanceof StandardGraphChildVariable) {
664                         return standardChildDomainProperties.getVariables(graph, context, map);
665                 } else {
666                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
667                 }       
668                 
669         }       
670         
671     @SCLValue(type = "VariableMap")
672         public static VariableMap standardPropertyDomainProperties = new VariableMapImpl() {
673
674         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
675                 Resource represents = context.getPossibleRepresents(graph);
676                 if(represents == null) return null;
677                 
678                 VariableMap map = graph.isImmutable(represents) ?
679                                 graph.syncRequest(new PropertyVariableMapRequest(represents), TransientCacheListener.<VariableMap>instance()) :
680                                         (VariableMap)graph.getPossibleRelatedValue2(represents, Layer0.getInstance(graph).domainProperties, represents);
681                 
682                 if(map == standardPropertyDomainProperties) return null;
683                 else return map;
684                 
685         }
686         
687                 @Override
688                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
689                         VariableMap valueMap = getValueVariableMap(graph, context);
690                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
691                         return getStandardPropertyDomainPropertyVariableFromValue(graph, context, name);
692                 }
693
694                 @Override
695                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
696                         VariableMap valueMap = getValueVariableMap(graph, context);
697                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
698                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, map);
699                 }
700                 
701                 @Override
702                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
703                         VariableMap valueMap = getValueVariableMap(graph, context);
704                         if(valueMap != null) return valueMap.getVariables(graph, context, classification, map);
705                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, classification, map);
706                 }
707                 
708         };
709
710     public static Resource getPossibleGraphChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
711         Resource resource = variable.getPossibleRepresents(graph);
712         if(resource == null) return null;
713         Map<String, Resource> graphChildren = graph.syncRequest(new UnescapedChildMapOfResource(resource));
714         return graphChildren.get(name);
715     }
716
717     public static Map<String,Resource> getPossibleGraphChildren(ReadGraph graph, Variable variable) throws DatabaseException {
718         Resource resource = variable.getPossibleRepresents(graph);
719         if(resource == null) return Collections.emptyMap();
720         return graph.syncRequest(new UnescapedChildMapOfResource(resource));
721     }
722
723     public static Object getPossibleNodeChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
724         if (!(variable instanceof AbstractVariable)) return null;
725         VariableNode<?> node = ((AbstractVariable)variable).node;
726         if(node == null) return null;
727         NodeStructure structure = Variables.requestNodeStructure(graph, node);
728         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
729         return structure.children.get(name);
730         }
731         
732     public static Collection<Object> getPossibleNodeChildren(ReadGraph graph, Variable variable) throws DatabaseException {
733         if (!(variable instanceof AbstractVariable)) return null;
734         VariableNode<?> node = ((AbstractVariable)variable).node;
735         if(node == null) return Collections.emptyList();
736         NodeStructure structure = Variables.requestNodeStructure(graph, node);
737         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
738         return structure.children.values();
739     }
740     
741     public static Object getPossibleNodeProperty(ReadGraph graph, Variable variable, String name, boolean throwPending) throws DatabaseException {
742         if (!(variable instanceof AbstractVariable)) return null;
743         VariableNode<?> node = ((AbstractVariable)variable).node;
744         if(node == null) return null;
745         NodeStructure structure = Variables.requestNodeStructure(graph, node);
746         if(throwPending && Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
747         return structure.properties.get(name);
748     }
749     
750     public static Collection<Object> getPossibleNodeProperties(ReadGraph graph, Variable variable) throws DatabaseException {
751         if (!(variable instanceof AbstractVariable)) return null;
752         VariableNode<?> node = ((AbstractVariable)variable).node;
753         if(node == null) return Collections.emptyList();
754         NodeStructure structure = Variables.requestNodeStructure(graph, node);
755         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
756         return structure.properties.values();
757     }
758
759     @SuppressWarnings({ "rawtypes", "unchecked" })
760     public static VariableNode build(VariableNode parent, Object node) {
761         if(node == null) return null;
762         return new VariableNode(parent.support, node);
763     }
764
765     @Deprecated
766     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
767         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
768     }
769
770     @Deprecated
771     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, Resource graphChild, String name) throws DatabaseException {
772         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, graphChild, name);
773     }
774         
775     @Deprecated
776     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
777         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
778     }
779
780     @Deprecated
781     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String,Resource> graphChildren, Map<String, Variable> map) throws DatabaseException {
782         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, graphChildren, map);
783     }
784     
785     /**
786      * Get a map of child Variables from a node manager-based Variable, combined with the existing variables in #map.
787      * @param graph  The read graph.
788      * @param context  The parent Variable.
789      * @param map  A map of variables into which the new variables are merged.
790      * @return  A map from variable names to instances
791      * @throws DatabaseException
792      */
793     public static Map<String, Variable> getStandardNodeChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
794         AbstractVariable variable = (AbstractVariable)context;
795         if (variable.node == null) return map;
796         
797         Collection<Object> nodeChildren = getPossibleNodeChildren(graph, variable);
798         if (nodeChildren.isEmpty()) return map;
799         
800         map = ensureVariableMap(map, nodeChildren.size());
801
802         for(Object nodeChild : nodeChildren) {
803             String name = getNodeName(variable, nodeChild);
804             if (!map.containsKey(name))
805                 map.put(name, createStandardGraphChildVariable(variable, nodeChild));
806         }
807
808         return map;
809     }
810
811     /**
812      * Get a map of property Variables from a node manager-based Variable, combined with the existing variables in #map.
813      * @param graph  The read graph.
814      * @param context  The parent Variable.
815      * @param map  A map of variables into which the new variables are merged.
816      * @return  A map from variable names to instances
817      * @throws DatabaseException
818      */
819     public static Map<String, Variable> getStandardNodePropertyVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
820         AbstractVariable variable = (AbstractVariable)context;
821         if (variable.node == null) return map;
822         
823         Collection<Object> nodeProperties = getPossibleNodeProperties(graph, variable);
824         if (nodeProperties.isEmpty()) return map;
825         
826         map = ensureVariableMap(map, nodeProperties.size());
827
828         for(Object nodeProperty : nodeProperties) {
829             String name = getNodeName(variable, nodeProperty);
830             if (!map.containsKey(name)) {
831                 map.put(name, createStandardGraphPropertyVariable(graph, variable, nodeProperty));
832             }
833         }
834
835         return map;
836     }    
837
838         @SCLValue(type = "VariableMap")
839         public static VariableMap standardChildDomainChildren = new VariableMapImpl() {
840
841                 @Override
842                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
843                         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
844                 }
845
846                 @Override
847                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
848                     return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
849                 }
850                 
851         };
852
853     @SCLValue(type = "VariableMap")
854         public static VariableMap standardPropertyDomainChildren = new VariableMapImpl() {
855
856         /**
857          * Get a possible non-standard VariableMap defined in the graph.
858          * @param graph  The graph
859          * @param context  The context node
860          * @return  A non-standard VariableMap instance for the context node,
861          *          or null, if not defined or defined as this instance.
862          * @throws DatabaseException
863          */
864         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
865                 Resource represents = context.getPossibleRepresents(graph);
866                 if(represents == null) return null;
867                         VariableMap map = graph.syncRequest(new ChildVariableMapRequest(represents));
868                 if(map == standardPropertyDomainChildren) return null;
869                 else return map;
870         }
871         
872                 @Override
873                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
874                         // Delegate call to a non-standard variable map?
875                         VariableMap valueMap = getValueVariableMap(graph, context);
876                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
877                         
878                         if(context instanceof StandardGraphPropertyVariable) {
879                                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
880                                 Datatype dt = variable.getDatatype(graph);
881                                 if (dt instanceof ArrayType) {
882                                         ChildReference ref = getPossibleIndexReference(name);
883                                         if (ref != null)
884                                                 return new SubliteralPropertyVariableDeprecated(variable, ref);
885                                 }
886                                 
887                                 // Check for a child node provided by the NodeManager
888                                 if (variable.node != null) {
889                                         Object childNode = getPossibleNodeChild(graph, variable, name);
890                                         if (childNode != null)
891                                                 return createStandardGraphChildVariable(variable, childNode);
892                                 }
893                                 return standardChildDomainChildren.getVariable(graph, context, name);
894                         } else if (context instanceof StandardGraphChildVariable) {
895                                 return standardChildDomainChildren.getVariable(graph, context, name);
896                         } else {
897                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
898                         }
899                 }
900
901                 @Override
902                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
903                         // Delegate call to a non-standard variable map?
904                         VariableMap valueMap = getValueVariableMap(graph, context);
905                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
906                         
907                         if(context instanceof StandardGraphPropertyVariable) {
908                                 // Get child variables provided by the NodeManager
909                                 Map<String, Variable> result = getStandardNodeChildVariables(graph, context, map); 
910                                 return standardChildDomainChildren.getVariables(graph, context, result);
911                         } else if (context instanceof StandardGraphChildVariable) {
912                                 return standardChildDomainChildren.getVariables(graph, context, map);
913                         } else {
914                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
915                         }       
916                 }
917                 
918         };
919         
920         protected static ChildReference getPossibleIndexReference(String name) {
921                 if (name.startsWith("i-")) {
922                     try {
923                         int index = Integer.parseInt(name.substring(2));
924                         return new IndexReference(index);
925                     } catch (NumberFormatException e) {}
926                 }
927                 return null;
928         }
929
930         protected static ValueAccessor getPossiblePropertyValueAccessor(ReadGraph graph, StandardGraphPropertyVariable variable) throws DatabaseException {
931             if(variable.property == null) return null;
932             return variable.property.valueAccessor;
933 //        return graph.syncRequest(new PropertyValueAccessorRequest(variable.property), TransientCacheAsyncListener.<ValueAccessor>instance());
934 //              return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(variable.property, Layer0.getInstance(graph).valueAccessor));
935         }
936
937         public static ValueAccessor getPossibleValueValueAccessor(ReadGraph graph, Variable variable) throws DatabaseException {
938             Resource value = variable.getPossibleRepresents(graph);
939             if(value == null) return null;
940             //return graph.syncRequest(new PropertyValueAccessorRequest(value));
941                 return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(value, Layer0.getInstance(graph).valueAccessor));      
942         }
943         
944         public static PropertyInfo getPossiblePropertyInfoFromContext(ReadGraph graph, Variable variable, Resource context, String name) throws DatabaseException {
945             if(context == null) return null;
946                 Map<String, PropertyInfo> predicates = graph.syncRequest(new UnescapedPropertyMapOfResource(context));
947                 return predicates.get(name);
948         }
949
950     public static Variable getPossiblePropertyFromContext(ReadGraph graph, Variable variable, Resource context, String name, Object propertyNode) throws DatabaseException {
951         PropertyInfo info = getPossiblePropertyInfoFromContext(graph, variable, context, name);
952         if(info == null || info.builder == null) return null;
953         return buildPropertyVariable(graph, variable, context, info, propertyNode);
954     }
955     
956     public static Variable getPossibleSubliteralPropertyFromContext(ReadGraph graph, StandardGraphPropertyVariable variable, String name) throws DatabaseException {
957         
958                 Resource predicate = variable.property.predicate;
959                 if(predicate == null) return null;
960
961         PropertyInfo info = getPropertyInfo(graph, predicate);
962         Pair<Resource, ChildReference> p = info.subliteralPredicates.get(name);
963         if(p == null) return null;
964         
965                 return new SubliteralPropertyVariable(graph, variable, p.first, p.second);
966         
967     }
968
969     public static Map<String, PropertyInfo> collectPropertyInfosFromContext(ReadGraph graph, Variable variable, Resource context) throws DatabaseException {
970         if(context == null) return Collections.emptyMap();
971                 return graph.isImmutable(context) ?
972                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
973                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
974     }
975
976         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, Map<String, Variable> map) throws DatabaseException {
977
978                 Map<String,PropertyInfo> properties = graph.isImmutable(context) ?
979                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
980                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
981                                 
982                 if(properties.isEmpty()) return map;
983                 
984                 map = ensureVariableMap(map, properties.size());
985                 
986                 for(PropertyInfo info : properties.values()) {
987                         String name = info.name;
988                         if (info.builder != null) {
989                                 Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
990                                 map.put(name, v);
991                         }
992                 }
993                 
994                 return map;
995                 
996         }
997
998         public static Map<String, Variable> collectSubliteralProperties(ReadGraph graph, StandardGraphPropertyVariable variable, Map<String, Variable> map) throws DatabaseException {
999
1000                 Resource predicate = variable.property.predicate;
1001                 if(predicate == null) return map;
1002                 
1003                 PropertyInfo info = getPropertyInfo(graph, predicate);
1004                 if(info.subliteralPredicates.isEmpty()) return map;
1005                 
1006                 map = ensureVariableMap(map, info.subliteralPredicates.size());
1007                 
1008                 for(Map.Entry<String, Pair<Resource, ChildReference>> entry : info.subliteralPredicates.entrySet()) {
1009                         String key = entry.getKey();
1010                         Pair<Resource, ChildReference> p = entry.getValue();
1011                         if(map == null) map = new THashMap<String,Variable>();
1012                         map.put(key, new SubliteralPropertyVariable(graph, variable, p.first, p.second));
1013                 }
1014         
1015         return map;
1016                 
1017         }
1018
1019         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, String classification, Map<String, Variable> map) throws DatabaseException {
1020
1021                 if(graph.isImmutable(context)) {
1022
1023                         Map<String,PropertyInfo> properties = graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance());
1024                         for(PropertyInfo info : properties.values()) {
1025
1026                                 if(info.classifications.contains(classification) && info.builder != null) {
1027                                         String name = info.name;
1028                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1029                                         if(map == null) map = new THashMap<String,Variable>();
1030                                         map.put(name, v);
1031                                 }
1032
1033                         }
1034
1035                 } else {
1036                                 
1037                         Collection<Resource> predicates = graph.getPredicates(context);
1038                                         
1039                         if(predicates.isEmpty()) return map;
1040                         
1041                         map = ensureVariableMap(map, predicates.size());
1042                 
1043                         for(Resource predicate : predicates) {
1044                                 
1045                                 PropertyInfo info = graph.isImmutable(predicate) ?
1046                                                 graph.syncRequest(new PropertyInfoRequest(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
1047                                                         graph.syncRequest(new PropertyInfoRequest(predicate));
1048                                                 
1049                                 if(!info.isHasProperty) continue;
1050         
1051                                 if(info.classifications.contains(classification) && info.builder != null) {
1052                                         String name = info.name;
1053                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1054                                         if(map == null) map = new THashMap<String,Variable>();
1055                                         map.put(name, v);
1056                                 }
1057                                 
1058                         }
1059                         
1060                 }
1061                 
1062                 return map;
1063                 
1064         }       
1065         
1066     @SCLValue(type = "ReadGraph -> Resource -> a -> String")
1067     public static String entityLabel(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1068         if(context instanceof Resource) {
1069                 return NameUtils.getSafeLabel(graph, ((Resource)context));      
1070         } else if (context instanceof Variable) {
1071                 Variable parent = ((Variable)context).getParent(graph);
1072                 Resource represents = parent.getRepresents(graph);
1073                 return NameUtils.getSafeLabel(graph, represents);
1074         } else {
1075                 throw new DatabaseException("Unknown context " + context);
1076         }
1077     }
1078
1079     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1080     public static Object listResources(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1081         return ListUtils.toList(graph, resource);
1082     }
1083
1084     @SCLValue(type = "ReadGraph -> Resource -> Variable -> [String]")
1085     public static List<String> standardClassifications(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1086         ArrayList<String> result = new ArrayList<String>();
1087         Resource predicate = context.getParent(graph).getPossiblePredicateResource(graph);
1088         if(predicate != null) {
1089                 for(Resource type : graph.getTypes(predicate)) {
1090                         String uri = graph.getPossibleURI(type);
1091                         if(uri != null) result.add(uri);
1092                 }
1093         }
1094         return result;
1095     }
1096
1097     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1098     public static Boolean standardValidValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1099         return Boolean.TRUE;
1100     }
1101
1102     @SCLValue(type = "ReadGraph -> Resource -> a -> StringInputValidator")
1103     public static StringInputValidator standardValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1104         return StringInputValidator.PASS;
1105     }
1106
1107     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1108     public static Boolean standardRequiredValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1109         return Boolean.FALSE;
1110     }
1111
1112     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean")
1113     public static Boolean standardDefaultValue(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1114         Variable property = context.getParent(graph);
1115         if(property instanceof StandardGraphPropertyVariable) {
1116                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)property;
1117                 if (variable.parentResource != null) {
1118                         Statement stm = graph.getPossibleStatement(variable.parentResource, variable.property.predicate);
1119                         return stm != null && stm.isAsserted(variable.parentResource);
1120                         }
1121         }
1122         return Boolean.FALSE;
1123     }
1124
1125     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1126     public static Boolean standardReadOnlyValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1127         return Boolean.FALSE;
1128     }
1129
1130     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1131     public static Object resourceAsValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1132         return resource;
1133     }
1134     
1135     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1136     public static Object functionApplication(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1137         return Functions.exec(graph, resource, graph, resource, context);
1138     }
1139
1140     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1141     public static Object computeExpression(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1142         if(context instanceof Variable) {
1143             return CompileValueRequest.compileAndEvaluate(graph, (Variable)context);
1144         } if (context instanceof Resource) {
1145             return CompileResourceValueRequest.compileAndEvaluate(graph, (Resource)converter);
1146         } else {
1147                 throw new IllegalStateException("Unknown context " + context);
1148         }
1149     }
1150
1151     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1152     public static Object composedPropertyValue(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1153         if(context instanceof Variable) {
1154                 return new StandardComposedProperty();
1155         } if (context instanceof Resource) {
1156                 return new StandardComposedProperty();
1157         } else {
1158                 throw new IllegalStateException("Unknown context " + context);
1159         }
1160     }
1161     
1162     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1163     public static Object numberInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1164         
1165         class Validator extends FunctionImpl1<String, String> {
1166
1167             private final Datatype datatype;
1168             
1169             public Validator(Datatype datatype) {
1170                 this.datatype = datatype;
1171             }
1172             
1173             @Override
1174             public String apply(String input) {
1175                 
1176                 if(datatype == null) return null;
1177                 
1178                 try {
1179
1180                     if(datatype instanceof NumberType) {
1181                         
1182                         Number number = (Number)PrimitiveValueParser.parse(input, datatype);
1183                         NumberType nt = (NumberType)datatype;
1184                         Range r = nt.getRange();
1185                         if(r != null) {
1186                             if(!r.contains(number)) return "Value is out of valid range";
1187                         }
1188                     }
1189                     return null;
1190                     
1191                 } catch (NumberFormatException e) {
1192                     return "Not a valid floating-point number";
1193                 } catch (IllegalArgumentException e) {
1194                     return "Not a valid floating-point number";
1195                 }
1196                 
1197             }
1198             
1199         }
1200
1201         if(context instanceof Variable) {
1202             
1203             Variable variable = (Variable)context;
1204             Variable property = variable.getParent(graph);
1205             Datatype datatype = property.getPossibleDatatype(graph);
1206             return new Validator(datatype);
1207             
1208         } else if (context instanceof Resource) {
1209
1210             Layer0 L0 = Layer0.getInstance(graph);
1211             Resource literal = (Resource)context;
1212             Datatype datatype = graph.getRelatedValue(literal, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
1213             return new Validator(datatype);
1214             
1215         } else {
1216             
1217             return new Validator(null);
1218             
1219         }
1220         
1221     }
1222     
1223     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1224     public static Object booleanInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1225         
1226         return new FunctionImpl1<String, String>() {
1227
1228             @Override
1229             public String apply(String input) {
1230                 
1231                 String lower = input.toLowerCase();
1232                 if("true".equals(lower) || "false".equals(lower)) return null;
1233
1234                 return "Not a valid boolean: " + input;
1235                 
1236             }
1237             
1238         };
1239         
1240     }
1241
1242     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Resource")
1243     public static Resource hasStandardResource(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1244         Variable parent = context.getParent(graph);
1245         if(parent instanceof StandardGraphChildVariable) {
1246                 StandardGraphChildVariable variable = (StandardGraphChildVariable)parent;
1247                 return variable.resource;
1248         }
1249         return null;
1250     }
1251
1252
1253     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1254         public static Object valueWithoutBinding(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1255
1256         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1257                 
1258                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1259                         Layer0 L0 = Layer0.getInstance(graph);
1260                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel);
1261                 }
1262
1263                 if (variable.parentResource == null)
1264                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1265
1266                 try {
1267                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1268                 } catch (NoSingleResultException e) {
1269                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1270                 } catch (DoesNotContainValueException e) {
1271                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1272                 }
1273                 
1274         }
1275
1276     @SCLValue(type = "ReadGraph -> Variable -> Binding -> a")
1277         public static Object valueWithBinding(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
1278
1279         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1280                 
1281                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1282                         Layer0 L0 = Layer0.getInstance(graph);
1283                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel, binding);
1284                 }
1285
1286                 if (variable.parentResource == null)
1287                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1288
1289                 try {
1290                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1291                 } catch (NoSingleResultException e) {
1292                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1293                 } catch (DoesNotContainValueException e) {
1294                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1295                 }
1296                 
1297         }
1298
1299     @SCLValue(type = "WriteGraph -> Variable -> a -> Binding -> b")
1300         public static Object valueSetterWithBinding(WriteGraph graph, Variable variable, Object value, Binding binding) throws DatabaseException {
1301                 
1302                 Function4<WriteGraph, Variable, Object, Object, String> modifier = variable.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
1303                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
1304                 modifier.apply(graph, variable, value, binding);
1305                 return null;
1306                 
1307         }
1308     
1309     static class L0Issue extends StandardIssue {
1310         
1311         private final String description;
1312         
1313         public L0Issue(String description, Resource type, Resource ... contexts) {
1314             super(type, contexts);
1315             this.description = description;
1316         }
1317
1318         @Override
1319         public Resource write(WriteGraph graph, Resource source) throws DatabaseException {
1320             Layer0 L0 = Layer0.getInstance(graph);
1321             IssueResource IR = IssueResource.getInstance(graph);
1322             Resource issue = super.write(graph, source);
1323             graph.claim(issue, IR.Issue_HasSeverity, IR.Severity_Fatal);
1324             graph.addLiteral(issue, L0.HasDescription, L0.HasDescription_Inverse, description, Bindings.STRING);
1325             return issue;
1326         }
1327         
1328     }
1329     
1330         private static List<Issue> reportInconsistency(ReadGraph graph, Resource subject, String description, List<Issue> issues) throws DatabaseException {
1331             if(issues == null) issues = new ArrayList<Issue>();
1332                 System.err.println("Change set validation reports the following issue: " + NameUtils.getSafeName(graph, subject, true) + ": " + description);
1333                 IssueResource IR = IssueResource.getInstance(graph);
1334                 issues.add(new L0Issue(description, IR.Issue, subject));
1335                 return issues;
1336         }
1337     
1338     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1339     public static List<Issue> relationValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1340
1341         Layer0 L0 = Layer0.getInstance(graph);
1342
1343         List<Issue> issues = null;
1344         
1345         for(Statement stm : graph.getStatements(resource, L0.IsWeaklyRelatedTo)) {
1346                 Resource predicate = stm.getPredicate();
1347                 Resource object = stm.getObject();
1348                 if(!isRelation(graph, L0, predicate)) {
1349                         issues = reportInconsistency(graph, resource, "The predicate of a statement must be a relation: " + NameUtils.toString(graph, stm), issues);
1350                 }
1351                 if(graph.isInstanceOf(predicate, L0.FunctionalRelation)) {
1352                         if(graph.getObjects(resource, predicate).size() > 1)
1353                                 issues = reportInconsistency(graph, resource, 
1354                                                 "Relation " +
1355                                                                 NameUtils.getSafeName(graph, predicate)
1356                                                                 + " is functional.", issues);
1357                 }
1358                 {
1359                         Collection<Resource> domain = graph.getObjects(predicate, L0.HasDomain);
1360                         if (!isInstanceOfAny(graph, resource, domain, true)) {
1361                                 StringBuilder sb = new StringBuilder()
1362                                 .append("The domain of ")
1363                                 .append(NameUtils.getSafeName(graph, predicate))
1364                                 .append(" relation is ");
1365                                 orString(graph, sb, domain).append(".");
1366                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1367                         }
1368                 }
1369                 {
1370                         Collection<Resource> range = graph.getObjects(predicate, L0.HasRange);
1371                         if (!isInstanceOfAny(graph, object, range, true) && !graph.isInstanceOf(object, L0.SCLValue)) {
1372                                 StringBuilder sb = new StringBuilder()
1373                                 .append("The range of ")
1374                                 .append(NameUtils.getSafeName(graph, predicate))
1375                                 .append(" relation is ");
1376                                 orString(graph, sb, range).append(" but current object is ")
1377                                 .append(NameUtils.getSafeName(graph, object)).append(".");
1378                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1379                         }
1380                 }               
1381         }
1382                 
1383                 return issues != null ? issues : Collections.<Issue>emptyList();
1384
1385     }
1386
1387     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1388     public static List<Issue> propertyValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1389
1390         List<Issue> issues = null;
1391
1392         Layer0 L0 = Layer0.getInstance(graph);
1393         for(Statement stm : graph.getStatements(resource, L0.HasProperty)) {
1394                 Resource subject = stm.getSubject();
1395                 Resource predicate = stm.getPredicate();
1396                 String error = L0Validations.checkValueType(graph, subject, predicate);
1397                 if(error != null) issues = reportInconsistency(graph, subject, error, issues);
1398         }
1399                 
1400         return issues != null ? issues : Collections.<Issue>emptyList();
1401
1402     }
1403     
1404     
1405     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1406     public static List<Issue> valueValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1407
1408         List<Issue> issues = null;
1409
1410         Layer0 L0 = Layer0.getInstance(graph);
1411         if(graph.hasValue(resource)) {
1412                 if(!graph.isInstanceOf(resource, L0.Literal)) {
1413                         issues = reportInconsistency(graph, resource, 
1414                                         "Resource has a value but it is not a literal.", issues);
1415                 }
1416                 else {
1417                         // TODO check that the value is valid for the data type
1418                 }
1419         }
1420         else {
1421                 if(graph.isInstanceOf(resource, L0.Literal)) {
1422                         issues = reportInconsistency(graph, resource, 
1423                                         "Resource is a literal but it does not have a value.", issues);
1424                 }
1425         }
1426         
1427         return issues != null ? issues : Collections.<Issue>emptyList();
1428         
1429     }
1430
1431     
1432     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1433     public static List<Issue> uriValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1434         
1435         List<Issue> issues = null;
1436
1437         Layer0 L0 = Layer0.getInstance(graph);
1438         Resource parent = graph.getPossibleObject(resource, L0.PartOf);
1439         if(parent != null) {
1440             String parentURI = graph.syncRequest(new PossibleURI(parent));
1441             if(parentURI != null) {
1442                         String name = graph.getPossibleRelatedValue(resource, L0.HasName);
1443                         if(name == null) {
1444                                 issues = reportInconsistency(graph, resource, "Resource has a parent with URI but has no valid HasName.", issues);
1445                         }
1446             }
1447         }
1448
1449         return issues != null ? issues : Collections.<Issue>emptyList();
1450         
1451     }
1452     
1453     private static Resource getPossibleNearestClusterSet(ReadGraph graph, Resource base, Resource resource) throws DatabaseException {
1454
1455         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1456         if(cs.isClusterSet(resource) && !base.equals(resource)) return resource;
1457         
1458         Resource nearest = CommonDBUtils.getNearestOwner(graph, Collections.singletonList(resource));
1459         if(nearest == null) return null;
1460         
1461         return getPossibleNearestClusterSet(graph, base, nearest);
1462
1463     }
1464
1465     private static boolean quirks(ReadGraph graph, Resource resource) throws DatabaseException {
1466
1467         if(!resource.isPersistent()) return true;
1468         if(graph.isImmutable(resource)) return true;
1469         if(resource.getResourceId() < 0x2000) return true;
1470
1471         return false;
1472         
1473     }
1474     
1475     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1476     public static List<Issue> clusterValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1477
1478         if(!Development.DEVELOPMENT) return Collections.<Issue>emptyList();
1479         
1480         if(quirks(graph, resource)) return Collections.<Issue>emptyList();
1481         
1482         List<Issue> issues = null;
1483
1484         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1485         Resource set = cs.getClusterSetOfCluster(resource);
1486         
1487         if(set == null) return reportInconsistency(graph, resource, "Resource cluster is not part of any cluster set", issues);
1488         
1489         Resource nearestSet = getPossibleNearestClusterSet(graph, resource, resource);
1490         if(nearestSet == null) {
1491                 // This means that there is no owner since RootLibrary is a cluster set
1492                 return Collections.<Issue>emptyList();
1493         }
1494         
1495         if(!set.equals(nearestSet)) return reportInconsistency(graph, resource, "The cluster set of a resource is not the nearest owner set", issues);
1496
1497         return Collections.<Issue>emptyList();
1498         
1499     }
1500
1501     private static boolean isInstanceOfAny(ReadGraph graph, Resource r, Collection<Resource> types, boolean ifEmpty) throws DatabaseException {
1502                 if (types.isEmpty())
1503                         return ifEmpty;
1504                 for (Resource type : types) {
1505                         if (graph.isInstanceOf(r, type)) {
1506                                 return true;
1507                         }
1508                 }
1509                 return false;
1510         }
1511
1512         private static StringBuilder orString(ReadGraph graph, StringBuilder sb, Collection<Resource> rs) throws DatabaseException {
1513                 sb.append("(");
1514                 boolean first = true;
1515                 for (Resource r : rs) {
1516                         if (!first)
1517                                 sb.append(" | ");
1518                         first = false;
1519                         sb.append(NameUtils.getSafeName(graph, r));
1520                 }
1521                 sb.append(")");
1522                 return sb;
1523         }
1524
1525     public static boolean isRelation(ReadGraph g, Layer0 l0, Resource relation) throws DatabaseException {
1526                 return g.hasStatement(relation, l0.SubrelationOf) || relation == l0.IsWeaklyRelatedTo;
1527         }
1528         
1529         public static boolean isType(ReadGraph g, Layer0 l0, Resource type) throws DatabaseException {
1530                 return g.hasStatement(type, l0.Inherits) || type == l0.Entity;
1531         }
1532         
1533     public static Variable buildChildVariable(ReadGraph graph, Variable context, Resource graphChild, Object nodeChild) throws DatabaseException {
1534         VariableBuilder builder = graph.adapt(graphChild, VariableBuilder.class);
1535         return builder.buildChild(graph, context, build(((AbstractVariable)context).node, nodeChild), graphChild);
1536         }
1537
1538         private static Variable buildPropertyVariable(ReadGraph graph, Variable variable, Resource parentResource, PropertyInfo graphProperty, Object propertyNode) throws DatabaseException {
1539                 VariableNode<?> node = variable instanceof AbstractVariable ? build(((AbstractVariable)variable).node, propertyNode) : null;
1540                 return graphProperty.builder.buildProperty(graph, variable, node, parentResource, graphProperty.predicate);
1541         }
1542         
1543         static StandardGraphChildVariable createStandardGraphChildVariable(
1544                         AbstractVariable parent, Object child) {
1545                 return new StandardGraphChildVariable(parent, build(parent.node, child), null);
1546         }
1547
1548         private static StandardGraphPropertyVariable createStandardGraphPropertyVariable(
1549                         ReadGraph graph, AbstractVariable variable, Object nodeProperty) throws DatabaseException {
1550         Resource propertyResource = getPossiblePropertyResource(graph, variable, nodeProperty);
1551         return new StandardGraphPropertyVariable(graph, variable, build(variable.node, nodeProperty), null, propertyResource);
1552         }
1553         
1554         static Map<String, Variable> ensureVariableMap(
1555                         Map<String, Variable> map, int size) {
1556                 if(map == null) map = new THashMap<String,Variable>(size);
1557                 return map;
1558         }
1559
1560         private static PropertyInfo getPropertyInfo(ReadGraph graph, Resource predicate) throws DatabaseException {
1561                 return graph.syncRequest(new PropertyInfoRequest(predicate));
1562         }
1563
1564         @SuppressWarnings("unchecked")
1565         static String getNodeName(AbstractVariable parent, Object child) {
1566                 return parent.node.support.manager.getName(child);
1567         }
1568
1569         @SuppressWarnings("unchecked")
1570         private static Object getNodeValue(final AbstractVariable variable, final Binding binding) throws NodeManagerException, BindingException {
1571                 return variable.node.support.manager.getValue(variable.node.node, binding);
1572         }
1573
1574         @SuppressWarnings("unchecked")
1575         private static void setNodeValue(final AbstractVariable variable, final Object value, final Binding binding) throws NodeManagerException, BindingException {
1576                 variable.node.support.manager.setValue(variable.node.node, value, binding);
1577         }
1578         
1579         @SuppressWarnings("unchecked")
1580         private static String getPossiblePropertyURI(AbstractVariable parent, Object node) {
1581                 return parent.node.support.manager.getPropertyURI(parent.node.node, node);
1582         }
1583     
1584 }