]> gerrit.simantics Code Review - simantics/district.git/commitdiff
Add enable/disable feature for tech type tables 86/4486/1
authorReino Ruusu <reino.ruusu@semantum.fi>
Mon, 5 Oct 2020 12:52:51 +0000 (15:52 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 6 Oct 2020 21:43:27 +0000 (21:43 +0000)
gitlab #101

Also:
- Some new SCL entry points for DB requests
- Some refactoring of tech type table data provider
- Some refactoring of TechTypeTableData request

Change-Id: I1b335b49f387e0b9b2651879279f79f40ed293f8
(cherry picked from commit 63b3e619ad5b28b382f97d5b607060365b6e1caa)

12 files changed:
org.simantics.district.network.ontology/graph/DistrictNetworkTechType.pgraph
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/adapters/TechTypeEditorAdapter.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/EnableListener.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java
org.simantics.district.network/scl/Simantics/District/TechType.scl
org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeUtils.java
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/EnableTechTypeItem.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableData.java
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableRecords.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/WriteTechTypeTable.java

index d63271af5befbfb607c17943885690851cfdd8c8..16f315c28c9096a99fdebff539ad8659549fec88 100644 (file)
@@ -10,10 +10,15 @@ table = TT.TechTypeTable <T L0.Entity
     // Table data as a CSV string literal
     >-- table.HasData <R L0.HasProperty : L0.TotalFunction
         --> L0.String
+    >-- table.HasEnabledItems <R L0.HasProperty : L0.FunctionalRelation
+        --> L0.IntegerArray
     
     // Link to corresponding component type
     >-- table.HasComponentType <R L0.IsRelatedTo : L0.FunctionalRelation
         --> STR.ComponentType
+        
+    @L0.assert table.HasEnabledItems
+        [] : L0.IntegerArray
 
 TT.Functions : L0.Library
 TT.Functions.techTypeCodeValueAccessor : L0.ExternalValue
index 5ebe6ed479060d086b9c30783c0b3ef71ef17e53..6ab176ede84979537a7b1dc303cbcfcf8984afd7 100644 (file)
@@ -64,11 +64,9 @@ public class TechTypeEditorAdapter extends AbstractResourceEditorAdapter impleme
                                public void run(ReadGraph graph) throws DatabaseException {
                                        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
                                        Resource componentType = graph.getPossibleObject(table, DN.TechType_TechTypeTable_HasComponentType);
-                                       String data = graph.getPossibleRelatedValue2(table, DN.TechType_TechTypeTable_HasData);
 
                                        TechTypeTableView.table.getDisplay().asyncExec(() -> {
                                                TechTypeTableView.table.setComponentType(componentType);
-                                               TechTypeTableView.table.setTechTypeData(data);
                                        });
                                }
                        });
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/EnableListener.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/EnableListener.java
new file mode 100644 (file)
index 0000000..71fceac
--- /dev/null
@@ -0,0 +1,5 @@
+package org.simantics.district.network.ui.techtype.table;
+
+public interface EnableListener {
+       void rowEnabled(int rowIndex, boolean enabled);
+}
index 8ee627a57989c09a79732b4773281c1a1b493352..fa0a1acb4632957bd329bcc6f4e4d11ca9beedbb 100644 (file)
@@ -4,7 +4,11 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import org.eclipse.jface.layout.GridDataFactory;
@@ -14,8 +18,11 @@ import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
 import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
 import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
 import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
 import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler;
 import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.edit.editor.CheckBoxCellEditor;
 import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer;
 import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
 import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
@@ -29,16 +36,16 @@ import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
-import org.eclipse.nebula.widgets.nattable.hover.HoverLayer;
-import org.eclipse.nebula.widgets.nattable.hover.config.BodyHoverStylingBindings;
 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
 import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
 import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
 import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
 import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
 import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
 import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
 import org.eclipse.nebula.widgets.nattable.style.Style;
@@ -51,23 +58,35 @@ import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Text;
 import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.ResourceRead;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.PossibleActiveModel;
+import org.simantics.db.procedure.Listener;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.techtype.requests.EnableTechTypeItem;
 import org.simantics.district.network.techtype.requests.PossibleTechTypeKeyName;
 import org.simantics.district.network.techtype.requests.PossibleTechTypeTable;
 import org.simantics.district.network.techtype.requests.WriteTechTypeTable;
+import org.simantics.utils.datastructures.Triple;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class TechTypeTable extends Composite {
 
-       private static final String INVALID_LABEL = "INVALID";
+       private static final CheckBoxCellEditor CHECK_BOX_CELL_EDITOR = new CheckBoxCellEditor();
+
+       private static final CheckBoxPainter CHECK_BOX_PAINTER = new CheckBoxPainter();
+
+    private static final String INVALID_LABEL = "INVALID";
+    protected static final String CHECK_BOX_LABEL = "CHECK_BOX";
 
        private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTable.class);
 
+
        NatTable table;
        private TechTypeTableDataProvider bodyDataProvider;
        DataLayer bodyDataLayer;
