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());
361 selectorIndex = selectorField.getSelectionIndex();
362 int propertyIndex = propertyField.getSelectionIndex();
363 propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
364 if (propertyName.isEmpty()) {
365 propertyField.setFocus();
366 throw new ValidationException("Please select a property");
369 // Try to parse number of items
370 if (useNumberOfItems()) {
372 numberOfItems = Integer.parseInt(nField.getText());
373 if (numberOfItems <= 0) {
376 throw new ValidationException("Number of elements must be positive");
378 } catch (NumberFormatException e) {
381 throw new ValidationException("Please enter a valid number of elements");
385 // To to update condition definitions
386 updater.update(true);
387 } catch (ValidationException e) {
388 MessageDialog.openError(this.getShell(), "Missing data", e.getMessage());
395 public void writeSelection() throws DatabaseException {
396 Simantics.getSession().syncRequest(new WriteRequest() {
398 public void perform(WriteGraph graph) throws DatabaseException {
399 Layer0 L0 = Layer0.getInstance(graph);
400 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
401 ModelingResources MOD = ModelingResources.getInstance(graph);
402 DiagramResource DIA = DiagramResource.getInstance(graph);
404 graph.markUndoPoint();
405 Layer0Utils.addCommentMetadata(graph, "Created new element selection");
407 Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
411 if (elementSelector != null) {
412 selection = elementSelector.getResource();
413 graph.deny(selection);
416 selection = graph.newResource();
419 graph.claim(selection, L0.InstanceOf, ES.Selection);
420 graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
421 graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
422 graph.claim(selection, L0.PartOf, lib);
425 Resource generator = graph.newResource();
426 Resource generatorType;
427 switch (generatorIndex) {
429 generatorType = ES.Generator_Model;
432 generatorType = ES.Generator_Diagram;
433 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
434 graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
437 generatorType = ES.Generator_Explicit;
438 for (Resource r : currentSelection) {
440 if (graph.isInstanceOf(r, DIA.Connection))
442 if (!graph.isInstanceOf(r, DIA.Element)) {
443 if (!graph.hasStatement(r, MOD.ComponentToElement))
446 r = graph.getPossibleObject(r, MOD.ComponentToElement);
451 graph.claim(generator, ES.Generator_HasSelectedElement, r);
454 default: throw new IllegalStateException("Invalid source index " + generatorIndex);
456 graph.claim(generator, L0.InstanceOf, generatorType);
457 graph.claim(selection, ES.Selection_HasGenerator, generator);
460 Resource selector = graph.newResource();
461 Resource selectorType;
462 switch (selectorIndex) {
463 case 0: selectorType = ES.Selector_All; break;
464 case 1: selectorType = ES.Selector_NLowest; break;
465 case 2: selectorType = ES.Selector_NHighest; break;
466 default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
468 graph.claim(selector, L0.InstanceOf, selectorType);
469 graph.claim(selection, ES.Selection_HasSelector, selector);
470 graph.deny(selector, ES.Selector_HasMapping);
471 if (componentType != null)
472 graph.claim(selector, ES.Selector_HasMapping, componentType);
474 if (selectorIndex > 0) {
475 graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
476 graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
480 if (condition != null) {
481 Resource conditionResource = condition.update(graph);
482 graph.claim(selection, ES.Selection_HasCondition, conditionResource);
488 private boolean isDiagramFieldVisible() {
489 return generatorIndex == 1;
492 private boolean useNumberOfItems() {
493 return selectorIndex != 0;
497 protected Control createDialogArea(Composite parent) {
498 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
501 getShell().setText("Edit element selector");
503 content = new Composite(parent, SWT.NONE);
504 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
507 Label nameLabel = new Label(content, SWT.NONE);
508 nameLabel.setText("Name");
509 GridDataFactory.swtDefaults().applyTo(nameLabel);
511 nameField = new Text(content, SWT.BORDER);
512 nameField.setEditable(true);
513 nameField.setText(name);
514 GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
517 Label selectorLabel = new Label(content, SWT.NONE);
518 selectorLabel.setText("Select");
519 GridDataFactory.swtDefaults().applyTo(selectorLabel);
521 Composite selectorComposite = new Composite(content, SWT.NONE);
522 GridDataFactory.swtDefaults().applyTo(selectorComposite);
523 RowLayoutFactory.fillDefaults().applyTo(selectorComposite);
525 nField = new Text(selectorComposite, SWT.BORDER);
526 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
527 if (useNumberOfItems())
528 nField.setText(Integer.toString(numberOfItems));
529 nField.setEnabled(useNumberOfItems());
531 componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
532 RowDataFactory.swtDefaults().applyTo(componentTypeField);
533 componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
535 int index = componentTypes.indexOf(componentType);
536 componentTypeField.select(index >= 0 ? index : 0);
539 // Update property selection controls when component type changes
540 componentTypeField.addSelectionListener(new SelectionAdapter() {
542 public void widgetSelected(SelectionEvent e) {
543 int index = componentTypeField.getSelectionIndex();
544 componentType = index >= 0 ? componentTypes.get(index) : null;
545 updatePropertyList();
546 propertyField.setItems(propertyLabels.toArray(new String[] {}));
552 new Label(selectorComposite, SWT.NONE).setText("with");
554 selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
555 selectorField.setItems("All", "Lowest", "Highest");
556 selectorField.select(selectorIndex);
557 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
559 propertyField = new Combo(selectorComposite, SWT.NONE);
560 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyField);
561 propertyField.setItems(propertyLabels.toArray(new String[] {}));
563 int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
565 propertyField.select(index);
567 propertyField.setText(propertyName != null ? propertyName : "");
569 propertyField.setEnabled(useNumberOfItems());
571 selectorField.addSelectionListener(new SelectionAdapter() {
573 public void widgetSelected(SelectionEvent e) {
574 selectorIndex = selectorField.getSelectionIndex();
576 boolean enable = useNumberOfItems();
577 nField.setEnabled(enable);
578 propertyField.setEnabled(enable);
580 nField.setText(enable ? Integer.toString(numberOfItems) : "");
585 Label sourceLabel = new Label(content, SWT.NONE);
586 sourceLabel.setText("from");
587 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
589 Composite sourceComposite = new Composite(content, SWT.NONE);
590 GridDataFactory.swtDefaults().applyTo(sourceComposite);
591 RowLayoutFactory.fillDefaults().applyTo(sourceComposite);
593 sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
594 RowDataFactory.swtDefaults().applyTo(sourceField);
595 sourceField.setItems("Whole model", "Diagram", "Current selection");
596 sourceField.select(generatorIndex);
598 diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
599 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(diagramField);
600 diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
601 diagramField.setEnabled(isDiagramFieldVisible());
603 diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
604 diagramField.select(diagramIndex);
606 sourceField.addSelectionListener(new SelectionAdapter() {
608 public void widgetSelected(SelectionEvent e) {
609 generatorIndex = sourceField.getSelectionIndex();
610 boolean enabled = isDiagramFieldVisible();
612 diagramIndex = diagramField.getSelectionIndex();
613 diagramField.clearSelection();
615 if (diagramIndex >= 0)
616 diagramField.select(diagramIndex);
618 diagramField.clearSelection();
620 diagramField.setEnabled(enabled);
622 // Refresh list of regions for current diagram
623 diagram = enabled ? (diagramIndex >= 0 ? diagrams.get(diagramIndex) : null) : null;
624 readRegions(diagram);
629 sourceField.select(generatorIndex);
632 Label label = new Label(content, SWT.NONE);
633 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
634 label.setText("where");
636 conditionPanel = new Composite(content, SWT.NONE);
637 GridDataFactory.swtDefaults().span(1, 2).minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
638 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
640 updater = updateConditionPanel();
645 private Updater updateConditionPanel() {
647 for (Widget c : conditionPanel.getChildren())
650 return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
653 private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
654 // Create new contents
655 Button notCheck = new Button(parent, SWT.CHECK);
656 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
657 notCheck.setText("not");
658 notCheck.setSelection(condition != null && condition.isInverse);
660 Composite conditionComp = new Composite(parent, SWT.NONE);
661 GridDataFactory.fillDefaults().applyTo(conditionComp);
663 Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
673 typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
675 final Updater updater;
676 if (condition instanceof PropertyCondition) {
678 updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
679 } else if (condition instanceof RegionCondition) {
681 updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
682 } else if (condition instanceof RouteCondition) {
684 updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
685 } else if (condition instanceof AggregateCondition) {
686 AggregateCondition cond = (AggregateCondition) condition;
687 typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
688 updater = createAggregateConditionPanel(conditionComp, cond);
690 ROW_LAYOUT.applyTo(conditionComp);
691 notCheck.setEnabled(false);
697 updater.update(validate);
698 condition.isInverse = notCheck.getSelection();
702 private final class ConditionTypeSelectionListener extends SelectionAdapter {
703 private final Combo typeCombo;
704 private final Consumer<Condition> consumer;
705 private final Condition finalCondition;
707 private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
708 this.typeCombo = typeCombo;
709 this.consumer = consumer;
710 this.finalCondition = finalCondition;
714 public void widgetSelected(SelectionEvent e) {
715 int index = typeCombo.getSelectionIndex();
716 Condition newCondition = finalCondition;
722 newCondition = createPropertyCondition("", null, null);
725 newCondition = createRegionCondition(null);
728 newCondition = createRouteCondition(null);
731 if (newCondition instanceof AggregateCondition)
732 ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
734 newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
737 if (newCondition instanceof AggregateCondition)
738 ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
740 newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
744 consumer.accept(newCondition);
750 private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
751 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
752 new Label(conditionComp, SWT.NONE); // Eat extra column
754 int n = cond.conditions.size();
755 final Updater[] updates = new Updater[n];
756 for (int i = 0; i < n; i++) {
757 updates[i] = createConditionRowPanel(conditionComp, cond, i);
760 Button addButton = new Button(conditionComp, SWT.PUSH);
761 GridDataFactory.swtDefaults().applyTo(addButton);
762 addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
764 addButton.addSelectionListener(new SelectionAdapter() {
766 public void widgetSelected(SelectionEvent e) {
767 cond.conditions.add(createPropertyCondition("", null, null));
773 for (Updater updater : updates)
774 updater.update(validate);
778 private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
779 GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
780 GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
782 Condition c = parentCondition.conditions.get(i);
784 Composite row = new Composite(parent, SWT.NONE);
785 conditionLayout.applyTo(row);
786 swtDefaults.applyTo(row);
788 Consumer<Condition> update = cd -> {
790 parentCondition.conditions.set(i, cd);
792 parentCondition.conditions.remove(i);
795 Updater updater = createConditionPanelFor(row, c, update);
797 Button removeButton = new Button(parent, SWT.PUSH);
798 swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
799 removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
801 removeButton.addSelectionListener(new SelectionAdapter() {
803 public void widgetSelected(SelectionEvent e) {
804 parentCondition.conditions.remove(i);
812 private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
813 ROW_LAYOUT.applyTo(conditionComp);
816 Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
817 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(routeCombo);
818 routeCombo.setItems(routeNames);
820 // Set current selection
821 int index = Arrays.indexOf(routeResources, condition.routeResource);
823 routeCombo.select(index);
827 int i = routeCombo.getSelectionIndex();
828 if (validate && i < 0) {
829 routeCombo.forceFocus();
830 throw new RuntimeException("Must select a route");
833 condition.routeResource = i >= 0 ? routeResources[i] : null;
837 private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
838 ROW_LAYOUT.applyTo(conditionComp);
841 Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
842 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(regionCombo);
843 regionCombo.setItems(regionNames);
845 // Set current selection
846 int index = Arrays.indexOf(regionResources, condition.regionResource);
848 regionCombo.select(index);
852 int i = regionCombo.getSelectionIndex();
853 if (validate && i < 0) {
854 regionCombo.forceFocus();
855 throw new ValidationException("Please select a region");
858 condition.regionResource = i >= 0 ? regionResources[i] : null;
862 private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
863 ROW_LAYOUT.applyTo(conditionComp);
865 Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
866 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(lowerLimitText);
867 lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
869 new Label(conditionComp, SWT.NONE).setText("\u2264");
871 Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
872 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyNameText);
873 propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
874 int index = propertyNames.indexOf(condition.propertyName);
876 propertyNameText.select(index);
878 propertyNameText.setText(condition.propertyName);
880 new Label(conditionComp, SWT.NONE).setText("\u2264");
882 Text upperLimitText = new Text(conditionComp, SWT.BORDER);
883 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(upperLimitText);
884 upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
889 String text = lowerLimitText.getText();
890 condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
891 } catch (NumberFormatException e) {
893 lowerLimitText.selectAll();
894 lowerLimitText.forceFocus();
895 throw new ValidationException("Please enter a valid lower limit");
900 String text = upperLimitText.getText();
901 condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
902 } catch (NumberFormatException e) {
904 upperLimitText.selectAll();
905 upperLimitText.forceFocus();
906 throw new ValidationException("Please enter a valid upper limit");
910 int ind = propertyNameText.getSelectionIndex();
913 name = propertyNames.get(ind);
915 name = propertyNameText.getText();
917 if (validate && name.isEmpty()) {
918 propertyNameText.forceFocus();
919 throw new ValidationException("Please select a property");
921 condition.propertyName = name;
926 private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
927 return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
930 private static Condition createRegionCondition(Resource regionResource) {
931 return new RegionCondition(null, regionResource, null);
934 private static Condition createRouteCondition(Resource route) {
935 return new RouteCondition(null, route, null);
938 private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
939 Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
940 AggregateCondition condition = new AggregateCondition(null, type, subConditions);
941 condition.isInverse = isInverse;
945 static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
946 DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
947 Resource project = Simantics.getProjectResource();
948 Resource model = ActiveModels.getPossibleActiveModel(graph, project);
950 return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
953 void updatePropertyList() {
954 propertyNames = new ArrayList<>();
955 propertyLabels = new ArrayList<>();
957 Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
958 Set<Pair<String, String>> properties = new HashSet<>();
961 Simantics.getSession().syncRequest(new ReadRequest() {
963 public void run(ReadGraph graph) throws DatabaseException {
964 Layer0 L0 = Layer0.getInstance(graph);
966 for (Resource type : types) {
970 Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
974 if (graph.isInstanceOf(ct, L0.String)) {
975 Resource indexRoot = graph.syncRequest(new IndexRoot(type));
976 String name = graph.getValue(ct);
977 ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
982 for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
983 if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
986 // Filter only numeric properties
987 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
988 if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
991 String name = graph.getRelatedValue2(prop, L0.HasName);
992 String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
993 if (label == null) label = name;
995 properties.add(Pair.make(label, name));
1000 } catch (DatabaseException e) {
1001 LOGGER.error("Failed to read district component properties", e);
1004 propertyNames.clear();
1005 propertyLabels.clear();
1006 properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
1007 propertyLabels.add(p.first);
1008 propertyNames.add(p.second);
1012 static boolean isNumericValueType(String requiredValueType) {
1013 switch (requiredValueType) {