]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / element / ElementClass.java
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java
new file mode 100644 (file)
index 0000000..124a26c
--- /dev/null
@@ -0,0 +1,243 @@
+/*******************************************************************************\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.element;\r
+\r
+import java.lang.annotation.ElementType;\r
+import java.lang.annotation.Retention;\r
+import java.lang.annotation.RetentionPolicy;\r
+import java.lang.annotation.Target;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.g2d.diagram.impl.AbstractHandlerClass;\r
+import org.simantics.g2d.element.handler.ElementHandler;\r
+import org.simantics.g2d.element.handler.HandleMouseEvent;\r
+import org.simantics.g2d.element.handler.InternalSize;\r
+import org.simantics.g2d.element.handler.Move;\r
+import org.simantics.g2d.element.handler.Rotate;\r
+import org.simantics.g2d.element.handler.SceneGraph;\r
+import org.simantics.g2d.element.handler.Transform;\r
+import org.simantics.g2d.element.handler.Validator;\r
+\r
+/**\r
+ * ElementClass is a class of elements. It consists of element handlers.\r
+ * Each handler contributes functions to some aspects of the diagram.\r
+ * <p>\r
+ * Handlers are ordered. For instance, if there are multiple Paint handlers,\r
+ * the paint order is determined by the order of the handlers in the class.\r
+ * <p>\r
+ * The minimum requirement is implementation for the following interfaces:\r
+ *    @see Transform\r
+ *    @see InternalSize\r
+ * \r
+ * Typical element class has the following implementations:\r
+ *    @see Transform\r
+ *    @see SceneGraph\r
+ *    @see Move\r
+ *    @see Rotate\r
+ *    @see InternalSize or @see Scale\r
+ *    @see Selectable\r
+ * \r
+ * Interactive element classes have implementation to:\r
+ *    @see HandleMouseEvent\r
+ * \r
+ * See also:\r
+ *    @see Validator\r
+ * \r
+ * See package org.simantics.g2d.element.handler for handler interfaces.\r
+ * See package org.simantics.g2d.element.handler.impl for common implementations.\r
+ * Note that some implementations implement several interface. Also note that\r
+ * some handler interfaces may be implemented several times (e.g. Paint,\r
+ * Validator, ToolTip, Pick). In contrast, some handlers may have at most one\r
+ * implementation, such as Transform, Move, Rotate, Scale, Size.\r
+ * <p>\r
+ * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images).\r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public final class ElementClass extends AbstractHandlerClass<ElementHandler> {\r
+\r
+    private static final long serialVersionUID = -446421782709451743L;\r
+\r
+    /** Handler with this annotation may have at most one implementation */\r
+    @Retention(RetentionPolicy.RUNTIME)\r
+    @Target(ElementType.TYPE)\r
+    public static @interface Single {}\r
+\r
+    /** Handler with this annotation must be implemented (once or more) */\r
+    @Retention(RetentionPolicy.RUNTIME)\r
+    @Target(ElementType.TYPE)\r
+    public static @interface Required {}\r
+\r
+    private static final Class<?>[] REQUIRED_HANDLERS =\r
+        new Class<?>[] {Transform.class, InternalSize.class};\r
+\r
+    private String id = "";\r
+\r
+    /**\r
+     * Compile new element class from a set of handler\r
+     * @param contributions\r
+     * @return\r
+     */\r
+    public static ElementClass compile(Collection<ElementHandler> contributions)\r
+    {\r
+        assertClassValid(contributions);\r
+        return new ElementClass(contributions);\r
+    }\r
+\r
+    public static ElementClass compile(Collection<ElementHandler> contributions, boolean check)\r
+    {\r
+        if(check) assertClassValid(contributions);\r
+        return new ElementClass(contributions, check);\r
+    }\r
+\r
+    /**\r
+     * Compile new element class from a set of handler\r
+     * @param contributions\r
+     * @return\r
+     */\r
+    public static ElementClass compile(ElementHandler... contributions)\r
+    {\r
+        if (contributions.length == 0)\r
+            return new ElementClass(Arrays.asList(contributions));\r
+        ArrayList<ElementHandler> al = new ArrayList<ElementHandler>(contributions.length);\r
+        for (ElementHandler eh : contributions)\r
+            al.add(eh);\r
+        return new ElementClass(al);\r
+    }\r
+\r
+    ElementClass(Collection<ElementHandler> contributions) {\r
+        super(contributions);\r
+        assertClassValid(contributions);\r
+    }\r
+\r
+    ElementClass(Collection<ElementHandler> contributions, boolean check) {\r
+        super(contributions);\r
+        if(check) assertClassValid(contributions);\r
+    }\r
+\r
+    /**\r
+     * Validates that handler is valid.\r
+     * \r
+     * @param contributions\r
+     * @return\r
+     */\r
+    public static void assertClassValid(Collection<ElementHandler> contributions)\r
+    {\r
+        // 1. Verify requirements\r
+        nextRequirement:\r
+            for (Class<?> requiredClass : REQUIRED_HANDLERS) {\r
+                for (ElementHandler eh : contributions)\r
+                    if (requiredClass.isInstance(eh))\r
+                        continue nextRequirement;\r
+                throw new Error("Element class does not implement "+requiredClass.getName());\r
+            }\r
+\r
+    // 2. Verify singletons\r
+    // 2.1. Collect implemented handlers\r
+    Set<Class<ElementHandler>> implementedHandlerInterfaces = new HashSet<Class<ElementHandler>>();\r
+    for (ElementHandler eh : contributions)\r
+        _traverseElementHandlerInterfaces(eh.getClass(), implementedHandlerInterfaces);\r
+\r
+    // 2.2. Verify singletons are implemented only once\r
+    for (Class<ElementHandler> ehc : implementedHandlerInterfaces)\r
+    {\r
+        if (!_isSingletonHandler(ehc)) continue;\r
+        int implementationCount = 0;\r
+        for (ElementHandler eh : contributions)\r
+        {\r
+            if (!ehc.isInstance(eh)) continue;\r
+            implementationCount++;\r
+        }\r
+        if (implementationCount>1)\r
+            throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions);\r
+    }\r
+    }\r
+\r
+    private static boolean _isSingletonHandler(Class<ElementHandler> elementHandlerClass)\r
+    {\r
+        Single s = elementHandlerClass.getAnnotation(Single.class);\r
+        return s != null;\r
+    }\r
+\r
+    @SuppressWarnings("unchecked")\r
+    private static void _traverseElementHandlerInterfaces(Class<?> clazz, Collection<Class<ElementHandler>> result)\r
+    {\r
+        // Add implemented interfaces (that are inherited from ElementHandler)\r
+        for (Class<?> inf : clazz.getInterfaces())\r
+        {\r
+            if (!ElementHandler.class.isAssignableFrom(inf)) continue;\r
+            result.add((Class<ElementHandler>)inf);\r
+        }\r
+\r
+        // Traverse parent\r
+        Class<?> superType = clazz.getSuperclass();\r
+        if (superType!=null)\r
+            _traverseElementHandlerInterfaces(superType, result);\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+        if (!id.isEmpty())\r
+            return id;\r
+\r
+        StringBuilder sb = new StringBuilder();\r
+        sb.append("[");\r
+        int i=0;\r
+        for (ElementHandler eh : super.getAll())\r
+        {\r
+            if (i++>0) sb.append(", ");\r
+            sb.append( eh.getClass().getSimpleName() );\r
+        }\r
+        sb.append("]");\r
+        return sb.toString();\r
+    }\r
+\r
+    public ElementClass newClassWith(ElementHandler... addedHandlers) {\r
+        if (addedHandlers.length == 0)\r
+            return this;\r
+        Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
+        for (ElementHandler h : addedHandlers)\r
+            newHandlers.add(h);\r
+        return ElementClass.compile(newHandlers).setId(id);\r
+    }\r
+\r
+    public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) {\r
+        if (addedHandlers.length == 0)\r
+            return this;\r
+        Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
+        for (ElementHandler h : addedHandlers)\r
+            newHandlers.add(h);\r
+        return ElementClass.compile(newHandlers, check).setId(id);\r
+    }\r
+\r
+    public ElementClass newClassWith(Collection<ElementHandler> addedHandlers) {\r
+        if (addedHandlers.isEmpty())\r
+            return this;\r
+        Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
+        newHandlers.addAll(addedHandlers);\r
+        return ElementClass.compile(newHandlers).setId(id);\r
+    }\r
+\r
+    public ElementClass setId(String id) {\r
+        this.id = id;\r
+        return this;\r
+    }\r
+\r
+    public String getId() {\r
+        return id;\r
+    }\r
+\r
+}\r