X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fadapter%2FFlagClassFactory.java;h=56c2ceef42ae172e53f5aae6624e568bd9f23d13;hp=a672c6fe9502c335ac18c8329c1bbc9667096ffc;hb=560d8aa2e37cb6b0249aec6d7e96e67d5a64c59f;hpb=969bd23cab98a79ca9101af33334000879fb60c5 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 index a672c6fe9..56c2ceef4 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/FlagClassFactory.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/FlagClassFactory.java @@ -1,543 +1,548 @@ -/******************************************************************************* - * 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; - } - +/******************************************************************************* + * 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.Font; +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.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, canvas); + + 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; + } + + Font flagFont = info.getFont(); + if(flagFont != null) { + e.setHint(FlagClass.KEY_FLAG_FONT, flagFont); + } + + 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