package org.simantics.modeling.ui.componentTypeEditor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; 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.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.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.PlatformUI; 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.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.CommentMetadata; import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.CopyHandler2; import org.simantics.db.layer0.adapter.Instances; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.layer0.Layer0; import org.simantics.modeling.ui.Activator; import org.simantics.modeling.ui.componentTypeEditor.LiftPropertiesDialog.LiftedProperty; import org.simantics.modeling.userComponent.ComponentTypeCommands; import org.simantics.modeling.utils.ComponentTypePropertiesResult; import org.simantics.modeling.utils.ComponentTypeViewerPropertyInfo; import org.simantics.selectionview.SelectionViewResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ConfigurationPropertiesSection implements ComponentTypeViewerSection { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationPropertiesSection.class); private static final String[] COLUMN_NAMES = { Messages.ConfigurationPropertiesSection_Name, Messages.ConfigurationPropertiesSection_Type, Messages.ConfigurationPropertiesSection_DefaultValue, Messages.ConfigurationPropertiesSection_Unit, Messages.ConfigurationPropertiesSection_Range, Messages.ConfigurationPropertiesSection_Label, Messages.ConfigurationPropertiesSection_Description }; private static final int[] COLUMN_LENGTHS = new int[] { 120, 100, 100, 50, 100, 100, 100 }; private static final int[] COLUMN_WEIGHTS = new int[] { 0, 0, 0, 0, 0, 50, 100 }; /** * Configuration property table column indexes that are to be considered * immutable when the property relation is immutable. Note that relation * immutability does not make the asserted default value immutable. */ private static final int[] IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION = { 0, 1, 3, 4, 5, 6 }; ComponentTypeViewerData data; Table table; TableColumn[] columns; TableEditor editor; Button newProperty; Button removeProperty; Button liftProperties; boolean hasTypes = false; Button setTypes; Section section; public ConfigurationPropertiesSection(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.ConfigurationPropertiesSection_ConfigurationProperties); 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()) { ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData(); if (!info.immutable) propertiesToBeRemoved.add(info.resource); } //System.out.println("remove " + propertiesToBeRemoved.size() + " resources"); //$NON-NLS-1$ //$NON-NLS-2$ 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); } }); } }); liftProperties.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if(editor.getEditor() != null) editor.getEditor().dispose(); try { Map> map = Simantics.sync(new UniqueRead>>() { @Override public Map> perform(ReadGraph graph) throws DatabaseException { Map> map = new HashMap<>(); Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); SelectionViewResources SEL = SelectionViewResources.getInstance(graph); Resource composite = graph.getPossibleObject(data.componentType, STR.IsDefinedBy); if(composite == null) return map; Set existing = new HashSet<>(); for(Resource predicate : graph.getObjects(data.componentType, L0.DomainOf)) { if(graph.isSubrelationOf(predicate, L0.HasProperty)) { existing.add(NameUtils.getSafeName(graph, predicate)); } } for(Resource component : graph.syncRequest(new ObjectsWithType(composite, L0.ConsistsOf, STR.Component))) { Resource type = graph.getPossibleType(component, STR.Component); if(type == null) continue; String componentName = NameUtils.getSafeName(graph, component); for(Resource predicate : graph.getPredicates(component)) { if(graph.isSubrelationOf(predicate, L0.HasProperty)) { // Do not list properties shown under other properties if(graph.hasStatement(predicate, SEL.IsShownUnder)) continue; // Do not list properties that are not visible in selection view if(!graph.hasStatement(predicate, SEL.HasStandardPropertyInfo)) continue; // Some properties are explicitly marked as non-liftable Boolean canBeLifted = graph.getPossibleRelatedValue(predicate, SEL.canBeLifted, Bindings.BOOLEAN); if(canBeLifted != null && !canBeLifted) continue; String predicateName = NameUtils.getSafeName(graph, predicate); if(existing.contains(predicateName)) continue; String name = componentName + " " + predicateName; //$NON-NLS-1$ map.put(new LiftedProperty(component, type, predicate), new Pair(name, null)); } } } return map; } }); Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); LiftPropertiesDialog dialog = new LiftPropertiesDialog(shell, map, Messages.ConfigurationPropertiesSection_SelectPropertiesToLift) { @Override protected IDialogSettings getBaseDialogSettings() { return Activator.getDefault().getDialogSettings(); } }; if (dialog.open() == Window.OK) { final Collection _result = dialog.getResultT(); final boolean mapProperties = dialog.getMapProperties(); if (!_result.isEmpty()) { Simantics.getSession().async(new WriteRequest() { public Resource findAssertion(ReadGraph graph, Resource sourceType, Resource predicate) throws DatabaseException { Collection ass = graph.getAssertedObjects(sourceType, predicate); if(ass.size() == 1) return ass.iterator().next(); return null; } public void processSubs(ReadGraph graph, Resource predicate, Resource component, Resource componentType, List result, List properties, List assertions) throws DatabaseException { SelectionViewResources SEL = SelectionViewResources.getInstance(graph); for(Resource sub : graph.getObjects(predicate, SEL.UnderOf)) { Resource ass = findAssertion(graph, componentType, sub); if(ass == null) continue; result.add(new LiftedProperty(component, componentType, sub)); properties.add(sub); assertions.add(ass); processSubs(graph, sub, component, componentType, result, properties, assertions); } } @Override public void perform(WriteGraph graph) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); graph.markUndoPoint(); List properties = new ArrayList<>(); List assertions = new ArrayList<>(); List result = new ArrayList<>(); for(LiftedProperty p : _result) { Resource ass = findAssertion(graph, p.getComponentType(), p.getPredicate()); if(ass == null) continue; result.add(p); properties.add(p.getPredicate()); assertions.add(ass); processSubs(graph, p.getPredicate(), p.getComponent(), p.getComponentType(), result, properties, assertions); } CopyHandler2 ch = Layer0Utils.getPossibleCopyHandler(graph, properties); Collection copies = Layer0Utils.copyTo(graph, data.componentType, null, ch, null); int index = 0; for(Resource copy : copies) { Resource ass = assertions.get(index); LiftedProperty p = result.get(index); Collection copyAss = Layer0Utils.copyTo(graph, null, ass); if(copyAss.size() == 1) { graph.deny(copy, L0.HasDomain); graph.claim(data.componentType, L0.DomainOf, copy); Layer0Utils.assert_(graph, data.componentType, copy, copyAss.iterator().next()); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Lifted property " + NameUtils.getSafeName(graph, copy) + " into "+ NameUtils.getSafeName(graph, data.componentType))); //$NON-NLS-1$ //$NON-NLS-2$ } if(mapProperties) { Variable v = Variables.getVariable(graph, p.getComponent()); Variable property = v.getProperty(graph, p.getPredicate()); Variable displayValue = property.getProperty(graph, Variables.DISPLAY_VALUE); displayValue.setValue(graph, "=" + NameUtils.getSafeName(graph, p.getPredicate()), Bindings.STRING); //$NON-NLS-1$ } index++; } } }); } } } catch (DatabaseException e1) { LOGGER.error("Lifting properties failed", e1); //$NON-NLS-1$ return; } } }); if(hasTypes) { setTypes.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if(editor.getEditor() != null) editor.getEditor().dispose(); final List propertiesToSet = new ArrayList<>(); for(TableItem item : table.getSelection()) propertiesToSet.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource); if(propertiesToSet.size() != 1) return; Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); SetTypesDialog page = new SetTypesDialog(shell, getTypes(), Messages.ConfigurationPropertiesSection_SelectUserTypesForProp); if (page.open() == Window.OK) { final Object[] result = page.getResult(); if (result != null && result.length > 0) { Simantics.getSession().async(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { for(Object type : result) { Layer0 L0 = Layer0.getInstance(graph); graph.claim(propertiesToSet.get(0), L0.InstanceOf, null, (Resource)type); } } }); } } } }); } table.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { tk.dispose(); } }); } public void update(ComponentTypePropertiesResult result) { if (table.isDisposed()) return; // Save old selection 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 != null) continue; TableItem item = new TableItem(table, SWT.NONE); item.setText(0, info.name); item.setText(1, info.type); item.setText(2, info.defaultValue); item.setText(3, info.unitString()); item.setText(4, info.rangeString()); item.setText(5, info.label); item.setText(6, info.description); for (int columnIndex : IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION) item.setForeground(columnIndex, fg); item.setData(info); if (selected.contains(info)) selectedItems.add(item); } // Restore old selection table.setTopIndex(topIndex); table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()])); table.redraw(); } private Map> getTypes() { try { return Simantics.getSession().syncRequest(new UniqueRead>>() { @Override public Map> perform(ReadGraph graph) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(data.componentType)); Instances query = graph.adapt(STR.UserDefinedProperty, Instances.class); Collection types = query.find(graph, indexRoot); Map> result = new HashMap<>(); for(Resource type : types) { String name = NameUtils.getSafeLabel(graph, type); result.put(type, new Pair(name, null)); } return result; } }); } catch (DatabaseException e) { LOGGER.error("Finding UserDefinedProperties failed.", e); //$NON-NLS-1$ return Collections.emptyMap(); } } @Override public void setReadOnly(boolean readOnly) { boolean e = !readOnly; newProperty.setEnabled(e); removeProperty.setEnabled(e); liftProperties.setEnabled(e); } @Override public Section getSection() { return section; } @Override public double getPriority() { return 0; } @Override public Object getSectionSpecificData(ReadGraph graph, ComponentTypeViewerPropertyInfo info) throws DatabaseException { return null; } @Override public double getDataPriority() { return 0.0; } }