X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fadapter%2FFlagClassFactory.java;fp=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fadapter%2FFlagClassFactory.java;h=a672c6fe9502c335ac18c8329c1bbc9667096ffc;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/FlagClassFactory.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/FlagClassFactory.java new file mode 100644 index 000000000..a672c6fe9 --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/FlagClassFactory.java @@ -0,0 +1,543 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.diagram.adapter; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; +import org.simantics.databoard.util.Bean; +import org.simantics.datatypes.literal.Font; +import org.simantics.datatypes.literal.RGB; +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.common.request.TernaryRead; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.content.ResourceTerminal; +import org.simantics.diagram.flag.AbstractFlagType; +import org.simantics.diagram.flag.BasicFlagType; +import org.simantics.diagram.flag.FlagSceneGraph; +import org.simantics.diagram.flag.FlagUtil; +import org.simantics.diagram.flag.IFlagType; +import org.simantics.diagram.flag.IFlagType.FlagInfo; +import org.simantics.diagram.flag.IFlagTypeReader; +import org.simantics.diagram.function.All; +import org.simantics.diagram.function.PredefinedVariables; +import org.simantics.diagram.query.DiagramRequests; +import org.simantics.diagram.query.FlagTables; +import org.simantics.diagram.query.FlagTypeFilter; +import org.simantics.diagram.query.FlagTypeFilters; +import org.simantics.diagram.query.FlagTypeVisual; +import org.simantics.diagram.query.FlagTypeVisuals; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.synchronization.CompositeHintSynchronizer; +import org.simantics.diagram.synchronization.IHintSynchronizer; +import org.simantics.diagram.synchronization.SynchronizationHints; +import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; +import org.simantics.diagram.synchronization.graph.FlagSynchronizer; +import org.simantics.diagram.synchronization.graph.TransformSynchronizer; +import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.Topology.Terminal; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.TextEditor; +import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; +import org.simantics.g2d.elementclass.FlagClass; +import org.simantics.g2d.elementclass.FlagClass.Mode; +import org.simantics.g2d.elementclass.FlagHandler; +import org.simantics.g2d.utils.Alignment; +import org.simantics.g2d.utils.geom.DirectionSet; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.template2d.ontology.Template2dResource; +import org.simantics.structural2.modelingRules.IModelingRules; +import org.simantics.utils.ui.ErrorLogger; + +/** + * @author Tuukka Lehtonen + */ +public class FlagClassFactory extends SyncElementFactory { + + private static final Bean[] NO_BEANS = {}; + + private static final IHintSynchronizer HINT_SYNCHRONIZER = new CompositeHintSynchronizer(FlagSynchronizer.INSTANCE, TransformSynchronizer.INSTANCE); + + public static ElementClass createFlagClass(Resource elementClass, Resource terminalResource) { + Terminal terminal = new ResourceTerminal(terminalResource, new AffineTransform(), DirectionSet.ANY); + return FlagClass.create(terminal, FlagSceneGraph.INSTANCE).newClassWith(new StaticObjectAdapter(elementClass)); + } + + @Override + public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, AsyncProcedure procedure) { + DiagramResource DIA = graph.getService(DiagramResource.class); + procedure.execute(graph, createFlagClass(DIA.Flag, DIA.Flag_Terminal)); + } + + @Override + public void load(ReadGraph g, ICanvasContext canvas, IDiagram diagram, Resource flag, IElement e) throws DatabaseException { + Layer0 l0 = g.getService(Layer0.class); + DiagramResource dr = g.getService(DiagramResource.class); + + ElementClass ec = e.getElementClass(); + final FlagHandler fh = ec.getSingleItem(FlagHandler.class); + + fh.connectData(e, flag, FlagUtil.getPossibleCounterpart(g, flag)); + fh.setExternal(e, FlagUtil.isExternal(g, flag)); + + TextEditor ed = ec.getAtMostOneItemOfClass(TextEditor.class); + if (ed != null) { + final Session session = g.getSession(); + ed.setModifier(e, new TextEditor.Modifier() { + @Override + public String getValue(IElement element) { + String s = ElementUtils.getText(element); + return s != null ? s : ""; + } + + @Override + public String isValid(IElement element, String text) { + return null; + } + + @Override + public void modify(final IElement element, final String text) { + final Resource flag = (Resource) ElementUtils.getObject(element); + try { + session.syncRequest(new WriteRequest() { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(graph); + DiagramGraphUtil.setRelatedValue(graph, flag, l0.HasLabel, l0.String, text, Bindings.STRING); + Resource otherFlag = FlagUtil.getPossibleCounterpart(graph, flag); + if (otherFlag != null) + DiagramGraphUtil.setRelatedValue(graph, otherFlag, l0.HasLabel, l0.String, text, Bindings.STRING); + } + }); + } catch (DatabaseException e) { + ErrorLogger.defaultLogError("Flag label editing failed, see exception for details.", e); + } + } + }); + } + + Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE); + AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(g, diagramRuntime, flag); + ElementUtils.setTransform(e, at); + + String label = g.getPossibleRelatedValue(flag, l0.HasLabel); + if (label == null) + label = ""; + ElementUtils.setText(e, label); + + boolean shapeIsSet = false; + boolean flagTextIsSet = false; + boolean flagTypeIsSet = false; + boolean textAreaIsSet = false; + + // Defaults + e.setHint(FlagClass.KEY_TEXT_HORIZONTAL_ALIGN, Alignment.LEADING); + e.setHint(FlagClass.KEY_TEXT_VERTICAL_ALIGN, Alignment.CENTER); + + IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES); + if (modelingRules != null) { + Resource connectionType = DiagramGraphUtil.getConnectionTypeForFlag(g, flag); + IFlagTypeReader ftr = null; + if (connectionType != null) { + //System.out.println("FLAG " + NameUtils.getSafeName(g, flag) + ", CONNECTION TYPE " + NameUtils.getSafeName(g, connectionType)); + ftr = g.getPossibleAdapter(connectionType, IFlagTypeReader.class); + } + if (ftr == null) { + //System.out.println("FLAG " + NameUtils.getSafeName(g, flag) + ", NO CONNECTION TYPE"); + ftr = g.getPossibleAdapter(flag, IFlagTypeReader.class); + } + + if (ftr != null) { + IFlagType ft = ftr.read(g, flag, modelingRules); + + FlagInfo info = ft.getInfo(g); + + Shape shape = info.getShape(); + if (shape != null) { + e.setHint(FlagClass.KEY_SHAPE, shape); + shapeIsSet = true; + } + + FlagClass.Type type = info.getType(); + if (type != null) { + e.setHint(FlagClass.KEY_FLAG_TYPE, type); + flagTypeIsSet = true; + } + + String[] flagText = info.getText(); + if (flagText != null) { + e.setHint(FlagClass.KEY_FLAG_TEXT, flagText); + flagTextIsSet = true; + } + + if (info.getTextArea() != null) { + e.setHint(FlagClass.KEY_FLAG_TEXT_AREA, info.getTextArea()); + textAreaIsSet = true; + } + + if (info.getHorizontalAlignment() != null) + e.setHint(FlagClass.KEY_TEXT_HORIZONTAL_ALIGN, info.getHorizontalAlignment()); + if (info.getVerticalAlignment() != null) + e.setHint(FlagClass.KEY_TEXT_VERTICAL_ALIGN, info.getVerticalAlignment()); + } + } + + if (!flagTypeIsSet) + // Fall back to reading flag type from a property. + e.setHint(FlagClass.KEY_FLAG_TYPE, DiagramGraphUtil.toFlagType(dr, g.getPossibleObject(flag, dr.HasFlagType), FlagClass.Type.In)); + if (!flagTextIsSet) +// e.setHint(FlagClass.KEY_FLAG_TEXT, new String[] { label }); + e.setHint(FlagClass.KEY_FLAG_TEXT, g.syncRequest(DiagramRequests.getFlagText(flag))); + if (!textAreaIsSet) { + FlagClass.Type type = e.getHint(FlagClass.KEY_FLAG_TYPE); + Mode mode = AbstractFlagType.getMode(g, flag); + e.setHint(FlagClass.KEY_FLAG_TEXT_AREA, BasicFlagType.getArea(type, mode)); + } + if (!shapeIsSet) { + FlagClass.Type type = e.getHint(FlagClass.KEY_FLAG_TYPE); + Mode mode = AbstractFlagType.getMode(g, flag); + e.setHint(FlagClass.KEY_SHAPE, BasicFlagType.getShape(type, mode)); + } + + e.setHint(SynchronizationHints.HINT_SYNCHRONIZER, HINT_SYNCHRONIZER); + e.setHint(FlagSceneGraph.KEY_FLAG_VISUALS, NO_BEANS); + + DiagramResource DIA = DiagramResource.getInstance(g); + Layer0 L0 = Layer0.getInstance(g); + + Resource template = diagramRuntime != null ? All.getTemplate(g, diagramRuntime) : null; + Resource flagTable = getFlagTable(g, template, flag); + + if (template != null && flagTable != null) { + + Resource flagTypeVisual = getVisualOfFlag(g, template, flagTable, flag); + if (flagTypeVisual != null) { + Template2dResource TEMPLATE2D = Template2dResource.getInstance(g); + float tableWidth = g.getRelatedValue(flagTable, TEMPLATE2D.FlagTable_HasWidth, Bindings.FLOAT); + Resource align = g.getPossibleObject(flagTable, TEMPLATE2D.FlagTable_HasAlignment); + float height = g.getRelatedValue(flagTable, TEMPLATE2D.FlagTable_HasRowHeigth, Bindings.FLOAT); + float halfHeight = height / 2; + float horizontalOffset = tableWidth; + + if (align != null) { + if (align.equals(TEMPLATE2D.FlagTable_Alignment_Left)) { + horizontalOffset = 0.0f; + } else if (align.equals(TEMPLATE2D.FlagTable_Alignment_Right)) { + tableWidth = -tableWidth; + horizontalOffset = -horizontalOffset; + } + } + + Collection monitorsAndTexts = g.getObjects(flagTypeVisual, L0.ConsistsOf); + List flagVisuals = Collections.emptyList(); + if (!monitorsAndTexts.isEmpty()) { + flagVisuals = new ArrayList(monitorsAndTexts.size()); + + ModelingResources MOD = ModelingResources.getInstance(g); + Resource diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE); + Resource compositeResource = g.getSingleObject(diagramResource, MOD.DiagramToComposite); + Variable compositeVariable = Variables.getVariable(g, compositeResource); + + for (Resource visual : monitorsAndTexts) { + if (!acceptVisual(g, flag, visual)) + continue; + + if (g.isInstanceOf(visual, DIA.Scenegraph_Monitor)) { + FlagTextInfo i = g.syncRequest(new ReadFlagTextInfo(visual), TransientCacheListener.instance()); + FlagTextInfo monitorInfo = (FlagTextInfo) i.clone(); + if (monitorInfo.transform != null) + monitorInfo.transform[4] += horizontalOffset; + + String path = g.getRelatedValue(visual, DIA.Scenegraph_Monitor_reference, Bindings.STRING); + String value = ""; + if (path != null && path.length() > 0) { + value = evaluatePath(g,flag,path); + } + monitorInfo.text = value; + monitorInfo.id = Long.toString(visual.getResourceId()); + + flagVisuals.add(monitorInfo); + } else if (g.isInstanceOf(visual, DIA.Scenegraph_Text)) { + FlagTextInfo i = g.syncRequest(new ReadFlagTextInfo(visual), TransientCacheListener.instance()); + FlagTextInfo info = (FlagTextInfo) i.clone(); + if (info.transform != null) + info.transform[4] += horizontalOffset; + + String path = g.getRelatedValue(visual, DIA.Scenegraph_Text_text, Bindings.STRING); + if (path != null && path.length() > 0) { + info.text = path; + } + info.id = Long.toString(visual.getResourceId()); + + flagVisuals.add(info); + } else if (g.isInstanceOf(visual, DIA.Scenegraph_SVGImage)) { + SVGImageInfo info = g.syncRequest(new ReadSVGImageInfo(visual, compositeResource, compositeVariable), TransientCacheListener.instance()); + flagVisuals.add(info); + } + } + } + + if (flagVisuals.size() > 0) { + // Make sure that the flag shape is set to something that + // should contain the flag visual to make selection borders + // work properly. + Rectangle2D newShape = new Rectangle2D.Double(); + newShape.setFrameFromDiagonal(0, -halfHeight, tableWidth, halfHeight); + e.setHint(FlagClass.KEY_SHAPE, newShape); + e.setHint(FlagSceneGraph.KEY_FLAG_VISUALS, flagVisuals.toArray(NO_BEANS)); + } + } + } + } + + private static String evaluatePath(ReadGraph graph, Resource resource, String path) throws DatabaseException{ + Variable resourceVariable = Variables.getPossibleVariable(graph, resource); + if (resourceVariable == null) + return ""; + return evaluatePath(graph, resource, resourceVariable, path); + } + + private static String evaluatePath(ReadGraph graph, Resource resource, Variable resourceVariable, String path) throws DatabaseException{ + PredefinedVariables vars = PredefinedVariables.getInstance(); + Variable property = vars.getVariable(graph, path, resource, resourceVariable); + if (property == null) + return ""; + Object value = property.getPossibleValue(graph); + if (value == null || !(value instanceof String)) + return ""; + return value.toString(); + } + + static class ReadSVGImageInfo extends TernaryRead { + + public ReadSVGImageInfo(Resource svgImageNode, Resource composite, Variable compositeVariable) { + super(svgImageNode, composite, compositeVariable); + } + + @Override + public SVGImageInfo perform(ReadGraph graph) throws DatabaseException { + SVGImageInfo info = new SVGImageInfo(); + DiagramResource DIA = DiagramResource.getInstance(graph); + info.id = Long.toString(parameter.getResourceId()); + Resource document = graph.getPossibleObject(parameter, DIA.Scenegraph_SVGImage_document); + if (document != null) { + Template2dResource TMPL = Template2dResource.getInstance(graph); + String path = graph.getPossibleRelatedValue(document, TMPL.Profiles_VariableReference_path, Bindings.STRING); + if (path != null) { + String svg = evaluatePath(graph, parameter2, parameter3, path); + if (svg != null && !svg.isEmpty()) + info.svgDocument = svg; + //System.out.println("svgDocument: " + info.svgDocument); + } + } + info.transform = graph.getPossibleRelatedValue(parameter, DIA.Scenegraph_SVGImage_transform, Bindings.DOUBLE_ARRAY); + return info; + } + + } + + static class ReadFlagTextInfo extends ResourceRead { + + public ReadFlagTextInfo(Resource textNode) { + super(textNode); + } + + @Override + public FlagTextInfo perform(ReadGraph graph) throws DatabaseException { + return createTextInfo(graph, resource); + } + + } + + /** + * @param g + * @param visual + * @return + * @throws DatabaseException + * @throws RuntimeBindingConstructionException + */ + private static FlagTextInfo createTextInfo(ReadGraph g, Resource visual) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(g); + //Template2d TEMPLATE2D = Template2d.getInstance(g); + //Layer0 L0 = Layer0.getInstance(g); + + FlagTextInfo info = new FlagTextInfo(); + info.id = Long.toString(visual.getResourceId()); + + info.font = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_font, Bindings.getBindingUnchecked(Font.class)); + info.color = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_color, RGB.Integer.BINDING/*Bindings.getBindingUnchecked(RGB.Integer.class)*/); + info.borderColor = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_borderColor, RGB.Integer.BINDING/*Bindings.getBindingUnchecked(RGB.Integer.class)*/); + info.backgroundColor = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_backgroundColor, RGB.Integer.BINDING/*Bindings.getBindingUnchecked(RGB.Integer.class)*/); + Float width = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_width, Bindings.getBindingUnchecked(Float.class)); + if (width != null) + info.width = width; + + Float borderWidth = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_borderWidth, Bindings.getBindingUnchecked(Float.class)); + if (borderWidth != null) + info.borderWidth = borderWidth; + + Boolean wrapText = g.getRelatedValue2(visual, DIA.Scenegraph_AbstractText_wrapText, Bindings.BOOLEAN); + if (wrapText != null) + info.wrapText = wrapText; + + info.hAlignment = Alignment.LEADING; + Byte hAlignment = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_horizontalAlignment, Bindings.BYTE); + if (hAlignment != null) { + if (hAlignment == 1) + info.hAlignment = Alignment.TRAILING; + else if (hAlignment == 2) + info.hAlignment = Alignment.CENTER; + } + + info.vAlignment = Alignment.LEADING; + Byte vAlignment = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_verticalAlignment, Bindings.BYTE); + if (vAlignment != null) { + if (vAlignment == 1) + info.vAlignment = Alignment.TRAILING; + else if (vAlignment == 2) + info.vAlignment = Alignment.CENTER; + else if (vAlignment == 3) + info.vAlignment = Alignment.BASELINE; + } + + info.transform = g.getPossibleRelatedValue2(visual, DIA.Scenegraph_AbstractText_transform, Bindings.getBindingUnchecked(double[].class)); + return info; + } + + private static Resource getFlagTable(ReadGraph g, Resource template, Resource flag) throws DatabaseException { + if (template == null || flag == null) + return null; + + DiagramResource DIA = DiagramResource.getInstance(g); + String tableName = g.getPossibleRelatedValue(flag, DIA.Flag_HasIOTableBinding, Bindings.STRING); + if (tableName == null) + return null; + + Map flagTables = g.syncRequest(new FlagTables(template), TransientCacheListener.>instance()); + return flagTables.get(tableName); + } + + private Resource getVisualOfFlag(ReadGraph g, Resource template, Resource flagTable, Resource flag) throws DatabaseException { + if (template == null || flagTable == null || flag == null) + return null; + + // First make sure that there are possible visuals. + // There's no point in proceeding if no visuals exist. + List flagTypeVisuals = g.syncRequest( new FlagTypeVisuals(flagTable), + TransientCacheListener.>instance()); + if (flagTypeVisuals == null || flagTypeVisuals.isEmpty()) + return null; + + Variable flagVariable = Variables.getPossibleVariable(g, flag); + if (flagVariable == null) + return null; + + for (FlagTypeVisual visual : flagTypeVisuals) { + Resource visualComposite = visual.getVisualComposite(); + + String filterReference = visual.getFilteredPropertyReference(); + if (filterReference == null || filterReference.isEmpty()) + return visualComposite; + + String filterPattern = visual.getFilterPattern(); + if (filterPattern == null || filterPattern.isEmpty()) + return visualComposite; + + String value = evaluatePath(g, flag, flagVariable, filterReference); + if (value == null) + return visualComposite; + + try { + if (!Pattern.matches(filterPattern, value)) { + // filter is defined but property don't match + continue; + } + } catch (PatternSyntaxException ex) { + ErrorLogger.defaultLogError(ex); + continue; + } + + return visualComposite; + } + + return null; + } + + /** + * @param graph + * @param flag the flag to which to apply the filter reference paths + * @param visual the visual to look for filters from + * @return true if filter passes, false if not + * @throws DatabaseException + */ + private boolean acceptVisual(ReadGraph graph, Resource flag, Resource visual) throws DatabaseException { + List filters = graph.syncRequest(new FlagTypeFilters(visual), + TransientCacheListener.>instance()); + if (filters.isEmpty()) + return true; + + Variable flagVariable = Variables.getPossibleVariable(graph, flag); + if (flagVariable == null) + return false; + + for (FlagTypeFilter filter : filters) { + String reference = filter.getReference(); + if (reference == null || reference.isEmpty()) + continue; + + String pattern = filter.getPattern(); + if (pattern == null || pattern.isEmpty()) + continue; + + String value = evaluatePath(graph, flag, flagVariable, reference); + if (value == null) + continue; + + try { + if (Pattern.matches(pattern, value) == filter.isMatchRequired()) { + return true; + } + } catch (PatternSyntaxException ex) { + ErrorLogger.defaultLogError(ex); + continue; + } + } + + return false; + } + +} \ No newline at end of file