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;
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();
}
}
}
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());
--- /dev/null
+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
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;
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";
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);
<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">
<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>
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">
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
+
--- /dev/null
+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
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
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,
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
bin.includes = META-INF/,\
.,\
adapters.xml,\
- scl/
+ scl/,\
+ plugin.xml
--- /dev/null
+<?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>
--- /dev/null
+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> ()
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;
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;
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;
}
}
+ 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());
+ }
+
}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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));
+ }
+}