]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/FlagClass.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / elementclass / FlagClass.java
index 15b5194908014303778357ecbef2b96b7c4513ba..604fa15a58e04b1714c58e4f15b0d50295e56e79 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.elementclass;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Shape;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Collection;\r
-\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.DataElementMap;\r
-import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
-import org.simantics.g2d.element.ElementClass;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.SceneGraphNodeKey;\r
-import org.simantics.g2d.element.handler.InternalSize;\r
-import org.simantics.g2d.element.handler.LifeCycle;\r
-import org.simantics.g2d.element.handler.Outline;\r
-import org.simantics.g2d.element.handler.Rotate;\r
-import org.simantics.g2d.element.handler.SceneGraph;\r
-import org.simantics.g2d.element.handler.TerminalLayout;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.element.handler.Text;\r
-import org.simantics.g2d.element.handler.impl.BorderColorImpl;\r
-import org.simantics.g2d.element.handler.impl.DefaultTransform;\r
-import org.simantics.g2d.element.handler.impl.FillColorImpl;\r
-import org.simantics.g2d.element.handler.impl.SimpleElementLayers;\r
-import org.simantics.g2d.element.handler.impl.StaticSymbolImpl;\r
-import org.simantics.g2d.element.handler.impl.TextImpl;\r
-import org.simantics.g2d.image.Image;\r
-import org.simantics.g2d.image.impl.ShapeImage;\r
-import org.simantics.g2d.utils.Alignment;\r
-import org.simantics.g2d.utils.geom.DirectionSet;\r
-import org.simantics.scenegraph.Node;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.nodes.FlagNode;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class FlagClass {\r
-\r
-    public static enum Type {\r
-        /// The input part of a pair of flags.\r
-        In,\r
-        /// The output part of a pair of flags.\r
-        Out;\r
-        public Type other() {\r
-            return this == Out ? In: Out;\r
-        }\r
-    }\r
-\r
-    public static class Mode {\r
-        public static final Mode External = new External(1);\r
-        public static final Mode Internal = new Mode() {\r
-            public String toString() { return "Internal"; }\r
-        };\r
-    }\r
-\r
-    public static class External extends Mode {\r
-        public final int count;\r
-        public External(int count) {\r
-            this.count = count;\r
-        }\r
-        @Override\r
-        public String toString() {\r
-            return "External(" + count + ")";\r
-        }\r
-    }\r
-\r
-    private static final double       GLOBAL_SCALE                 = 0.1;\r
-    private static final double       FLAG_SIZE_SCALE              = 3 * GLOBAL_SCALE;\r
-\r
-    public static final double        DEFAULT_WIDTH                = 70 * FLAG_SIZE_SCALE;\r
-    public static final double        DEFAULT_HEIGHT               = 20 * FLAG_SIZE_SCALE;\r
-    public static final double        DEFAULT_BEAK_ANGLE           = 60;\r
-\r
-    public static final Key           KEY_FLAG_TYPE                = new KeyOf(Type.class, "FLAG_TYPE");\r
-    public static final Key           KEY_EXTERNAL                 = new KeyOf(Boolean.class, "FLAG_EXTERNAL");\r
-    public static final Key           KEY_FLAG_MODE                = new KeyOf(Mode.class, "FLAG_MODE");\r
-    public static final Key           KEY_FLAG_WIDTH               = new KeyOf(Double.class, "FLAG_WIDTH");\r
-    public static final Key           KEY_FLAG_HEIGHT              = new KeyOf(Double.class, "FLAG_HEIGHT");\r
-    public static final Key           KEY_FLAG_BEAK_ANGLE          = new KeyOf(Double.class, "FLAG_BEAK_ANGLE");\r
-    public static final Key           KEY_FLAG_TEXT                = new KeyOf(String[].class, "FLAG_TEXT");\r
-    public static final Key           KEY_FLAG_TEXT_AREA           = new KeyOf(Rectangle2D.class, "FLAG_TEXT_AREA_SIZE");\r
-    public static final Key           KEY_SHAPE                    = new KeyOf(Shape.class, "SHAPE");\r
-    public static final Key           KEY_TEXT_HORIZONTAL_ALIGN    = new KeyOf(Alignment.class, "TEXT_HORIZONTAL_ALIGN");\r
-    public static final Key           KEY_TEXT_VERTICAL_ALIGN      = new KeyOf(Alignment.class, "TEXT_VERTICAL_ALIGN");\r
-\r
-    public static final Key          KEY_SG_NODE                  = new SceneGraphNodeKey(Node.class, "FLAG_SG_NODE");\r
-\r
-    /**\r
-     * Indicates that this flag is connected to another flag.\r
-     */\r
-    private static final Key KEY_FLAG_CONNECTION_DATA     = new KeyOf(DataConnection.class, "FLAG_CONNECTION_DATA");\r
-    private static final Key KEY_FLAG_CONNECTION_ELEMENTS = new KeyOf(ElementConnection.class, "FLAG_CONNECTION_ELEMENTS");\r
-\r
-    public interface Connection<T> {\r
-        T getFirst();\r
-        T getSecond();\r
-    }\r
-\r
-    private static class Conn<T> implements Connection<T> {\r
-        private final T first;\r
-        private final T second;\r
-        public Conn(T first, T second) {\r
-            this.first = first;\r
-            this.second = second;\r
-        }\r
-        @Override\r
-        public T getFirst() {\r
-            return first;\r
-        }\r
-        @Override\r
-        public T getSecond() {\r
-            return second;\r
-        }\r
-    }\r
-    private static class ElementConnection extends Conn<IElement> {\r
-        public ElementConnection(IElement first, IElement second) {\r
-            super(first, second);\r
-            if (first == null)\r
-                throw new IllegalArgumentException("first is null");\r
-            if (second == null)\r
-                throw new IllegalArgumentException("second is null");\r
-        }\r
-    }\r
-    private static class DataConnection extends Conn<Object> {\r
-        public DataConnection(Object first, Object second) {\r
-            super(first, second);\r
-            if (first == null)\r
-                throw new IllegalArgumentException("first is null");\r
-            // Second may be null to indicate "not-connected"\r
-        }\r
-        public boolean isConnected() {\r
-            return getSecond() != null;\r
-        }\r
-    }\r
-\r
-    public static final FlagHandler  FLAG_HANDLER = new FlagHandler() {\r
-\r
-        private static final long serialVersionUID = -4258875745321808416L;\r
-\r
-        @Override\r
-        public Type getType(IElement e) {\r
-            return FlagClass.getType(e);\r
-        }\r
-\r
-        @Override\r
-        public void setType(IElement e, Type type) {\r
-            e.setHint(KEY_FLAG_TYPE, type);\r
-        }\r
-\r
-        @Override\r
-        public boolean isExternal(IElement e) {\r
-            return Boolean.TRUE.equals(e.getHint(KEY_EXTERNAL));\r
-        }\r
-\r
-        @Override\r
-        public void setExternal(IElement e, boolean external) {\r
-            e.setHint(KEY_EXTERNAL, Boolean.valueOf(external));\r
-        }\r
-\r
-        @Override\r
-        public Connection<IElement> getConnection(IElement e) {\r
-            return e.getHint(KEY_FLAG_CONNECTION_ELEMENTS);\r
-        }\r
-\r
-        @Override\r
-        public Connection<Object> getConnectionData(IElement e) {\r
-            DataConnection dc = e.getHint(KEY_FLAG_CONNECTION_DATA);\r
-            return (dc != null && dc.isConnected()) ? dc : null;\r
-        }\r
-\r
-        @Override\r
-        public void connect(IElement e1, IElement e2) {\r
-            assert e1 != null && e2 != null;\r
-\r
-            ElementConnection ce = new ElementConnection(e1, e2);\r
-            e1.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);\r
-            e2.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);\r
-        }\r
-\r
-        @Override\r
-        public void connectData(IElement e1, Object o1, Object o2) {\r
-            e1.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);\r
-            e1.setHint(KEY_FLAG_CONNECTION_DATA, new DataConnection(o1, o2));\r
-        }\r
-\r
-        @Override\r
-        public void disconnect(IElement local) {\r
-            assert local != null;\r
-            local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);\r
-            DataConnection c = (DataConnection) local.removeHint(KEY_FLAG_CONNECTION_DATA);\r
-            if (c != null) {\r
-                IElement remote = otherElement(local, c);\r
-                if (remote != null) {\r
-                    local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);\r
-                    remote.removeHint(KEY_FLAG_CONNECTION_DATA);\r
-                }\r
-            }\r
-        }\r
-\r
-        @Override\r
-        public boolean isWithinDiagram(IDiagram d, Connection<?> c) {\r
-            assert d != null;\r
-            assert c != null;\r
-            if (c instanceof DataConnection)\r
-                return bothOnDiagram(d, (DataConnection) c);\r
-            if (c instanceof ElementConnection)\r
-                return bothOnDiagram(d, (ElementConnection) c);\r
-            return false;\r
-        }\r
-\r
-        @Override\r
-        public IElement getCorrespondence(IElement end) {\r
-            assert end != null;\r
-            DataConnection dc = (DataConnection) end.getHint(KEY_FLAG_CONNECTION_DATA);\r
-            if (dc != null && dc.isConnected())\r
-                return otherElement(end, dc);\r
-            ElementConnection ec = (ElementConnection) end.getHint(KEY_FLAG_CONNECTION_ELEMENTS);\r
-            if (ec != null)\r
-                return otherElement(end, ec);\r
-            return null;\r
-        }\r
-\r
-        boolean bothOnDiagram(IDiagram d, DataConnection c) {\r
-            if (!c.isConnected())\r
-                return false;\r
-\r
-            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);\r
-            IElement eout = dem.getElement(d, c.getFirst());\r
-            IElement ein = dem.getElement(d, c.getSecond());\r
-            return eout != null && ein != null;\r
-        }\r
-\r
-        boolean bothOnDiagram(IDiagram d, ElementConnection c) {\r
-            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);\r
-            Object o1 = dem.getData(d, c.getFirst());\r
-            Object o2 = dem.getData(d, c.getSecond());\r
-            return o1 != null && o2 != null;\r
-        }\r
-\r
-        public IElement otherElement(IElement e, DataConnection c) {\r
-            if (!c.isConnected())\r
-                return null;\r
-\r
-            IDiagram d = ElementUtils.peekDiagram(e);\r
-            if (d == null)\r
-                return null;\r
-\r
-            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);\r
-            Object o = dem.getData(d, e);\r
-            if (c.getFirst().equals(o))\r
-                return dem.getElement(d, c.getSecond());\r
-            if (c.getSecond().equals(o))\r
-                return dem.getElement(d, c.getFirst());\r
-            throw new IllegalArgumentException("specified object '" + o + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");\r
-        }\r
-\r
-        public IElement otherElement(IElement e, ElementConnection c) {\r
-            IElement a = c.getFirst();\r
-            IElement b = c.getSecond();\r
-            if (e == a)\r
-                return b;\r
-            if (e == b)\r
-                return a;\r
-            throw new IllegalArgumentException("specified element '" + e + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");\r
-        }\r
-    };\r
-\r
-    static final Shape staticShape;\r
-\r
-    static {\r
-        Path2D path = new Path2D.Double();\r
-        staticShape = path;\r
-        createFlagShape(path, Type.In, Mode.External, DEFAULT_WIDTH, DEFAULT_HEIGHT, getBeakLength(DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE));\r
-    }\r
-\r
-    public static final BasicStroke         STROKE                = new BasicStroke(0.15f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);\r
-    static final Image               DEFAULT_IMAGE         = new ShapeImage(staticShape, null, STROKE);\r
-    static final StaticSymbolImpl    DEFAULT_STATIC_SYMBOL = new StaticSymbolImpl(DEFAULT_IMAGE);\r
-    static final FlagSize            DEFAULT_FLAG_SIZE     = new FlagSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE);\r
-    static final Initializer         DEFAULT_INITIALIZER   = new Initializer(Type.In, Mode.External);\r
-\r
-    public static final ElementClass FLAGCLASS =\r
-        ElementClass.compile(\r
-                DEFAULT_INITIALIZER,\r
-                FLAG_HANDLER,\r
-                DefaultTransform.INSTANCE,\r
-                DEFAULT_FLAG_SIZE,\r
-                BorderColorImpl.BLACK,\r
-                FillColorImpl.WHITE,\r
-                TextImpl.INSTANCE,\r
-                FlagTerminalTopology.DEFAULT,\r
-                FlagSceneGraph.INSTANCE,\r
-                DEFAULT_STATIC_SYMBOL\r
-        ).setId(FlagClass.class.getSimpleName());\r
-\r
-    public static ElementClass create(Terminal terminal) {\r
-        return ElementClass.compile(\r
-                DEFAULT_INITIALIZER,\r
-                FLAG_HANDLER,\r
-                DefaultTransform.INSTANCE,\r
-                DEFAULT_FLAG_SIZE,\r
-                BorderColorImpl.BLACK,\r
-                FillColorImpl.WHITE,\r
-                TextImpl.INSTANCE,\r
-                new FlagTerminalTopology(terminal),\r
-                FlagSceneGraph.INSTANCE,\r
-                DEFAULT_STATIC_SYMBOL,\r
-                SimpleElementLayers.INSTANCE\r
-        ).setId(FlagClass.class.getSimpleName());\r
-    }\r
-\r
-    public static ElementClass create(Terminal terminal, SceneGraph scn) {\r
-        return ElementClass.compile(\r
-                DEFAULT_INITIALIZER,\r
-                FLAG_HANDLER,\r
-                DefaultTransform.INSTANCE,\r
-                DEFAULT_FLAG_SIZE,\r
-                BorderColorImpl.BLACK,\r
-                FillColorImpl.WHITE,\r
-                TextImpl.INSTANCE,\r
-                new FlagTerminalTopology(terminal),\r
-                scn,\r
-                DEFAULT_STATIC_SYMBOL,\r
-                SimpleElementLayers.INSTANCE\r
-        ).setId(FlagClass.class.getSimpleName());\r
-    }\r
-\r
-    static class Initializer implements LifeCycle {\r
-        private static final long serialVersionUID = 4404942036933073584L;\r
-\r
-        private final Type type;\r
-        private final Mode mode;\r
-\r
-        Initializer(Type type, Mode mode) {\r
-            assert type != null;\r
-            assert mode != null;\r
-            this.type = type;\r
-            this.mode = mode;\r
-        }\r
-\r
-        @Override\r
-        public void onElementActivated(IDiagram d, IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public void onElementCreated(IElement e) {\r
-            e.setHint(KEY_FLAG_TYPE, type);\r
-            e.setHint(KEY_FLAG_MODE, mode);\r
-            //e.setHint(ElementHints.KEY_COMPOSITE, AlphaComposite.SrcOver.derive(0.5f));\r
-        }\r
-\r
-        @Override\r
-        public void onElementDeactivated(IDiagram d, IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public void onElementDestroyed(IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public int hashCode() {\r
-            final int prime = 31;\r
-            int result = 1;\r
-            result = prime * result + mode.hashCode();\r
-            result = prime * result + type.hashCode();\r
-            return result;\r
-        }\r
-\r
-        @Override\r
-        public boolean equals(Object obj) {\r
-            if (this == obj)\r
-                return true;\r
-            if (obj == null)\r
-                return false;\r
-            if (getClass() != obj.getClass())\r
-                return false;\r
-            Initializer other = (Initializer) obj;\r
-            if (!mode.equals(other.mode))\r
-                return false;\r
-            if (!type.equals(other.type))\r
-                return false;\r
-            return true;\r
-        }\r
-    };\r
-\r
-    public static Path2D createFlagShape(Path2D path, Type type, Mode mode, double width, double height, double beakLength) {\r
-        double hh = height / 2;        \r
-        path.reset();\r
-        switch (type) {\r
-            case Out:\r
-                if (mode instanceof External) {\r
-                    path.moveTo(0, hh);\r
-                    path.lineTo(width, hh);\r
-                    path.lineTo(width+beakLength, 0);\r
-                    path.lineTo(width, -hh);\r
-                    path.lineTo(0, -hh);\r
-                    path.closePath();\r
-                    path.moveTo(width, hh);\r
-                    path.lineTo(width, -hh);    \r
-                    int count = ((External)mode).count;\r
-                    if(count > 1) {\r
-                        double shadow=hh*0.25;\r
-                        double ix = beakLength \r
-                                - 0.5*shadow*(1.0 + beakLength/hh);\r
-                        double iy = hh * (ix / beakLength - 1.0);\r
-                        for(int sid=1;sid<=Math.min(count-1, 4);++sid) {\r
-                            double dis = sid*shadow;\r
-                            path.moveTo(dis, hh+dis-shadow);\r
-                            path.lineTo(dis, hh+dis);\r
-                            path.lineTo(dis+width, hh+dis);\r
-                            path.lineTo(dis+width+beakLength, dis);\r
-                            path.lineTo(width + ix + dis, iy + dis);\r
-                        }\r
-                    } else {\r
-                        double left = 0;\r
-                        double right = width - 0;\r
-                        if (left < right) {\r
-                            path.moveTo(left, 0);\r
-                            path.lineTo(right, 0);\r
-                        }\r
-                    }\r
-                } else if (mode == Mode.Internal) {\r
-                    path.moveTo(0, hh);\r
-                    path.lineTo(beakLength, 0);\r
-                    path.lineTo(0, -hh);\r
-                    path.closePath();\r
-                }\r
-                break;\r
-            case In:\r
-                path.moveTo(0, 0);\r
-                if (mode instanceof External) {\r
-                    path.lineTo(-beakLength, -hh);\r
-                    path.lineTo(-width-beakLength, -hh);\r
-                    path.lineTo(-width-beakLength, hh);\r
-                    path.lineTo(-beakLength, hh);\r
-                    path.closePath();\r
-                    path.moveTo(-beakLength, -hh);\r
-                    path.lineTo(-beakLength, hh);  \r
-                    int count = ((External)mode).count;\r
-                    if(count > 1) {\r
-                        double shadow=hh*0.25;\r
-                        double ix = beakLength \r
-                                - 0.5*shadow*(1.0 + beakLength/hh);\r
-                        double iy = hh * (ix / beakLength - 1.0);\r
-                        double xDisp = -width-beakLength;\r
-                        for(int sid=1;sid<=Math.min(count-1, 4);++sid) {\r
-                            double dis = sid*shadow;\r
-                            path.moveTo(xDisp+dis, hh+dis-shadow);\r
-                            path.lineTo(xDisp+dis, hh+dis);\r
-                            path.lineTo(xDisp+dis+width, hh+dis);\r
-                            path.lineTo(xDisp+dis+width+beakLength, dis);                            \r
-                            path.lineTo(xDisp+width + ix + dis, iy + dis);\r
-                        }\r
-                    } else {\r
-                        double left = -width-beakLength+0;\r
-                        double right = -beakLength-0;       \r
-                        if (left < right) {\r
-                            path.moveTo(left, 0);\r
-                            path.lineTo(right, 0);\r
-                        }\r
-                    }\r
-                } else if (mode == Mode.Internal) {\r
-                    path.lineTo(-beakLength, -hh);\r
-                    path.lineTo(-beakLength, hh);\r
-                    path.closePath();\r
-                }\r
-                break;\r
-        }\r
-        return path;\r
-    }\r
-\r
-    public static Path2D createFlagShape(IElement e) {\r
-        Type type = getType(e);\r
-        Mode mode = e.getHint(KEY_FLAG_MODE);\r
-        double width = e.getHint(KEY_FLAG_WIDTH);\r
-        double height = e.getHint(KEY_FLAG_HEIGHT);\r
-        double beakLength = getBeakLength(e);\r
-        Path2D path = new Path2D.Double();\r
-        createFlagShape(path, type, mode, width, height, beakLength);\r
-        return path;\r
-    }\r
-\r
-    static class FlagSize implements InternalSize, Outline, LifeCycle {\r
-\r
-        private static final long serialVersionUID = 829379327756475944L;\r
-\r
-        private final double length;\r
-        private final double thickness;\r
-        private final double beakAngle;\r
-\r
-        public FlagSize(double length, double thickness, double beakAngle) {\r
-            this.length = length;\r
-            this.thickness = thickness;\r
-            this.beakAngle = beakAngle;\r
-        }\r
-\r
-        @Override\r
-        public Shape getElementShape(IElement e) {\r
-            Shape shape = e.getHint(KEY_SHAPE);\r
-            if (shape != null)\r
-                return shape;\r
-            return createFlagShape(e);\r
-        }\r
-\r
-        @Override\r
-        public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
-            if (size == null)\r
-                size = new Rectangle2D.Double();\r
-            Shape shape = getElementShape(e);\r
-            size.setFrame(shape.getBounds2D());\r
-            return size;\r
-        }\r
-\r
-        @Override\r
-        public void onElementActivated(IDiagram d, IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public void onElementCreated(IElement e) {\r
-            e.setHint(KEY_FLAG_WIDTH, length);\r
-            e.setHint(KEY_FLAG_HEIGHT, thickness);\r
-            e.setHint(KEY_FLAG_BEAK_ANGLE, beakAngle);\r
-        }\r
-\r
-        @Override\r
-        public void onElementDeactivated(IDiagram d, IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public void onElementDestroyed(IElement e) {\r
-        }\r
-\r
-        @Override\r
-        public int hashCode() {\r
-            final int prime = 31;\r
-            int result = 1;\r
-            long temp;\r
-            temp = Double.doubleToLongBits(beakAngle);\r
-            result = prime * result + (int) (temp ^ (temp >>> 32));\r
-            temp = Double.doubleToLongBits(length);\r
-            result = prime * result + (int) (temp ^ (temp >>> 32));\r
-            temp = Double.doubleToLongBits(thickness);\r
-            result = prime * result + (int) (temp ^ (temp >>> 32));\r
-            return result;\r
-        }\r
-\r
-        @Override\r
-        public boolean equals(Object obj) {\r
-            if (this == obj)\r
-                return true;\r
-            if (obj == null)\r
-                return false;\r
-            if (getClass() != obj.getClass())\r
-                return false;\r
-            FlagSize other = (FlagSize) obj;\r
-            if (Double.doubleToLongBits(beakAngle) != Double.doubleToLongBits(other.beakAngle))\r
-                return false;\r
-            if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length))\r
-                return false;\r
-            if (Double.doubleToLongBits(thickness) != Double.doubleToLongBits(other.thickness))\r
-                return false;\r
-            return true;\r
-        }\r
-    }\r
-\r
-    static class FlagSceneGraph implements SceneGraph {\r
-        private static final long serialVersionUID = 35208146123929197L;\r
-\r
-        public static final FlagSceneGraph INSTANCE = new FlagSceneGraph();\r
-\r
-        @Override\r
-        public void cleanup(IElement e) {\r
-            ElementUtils.removePossibleNode(e, KEY_SG_NODE);\r
-        }\r
-\r
-        @Override\r
-        public void init(IElement e, G2DParentNode parent) {\r
-            Color fc = ElementUtils.getFillColor(e, Color.WHITE);\r
-            Color bc = ElementUtils.getBorderColor(e, Color.BLACK);\r
-            Color tc = ElementUtils.getTextColor(e, Color.BLACK);\r
-\r
-            Outline outline = e.getElementClass().getSingleItem(Outline.class);\r
-            Shape shape = outline.getElementShape(e);\r
-            Type type = getType(e);\r
-            double dir = getDirection(e);\r
-            double width = e.getHint(KEY_FLAG_WIDTH);\r
-            double height = e.getHint(KEY_FLAG_HEIGHT);\r
-            double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);\r
-\r
-            String[] flagText = e.getHint(KEY_FLAG_TEXT);\r
-            if (flagText == null) {\r
-                // fallback option.\r
-                Text t = e.getElementClass().getAtMostOneItemOfClass(Text.class);\r
-                if (t != null) {\r
-                    String text = t.getText(e);\r
-                    if (text != null)\r
-                        flagText = new String[] { text };\r
-                }\r
-            }\r
-\r
-            // DEBUG TEXT\r
-            //flagText = new String[] { String.format("%3.1f", dir) + " deg", "FOO"};\r
-\r
-            Rectangle2D textArea = e.getHint(KEY_FLAG_TEXT_AREA);\r
-            if (textArea == null) {\r
-                double beakLength = getBeakLength(height, beakAngle);\r
-                textArea = type == Type.In\r
-                ? new Rectangle2D.Double(-width-beakLength, -height*0.5, width, height)\r
-                : new Rectangle2D.Double(0, -height*0.5, width, height);\r
-            }\r
-\r
-            Alignment horizAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_HORIZONTAL_ALIGN, Alignment.LEADING);\r
-            Alignment vertAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_VERTICAL_ALIGN, Alignment.CENTER);\r
-\r
-            FlagNode flag = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, ElementUtils.generateNodeId(e), FlagNode.class);\r
-            flag.init(shape,\r
-                    flagText,\r
-                    STROKE,\r
-                    bc,\r
-                    fc,\r
-                    tc,\r
-                    (float) width,\r
-                    (float) height,\r
-                    (float) dir,\r
-                    (float) beakAngle,\r
-                    textArea,\r
-                    horizAlign.ordinal(),\r
-                    vertAlign.ordinal());\r
-            AffineTransform at = ElementUtils.getTransform(e);\r
-            if(at != null) flag.setTransform(at);\r
-\r
-        }\r
-    }\r
-\r
-    static class TerminalPoint implements Terminal {\r
-    }\r
-\r
-    public static class FlagTerminalTopology implements TerminalTopology, TerminalLayout {\r
-        private static final long    serialVersionUID = -4194634598346105458L;\r
-\r
-        public static final Terminal             DEFAULT_T0       = new TerminalPoint();\r
-        public static final FlagTerminalTopology DEFAULT          = new FlagTerminalTopology(DEFAULT_T0);\r
-\r
-        final Terminal T0;\r
-\r
-        public FlagTerminalTopology(Terminal t) {\r
-            this.T0 = t;\r
-        }\r
-\r
-        @Override\r
-        public void getTerminals(IElement e, Collection<Terminal> result) {\r
-            result.add(T0);\r
-        }\r
-\r
-        @Override\r
-        public AffineTransform getTerminalPosition(IElement node, Terminal t) {\r
-            if (t == T0) {\r
-                return new AffineTransform();\r
-            }\r
-            return null;\r
-        }\r
-\r
-        @Override\r
-        public boolean getTerminalDirection(IElement node, Terminal t, DirectionSet directions) {\r
-            Type type = getType(node);\r
-            double d = getDirection(node);\r
-            if (t == T0) {\r
-                switch (type) {\r
-                    case In: directions.add(d); break;\r
-                    case Out: directions.add(Math.IEEEremainder(d + 180.0, 360.0)); break;\r
-                }\r
-                //System.out.println("directions T0: " + Arrays.toString(directions.toArray()));\r
-                return true;\r
-            }\r
-            return false;\r
-        }\r
-\r
-//        static final Path2D terminalShape;\r
-//\r
-//        static {\r
-//            double s = .5;\r
-//            Path2D p = new Path2D.Double();\r
-//            p.moveTo(s, s);\r
-//            p.lineTo(s, -s);\r
-//            p.lineTo(-s, -s);\r
-//            p.lineTo(-s, s);\r
-//            p.closePath();\r
-//            terminalShape = p;\r
-//        }\r
-\r
-        @Override\r
-        public Shape getTerminalShape(IElement node, Terminal t) {\r
-            //return terminalShape;\r
-            //return null;\r
-            // For each terminal, return the whole shape of the element.\r
-            return ElementUtils.getElementShapeOrBounds(node);\r
-        }\r
-\r
-        @Override\r
-        public int hashCode() {\r
-            final int prime = 31;\r
-            int result = 1;\r
-            result = prime * result + ((T0 == null) ? 0 : T0.hashCode());\r
-            return result;\r
-        }\r
-\r
-        @Override\r
-        public boolean equals(Object obj) {\r
-            if (this == obj)\r
-                return true;\r
-            if (obj == null)\r
-                return false;\r
-            if (getClass() != obj.getClass())\r
-                return false;\r
-            FlagTerminalTopology other = (FlagTerminalTopology) obj;\r
-            if (T0 == null) {\r
-                if (other.T0 != null)\r
-                    return false;\r
-            } else if (!T0.equals(other.T0))\r
-                return false;\r
-            return true;\r
-        }\r
-    }\r
-\r
-    public static AffineTransform getTransform(IElement e) {\r
-        AffineTransform at = ElementUtils.getTransform(e);\r
-        if (at == null)\r
-            return new AffineTransform();\r
-        return at;\r
-    }\r
-\r
-    public static double getDirection(IElement e) {\r
-        Rotate rotate = e.getElementClass().getAtMostOneItemOfClass(Rotate.class);\r
-        if (rotate != null) {\r
-            return rotate.getAngle(e);\r
-        }\r
-        return 0.0;\r
-    }\r
-\r
-    public static Type getType(IElement e) {\r
-        Type t = e.getHint(KEY_FLAG_TYPE);\r
-        return t != null ? t : Type.In;\r
-    }\r
-\r
-    public static Mode getMode(IElement e) {\r
-        Mode m = e.getHint(KEY_FLAG_MODE);\r
-        return m != null ? m : Mode.External;\r
-    }\r
-\r
-    public static double getBeakLength(IElement e) {\r
-        double height = e.getHint(KEY_FLAG_HEIGHT);\r
-        double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);\r
-        beakAngle = Math.min(180, Math.max(10, beakAngle));\r
-        return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));\r
-    }\r
-\r
-    public static double getBeakLength(double height, double beakAngle) {\r
-        beakAngle = Math.min(180, Math.max(10, beakAngle));\r
-        return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * 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.g2d.elementclass;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Collection;
+
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
+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.SceneGraphNodeKey;
+import org.simantics.g2d.element.handler.InternalSize;
+import org.simantics.g2d.element.handler.LifeCycle;
+import org.simantics.g2d.element.handler.Outline;
+import org.simantics.g2d.element.handler.Rotate;
+import org.simantics.g2d.element.handler.SceneGraph;
+import org.simantics.g2d.element.handler.TerminalLayout;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.element.handler.Text;
+import org.simantics.g2d.element.handler.impl.BorderColorImpl;
+import org.simantics.g2d.element.handler.impl.DefaultTransform;
+import org.simantics.g2d.element.handler.impl.FillColorImpl;
+import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
+import org.simantics.g2d.element.handler.impl.StaticSymbolImpl;
+import org.simantics.g2d.element.handler.impl.TextImpl;
+import org.simantics.g2d.image.Image;
+import org.simantics.g2d.image.impl.ShapeImage;
+import org.simantics.g2d.utils.Alignment;
+import org.simantics.g2d.utils.geom.DirectionSet;
+import org.simantics.scenegraph.Node;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.nodes.FlagNode;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class FlagClass {
+
+    public static enum Type {
+        /// The input part of a pair of flags.
+        In,
+        /// The output part of a pair of flags.
+        Out;
+        public Type other() {
+            return this == Out ? In: Out;
+        }
+    }
+
+    public static class Mode {
+        public static final Mode External = new External(1);
+        public static final Mode Internal = new Mode() {
+            public String toString() { return "Internal"; }
+        };
+    }
+
+    public static class External extends Mode {
+        public final int count;
+        public External(int count) {
+            this.count = count;
+        }
+        @Override
+        public String toString() {
+            return "External(" + count + ")";
+        }
+    }
+
+    private static final double       GLOBAL_SCALE                 = 0.1;
+    private static final double       FLAG_SIZE_SCALE              = 3 * GLOBAL_SCALE;
+
+    public static final double        DEFAULT_WIDTH                = 70 * FLAG_SIZE_SCALE;
+    public static final double        DEFAULT_HEIGHT               = 20 * FLAG_SIZE_SCALE;
+    public static final double        DEFAULT_BEAK_ANGLE           = 60;
+
+    public static final Key           KEY_FLAG_TYPE                = new KeyOf(Type.class, "FLAG_TYPE");
+    public static final Key           KEY_EXTERNAL                 = new KeyOf(Boolean.class, "FLAG_EXTERNAL");
+    public static final Key           KEY_FLAG_MODE                = new KeyOf(Mode.class, "FLAG_MODE");
+    public static final Key           KEY_FLAG_WIDTH               = new KeyOf(Double.class, "FLAG_WIDTH");
+    public static final Key           KEY_FLAG_HEIGHT              = new KeyOf(Double.class, "FLAG_HEIGHT");
+    public static final Key           KEY_FLAG_BEAK_ANGLE          = new KeyOf(Double.class, "FLAG_BEAK_ANGLE");
+    public static final Key           KEY_FLAG_TEXT                = new KeyOf(String[].class, "FLAG_TEXT");
+    public static final Key           KEY_FLAG_TEXT_AREA           = new KeyOf(Rectangle2D.class, "FLAG_TEXT_AREA_SIZE");
+    public static final Key           KEY_SHAPE                    = new KeyOf(Shape.class, "SHAPE");
+    public static final Key           KEY_TEXT_HORIZONTAL_ALIGN    = new KeyOf(Alignment.class, "TEXT_HORIZONTAL_ALIGN");
+    public static final Key           KEY_TEXT_VERTICAL_ALIGN      = new KeyOf(Alignment.class, "TEXT_VERTICAL_ALIGN");
+
+    public static final Key          KEY_SG_NODE                  = new SceneGraphNodeKey(Node.class, "FLAG_SG_NODE");
+
+    /**
+     * Indicates that this flag is connected to another flag.
+     */
+    private static final Key KEY_FLAG_CONNECTION_DATA     = new KeyOf(DataConnection.class, "FLAG_CONNECTION_DATA");
+    private static final Key KEY_FLAG_CONNECTION_ELEMENTS = new KeyOf(ElementConnection.class, "FLAG_CONNECTION_ELEMENTS");
+
+    public interface Connection<T> {
+        T getFirst();
+        T getSecond();
+    }
+
+    private static class Conn<T> implements Connection<T> {
+        private final T first;
+        private final T second;
+        public Conn(T first, T second) {
+            this.first = first;
+            this.second = second;
+        }
+        @Override
+        public T getFirst() {
+            return first;
+        }
+        @Override
+        public T getSecond() {
+            return second;
+        }
+    }
+    private static class ElementConnection extends Conn<IElement> {
+        public ElementConnection(IElement first, IElement second) {
+            super(first, second);
+            if (first == null)
+                throw new IllegalArgumentException("first is null");
+            if (second == null)
+                throw new IllegalArgumentException("second is null");
+        }
+    }
+    private static class DataConnection extends Conn<Object> {
+        public DataConnection(Object first, Object second) {
+            super(first, second);
+            if (first == null)
+                throw new IllegalArgumentException("first is null");
+            // Second may be null to indicate "not-connected"
+        }
+        public boolean isConnected() {
+            return getSecond() != null;
+        }
+    }
+
+    public static final FlagHandler  FLAG_HANDLER = new FlagHandler() {
+
+        private static final long serialVersionUID = -4258875745321808416L;
+
+        @Override
+        public Type getType(IElement e) {
+            return FlagClass.getType(e);
+        }
+
+        @Override
+        public void setType(IElement e, Type type) {
+            e.setHint(KEY_FLAG_TYPE, type);
+        }
+
+        @Override
+        public boolean isExternal(IElement e) {
+            return Boolean.TRUE.equals(e.getHint(KEY_EXTERNAL));
+        }
+
+        @Override
+        public void setExternal(IElement e, boolean external) {
+            e.setHint(KEY_EXTERNAL, Boolean.valueOf(external));
+        }
+
+        @Override
+        public Connection<IElement> getConnection(IElement e) {
+            return e.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
+        }
+
+        @Override
+        public Connection<Object> getConnectionData(IElement e) {
+            DataConnection dc = e.getHint(KEY_FLAG_CONNECTION_DATA);
+            return (dc != null && dc.isConnected()) ? dc : null;
+        }
+
+        @Override
+        public void connect(IElement e1, IElement e2) {
+            assert e1 != null && e2 != null;
+
+            ElementConnection ce = new ElementConnection(e1, e2);
+            e1.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
+            e2.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
+        }
+
+        @Override
+        public void connectData(IElement e1, Object o1, Object o2) {
+            e1.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
+            e1.setHint(KEY_FLAG_CONNECTION_DATA, new DataConnection(o1, o2));
+        }
+
+        @Override
+        public void disconnect(IElement local) {
+            assert local != null;
+            local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
+            DataConnection c = (DataConnection) local.removeHint(KEY_FLAG_CONNECTION_DATA);
+            if (c != null) {
+                IElement remote = otherElement(local, c);
+                if (remote != null) {
+                    local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
+                    remote.removeHint(KEY_FLAG_CONNECTION_DATA);
+                }
+            }
+        }
+
+        @Override
+        public boolean isWithinDiagram(IDiagram d, Connection<?> c) {
+            assert d != null;
+            assert c != null;
+            if (c instanceof DataConnection)
+                return bothOnDiagram(d, (DataConnection) c);
+            if (c instanceof ElementConnection)
+                return bothOnDiagram(d, (ElementConnection) c);
+            return false;
+        }
+
+        @Override
+        public IElement getCorrespondence(IElement end) {
+            assert end != null;
+            DataConnection dc = (DataConnection) end.getHint(KEY_FLAG_CONNECTION_DATA);
+            if (dc != null && dc.isConnected())
+                return otherElement(end, dc);
+            ElementConnection ec = (ElementConnection) end.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
+            if (ec != null)
+                return otherElement(end, ec);
+            return null;
+        }
+
+        boolean bothOnDiagram(IDiagram d, DataConnection c) {
+            if (!c.isConnected())
+                return false;
+
+            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
+            IElement eout = dem.getElement(d, c.getFirst());
+            IElement ein = dem.getElement(d, c.getSecond());
+            return eout != null && ein != null;
+        }
+
+        boolean bothOnDiagram(IDiagram d, ElementConnection c) {
+            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
+            Object o1 = dem.getData(d, c.getFirst());
+            Object o2 = dem.getData(d, c.getSecond());
+            return o1 != null && o2 != null;
+        }
+
+        public IElement otherElement(IElement e, DataConnection c) {
+            if (!c.isConnected())
+                return null;
+
+            IDiagram d = ElementUtils.peekDiagram(e);
+            if (d == null)
+                return null;
+
+            DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
+            Object o = dem.getData(d, e);
+            if (c.getFirst().equals(o))
+                return dem.getElement(d, c.getSecond());
+            if (c.getSecond().equals(o))
+                return dem.getElement(d, c.getFirst());
+            throw new IllegalArgumentException("specified object '" + o + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
+        }
+
+        public IElement otherElement(IElement e, ElementConnection c) {
+            IElement a = c.getFirst();
+            IElement b = c.getSecond();
+            if (e == a)
+                return b;
+            if (e == b)
+                return a;
+            throw new IllegalArgumentException("specified element '" + e + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
+        }
+    };
+
+    static final Shape staticShape;
+
+    static {
+        Path2D path = new Path2D.Double();
+        staticShape = path;
+        createFlagShape(path, Type.In, Mode.External, DEFAULT_WIDTH, DEFAULT_HEIGHT, getBeakLength(DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE));
+    }
+
+    public static final BasicStroke         STROKE                = new BasicStroke(0.15f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
+    static final Image               DEFAULT_IMAGE         = new ShapeImage(staticShape, null, STROKE);
+    static final StaticSymbolImpl    DEFAULT_STATIC_SYMBOL = new StaticSymbolImpl(DEFAULT_IMAGE);
+    static final FlagSize            DEFAULT_FLAG_SIZE     = new FlagSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE);
+    static final Initializer         DEFAULT_INITIALIZER   = new Initializer(Type.In, Mode.External);
+
+    public static final ElementClass FLAGCLASS =
+        ElementClass.compile(
+                DEFAULT_INITIALIZER,
+                FLAG_HANDLER,
+                DefaultTransform.INSTANCE,
+                DEFAULT_FLAG_SIZE,
+                BorderColorImpl.BLACK,
+                FillColorImpl.WHITE,
+                TextImpl.INSTANCE,
+                FlagTerminalTopology.DEFAULT,
+                FlagSceneGraph.INSTANCE,
+                DEFAULT_STATIC_SYMBOL
+        ).setId(FlagClass.class.getSimpleName());
+
+    public static ElementClass create(Terminal terminal) {
+        return ElementClass.compile(
+                DEFAULT_INITIALIZER,
+                FLAG_HANDLER,
+                DefaultTransform.INSTANCE,
+                DEFAULT_FLAG_SIZE,
+                BorderColorImpl.BLACK,
+                FillColorImpl.WHITE,
+                TextImpl.INSTANCE,
+                new FlagTerminalTopology(terminal),
+                FlagSceneGraph.INSTANCE,
+                DEFAULT_STATIC_SYMBOL,
+                SimpleElementLayers.INSTANCE
+        ).setId(FlagClass.class.getSimpleName());
+    }
+
+    public static ElementClass create(Terminal terminal, SceneGraph scn) {
+        return ElementClass.compile(
+                DEFAULT_INITIALIZER,
+                FLAG_HANDLER,
+                DefaultTransform.INSTANCE,
+                DEFAULT_FLAG_SIZE,
+                BorderColorImpl.BLACK,
+                FillColorImpl.WHITE,
+                TextImpl.INSTANCE,
+                new FlagTerminalTopology(terminal),
+                scn,
+                DEFAULT_STATIC_SYMBOL,
+                SimpleElementLayers.INSTANCE
+        ).setId(FlagClass.class.getSimpleName());
+    }
+
+    static class Initializer implements LifeCycle {
+        private static final long serialVersionUID = 4404942036933073584L;
+
+        private final Type type;
+        private final Mode mode;
+
+        Initializer(Type type, Mode mode) {
+            assert type != null;
+            assert mode != null;
+            this.type = type;
+            this.mode = mode;
+        }
+
+        @Override
+        public void onElementActivated(IDiagram d, IElement e) {
+        }
+
+        @Override
+        public void onElementCreated(IElement e) {
+            e.setHint(KEY_FLAG_TYPE, type);
+            e.setHint(KEY_FLAG_MODE, mode);
+            //e.setHint(ElementHints.KEY_COMPOSITE, AlphaComposite.SrcOver.derive(0.5f));
+        }
+
+        @Override
+        public void onElementDeactivated(IDiagram d, IElement e) {
+        }
+
+        @Override
+        public void onElementDestroyed(IElement e) {
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + mode.hashCode();
+            result = prime * result + type.hashCode();
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Initializer other = (Initializer) obj;
+            if (!mode.equals(other.mode))
+                return false;
+            if (!type.equals(other.type))
+                return false;
+            return true;
+        }
+    };
+
+    public static Path2D createFlagShape(Path2D path, Type type, Mode mode, double width, double height, double beakLength) {
+        double hh = height / 2;        
+        path.reset();
+        switch (type) {
+            case Out:
+                if (mode instanceof External) {
+                    path.moveTo(0, hh);
+                    path.lineTo(width, hh);
+                    path.lineTo(width+beakLength, 0);
+                    path.lineTo(width, -hh);
+                    path.lineTo(0, -hh);
+                    path.closePath();
+                    path.moveTo(width, hh);
+                    path.lineTo(width, -hh);    
+                    int count = ((External)mode).count;
+                    if(count > 1) {
+                        double shadow=hh*0.25;
+                        double ix = beakLength 
+                                - 0.5*shadow*(1.0 + beakLength/hh);
+                        double iy = hh * (ix / beakLength - 1.0);
+                        for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
+                            double dis = sid*shadow;
+                            path.moveTo(dis, hh+dis-shadow);
+                            path.lineTo(dis, hh+dis);
+                            path.lineTo(dis+width, hh+dis);
+                            path.lineTo(dis+width+beakLength, dis);
+                            path.lineTo(width + ix + dis, iy + dis);
+                        }
+                    } else {
+                        double left = 0;
+                        double right = width - 0;
+                        if (left < right) {
+                            path.moveTo(left, 0);
+                            path.lineTo(right, 0);
+                        }
+                    }
+                } else if (mode == Mode.Internal) {
+                    path.moveTo(0, hh);
+                    path.lineTo(beakLength, 0);
+                    path.lineTo(0, -hh);
+                    path.closePath();
+                }
+                break;
+            case In:
+                path.moveTo(0, 0);
+                if (mode instanceof External) {
+                    path.lineTo(-beakLength, -hh);
+                    path.lineTo(-width-beakLength, -hh);
+                    path.lineTo(-width-beakLength, hh);
+                    path.lineTo(-beakLength, hh);
+                    path.closePath();
+                    path.moveTo(-beakLength, -hh);
+                    path.lineTo(-beakLength, hh);  
+                    int count = ((External)mode).count;
+                    if(count > 1) {
+                        double shadow=hh*0.25;
+                        double ix = beakLength 
+                                - 0.5*shadow*(1.0 + beakLength/hh);
+                        double iy = hh * (ix / beakLength - 1.0);
+                        double xDisp = -width-beakLength;
+                        for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
+                            double dis = sid*shadow;
+                            path.moveTo(xDisp+dis, hh+dis-shadow);
+                            path.lineTo(xDisp+dis, hh+dis);
+                            path.lineTo(xDisp+dis+width, hh+dis);
+                            path.lineTo(xDisp+dis+width+beakLength, dis);                            
+                            path.lineTo(xDisp+width + ix + dis, iy + dis);
+                        }
+                    } else {
+                        double left = -width-beakLength+0;
+                        double right = -beakLength-0;       
+                        if (left < right) {
+                            path.moveTo(left, 0);
+                            path.lineTo(right, 0);
+                        }
+                    }
+                } else if (mode == Mode.Internal) {
+                    path.lineTo(-beakLength, -hh);
+                    path.lineTo(-beakLength, hh);
+                    path.closePath();
+                }
+                break;
+        }
+        return path;
+    }
+
+    public static Path2D createFlagShape(IElement e) {
+        Type type = getType(e);
+        Mode mode = e.getHint(KEY_FLAG_MODE);
+        double width = e.getHint(KEY_FLAG_WIDTH);
+        double height = e.getHint(KEY_FLAG_HEIGHT);
+        double beakLength = getBeakLength(e);
+        Path2D path = new Path2D.Double();
+        createFlagShape(path, type, mode, width, height, beakLength);
+        return path;
+    }
+
+    static class FlagSize implements InternalSize, Outline, LifeCycle {
+
+        private static final long serialVersionUID = 829379327756475944L;
+
+        private final double length;
+        private final double thickness;
+        private final double beakAngle;
+
+        public FlagSize(double length, double thickness, double beakAngle) {
+            this.length = length;
+            this.thickness = thickness;
+            this.beakAngle = beakAngle;
+        }
+
+        @Override
+        public Shape getElementShape(IElement e) {
+            Shape shape = e.getHint(KEY_SHAPE);
+            if (shape != null)
+                return shape;
+            return createFlagShape(e);
+        }
+
+        @Override
+        public Rectangle2D getBounds(IElement e, Rectangle2D size) {
+            if (size == null)
+                size = new Rectangle2D.Double();
+            Shape shape = getElementShape(e);
+            size.setFrame(shape.getBounds2D());
+            return size;
+        }
+
+        @Override
+        public void onElementActivated(IDiagram d, IElement e) {
+        }
+
+        @Override
+        public void onElementCreated(IElement e) {
+            e.setHint(KEY_FLAG_WIDTH, length);
+            e.setHint(KEY_FLAG_HEIGHT, thickness);
+            e.setHint(KEY_FLAG_BEAK_ANGLE, beakAngle);
+        }
+
+        @Override
+        public void onElementDeactivated(IDiagram d, IElement e) {
+        }
+
+        @Override
+        public void onElementDestroyed(IElement e) {
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            long temp;
+            temp = Double.doubleToLongBits(beakAngle);
+            result = prime * result + (int) (temp ^ (temp >>> 32));
+            temp = Double.doubleToLongBits(length);
+            result = prime * result + (int) (temp ^ (temp >>> 32));
+            temp = Double.doubleToLongBits(thickness);
+            result = prime * result + (int) (temp ^ (temp >>> 32));
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            FlagSize other = (FlagSize) obj;
+            if (Double.doubleToLongBits(beakAngle) != Double.doubleToLongBits(other.beakAngle))
+                return false;
+            if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length))
+                return false;
+            if (Double.doubleToLongBits(thickness) != Double.doubleToLongBits(other.thickness))
+                return false;
+            return true;
+        }
+    }
+
+    static class FlagSceneGraph implements SceneGraph {
+        private static final long serialVersionUID = 35208146123929197L;
+
+        public static final FlagSceneGraph INSTANCE = new FlagSceneGraph();
+
+        @Override
+        public void cleanup(IElement e) {
+            ElementUtils.removePossibleNode(e, KEY_SG_NODE);
+        }
+
+        @Override
+        public void init(IElement e, G2DParentNode parent) {
+            Color fc = ElementUtils.getFillColor(e, Color.WHITE);
+            Color bc = ElementUtils.getBorderColor(e, Color.BLACK);
+            Color tc = ElementUtils.getTextColor(e, Color.BLACK);
+
+            Outline outline = e.getElementClass().getSingleItem(Outline.class);
+            Shape shape = outline.getElementShape(e);
+            Type type = getType(e);
+            double dir = getDirection(e);
+            double width = e.getHint(KEY_FLAG_WIDTH);
+            double height = e.getHint(KEY_FLAG_HEIGHT);
+            double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
+
+            String[] flagText = e.getHint(KEY_FLAG_TEXT);
+            if (flagText == null) {
+                // fallback option.
+                Text t = e.getElementClass().getAtMostOneItemOfClass(Text.class);
+                if (t != null) {
+                    String text = t.getText(e);
+                    if (text != null)
+                        flagText = new String[] { text };
+                }
+            }
+
+            // DEBUG TEXT
+            //flagText = new String[] { String.format("%3.1f", dir) + " deg", "FOO"};
+
+            Rectangle2D textArea = e.getHint(KEY_FLAG_TEXT_AREA);
+            if (textArea == null) {
+                double beakLength = getBeakLength(height, beakAngle);
+                textArea = type == Type.In
+                ? new Rectangle2D.Double(-width-beakLength, -height*0.5, width, height)
+                : new Rectangle2D.Double(0, -height*0.5, width, height);
+            }
+
+            Alignment horizAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_HORIZONTAL_ALIGN, Alignment.LEADING);
+            Alignment vertAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_VERTICAL_ALIGN, Alignment.CENTER);
+
+            FlagNode flag = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, ElementUtils.generateNodeId(e), FlagNode.class);
+            flag.init(shape,
+                    flagText,
+                    STROKE,
+                    bc,
+                    fc,
+                    tc,
+                    (float) width,
+                    (float) height,
+                    (float) dir,
+                    (float) beakAngle,
+                    textArea,
+                    horizAlign.ordinal(),
+                    vertAlign.ordinal());
+            AffineTransform at = ElementUtils.getTransform(e);
+            if(at != null) flag.setTransform(at);
+
+        }
+    }
+
+    static class TerminalPoint implements Terminal {
+    }
+
+    public static class FlagTerminalTopology implements TerminalTopology, TerminalLayout {
+        private static final long    serialVersionUID = -4194634598346105458L;
+
+        public static final Terminal             DEFAULT_T0       = new TerminalPoint();
+        public static final FlagTerminalTopology DEFAULT          = new FlagTerminalTopology(DEFAULT_T0);
+
+        final Terminal T0;
+
+        public FlagTerminalTopology(Terminal t) {
+            this.T0 = t;
+        }
+
+        @Override
+        public void getTerminals(IElement e, Collection<Terminal> result) {
+            result.add(T0);
+        }
+
+        @Override
+        public AffineTransform getTerminalPosition(IElement node, Terminal t) {
+            if (t == T0) {
+                return new AffineTransform();
+            }
+            return null;
+        }
+
+        @Override
+        public boolean getTerminalDirection(IElement node, Terminal t, DirectionSet directions) {
+            Type type = getType(node);
+            double d = getDirection(node);
+            if (t == T0) {
+                switch (type) {
+                    case In: directions.add(d); break;
+                    case Out: directions.add(Math.IEEEremainder(d + 180.0, 360.0)); break;
+                }
+                //System.out.println("directions T0: " + Arrays.toString(directions.toArray()));
+                return true;
+            }
+            return false;
+        }
+
+//        static final Path2D terminalShape;
+//
+//        static {
+//            double s = .5;
+//            Path2D p = new Path2D.Double();
+//            p.moveTo(s, s);
+//            p.lineTo(s, -s);
+//            p.lineTo(-s, -s);
+//            p.lineTo(-s, s);
+//            p.closePath();
+//            terminalShape = p;
+//        }
+
+        @Override
+        public Shape getTerminalShape(IElement node, Terminal t) {
+            //return terminalShape;
+            //return null;
+            // For each terminal, return the whole shape of the element.
+            return ElementUtils.getElementShapeOrBounds(node);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((T0 == null) ? 0 : T0.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            FlagTerminalTopology other = (FlagTerminalTopology) obj;
+            if (T0 == null) {
+                if (other.T0 != null)
+                    return false;
+            } else if (!T0.equals(other.T0))
+                return false;
+            return true;
+        }
+    }
+
+    public static AffineTransform getTransform(IElement e) {
+        AffineTransform at = ElementUtils.getTransform(e);
+        if (at == null)
+            return new AffineTransform();
+        return at;
+    }
+
+    public static double getDirection(IElement e) {
+        Rotate rotate = e.getElementClass().getAtMostOneItemOfClass(Rotate.class);
+        if (rotate != null) {
+            return rotate.getAngle(e);
+        }
+        return 0.0;
+    }
+
+    public static Type getType(IElement e) {
+        Type t = e.getHint(KEY_FLAG_TYPE);
+        return t != null ? t : Type.In;
+    }
+
+    public static Mode getMode(IElement e) {
+        Mode m = e.getHint(KEY_FLAG_MODE);
+        return m != null ? m : Mode.External;
+    }
+
+    public static double getBeakLength(IElement e) {
+        double height = e.getHint(KEY_FLAG_HEIGHT);
+        double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
+        beakAngle = Math.min(180, Math.max(10, beakAngle));
+        return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
+    }
+
+    public static double getBeakLength(double height, double beakAngle) {
+        beakAngle = Math.min(180, Math.max(10, beakAngle));
+        return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
+    }
+
+}