package org.simantics.modeling.ui.componentTypeEditor; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.window.DefaultToolTip; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; import org.simantics.Simantics; 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.TransientCacheAsyncListener; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PropertyInfo; import org.simantics.db.layer0.request.PropertyInfoRequest; import org.simantics.db.layer0.variable.StandardGraphChildVariable; import org.simantics.db.layer0.variable.StandardGraphPropertyVariable; import org.simantics.db.layer0.variable.Variable; import org.simantics.layer0.Layer0; import org.simantics.modeling.scl.CompileProceduralSCLMonitorRequest; import org.simantics.modeling.scl.CompileSCLMonitorRequest; import org.simantics.modeling.userComponent.ComponentTypeCommands; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function4; import org.simantics.structural.stubs.StructuralResource2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DerivedPropertiesSection implements ComponentTypeViewerSection { private static final String[] COLUMN_NAMES = { Messages.DerivedPropertiesSection_Name, Messages.DerivedPropertiesSection_Type, Messages.DerivedPropertiesSection_Expression, Messages.DerivedPropertiesSection_Unit, Messages.DerivedPropertiesSection_Label, Messages.DerivedPropertiesSection_Description }; private static final int[] COLUMN_LENGTHS = new int[] { 120, 100, 100, 70, 100, 100 }; private static final int[] COLUMN_WEIGHTS = new int[] { 0, 0, 100, 0, 0, 0 }; private static Function4 VALIDATE_MONITOR_EXPRESSION = new Function4() { @Override public String apply(RequestProcessor p0, Resource p1, Resource p2, String p3) { return validateMonitorExpression(p0, p1, p2, p3); } }; private static final Logger LOGGER = LoggerFactory.getLogger(DerivedPropertiesSection.class); ComponentTypeViewerData data; Table table; TableColumn[] columns; TableEditor editor; Button newProperty; Button removeProperty; Section section; public DerivedPropertiesSection(ComponentTypeViewerData data) { this.data = data; FormToolkit tk = data.tk; Form form = data.form; section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED); section.setLayout(new FillLayout()); section.setText(Messages.DerivedPropertiesSection_DerivedProperties); Composite sectionBody = tk.createComposite(section); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody); section.setClient(sectionBody); Composite tableComposite = tk.createComposite(sectionBody); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite); TableColumnLayout tcl = new TableColumnLayout(); tableComposite.setLayout(tcl); table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER); table.setLinesVisible(true); table.setHeaderVisible(true); columns = new TableColumn[COLUMN_NAMES.length]; for(int i=0;i propertiesToBeRemoved = new ArrayList<>(); for(TableItem item : table.getSelection()) propertiesToBeRemoved.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource); //System.out.println("remove " + propertiesToBeRemoved.size() + " resources"); if(!propertiesToBeRemoved.isEmpty()) Simantics.getSession().async(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { graph.markUndoPoint(); for(Resource property : propertiesToBeRemoved) ComponentTypeCommands.removeProperty(graph, data.componentType, property); } }); } }); } @Override public void setReadOnly(boolean readOnly) { boolean e = !readOnly; newProperty.setEnabled(e); removeProperty.setEnabled(e); } public static String validateMonitorExpression(final RequestProcessor processor, final Resource componentType, final Resource relation, final String expression) { if (expression.trim().isEmpty()) { return Messages.DerivedPropertiesSection_ExpressionIsEmpty; } if (expression.trim().isEmpty()) { return Messages.DerivedPropertiesSection_ExpressionIsEmpty; } try { return processor.sync(new UniqueRead() { @Override public String perform(ReadGraph graph) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(graph); // TODO: this is a bit hackish but should get the job done in most parts and // importantly indicates something for the user PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheAsyncListener.instance()); Variable parent = new StandardGraphChildVariable(null, null, componentType) { public Resource getType(ReadGraph graph) throws DatabaseException { return componentType; }; public Variable getPossibleProperty(ReadGraph graph, String name) throws DatabaseException { Variable prop = super.getPossibleProperty(graph, name); if (prop != null) { return prop; } else { return getChild(graph, name); } }; }; for(Resource literal : graph.getAssertedObjects(componentType, relation)) { try { Variable context = new StandardGraphPropertyVariable(parent, null, null, info, literal); if(graph.isInstanceOf(componentType, STR.ProceduralComponentType)) { CompileProceduralSCLMonitorRequest.compileAndEvaluate(graph, context); } else { compileAndEvaluate(graph, context, expression); } } catch (Exception e) { String msg = e.getMessage(); int index = msg.indexOf(":"); //$NON-NLS-1$ if(index > 0) msg = msg.substring(index); return msg; } } return null; } }); } catch (DatabaseException e) { LOGGER.error("Could not validate ", e); } return null; } public static void compileAndEvaluate(ReadGraph graph, Variable context, String expression) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.get("graph"); try { CompileSCLMonitorRequest compileSCLMonitorRequest = new ValidationCompilationRequest(graph, context, expression); Function1 exp = graph.syncRequest(compileSCLMonitorRequest); sclContext.put("graph", graph); //return exp.apply(context.getParent(graph)); } catch (DatabaseException e) { throw (DatabaseException)e; } catch (Throwable t) { throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } @Override public void update(ComponentTypePropertiesResult result) { if (table.isDisposed()) return; Set selected = new HashSet<>(); List selectedItems = new ArrayList(selected.size()); for (int i : table.getSelectionIndices()) { TableItem item = table.getItem(i); selected.add((ComponentTypeViewerPropertyInfo) item.getData()); } int topIndex = table.getTopIndex(); table.removeAll(); if(editor.getEditor() != null) editor.getEditor().dispose(); for(ComponentTypeViewerPropertyInfo info : result.getProperties()) { boolean immutable = result.isImmutable() || info.immutable; Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null; if(info.sectionSpecificData != MONITOR) continue; TableItem item = new TableItem(table, SWT.NONE); item.setText(0, info.valid != null ? info.name + " (!)" : info.name); //$NON-NLS-1$ item.setText(1, info.type); item.setText(2, info.expression); item.setText(3, info.unitString()); item.setText(4, info.label); item.setText(5, info.description); if (info.valid != null) item.setBackground(table.getDisplay().getSystemColor(SWT.COLOR_RED)); item.setForeground(fg); item.setData(info); if (selected.contains(info)) selectedItems.add(item); } new DefaultToolTip(table, ToolTip.NO_RECREATE, false) { @Override protected boolean shouldCreateToolTip(Event event) { TableItem item = table.getItem(new Point(event.x, event.y)); if (item != null) { ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData(); return info.valid != null; } return false; } @Override protected String getText(Event event) { TableItem item = table.getItem(new Point(event.x, event.y)); if (item != null) { ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData(); return info.valid.replaceAll("\n", ""); } return super.getText(event); } }; table.setTopIndex(topIndex); table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()])); table.redraw(); } @Override public Section getSection() { return section; } @Override public double getPriority() { return 100.0; } private static final Object MONITOR = new Object(); @Override public Object getSectionSpecificData(ReadGraph graph, ComponentTypeViewerPropertyInfo info) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource range = graph.getPossibleObject(info.resource, L0.HasRange); if(range != null && graph.isInstanceOf(range, STR.MonitorValueType)) return MONITOR; else return null; } @Override public double getDataPriority() { return 100.0; } private static final class ValidationCompilationRequest extends CompileSCLMonitorRequest { private final String expression; private ValidationCompilationRequest(ReadGraph graph, Variable context, String expression) throws DatabaseException { super(graph, context); this.expression = expression; } @Override protected String getExpressionText(ReadGraph graph) throws DatabaseException { return expression; } @Override public int hashCode() { return super.hashCode() + 37 * expression.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj) && ((ValidationCompilationRequest)obj).expression.equals(expression); } } }