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.diagram.stubs.DiagramResource;
53 import org.simantics.district.network.ontology.DistrictNetworkResource;
54 import org.simantics.district.region.ontology.DiagramRegionsResource;
55 import org.simantics.district.route.ontology.RouteResource;
56 import org.simantics.district.selection.ElementSelectionResource;
57 import org.simantics.district.selection.ElementSelectionUtils;
58 import org.simantics.district.selection.ElementSelector;
59 import org.simantics.district.selection.ElementSelector.AggregateCondition;
60 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
61 import org.simantics.district.selection.ElementSelector.All;
62 import org.simantics.district.selection.ElementSelector.Condition;
63 import org.simantics.district.selection.ElementSelector.DiagramGenerator;
64 import org.simantics.district.selection.ElementSelector.ExplicitGenerator;
65 import org.simantics.district.selection.ElementSelector.Generator;
66 import org.simantics.district.selection.ElementSelector.ModelGenerator;
67 import org.simantics.district.selection.ElementSelector.PropertyCondition;
68 import org.simantics.district.selection.ElementSelector.PropertySelector;
69 import org.simantics.district.selection.ElementSelector.RegionCondition;
70 import org.simantics.district.selection.ElementSelector.RouteCondition;
71 import org.simantics.district.selection.ElementSelector.Selector;
72 import org.simantics.layer0.Layer0;
73 import org.simantics.layer0.utils.direct.GraphUtils;
74 import org.simantics.modeling.ModelingResources;
75 import org.simantics.structural.stubs.StructuralResource2;
76 import org.simantics.utils.datastructures.Arrays;
77 import org.simantics.utils.datastructures.Pair;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
81 public class EditSelectorDialog extends Dialog {
83 private static final RowLayoutFactory ROW_LAYOUT = RowLayoutFactory.fillDefaults().wrap(false);
84 private static final ImageDescriptor CROSS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/cross.png").get();
85 private static final ImageDescriptor PLUS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/add.png").get();
87 private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
89 private ElementSelector elementSelector;
91 // Currently selected elements
92 Collection<Resource> currentSelection;
94 // Data for comboboxes
95 private List<Resource> diagrams;
96 private ArrayList<String> diagramNames;
98 private String[] regionNames;
99 private Resource[] regionResources;
101 private String[] routeNames;
102 private Resource[] routeResources;
104 private List<Resource> componentTypes;
105 private List<String> componentTypeNames;
107 private List<String> propertyNames;
108 private List<String> propertyLabels;
110 private Composite conditionPanel;
113 private int generatorIndex;
114 private Combo sourceField;
117 private Text nameField;
119 private Resource diagram;
120 private Combo diagramField;
122 private int selectorIndex;
123 private Combo selectorField;
125 private Resource componentType;
126 private Combo componentTypeField;
128 private String propertyName;
129 private Combo propertyField;
131 private int numberOfItems;
134 private Condition condition;
136 // Dialog area component
137 private Composite content;
139 private int diagramIndex;
141 private LocalResourceManager resourceManager;
143 static class ValidationException extends Exception {
144 private static final long serialVersionUID = 1L;
146 public ValidationException(String message) {
151 // Function type for updating condition objects with optional validation
152 static interface Updater {
153 // If 'validate' is true, a runtime exception may be thrown for invalid values
154 void update(boolean validate) throws ValidationException;
157 final static Updater NULL_UPDATE = validate -> {};
159 // Called to read values from controls into conditions
160 Updater updater = NULL_UPDATE;
163 public EditSelectorDialog(Shell shell, ElementSelector elementSelector, Collection<Resource> currentSelection) {
166 this.elementSelector = elementSelector;
167 if (elementSelector != null) {
169 Simantics.getSession().sync(new ReadRequest() {
171 public void run(ReadGraph graph) throws DatabaseException {
172 elementSelector.buildSelection(graph);
175 } catch (DatabaseException e1) {
176 LOGGER.error("Failed to read element selector resource " + elementSelector.getResource(), e1);
177 throw new RuntimeDatabaseException(e1);
181 this.currentSelection = currentSelection;
183 Map<Resource, String> diagramMap = ElementSelector.findDiagrams();
184 diagrams = new ArrayList<Resource>(diagramMap.size());
185 diagramNames = new ArrayList<String>(diagramMap.size());
186 diagramMap.entrySet()
188 .sorted(Comparator.comparing(e -> e.getValue()))
189 .forEachOrdered(e -> {
190 diagrams.add(e.getKey());
191 diagramNames.add(e.getValue());
194 name = elementSelector != null ? elementSelector.getName() : "";
202 if (elementSelector != null) {
203 Generator generator = elementSelector.getGenerator();
204 if (generator instanceof ModelGenerator) {
207 else if (generator instanceof DiagramGenerator) {
209 diagram = ((DiagramGenerator)generator).diagram;
211 else if (generator instanceof ExplicitGenerator) {
215 throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
218 Selector selector = elementSelector.getSelector();
219 if (selector instanceof All) {
222 else if (selector instanceof PropertySelector) {
223 PropertySelector propertySelector = (PropertySelector)selector;
224 selectorIndex = propertySelector.smallest ? 1 : 2;
225 propertyName = propertySelector.propertyName;
226 numberOfItems = propertySelector.resultCount;
229 throw new IllegalStateException("Unknwon selector type " + selector.getClass().getName());
232 condition = elementSelector.getCondition();
234 componentType = elementSelector.getSelector().componentType;
237 readRegions(diagram);
239 readComponentTypes();
240 updatePropertyList();
243 private void readComponentTypes() {
245 Simantics.getSession().syncRequest(new ReadRequest() {
247 public void run(ReadGraph graph) throws DatabaseException {
248 Layer0 L0 = Layer0.getInstance(graph);
249 List<Resource> types = findComponentTypes(graph);
251 componentTypes = new ArrayList<>(types.size() + 1);
252 componentTypeNames = new ArrayList<>(types.size() + 1);
254 componentTypes.add(null);
255 componentTypeNames.add("Any type");
256 componentTypes.addAll(types);
257 for (Resource t : types) {
258 componentTypeNames.add(graph.getValue2(t, L0.HasName));
262 } catch (DatabaseException e) {
263 LOGGER.error("Failed to read district component types", e);
267 private void readRoutes() {
268 final Map<Resource, String> routes = new HashMap<>();
271 Simantics.getSession().syncRequest(new ReadRequest() {
273 public void run(ReadGraph graph) throws DatabaseException {
274 Layer0 L0 = Layer0.getInstance(graph);
275 RouteResource ROUTE = RouteResource.getInstance(graph);
277 Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
278 List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, ROUTE.Route);
279 for (Resource r : routeCollection) {
280 String name = graph.getRelatedValue(r, L0.HasLabel);
285 } catch (DatabaseException e) {
286 LOGGER.error("Failed to read routes in the model", e);
289 routeNames = routes.values().toArray(new String[routes.size()]);
290 routeResources = routes.keySet().toArray(new Resource[routes.size()]);
293 private void readRegions(Resource diagram) {
294 final Map<Resource, String> regions = new HashMap<>();
297 Simantics.getSession().syncRequest(new ReadRequest() {
299 public void run(ReadGraph graph) throws DatabaseException {
300 Layer0 L0 = Layer0.getInstance(graph);
301 ModelingResources MOD = ModelingResources.getInstance(graph);
302 DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
304 // If a specific diagram is given, use that
305 Collection<Resource> ds = diagram != null ? Collections.singleton(diagram) : diagrams;
307 for (Resource composite : ds) {
308 Resource diagram = graph.getSingleObject(composite, MOD.CompositeToDiagram);
309 for (Resource r : graph.getObjects(diagram, DR.hasRegion)) {
310 if (!graph.isInstanceOf(r, DR.Region))
312 String name = graph.getRelatedValue(r, L0.HasLabel);
313 regions.put(r, name);
318 } catch (DatabaseException e) {
319 LOGGER.error("Failed to read regions in the model", e);
322 regionNames = regions.values().toArray(new String[regions.size()]);
323 regionResources = regions.keySet().toArray(new Resource[regions.size()]);
326 private void updateDialog() {
328 updater.update(false);
329 } catch (ValidationException e) {
330 // Should not happend with argument false
334 updater = updateConditionPanel();
336 content.layout(true, true);
341 protected void okPressed() {
343 generatorIndex = sourceField.getSelectionIndex();
344 if (generatorIndex == 1) {
345 int selectionIndex = diagramField.getSelectionIndex();
346 if (selectionIndex < 0) {
347 diagramField.setFocus();
348 throw new ValidationException("Please select a diagram");
351 diagram = diagrams.get(selectionIndex);
354 name = nameField.getText();
355 if (name.isEmpty()) {
356 nameField.setFocus();
357 throw new ValidationException("Please enter a name");
360 componentType = componentTypes.get(componentTypeField.getSelectionIndex());
362 selectorIndex = selectorField.getSelectionIndex();
363 if (selectorIndex != 0) {
364 int propertyIndex = propertyField.getSelectionIndex();
365 propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
366 if (propertyName.isEmpty()) {
367 propertyField.setFocus();
368 throw new ValidationException("Please select a property");
372 // Try to parse number of items
373 if (useNumberOfItems()) {
375 numberOfItems = Integer.parseInt(nField.getText());
376 if (numberOfItems <= 0) {
379 throw new ValidationException("Number of elements must be positive");
381 } catch (NumberFormatException e) {
384 throw new ValidationException("Please enter a valid number of elements");
388 // To to update condition definitions
389 updater.update(true);
390 } catch (ValidationException e) {
391 MessageDialog.openError(this.getShell(), "Missing data", e.getMessage());
398 public void writeSelection() throws DatabaseException {
399 Simantics.getSession().syncRequest(new WriteRequest() {
401 public void perform(WriteGraph graph) throws DatabaseException {
402 Layer0 L0 = Layer0.getInstance(graph);
403 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
404 ModelingResources MOD = ModelingResources.getInstance(graph);
405 DiagramResource DIA = DiagramResource.getInstance(graph);
407 graph.markUndoPoint();
408 Layer0Utils.addCommentMetadata(graph, "Created new element selection");
410 Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
414 if (elementSelector != null) {
415 selection = elementSelector.getResource();
416 graph.deny(selection);
419 selection = graph.newResource();
422 graph.claim(selection, L0.InstanceOf, ES.Selection);
423 graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
424 graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
425 graph.claim(selection, L0.PartOf, lib);
428 Resource generator = graph.newResource();
429 Resource generatorType;
430 switch (generatorIndex) {
432 generatorType = ES.Generator_Model;
435 generatorType = ES.Generator_Diagram;
436 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
437 graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
440 generatorType = ES.Generator_Explicit;
441 for (Resource r : currentSelection) {
443 if (graph.isInstanceOf(r, DIA.Connection))
445 if (!graph.isInstanceOf(r, DIA.Element)) {
446 if (!graph.hasStatement(r, MOD.ComponentToElement))
449 r = graph.getPossibleObject(r, MOD.ComponentToElement);
454 graph.claim(generator, ES.Generator_HasSelectedElement, r);
457 default: throw new IllegalStateException("Invalid source index " + generatorIndex);
459 graph.claim(generator, L0.InstanceOf, generatorType);
460 graph.claim(selection, ES.Selection_HasGenerator, generator);
463 Resource selector = graph.newResource();
464 Resource selectorType;
465 switch (selectorIndex) {
466 case 0: selectorType = ES.Selector_All; break;
467 case 1: selectorType = ES.Selector_NLowest; break;
468 case 2: selectorType = ES.Selector_NHighest; break;
469 default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
471 graph.claim(selector, L0.InstanceOf, selectorType);
472 graph.claim(selection, ES.Selection_HasSelector, selector);
473 graph.deny(selector, ES.Selector_HasMapping);
474 if (componentType != null)
475 graph.claim(selector, ES.Selector_HasMapping, componentType);
477 if (selectorIndex > 0) {
478 graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
479 graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
483 if (condition != null) {
484 Resource conditionResource = condition.update(graph);
485 graph.claim(selection, ES.Selection_HasCondition, conditionResource);
491 private boolean isDiagramFieldVisible() {
492 return generatorIndex == 1;
495 private boolean useNumberOfItems() {
496 return selectorIndex != 0;
500 protected Control createDialogArea(Composite parent) {
501 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
504 getShell().setText("Edit element selector");
506 content = new Composite(parent, SWT.NONE);
507 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
510 Label nameLabel = new Label(content, SWT.NONE);
511 nameLabel.setText("Name");
512 GridDataFactory.swtDefaults().applyTo(nameLabel);
514 nameField = new Text(content, SWT.BORDER);
515 nameField.setEditable(true);
516 nameField.setText(name);
517 GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
520 Label selectorLabel = new Label(content, SWT.NONE);
521 selectorLabel.setText("Select");
522 GridDataFactory.swtDefaults().applyTo(selectorLabel);
524 Composite selectorComposite = new Composite(content, SWT.NONE);
525 GridDataFactory.swtDefaults().applyTo(selectorComposite);
526 RowLayoutFactory.fillDefaults().applyTo(selectorComposite);
528 nField = new Text(selectorComposite, SWT.BORDER);
529 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
530 if (useNumberOfItems())
531 nField.setText(Integer.toString(numberOfItems));
532 nField.setEnabled(useNumberOfItems());
534 componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
535 RowDataFactory.swtDefaults().applyTo(componentTypeField);
536 componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
538 int index = componentTypes.indexOf(componentType);
539 componentTypeField.select(index >= 0 ? index : 0);
542 // Update property selection controls when component type changes
543 componentTypeField.addSelectionListener(new SelectionAdapter() {
545 public void widgetSelected(SelectionEvent e) {
546 int index = componentTypeField.getSelectionIndex();
547 componentType = index >= 0 ? componentTypes.get(index) : null;
548 updatePropertyList();
549 propertyField.setItems(propertyLabels.toArray(new String[] {}));
555 new Label(selectorComposite, SWT.NONE).setText("with");
557 selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
558 selectorField.setItems("All", "Lowest", "Highest");
559 selectorField.select(selectorIndex);
560 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
562 propertyField = new Combo(selectorComposite, SWT.NONE);
563 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyField);
564 propertyField.setItems(propertyLabels.toArray(new String[] {}));
566 int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
568 propertyField.select(index);
570 propertyField.setText(propertyName != null ? propertyName : "");
572 propertyField.setEnabled(useNumberOfItems());
574 selectorField.addSelectionListener(new SelectionAdapter() {
576 public void widgetSelected(SelectionEvent e) {
577 selectorIndex = selectorField.getSelectionIndex();
579 boolean enable = useNumberOfItems();
580 nField.setEnabled(enable);
581 propertyField.setEnabled(enable);
583 nField.setText(enable ? Integer.toString(numberOfItems) : "");
588 Label sourceLabel = new Label(content, SWT.NONE);
589 sourceLabel.setText("from");
590 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
592 Composite sourceComposite = new Composite(content, SWT.NONE);
593 GridDataFactory.swtDefaults().applyTo(sourceComposite);
594 RowLayoutFactory.fillDefaults().applyTo(sourceComposite);
596 sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
597 RowDataFactory.swtDefaults().applyTo(sourceField);
598 sourceField.setItems("Whole model", "Diagram", "Current selection");
599 sourceField.select(generatorIndex);
601 diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
602 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(diagramField);
603 diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
604 diagramField.setEnabled(isDiagramFieldVisible());
606 diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
607 diagramField.select(diagramIndex);
609 sourceField.addSelectionListener(new SelectionAdapter() {
611 public void widgetSelected(SelectionEvent e) {
612 generatorIndex = sourceField.getSelectionIndex();
613 boolean enabled = isDiagramFieldVisible();
615 diagramIndex = diagramField.getSelectionIndex();
616 diagramField.clearSelection();
618 if (diagramIndex >= 0)
619 diagramField.select(diagramIndex);
621 diagramField.clearSelection();
623 diagramField.setEnabled(enabled);
625 // Refresh list of regions for current diagram
626 diagram = enabled ? (diagramIndex >= 0 ? diagrams.get(diagramIndex) : null) : null;
627 readRegions(diagram);
632 sourceField.select(generatorIndex);
635 Label label = new Label(content, SWT.NONE);
636 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
637 label.setText("where");
639 conditionPanel = new Composite(content, SWT.NONE);
640 GridDataFactory.swtDefaults().span(1, 2).minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
641 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
643 updater = updateConditionPanel();
648 private Updater updateConditionPanel() {
650 for (Widget c : conditionPanel.getChildren())
653 return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
656 private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
657 // Create new contents
658 Button notCheck = new Button(parent, SWT.CHECK);
659 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
660 notCheck.setText("not");
661 notCheck.setSelection(condition != null && condition.isInverse);
663 Composite conditionComp = new Composite(parent, SWT.NONE);
664 GridDataFactory.fillDefaults().applyTo(conditionComp);
666 Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
676 typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
678 final Updater updater;
679 if (condition instanceof PropertyCondition) {
681 updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
682 } else if (condition instanceof RegionCondition) {
684 updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
685 } else if (condition instanceof RouteCondition) {
687 updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
688 } else if (condition instanceof AggregateCondition) {
689 AggregateCondition cond = (AggregateCondition) condition;
690 typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
691 updater = createAggregateConditionPanel(conditionComp, cond);
693 ROW_LAYOUT.applyTo(conditionComp);
694 notCheck.setEnabled(false);
700 updater.update(validate);
701 condition.isInverse = notCheck.getSelection();
705 private final class ConditionTypeSelectionListener extends SelectionAdapter {
706 private final Combo typeCombo;
707 private final Consumer<Condition> consumer;
708 private final Condition finalCondition;
710 private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
711 this.typeCombo = typeCombo;
712 this.consumer = consumer;
713 this.finalCondition = finalCondition;
717 public void widgetSelected(SelectionEvent e) {
718 int index = typeCombo.getSelectionIndex();
719 Condition newCondition = finalCondition;
725 newCondition = createPropertyCondition("", null, null);
728 newCondition = createRegionCondition(null);
731 newCondition = createRouteCondition(null);
734 if (newCondition instanceof AggregateCondition)
735 ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
737 newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
740 if (newCondition instanceof AggregateCondition)
741 ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
743 newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
747 consumer.accept(newCondition);
753 private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
754 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
755 new Label(conditionComp, SWT.NONE); // Eat extra column
757 int n = cond.conditions.size();
758 final Updater[] updates = new Updater[n];
759 for (int i = 0; i < n; i++) {
760 updates[i] = createConditionRowPanel(conditionComp, cond, i);
763 Button addButton = new Button(conditionComp, SWT.PUSH);
764 GridDataFactory.swtDefaults().applyTo(addButton);
765 addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
767 addButton.addSelectionListener(new SelectionAdapter() {
769 public void widgetSelected(SelectionEvent e) {
770 cond.conditions.add(createPropertyCondition("", null, null));
776 for (Updater updater : updates)
777 updater.update(validate);
781 private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
782 GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
783 GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
785 Condition c = parentCondition.conditions.get(i);
787 Composite row = new Composite(parent, SWT.NONE);
788 conditionLayout.applyTo(row);
789 swtDefaults.applyTo(row);
791 Consumer<Condition> update = cd -> {
793 parentCondition.conditions.set(i, cd);
795 parentCondition.conditions.remove(i);
798 Updater updater = createConditionPanelFor(row, c, update);
800 Button removeButton = new Button(parent, SWT.PUSH);
801 swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
802 removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
804 removeButton.addSelectionListener(new SelectionAdapter() {
806 public void widgetSelected(SelectionEvent e) {
807 parentCondition.conditions.remove(i);
815 private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
816 ROW_LAYOUT.applyTo(conditionComp);
819 Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
820 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(routeCombo);
821 routeCombo.setItems(routeNames);
823 // Set current selection
824 int index = Arrays.indexOf(routeResources, condition.routeResource);
826 routeCombo.select(index);
830 int i = routeCombo.getSelectionIndex();
831 if (validate && i < 0) {
832 routeCombo.forceFocus();
833 throw new RuntimeException("Must select a route");
836 condition.routeResource = i >= 0 ? routeResources[i] : null;
840 private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
841 ROW_LAYOUT.applyTo(conditionComp);
844 Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
845 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(regionCombo);
846 regionCombo.setItems(regionNames);
848 // Set current selection
849 int index = Arrays.indexOf(regionResources, condition.regionResource);
851 regionCombo.select(index);
855 int i = regionCombo.getSelectionIndex();
856 if (validate && i < 0) {
857 regionCombo.forceFocus();
858 throw new ValidationException("Please select a region");
861 condition.regionResource = i >= 0 ? regionResources[i] : null;
865 private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
866 ROW_LAYOUT.applyTo(conditionComp);
868 Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
869 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(lowerLimitText);
870 lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
872 new Label(conditionComp, SWT.NONE).setText("\u2264");
874 Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
875 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyNameText);
876 propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
877 int index = propertyNames.indexOf(condition.propertyName);
879 propertyNameText.select(index);
881 propertyNameText.setText(condition.propertyName);
883 new Label(conditionComp, SWT.NONE).setText("\u2264");
885 Text upperLimitText = new Text(conditionComp, SWT.BORDER);
886 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(upperLimitText);
887 upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
892 String text = lowerLimitText.getText();
893 condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
894 } catch (NumberFormatException e) {
896 lowerLimitText.selectAll();
897 lowerLimitText.forceFocus();
898 throw new ValidationException("Please enter a valid lower limit");
903 String text = upperLimitText.getText();
904 condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
905 } catch (NumberFormatException e) {
907 upperLimitText.selectAll();
908 upperLimitText.forceFocus();
909 throw new ValidationException("Please enter a valid upper limit");
913 int ind = propertyNameText.getSelectionIndex();
916 name = propertyNames.get(ind);
918 name = propertyNameText.getText();
920 if (validate && name.isEmpty()) {
921 propertyNameText.forceFocus();
922 throw new ValidationException("Please select a property");
924 condition.propertyName = name;
929 private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
930 return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
933 private static Condition createRegionCondition(Resource regionResource) {
934 return new RegionCondition(null, regionResource, null);
937 private static Condition createRouteCondition(Resource route) {
938 return new RouteCondition(null, route, null);
941 private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
942 Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
943 AggregateCondition condition = new AggregateCondition(null, type, subConditions);
944 condition.isInverse = isInverse;
948 static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
949 DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
950 Resource project = Simantics.getProjectResource();
951 Resource model = ActiveModels.getPossibleActiveModel(graph, project);
953 return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
956 void updatePropertyList() {
957 propertyNames = new ArrayList<>();
958 propertyLabels = new ArrayList<>();
960 Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
961 Set<Pair<String, String>> properties = new HashSet<>();
964 Simantics.getSession().syncRequest(new ReadRequest() {
966 public void run(ReadGraph graph) throws DatabaseException {
967 Layer0 L0 = Layer0.getInstance(graph);
969 for (Resource type : types) {
973 Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
977 if (graph.isInstanceOf(ct, L0.String)) {
978 Resource indexRoot = graph.syncRequest(new IndexRoot(type));
979 String name = graph.getValue(ct);
980 ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
985 for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
986 if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
989 // Filter only numeric properties
990 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
991 if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
994 String name = graph.getRelatedValue2(prop, L0.HasName);
995 String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
996 if (label == null) label = name;
998 properties.add(Pair.make(label, name));
1003 } catch (DatabaseException e) {
1004 LOGGER.error("Failed to read district component properties", e);
1007 propertyNames.clear();
1008 propertyLabels.clear();
1009 properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
1010 propertyLabels.add(p.first);
1011 propertyNames.add(p.second);
1015 static boolean isNumericValueType(String requiredValueType) {
1016 switch (requiredValueType) {