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