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