6ccffbe973ddecdeef07e4a8bcbdaebe3acf52e2
[simantics/platform.git] / bundles / org.simantics.document.server / src / org / simantics / document / server / Functions.java
1 package org.simantics.document.server;
2
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.TreeMap;
12
13 import org.simantics.Simantics;
14 import org.simantics.databoard.Bindings;
15 import org.simantics.databoard.Datatypes;
16 import org.simantics.databoard.binding.Binding;
17 import org.simantics.databoard.type.Datatype;
18 import org.simantics.db.AsyncReadGraph;
19 import org.simantics.db.DirectStatements;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.RequestProcessor;
22 import org.simantics.db.Resource;
23 import org.simantics.db.Session;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.primitiverequest.Adapter;
27 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
28 import org.simantics.db.common.request.UnaryRead;
29 import org.simantics.db.common.request.UniqueRead;
30 import org.simantics.db.common.request.WriteResultRequest;
31 import org.simantics.db.common.utils.Logger;
32 import org.simantics.db.common.utils.NameUtils;
33 import org.simantics.db.exception.DatabaseException;
34 import org.simantics.db.layer0.function.All;
35 import org.simantics.db.layer0.request.ProjectModels;
36 import org.simantics.db.layer0.request.PropertyInfo;
37 import org.simantics.db.layer0.request.PropertyInfoRequest;
38 import org.simantics.db.layer0.request.VariableProperty;
39 import org.simantics.db.layer0.request.VariableRead;
40 import org.simantics.db.layer0.request.VariableValueWithBinding;
41 import org.simantics.db.layer0.variable.ConstantPropertyVariable;
42 import org.simantics.db.layer0.variable.ProxyChildVariable;
43 import org.simantics.db.layer0.variable.ProxySessionRequest;
44 import org.simantics.db.layer0.variable.ProxyVariables;
45 import org.simantics.db.layer0.variable.StandardAssertedGraphPropertyVariable;
46 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
47 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
48 import org.simantics.db.layer0.variable.Variable;
49 import org.simantics.db.layer0.variable.VariableMap;
50 import org.simantics.db.layer0.variable.VariableMapImpl;
51 import org.simantics.db.layer0.variable.Variables;
52 import org.simantics.db.procedure.AsyncProcedure;
53 import org.simantics.db.service.DirectQuerySupport;
54 import org.simantics.document.base.ontology.DocumentationResource;
55 import org.simantics.document.server.bean.Command;
56 import org.simantics.document.server.bean.DataDefinition;
57 import org.simantics.document.server.handler.AbstractEventHandler;
58 import org.simantics.document.server.handler.AbstractResponseHandler;
59 import org.simantics.document.server.handler.EventHandler;
60 import org.simantics.document.server.io.CommandContext;
61 import org.simantics.document.server.io.CommandContextImpl;
62 import org.simantics.document.server.io.CommandContextMutable;
63 import org.simantics.document.server.io.CommandResult;
64 import org.simantics.document.server.io.IConsole;
65 import org.simantics.document.server.request.ServerSCLHandlerValueRequest;
66 import org.simantics.document.server.request.ServerSCLValueRequest;
67 import org.simantics.document.server.serverResponse.ServerResponse;
68 import org.simantics.document.server.serverResponse.SuccessResponse;
69 import org.simantics.modeling.ModelingResources;
70 import org.simantics.modeling.scl.SCLRealm;
71 import org.simantics.modeling.scl.SCLSessionManager;
72 import org.simantics.modeling.services.CaseInsensitiveComponentFunctionNamingStrategy;
73 import org.simantics.modeling.services.ComponentNamingStrategy;
74 import org.simantics.operation.Layer0X;
75 import org.simantics.project.IProject;
76 import org.simantics.scl.compiler.types.TCon;
77 import org.simantics.scl.compiler.types.Type;
78 import org.simantics.scl.compiler.types.Types;
79 import org.simantics.scl.reflection.annotations.SCLValue;
80 import org.simantics.scl.runtime.SCLContext;
81 import org.simantics.scl.runtime.function.Function;
82 import org.simantics.scl.runtime.function.Function1;
83 import org.simantics.scl.runtime.function.FunctionImpl1;
84 import org.simantics.scl.runtime.function.FunctionImpl4;
85 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
86 import org.simantics.scl.runtime.tuple.Tuple;
87 import org.simantics.scl.runtime.tuple.Tuple0;
88 import org.simantics.scl.runtime.tuple.Tuple2;
89 import org.simantics.scl.runtime.tuple.Tuple3;
90 import org.simantics.scl.runtime.tuple.Tuple4;
91 import org.simantics.scl.runtime.tuple.Tuple5;
92 import org.simantics.simulation.experiment.IExperiment;
93 import org.simantics.simulation.ontology.SimulationResource;
94 import org.simantics.simulation.project.IExperimentManager;
95 import org.simantics.structural2.variables.Connection;
96 import org.simantics.structural2.variables.StandardProceduralChildVariable;
97 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
98
99 import gnu.trove.map.hash.THashMap;
100
101 public class Functions {
102
103     private static class PrimitivePropertyStatementsProcedure implements AsyncProcedure<DirectStatements> {
104
105         public DirectStatements result;
106
107         @Override
108         public void execute(AsyncReadGraph graph, DirectStatements result) {
109             this.result = result;
110         }
111
112         @Override
113         public void exception(AsyncReadGraph graph, Throwable throwable) {
114         }
115
116     }
117
118     @SCLValue(type = "VariableMap")
119     public static VariableMap primitiveProperties = new VariableMapImpl() {
120
121         @Override
122         public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
123             return All.getStandardPropertyDomainPropertyVariableFromValue(graph, context, name);
124         }
125
126         @Override
127         public Map<String, Variable> getVariables(final ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
128
129             Variable parent = context.getParent(graph);
130
131             DocumentationResource DOC = DocumentationResource.getInstance(graph);
132
133             if(parent instanceof StandardProceduralChildVariable) {
134
135                 StandardProceduralChildVariable procedural = (StandardProceduralChildVariable)parent;
136                 for(Variable property : procedural.getProperties(graph/*, DocumentationResource.URIs.Document_AttributeRelation*/)) {
137                     if(property instanceof StandardAssertedGraphPropertyVariable) {
138                         StandardAssertedGraphPropertyVariable ass = (StandardAssertedGraphPropertyVariable)property;
139                         if("datadefinitions".equals(ass.property.name) || "commands".equals(ass.property.name)  || "pollingFunction".equals(ass.property.name)) {
140                             Object value = property.getPossibleValue(graph);
141                             if(value != null) map.put(ass.property.name, new ConstantPropertyVariable(parent, ass.property.name, value, null));
142                         }
143                         continue;
144                     }
145                     Resource predicate = property.getPossiblePredicateResource(graph);
146                     if(predicate != null) {
147                         PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate));
148                         if(info.hasClassification(DocumentationResource.URIs.Document_AttributeRelation)) {
149                             Variable prop = parent.getProperty(graph, predicate);
150                             Object value = prop.getValue(graph);
151                             if(map == null) map = new HashMap<String,Variable>();
152                             map.put(info.name, new ConstantPropertyVariable(parent, info.name, value, null));
153                         }
154                     }
155                 }
156
157             } else {
158
159                 DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
160                 PrimitivePropertyStatementsProcedure foo = new PrimitivePropertyStatementsProcedure();
161
162                 dqs.forEachDirectPersistentStatement(graph, parent.getRepresents(graph), foo);
163
164                 for(Statement stm : foo.result) {
165                     Resource predicate = stm.getPredicate();
166                     PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate));
167
168                     if(info.isHasProperty && info.hasClassification(DocumentationResource.URIs.Document_AttributeRelation)) {
169                         if(map == null) map = new HashMap<String,Variable>();
170                         Variable prop = new StandardGraphPropertyVariable(graph, parent, predicate);
171                         Object value = prop.getValue(graph);
172                         map.put(info.name, new ConstantPropertyVariable(parent, info.name, value, null));
173                     } else {
174                         Resource definition = graph.getPossibleObject(predicate, DOC.Document_definesAttributeRelation);
175                         if(definition != null) {
176                             if(map == null) map = new HashMap<String,Variable>();
177                             try {
178                                 PropertyInfo info2 = graph.syncRequest(new PropertyInfoRequest(definition));
179                                 Variable prop = new StandardGraphPropertyVariable(graph, parent, definition);
180                                 map.put(info2.name, new PrimitiveValueVariable(parent, info2.name, prop));
181                             } catch (DatabaseException e) {
182                                 Logger.defaultLogError(e);
183                             }
184                         }
185                     }
186                 }
187
188                 Variable prop = new StandardGraphPropertyVariable(graph, parent, DOC.Properties_dataDefinitions);
189                 Object value = prop.getPossibleValue(graph);
190                 if(value != null) map.put("dataDefinitions", new ConstantPropertyVariable(parent, "dataDefinitions", value, null));
191                 prop = new StandardGraphPropertyVariable(graph, parent, DOC.Properties_commands);
192                 value = prop.getPossibleValue(graph);
193                 if(value != null) map.put("commands", new ConstantPropertyVariable(parent, "commands", value, null));
194
195             }
196
197             if(map == null) return Collections.emptyMap();
198
199             return map;
200
201         }
202
203     };
204
205     @SCLValue(type = "VariableMap")
206     public static VariableMap inputSpaceChildren = new VariableMapImpl() {
207
208         private Variable getProxy(ReadGraph graph, Variable context) throws DatabaseException {
209             Variable root = Variables.getRootVariable(graph);
210             return new DocumentProxyChildVariable(context, context, root, ProxyChildVariable.CONTEXT_BEGIN);
211         }
212
213         @Override
214         public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
215
216             if(ProxyChildVariable.CONTEXT_BEGIN.equals(name)) return getProxy(graph, context);
217             return All.standardChildDomainChildren.getVariable(graph, context, name);
218
219         }
220
221         @Override
222         public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
223
224             map = All.standardChildDomainChildren.getVariables(graph, context, map);
225             if(map == null) map = new THashMap<String,Variable>();
226             map.put(ProxyChildVariable.CONTEXT_BEGIN, getProxy(graph, context));
227             return map;
228
229         }
230
231     };
232
233     static class DocumentProxyChildVariable extends ProxyChildVariable {
234
235         public DocumentProxyChildVariable(Variable base, Variable parent, Variable other, String name) {
236             super(base, parent, other, name);
237         }
238
239         @Override
240         public Variable create(Variable base, Variable parent, Variable other, String name) {
241             return new DocumentProxyChildVariable(base, parent, other, name);
242         }
243
244         public Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException {
245
246                 if(CONTEXT_END.equals(name)) {
247                 if(other instanceof ProxyChildVariable) {
248                         // The context is also a proxy - let it do the job
249                     return super.getPossibleChild(graph, name);
250                 } else {
251                         return new RootVariable(this, base.getRepresents(graph));
252                 }
253                 }
254
255             return super.getPossibleChild(graph, name);
256
257         }
258
259         public Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException {
260
261             Collection<Variable> result = super.getChildren(graph);
262             if(!(base instanceof ProxyChildVariable)) {
263                 result.add(new RootVariable(this, base.getRepresents(graph)));
264             }
265             return result;
266
267         }
268
269     }
270
271     static class RootVariable extends StandardGraphChildVariable {
272
273         public RootVariable(DocumentProxyChildVariable parent, Resource resource) {
274             super(parent, null, resource);
275         }
276
277         @Override
278         public String getName(ReadGraph graph) throws DatabaseException {
279             return ProxyChildVariable.CONTEXT_END;
280         }
281
282         @SuppressWarnings("deprecation")
283         @Override
284         public Variable getNameVariable(ReadGraph graph) throws DatabaseException {
285             return new ConstantPropertyVariable(this, Variables.NAME, ProxyChildVariable.CONTEXT_END, Bindings.STRING);
286         }
287
288     }
289
290     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
291     public static Variable input(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
292         Variable session = graph.syncRequest(new ProxySessionRequest(context));
293         DocumentationResource DOC = DocumentationResource.getInstance(graph);
294         String uri = session.getPossiblePropertyValue(graph, DOC.Session_inputURI);
295         if(uri == null) {
296                 // TODO HAXX - Please fix this
297                 // we have no URI so this probably means that someone has inserted a non-session 
298                 // into the proxy variable => return that instead
299                 return session;
300         }
301         return Variables.getVariable(graph, uri);
302     }
303
304     public static Variable stateVariable(ReadGraph graph, Variable self) throws DatabaseException {
305         Variable session = graph.syncRequest(new ProxySessionRequest(self));
306         if (session == null)
307                 throw new DatabaseException("No state for " + self.getURI(graph));
308         return session.getPossibleChild(graph, "__scl__");
309     }
310
311     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
312     public static Variable state(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
313         Variable session = graph.syncRequest(new ProxySessionRequest(context));
314         if (session == null)
315             throw new DatabaseException("No state for " + context.getURI(graph));
316         return session.getPossibleChild(graph, "__scl__");
317     }
318
319     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
320     public static Variable icstate(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
321         Variable session = graph.syncRequest(new ProxySessionRequest(context));
322         return session.getPossibleChild(graph, "__icstate__");
323     }
324
325     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
326     public static Variable session(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
327         return graph.syncRequest(new ProxySessionRequest(context));
328     }
329
330     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
331     public static Variable experiment(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
332         // Try if experiment (run) has been used as the input
333         Variable var = input(graph, converter, context);
334         SimulationResource SR = SimulationResource.getInstance(graph);
335         while(var != null && !graph.isInstanceOf(var.getRepresents(graph), SR.Run)) {
336             var = var.getParent(graph);
337         }
338
339         if(var != null) {
340             IExperiment exp = getExperiment(graph, var);
341             if(exp == null)
342                 return null;
343         }
344
345         return var;
346     }
347
348     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
349     public static Variable model(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
350         Variable var = input(graph, converter, context);
351         ModelingResources MOD = ModelingResources.getInstance(graph);
352         while(var != null && !graph.isInstanceOf(var.getRepresents(graph), MOD.StructuralModel)) {
353             var = var.getParent(graph);
354         }
355
356         return var;
357     }
358
359     private static Collection<Variable> getBroadcasted(ReadGraph graph, Variable target) throws DatabaseException {
360
361         ArrayList<Variable> result = new ArrayList<Variable>();
362
363         DocumentationResource DOC = DocumentationResource.getInstance(graph);
364
365         Variable broadcasted = target.getPossibleProperty(graph, DOC.Relations_broadcasted);
366         if(broadcasted != null) result.add(broadcasted);
367
368         for(Variable child : DocumentServerUtils.getChildrenInOrdinalOrder(graph, target)) {
369                 result.addAll(getBroadcasted(graph, child));
370         }
371
372         return result;
373
374     }
375
376     private static List<Command> getCommands(ReadGraph graph, Collection<Variable> commandVariables, String trigger, CommandContext constants, boolean broadcast) throws DatabaseException {
377
378         if(commandVariables.isEmpty()) return Collections.emptyList();
379
380         String t = trigger;
381         TreeMap<Integer, List<Command>> sequences = new TreeMap<Integer, List<Command>>();
382
383         DocumentationResource DOC = DocumentationResource.getInstance(graph);
384
385         int count = 0;
386         for (Variable c : commandVariables) {
387
388             if(trigger == null)
389                 t = c.getName(graph);
390
391             Connection conn = c.getValue(graph);
392             Variable targetConnectionPoint = DocumentServerUtils.getPossibleCommandTriggerConnectionPoint(graph, c, conn);
393             if (targetConnectionPoint != null) {
394                 Variable target = targetConnectionPoint.getParent(graph);
395                 if (target != null) {
396
397                     Boolean enabled = target.getPossiblePropertyValue(graph, DOC.Properties_exists, Bindings.BOOLEAN);
398                     if(enabled != null && !enabled) continue;
399
400                     Integer ordinal;
401                     if (broadcast) {
402                         ordinal = ++count;
403                     } else {
404                         ordinal = 1;
405                             try {
406                                 String o = c.getPossiblePropertyValue(graph, "ordinal");
407                                 if(o != null)
408                                     ordinal = Integer.parseInt(o);
409                             } catch (NumberFormatException e) {}
410                     }
411
412                     String constantKey = target.getPossiblePropertyValue(graph, "constantKey");
413                     String constantValue = target.getPossiblePropertyValue(graph, "constantValue");
414
415                     CommandContextMutable newConstants = (CommandContextMutable)constants; 
416                     if(constantKey != null && constantValue != null) {
417                         if(!constantKey.isEmpty()) {
418                                 newConstants = new CommandContextImpl().merge(constants);
419                                 newConstants.putString(constantKey, constantValue);
420                         }
421                     }
422
423                     String requiredKey = target.getPossiblePropertyValue(graph, "requiredKey");
424                     if(requiredKey != null && !requiredKey.isEmpty()) {
425                         if (newConstants == constants) {
426                                 newConstants = new CommandContextImpl().merge(constants);
427                         }
428                                 newConstants.putRow(CommandContext.REQUIRED_KEYS, Collections.<Object>singletonList(requiredKey));
429                     }
430
431                     String forbiddenKey = target.getPossiblePropertyValue(graph, "forbiddenKey");
432                     if(forbiddenKey != null && !forbiddenKey.isEmpty()) {
433                         if (newConstants == constants) {
434                                 newConstants = new CommandContextImpl().merge(constants);
435                         }
436                                 newConstants.putRow(CommandContext.FORBIDDEN_KEYS, Collections.<Object>singletonList(forbiddenKey));
437                     }
438
439                     if(DOC.Relations_broadcast.equals(targetConnectionPoint.getPredicateResource(graph))) {
440
441                         // This is a broadcast terminal of a container
442                         List<Command> broadcastCommands = getCommands(graph, getBroadcasted(graph, target), t, newConstants, true);
443                         sequences.put(ordinal, broadcastCommands);
444
445                     } else {
446
447                         Command command = new Command(DocumentServerUtils.getId(graph, target), t, 
448                                         targetConnectionPoint.getName(graph), newConstants);
449                         sequences.put(ordinal, Collections.singletonList(command));
450
451                     }
452
453                 }
454             }
455         }
456
457         List<Command> commands = new ArrayList<Command>();
458         for (List<Command> commandList : sequences.values()) {
459                 for (Command command : commandList) {
460                         commands.add(command);
461                 }
462         }
463
464         return commands;
465     }
466
467     /**
468      * Commands
469      * 
470      * @param graph
471      * @param variable
472      * @return
473      * @throws DatabaseException
474      */
475     public static List<Command> commandList(ReadGraph graph, Variable variable) throws DatabaseException {
476         return getCommands(graph, DocumentServerUtils.getTriggerCommands(graph, variable.getParent(graph)), null, new CommandContextImpl(), false);
477     }
478
479     /**
480      * Data definitions
481      * 
482      * @param graph
483      * @param variable
484      * @return
485      * @throws DatabaseException
486      */
487     public static List<DataDefinition> dataDefinitions(ReadGraph graph, Variable variable) throws DatabaseException {
488         DocumentationResource DOC = DocumentationResource.getInstance(graph); 
489         ArrayList<DataDefinition> dataDefinitions = new ArrayList<DataDefinition>();
490         // Find data definition connections
491         for (Variable dataDefinitionRelation : DocumentServerUtils.getDataDefinitions(graph, variable.getParent(graph))) {
492             Connection dataDefinitionConnection = dataDefinitionRelation.getValue(graph);
493             // Find data the other end of definition connection
494             Collection<Variable> dataDefinitionProviders = DocumentServerUtils.getPossibleOtherConnectionPoints(graph,
495                     dataDefinitionRelation, dataDefinitionConnection);
496             if (dataDefinitionProviders != null) {
497
498                 for(Variable dataDefinitionProvider : dataDefinitionProviders) {
499
500                     Variable dataDefinition = dataDefinitionProvider.getParent(graph);
501                     if (dataDefinition != null) {
502                         // Found other end. Is should contain ONE data
503 // definition connection to the actual data
504                         Collection<Variable> dataCollection = DocumentServerUtils.getDataRelations(graph, dataDefinition);
505                         if (dataCollection.size() == 1) {
506                             Variable dataRelation = dataCollection.iterator().next();
507                             Connection dataConnection = dataRelation.getValue(graph);
508                             // Find data the other end of definition connection
509                             Variable dataConnectionPoint = DocumentServerUtils.getPossibleOtherConnectionPoint(graph,
510                                     dataRelation, dataConnection);
511                             if (dataConnectionPoint != null) {
512                                 Variable data = dataConnectionPoint.getParent(graph);
513                                 Resource type = dataDefinition.getPossibleType(graph, DOC.Components_Component);
514
515                                 if (graph.isInheritedFrom(type, DOC.Components_DefVar)) {
516
517                                         String sourceProperty = dataDefinition.getPropertyValue(graph, DOC.Properties_source,
518                                                 Bindings.STRING);
519                                         String targetProperty = dataDefinition.getPropertyValue(graph, DOC.Properties_target,
520                                                 Bindings.STRING);
521
522                                         dataDefinitions.add(new DataDefinition(DocumentServerUtils.getId(graph, data),
523                                                         sourceProperty, targetProperty));
524
525                                 } else if (graph.isInheritedFrom(type, DOC.Components_DefVars)) {
526
527                                         List<String> sourcesProperty = toList(dataDefinition.getPropertyValue(graph, DOC.Properties_sources), String.class);
528                                         List<String> targetsProperty = toList(dataDefinition.getPropertyValue(graph, DOC.Properties_targets), String.class);
529
530                                         for (int i = 0; i < Math.min(sourcesProperty.size(), targetsProperty.size()); i++) {
531                                                 dataDefinitions.add(new DataDefinition(DocumentServerUtils.getId(graph, data),
532                                                         sourcesProperty.get(i), targetsProperty.get(i)));
533                                         }
534                                 }
535                             }
536                         }
537                     }
538                 }
539             }
540         }
541         return dataDefinitions;
542     }
543
544     @SuppressWarnings("unchecked")
545         private static <T> List<T> toList(Object o, Class<T> c) {
546         List<T> result = null;
547         if (o instanceof List) {
548                 return (List<T>)o;
549         } else if (o instanceof Object[]) {
550                 result = new ArrayList<T>(((Object[])o).length);
551                 for (T item : (T[])o) {
552                         result.add(item);
553                 }
554                 return result;
555         } else {
556                 return Collections.<T>emptyList();
557         }
558     }
559
560     public static AbstractEventHandler emptyOnClick(ReadGraph graph) throws DatabaseException {
561         return new EventHandler() {
562             @Override
563             public ServerResponse handle(ReadGraph graph, CommandContext parameters) throws DatabaseException {
564                 return null;
565             }
566         };
567     }
568
569     public static AbstractEventHandler writeEventHandler(ReadGraph graph, final Variable variable, final Function fn) {
570
571         final Session session = graph.getSession();
572
573         return new AbstractEventHandler() {
574
575                         @Override
576                         public CommandResult handle(final CommandContext parameters) {
577
578                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
579
580                 try {
581
582                                         String result = session.sync(new WriteResultRequest<String>() {
583
584                                             @Override
585                                             public String perform(WriteGraph graph) throws DatabaseException {
586                                                 SCLContext sclContext = SCLContext.getCurrent();
587                                                 Object oldGraph = sclContext.put("graph", graph);
588                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
589                                                 try {
590                                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
591                                                         @Override
592                                                         public String apply(String key) {
593                                                             return parameters.getString(key);
594                                                         }
595                                                     }; 
596                                                     Object response = (String)fn.apply(variable, pf);
597                                                     if(response instanceof String) {
598                                                         return (String)response;
599                                                     }
600                                                     return null;
601                                                 } catch (Throwable t) {
602                                                     t.printStackTrace();
603                                                 } finally {
604                                                         sclContext.put("graph", oldGraph);
605                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
606                                                 }
607
608                                                 return null;
609
610                                             }
611
612                                         });
613
614                         return new SuccessResponse(result);
615
616                                 } catch (Throwable e) {
617                                         Logger.defaultLogError(e);
618                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
619                                 }
620
621                         }
622
623         };
624     }
625
626     public static AbstractEventHandler readEventHandler(ReadGraph graph, final Variable variable, final Function fn) {
627
628         final Session session = graph.getSession();
629
630         return new AbstractEventHandler() {
631
632                         @Override
633                         public CommandResult handle(final CommandContext parameters) {
634
635                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
636
637                 try {
638
639                                         String result = session.sync(new UniqueRead<String>() {
640
641                                             @Override
642                                             public String perform(ReadGraph graph) throws DatabaseException {
643                                                 SCLContext sclContext = SCLContext.getCurrent();
644                                                 Object oldGraph = sclContext.put("graph", graph);
645                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
646                                                 try {
647                                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
648                                                         @Override
649                                                         public String apply(String key) {
650                                                             return parameters.getString(key);
651                                                         }
652                                                     }; 
653                                                     Object response = (String)fn.apply(variable, pf);
654                                                     if(response instanceof String) {
655                                                         return (String)response;
656                                                     }
657                                                     return null;
658                                                 } catch (Throwable t) {
659                                                     t.printStackTrace();
660                                                 } finally {
661                                                         sclContext.put("graph", oldGraph);
662                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
663                                                 }
664
665                                                 return null;
666
667                                             }
668
669                                         });
670
671                         return new SuccessResponse(result);
672
673                                 } catch (Throwable e) {
674                                         Logger.defaultLogError(e);
675                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
676                                 }
677
678                         }
679
680         };
681     }
682
683     public static AbstractEventHandler readEventHandler2(ReadGraph graph, final Function fn) {
684
685         final Session session = graph.getSession();
686
687         return new AbstractEventHandler() {
688
689                         @Override
690                         public CommandResult handle(final CommandContext parameters) {
691
692                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
693
694                 try {
695
696                         CommandResult result = session.sync(new UniqueRead<CommandResult>() {
697
698                                             @Override
699                                             public CommandResult perform(ReadGraph graph) throws DatabaseException {
700                                                 SCLContext sclContext = SCLContext.getCurrent();
701                                                 Object oldGraph = sclContext.put("graph", graph);
702                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
703                                                 try {
704                                                     Object response = fn.apply(parameters);
705                                                     if(response instanceof CommandResult) {
706                                                         return (CommandResult)response;
707                                                     }
708                                                     return null;
709                                                 } catch (Throwable t) {
710                                                     t.printStackTrace();
711                                                 } finally {
712                                                         sclContext.put("graph", oldGraph);
713                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
714                                                 }
715
716                                                 return null;
717
718                                             }
719
720                                         });
721
722                         return result;
723
724                                 } catch (Throwable e) {
725                                         Logger.defaultLogError(e);
726                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
727                                 }
728
729                         }
730
731         };
732     }
733
734     public static AbstractEventHandler responseHandler(ReadGraph graph, Variable self, String function) throws DatabaseException {
735
736         Variable anyFunction = self.browse(graph, ".#" + function);
737         if(anyFunction == null) return null;
738
739         final List<TCon> effects = ServerSCLHandlerValueRequest.getEffects(graph, anyFunction);
740
741         final Function1<CommandContext, CommandResult> fn = anyFunction.getValue(graph);
742         String expression = anyFunction.getPropertyValue(graph, "expression");
743
744         final Session session = graph.getSession();
745
746         return new AbstractResponseHandler(expression) {
747
748                 private String formatError(RequestProcessor proc, Throwable t) {
749
750                         try {
751
752                                         return proc.syncRequest(new UniqueRead<String>() {
753
754                                                 @Override
755                                                 public String perform(ReadGraph graph) throws DatabaseException {
756                                                         Variable proxy = ProxyVariables.proxyVariableRoot(graph, anyFunction);
757                                                         String uri2 = proxy.getURI(graph);
758                                                         String uri = self.getParent(graph).getURI(graph);
759
760                                                         String path = uri.substring(uri2.length());
761
762                                                         String expr = anyFunction.getPossiblePropertyValue(graph, "expression");
763                                                         StringBuilder message = new StringBuilder();
764                                                         message.append("Handler execution failed\n");
765                                                         message.append(" handler=" + path + "\n");
766                                                         message.append(" expression=" + expr + "\n");
767                                                         message.append(" message=" + t.getMessage() + "\n");
768
769                                                         StringWriter sw = new StringWriter();
770                                                         t.printStackTrace(new PrintWriter(sw));
771                                                         message.append(" stack trace=" + sw);
772
773                                                         return message.toString();
774                                                 }
775
776                                         });
777
778                                 } catch (DatabaseException e) {
779
780                                         return e.getMessage();
781
782                                 }
783
784                 }
785
786                         @Override
787                         public CommandResult handle(final CommandContext parameters) {
788
789                                 IConsole console = parameters.getValue("__console__");
790                                 SCLReportingHandler printer = (console != null) ? new ConsoleSCLReportingHandler(console)
791                                                 : (SCLReportingHandler) SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
792
793                 try {
794
795                         Object result = null;
796                         if(effects.contains(Types.WRITE_GRAPH)) {
797
798                                 result = session.syncRequest(new WriteResultRequest<Object>() {
799
800                                                         @Override
801                                                         public Object perform(WriteGraph graph)
802                                                                         throws DatabaseException {
803                                                 SCLContext sclContext = SCLContext.getCurrent();
804                                                 Object oldGraph = sclContext.put("graph", graph);
805                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
806                                                 try {
807                                                         Object response = fn.apply(parameters);
808                                                         return response;
809                                                 } catch (Throwable t) {
810                                                         return new org.simantics.document.server.serverResponse.Error(formatError(graph, t));
811                                                 } finally {
812                                                         sclContext.put("graph", oldGraph);
813                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
814                                                 }
815
816                                                         }
817                                 });
818
819                         } else if(effects.contains(Types.READ_GRAPH)) {
820
821                                 result = session.sync(new UniqueRead<Object>() {
822
823                                         @Override
824                                         public Object perform(ReadGraph graph) throws DatabaseException {
825
826                                                 SCLContext sclContext = SCLContext.getCurrent();
827                                                 Object oldGraph = sclContext.put("graph", graph);
828                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
829
830                                                 try {
831                                                         Object response = fn.apply(parameters);
832                                                         return response;
833                                                 } catch (Throwable t) {
834                                                         return new org.simantics.document.server.serverResponse.Error(formatError(graph, t));
835                                                 } finally {
836                                                         sclContext.put("graph", oldGraph);
837                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
838                                                 }
839
840                                         }
841                                 });
842
843                         } else {
844
845                                                 SCLContext sclContext = SCLContext.getCurrent();
846                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
847                                                 try {
848                                                         result = fn.apply(parameters);
849                                                 } finally {
850                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
851                                                 }
852
853                         }
854
855                         if (result instanceof org.simantics.document.server.serverResponse.Error) {
856                                 return (CommandResult)result;
857                         }
858
859                         if (result instanceof CommandResult) {
860                                 return (CommandResult)result;
861                         } else {
862                                 CommandContextMutable assignments = new CommandContextImpl();
863                                 assignments.putValue("result", result);
864                                 return new ServerResponse(200, "", assignments);
865                         }
866
867                                 } catch (Throwable e) {
868                                         return new org.simantics.document.server.serverResponse.Error(formatError(Simantics.getSession(), e));
869                                 }
870
871                         }
872
873         };
874     }
875
876     public static AbstractEventHandler writeEventHandler2(ReadGraph graph, final Function fn) {
877
878         final Session session = graph.getSession();
879
880         return new AbstractEventHandler() {
881
882                         @Override
883                         public CommandResult handle(final CommandContext parameters) {
884
885                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
886
887                 try {
888
889                         CommandResult result = session.syncRequest(new WriteResultRequest<CommandResult>() {
890
891                                             @Override
892                                             public CommandResult perform(WriteGraph graph) throws DatabaseException {
893                                                 SCLContext sclContext = SCLContext.getCurrent();
894                                                 Object oldGraph = sclContext.put("graph", graph);
895                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
896                                                 try {
897                                                     Object response = fn.apply(parameters);
898                                                     if(response instanceof CommandResult) {
899                                                         return (CommandResult)response;
900                                                     }
901                                                     return null;
902                                                 } catch (Throwable t) {
903                                                     t.printStackTrace();
904                                                 } finally {
905                                                         sclContext.put("graph", oldGraph);
906                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
907                                                 }
908
909                                                 return null;
910
911                                             }
912
913                                         });
914
915                         return result;
916
917                                 } catch (Throwable e) {
918                                         Logger.defaultLogError(e);
919                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
920                                 }
921
922                         }
923
924         };
925     }
926
927     public static AbstractEventHandler eventHandler2(ReadGraph graph, final Function fn) {
928
929         return new AbstractEventHandler() {
930
931                         @Override
932                         public CommandResult handle(final CommandContext parameters) {
933
934                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
935
936                 try {
937
938                         SCLContext sclContext = SCLContext.getCurrent();
939                         Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
940                         try {
941                                 Object response = fn.apply(parameters);
942                                 if(response instanceof CommandResult) {
943                                         return (CommandResult)response;
944                                 }
945                                 return null;
946                         } catch (Throwable t) {
947                                 t.printStackTrace();
948                         } finally {
949                                 sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
950                         }
951
952                         return null;
953
954                                 } catch (Throwable e) {
955                                         Logger.defaultLogError(e);
956                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
957                                 }
958
959                         }
960
961         };
962     }
963
964     public static AbstractEventHandler eventHandler(ReadGraph graph, final Function fn) {
965
966         return new AbstractEventHandler() {
967
968                         @Override
969                         public CommandResult handle(final CommandContext parameters) {
970
971                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
972
973                 try {
974
975                         String result = "";
976
977                                 SCLContext sclContext = SCLContext.getCurrent();
978                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
979                                 try {
980                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
981                                         @Override
982                                         public String apply(String key) {
983                                             return parameters.getString(key);
984                                         }
985                                     }; 
986                                     Object response = (String)fn.apply(pf);
987                                     if(response instanceof String) {
988                                         result = (String)response;
989                                     }
990                                 } catch (Throwable t) {
991                                     t.printStackTrace();
992                                 } finally {
993                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
994                                 }
995
996                         return new SuccessResponse(result);
997
998                                 } catch (Throwable e) {
999                                         Logger.defaultLogError(e);
1000                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
1001                                 }
1002
1003                         }
1004
1005         };
1006     }
1007
1008     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1009     public static Object sclValue(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1010         return ServerSCLValueRequest.compileAndEvaluate(graph, context);
1011     }
1012
1013     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1014     public static Object sclHandlerValue(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1015         return ServerSCLHandlerValueRequest.compileAndEvaluate(graph, context);
1016     }
1017
1018     @SCLValue(type = "ReadGraph -> Resource -> Resource -> ComponentNamingStrategy")
1019     public static ComponentNamingStrategy componentNamingStrategy(ReadGraph graph, Resource converter, Resource context) throws DatabaseException {
1020         return createComponentNamingStrategy(graph);
1021     }
1022
1023     public static CaseInsensitiveComponentFunctionNamingStrategy createComponentNamingStrategy(ReadGraph graph) throws DatabaseException {
1024         Layer0X L0X = Layer0X.getInstance(graph);
1025         @SuppressWarnings({ "unchecked", "rawtypes" })
1026         final Function dependencies = graph.syncRequest(new Adapter(L0X.DependencyResources, Function.class), TransientCacheListener.<Function>instance());
1027         @SuppressWarnings("rawtypes")
1028         Function moduleNameFunction = new FunctionImpl4<ReadGraph, Resource, String, Integer, List<Map<String, Object>>>() {
1029             @Override
1030             public Object apply(ReadGraph p0, Resource p1, String p2) {
1031                 return apply(p0, p1, p2);
1032             }
1033             @SuppressWarnings("unchecked")
1034             @Override
1035             public List<Map<String, Object>> apply(ReadGraph graph, Resource model, String search, Integer maxResults) {
1036                 return (List<Map<String, Object>>)dependencies.apply(graph, model, search, maxResults);
1037             }
1038         };
1039         return new CaseInsensitiveComponentFunctionNamingStrategy("%s%02d", moduleNameFunction);
1040     }
1041
1042     public static IExperiment getExperiment(ReadGraph graph, Variable variable) throws DatabaseException {
1043         if (variable == null)
1044             return null;
1045
1046         SimulationResource SIMU = SimulationResource.getInstance(graph);
1047
1048         Variable var = variable;
1049         Resource represents = var.getRepresents(graph);
1050         Resource activeRun = null;
1051         if (represents != null && graph.isInstanceOf(represents, SIMU.Run)) {
1052             activeRun = represents;
1053         }
1054
1055         IProject project = Simantics.peekProject();
1056         if (activeRun != null && project != null) {
1057             IExperimentManager expMan = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER);
1058             if (expMan != null)
1059                 return expMan.getExperiment(NameUtils.getSafeName(graph, activeRun));
1060         }
1061
1062         return null;
1063     }
1064
1065     public static CommandContextMutable putTuple(CommandContextMutable context, String key, Object tuple) {
1066         List<Object> list = new ArrayList<Object>();
1067         if (tuple instanceof Tuple) {
1068                 Collections.addAll(list, ((Tuple)tuple).toArray());
1069         } else {
1070                 list.add(tuple);
1071         }
1072         context.putRow(key, list);
1073         return context;
1074     }
1075
1076     public static List<Object> getTuples(CommandContext context, String key) {
1077         List<List<Object>> rows = context.getRows(key);
1078         List<Object> tuples = new ArrayList<Object>();
1079         if (rows != null) {
1080                 for (List<Object> row : rows) {
1081                         switch (row.size()) {
1082                         case 0: tuples.add(Tuple0.INSTANCE); break;
1083                         case 1: tuples.add(row.get(1)); break;
1084                         case 2: tuples.add(new Tuple2(row.get(0), row.get(1))); break;
1085                         case 3: tuples.add(new Tuple3(row.get(0), row.get(1), row.get(2))); break;
1086                         case 4: tuples.add(new Tuple4(row.get(0), row.get(1), row.get(2), row.get(3))); break;
1087                         case 5: tuples.add(new Tuple5(row.get(0), row.get(1), row.get(2), row.get(3), row.get(4))); break;
1088                         }
1089                 }
1090         }
1091         return tuples;
1092     }
1093
1094     public static String printContext(CommandContext context) {
1095         return context.toString();
1096     }
1097
1098     @SCLValue(type = "AbstractEventHandler")
1099     public static AbstractEventHandler emptyEvent = new AbstractEventHandler() {
1100
1101                 @Override
1102                 public CommandResult handle(CommandContext context) {
1103                         return null;
1104                 }
1105
1106     };
1107
1108     public static String sclStateKey(ReadGraph graph, Variable base, Variable self, String ref) throws DatabaseException {
1109
1110         String baseURI = base.getURI(graph);
1111
1112         String selfURI = self.getURI(graph);
1113
1114         String prefix = selfURI.substring(0, selfURI.indexOf(ProxyChildVariable.CONTEXT_BEGIN));
1115         String suffix = selfURI.substring(selfURI.lastIndexOf(ProxyChildVariable.CONTEXT_END) + ProxyChildVariable.CONTEXT_END.length());
1116         String stripped = prefix + suffix;
1117
1118         String relative = Variables.getRelativeRVI(baseURI, stripped); 
1119
1120         return Variables.getRVI(relative, ref);
1121
1122     }
1123
1124     public static Variable sclStateVariable(ReadGraph graph, Variable base, Variable self, String ref) throws DatabaseException {
1125
1126         String id = sclStateKey(graph, base, self, ref);
1127
1128         Variable sclVar = base.getPossibleChild(graph, "__scl__");
1129         if(sclVar == null) return null;
1130
1131         return sclVar.getPossibleProperty(graph, id);
1132
1133     }
1134
1135     public static Object sclStateValueOrDefault(ReadGraph graph, Variable base, Variable self, String ref, Object defaultValue) throws DatabaseException {
1136
1137         Variable stateVariable = sclStateVariable(graph, base, self, ref);
1138         if (stateVariable != null) {
1139
1140                 return stateVariable.getValue(graph);
1141
1142         } else {
1143
1144                 String id = sclStateKey(graph, base, self, ref);
1145
1146                 SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph) + "/__scl__");
1147                 realm.getConnection().setVariable(id, getSCLType(defaultValue), defaultValue);
1148
1149                 return defaultValue;
1150
1151         }
1152     }
1153
1154     public static void setSclStateValue(WriteGraph graph, Variable base, Variable self, String ref, Object value) throws DatabaseException {
1155
1156         String id = sclStateKey(graph, base, self, ref);
1157
1158         SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph)+"/__scl__");
1159         realm.getConnection().setVariable(id, getSCLType(value), value);
1160         realm.refreshVariablesSync();
1161
1162     }
1163
1164     public static Object projectComponentState(ReadGraph graph, Variable self, String ref, Object defaultValue) throws DatabaseException {
1165         Resource project = Simantics.getProjectResource();
1166         Variable component = self.getParent(graph);
1167         Variable projectVariable = Variables.getVariable(graph, project);
1168         return sclStateValueOrDefault(graph, projectVariable, component, ref, defaultValue);
1169     }
1170
1171     public static void setProjectComponentState(WriteGraph graph, Variable self, String ref, Object value) throws DatabaseException {
1172         Resource project = Simantics.getProjectResource();
1173         Variable component = self.getParent(graph);
1174         Variable projectVariable = Variables.getVariable(graph, project);
1175         setSclStateValue(graph, projectVariable, component, ref, value);
1176     }
1177
1178     private static Type getSCLType(Object value) throws DatabaseException {
1179         Binding b = Bindings.getBindingUnchecked(value.getClass());
1180         Datatype t = b.type();
1181         if(Datatypes.STRING.equals(t)) return Types.STRING;
1182         if(Datatypes.DOUBLE.equals(t)) return Types.DOUBLE;
1183         throw new DatabaseException("Type not supported");
1184     }
1185
1186     public static List<Variable> documentModelContribution(ReadGraph graph, Resource doc) throws DatabaseException {
1187         Resource project = Simantics.getProjectResource();
1188         ArrayList<Variable> result = new ArrayList<Variable>();
1189         for(Resource model : graph.syncRequest(new ProjectModels(project))) {
1190                 result.add(Variables.getVariable(graph, model));
1191         }
1192         return result;
1193     }
1194
1195     public static String documentModelContributionLabel(ReadGraph graph, Variable var) throws DatabaseException {
1196         return var.getName(graph);
1197     }
1198
1199     public static Object getPropertyValueCached(ReadGraph graph, Variable variable, String name, Binding binding) throws DatabaseException {
1200         Variable property = graph.syncRequest(new VariableProperty(variable, name));
1201         return graph.syncRequest(new VariableValueWithBinding<Object>(property, binding));
1202     }
1203
1204     public static class ParentExistsRequest extends VariableRead<Boolean> {
1205
1206                 public ParentExistsRequest(Variable parent) {
1207                         super(parent);
1208                 }
1209
1210                 @Override
1211                 public Boolean perform(ReadGraph graph) throws DatabaseException {
1212                         
1213                         Variable existsProperty = variable.getPossibleProperty(graph, "exists");
1214                         if(existsProperty == null) return true;
1215                         
1216                         Boolean exists = existsProperty.getPossibleValue(graph, Bindings.BOOLEAN);
1217                         if (exists == null || !exists) return false;
1218
1219                 return graph.syncRequest(new ParentExistsRequest(variable.getParent(graph)));
1220
1221                 }
1222
1223     } 
1224
1225     public static class PathExistsRequest extends VariableRead<Boolean> {
1226
1227                 public PathExistsRequest(Variable variable) {
1228                         super(variable);
1229                 }
1230
1231                 @Override
1232                 public Boolean perform(ReadGraph graph) throws DatabaseException {
1233
1234                         Variable widget = variable.getParent(graph);
1235
1236                 Boolean exists = widget.getPossiblePropertyValue(graph, "exists");
1237                         if (exists == null || !exists) return false;
1238
1239                 if (!graph.syncRequest(new ParentExistsRequest(widget.getParent(graph)))) return false;
1240
1241                 DocumentationResource DOC = DocumentationResource.getInstance(graph);
1242                 Collection <Variable> cps = widget.getProperties(graph, DocumentationResource.URIs.Relations_parentRelation);
1243                 for (Variable cp : cps) {
1244
1245                         Connection conn = cp.getValue(graph);
1246                                 Variable otherCp = DocumentServerUtils.getPossibleChildConnectionPoint(graph, cp, conn);
1247                                 if (otherCp != null) {
1248                                         Variable parentWidget = otherCp.getParent(graph);
1249                                         if (parentWidget.<Boolean>getPropertyValue(graph, "pathExists")) {
1250                                                 return true;
1251                                         }
1252                                 } else {
1253                                         Variable parentCp = graph.sync(new UnaryRead<Connection, Variable>(conn) {
1254                                     @Override
1255                                     public Variable perform(ReadGraph graph) throws DatabaseException {
1256                                         DocumentationResource DOC = DocumentationResource.getInstance(graph);
1257                                         Collection<VariableConnectionPointDescriptor> descs = parameter.getConnectionPointDescriptors(graph, null);
1258
1259                                                 for(VariableConnectionPointDescriptor desc : descs) {
1260                                                         if (DOC.Relations_partN.equals(desc.getConnectionPointResource(graph))) {
1261                                                                 return desc.getVariable(graph);
1262                                                         }
1263                                                 }
1264                                                 return null;
1265                                     }
1266                                 });
1267                                         if (parentCp != null) {
1268                                                 Variable parentWidget = parentCp.getParent(graph);
1269                                                 if (parentWidget.<Boolean>getPropertyValue(graph, "pathExists")) {
1270                                                         return true;
1271                                                 }
1272                                         }
1273                                 }
1274                         }
1275
1276                         Resource type = widget.getType(graph);
1277                         return graph.isInheritedFrom(type, DOC.Components_ParentlessComponent) ||
1278                                 (graph.isInheritedFrom(type, DOC.DocumentComponent) && cps.isEmpty());
1279                 }
1280     }
1281
1282     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean")
1283     public static boolean pathExists(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1284         return graph.syncRequest(new PathExistsRequest(context));
1285     }
1286
1287 }