@@ -86,18 +105,19 @@ public class TechTypeTable extends Composite {
 
        private Resource componentType;
        private Resource tableResource;
+       private String keyName;
 
        protected Set<String> validationResult;
 
-       public TechTypeTable(Composite parent, int style, Resource componentType, Resource tableResource, String data) {
+       public TechTypeTable(Composite parent, int style, Resource componentType) {
                super(parent, style);
+               this.tableResource = null;
                this.componentType = componentType;
-               this.tableResource = tableResource;
 
-               defaultInitializeUI(data);
+               defaultInitializeUI();
        }
 
-       private void defaultInitializeUI(String data) {
+       private void defaultInitializeUI() {
                GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
                GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
 
@@ -111,7 +131,7 @@ public class TechTypeTable extends Composite {
                GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
                GridLayoutFactory.fillDefaults().numColumns(1).applyTo(tableComposite);
 
-               createTable(tableComposite, data);
+               createTable(tableComposite);
        }
 
        private void createFilterBar(Composite filterComposite) {
@@ -130,26 +150,76 @@ public class TechTypeTable extends Composite {
 
        }
 
-       private void createTable(Composite parent, String data) {
+       private void createTable(Composite parent) {
 
+               Triple<Resource, String, List<Integer>> tableData;
+               String data = null;
+               int[] enabled = null;
+               if (componentType != null) {
+                       try {
+                               tableData = Simantics.getSession().syncRequest(new TableDataRequest(componentType), new TableDataListener(componentType));
+                               if (tableData != null) {
+                                       this.tableResource = tableData.first;
+                                       data = tableData.second;
+                                       enabled = tableData.third.stream().mapToInt(Integer::intValue).toArray();
+                               } else {
+                                       this.tableResource = null;
+                               }
+                       } catch (DatabaseException e) {
+                               LOGGER.error("Failed to read tech type table data for {}", componentType, e);
+                       }
+               }
+               
                // build the body layer stack
                // Usually you would create a new layer stack by extending
                // AbstractIndexLayerTransform and
                // setting the ViewportLayer as underlying layer. But in this case using the
                // ViewportLayer
                // directly as body layer is also working.
-               bodyDataProvider = new TechTypeTableDataProvider(data);
+               bodyDataProvider = new TechTypeTableDataProvider(data, enabled);
                bodyDataLayer = new DataLayer(bodyDataProvider);
                
+               bodyDataLayer.setConfigLabelAccumulator(new IConfigLabelAccumulator() {
+                       @Override
+                       public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
+                               if (columnPosition == 0) {
+                                       configLabels.addLabel(CHECK_BOX_LABEL);
+                               } else if (validationResult != null && keyName != null) {
+                                       int keyColumn = bodyDataProvider.getVariableIndex(keyName);
+                                       if (keyColumn >= 0) {
+                                               String key = (String) bodyDataProvider.getDataValue(keyColumn, rowPosition);
+                                               String columnName = bodyDataProvider.getVariableName(columnPosition);
+                                               
+                                               if (validationResult.contains(key + "/" + columnName)) {
+                                                       configLabels.addLabel(INVALID_LABEL);
+                                               }
+                                       }
+                               }
+                       }
+               });
+               
+               bodyDataProvider.addEnableListener((rowIndex, enable) -> {
+                       if (this.tableResource != null) {
+                               try {
+                                       Simantics.getSession().syncRequest(
+                                                       new EnableTechTypeItem(this.tableResource, rowIndex, enable)
+                                               );
+                               } catch (DatabaseException e) {
+                                       LOGGER.error("Failed to update enable state for {}", tableResource, e);
+                               }
+                       }
+               });
+               
                RowReorderLayer rowReorderLayer = new RowReorderLayer(
                                columnHideShowLayer = new ColumnHideShowLayer(bodyDataLayer));
 
-               HoverLayer hoverLayer = new HoverLayer(rowReorderLayer, false);
-               // we need to ensure that the hover styling is removed when the mouse
-               // cursor moves out of the cell area
-               hoverLayer.addConfiguration(new BodyHoverStylingBindings(hoverLayer));
-
-               selectionLayer = new SelectionLayer(hoverLayer);
+//             HoverLayer hoverLayer = new HoverLayer(rowReorderLayer, false);
+//             // we need to ensure that the hover styling is removed when the mouse
+//             // cursor moves out of the cell area
+//             hoverLayer.addConfiguration(new BodyHoverStylingBindings(hoverLayer));
+//
+//             selectionLayer = new SelectionLayer(hoverLayer);
+               selectionLayer = new SelectionLayer(rowReorderLayer);
 
                viewportLayer = new ViewportLayer(selectionLayer);
                viewportLayer.setRegionName(GridRegion.BODY);
@@ -188,18 +258,38 @@ public class TechTypeTable extends Composite {
                table = new NatTable(parent, NatTable.DEFAULT_STYLE_OPTIONS | SWT.BORDER, gridLayer, false);
                GridDataFactory.fillDefaults().grab(true, true).applyTo(table);
                
+               table.addConfiguration(new SingleClickSortConfiguration());
+               
                // Show entries labeled "INVALID" with red text
                table.addConfiguration(new AbstractRegistryConfiguration() {
                        @Override
                        public void configureRegistry(IConfigRegistry configRegistry) {
                                Style cellStyle = new Style();
                                cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_RED);
+                               
+                               configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
+                                               IEditableRule.ALWAYS_EDITABLE);
+                               
                                configRegistry.registerConfigAttribute(
                                                CellConfigAttributes.CELL_STYLE,
                                                cellStyle,
                                                DisplayMode.NORMAL,
                                                INVALID_LABEL
                                        );
+                               
+                               configRegistry.registerConfigAttribute(
+                                               EditConfigAttributes.CELL_EDITOR,
+                                               CHECK_BOX_CELL_EDITOR,
+                                               DisplayMode.EDIT,
+                                               CHECK_BOX_LABEL
+                                       );
+                               
+                               configRegistry.registerConfigAttribute(
+                                               CellConfigAttributes.CELL_PAINTER,
+                                               CHECK_BOX_PAINTER,
+                                               DisplayMode.NORMAL,
+                                               CHECK_BOX_LABEL
+                                       );
                        }
                });
 
@@ -239,6 +329,68 @@ public class TechTypeTable extends Composite {
                cpb.dispose();
                super.dispose();
        }
+       
+       public void setComponentType(Resource componentType) {
+               if (Objects.equals(this.componentType, componentType))
+                       return;
+               
+               this.componentType = componentType;
+               this.keyName = getKeyColumnName(componentType);
+               
+               Simantics.getSession().asyncRequest(new TableDataRequest(componentType), new TableDataListener(componentType));
+       }
+
+       private final class TableDataListener implements Listener<Triple<Resource, String, List<Integer>>> {
+               private final Resource componentType;
+
+               private TableDataListener(Resource componentType) {
+                       this.componentType = componentType;
+               }
+
+               @Override
+               public void execute(Triple<Resource, String, List<Integer>> result) {
+                       TechTypeTable.this.getDisplay().asyncExec(() -> {
+                               TechTypeTable.this.tableResource = result.first;
+                               String data = result.second;
+                               int[] enabled = result.third.stream().mapToInt(Integer::intValue).toArray();
+                               
+                               setTechTypeData(data, enabled);
+                               setValidationResult(null);
+                       });
+               }
+
+               @Override
+               public void exception(Throwable t) {
+                       LOGGER.error("Error updating tech type table data for {}", componentType, t);
+               }
+
+               @Override
+               public boolean isDisposed() {
+                       return TechTypeTable.this.table != null && TechTypeTable.this.table.isDisposed() ||
+                                       !Objects.equals(TechTypeTable.this.componentType, componentType);
+               }
+       }
+
+       private final class TableDataRequest extends ResourceRead<Triple<Resource, String, List<Integer>>> {
+               private TableDataRequest(Resource componentType) {
+                       super(componentType);
+               }
+       
+               @Override
+               public Triple<Resource, String, List<Integer>> perform(ReadGraph graph) throws DatabaseException {
+                       Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
+                       if (model == null)
+                               return null;
+                       Resource tableResource = graph.syncRequest(new PossibleTechTypeTable(model, this.resource));
+                       if (tableResource == null)
+                               return null;
+                       
+                       String data = graph.getRelatedValue2(tableResource, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasData);
+                       int[] enabled = graph.getRelatedValue2(tableResource, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasEnabledItems);
+                       List<Integer> list = Arrays.stream(enabled).mapToObj(Integer::valueOf).collect(Collectors.toList());
+                       return Triple.make(tableResource, data, list);
+               }
+       }
 
        public void setTechTypePath(String path) {
                String data;
@@ -254,29 +406,21 @@ public class TechTypeTable extends Composite {
                                @Override
                                public void perform(WriteGraph graph) throws DatabaseException {
                                        graph.syncRequest(new WriteTechTypeTable(componentType, data));
-                                       Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
-                                       if (model != null) {
-                                               tableResource = graph.syncRequest(new PossibleTechTypeTable(model, componentType));
-                                       }
                                }
                        });
                } catch (DatabaseException e) {
                        LOGGER.error("Failed to write tech type table data to model", e);
                }
 
-               setTechTypeData(data);
-               setValidationResult(null);
+               setComponentType(componentType);
        }
 
-       public void setTechTypeData(String data) {
+       public void setTechTypeData(String data, int[] enabled) {
                bodyDataProvider.setData(data);
+               bodyDataProvider.setEnabledFlags(enabled);
                table.refresh(true);
        }
 
-       public void setComponentType(Resource componentType) {
-               this.componentType = componentType;
-       }
-
        /**
         * Set results of a validation operation
         * 
@@ -292,26 +436,7 @@ public class TechTypeTable extends Composite {
                
                this.validationResult = result;
                if (result != null) {
-                       String keyName = getKeyColumnName(componentType);
-                       
-                       bodyDataLayer.setConfigLabelAccumulator(new IConfigLabelAccumulator() {
-                               @Override
-                               public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
-                                       if (keyName != null) {
-                                               int keyColumn = bodyDataProvider.getVariableIndex(keyName);
-                                               if (keyColumn >= 0) {
-                                                       String key = (String) bodyDataProvider.getDataValue(keyColumn, rowPosition);
-                                                       String columnName = bodyDataProvider.getVariableName(columnPosition);
-                                                       
-                                                       if (validationResult.contains(key + "/" + columnName)) {
-                                                               configLabels.addLabel(INVALID_LABEL);
-                                                       }
-                                               }
-                                       }
-                               }
-                       });
-               } else {
-                       bodyDataLayer.setConfigLabelAccumulator(null);
+                       keyName = getKeyColumnName(componentType);
                }
                
                table.refresh();
index d6b0c7d228bbf55554141b6cda434c426694718f..c23a6079952a82eb6927c1cdc28ef5de1a7853f0 100644 (file)
@@ -7,9 +7,10 @@ import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import org.apache.commons.csv.CSVRecord;
+import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
 import org.simantics.district.imports.DistrictImportUtils;
 import org.slf4j.Logger;
@@ -17,43 +18,86 @@ import org.slf4j.LoggerFactory;
 
 public class TechTypeTableDataProvider implements IDataProvider {
 
-    @SuppressWarnings("unused")
     private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableDataProvider.class);
 
     private List<CSVRecord> records = new ArrayList<>();
-    private List<CSVRecord> filteredRecords = new ArrayList<>();
-    private String filter = "";
+    private boolean[] enabled;
+    private String filter = null;
     private List<String> variables = null;
     private List<String> headers = null;
+    private int[] filteredRows;
 
+    private ListenerList<EnableListener> enableListeners = new ListenerList<EnableListener>();
+
+    public TechTypeTableDataProvider(String data, int[] enabledList) {
+        setData(data);
+        setEnabledFlags(enabledList);
+    }
+    
     public TechTypeTableDataProvider(String data) {
         // load csv
         setData(data);
     }
+    
+    public void setEnabledFlags(int[] enabledList) {
+        this.enabled = new boolean[records.size()];
+        if (enabledList != null) {
+            for (int i : enabledList) {
+                if (i >= 0 && i < enabled.length)
+                    enabled[i] = true;
+            }
+        }
+    }
 
     public String getVariableName(int columnIndex) {
-        return variables != null && columnIndex < variables.size() ? variables.get(columnIndex) : null;
+        return variables != null && columnIndex > 0 && columnIndex <= variables.size() ? variables.get(columnIndex - 1) : null;
     }
 
     public int getVariableIndex(String variableName) {
-        return variables != null ? variables.indexOf(variableName) : -1;
+        return variables != null ? variables.indexOf(variableName) + 1 : -1;
+    }
+    
+    public CSVRecord getRecord(int rowIndex) {
+        return records.get(filteredRows[rowIndex]);
+    }
+    
+    public boolean isEnabled(int rowIndex) {
+        return enabled[filteredRows[rowIndex]];
     }
 
     public String getHeaderValue(int columnIndex) {
         if (headers == null) {
             return "<empty>";
+        } else if (columnIndex == 0) {
+            return "Enabled";
+        } else {
+            return headers.get(columnIndex - 1);
         }
-        return headers.get(columnIndex);
     }
 
     @Override
     public Object getDataValue(int columnIndex, int rowIndex) {
-        return filteredRecords.get(rowIndex).get(columnIndex);
+        if (columnIndex == 0) {
+            return isEnabled(rowIndex);
+        }
+        return getRecord(rowIndex).get(columnIndex - 1);
     }
 
     @Override
     public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+        if (columnIndex == 0) {
+            boolean value = Boolean.parseBoolean((String) newValue);
+            enabled[filteredRows[rowIndex]] = value;
+            fireEnableEvent(rowIndex, value);
+        }
+    }
+    
+    public void addEnableListener(EnableListener listener) {
+        enableListeners.add(listener);
+    }
 
+    private void fireEnableEvent(int rowIndex, boolean newValue) {
+        enableListeners.forEach(l -> l.rowEnabled(rowIndex, newValue));
     }
 
     @Override
@@ -61,30 +105,38 @@ public class TechTypeTableDataProvider implements IDataProvider {
         if (records.isEmpty()) {
             return 0;
         }
-        return records.get(0).size();
+        return records.get(0).size() + 1;
     }
 
     @Override
     public int getRowCount() {
-        return filteredRecords.size();
+        return filteredRows.length;
     }
 
     public boolean isEditable(int columnIndex, int rowIndex) {
-        return false;
+        return columnIndex == 0;
     }
 
     public void setFilter(String text) {
-        this.filter = text.toLowerCase();
+        this.filter = text != null ? text.toLowerCase() : null;
 
-        filteredRecords = records.stream().filter(record -> {
-            for (int i = 0; i < record.size(); i++) {
-                String columnContent = record.get(i);
-                if (columnContent.toLowerCase().contains(filter)) {
-                    return true;
-                }
+        filteredRows = IntStream.range(0, records.size())
+                .filter(k -> isMatch(records.get(k), filter))
+                .toArray();
+    }
+
+    private static boolean isMatch(CSVRecord record, String filterString) {
+        if (filterString == null || filterString.isEmpty())
+            return true;
+        
+        for (int i = 0; i < record.size(); i++) {
+            String columnContent = record.get(i);
+            if (columnContent.toLowerCase().contains(filterString)) {
+                return true;
             }
-            return false;
-        }).collect(Collectors.toList());
+        }
+        
+        return false;
     }
 
     /**
@@ -96,7 +148,7 @@ public class TechTypeTableDataProvider implements IDataProvider {
      */
     public void setPath(String path) {
         records.clear();
-        filteredRecords.clear();
+
         if (path != null) {
             Path techTypeCsv = Paths.get(path);
             try {
@@ -109,7 +161,7 @@ public class TechTypeTableDataProvider implements IDataProvider {
             }
         }
 
-        setFilter("");
+        setFilter(null);
     }
 
     /**
@@ -121,7 +173,7 @@ public class TechTypeTableDataProvider implements IDataProvider {
      */
     public void setData(String data) {
         records.clear();
-        filteredRecords.clear();
+
         if (data != null) {
             long ncommas = data.chars().filter(c -> c == ',').count();
             long nsemis = data.chars().filter(c -> c == ';').count();
@@ -154,8 +206,10 @@ public class TechTypeTableDataProvider implements IDataProvider {
                 headers.add(variable + (unit != null && !unit.isEmpty() && !(unit.startsWith("(") && unit.endsWith(")")) ? " [" + unit + "]" : ""));
             }
         }
+        
+        enabled = new boolean[records.size()];
 
-        setFilter("");
+        setFilter(null);
     }
 
 }
\ No newline at end of file
index 8f4b57c98b88384bbfaa9f46594406ab7976c7eb..d66c9a8caa05be2a1d4630fedb24d9a68525e1e9 100644 (file)
@@ -16,15 +16,10 @@ import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
 import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
 import org.eclipse.swt.widgets.Composite;
-import org.simantics.Simantics;
 import org.simantics.db.Resource;
 import org.simantics.db.common.NamedResource;
-import org.simantics.db.common.procedure.adapter.TransientCacheListener;
 import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.layer0.request.PossibleActiveModel;
 import org.simantics.district.network.DistrictNetworkUtil;
-import org.simantics.district.network.techtype.requests.PossibleTechTypeTable;
-import org.simantics.district.network.techtype.requests.PossibleTechTypeTableData;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -94,21 +89,7 @@ public class TechTypeTableView {
 
                LOGGER.debug("Pipe component type is {}", pipe);
 
-               String data = null;
-               Resource tableResource = null;
-               if (pipe != null) {
-                       try {
-                               Resource model = Simantics.getSession().syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
-                               if (model != null) {
-                                       tableResource = Simantics.getSession().syncRequest(new PossibleTechTypeTable(model, pipe), TransientCacheListener.instance());
-                                       data = Simantics.getSession().syncRequest(new PossibleTechTypeTableData(model, pipe), TransientCacheListener.instance());
-                               }
-                       } catch (DatabaseException e) {
-                               LOGGER.error("Failed to read tech type table data for {}", pipe, e);
-                       }
-               }
-
-               table = new TechTypeTable(parent, 0, pipe, tableResource, data);
+               table = new TechTypeTable(parent, 0, pipe);
        }
 
        @PreDestroy
index e8ab4eec1d00ae1d788bb16bc6d25098c66392c9..33d40360fedb8993aace95ac17885a4c7bb145b1 100644 (file)
@@ -1,4 +1,5 @@
 import "Simantics/DB"
+import "Map" as Map
 
 importJava "org.simantics.district.network.techtype.TechTypeUtils" where
     "Update component properties from tech type table: `updateComponent component`"
@@ -9,3 +10,14 @@ importJava "org.simantics.district.network.techtype.TechTypeUtils" where
     resetMapElements :: Resource -> <Proc> ()
     "Load a tech type table for a given component type from a CSV file: `loadTechTypeTable componentType filePath`"
     loadTechTypeTable :: Resource -> String -> <Proc> ()
+    
+    "Get a possible tech type key property name for a component type"
+    getPossibleTechTypeKeyName :: Resource -> <ReadGraph> Maybe String
+    "Get a possible tech type table in a model for a component type: `getPossibleTechTypeTable model componentType`"
+    getPossibleTechTypeTable :: Resource -> Resource -> <ReadGraph> Maybe Resource
+    "Get data from a tech type table"
+    getTechTypeData :: Resource -> <ReadGraph> Map.T String (Map.T String String)
+    "Get data for enabled items in a tech type table"
+    getEnabledTechTypeData :: Resource -> <ReadGraph> Map.T String (Map.T String String)
+    "Get a possible row from a tech type table for the given key value"
+    getPossibleTechTypeItem :: Resource -> String -> <ReadGraph> Maybe (Map.T String String)
index 377253dde3d108b433c572d6e0ddc576872b30ab..930435ba2ef6b859339311852fc1de49396688d7 100644 (file)
@@ -32,6 +32,7 @@ import org.simantics.db.layer0.request.PropertyInfoRequest;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.network.techtype.requests.PossibleTechTypeItem;
+import org.simantics.district.network.techtype.requests.PossibleTechTypeKeyName;
 import org.simantics.district.network.techtype.requests.PossibleTechTypeTable;
 import org.simantics.district.network.techtype.requests.TechTypeTableData;
 import org.simantics.district.network.techtype.requests.TechTypeTableKeyName;
@@ -341,11 +342,86 @@ public class TechTypeUtils {
                }
        }
 
+       /**
+        * Find the tech type table key property name for a table.
+        * 
+        * @param graph  A read interface to the graph db
+        * @param table  A tech type table resource
+        * @return  Name of the key property
+        * @throws DatabaseException
+        */
        public static String getKeyPropertyName(ReadGraph graph, Resource table) throws DatabaseException {
                String name = graph.syncRequest(new TechTypeTableKeyName(table), TransientCacheListener.instance());
                if (name == null) name = "_" + DEFAULT_KEY_NAME;
                return name; 
        }
+       
+       /**
+        * Get a possible tech type table key property for a component type
+        * 
+        * @param graph  A read interface to the graph db
+        * @param componentType  An STR.ComponentType instance
+        * @return  The name of a property that uses DN.TechType.Functions.techTypeCodeValueAccessor or null if not found
+        * @throws DatabaseException
+        */
+       public static String getPossibleTechTypeKeyName(ReadGraph graph, Resource componentType) throws DatabaseException {
+               return graph.syncRequest(new PossibleTechTypeKeyName(componentType), TransientCacheListener.instance());
+       }
+
+       /**
+        * Get a possible tech type table from a given model, for a given component type.
+        * 
+        * @param graph  A read interface to the graph db
+        * @param model  A model resource (or other index root)
+        * @param componentType  An STR.ComponentType instance
+        * @return  A tech type table resource or null if not found
+        * @throws DatabaseException
+        */
+       public static Resource getPossibleTechTypeTable(ReadGraph graph, Resource model, Resource componentType) throws DatabaseException {
+               return graph.syncRequest(new PossibleTechTypeTable(model, componentType), TransientCacheListener.instance());
+       }
+
+       /**
+        * Get the contents of a tech type table, indexed by item code and property name.
+        * 
+        * @param graph  A read interface to the graph db
+        * @param table  A tech type table resource
+        * @return  A map from item code to a map of property values
+        * @throws DatabaseException
+        */
+       public static Map<String, Map<String, String>> getTechTypeData(ReadGraph graph, Resource table) throws DatabaseException {
+               return graph.syncRequest(new TechTypeTableData(table), TransientCacheListener.instance());
+       }
+
+       /**
+        * Get the enabled contents of a tech type table, indexed by item code and property name.
+        * 
+        * The returned map is a subset of the one returned by {@link #getTechTypeData(ReadGraph, Resource)}
+        * with only the items whose index is listed in the HasEnabledItems property.
+        * 
+        * @param graph  A read interface to the graph db
+        * @param table  A tech type table resource
+        * @return  A map from item code to a map of property values
+        * @throws DatabaseException
+        */
+       public static Map<String, Map<String, String>> getEnabledTechTypeData(ReadGraph graph, Resource table) throws DatabaseException {
+               return graph.syncRequest(new TechTypeTableData(table, true), TransientCacheListener.instance());
+       }
+       
+       /**
+        * Get a single tech type table item that has the given item code, or null, if not found.
+        * 
+        * The returned map is a subset of the one returned by {@link #getTechTypeData(ReadGraph, Resource)}
+        * with only the items whose index is listed in the HasEnabledItems property.
+        * 
+        * @param graph  A read interface to the graph db
+        * @param table  A tech type table resource
+        * @return  A map of property values or null if not found
+        * @throws DatabaseException
+        */
+       public static Map<String, String> getPossibleTechTypeItem(ReadGraph graph, Resource table, String itemCode) throws DatabaseException {
+               return graph.syncRequest(new PossibleTechTypeItem(table, itemCode), TransientCacheListener.instance());
+       }
 
        /**
         * Compare strings so that contained numbers are sorted in numerical order instead of lexicographic.
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/EnableTechTypeItem.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/EnableTechTypeItem.java
new file mode 100644 (file)
index 0000000..c202325
--- /dev/null
@@ -0,0 +1,64 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Datatypes;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.impl.ArrayListBinding;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.CommonDBUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.service.UndoRedoSupport;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+
+/**
+ * Change the enabled/disabled state of a single tech type table record
+ */
+public class EnableTechTypeItem extends WriteRequest {
+       
+       Resource table;
+       int itemIndex;
+       boolean enable;
+
+       public EnableTechTypeItem(Resource table, int itemIndex, boolean enable) {
+               super();
+               this.table = table;
+               this.itemIndex = itemIndex;
+               this.enable = enable;
+       }
+
+       @Override
+       public void perform(WriteGraph graph) throws DatabaseException {
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+               
+               Binding binding = new ArrayListBinding(Datatypes.INTEGER_ARRAY, Bindings.INTEGER);
+               List<Integer> enabled = graph.getPossibleRelatedValue2(table, DN.TechType_TechTypeTable_HasEnabledItems, binding);
+               if (enabled == null)
+                       enabled = Collections.emptyList();
+               
+               if (enable) {
+                       if (!enabled.contains(itemIndex)) {
+                               enabled = new ArrayList<>(enabled);
+                               enabled.add((Integer) itemIndex);
+                               graph.getSession().markUndoPoint();
+                               graph.claimLiteral(table, DN.TechType_TechTypeTable_HasEnabledItems, enabled, binding);
+                               Layer0Utils.addCommentMetadata(graph, "Enable tech type table item " + (itemIndex + 1));
+                       }
+               } else {
+                       if (enabled.contains(itemIndex)) {
+                               enabled = new ArrayList<>(enabled);
+                               enabled.remove((Integer) itemIndex);  // note - without the cast, itemIndex is list index, not element value
+                               graph.getSession().markUndoPoint();
+                               graph.claimLiteral(table, DN.TechType_TechTypeTable_HasEnabledItems, enabled, binding);
+                               Layer0Utils.addCommentMetadata(graph, "Disable tech type table item " + (itemIndex + 1));
+                       }
+               }
+       }
+
+}
index cbedd1acdf9e47ba3bc7dedef49954326b29b496..60929eddc999c56aad719616ba81bc21f90ade44 100644 (file)
@@ -1,97 +1,75 @@
 package org.simantics.district.network.techtype.requests;
 
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-import org.apache.commons.csv.CSVFormat;
-import org.apache.commons.csv.CSVParser;
-import org.apache.commons.csv.CSVRecord;
+import org.simantics.databoard.Bindings;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
-import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.BinaryRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.network.techtype.TechTypeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class TechTypeTableData extends ResourceRead<Map<String, Map<String, String>>> {
+/**
+ * Get a map of tech type table records, indexed by the key column.
+ *
+ * The query result value is list of records in the form of a map from property name
+ * to string value.
+ * 
+ * The second constructor argument allows selection of enabled records only.
+ *
+ * @author Reino Ruusu
+ */
+public class TechTypeTableData extends BinaryRead<Resource, Boolean, Map<String, Map<String, String>>> {
 
-       private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableData.class);
+       final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableData.class);
        
        public TechTypeTableData(Resource table) {
-               super(table);
+               super(table, Boolean.FALSE);
+       }
+       
+       public TechTypeTableData(Resource table, boolean enabledOnly) {
+               super(table, enabledOnly);
        }
 
        @Override
        public Map<String, Map<String, String>> perform(ReadGraph graph) throws DatabaseException {
-               Resource table = this.resource;
+               Resource table = this.parameter;
+               boolean enabledOnly = this.parameter2;
 
-               String data = graph.getRelatedValue2(table,
-                               DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasData);
-               if (data == null)
-                       return Collections.emptyMap();
+               String keyName = getKeyName(graph, table);
                
-               String keyName = TechTypeUtils.getKeyPropertyName(graph, table);
-               if (keyName.startsWith("_"))
-                       keyName = keyName.substring(1);
-
-               Map<String, Map<String, String>> map = new HashMap<>();
-               
-               long ncommas = data.chars().filter(c -> c == ',').count();
-               long nsemis = data.chars().filter(c -> c == ';').count();
-               char delim = nsemis > ncommas ? ';' : ',';
-               StringReader reader = new StringReader(data);
-
-               List<CSVRecord> records = new ArrayList<>();
-               try {
-                       CSVFormat format = CSVFormat.newFormat(delim).withQuote('"');
-                       try (CSVParser parser = format.parse(reader)) {
-                               Iterator<CSVRecord> it = parser.iterator();
-                               while (it.hasNext()) {
-                                       records.add(it.next());
-                               }
-                       }
-               } catch (IOException e) {
-                       LOGGER.error("Error reading CSV data", e);
+               List<Map<String, String>> records = graph.syncRequest(new TechTypeTableRecords(table), TransientCacheListener.instance());
+               if (records == null || records.size() < 2)
                        return Collections.emptyMap();
-               }
-
-               CSVRecord header = records.remove(0);
-               records.remove(0); // Eliminate units row
                
-               int keyIndex = 0;
-               for (int i = 0; i < header.size(); i++) {
-                       if (header.get(i).equals(keyName)) {
-                               keyIndex = i;
-                               break;
-                       }
-               }
-
-               for (CSVRecord r : records) {
-                       String key = r.get(keyIndex).trim().intern();
-                       if (key == null)
-                               continue;
-                       
-                       Map<String, String> valueMap = new HashMap<>();
-                       map.put(key, valueMap);
-                       
-                       Iterator<String> h = header.iterator();
-                       Iterator<String> v = r.iterator();
-                       
-                       while (h.hasNext() && v.hasNext()) {
-                               String name = h.next().trim().intern();
-                               String value = v.next().trim();
-                               valueMap.put(name, value);
-                       }
+               Stream<Map<String, String>> enabled;
+               if (enabledOnly) {
+                       int[] enabledKeys = graph.getRelatedValue2(table, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasEnabledItems, Bindings.INT_ARRAY);
+                       enabled = Arrays.stream(enabledKeys)
+                                       .filter(i -> i >= 0 && i < records.size())
+                                       .mapToObj(i -> records.get(i));
+               } else {
+                       enabled = records.stream();
                }
                
-               return map;
+               return enabled
+                               .filter(r -> r.containsKey(keyName))
+                               .collect(Collectors.toMap(r -> r.get(keyName), r -> r));
+       }
+
+       private String getKeyName(ReadGraph graph, Resource table) throws DatabaseException {
+               String keyName = TechTypeUtils.getKeyPropertyName(graph, table);
+               if (keyName.startsWith("_"))
+                       keyName = keyName.substring(1);
+               return keyName;
        }
-}
\ No newline at end of file
+}
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableRecords.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableRecords.java
new file mode 100644 (file)
index 0000000..81775b7
--- /dev/null
@@ -0,0 +1,86 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Get a list of the tech type table records in the order of appearance.
+ * 
+ * The query result value is list of records in the form of a map from property name
+ * to string value.
+ * 
+ * @author Reino Ruusu
+ */
+public class TechTypeTableRecords extends ResourceRead<List<Map<String, String>>> {
+       
+       final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableRecords.class);
+       
+       public TechTypeTableRecords(Resource resource) {
+               super(resource);
+       }
+       
+       @Override
+       public List<Map<String, String>> perform(ReadGraph graph) throws DatabaseException {
+               Resource table = this.resource;
+               
+               String data = graph.getRelatedValue2(table,
+                               DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasData);
+               if (data == null)
+                       return null;
+               
+               long ncommas = data.chars().filter(c -> c == ',').count();
+               long nsemis = data.chars().filter(c -> c == ';').count();
+               char delim = nsemis > ncommas ? ';' : ',';
+               StringReader reader = new StringReader(data);
+               
+               List<CSVRecord> records = new ArrayList<>();
+               try {
+                       CSVFormat format = CSVFormat.newFormat(delim).withQuote('"');
+                       try (CSVParser parser = format.parse(reader)) {
+                               Iterator<CSVRecord> it = parser.iterator();
+                               while (it.hasNext()) {
+                                       records.add(it.next());
+                               }
+                       }
+               } catch (IOException e) {
+                       LOGGER.error("Error reading CSV data", e);
+                       throw new DatabaseException("Invalid CSV data in table " + table, e);
+               }
+               
+               CSVRecord header = records.remove(0);
+               records.remove(0); // Eliminate units row
+               
+               List<Map<String, String>> result = new ArrayList<>();
+               for (CSVRecord r : records) {
+                       Map<String, String> valueMap = new HashMap<>();
+                       result.add(valueMap);
+                       
+                       Iterator<String> h = header.iterator();
+                       Iterator<String> v = r.iterator();
+                       
+                       while (h.hasNext() && v.hasNext()) {
+                               String name = h.next().trim().intern();
+                               String value = v.next().trim();
+                               valueMap.put(name, value);
+                       }
+               }
+               
+               return result;
+       }
+}
index 1f85c5a43a039ba1fec5da57090eb86ee4c480b0..500c83e391355c48ef7fc593d714363e6cf89253 100644 (file)
@@ -9,6 +9,7 @@ import org.simantics.db.common.procedure.adapter.TransientCacheListener;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.PossibleActiveModel;
+import org.simantics.db.layer0.util.Layer0Utils;
 import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.layer0.Layer0;
 
@@ -32,12 +33,18 @@ public final class WriteTechTypeTable extends WriteRequest {
 
                Resource ttt = graph.syncRequest(new PossibleTechTypeTable(model, componentType), TransientCacheListener.instance());
 
+               graph.getSession().markUndoPoint();
+               
                if (ttt == null) {
                        ttt = graph.newResource();
                        graph.claim(ttt, L0.InstanceOf, DN.TechType_TechTypeTable);
                        graph.claimLiteral(ttt, L0.HasName, UUID.randomUUID().toString());
                        graph.claim(model, L0.ConsistsOf, ttt);
                        graph.claim(ttt, DN.TechType_TechTypeTable_HasComponentType, componentType);
+                       Layer0Utils.addCommentMetadata(graph, "Create tech type table");
+               } else {
+                       graph.deny(ttt, DN.TechType_TechTypeTable_HasEnabledItems);
+                       Layer0Utils.addCommentMetadata(graph, "Update tech type table");
                }
 
                graph.claimLiteral(ttt, DN.TechType_TechTypeTable_HasData, data);