]> gerrit.simantics Code Review - simantics/district.git/commitdiff
TechTypeTable features 45/4445/1
authorjsimomaa <jani.simomaa@gmail.com>
Wed, 12 Aug 2020 14:35:56 +0000 (17:35 +0300)
committerReino Ruusu <reino.ruusu@semantum.fi>
Thu, 10 Sep 2020 19:47:53 +0000 (22:47 +0300)
Squashed cherry pick of change ids
* I552cfac33369bf960342836428bc005ed3b1505f
* I24f2b651176a7b9aa8b8871cf8e49473350d9193
* I37e9db42c3e7b4a95ba12bed87107cddccd66bc1
* Iee2cae3751983d059216fea7ec11fd47a9e9dda4
* Iba22b4ea4db7a772664e2b76ab44a88a70ea8e80
* I7f7dd9aae0d90b00250ecf42e01ce566e57544d0
* I8550ac1edb7d650211c51ce6379487788001569c
* Ia5ac3f513adfd57d4a2e499c9a7dd4ef77355e9f

gitlab #93

Change-Id: I33967fb28b9ec41364d68787c359d6c363089b65

27 files changed:
org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java
org.simantics.district.network.ontology/graph/DistrictNetworkTechType.pgraph [new file with mode: 0644]
org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java
org.simantics.district.network.ui/fragment.e4xmi
org.simantics.district.network.ui/plugin.xml
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/adapters/TechTypeEditorAdapter.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java [new file with mode: 0644]
org.simantics.district.network/META-INF/MANIFEST.MF
org.simantics.district.network/build.properties
org.simantics.district.network/plugin.xml [new file with mode: 0644]
org.simantics.district.network/scl/Simantics/District/TechType.scl [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java
org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeUtils.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeItem.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTable.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTableData.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableData.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableKeyName.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/requests/WriteTechTypeTable.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/techtype/variable/Functions.java [new file with mode: 0644]

index 836ebd02dd758e609ec4e12341acc348722ee720..1ff69279083ee23675a2d92731a45a752923880e 100644 (file)
@@ -1,6 +1,8 @@
 package org.simantics.district.imports;
 
 import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -64,7 +66,7 @@ public class DistrictImportUtils {
     public static Map<String, Integer> readCSVHeader(Path source, CSVFormat format, boolean firstAsHeader) throws IOException {
         if (firstAsHeader)
             format = format.withFirstRecordAsHeader();
-        try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
+        try (CSVParser parser = format.parse(Files.newBufferedReader(source, Charset.defaultCharset()))) {
             return parser.getHeaderMap();
         }
     }
@@ -105,11 +107,16 @@ public class DistrictImportUtils {
     }
     
     public static void consumeCSV(Path source, char delim, boolean firstAsHeader, Function<CSVRecord, Boolean> consumer) throws IOException {
-        CSVFormat format = CSVFormat.newFormat(delim);
+        consumeCSV(Files.newBufferedReader(source), delim, firstAsHeader, consumer);
+    }
+
+    public static void consumeCSV(Reader reader, char delim, boolean firstAsHeader,
+            Function<CSVRecord, Boolean> consumer) throws IOException {
+        CSVFormat format = CSVFormat.newFormat(delim).withQuote('"');
         if (firstAsHeader) {
             format = format.withFirstRecordAsHeader();
         }
-        try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
+        try (CSVParser parser = format.parse(reader)) {
             Iterator<CSVRecord> records = parser.iterator();
             while (records.hasNext()) {
                 Boolean cont = consumer.apply(records.next());
diff --git a/org.simantics.district.network.ontology/graph/DistrictNetworkTechType.pgraph b/org.simantics.district.network.ontology/graph/DistrictNetworkTechType.pgraph
new file mode 100644 (file)
index 0000000..96865b3
--- /dev/null
@@ -0,0 +1,23 @@
+L0 = <http://www.simantics.org/Layer0-1.1>
+STR = <http://www.simantics.org/Structural-1.2>
+SEL = <http://www.simantics.org/SelectionView-1.2>
+
+DN = <http://www.simantics.org/DistrictNetwork-1.0>
+
+TT = DN.TechType : L0.Library
+
+table = TT.TechTypeTable <T L0.Entity
+    // Table data as a CSV string literal
+    >-- table.HasData <R L0.HasProperty : L0.TotalFunction
+        --> L0.String
+    
+    // Link to corresponding component type
+    >-- table.HasComponentType <R L0.IsRelatedTo : L0.FunctionalRelation
+        --> STR.ComponentType
+
+TT.Functions : L0.Library
+TT.Functions.techTypeCodeValueAccessor : L0.ExternalValue
+  L0.HasValueType "ValueAccessor"
+
+TT.TechTypeCodeParameterType : SEL.GenericParameterType
+    @L0.assert L0.valueAccessor TT.Functions.techTypeCodeValueAccessor
index d12c2755c11540b2df84e452f035762276de01cb..1b4d4e245637435aa3345313dc9fa6fd319578d8 100644 (file)
@@ -376,6 +376,14 @@ public class DistrictNetworkResource {
     public final Resource SupplyConnectionType;
     public final Resource SupplyInConnectionType;
     public final Resource SupplyOutConnectionType;
+    public final Resource TechType;
+    public final Resource TechType_Functions;
+    public final Resource TechType_Functions_techTypeCodeValueAccessor;
+    public final Resource TechType_TechTypeCodeParameterType;
+    public final Resource TechType_TechTypeTable;
+    public final Resource TechType_TechTypeTable_HasComponentType;
+    public final Resource TechType_TechTypeTable_HasData;
+    public final Resource TechType_TechTypeTable_HasData_Inverse;
     public final Resource Vertex;
     public final Resource VertexDefaultMapping;
     public final Resource VertexDefaultMapping_Inverse;
@@ -828,6 +836,14 @@ public class DistrictNetworkResource {
         public static final String SupplyConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyConnectionType";
         public static final String SupplyInConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyInConnectionType";
         public static final String SupplyOutConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyOutConnectionType";
+        public static final String TechType = "http://www.simantics.org/DistrictNetwork-1.0/TechType";
+        public static final String TechType_Functions = "http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions";
+        public static final String TechType_Functions_techTypeCodeValueAccessor = "http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions/techTypeCodeValueAccessor";
+        public static final String TechType_TechTypeCodeParameterType = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeCodeParameterType";
+        public static final String TechType_TechTypeTable = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable";
+        public static final String TechType_TechTypeTable_HasComponentType = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable/HasComponentType";
+        public static final String TechType_TechTypeTable_HasData = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable/HasData";
+        public static final String TechType_TechTypeTable_HasData_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable/HasData/Inverse";
         public static final String Vertex = "http://www.simantics.org/DistrictNetwork-1.0/Vertex";
         public static final String VertexDefaultMapping = "http://www.simantics.org/DistrictNetwork-1.0/VertexDefaultMapping";
         public static final String VertexDefaultMapping_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/VertexDefaultMapping/Inverse";
@@ -1290,6 +1306,14 @@ public class DistrictNetworkResource {
         SupplyConnectionType = getResourceOrNull(graph, URIs.SupplyConnectionType);
         SupplyInConnectionType = getResourceOrNull(graph, URIs.SupplyInConnectionType);
         SupplyOutConnectionType = getResourceOrNull(graph, URIs.SupplyOutConnectionType);
+        TechType = getResourceOrNull(graph, URIs.TechType);
+        TechType_Functions = getResourceOrNull(graph, URIs.TechType_Functions);
+        TechType_Functions_techTypeCodeValueAccessor = getResourceOrNull(graph, URIs.TechType_Functions_techTypeCodeValueAccessor);
+        TechType_TechTypeCodeParameterType = getResourceOrNull(graph, URIs.TechType_TechTypeCodeParameterType);
+        TechType_TechTypeTable = getResourceOrNull(graph, URIs.TechType_TechTypeTable);
+        TechType_TechTypeTable_HasComponentType = getResourceOrNull(graph, URIs.TechType_TechTypeTable_HasComponentType);
+        TechType_TechTypeTable_HasData = getResourceOrNull(graph, URIs.TechType_TechTypeTable_HasData);
+        TechType_TechTypeTable_HasData_Inverse = getResourceOrNull(graph, URIs.TechType_TechTypeTable_HasData_Inverse);
         Vertex = getResourceOrNull(graph, URIs.Vertex);
         VertexDefaultMapping = getResourceOrNull(graph, URIs.VertexDefaultMapping);
         VertexDefaultMapping_Inverse = getResourceOrNull(graph, URIs.VertexDefaultMapping_Inverse);
index c199a95928557e5b505a47ed9ed76bc79ca0e153..89afdc5d9435258dd03c497252f391a7527069c9 100644 (file)
@@ -16,7 +16,7 @@
     <elements xsi:type="commands:Command" xmi:id="_CLKOUHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.command.changeroutepointtovertex" commandName="Change Route Point to Vertex"/>
     <elements xsi:type="commands:Command" xmi:id="_Ee0TAHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.command.changevertextoroutepoint" commandName="Change Vertex to Route Point"/>
     <elements xsi:type="commands:Command" xmi:id="_QcEKQIAGEemKlokjSaREFQ" elementId="org.simantics.district.network.ui.command.importcsv" commandName="Import CSV"/>
-    <!--<elements xsi:type="commands:Command" xmi:id="_VWtY8LqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.command.importtechtypecsv" commandName="Import Tech Type CSV"/>-->
+    <elements xsi:type="commands:Command" xmi:id="_VWtY8LqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.command.importtechtypecsv" commandName="Import Tech Type CSV"/>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_Js7rUMMAEea1mdgpHNVHMA" featurename="menuContributions" parentElementId="xpath:/">
     <elements xsi:type="menu:MenuContribution" xmi:id="_T-jiEN8NEeaigNyzMJBOrg" elementId="org.simantics.district.network.ui.menucontribution.districtDiagramPopup" parentId="#DistrictDiagramPopup">
@@ -49,6 +49,6 @@
     <elements xsi:type="commands:Handler" xmi:id="_F9vvIHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handler.7" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ChangeRoutePointToVertexHandler" command="_CLKOUHVvEemS_rRAHnCQSw"/>
     <elements xsi:type="commands:Handler" xmi:id="_H7TA8HVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handler.8" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ChangeVertexToRoutePointHandler" command="_Ee0TAHVvEemS_rRAHnCQSw"/>
     <elements xsi:type="commands:Handler" xmi:id="_akE8EIAGEemKlokjSaREFQ" elementId="org.simantics.district.network.ui.handler.9" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.table.ImportCSVHandler" command="_QcEKQIAGEemKlokjSaREFQ"/>
-    <!--<elements xsi:type="commands:Handler" xmi:id="_XjbIILqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.handler.10" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.table.ImportTechTypeCSVHandler" command="_VWtY8LqWEemcscVaZzEyWw"/>-->
+    <elements xsi:type="commands:Handler" xmi:id="_XjbIILqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.handler.10" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.table.ImportTechTypeCSVHandler" command="_VWtY8LqWEemcscVaZzEyWw"/>
   </fragments>
 </fragment:ModelFragments>
index cee0a77f6b24ecd5a7b14b9d5da3bc5f71feb02d..3b8bf45c07df28c51deca62bf2f5b77040bd1edd 100644 (file)
             priority="300"
             class="org.simantics.district.network.ui.OpenDiagramFromNetworkElementAdapter">
       </adapterClass>
+      <adapterClass
+            class="org.simantics.district.network.ui.techtype.adapters.TechTypeEditorAdapter"
+            priority="100">
+      </adapterClass>
    </extension>
    <extension
          point="org.simantics.scl.reflection.binding">
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java
new file mode 100644 (file)
index 0000000..4f833fc
--- /dev/null
@@ -0,0 +1,54 @@
+package org.simantics.district.network.ui.table;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.district.imports.DistrictImportUtils;
+import org.simantics.district.network.ui.techtype.table.TechTypeTableView;
+import org.simantics.utils.ui.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ImportTechTypeCSVHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ImportTechTypeCSVHandler.class);
+
+    @Inject
+    EPartService partService;
+
+    @Execute
+    public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell s) {
+        // here we can import based on the current CSV table data
+
+        FileDialog dialog = new FileDialog(s);
+        String path = dialog.open();
+        try {
+            if (path != null) {
+
+                Path p = Paths.get(path);
+                if (Files.exists(p)) {
+                    @SuppressWarnings("unused")
+                    Map<String, Integer> readCSVHeader = DistrictImportUtils.readCSVHeader(p, ';', true);
+                    TechTypeTableView.table.setTechTypePath(path);
+                } else {
+                    LOGGER.warn("Path does not exist even though path != null: {}", p);
+                }
+            } else {
+                LOGGER.error("Invalid file path given {}", path);
+            }
+        } catch (Exception e) {
+            LOGGER.error("Could not read file {}", path, e);
+            ExceptionUtils.logAndShowError("Could not read file " + path + " : " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/adapters/TechTypeEditorAdapter.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/adapters/TechTypeEditorAdapter.java
new file mode 100644 (file)
index 0000000..5ebe6ed
--- /dev/null
@@ -0,0 +1,79 @@
+package org.simantics.district.network.ui.techtype.adapters;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.PlatformUI;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.ui.techtype.table.TechTypeTableView;
+import org.simantics.ui.utils.ResourceAdaptionUtils;
+import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
+import org.simantics.ui.workbench.editor.EditorAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Reino Ruusu
+ */
+public class TechTypeEditorAdapter extends AbstractResourceEditorAdapter implements EditorAdapter {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(TechTypeEditorAdapter.class);
+
+       public TechTypeEditorAdapter() throws MalformedURLException, IOException {
+               super("Tech Type Table View");
+               setIcon(ImageDescriptor.createFromURL(FileLocator.resolve(new URL("platform:/plugin/com.famfamfam.silk/icons/table.png")))); //$NON-NLS-1$
+       }
+
+       @Override
+       public boolean canHandle(ReadGraph graph, Object input) throws DatabaseException {
+               Resource r = ResourceAdaptionUtils.toSingleResource(input);
+               if (r == null)
+                       return false;
+               return graph.isInstanceOf(r, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable);
+       }
+
+       @Override
+       public void openEditor(Resource table) throws Exception {
+               IEclipseContext eclipseContext = PlatformUI.getWorkbench().getService(IEclipseContext.class);
+               if (eclipseContext == null) {
+                       LOGGER.error("No Eclipse context available");
+                       return;
+               }
+
+               eclipseContext = eclipseContext.getActiveLeaf();
+               EPartService partService = eclipseContext.get(EPartService.class);
+               if (partService == null)
+                       return;
+
+               partService.showPart(TechTypeTableView.ID, PartState.ACTIVATE);
+
+               try {
+                       Simantics.getSession().syncRequest(new ReadRequest() {
+                               @Override
+                               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);
+                                       });
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read tech type table {}", table, e);
+               }
+       }
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java
new file mode 100644 (file)
index 0000000..9b63219
--- /dev/null
@@ -0,0 +1,33 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+
+public class TechTypeColumnHeaderTableDataProvider implements IDataProvider {
+
+       private TechTypeTableDataProvider bodyDataProvider;
+
+    public TechTypeColumnHeaderTableDataProvider(TechTypeTableDataProvider bodyDataProvider) {
+           this.bodyDataProvider = bodyDataProvider;
+    }
+
+    @Override
+       public Object getDataValue(int columnIndex, int rowIndex) {
+               return bodyDataProvider.getHeaderValue(columnIndex);
+       }
+
+       @Override
+       public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+               
+       }
+
+       @Override
+       public int getColumnCount() {
+               return bodyDataProvider.getColumnCount();
+       }
+
+       @Override
+       public int getRowCount() {
+               return 1;
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java
new file mode 100644 (file)
index 0000000..4d21bd5
--- /dev/null
@@ -0,0 +1,33 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+
+public class TechTypeRowHeaderTableDataProvider implements IDataProvider {
+
+    protected final IDataProvider bodyDataProvider;
+
+    public TechTypeRowHeaderTableDataProvider(IDataProvider bodyDataProvider) {
+        this.bodyDataProvider = bodyDataProvider;
+    }
+
+    @Override
+    public int getColumnCount() {
+        return 1;
+    }
+
+    @Override
+    public int getRowCount() {
+        return this.bodyDataProvider.getRowCount();
+    }
+
+    @Override
+    public Object getDataValue(int columnIndex, int rowIndex) {
+        return Integer.valueOf(rowIndex + 1);
+    }
+
+    @Override
+    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java
new file mode 100644 (file)
index 0000000..0c44bff
--- /dev/null
@@ -0,0 +1,224 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.stream.Collectors;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer;
+import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
+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.reorder.RowReorderLayer;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.events.ModifyEvent;
+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.Resource;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.techtype.requests.WriteTechTypeTable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeTable extends Composite {
+
+       private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTable.class);
+
+       NatTable table;
+       private TechTypeTableDataProvider bodyDataProvider;
+       DataLayer bodyDataLayer;
+       private IConfigRegistry summaryConfigRegistry;
+       private IUniqueIndexLayer summaryRowLayer;
+       private ViewportLayer viewportLayer;
+       private CompositeFreezeLayer compositeFreezeLayer;
+       private FreezeLayer freezeLayer;
+       // private TableDataSortModel sortModel;
+       private ColumnHideShowLayer columnHideShowLayer;
+       private ColumnGroupModel columnGroupModel = new ColumnGroupModel();
+       private TechTypeColumnHeaderTableDataProvider columnHeaderDataProvider;
+       Clipboard cpb;
+       public SelectionLayer selectionLayer;
+       private TechTypeTableSortModel sortModel;
+
+       private Resource componentType;
+
+       public TechTypeTable(Composite parent, int style, Resource componentType, String data) {
+               super(parent, style);
+               this.componentType = componentType;
+
+               defaultInitializeUI(data);
+       }
+
+       private void defaultInitializeUI(String data) {
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
+               GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
+
+               Composite filterComposite = new Composite(this, SWT.NONE);
+               GridDataFactory.fillDefaults().grab(true, false).applyTo(filterComposite);
+               GridLayoutFactory.fillDefaults().numColumns(1).applyTo(filterComposite);
+
+               createFilterBar(filterComposite);
+
+               Composite tableComposite = new Composite(this, SWT.NONE);
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
+               GridLayoutFactory.fillDefaults().numColumns(1).applyTo(tableComposite);
+
+               createTable(tableComposite, data);
+       }
+
+       private void createFilterBar(Composite filterComposite) {
+
+               Text filterText = new Text(filterComposite, SWT.BORDER);
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(filterText);
+               filterText.addModifyListener(new ModifyListener() {
+
+                       @Override
+                       public void modifyText(ModifyEvent e) {
+                               System.out.println("text modified");
+                               bodyDataProvider.setFilter(filterText.getText());
+                               table.refresh(true);
+                       }
+               });
+
+       }
+
+       private void createTable(Composite parent, String data) {
+
+               // 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);
+               bodyDataLayer = new DataLayer(bodyDataProvider);
+
+               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);
+
+               viewportLayer = new ViewportLayer(selectionLayer);
+               viewportLayer.setRegionName(GridRegion.BODY);
+               freezeLayer = new FreezeLayer(selectionLayer);
+               compositeFreezeLayer = new CompositeFreezeLayer(freezeLayer, viewportLayer, selectionLayer);
+
+               // build the column header layer
+               columnHeaderDataProvider = new TechTypeColumnHeaderTableDataProvider(bodyDataProvider);
+               DataLayer columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
+               columnHeaderDataLayer.setRowsResizableByDefault(false);
+               columnHeaderDataLayer.setColumnsResizableByDefault(true);
+               ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, compositeFreezeLayer,
+                               selectionLayer);
+               ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer(columnHeaderLayer, selectionLayer,
+                               columnGroupModel);
+               columnGroupHeaderLayer.setCalculateHeight(true);
+               SortHeaderLayer<String> columnSortHeaderLayer = new SortHeaderLayer<>(columnGroupHeaderLayer,
+                               sortModel = new TechTypeTableSortModel(bodyDataProvider));
+
+               // build the row header layer
+               IDataProvider rowHeaderDataProvider = new TechTypeRowHeaderTableDataProvider(bodyDataProvider);
+               DataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
+               rowHeaderDataLayer.setRowsResizableByDefault(false);
+               rowHeaderDataLayer.setColumnsResizableByDefault(false);
+               RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, compositeFreezeLayer, selectionLayer);
+
+               // build the corner layer
+               IDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider,
+                               rowHeaderDataProvider);
+               DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
+               ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnSortHeaderLayer);
+
+               // build the grid layer
+               GridLayer gridLayer = new GridLayer(compositeFreezeLayer, columnSortHeaderLayer, rowHeaderLayer, cornerLayer);
+
+               table = new NatTable(parent, NatTable.DEFAULT_STYLE_OPTIONS | SWT.BORDER, gridLayer, false);
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(table);
+
+               // Register a CopyDataCommandHandler that also copies the headers and
+               // uses the configured IDisplayConverters
+               CopyDataCommandHandler copyHandler = new CopyDataCommandHandler(selectionLayer, columnHeaderDataLayer,
+                               rowHeaderDataLayer);
+               copyHandler.setCopyFormattedText(true);
+               gridLayer.registerCommandHandler(copyHandler);
+
+               // initialize paste handler with SWT clipboard
+               cpb = new Clipboard(getDisplay());
+               // PasteDataCommandHandler pasteHandler = new
+               // PasteDataCommandHandler(bodyDataProvider, bodyDataLayer, selectionLayer,
+               // cpb);
+               // bodyDataLayer.registerCommandHandler(pasteHandler);
+
+               table.addConfiguration(new DefaultNatTableStyleConfiguration());
+               // table.addConfiguration(new EditingSupportConfiguration(bodyDataProvider));
+               table.configure();
+       }
+
+       @Override
+       public void dispose() {
+               cpb.dispose();
+               super.dispose();
+       }
+
+       public void setTechTypePath(String path) {
+               String data;
+               try {
+                       data = Files.lines(Paths.get(path), Charset.defaultCharset()).collect(Collectors.joining("\n"));
+               } catch (IOException e) {
+                       LOGGER.error("Failed to read contents of file '{}' as {}", path, Charset.defaultCharset(), e);
+                       return;
+               }
+
+               try {
+                       Simantics.getSession().syncRequest(new WriteTechTypeTable(componentType, data));
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to write tech type table data to model", e);
+               }
+
+               setTechTypeData(data);
+       }
+
+       public void setTechTypeData(String data) {
+               bodyDataProvider.setData(data);
+               table.refresh(true);
+       }
+
+       public void setComponentType(Resource componentType) {
+               this.componentType = componentType;
+       }
+
+}
+
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java
new file mode 100644 (file)
index 0000000..850cc39
--- /dev/null
@@ -0,0 +1,161 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.csv.CSVRecord;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.simantics.district.imports.DistrictImportUtils;
+import org.slf4j.Logger;
+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 List<String> variables = null;
+    private List<String> headers = null;
+
+    public TechTypeTableDataProvider(String data) {
+        // load csv
+        setData(data);
+    }
+
+    public String getVariableName(int columnIndex) {
+        return variables != null && columnIndex < variables.size() ? variables.get(columnIndex) : null;
+    }
+
+    public int getVariableIndex(String variableName) {
+        return variables != null ? variables.indexOf(variableName) : -1;
+    }
+
+    public String getHeaderValue(int columnIndex) {
+        if (headers == null) {
+            return "<empty>";
+        }
+        return headers.get(columnIndex);
+    }
+
+    @Override
+    public Object getDataValue(int columnIndex, int rowIndex) {
+        return filteredRecords.get(rowIndex).get(columnIndex);
+    }
+
+    @Override
+    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+
+    }
+
+    @Override
+    public int getColumnCount() {
+        if (records.isEmpty()) {
+            return 0;
+        }
+        return records.get(0).size();
+    }
+
+    @Override
+    public int getRowCount() {
+        return filteredRecords.size() - 1;
+    }
+
+    public boolean isEditable(int columnIndex, int rowIndex) {
+        return false;
+    }
+
+    public void setFilter(String text) {
+        this.filter = text.toLowerCase();
+
+        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;
+                }
+            }
+            return false;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * Read a CSV file into table contents.
+     * 
+     * Set path to null to create an empty table.
+     * 
+     * @param path  The path of the CSV file to be read.
+     */
+    public void setPath(String path) {
+        records.clear();
+        filteredRecords.clear();
+        if (path != null) {
+            Path techTypeCsv = Paths.get(path);
+            try {
+                DistrictImportUtils.consumeCSV(techTypeCsv, ';', false, record -> {
+                    records.add(record);
+                    return true;
+                });
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        setFilter("");
+    }
+
+    /**
+     * Set table data contents to a given string of CSV data.
+     * 
+     * Set 'data' to null to create an empty table.
+     * 
+     * @param data  The CSV data to be shown in the table.
+     */
+    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();
+            char delim = nsemis > ncommas ? ';' : ',';
+            StringReader reader = new StringReader(data);
+            try {
+                DistrictImportUtils.consumeCSV(reader, delim, false, record -> {
+                    records.add(record);
+                    return true;
+                });
+            } catch (IOException e) {
+                LOGGER.error("Error reading CSV file", e);
+                return;
+            }
+
+            CSVRecord header = records.remove(0);
+            CSVRecord units = records.remove(0);
+
+            variables = new ArrayList<>();
+            headers = new ArrayList<>();
+
+            Iterator<String> it = header.iterator();
+            Iterator<String> uit = units.iterator();
+
+            while (it.hasNext()) {
+                String variable = it.next().trim();
+                String unit = uit.hasNext() ? uit.next().trim() : null;
+
+                variables.add(variable);
+                headers.add(variable + (unit != null && !unit.isEmpty() && !(unit.startsWith("(") && unit.endsWith(")")) ? " [" + unit + "]" : ""));
+            }
+        }
+
+        setFilter("");
+    }
+
+}
\ No newline at end of file
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java
new file mode 100644 (file)
index 0000000..f6a02f4
--- /dev/null
@@ -0,0 +1,127 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.nebula.widgets.nattable.sort.ISortModel;
+import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum;
+import org.simantics.utils.strings.AlphanumComparator;
+
+public class TechTypeTableSortModel implements ISortModel  {
+
+    private static final SortDirectionEnum[] NO_DIRECTIONS = {};
+    private static final boolean[] NO_BOOLEANS = {};
+
+    /**
+     * Array that contains the sort direction for every column.
+     * Needed to access the current sort state of a column.
+     */
+    protected SortDirectionEnum[] sortDirections = NO_DIRECTIONS;
+
+    /**
+     * Array that contains the sorted flags for every column.
+     * Needed to access the current sort state of a column.
+     */
+    protected boolean[] sorted = NO_BOOLEANS;
+
+    /**
+     * As this implementation only supports single column sorting,
+     * this property contains the the column index of the column that
+     * is currently used for sorting.
+     * Initial value = -1 for no sort column
+     */
+    protected int currentSortColumn = -1;
+
+    /**
+     * As this implementation only supports single column sorting,
+     * this property contains the current sort direction of the column that
+     * is currently used for sorting.
+     */
+    protected SortDirectionEnum currentSortDirection = SortDirectionEnum.ASC;
+    
+    private TechTypeTableDataProvider bodyDataProvider;
+
+    public TechTypeTableSortModel(TechTypeTableDataProvider bodyDataProvider) {
+        this.bodyDataProvider = bodyDataProvider;
+    }
+    
+    
+    @Override
+    public List<Integer> getSortedColumnIndexes() {
+        List<Integer> indexes = new ArrayList<Integer>();
+        if (currentSortColumn > -1) {
+            indexes.add(Integer.valueOf(currentSortColumn));
+        }
+        return indexes;
+    }
+
+    @Override
+    public boolean isColumnIndexSorted(int columnIndex) {
+        if (sorted.length <= columnIndex)
+            return false;
+        return sorted[columnIndex];
+    }
+
+    @Override
+    public SortDirectionEnum getSortDirection(int columnIndex) {
+        if (sortDirections.length <= columnIndex)
+            return SortDirectionEnum.NONE;
+        return sortDirections[columnIndex];
+    }
+
+    @Override
+    public int getSortOrder(int columnIndex) {
+        return 0;
+    }
+
+    @Override
+    public List<Comparator> getComparatorsForColumnIndex(int columnIndex) {
+        return Collections.singletonList(AlphanumComparator.COMPARATOR);
+    }
+
+    @Override
+    public Comparator<?> getColumnComparator(int columnIndex) {
+        return AlphanumComparator.COMPARATOR;
+    }
+
+    @Override
+    public void sort(int columnIndex, SortDirectionEnum sortDirection, boolean accumulate) {
+        
+        if (!isColumnIndexSorted(columnIndex)) {
+            clear();
+        }
+        int columnCount = bodyDataProvider.getColumnCount();
+        sortDirections = ensureArraySize(sortDirections, columnCount, SortDirectionEnum.class, SortDirectionEnum.NONE);
+        
+        
+        sortDirections[columnIndex] = sortDirection;
+        sorted[columnIndex] = !sortDirection.equals(SortDirectionEnum.NONE);
+        currentSortColumn = columnIndex;
+        currentSortDirection = sortDirection;
+    }
+
+    @Override
+    public void clear() {
+        Arrays.fill(this.sortDirections, SortDirectionEnum.NONE);
+        Arrays.fill(this.sorted, false);
+        this.currentSortColumn = -1;
+        this.currentSortDirection = SortDirectionEnum.NONE;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T[] ensureArraySize(T[] array, int l, Class<T> clazz, T defaultValue) {
+        boolean fill = true;
+        if (array == null || array.length != l) {
+            array = (T[]) Array.newInstance(clazz, l);
+            fill = defaultValue != null;
+        }
+        if (fill)
+            Arrays.fill(array, defaultValue);
+        return array;
+    }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java
new file mode 100644 (file)
index 0000000..7cfa44d
--- /dev/null
@@ -0,0 +1,11 @@
+package org.simantics.district.network.ui.techtype.table;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class TechTypeTableUI extends Composite {
+
+       public TechTypeTableUI(Composite parent, int style) {
+               super(parent, style);
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java
new file mode 100644 (file)
index 0000000..15b3178
--- /dev/null
@@ -0,0 +1,96 @@
+package org.simantics.district.network.ui.techtype.table;
+
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.commands.MCommand;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem;
+import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
+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.db.layer0.request.PossibleResource;
+import org.simantics.district.network.DistrictNetworkUtil;
+import org.simantics.district.network.techtype.requests.PossibleTechTypeTableData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeTableView {
+
+       private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableView.class);
+
+       public static final String ID = "org.simantics.district.network.ui.techtype.table.techtypeTableView";
+
+       @Inject ESelectionService selectionService;
+
+       public static TechTypeTable table;
+
+       @Inject
+       public void init(MPart part, MApplication app) {
+               MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
+               toolBar.setToBeRendered(true);
+               toolBar.getChildren().add(createImportCSVDataToolItem(app));
+               part.setToolbar(toolBar);
+       }
+
+       private MHandledToolItem createImportCSVDataToolItem(MApplication app) {
+               MHandledToolItem createHandledToolItem = MMenuFactory.INSTANCE.createHandledToolItem();
+               // Command is contributed via fragment
+               MCommand command = app.getCommand("org.simantics.district.network.ui.command.importtechtypecsv");
+               createHandledToolItem.setCommand(command); //$NON-NLS-1$
+               createHandledToolItem.setLabel("Import Tech Type");
+               createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_edit.png"); //$NON-NLS-1$
+               return createHandledToolItem;
+       }
+
+       @PostConstruct
+       public void postConstruct(Composite parent) {
+               Resource pipe = null;
+               try {
+                       List<NamedResource> componentTypes = DistrictNetworkUtil.getDistrictComponents();
+
+                       pipe = componentTypes.stream()
+                                       .filter(r -> r.getName().toLowerCase().contains("pipe"))
+                                       .map(r -> r.getResource())
+                                       .findFirst().orElse(null);
+
+                       if (pipe == null) {
+                               pipe = Simantics.getSession().syncRequest(new PossibleResource("http://DistrictComponents@C/dh_pipe@1"));
+                       }
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read district component types for active model", e);
+               }
+
+               LOGGER.debug("Pipe component type is {}", pipe);
+
+               String data = null;
+               try {
+                       Resource model = Simantics.getSession().syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
+                       if (model != null)
+                               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, data);
+       }
+
+       @PreDestroy
+       public void dispose() {
+               table.dispose();
+               table = null;
+       }
+
+}
index 79671648d82b43a460d629040d46cb63526a7222..022a0cfabbf5d7ad21e533ea51d1e84a6f4a1ecc 100644 (file)
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: Simantics District Network
-Bundle-SymbolicName: org.simantics.district.network
+Bundle-SymbolicName: org.simantics.district.network;singleton:=true
 Bundle-Version: 1.0.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Require-Bundle: org.simantics.db,
@@ -22,10 +22,14 @@ Require-Bundle: org.simantics.db,
  org.simantics.scl.osgi,
  org.eclipse.collections.eclipse-collections,
  org.eclipse.collections.eclipse-collections-api,
- org.simantics.structural.synchronization.client
+ org.simantics.structural.synchronization.client,
+ org.apache.commons.csv
 Export-Package: org.simantics.district.network,
  org.simantics.district.network.changeset,
  org.simantics.district.network.profile,
+ org.simantics.district.network.techtype,
+ org.simantics.district.network.techtype.requests,
+ org.simantics.district.network.techtype.variable,
  org.simantics.district.network.visualisations,
  org.simantics.district.network.visualisations.model,
  org.simantics.district.network.visualisations.triggers
index 21a6c989ec06e97251b51ed84de4cc61ec873c18..1ae7e4e954eb5fa589f9adccfbd35f34191634cc 100644 (file)
@@ -3,4 +3,5 @@ output.. = bin/
 bin.includes = META-INF/,\
                .,\
                adapters.xml,\
-               scl/
+               scl/,\
+               plugin.xml
diff --git a/org.simantics.district.network/plugin.xml b/org.simantics.district.network/plugin.xml
new file mode 100644 (file)
index 0000000..b8e40a3
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.simantics.scl.reflection.binding">
+      <namespace path="http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions">
+         <externalClass className="org.simantics.db.layer0.variable.ValueAccessor"/>
+         <class className="org.simantics.district.network.techtype.variable.Functions"/>
+      </namespace>
+   </extension>
+
+</plugin>
diff --git a/org.simantics.district.network/scl/Simantics/District/TechType.scl b/org.simantics.district.network/scl/Simantics/District/TechType.scl
new file mode 100644 (file)
index 0000000..9b6ddeb
--- /dev/null
@@ -0,0 +1,7 @@
+import "Simantics/DB"
+
+importJava "org.simantics.district.network.techtype.TechTypeUtils" where
+    "Update component properties from tech type table: `updateComponent component`"
+    updateComponent :: Resource -> <Proc> ()
+    "Reset all components to values in a tech type table: `resetComponents table`"
+    resetComponents :: Resource -> <Proc> ()
index 2bb29660930b82c9a30e010a118538fe73230c96..85ebd06c488ba27a1c4912dbd3eb40437a57eab1 100644 (file)
@@ -3,8 +3,12 @@ package org.simantics.district.network;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -13,12 +17,16 @@ import org.simantics.databoard.Bindings;
 import org.simantics.datatypes.literal.RGB;
 import org.simantics.datatypes.literal.RGB.Integer;
 import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
 import org.simantics.db.Resource;
 import org.simantics.db.WriteGraph;
+import org.simantics.db.common.NamedResource;
 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
 import org.simantics.db.common.request.BinaryRead;
 import org.simantics.db.common.request.IndexRoot;
 import org.simantics.db.common.request.ObjectsWithType;
+import org.simantics.db.common.request.PossibleChild;
+import org.simantics.db.common.request.PossibleIndexRoot;
 import org.simantics.db.common.request.ResourceRead;
 import org.simantics.db.common.utils.OrderedSetUtils;
 import org.simantics.db.exception.BindingException;
@@ -26,6 +34,8 @@ import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
 import org.simantics.db.exception.ServiceException;
 import org.simantics.db.indexing.IndexUtils;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.PossibleActiveModel;
 import org.simantics.db.layer0.request.PossibleVariable;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.diagram.stubs.DiagramResource;
@@ -466,4 +476,53 @@ public class DistrictNetworkUtil {
         }
     }
 
+    public static class DistrictComponentListRequest extends ResourceRead<List<NamedResource>> {
+        protected DistrictComponentListRequest(Resource model) {
+            super(model);
+        }
+
+        @Override
+        public List<NamedResource> perform(ReadGraph graph) throws DatabaseException {
+            Layer0 L0 = Layer0.getInstance(graph);
+            DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+            
+            Resource model = this.resource;
+            
+            Set<Resource> componentTypes = new HashSet<>();
+            Collection<Resource> mappings = QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
+            for (Resource r : mappings) {
+                String componentType = graph.getPossibleRelatedValue2(r, DN.Mapping_ComponentType);
+                if (componentType != null) {
+                    Resource root = graph.syncRequest(new PossibleIndexRoot(r));
+                    if (root != null) {
+                        Resource type = graph.syncRequest(new PossibleChild(root, componentType));
+                        if (type != null)
+                            componentTypes.add(type);
+                    }
+                }
+            }
+            
+            List<NamedResource> result = new ArrayList<NamedResource>(componentTypes.size());
+            for (Resource r : componentTypes) {
+                String name = graph.getPossibleRelatedValue(r, L0.HasName);
+                result.add(new NamedResource(name, r));
+            }
+            
+            result.sort(Comparator.comparing(NamedResource::getName));
+            return result;
+        }
+    }
+    
+    public static List<NamedResource> getDistrictComponents() throws DatabaseException {
+        return getDistrictComponents(Simantics.getSession());
+    }
+
+    public static List<NamedResource> getDistrictComponents(RequestProcessor session) throws DatabaseException {
+        Resource model = session.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
+        if (model == null)
+            return Collections.emptyList();
+        
+        return session.syncRequest(new DistrictComponentListRequest(model), TransientCacheListener.instance());
+    }
+
 }
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeUtils.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeUtils.java
new file mode 100644 (file)
index 0000000..7a25ff8
--- /dev/null
@@ -0,0 +1,207 @@
+package org.simantics.district.network.techtype;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.NumberBinding;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.NumberType;
+import org.simantics.databoard.type.StringType;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.PossibleVariable;
+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.PossibleTechTypeTable;
+import org.simantics.district.network.techtype.requests.TechTypeTableKeyName;
+import org.simantics.scl.runtime.SCLContext;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeUtils {
+       
+       final static Logger LOGGER = LoggerFactory.getLogger(TechTypeUtils.class);
+
+       public static String DEFAULT_KEY_NAME = "pipeCode";
+
+       /**
+        * Execute a cached query for a possible tech type table item in a table with a given item key.
+        * 
+        * Result is null, if no such item was found.
+        * 
+        * @param session  A request processor on which to run the query
+        * @param itemCode  A key value, such as a pipe code
+        * @throws DatabaseException
+        */
+       public static Map<String, String> getTableItem(RequestProcessor session, Resource table, String itemCode) throws DatabaseException {
+               return session.syncRequest(new PossibleTechTypeItem(table, itemCode), TransientCacheListener.instance());
+       }
+
+       /**
+        * Get a single row in a tech type table for a given item code
+        * 
+        * Result is null, if no such item was found.
+        * 
+        * @param table  A TechTypeTable resource
+        * @param itemCode  A key value, such as a pipe code
+        * @return  A map from property name to value
+        * @throws DatabaseException
+        */
+       public static Map<String, String> getTableItem(Resource table, String itemCode) throws DatabaseException {
+               Object graph = SCLContext.getCurrent().get("graph");
+               if (graph != null && graph instanceof ReadGraph)
+                       return getTableItem((ReadGraph) graph, table, itemCode);
+               else
+                       return getTableItem(Simantics.getSession(), table, itemCode);
+       }
+       
+       /**
+        * Reset all components that address the given tech type table to the table values.
+        * 
+        * @param table  A tech type table
+        * @throws DatabaseException
+        */
+       public static void resetComponents(Resource table) throws DatabaseException {
+               Simantics.getSession().syncRequest(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                               Resource model = graph.syncRequest(new PossibleIndexRoot(table), TransientCacheListener.instance());
+                               if (model == null)
+                                       return;
+                               
+                               Resource type = graph.getPossibleObject(table, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasComponentType);
+                               if (type == null)
+                                       return;
+                               
+                               Collection<Resource> components = QueryIndexUtils.searchByType(graph, model, type);
+                               for (Resource component : components) {
+                                       updateComponent(graph, component, table);
+                               }
+                       }
+               });
+       }
+       
+       /**
+        * Update property values of a component based on the values in an associated tech type table, if any
+        * 
+        * @param graph  A write access interface to the graph database
+        * @param component  A structural component
+        * @throws DatabaseException
+        */
+       public static void updateComponent(WriteGraph graph, Resource component) throws DatabaseException {
+               StructuralResource2 STR = StructuralResource2.getInstance(graph);
+               
+               Resource type = graph.getSingleType(component, STR.Component);
+               Resource model = graph.syncRequest(new PossibleIndexRoot(component));
+               if (model == null) {
+                       LOGGER.info("updateComponent: No model for {}", component);
+                       return;
+               }
+               
+               Resource table = graph.syncRequest(new PossibleTechTypeTable(model, type), TransientCacheListener.instance());
+               if (table == null) {
+                       LOGGER.info("updateComponent: No tech type table for {} in {}", type, model);
+                       return;
+               }
+               
+               updateComponent(graph, component, table);
+       }
+
+       /**
+        * Update property values of a component based on the values in an associated tech type table, if any
+        * 
+        * @param component  A structural component
+        * @throws DatabaseException
+        */
+       public static void updateComponent(Resource component) throws DatabaseException {
+               if (LOGGER.isTraceEnabled())
+                       LOGGER.trace("updateComponent({})", component);
+                                       
+               Simantics.getSession().syncRequest(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                               updateComponent(graph, component);
+                       }
+               });
+       }
+
+       /**
+        * Update a single component's property values to those selected from a tech
+        * type table
+        * 
+        * @param graph  A write interface to the db
+        * @param component  The component to be updated
+        * @param table  A tech type table
+        * @throws DatabaseException
+        */
+       public static void updateComponent(WriteGraph graph, Resource component, Resource table)
+                       throws DatabaseException {
+               if (LOGGER.isTraceEnabled())
+                       LOGGER.trace("updateComponent(graph, {}, {}), component, table)");
+                                       
+               Variable v = graph.syncRequest(new PossibleVariable(component));
+               if (v == null) {
+                       LOGGER.info("No variable found for {}", component);
+                       return;
+               }
+               
+               String keyProp = getKeyPropertyName(graph, table);
+               String itemCode = v.getPropertyValue(graph, keyProp);
+               
+               Map<String, String> map = graph.syncRequest(new PossibleTechTypeItem(table, itemCode), TransientCacheListener.instance());
+               if (map == null) {
+                       LOGGER.info("No entry found for \"{}\" in tech type table {}", itemCode, table);
+                       return;
+               }
+               
+               for (String key : map.keySet()) {
+                       // Don't write the key property
+                       if (key.equals(DEFAULT_KEY_NAME))
+                               continue;
+                       
+                       Variable prop = v.getPossibleProperty(graph, key);
+                       if (prop == null) continue;
+                       
+                       Datatype dt = prop.getPossibleDatatype(graph);
+                       if (dt == null) continue;
+                       
+                       String value = map.get(key);
+                       if (dt instanceof NumberType) {
+                               try {
+                                       Double num = Double.valueOf(value.replace(",", "."));
+                                       NumberBinding binding = Bindings.getBinding(dt);
+                                       prop.setValue(graph, binding.create(num));
+                               } catch (NumberFormatException e) {
+                                       // Revert to asserted value
+                                       Resource pred = prop.getPossiblePredicateResource(graph);
+                                       if (pred != null)
+                                               graph.deny(component, pred);
+                               } catch (BindingException e) {
+                                       LOGGER.error("Failed to get binding for datatype {}", dt, e);
+                               }
+                       } else if (dt instanceof StringType) {
+                               prop.setValue(graph, value);
+                       } else {
+                               LOGGER.warn("updateComponent: Unsupported property type {}", dt);
+                       }
+               }
+       }
+
+       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; 
+       }
+}
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeItem.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeItem.java
new file mode 100644 (file)
index 0000000..05e4308
--- /dev/null
@@ -0,0 +1,30 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.util.Map;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.BinaryRead;
+import org.simantics.db.exception.DatabaseException;
+
+/**
+ * Query for a possible tech type table item in a table with a given item key.
+ * 
+ * Results in null, if no such item was found.
+ */
+public class PossibleTechTypeItem extends BinaryRead<Resource, String, Map<String, String>> {
+
+       public PossibleTechTypeItem(Resource table, String itemCode) {
+               super(table, itemCode);
+       }
+       
+       @Override
+       public Map<String, String> perform(ReadGraph graph) throws DatabaseException {
+               Resource table = this.parameter;
+               String itemCode = this.parameter2;
+               
+               Map<String, Map<String, String>> map = graph.syncRequest(new TechTypeTableData(table), TransientCacheListener.instance());
+               return map.get(itemCode);
+       }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTable.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTable.java
new file mode 100644 (file)
index 0000000..68fc12f
--- /dev/null
@@ -0,0 +1,33 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.util.Collection;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead2;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.layer0.Layer0;
+
+public class PossibleTechTypeTable extends ResourceRead2<Resource> {
+       public PossibleTechTypeTable(Resource model, Resource componentType) {
+               super(model, componentType);
+       }
+
+       @Override
+       public Resource perform(ReadGraph graph) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+               
+               Collection<Resource> children = graph.getObjects(resource, L0.ConsistsOf);
+               for (Resource child : children) {
+                       if (!graph.isInstanceOf(child, DN.TechType_TechTypeTable))
+                               continue;
+                       
+                       if (graph.hasStatement(child, DN.TechType_TechTypeTable_HasComponentType, resource2))
+                               return child;
+               }
+               
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTableData.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/PossibleTechTypeTableData.java
new file mode 100644 (file)
index 0000000..8dd8866
--- /dev/null
@@ -0,0 +1,23 @@
+package org.simantics.district.network.techtype.requests;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.ResourceRead2;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+
+public class PossibleTechTypeTableData extends ResourceRead2<String> {
+       public PossibleTechTypeTableData(Resource model, Resource componentType) {
+               super(model, componentType);
+       }
+
+       @Override
+       public String perform(ReadGraph graph) throws DatabaseException {
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+
+               Resource ttt = graph.syncRequest(new PossibleTechTypeTable(resource, resource2), TransientCacheListener.instance());
+               return ttt != null ? graph.getPossibleRelatedValue2(ttt, DN.TechType_TechTypeTable_HasData, Bindings.STRING) : null;
+       }
+}
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableData.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableData.java
new file mode 100644 (file)
index 0000000..2ff575b
--- /dev/null
@@ -0,0 +1,97 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+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.simantics.district.network.techtype.TechTypeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeTableData extends ResourceRead<Map<String, Map<String, String>>> {
+
+       private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableData.class);
+       
+       protected TechTypeTableData(Resource table) {
+               super(table);
+       }
+
+       @Override
+       public Map<String, 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 Collections.emptyMap();
+               
+               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);
+                       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).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().intern();
+                               String value = v.next();
+                               valueMap.put(name, value);
+                       }
+               }
+               
+               return map;
+       }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableKeyName.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/TechTypeTableKeyName.java
new file mode 100644 (file)
index 0000000..86b7d72
--- /dev/null
@@ -0,0 +1,33 @@
+package org.simantics.district.network.techtype.requests;
+
+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.simantics.layer0.Layer0;
+
+public class TechTypeTableKeyName extends ResourceRead<String> {
+       
+       public TechTypeTableKeyName(Resource table) {
+               super(table);
+       }
+
+       @Override
+       public String perform(ReadGraph graph) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+               
+               Resource type = graph.getPossibleObject(resource, DN.TechType_TechTypeTable_HasComponentType);
+               if (type != null) {
+                       for (Resource r : graph.getObjects(type, L0.DomainOf)) {
+                               Resource accessor = graph.getPossibleObject(r, L0.valueAccessor);
+                               if (accessor.equals(DN.TechType_Functions_techTypeCodeValueAccessor)) {
+                                       return graph.getRelatedValue2(r, L0.HasName);
+                               }
+                       }
+               }
+               
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/WriteTechTypeTable.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/requests/WriteTechTypeTable.java
new file mode 100644 (file)
index 0000000..1f85c5a
--- /dev/null
@@ -0,0 +1,45 @@
+package org.simantics.district.network.techtype.requests;
+
+import java.util.UUID;
+
+import org.simantics.Simantics;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+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.district.network.ontology.DistrictNetworkResource;
+import org.simantics.layer0.Layer0;
+
+public final class WriteTechTypeTable extends WriteRequest {
+       private final String data;
+       private final Resource componentType;
+
+       public WriteTechTypeTable(Resource componentType, String data) {
+               this.data = data;
+               this.componentType = componentType;
+       }
+
+       @Override
+       public void perform(WriteGraph graph) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+
+               Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
+               if (model == null)
+                       throw new DatabaseException("No active model for storing tech type data");
+
+               Resource ttt = graph.syncRequest(new PossibleTechTypeTable(model, componentType), TransientCacheListener.instance());
+
+               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);
+               }
+
+               graph.claimLiteral(ttt, DN.TechType_TechTypeTable_HasData, data);
+       }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/variable/Functions.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/variable/Functions.java
new file mode 100644 (file)
index 0000000..beb9268
--- /dev/null
@@ -0,0 +1,59 @@
+package org.simantics.district.network.techtype.variable;
+
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.ValueAccessor;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.district.network.techtype.TechTypeUtils;
+import org.simantics.scl.reflection.annotations.SCLValue;
+import org.simantics.structural.stubs.StructuralResource2;
+
+public class Functions {
+       @SCLValue(type = "ValueAccessor")
+       public static final ValueAccessor techTypeCodeValueAccessor = new ValueAccessor() {
+
+               @Override
+               public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
+                       return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context);
+               }
+
+               @Override
+               public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
+                       return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context, binding);
+               }
+
+               @Override
+               public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
+                       org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, value);
+                       
+                       updateComponentProperties(graph, context, value);
+               }
+
+               @Override
+               public void setValue(WriteGraph graph, Variable context, Object value, Binding binding)
+                               throws DatabaseException {
+                       org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, value, binding);
+                       
+                       updateComponentProperties(graph, context, value);
+               }
+
+               @Override
+               public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
+                       return org.simantics.db.layer0.function.All.standardValueAccessor.getDatatype(graph, context);
+               }
+               
+       };
+
+       protected static void updateComponentProperties(WriteGraph graph, Variable context, Object value) throws DatabaseException {
+               Variable component = context.getParent(graph);
+               Resource type = component.getPossibleType(graph, StructuralResource2.getInstance(graph).Component);
+               if (type == null)
+                       return;
+               
+               TechTypeUtils.updateComponent(graph, component.getRepresents(graph));
+       }
+}