1 package org.simantics.district.selection.ui.parts;
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;
12 import java.util.UUID;
13 import java.util.function.Consumer;
15 import javax.inject.Inject;
17 import org.eclipse.jface.dialogs.Dialog;
18 import org.eclipse.jface.dialogs.MessageDialog;
19 import org.eclipse.jface.layout.GridDataFactory;
20 import org.eclipse.jface.layout.GridLayoutFactory;
21 import org.eclipse.jface.layout.RowDataFactory;
22 import org.eclipse.jface.layout.RowLayoutFactory;
23 import org.eclipse.jface.resource.ImageDescriptor;
24 import org.eclipse.jface.resource.JFaceResources;
25 import org.eclipse.jface.resource.LocalResourceManager;
26 import org.eclipse.jface.resource.ResourceLocator;
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.events.SelectionAdapter;
29 import org.eclipse.swt.events.SelectionEvent;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Combo;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Label;
35 import org.eclipse.swt.widgets.Shell;
36 import org.eclipse.swt.widgets.Text;
37 import org.eclipse.swt.widgets.Widget;
38 import org.simantics.Simantics;
39 import org.simantics.db.ReadGraph;
40 import org.simantics.db.Resource;
41 import org.simantics.db.WriteGraph;
42 import org.simantics.db.common.request.IndexRoot;
43 import org.simantics.db.common.request.ReadRequest;
44 import org.simantics.db.common.request.WriteRequest;
45 import org.simantics.db.exception.DatabaseException;
46 import org.simantics.db.exception.RuntimeDatabaseException;
47 import org.simantics.db.layer0.QueryIndexUtils;
48 import org.simantics.db.layer0.request.ActiveModels;
49 import org.simantics.db.layer0.request.PropertyInfo;
50 import org.simantics.db.layer0.request.PropertyInfoRequest;
51 import org.simantics.db.layer0.util.Layer0Utils;
52 import org.simantics.db.request.Read;
53 import org.simantics.diagram.stubs.DiagramResource;
54 import org.simantics.district.network.ontology.DistrictNetworkResource;
55 import org.simantics.district.region.ontology.DiagramRegionsResource;
56 import org.simantics.district.route.ontology.RouteResource;
57 import org.simantics.district.selection.ElementSelectionResource;
58 import org.simantics.district.selection.ElementSelectionUtils;
59 import org.simantics.district.selection.ElementSelector;
60 import org.simantics.district.selection.ElementSelector.AggregateCondition;
61 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
62 import org.simantics.district.selection.ElementSelector.All;
63 import org.simantics.district.selection.ElementSelector.Condition;
64 import org.simantics.district.selection.ElementSelector.DiagramGenerator;
65 import org.simantics.district.selection.ElementSelector.ExplicitGenerator;
66 import org.simantics.district.selection.ElementSelector.Generator;
67 import org.simantics.district.selection.ElementSelector.ModelGenerator;
68 import org.simantics.district.selection.ElementSelector.PropertyCondition;
69 import org.simantics.district.selection.ElementSelector.PropertySelector;
70 import org.simantics.district.selection.ElementSelector.RegionCondition;
71 import org.simantics.district.selection.ElementSelector.RouteCondition;
72 import org.simantics.district.selection.ElementSelector.Selector;
73 import org.simantics.layer0.Layer0;
74 import org.simantics.layer0.utils.direct.GraphUtils;
75 import org.simantics.modeling.ModelingResources;
76 import org.simantics.structural.stubs.StructuralResource2;
77 import org.simantics.utils.datastructures.Arrays;
78 import org.simantics.utils.datastructures.Pair;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
82 public class EditSelectorDialog extends Dialog {
84 private static final RowLayoutFactory ROW_LAYOUT = RowLayoutFactory.fillDefaults().wrap(false);
85 private static final ImageDescriptor CROSS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/cross.png").get();
86 private static final ImageDescriptor PLUS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/add.png").get();
88 private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
90 private ElementSelector elementSelector;
92 // Currently selected elements
93 Collection<Resource> currentSelection;
95 // Data for comboboxes
96 private List<Resource> diagrams;
97 private ArrayList<String> diagramNames;
99 private String[] regionNames;
100 private Resource[] regionResources;
102 private String[] routeNames;
103 private Resource[] routeResources;
105 private List<Resource> componentTypes;
106 private List<String> componentTypeNames;
108 private List<String> propertyNames;
109 private List<String> propertyLabels;
111 private Composite conditionPanel;
114 private int generatorIndex;
115 private Combo sourceField;
118 private Text nameField;
120 private Resource diagram;
121 private Combo diagramField;
123 private int selectorIndex;
124 private Combo selectorField;
126 private Resource componentType;
127 private Combo componentTypeField;
129 private String propertyName;
130 private Combo propertyField;
132 private int numberOfItems;
135 private Condition condition;
137 // Dialog area component
138 private Composite content;
140 private int diagramIndex;
142 private LocalResourceManager resourceManager;
144 static class ValidationException extends Exception {
145 private static final long serialVersionUID = 1L;
147 public ValidationException(String message) {
152 // Function type for updating condition objects with optional validation
153 static interface Updater {
154 // If 'validate' is true, a runtime exception may be thrown for invalid values
155 void update(boolean validate) throws ValidationException;
158 final static Updater NULL_UPDATE = validate -> {};
160 // Called to read values from controls into conditions
161 Updater updater = NULL_UPDATE;
164 public EditSelectorDialog(Shell shell, ElementSelector elementSelector, Collection<Resource> currentSelection) {
167 this.elementSelector = elementSelector;
168 if (elementSelector != null) {
170 Simantics.getSession().sync(new ReadRequest() {
172 public void run(ReadGraph graph) throws DatabaseException {
173 elementSelector.buildSelection(graph);
176 } catch (DatabaseException e1) {
177 LOGGER.error("Failed to read element selector resource " + elementSelector.getResource(), e1);
178 throw new RuntimeDatabaseException(e1);
182 this.currentSelection = currentSelection;
184 Map<Resource, String> diagramMap = ElementSelector.findDiagrams();
185 diagrams = new ArrayList<Resource>(diagramMap.size());
186 diagramNames = new ArrayList<String>(diagramMap.size());
187 diagramMap.entrySet()
189 .sorted(Comparator.comparing(e -> e.getValue()))
190 .forEachOrdered(e -> {
191 diagrams.add(e.getKey());
192 diagramNames.add(e.getValue());
195 final Map<Resource, String> regions = new HashMap<>();
196 final Map<Resource, String> routes = new HashMap<>();
199 Simantics.getSession().syncRequest(new Read<Void>() {
201 public Void perform(ReadGraph graph) throws DatabaseException {
202 Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
203 List<Resource> regionCollection = QueryIndexUtils.searchByType(graph, model, DiagramRegionsResource.getInstance(graph).Region);
204 for (Resource r : regionCollection) {
205 String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
206 regions.put(r, name);
209 List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, RouteResource.getInstance(graph).Route);
210 for (Resource r : routeCollection) {
211 String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
217 } catch (DatabaseException e) {
218 LOGGER.error("Failed to read routes and/or regions in the model", e);
221 regionNames = regions.values().toArray(new String[regions.size()]);
222 regionResources = regions.keySet().toArray(new Resource[regions.size()]);
224 routeNames = routes.values().toArray(new String[routes.size()]);
225 routeResources = routes.keySet().toArray(new Resource[routes.size()]);
228 Simantics.getSession().syncRequest(new ReadRequest() {
230 public void run(ReadGraph graph) throws DatabaseException {
231 Layer0 L0 = Layer0.getInstance(graph);
232 List<Resource> types = findComponentTypes(graph);
234 componentTypes = new ArrayList<>(types.size() + 1);
235 componentTypeNames = new ArrayList<>(types.size() + 1);
237 componentTypes.add(null);
238 componentTypeNames.add("Any type");
239 componentTypes.addAll(types);
240 for (Resource t : types) {
241 componentTypeNames.add(graph.getValue2(t, L0.HasName));
245 } catch (DatabaseException e) {
246 LOGGER.error("Failed to read district component types", e);
249 componentType = elementSelector.getSelector().componentType;
251 propertyNames = new ArrayList<>();
252 propertyLabels = new ArrayList<>();
255 updatePropertyList();
256 } catch (DatabaseException e) {
257 LOGGER.error("Failed to read district component properties", e);
260 name = elementSelector != null ? elementSelector.getName() : "";
268 if (elementSelector != null) {
269 Generator generator = elementSelector.getGenerator();
270 if (generator instanceof ModelGenerator) {
273 else if (generator instanceof DiagramGenerator) {
275 diagram = ((DiagramGenerator)generator).diagram;
277 else if (generator instanceof ExplicitGenerator) {
281 throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
284 Selector selector = elementSelector.getSelector();
285 if (selector instanceof All) {
288 else if (selector instanceof PropertySelector) {
289 PropertySelector propertySelector = (PropertySelector)selector;
290 selectorIndex = propertySelector.smallest ? 1 : 2;
291 propertyName = propertySelector.propertyName;
292 numberOfItems = propertySelector.resultCount;
295 throw new IllegalStateException("Unknwon selector type " + selector.getClass().getName());
298 condition = elementSelector.getCondition();
302 private void updateDialog() {
304 updater.update(false);
305 } catch (ValidationException e) {
306 // Should not happend with argument false
310 updater = updateConditionPanel();
312 content.layout(true, true);
317 protected void okPressed() {
319 generatorIndex = sourceField.getSelectionIndex();
320 if (generatorIndex == 1) {
321 int selectionIndex = diagramField.getSelectionIndex();
322 if (selectionIndex < 0) {
323 diagramField.setFocus();
324 throw new ValidationException("Please select a diagram");
327 diagram = diagrams.get(selectionIndex);
330 name = nameField.getText();
331 if (name.isEmpty()) {
332 nameField.setFocus();
333 throw new ValidationException("Please enter a name");
336 componentType = componentTypes.get(componentTypeField.getSelectionIndex());
337 selectorIndex = selectorField.getSelectionIndex();
338 int propertyIndex = propertyField.getSelectionIndex();
339 propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
340 if (propertyName.isEmpty()) {
341 propertyField.setFocus();
342 throw new ValidationException("Please select a property");
345 // Try to parse number of items
346 if (useNumberOfItems()) {
348 numberOfItems = Integer.parseInt(nField.getText());
349 if (numberOfItems <= 0) {
352 throw new ValidationException("Number of elements must be positive");
354 } catch (NumberFormatException e) {
357 throw new ValidationException("Please enter a valid number of elements");
361 // To to update condition definitions
362 updater.update(true);
363 } catch (ValidationException e) {
364 MessageDialog.openError(this.getShell(), "Missing data", e.getMessage());
371 public void writeSelection() throws DatabaseException {
372 Simantics.getSession().syncRequest(new WriteRequest() {
374 public void perform(WriteGraph graph) throws DatabaseException {
375 Layer0 L0 = Layer0.getInstance(graph);
376 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
377 ModelingResources MOD = ModelingResources.getInstance(graph);
378 DiagramResource DIA = DiagramResource.getInstance(graph);
380 graph.markUndoPoint();
381 Layer0Utils.addCommentMetadata(graph, "Created new element selection");
383 Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
387 if (elementSelector != null) {
388 selection = elementSelector.getResource();
389 graph.deny(selection);
392 selection = graph.newResource();
395 graph.claim(selection, L0.InstanceOf, ES.Selection);
396 graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
397 graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
398 graph.claim(selection, L0.PartOf, lib);
401 Resource generator = graph.newResource();
402 Resource generatorType;
403 switch (generatorIndex) {
405 generatorType = ES.Generator_Model;
408 generatorType = ES.Generator_Diagram;
409 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
410 graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
413 generatorType = ES.Generator_Explicit;
414 for (Resource r : currentSelection) {
416 if (graph.isInstanceOf(r, DIA.Connection))
418 if (!graph.isInstanceOf(r, DIA.Element)) {
419 if (!graph.hasStatement(r, MOD.ComponentToElement))
422 r = graph.getPossibleObject(r, MOD.ComponentToElement);
427 graph.claim(generator, ES.Generator_HasSelectedElement, r);
430 default: throw new IllegalStateException("Invalid source index " + generatorIndex);
432 graph.claim(generator, L0.InstanceOf, generatorType);
433 graph.claim(selection, ES.Selection_HasGenerator, generator);
436 Resource selector = graph.newResource();
437 Resource selectorType;
438 switch (selectorIndex) {
439 case 0: selectorType = ES.Selector_All; break;
440 case 1: selectorType = ES.Selector_NLowest; break;
441 case 2: selectorType = ES.Selector_NHighest; break;
442 default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
444 graph.claim(selector, L0.InstanceOf, selectorType);
445 graph.claim(selection, ES.Selection_HasSelector, selector);
446 graph.deny(selector, ES.Selector_HasMapping);
447 if (componentType != null)
448 graph.claim(selector, ES.Selector_HasMapping, componentType);
450 if (selectorIndex > 0) {
451 graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
452 graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
456 if (condition != null) {
457 Resource conditionResource = condition.update(graph);
458 graph.claim(selection, ES.Selection_HasCondition, conditionResource);
464 private boolean isDiagramFieldVisible() {
465 return generatorIndex == 1;
468 private boolean useNumberOfItems() {
469 return selectorIndex != 0;
473 protected Control createDialogArea(Composite parent) {
474 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
477 getShell().setText("Edit element selector");
479 content = new Composite(parent, SWT.NONE);
480 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
483 Label nameLabel = new Label(content, SWT.NONE);
484 nameLabel.setText("Name");
485 GridDataFactory.swtDefaults().applyTo(nameLabel);
487 nameField = new Text(content, SWT.BORDER);
488 nameField.setEditable(true);
489 nameField.setText(name);
490 GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
493 Label selectorLabel = new Label(content, SWT.NONE);
494 selectorLabel.setText("Select");
495 GridDataFactory.swtDefaults().applyTo(selectorLabel);
497 Composite selectorComposite = new Composite(content, SWT.NONE);
498 GridDataFactory.swtDefaults().applyTo(selectorComposite);
499 RowLayoutFactory.fillDefaults().applyTo(selectorComposite);
501 nField = new Text(selectorComposite, SWT.BORDER);
502 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
503 if (useNumberOfItems())
504 nField.setText(Integer.toString(numberOfItems));
505 nField.setEnabled(useNumberOfItems());
507 componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
508 RowDataFactory.swtDefaults().applyTo(componentTypeField);
509 componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
511 int index = componentTypes.indexOf(componentType);
512 componentTypeField.select(index >= 0 ? index : 0);
515 new Label(selectorComposite, SWT.NONE).setText("with");
517 selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
518 selectorField.setItems("All", "Lowest", "Highest");
519 selectorField.select(selectorIndex);
520 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
522 propertyField = new Combo(selectorComposite, SWT.NONE);
523 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyField);
524 propertyField.setItems(propertyLabels.toArray(new String[] {}));
526 int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
528 propertyField.select(index);
530 propertyField.setText(propertyName != null ? propertyName : "");
532 propertyField.setEnabled(useNumberOfItems());
534 selectorField.addSelectionListener(new SelectionAdapter() {
536 public void widgetSelected(SelectionEvent e) {
537 selectorIndex = selectorField.getSelectionIndex();
539 boolean enable = useNumberOfItems();
540 nField.setEnabled(enable);
541 propertyField.setEnabled(enable);
543 nField.setText(enable ? Integer.toString(numberOfItems) : "");
548 Label sourceLabel = new Label(content, SWT.NONE);
549 sourceLabel.setText("from");
550 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
552 Composite sourceComposite = new Composite(content, SWT.NONE);
553 GridDataFactory.swtDefaults().applyTo(sourceComposite);
554 RowLayoutFactory.fillDefaults().applyTo(sourceComposite);
556 sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
557 RowDataFactory.swtDefaults().applyTo(sourceField);
558 sourceField.setItems("Whole model", "Diagram", "Current selection");
559 sourceField.select(generatorIndex);
561 diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
562 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(diagramField);
563 diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
564 diagramField.setEnabled(isDiagramFieldVisible());
566 diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
567 diagramField.select(diagramIndex);
569 sourceField.addSelectionListener(new SelectionAdapter() {
571 public void widgetSelected(SelectionEvent e) {
572 generatorIndex = sourceField.getSelectionIndex();
573 boolean enabled = isDiagramFieldVisible();
575 diagramIndex = diagramField.getSelectionIndex();
576 diagramField.clearSelection();
578 if (diagramIndex >= 0)
579 diagramField.select(diagramIndex);
581 diagramField.clearSelection();
583 diagramField.setEnabled(enabled);
587 sourceField.select(generatorIndex);
590 Label label = new Label(content, SWT.NONE);
591 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
592 label.setText("where");
594 conditionPanel = new Composite(content, SWT.NONE);
595 GridDataFactory.swtDefaults().span(1, 2).minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
596 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
598 updater = updateConditionPanel();
603 private Updater updateConditionPanel() {
605 for (Widget c : conditionPanel.getChildren())
608 return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
611 private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
612 // Create new contents
613 Button notCheck = new Button(parent, SWT.CHECK);
614 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
615 notCheck.setText("not");
616 notCheck.setSelection(condition != null && condition.isInverse);
618 Composite conditionComp = new Composite(parent, SWT.NONE);
619 GridDataFactory.fillDefaults().applyTo(conditionComp);
621 Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
631 typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
633 final Updater updater;
634 if (condition instanceof PropertyCondition) {
636 updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
637 } else if (condition instanceof RegionCondition) {
639 updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
640 } else if (condition instanceof RouteCondition) {
642 updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
643 } else if (condition instanceof AggregateCondition) {
644 AggregateCondition cond = (AggregateCondition) condition;
645 typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
646 updater = createAggregateConditionPanel(conditionComp, cond);
648 ROW_LAYOUT.applyTo(conditionComp);
649 notCheck.setEnabled(false);
655 updater.update(validate);
656 condition.isInverse = notCheck.getSelection();
660 private final class ConditionTypeSelectionListener extends SelectionAdapter {
661 private final Combo typeCombo;
662 private final Consumer<Condition> consumer;
663 private final Condition finalCondition;
665 private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
666 this.typeCombo = typeCombo;
667 this.consumer = consumer;
668 this.finalCondition = finalCondition;
672 public void widgetSelected(SelectionEvent e) {
673 int index = typeCombo.getSelectionIndex();
674 Condition newCondition = finalCondition;
680 newCondition = createPropertyCondition("", null, null);
683 newCondition = createRegionCondition(null);
686 newCondition = createRouteCondition(null);
689 if (newCondition instanceof AggregateCondition)
690 ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
692 newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
695 if (newCondition instanceof AggregateCondition)
696 ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
698 newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
702 consumer.accept(newCondition);
708 private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
709 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
710 new Label(conditionComp, SWT.NONE); // Eat extra column
712 int n = cond.conditions.size();
713 final Updater[] updates = new Updater[n];
714 for (int i = 0; i < n; i++) {
715 updates[i] = createConditionRowPanel(conditionComp, cond, i);
718 Button addButton = new Button(conditionComp, SWT.PUSH);
719 GridDataFactory.swtDefaults().applyTo(addButton);
720 addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
722 addButton.addSelectionListener(new SelectionAdapter() {
724 public void widgetSelected(SelectionEvent e) {
725 cond.conditions.add(createPropertyCondition("", null, null));
731 for (Updater updater : updates)
732 updater.update(validate);
736 private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
737 GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
738 GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
740 Condition c = parentCondition.conditions.get(i);
742 Composite row = new Composite(parent, SWT.NONE);
743 conditionLayout.applyTo(row);
744 swtDefaults.applyTo(row);
746 Consumer<Condition> update = cd -> {
748 parentCondition.conditions.set(i, cd);
750 parentCondition.conditions.remove(i);
753 Updater updater = createConditionPanelFor(row, c, update);
755 Button removeButton = new Button(parent, SWT.PUSH);
756 swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
757 removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
759 removeButton.addSelectionListener(new SelectionAdapter() {
761 public void widgetSelected(SelectionEvent e) {
762 parentCondition.conditions.remove(i);
770 private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
771 ROW_LAYOUT.applyTo(conditionComp);
774 Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
775 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(routeCombo);
776 routeCombo.setItems(routeNames);
778 // Set current selection
779 int index = Arrays.indexOf(routeResources, condition.routeResource);
781 routeCombo.select(index);
785 int i = routeCombo.getSelectionIndex();
786 if (validate && i < 0) {
787 routeCombo.forceFocus();
788 throw new RuntimeException("Must select a route");
791 condition.routeResource = i >= 0 ? routeResources[i] : null;
795 private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
796 ROW_LAYOUT.applyTo(conditionComp);
799 Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
800 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(regionCombo);
801 regionCombo.setItems(regionNames);
803 // Set current selection
804 int index = Arrays.indexOf(regionResources, condition.regionResource);
806 regionCombo.select(index);
810 int i = regionCombo.getSelectionIndex();
811 if (validate && i < 0) {
812 regionCombo.forceFocus();
813 throw new ValidationException("Please select a region");
816 condition.regionResource = i >= 0 ? regionResources[i] : null;
820 private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
821 ROW_LAYOUT.applyTo(conditionComp);
823 Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
824 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(lowerLimitText);
825 lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
827 new Label(conditionComp, SWT.NONE).setText("\u2264");
829 Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
830 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyNameText);
831 propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
832 int index = propertyNames.indexOf(condition.propertyName);
834 propertyNameText.select(index);
836 propertyNameText.setText(condition.propertyName);
838 new Label(conditionComp, SWT.NONE).setText("\u2264");
840 Text upperLimitText = new Text(conditionComp, SWT.BORDER);
841 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(upperLimitText);
842 upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
847 String text = lowerLimitText.getText();
848 condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
849 } catch (NumberFormatException e) {
851 lowerLimitText.selectAll();
852 lowerLimitText.forceFocus();
853 throw new ValidationException("Please enter a valid lower limit");
858 String text = upperLimitText.getText();
859 condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
860 } catch (NumberFormatException e) {
862 upperLimitText.selectAll();
863 upperLimitText.forceFocus();
864 throw new ValidationException("Please enter a valid upper limit");
868 int ind = propertyNameText.getSelectionIndex();
871 name = propertyNames.get(ind);
873 name = propertyNameText.getText();
875 if (validate && name.isEmpty()) {
876 propertyNameText.forceFocus();
877 throw new ValidationException("Please select a property");
879 condition.propertyName = name;
884 private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
885 return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
888 private static Condition createRegionCondition(Resource regionResource) {
889 return new RegionCondition(null, regionResource, null);
892 private static Condition createRouteCondition(Resource route) {
893 return new RouteCondition(null, route, null);
896 private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
897 Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
898 AggregateCondition condition = new AggregateCondition(null, type, subConditions);
899 condition.isInverse = isInverse;
903 static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
904 DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
905 Resource project = Simantics.getProjectResource();
906 Resource model = ActiveModels.getPossibleActiveModel(graph, project);
908 return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
911 void updatePropertyList() throws DatabaseException {
912 Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
913 Set<Pair<String, String>> properties = new HashSet<>();
915 Simantics.getSession().syncRequest(new ReadRequest() {
917 public void run(ReadGraph graph) throws DatabaseException {
918 Layer0 L0 = Layer0.getInstance(graph);
920 for (Resource type : types) {
924 Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
928 if (graph.isInstanceOf(ct, L0.String)) {
929 Resource indexRoot = graph.syncRequest(new IndexRoot(type));
930 String name = graph.getValue(ct);
931 ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
936 for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
937 if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
940 // Filter only numeric properties
941 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
942 if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
945 String name = graph.getRelatedValue2(prop, L0.HasName);
946 String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
947 if (label == null) label = name;
949 properties.add(Pair.make(label, name));
955 propertyNames.clear();
956 propertyLabels.clear();
957 properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
958 propertyLabels.add(p.first);
959 propertyNames.add(p.second);
963 static boolean isNumericValueType(String requiredValueType) {
964 switch (requiredValueType) {