/******************************************************************************* * 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; } }