]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/EditSelectorDialog.java
4814124b010954cffb030c3aa39aeb3f81b31b43
[simantics/district.git] / org.simantics.district.selection.ui / src / org / simantics / district / selection / ui / parts / EditSelectorDialog.java
1 package org.simantics.district.selection.ui.parts;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.UUID;
13 import java.util.function.Consumer;
14
15 import javax.inject.Inject;
16
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.core.runtime.Status;
19 import org.eclipse.jface.dialogs.Dialog;
20 import org.eclipse.jface.dialogs.ErrorDialog;
21 import org.eclipse.jface.layout.GridDataFactory;
22 import org.eclipse.jface.layout.GridLayoutFactory;
23 import org.eclipse.jface.layout.RowDataFactory;
24 import org.eclipse.jface.layout.RowLayoutFactory;
25 import org.eclipse.jface.resource.ImageDescriptor;
26 import org.eclipse.jface.resource.JFaceResources;
27 import org.eclipse.jface.resource.LocalResourceManager;
28 import org.eclipse.jface.resource.ResourceLocator;
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.widgets.Button;
33 import org.eclipse.swt.widgets.Combo;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.Text;
39 import org.eclipse.swt.widgets.Widget;
40 import org.simantics.Simantics;
41 import org.simantics.db.ReadGraph;
42 import org.simantics.db.Resource;
43 import org.simantics.db.WriteGraph;
44 import org.simantics.db.common.request.IndexRoot;
45 import org.simantics.db.common.request.ReadRequest;
46 import org.simantics.db.common.request.WriteRequest;
47 import org.simantics.db.exception.DatabaseException;
48 import org.simantics.db.exception.RuntimeDatabaseException;
49 import org.simantics.db.layer0.QueryIndexUtils;
50 import org.simantics.db.layer0.request.ActiveModels;
51 import org.simantics.db.layer0.request.PropertyInfo;
52 import org.simantics.db.layer0.request.PropertyInfoRequest;
53 import org.simantics.db.layer0.util.Layer0Utils;
54 import org.simantics.db.request.Read;
55 import org.simantics.diagram.stubs.DiagramResource;
56 import org.simantics.district.network.ontology.DistrictNetworkResource;
57 import org.simantics.district.region.ontology.DiagramRegionsResource;
58 import org.simantics.district.route.ontology.RouteResource;
59 import org.simantics.district.selection.ElementSelectionResource;
60 import org.simantics.district.selection.ElementSelectionUtils;
61 import org.simantics.district.selection.ElementSelector;
62 import org.simantics.district.selection.ElementSelector.AggregateCondition;
63 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
64 import org.simantics.district.selection.ElementSelector.All;
65 import org.simantics.district.selection.ElementSelector.Condition;
66 import org.simantics.district.selection.ElementSelector.DiagramGenerator;
67 import org.simantics.district.selection.ElementSelector.ExplicitGenerator;
68 import org.simantics.district.selection.ElementSelector.Generator;
69 import org.simantics.district.selection.ElementSelector.ModelGenerator;
70 import org.simantics.district.selection.ElementSelector.PropertyCondition;
71 import org.simantics.district.selection.ElementSelector.PropertySelector;
72 import org.simantics.district.selection.ElementSelector.RegionCondition;
73 import org.simantics.district.selection.ElementSelector.RouteCondition;
74 import org.simantics.district.selection.ElementSelector.Selector;
75 import org.simantics.layer0.Layer0;
76 import org.simantics.layer0.utils.direct.GraphUtils;
77 import org.simantics.modeling.ModelingResources;
78 import org.simantics.structural.stubs.StructuralResource2;
79 import org.simantics.utils.datastructures.Arrays;
80 import org.simantics.utils.datastructures.Pair;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83
84 public class EditSelectorDialog extends Dialog {
85
86         private static final RowLayoutFactory ROW_LAYOUT = RowLayoutFactory.fillDefaults().wrap(false);
87         private static final ImageDescriptor CROSS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/cross.png").get();
88         private static final ImageDescriptor PLUS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/add.png").get();
89
90         private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
91         
92         private ElementSelector elementSelector;
93         
94         // Currently selected elements
95         Collection<Resource> currentSelection;
96
97         // Data for comboboxes
98         private List<Resource> diagrams;
99         private ArrayList<String> diagramNames;
100         
101         private String[] regionNames;
102         private Resource[] regionResources;
103
104         private String[] routeNames;
105         private Resource[] routeResources;
106         
107         private List<Resource> componentTypes;
108         private List<String> componentTypeNames;
109         
110         private List<String> propertyNames;
111         private List<String> propertyLabels;
112         
113         private Composite conditionPanel;
114
115         // Dialog fields
116         private int generatorIndex;
117         private Combo sourceField;
118
119         private String name;
120         private Text nameField;
121
122         private Resource diagram;
123         private Combo diagramField;
124
125         private int selectorIndex;
126         private Combo selectorField;
127
128         private Resource componentType;
129         private Combo componentTypeField;
130         
131         private String propertyName;
132         private Combo propertyField;
133
134         private int numberOfItems;
135         private Text nField;
136
137         private Condition condition;
138
139         // Dialog area component
140         private Composite content;
141
142         private int diagramIndex;
143
144         private LocalResourceManager resourceManager;
145         
146         // Function type for updating condition objects with optional validation
147         static interface Updater {
148                 // If 'validate' is true, a runtime exception may be thrown for invalid values
149                 void update(boolean validate);
150         }
151         
152         final static Updater NULL_UPDATE = validate -> {};
153         
154         // Called to read values from controls into conditions
155         Updater updater = NULL_UPDATE;
156
157         @Inject
158         public EditSelectorDialog(Shell shell, ElementSelector elementSelector, Collection<Resource> currentSelection) {
159                 super(shell);
160                 
161                 this.elementSelector = elementSelector;
162                 if (elementSelector != null) {
163                         try {
164                                 Simantics.getSession().sync(new ReadRequest() {
165                                         @Override
166                                         public void run(ReadGraph graph) throws DatabaseException {
167                                                 elementSelector.buildSelection(graph);
168                                         }
169                                 });
170                         } catch (DatabaseException e1) {
171                                 LOGGER.error("Failed to read element selector resource " + elementSelector.getResource(), e1);
172                                 throw new RuntimeDatabaseException(e1);
173                         }
174                 }
175                 
176                 this.currentSelection = currentSelection;
177                 
178                 Map<Resource, String> diagramMap = ElementSelector.findDiagrams();
179                 diagrams = new ArrayList<Resource>(diagramMap.size());
180                 diagramNames = new ArrayList<String>(diagramMap.size());
181                 diagramMap.entrySet()
182                         .stream()
183                         .sorted(Comparator.comparing(e -> e.getValue()))
184                         .forEachOrdered(e -> {
185                                 diagrams.add(e.getKey());
186                                 diagramNames.add(e.getValue());
187                         });
188                 
189                 final Map<Resource, String> regions = new HashMap<>();
190                 final Map<Resource, String> routes = new HashMap<>();
191                 
192                 try {
193                         Simantics.getSession().syncRequest(new Read<Void>() {
194                                 @Override
195                                 public Void perform(ReadGraph graph) throws DatabaseException {
196                                         Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
197                                         List<Resource> regionCollection = QueryIndexUtils.searchByType(graph, model, DiagramRegionsResource.getInstance(graph).Region);
198                                         for (Resource r : regionCollection) {
199                                                 String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
200                                                 regions.put(r, name);
201                                         }
202                                         
203                                         List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, RouteResource.getInstance(graph).Route);
204                                         for (Resource r : routeCollection) {
205                                                 String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
206                                                 routes.put(r, name);
207                                         }
208                                         return null;
209                                 }
210                         });
211                 } catch (DatabaseException e) {
212                         LOGGER.error("Failed to read routes and/or regions in the model", e);
213                 }
214                 
215                 regionNames = regions.values().toArray(new String[regions.size()]);
216                 regionResources = regions.keySet().toArray(new Resource[regions.size()]);
217                 
218                 routeNames = routes.values().toArray(new String[routes.size()]);
219                 routeResources = routes.keySet().toArray(new Resource[routes.size()]);
220                 
221                 try {
222                         Simantics.getSession().syncRequest(new ReadRequest() {
223                                 @Override
224                                 public void run(ReadGraph graph) throws DatabaseException {
225                                         Layer0 L0 = Layer0.getInstance(graph);
226                                         List<Resource> types = findComponentTypes(graph);
227                                         
228                                         componentTypes = new ArrayList<>(types.size() + 1);
229                                         componentTypeNames = new ArrayList<>(types.size() + 1);
230                                         
231                                         componentTypes.add(null);
232                                         componentTypeNames.add("Any type");
233                                         componentTypes.addAll(types);
234                                         for (Resource t : types) {
235                                                 componentTypeNames.add(graph.getValue2(t, L0.HasName));
236                                         }
237                                 }
238                         });
239                 } catch (DatabaseException e) {
240                         LOGGER.error("Failed to read district component types", e);
241                 }
242                 
243                 componentType = elementSelector.getSelector().componentType;
244                 
245                 propertyNames = new ArrayList<>();
246                 propertyLabels = new ArrayList<>();
247                 
248                 try {
249                         updatePropertyList();
250                 } catch (DatabaseException e) {
251                         LOGGER.error("Failed to read district component properties", e);
252                 }
253         
254                 name = elementSelector != null ? elementSelector.getName() : "";
255                 propertyName = "";
256                 numberOfItems = 1;
257                 generatorIndex = 0;
258                 selectorIndex = 0;
259                 diagram = null;
260                 condition = null;
261                 
262                 if (elementSelector != null) {
263                         Generator generator = elementSelector.getGenerator();
264                         if (generator instanceof ModelGenerator) {
265                                 generatorIndex = 0;
266                         }
267                         else if (generator instanceof DiagramGenerator) {
268                                 generatorIndex = 1;
269                                 diagram = ((DiagramGenerator)generator).diagram;
270                         }
271                         else if (generator instanceof ExplicitGenerator) {
272                                 generatorIndex = 2;
273                         }
274                         else {
275                                 throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
276                         }
277                         
278                         Selector selector = elementSelector.getSelector();
279                         if (selector instanceof All) {
280                                 selectorIndex = 0;
281                         }
282                         else if (selector instanceof PropertySelector) {
283                                 PropertySelector propertySelector = (PropertySelector)selector;
284                                 selectorIndex = propertySelector.smallest ? 1 : 2;
285                                 propertyName = propertySelector.propertyName;
286                                 numberOfItems = propertySelector.resultCount;
287                         }
288                         else {
289                                 throw new IllegalStateException("Unknwon selector type " + selector.getClass().getName());
290                         }
291                         
292                         condition = elementSelector.getCondition();
293                 }
294         }
295         
296         private void updateDialog() {
297                 updater.update(false);
298                 updater = updateConditionPanel();
299                 
300                 content.layout(true, true);
301                 getShell().pack();
302         }
303
304         @Override
305         protected void okPressed() {
306                 generatorIndex = sourceField.getSelectionIndex();
307                 if (generatorIndex == 1) {
308                         int selectionIndex = diagramField.getSelectionIndex();
309                         if (selectionIndex < 0) {
310                                 ErrorDialog.openError(getShell(), "Error", "Please select a diagram", new Status(IStatus.ERROR, "org.simantics.district.selection.ui", "No diagram selected"));
311                                 return;
312                         }
313                         
314                         diagram = diagrams.get(selectionIndex);
315                 }
316                 
317                 name = nameField.getText();
318                 componentType = componentTypes.get(componentTypeField.getSelectionIndex());
319                 selectorIndex = selectorField.getSelectionIndex();
320                 int propertyIndex = propertyField.getSelectionIndex();
321                 propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
322                 
323                 // Try to parse number of items
324                 if (useNumberOfItems()) {
325                         try {
326                                 numberOfItems = Integer.parseInt(nField.getText());
327                         } catch (RuntimeException e) {
328                                 nField.selectAll();
329                                 nField.forceFocus();
330                                 return;
331                         }
332                 }
333
334                 // To to update condition definitions
335                 try {
336                         updater.update(true);
337                 } catch (RuntimeException e) {
338                         return;
339                 }
340                 
341                 super.okPressed();
342         }
343         
344         public void writeSelection() throws DatabaseException {
345                 Simantics.getSession().syncRequest(new WriteRequest() {
346                         @Override
347                         public void perform(WriteGraph graph) throws DatabaseException {
348                                 Layer0 L0 = Layer0.getInstance(graph);
349                                 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
350                                 ModelingResources MOD = ModelingResources.getInstance(graph);
351                                 DiagramResource DIA = DiagramResource.getInstance(graph);
352                                 
353                                 graph.markUndoPoint();
354                                 Layer0Utils.addCommentMetadata(graph, "Created new element selection");
355                                 
356                                 Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
357                                 
358                                 // Selection
359                                 Resource selection;
360                                 if (elementSelector != null) {
361                                         selection = elementSelector.getResource();
362                                         graph.deny(selection);
363                                 }
364                                 else {
365                                         selection = graph.newResource();
366                                 }
367                                 
368                                 graph.claim(selection, L0.InstanceOf, ES.Selection);
369                                 graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
370                                 graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
371                                 graph.claim(selection, L0.PartOf, lib);
372                                 
373                                 // Generator
374                                 Resource generator = graph.newResource();
375                                 Resource generatorType;
376                                 switch (generatorIndex) {
377                                 case 0:
378                                         generatorType = ES.Generator_Model;
379                                         break;
380                                 case 1:
381                                         generatorType = ES.Generator_Diagram;
382                                         Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
383                                         graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
384                                         break;
385                                 case 2:
386                                         generatorType = ES.Generator_Explicit;
387                                         for (Resource r : currentSelection) {
388                                                 // No connections
389                                                 if (graph.isInstanceOf(r, DIA.Connection))
390                                                         continue;
391                                                 if (!graph.isInstanceOf(r, DIA.Element)) {
392                                                         if (!graph.hasStatement(r, MOD.ComponentToElement))
393                                                                 continue;
394                                                         
395                                                         r = graph.getPossibleObject(r, MOD.ComponentToElement);
396                                                         if (r == null)
397                                                                 continue;
398                                                 }
399                                                         
400                                                 graph.claim(generator, ES.Generator_HasSelectedElement, r);
401                                         }
402                                         break;
403                                 default: throw new IllegalStateException("Invalid source index " + generatorIndex);
404                                 }
405                                 graph.claim(generator, L0.InstanceOf, generatorType);
406                                 graph.claim(selection, ES.Selection_HasGenerator, generator);
407                                 
408                                 // Selector
409                                 Resource selector = graph.newResource();
410                                 Resource selectorType;
411                                 switch (selectorIndex) {
412                                 case 0: selectorType = ES.Selector_All; break;
413                                 case 1: selectorType = ES.Selector_NLowest; break;
414                                 case 2: selectorType = ES.Selector_NHighest; break;
415                                 default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
416                                 }
417                                 graph.claim(selector, L0.InstanceOf, selectorType);
418                                 graph.claim(selection, ES.Selection_HasSelector, selector);
419                                 graph.deny(selector, ES.Selector_HasMapping);
420                                 if (componentType != null)
421                                         graph.claim(selector, ES.Selector_HasMapping, componentType);
422                                 
423                                 if (selectorIndex > 0) {
424                                         graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
425                                         graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
426                                 }
427                                 
428                                 // Condition
429                                 if (condition != null) {
430                                         Resource conditionResource = condition.update(graph);
431                                         graph.claim(selection, ES.Selection_HasCondition, conditionResource);
432                                 }
433                         }
434                 });
435         }
436
437         private boolean isDiagramFieldVisible() {
438                 return generatorIndex == 1;
439         }
440
441         private boolean useNumberOfItems() {
442                 return selectorIndex != 0;
443         }
444
445         @Override
446         protected Control createDialogArea(Composite parent) {
447                 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
448                 
449                 // Set dialog title
450                 getShell().setText("Edit element selector");
451                 
452                 content = new Composite(parent, SWT.NONE);
453                 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
454                 
455                 // Name
456                 Label nameLabel = new Label(content, SWT.NONE);
457                 nameLabel.setText("Name");
458                 GridDataFactory.swtDefaults().applyTo(nameLabel);
459                 
460                 nameField = new Text(content, SWT.BORDER);
461                 nameField.setEditable(true);
462                 nameField.setText(name);
463                 GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
464                 
465                 // Selector
466                 Label selectorLabel = new Label(content, SWT.NONE);
467                 selectorLabel.setText("Select");
468                 GridDataFactory.swtDefaults().applyTo(selectorLabel);
469
470                 Composite selectorComposite = new Composite(content, SWT.NONE);
471                 GridDataFactory.swtDefaults().applyTo(selectorComposite);
472                 RowLayoutFactory.swtDefaults().applyTo(selectorComposite);
473                 
474                 nField = new Text(selectorComposite, SWT.BORDER);
475                 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
476                 if (useNumberOfItems())
477                         nField.setText(Integer.toString(numberOfItems));
478                 nField.setEnabled(useNumberOfItems());
479                 
480                 componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
481                 RowDataFactory.swtDefaults().applyTo(componentTypeField);
482                 componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
483                 {
484                         int index = componentTypes.indexOf(componentType);
485                         componentTypeField.select(index >= 0 ? index : 0);
486                 }
487                 
488                 new Label(selectorComposite, SWT.NONE).setText("with");
489                 
490                 selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
491                 selectorField.setItems("All", "Lowest", "Highest");
492                 selectorField.select(selectorIndex);
493                 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
494                 
495                 propertyField = new Combo(selectorComposite, SWT.NONE);
496                 RowDataFactory.swtDefaults().hint(80, SWT.DEFAULT).applyTo(propertyField);
497                 propertyField.setItems(propertyLabels.toArray(new String[] {}));
498                 {
499                         int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
500                         if (index >= 0)
501                                 propertyField.select(index);
502                         else
503                                 propertyField.setText(propertyName != null ? propertyName : "");
504                 }
505                 propertyField.setEnabled(useNumberOfItems());
506                 
507                 selectorField.addSelectionListener(new SelectionAdapter() {
508                         @Override
509                         public void widgetSelected(SelectionEvent e) {
510                                 selectorIndex = selectorField.getSelectionIndex();
511                                 
512                                 boolean enable = useNumberOfItems();
513                                 nField.setEnabled(enable);
514                                 propertyField.setEnabled(enable);
515                                 
516                                 nField.setText(enable ? Integer.toString(numberOfItems) : "");
517                         }
518                 });
519                 
520                 // Source
521                 Label sourceLabel = new Label(content, SWT.NONE);
522                 sourceLabel.setText("from");
523                 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
524                 
525                 Composite sourceComposite = new Composite(content, SWT.NONE);
526                 GridDataFactory.swtDefaults().applyTo(sourceComposite);
527                 RowLayoutFactory.swtDefaults().applyTo(sourceComposite);
528                 
529                 sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
530                 RowDataFactory.swtDefaults().applyTo(sourceField);
531                 sourceField.setItems("Whole model", "Diagram", "Current selection");
532                 sourceField.select(generatorIndex);
533                 
534                 diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
535                 RowDataFactory.swtDefaults().applyTo(diagramField);
536                 diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
537                 diagramField.setEnabled(isDiagramFieldVisible());
538                 
539                 diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
540                 diagramField.select(diagramIndex);
541                 
542                 sourceField.addSelectionListener(new SelectionAdapter() {
543                         @Override
544                         public void widgetSelected(SelectionEvent e) {
545                                 generatorIndex = sourceField.getSelectionIndex();
546                                 boolean enabled = isDiagramFieldVisible();
547                                 if (!enabled) {
548                                         diagramIndex = diagramField.getSelectionIndex();
549                                         diagramField.clearSelection();
550                                 } else {
551                                         if (diagramIndex >= 0)
552                                                 diagramField.select(diagramIndex);
553                                         else
554                                                 diagramField.clearSelection();
555                                 }
556                                 diagramField.setEnabled(enabled);
557                         }
558                 });
559                 
560                 sourceField.select(generatorIndex);
561                 
562                 // Condition
563                 Label label = new Label(content, SWT.NONE);
564                 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
565                 label.setText("where");
566                 
567                 conditionPanel = new Composite(content, SWT.NONE);
568                 GridDataFactory.swtDefaults().span(1, 2).minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
569                 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
570                 
571                 updater = updateConditionPanel();
572                 
573                 return content;
574         }
575         
576         private Updater updateConditionPanel() {
577                 // Erase contents
578                 for (Widget c : conditionPanel.getChildren())
579                         c.dispose();
580                 
581                 return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
582         }
583
584         private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
585                 // Create new contents
586                 Button notCheck = new Button(parent, SWT.CHECK);
587                 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
588                 notCheck.setText("not");
589                 notCheck.setSelection(condition.isInverse);
590                 
591                 Composite conditionComp = new Composite(parent, SWT.NONE);
592                 GridDataFactory.fillDefaults().applyTo(conditionComp);
593                 
594                 Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
595                 typeCombo.setItems(
596                                 "No condition",
597                                 "Property",
598                                 "In region",
599                                 "On route",
600                                 "All of",
601                                 "Any of"
602                         );
603                 
604                 final Updater updater;
605                 if (condition instanceof PropertyCondition) {
606                         typeCombo.select(1);
607                         updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
608                 } else if (condition instanceof RegionCondition) {
609                         typeCombo.select(2);
610                         updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
611                 } else if (condition instanceof RouteCondition) {
612                         typeCombo.select(3);
613                         updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
614                 } else if (condition instanceof AggregateCondition) {
615                         AggregateCondition cond = (AggregateCondition) condition;
616                         typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
617                         updater = createAggregateConditionPanel(conditionComp, cond);
618                 } else {
619                         ROW_LAYOUT.applyTo(conditionComp);
620                         notCheck.setEnabled(false);
621                         typeCombo.select(0);
622                         updater = validate -> {};
623                 }
624                 
625                 typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
626
627                 return validate -> {
628                         updater.update(validate);
629                         condition.isInverse = notCheck.getSelection();
630                 };
631         }
632         
633         private final class ConditionTypeSelectionListener extends SelectionAdapter {
634                 private final Combo typeCombo;
635                 private final Consumer<Condition> consumer;
636                 private final Condition finalCondition;
637         
638                 private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
639                         this.typeCombo = typeCombo;
640                         this.consumer = consumer;
641                         this.finalCondition = finalCondition;
642                 }
643         
644                 @Override
645                 public void widgetSelected(SelectionEvent e) {
646                         int index = typeCombo.getSelectionIndex();
647                         Condition newCondition = finalCondition;
648                         switch (index) {
649                         case 0:
650                                 newCondition = null;
651                                 break;
652                         case 1:
653                                 newCondition = createPropertyCondition("", null, null);
654                                 break;
655                         case 2:
656                                 newCondition = createRegionCondition(null);
657                                 break;
658                         case 3:
659                                 newCondition = createRouteCondition(null);
660                                 break;
661                         case 4:
662                                 if (newCondition instanceof AggregateCondition)
663                                         ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
664                                 else
665                                         newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
666                                 break;
667                         case 5:
668                                 if (newCondition instanceof AggregateCondition)
669                                         ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
670                                 else
671                                         newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
672                                 break;
673                         }
674                 
675                         consumer.accept(newCondition);
676                         
677                         updateDialog();
678                 }
679         }
680
681         private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
682                 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
683                 new Label(conditionComp, SWT.NONE); // Eat extra column
684                 
685                 int n = cond.conditions.size();
686                 final Updater[] updates = new Updater[n];
687                 for (int i = 0; i < n; i++) {
688                         updates[i] = createConditionRowPanel(conditionComp, cond, i);
689                 }
690                 
691                 Button addButton = new Button(conditionComp, SWT.PUSH);
692                 GridDataFactory.swtDefaults().applyTo(addButton);
693                 addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
694                 
695                 addButton.addSelectionListener(new SelectionAdapter() {
696                         @Override
697                         public void widgetSelected(SelectionEvent e) {
698                                 cond.conditions.add(createPropertyCondition("property", null, null));
699                                 updateDialog();
700                         }
701                 });
702                 
703                 return validate -> {
704                         for (Updater updater : updates)
705                                 updater.update(validate);
706                 };
707         }
708
709         private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
710                 GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
711                 GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
712                 
713                 Condition c = parentCondition.conditions.get(i);
714                 
715                 Composite row = new Composite(parent, SWT.NONE);
716                 conditionLayout.applyTo(row);
717                 swtDefaults.applyTo(row);
718                 
719                 Consumer<Condition> update = cd -> {
720                         if (cd != null)
721                                 parentCondition.conditions.set(i, cd);
722                         else
723                                 parentCondition.conditions.remove(i);
724                 };
725                 
726                 Updater updater = createConditionPanelFor(row, c, update);
727                 
728                 Button removeButton = new Button(parent, SWT.PUSH);
729                 swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
730                 removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
731                 
732                 removeButton.addSelectionListener(new SelectionAdapter() {
733                         @Override
734                         public void widgetSelected(SelectionEvent e) {
735                                 parentCondition.conditions.remove(i);
736                                 updateDialog();
737                         }
738                 });
739                 
740                 return updater;
741         }
742
743         private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
744                 ROW_LAYOUT.applyTo(conditionComp);
745
746                 // Create combo-box
747                 Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
748                 RowDataFactory.swtDefaults().applyTo(routeCombo);
749                 routeCombo.setItems(routeNames);
750                 
751                 // Set current selection
752                 int index = Arrays.indexOf(routeResources, condition.routeResource);
753                 if (index >= 0)
754                         routeCombo.select(index);
755
756                 // Register update
757                 return validate -> {
758                         int i = routeCombo.getSelectionIndex();
759                         condition.routeResource = i >= 0 ? routeResources[i] : null;
760                 };
761         }
762
763         private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
764                 ROW_LAYOUT.applyTo(conditionComp);
765                 
766                 // Create combo-box
767                 Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
768                 RowDataFactory.swtDefaults().applyTo(regionCombo);
769                 regionCombo.setItems(regionNames);
770                 
771                 // Set current selection
772                 int index = Arrays.indexOf(regionResources, condition.regionResource);
773                 if (index >= 0)
774                         regionCombo.select(index);
775                 
776                 // Register update
777                 return validate -> {
778                         int i = regionCombo.getSelectionIndex();
779                         condition.regionResource = i >= 0 ? regionResources[i] : null;
780                 };
781         }
782
783         private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
784                 ROW_LAYOUT.applyTo(conditionComp);
785                 
786                 Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
787                 RowDataFactory.swtDefaults().applyTo(lowerLimitText);
788                 lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
789                 
790                 new Label(conditionComp, SWT.NONE).setText("\u2264");
791                 
792                 Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
793                 RowDataFactory.swtDefaults().applyTo(propertyNameText);
794                 propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
795                 int index = propertyNames.indexOf(condition.propertyName);
796                 if (index >= 0)
797                         propertyNameText.select(index);
798                 else
799                         propertyNameText.setText(condition.propertyName);
800                 
801                 new Label(conditionComp, SWT.NONE).setText("\u2264");
802                 
803                 Text upperLimitText = new Text(conditionComp, SWT.BORDER);
804                 RowDataFactory.swtDefaults().applyTo(upperLimitText);
805                 upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
806                 
807                 // Register update
808                 return validate ->  {
809                         try {
810                                 String text = lowerLimitText.getText();
811                                 condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
812                         } catch (NumberFormatException e) {
813                                 if (validate) {
814                                         lowerLimitText.selectAll();
815                                         lowerLimitText.forceFocus();
816                                         throw e;
817                                 }
818                         }
819                         
820                         try {
821                                 String text = upperLimitText.getText();
822                                 condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
823                         } catch (NumberFormatException e) {
824                                 if (validate) {
825                                         upperLimitText.selectAll();
826                                         upperLimitText.forceFocus();
827                                         throw e;
828                                 }
829                         }
830                         
831                         int ind = propertyNameText.getSelectionIndex();
832                         String name;
833                         if (ind >= 0)
834                                 name = propertyNames.get(ind);
835                         else
836                                 name = propertyNameText.getText();
837                         
838                         if (validate && name.isEmpty()) {
839                                 propertyNameText.forceFocus();
840                                 throw new RuntimeException();
841                         } else {
842                                 condition.propertyName = name;
843                         }
844                 };
845         }
846
847         private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
848                 return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
849         }
850         
851         private static Condition createRegionCondition(Resource regionResource) {
852                 return new RegionCondition(null, regionResource, null);
853         }
854         
855         private static Condition createRouteCondition(Resource route) {
856                 return new RouteCondition(null, route, null);
857         }
858         
859         private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
860                 Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
861                 AggregateCondition condition = new AggregateCondition(null, type, subConditions);
862                 condition.isInverse = isInverse;
863                 return condition;
864         }
865
866         /*
867         class ConditionDialog extends Dialog {
868                 // Resource of the edited condition
869                 private Resource existingResource;
870                 
871                 // Inverse condition button
872                 private boolean isInverse;
873                 private Button inverseField;
874
875                 // Condition type
876                 private int typeIndex;
877                 private Combo typeField;
878                 
879                 // Type-specific control panels under a stack layout
880                 private Composite stackPanel;
881                 private StackLayout stack;
882                 
883                 private Composite propertyPanel;
884                 private Composite regionPanel;
885                 private Composite routePanel;
886                 private Composite aggregatePanel;
887                 
888                 // Property condition
889                 private Double lowerLimit;
890                 private Double upperLimit;
891                 private String propertyName;
892         
893                 private Text propertyNameField;
894                 private Text lowerLimitField;
895                 private Text upperLimitField;
896                 
897                 // Region condition
898                 private Resource region;
899                 private Combo regionField;
900                 
901                 // Route condition
902                 private Resource route;
903                 private Combo routeField;
904                 
905                 // Aggregate condition
906                 private List<Condition> subConditions;
907                 private boolean isConjunction;
908                 
909                 private org.eclipse.swt.widgets.List subConditionField;
910                 private Combo operatorField;
911                 
912                 public ConditionDialog(Shell shell, Condition condition) {
913                         super(shell);
914                         
915                         typeIndex = 0;
916                         isInverse = false;
917                         propertyName = "";
918                         upperLimit = null;
919                         lowerLimit = null;
920                         subConditions = new ArrayList<>();
921                         
922                         existingResource = condition != null ? condition.resource : null;
923
924                         if (condition != null) {
925                                 if (condition instanceof PropertyCondition) {
926                                         typeIndex = 0;
927                                         PropertyCondition propertyCondition = (PropertyCondition)condition;
928                                         propertyName = propertyCondition.propertyName;
929                                         upperLimit = propertyCondition.upperLimit;
930                                         lowerLimit = propertyCondition.lowerLimit;
931                                 }
932                                 else if (condition instanceof RegionCondition) {
933                                         typeIndex = 1;
934                                         region = ((RegionCondition)condition).regionResource;
935                                 }
936                                 else if (condition instanceof RouteCondition) {
937                                         typeIndex = 2;
938                                         route = ((RouteCondition)condition).routeResource;
939                                 }
940                                 else if (condition instanceof AggregateCondition) {
941                                         typeIndex = 3;
942                                         subConditions = new ArrayList<>(((AggregateCondition)condition).conditions);
943                                         isConjunction = ((AggregateCondition)condition).type == Type.CONJUNCTION;
944                                         isInverse = ((AggregateCondition)condition).type == Type.NEGATION;
945                                 }
946                         }
947                 }
948                 
949                 @Override
950                 protected Control createDialogArea(Composite parent) {
951                         getShell().setText("Edit selector condition");
952                         
953                         Composite content = (Composite)super.createDialogArea(parent);
954                         GridLayoutFactory.swtDefaults().numColumns(1).applyTo(content);
955                         
956                         GridDataFactory defaultWidth = GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT);
957                         
958                         // Is inverse
959                         inverseField = new Button(content, SWT.CHECK);
960                         inverseField.setText("Is inverse");
961                         inverseField.setSelection(isInverse);
962                         
963                         // Condition type
964                         typeField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
965                         typeField.setItems("Property value", "In region", "On route", "Combination");
966                         typeField.select(typeIndex);
967                         
968                         // Type-dependent stacked panels
969                         stackPanel = new Composite(content, SWT.NONE);
970                         stack = new StackLayout();
971                         stackPanel.setLayout(stack);
972                         
973                         // Property condition panel
974                         propertyPanel = new Composite(stackPanel, SWT.NONE);
975                         GridLayoutFactory.swtDefaults().numColumns(2).applyTo(propertyPanel);
976                         
977                         new Label(propertyPanel, SWT.NONE).setText("Property name");
978                         propertyNameField = new Text(propertyPanel, SWT.BORDER);
979                         propertyNameField.setText(propertyName);
980                         defaultWidth.applyTo(propertyNameField);
981                         
982                         new Label(propertyPanel, SWT.NONE).setText("Lower limit");
983                         lowerLimitField = new Text(propertyPanel, SWT.BORDER);
984                         defaultWidth.applyTo(lowerLimitField);
985                         if (lowerLimit != null) lowerLimitField.setText(lowerLimit.toString());
986                         
987                         new Label(propertyPanel, SWT.NONE).setText("Upper limit");
988                         upperLimitField = new Text(propertyPanel, SWT.BORDER);
989                         defaultWidth.applyTo(upperLimitField);
990                         if (upperLimit != null) upperLimitField.setText(upperLimit.toString());
991                         
992                         // Region condition panel
993                         regionPanel = new Composite(stackPanel, SWT.NONE);
994                         GridLayoutFactory.swtDefaults().numColumns(2).applyTo(regionPanel);
995                         
996                         new Label(regionPanel, SWT.NONE).setText("Region");
997                         regionField = new Combo(regionPanel, SWT.BORDER | SWT.READ_ONLY);
998                         regionField.setItems(regionNames);
999                         defaultWidth.applyTo(regionField);
1000                         
1001                         if (region != null) {
1002                                 int regionIndex = Arrays.indexOf(regionResources, region);
1003                                 regionField.select(regionIndex);
1004                         }
1005                         else {
1006                                 regionField.select(0);
1007                         }
1008                         
1009                         // Route condition panel
1010                         routePanel = new Composite(stackPanel, SWT.NONE);
1011                         GridLayoutFactory.swtDefaults().numColumns(2).applyTo(routePanel);
1012                         
1013                         new Label(routePanel, SWT.NONE).setText("Route");
1014                         routeField = new Combo(routePanel, SWT.BORDER | SWT.READ_ONLY);
1015                         routeField.setItems(routeNames);
1016                         defaultWidth.applyTo(routeField);
1017                         
1018                         if (route != null) {
1019                                 int routeIndex = Arrays.indexOf(routeResources, route);
1020                                 routeField.select(routeIndex);
1021                         }
1022                         else {
1023                                 routeField.select(0);
1024                         }
1025                         
1026                         // Aggregate condition panel
1027                         aggregatePanel = new Composite(stackPanel, SWT.NONE);
1028                         GridLayoutFactory.swtDefaults().numColumns(2).applyTo(aggregatePanel);
1029                         
1030                         new Label(aggregatePanel, SWT.NONE).setText("Operator");
1031                         operatorField = new Combo(aggregatePanel, SWT.READ_ONLY);
1032                         operatorField.setItems("And", "Or");
1033                         operatorField.select(isConjunction ? 0 : 1);
1034                         
1035                         new Label(aggregatePanel, SWT.NONE).setText("Sub-conditions");
1036                         Composite buttons = new Composite(aggregatePanel, SWT.NONE);
1037                         RowLayoutFactory.swtDefaults().justify(true).fill(true).extendedMargins(0, 0, 0, 0).type(SWT.HORIZONTAL).applyTo(buttons);
1038                         
1039                         Button addButton = new Button(buttons, SWT.PUSH);
1040                         addButton.setText("Add");
1041                         Button removeButton = new Button(buttons, SWT.PUSH);
1042                         removeButton.setText("Remove");
1043                         Button editButton = new Button(buttons, SWT.PUSH);
1044                         editButton.setText("Edit");
1045
1046                         new Label(aggregatePanel, SWT.NONE);
1047                         subConditionField = new org.eclipse.swt.widgets.List(aggregatePanel, SWT.BORDER);
1048                         GridDataFactory.swtDefaults().hint(200, 150).applyTo(subConditionField);
1049                         if (subConditions != null) {
1050                                 Session session = Simantics.getSession();
1051                                 List<String> items = new ArrayList<>();
1052                                 for (Condition c : subConditions) {
1053                                         try {
1054                                                 items.add(ElementSelector.getExpression(session, c.resource));
1055                                         } catch (DatabaseException e1) {
1056                                                 LOGGER.error("Condition expression read failed", e1);
1057                                                 items.add("<Unknown expression>");
1058                                         }
1059                                 }
1060                                 
1061                                 subConditionField.setItems(items.toArray(new String[items.size()]));
1062                         }
1063                         
1064                         addButton.addSelectionListener(new SelectionAdapter() {
1065                                 @Override
1066                                 public void widgetSelected(SelectionEvent e) {
1067                                         ConditionDialog conditionDialog = new ConditionDialog(getShell(), null);
1068                                         if (conditionDialog.open() == Window.OK) {
1069                                                 Condition condition;
1070                                                 try {
1071                                                         condition = conditionDialog.createCondition();
1072                                                         subConditions.add(condition);
1073                                                         
1074                                                         try {
1075                                                                 subConditionField.add(ElementSelector.getExpression(Simantics.getSession(), condition.resource));
1076                                                         } catch (DatabaseException e1) {
1077                                                                 LOGGER.error("Condition expression read failed", e1);
1078                                                                 subConditionField.add("<Unknown expression>");
1079                                                         }
1080                                                 } catch (DatabaseException e2) {
1081                                                         LOGGER.error("Create condition failed", e2);
1082                                                 }
1083                                         }
1084                                 }
1085                         });
1086                         
1087                         removeButton.addSelectionListener(new SelectionAdapter() {
1088                                 @Override
1089                                 public void widgetSelected(SelectionEvent e) {
1090                                         int index = subConditionField.getSelectionIndex();
1091                                         if (index >= 0) {
1092                                                 subConditionField.deselectAll();
1093                                                 subConditionField.remove(index);
1094                                                 subConditions.remove(index);
1095                                                 
1096                                                 if (index < subConditions.size())
1097                                                         subConditionField.setSelection(index);
1098                                         }
1099                                         
1100                                         boolean selected = subConditionField.getSelectionIndex() >= 0;
1101                                         removeButton.setEnabled(selected);
1102                                         editButton.setEnabled(selected);
1103                                 }
1104                         });
1105                         
1106                         editButton.addSelectionListener(new SelectionAdapter() {
1107                                 @Override
1108                                 public void widgetSelected(SelectionEvent e) {
1109                                         int index = subConditionField.getSelectionIndex();
1110                                         if (index >= 0) {
1111                                                 Condition condition = subConditions.get(index);
1112                                                 ConditionDialog conditionDialog = new ConditionDialog(getShell(), condition);
1113                                                 if (conditionDialog.open() == Window.OK) {
1114                                                         try {
1115                                                                 condition = conditionDialog.createCondition();
1116                                                                 subConditions.set(index, condition);
1117                                                                 
1118                                                                 try {
1119                                                                         subConditionField.setItem(index, ElementSelector.getExpression(Simantics.getSession(), condition.resource));
1120                                                                 } catch (DatabaseException e1) {
1121                                                                         LOGGER.error("Condition expression read failed", e1);
1122                                                                         subConditionField.setItem(index, "<Unknown expression>");
1123                                                                 }
1124                                                         } catch (DatabaseException e2) {
1125                                                                 LOGGER.error("Create condition failed", e2);
1126                                                         }
1127                                                 }
1128                                         }
1129                                 }
1130                         });
1131                         
1132                         subConditionField.addSelectionListener(new SelectionAdapter() {
1133                                 @Override
1134                                 public void widgetSelected(SelectionEvent e) {
1135                                         boolean selected = subConditionField.getSelectionIndex() >= 0;
1136                                         removeButton.setEnabled(selected);
1137                                         editButton.setEnabled(selected);
1138                                 }
1139                         });
1140
1141                         // Stack layout update
1142                         typeField.addSelectionListener(new SelectionAdapter() {
1143                                 @Override
1144                                 public void widgetSelected(SelectionEvent e) {
1145                                         updateStack();
1146                                 }
1147                         });
1148                         
1149                         updateStack();
1150                         
1151                         return content;
1152                 }
1153                 
1154                 @Override
1155                 protected void okPressed() {
1156                         isInverse = inverseField.getSelection();
1157                         
1158                         switch (typeIndex) {
1159                         case 0: // Property condition
1160                                 propertyName = propertyNameField.getText();
1161                                 try {
1162                                         String lowerLimitText = lowerLimitField.getText();
1163                                         lowerLimit = lowerLimitText.equals("") ? null : Double.valueOf(lowerLimitText);
1164                                         String upperLimitText = upperLimitField.getText();
1165                                         upperLimit = upperLimitText.equals("") ? null : Double.valueOf(upperLimitText);
1166                                 }
1167                                 catch (NumberFormatException e) {
1168                                         ErrorDialog.openError(getShell(), "Error", "Invalid numeric value: " + e.getMessage(), new Status(OK, "org.simantics.district.selection.ui", e.getMessage()));
1169                                         return;
1170                                 }
1171                                 break;
1172                         case 1: { // Region condition
1173                                 int selectionIndex = regionField.getSelectionIndex();
1174                                 if (selectionIndex < 0) {
1175                                         ErrorDialog.openError(getShell(), "Error", "Please select a region", new Status(OK, "org.simantics.district.selection.ui", "No region selection"));
1176                                         return;
1177                                 }
1178                                 region = regionResources[selectionIndex];
1179                                 break;
1180                         }
1181                         case 2: // Route condition
1182                                 route = routeResources[routeField.getSelectionIndex()];
1183                                 break;
1184                         case 3: // Aggregate condition
1185                                 isConjunction = operatorField.getSelectionIndex() == 0;
1186                                 break;
1187                         }
1188                         
1189                         super.okPressed();
1190                 }
1191         
1192                 protected Condition createCondition() throws DatabaseException {
1193                         if (isInverse && !(typeIndex == 3 && !isConjunction)) {
1194                                 Resource resource0 = createCondition0(typeIndex);
1195                                 
1196                                 // Create a negation
1197                                 Resource resource = Simantics.getSession().syncRequest(new WriteResult<Resource>() {
1198                                         @Override
1199                                         public Resource perform(WriteGraph graph) throws DatabaseException {
1200                                                 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
1201                                                 Layer0 L0 = Layer0.getInstance(graph);
1202                                                 
1203                                                 Resource r = graph.newResource();
1204                                                 graph.claim(r, L0.InstanceOf, ES.Negation);
1205                                                 graph.claim(r, ES.HasSubcondition, resource0);
1206                                                 return r;
1207                                         }
1208                                 });
1209                                 
1210                                 return ElementSelector.getCondition(Simantics.getSession(), resource);
1211                         }
1212                         else {
1213                                 return ElementSelector.getCondition(Simantics.getSession(), createCondition0(typeIndex));
1214                         }
1215                 }
1216         
1217                 private Resource createCondition0(int index) throws DatabaseException {
1218                         switch (index) {
1219                         case 0: return createPropertyCondition();
1220                         case 1: return createRegionCondition();
1221                         case 2: return createRouteCondition();
1222                         case 3: return createAggregateCondition();
1223                         default: throw new IllegalStateException("Invalid condition type code " + index);
1224                         }
1225                 }
1226         
1227                 private Resource createPropertyCondition() throws DatabaseException {
1228                         return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
1229                                 @Override
1230                                 public Resource perform(WriteGraph graph) throws DatabaseException {
1231                                         ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
1232                                         Layer0 L0 = Layer0.getInstance(graph);
1233                                         
1234                                         Resource r = graph.newResource();
1235                                         graph.claim(r, L0.InstanceOf, ES.PropertyCondition);
1236                                         graph.claimLiteral(r, ES.PropertyCondition_HasPropertyName, propertyName);
1237                                         if (lowerLimit != null)
1238                                                 graph.claimLiteral(r, ES.PropertyCondition_HasLowerLimit, L0.Double, lowerLimit);
1239                                         if (upperLimit != null)
1240                                                 graph.claimLiteral(r, ES.PropertyCondition_HasUpperLimit, L0.Double, upperLimit);
1241                                         
1242                                         return r;
1243                                 }
1244                         });
1245                 }
1246                 
1247                 private Resource createAggregateCondition() throws DatabaseException {
1248                         return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
1249                                 @Override
1250                                 public Resource perform(WriteGraph graph) throws DatabaseException {
1251                                         ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
1252                                         Layer0 L0 = Layer0.getInstance(graph);
1253                                         
1254                                         Resource r;
1255                                         if (existingResource != null) {
1256                                                 // Reuse existing resource
1257                                                 r = existingResource;
1258                                                 // Clear any previous statements
1259                                                 graph.deny(existingResource);
1260                                         }
1261                                         else {
1262                                                 r = graph.newResource();
1263                                         }
1264                                         
1265                                         Resource type;
1266                                         type = isConjunction ? ES.Conjunction : isInverse ? ES.Negation : ES.Disjunction;
1267                                         
1268                                         graph.claim(r, L0.InstanceOf, type);
1269                                         for (Condition c : subConditions) {
1270                                                 graph.claim(r, ES.HasSubcondition, c.resource);
1271                                         }
1272                                         
1273                                         return r;
1274                                 }
1275                         });
1276                 }
1277         
1278                 private Resource createRouteCondition() throws DatabaseException {
1279                         return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
1280                                 @Override
1281                                 public Resource perform(WriteGraph graph) throws DatabaseException {
1282                                         ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
1283                                         Layer0 L0 = Layer0.getInstance(graph);
1284                                         
1285                                         Resource r = graph.newResource();
1286                                         graph.claim(r, L0.InstanceOf, ES.RouteCondition);
1287                                         graph.claim(r, ES.RouteCondition_HasRoute, route);
1288                                         return r;
1289                                 }
1290                         });
1291                 }
1292         
1293                 private Resource createRegionCondition() throws DatabaseException {
1294                         return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
1295                                 @Override
1296                                 public Resource perform(WriteGraph graph) throws DatabaseException {
1297                                         ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
1298                                         Layer0 L0 = Layer0.getInstance(graph);
1299                                         
1300                                         Resource r = graph.newResource();
1301                                         graph.claim(r, L0.InstanceOf, ES.RegionCondition);
1302                                         graph.claim(r, ES.RegionCondition_HasRegion, region);
1303                                         return r;
1304                                 }
1305                         });
1306                 }
1307
1308                 private void updateStack() {
1309                         typeIndex = typeField.getSelectionIndex();
1310                         switch (typeIndex) {
1311                         case 0: stack.topControl = propertyPanel; break;
1312                         case 1: stack.topControl = regionPanel; break;
1313                         case 2: stack.topControl = routePanel; break;
1314                         case 3: stack.topControl = aggregatePanel; break;
1315                         }
1316                         
1317                         stackPanel.layout();
1318                 }
1319         }
1320         */
1321         
1322         static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
1323                 DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
1324                 Resource project = Simantics.getProjectResource();
1325                 Resource model = ActiveModels.getPossibleActiveModel(graph, project);
1326                 
1327                 return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
1328         }
1329         
1330         void updatePropertyList() throws DatabaseException {
1331                 Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
1332                 Set<Pair<String, String>> properties = new HashSet<>();
1333                 
1334                 Simantics.getSession().syncRequest(new ReadRequest() {
1335                         @Override
1336                         public void run(ReadGraph graph) throws DatabaseException {
1337                                 Layer0 L0 = Layer0.getInstance(graph);
1338                                 
1339                                 for (Resource type : types) {
1340                                         if (type == null)
1341                                                 continue;
1342                                         
1343                                         Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
1344                                         if (ct == null)
1345                                                 continue;
1346                                         
1347                                         if (graph.isInstanceOf(ct, L0.String)) {
1348                                                 Resource indexRoot = graph.syncRequest(new IndexRoot(type));
1349                                                 String name = graph.getValue(ct);
1350                                                 ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
1351                                                 if (ct == null)
1352                                                         continue;
1353                                         }
1354                                         
1355                                         for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
1356                                                 if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
1357                                                         continue;
1358                                                 
1359                                                 // Filter only numeric properties
1360                                                 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
1361                                                 if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
1362                                                         continue;
1363                                                 
1364                                                 String name = graph.getRelatedValue2(prop, L0.HasName);
1365                                                 String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
1366                                                 if (label == null) label = name;
1367                                                 
1368                                                 properties.add(Pair.make(label, name));
1369                                         }
1370                                 }
1371                         }
1372                 });
1373                 
1374                 propertyNames.clear();
1375                 propertyLabels.clear();
1376                 properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
1377                         propertyLabels.add(p.first);
1378                         propertyNames.add(p.second);
1379                 });
1380         }
1381         
1382         static boolean isNumericValueType(String requiredValueType) {
1383                 switch (requiredValueType) {
1384                 case "Integer":
1385                 case "Long":
1386                 case "Double":
1387                 case "Float":
1388                         return true;
1389                 default:
1390                         return false;
1391                 }
1392         }
1393 }