]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java
Merge commit 'bd5bc6e45f700e755b61bd112631796631330ecb'
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / element / ElementClass.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.element;\r
13 \r
14 import java.lang.annotation.ElementType;\r
15 import java.lang.annotation.Retention;\r
16 import java.lang.annotation.RetentionPolicy;\r
17 import java.lang.annotation.Target;\r
18 import java.util.ArrayList;\r
19 import java.util.Arrays;\r
20 import java.util.Collection;\r
21 import java.util.HashSet;\r
22 import java.util.Set;\r
23 \r
24 import org.simantics.g2d.diagram.impl.AbstractHandlerClass;\r
25 import org.simantics.g2d.element.handler.ElementHandler;\r
26 import org.simantics.g2d.element.handler.HandleMouseEvent;\r
27 import org.simantics.g2d.element.handler.InternalSize;\r
28 import org.simantics.g2d.element.handler.Move;\r
29 import org.simantics.g2d.element.handler.Rotate;\r
30 import org.simantics.g2d.element.handler.SceneGraph;\r
31 import org.simantics.g2d.element.handler.Transform;\r
32 import org.simantics.g2d.element.handler.Validator;\r
33 \r
34 /**\r
35  * ElementClass is a class of elements. It consists of element handlers.\r
36  * Each handler contributes functions to some aspects of the diagram.\r
37  * <p>\r
38  * Handlers are ordered. For instance, if there are multiple Paint handlers,\r
39  * the paint order is determined by the order of the handlers in the class.\r
40  * <p>\r
41  * The minimum requirement is implementation for the following interfaces:\r
42  *    @see Transform\r
43  *    @see InternalSize\r
44  * \r
45  * Typical element class has the following implementations:\r
46  *    @see Transform\r
47  *    @see SceneGraph\r
48  *    @see Move\r
49  *    @see Rotate\r
50  *    @see InternalSize or @see Scale\r
51  *    @see Selectable\r
52  * \r
53  * Interactive element classes have implementation to:\r
54  *    @see HandleMouseEvent\r
55  * \r
56  * See also:\r
57  *    @see Validator\r
58  * \r
59  * See package org.simantics.g2d.element.handler for handler interfaces.\r
60  * See package org.simantics.g2d.element.handler.impl for common implementations.\r
61  * Note that some implementations implement several interface. Also note that\r
62  * some handler interfaces may be implemented several times (e.g. Paint,\r
63  * Validator, ToolTip, Pick). In contrast, some handlers may have at most one\r
64  * implementation, such as Transform, Move, Rotate, Scale, Size.\r
65  * <p>\r
66  * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images).\r
67  * \r
68  * @author Toni Kalajainen\r
69  */\r
70 public final class ElementClass extends AbstractHandlerClass<ElementHandler> {\r
71 \r
72     private static final long serialVersionUID = -446421782709451743L;\r
73 \r
74     /** Handler with this annotation may have at most one implementation */\r
75     @Retention(RetentionPolicy.RUNTIME)\r
76     @Target(ElementType.TYPE)\r
77     public static @interface Single {}\r
78 \r
79     /** Handler with this annotation must be implemented (once or more) */\r
80     @Retention(RetentionPolicy.RUNTIME)\r
81     @Target(ElementType.TYPE)\r
82     public static @interface Required {}\r
83 \r
84     private static final Class<?>[] REQUIRED_HANDLERS =\r
85         new Class<?>[] {Transform.class, InternalSize.class};\r
86 \r
87     private String id = "";\r
88 \r
89     /**\r
90      * Compile new element class from a set of handler\r
91      * @param contributions\r
92      * @return\r
93      */\r
94     public static ElementClass compile(Collection<ElementHandler> contributions)\r
95     {\r
96         assertClassValid(contributions);\r
97         return new ElementClass(contributions);\r
98     }\r
99 \r
100     public static ElementClass compile(Collection<ElementHandler> contributions, boolean check)\r
101     {\r
102         if(check) assertClassValid(contributions);\r
103         return new ElementClass(contributions, check);\r
104     }\r
105 \r
106     /**\r
107      * Compile new element class from a set of handler\r
108      * @param contributions\r
109      * @return\r
110      */\r
111     public static ElementClass compile(ElementHandler... contributions)\r
112     {\r
113         if (contributions.length == 0)\r
114             return new ElementClass(Arrays.asList(contributions));\r
115         ArrayList<ElementHandler> al = new ArrayList<ElementHandler>(contributions.length);\r
116         for (ElementHandler eh : contributions)\r
117             al.add(eh);\r
118         return new ElementClass(al);\r
119     }\r
120 \r
121     ElementClass(Collection<ElementHandler> contributions) {\r
122         super(contributions);\r
123         assertClassValid(contributions);\r
124     }\r
125 \r
126     ElementClass(Collection<ElementHandler> contributions, boolean check) {\r
127         super(contributions);\r
128         if(check) assertClassValid(contributions);\r
129     }\r
130 \r
131     /**\r
132      * Validates that handler is valid.\r
133      * \r
134      * @param contributions\r
135      * @return\r
136      */\r
137     public static void assertClassValid(Collection<ElementHandler> contributions)\r
138     {\r
139         // 1. Verify requirements\r
140         nextRequirement:\r
141             for (Class<?> requiredClass : REQUIRED_HANDLERS) {\r
142                 for (ElementHandler eh : contributions)\r
143                     if (requiredClass.isInstance(eh))\r
144                         continue nextRequirement;\r
145                 throw new Error("Element class does not implement "+requiredClass.getName());\r
146             }\r
147 \r
148     // 2. Verify singletons\r
149     // 2.1. Collect implemented handlers\r
150     Set<Class<ElementHandler>> implementedHandlerInterfaces = new HashSet<Class<ElementHandler>>();\r
151     for (ElementHandler eh : contributions)\r
152         _traverseElementHandlerInterfaces(eh.getClass(), implementedHandlerInterfaces);\r
153 \r
154     // 2.2. Verify singletons are implemented only once\r
155     for (Class<ElementHandler> ehc : implementedHandlerInterfaces)\r
156     {\r
157         if (!_isSingletonHandler(ehc)) continue;\r
158         int implementationCount = 0;\r
159         for (ElementHandler eh : contributions)\r
160         {\r
161             if (!ehc.isInstance(eh)) continue;\r
162             implementationCount++;\r
163         }\r
164         if (implementationCount>1)\r
165             throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions);\r
166     }\r
167     }\r
168 \r
169     private static boolean _isSingletonHandler(Class<ElementHandler> elementHandlerClass)\r
170     {\r
171         Single s = elementHandlerClass.getAnnotation(Single.class);\r
172         return s != null;\r
173     }\r
174 \r
175     @SuppressWarnings("unchecked")\r
176     private static void _traverseElementHandlerInterfaces(Class<?> clazz, Collection<Class<ElementHandler>> result)\r
177     {\r
178         // Add implemented interfaces (that are inherited from ElementHandler)\r
179         for (Class<?> inf : clazz.getInterfaces())\r
180         {\r
181             if (!ElementHandler.class.isAssignableFrom(inf)) continue;\r
182             result.add((Class<ElementHandler>)inf);\r
183         }\r
184 \r
185         // Traverse parent\r
186         Class<?> superType = clazz.getSuperclass();\r
187         if (superType!=null)\r
188             _traverseElementHandlerInterfaces(superType, result);\r
189     }\r
190 \r
191     @Override\r
192     public String toString() {\r
193         if (!id.isEmpty())\r
194             return id;\r
195 \r
196         StringBuilder sb = new StringBuilder();\r
197         sb.append("[");\r
198         int i=0;\r
199         for (ElementHandler eh : super.getAll())\r
200         {\r
201             if (i++>0) sb.append(", ");\r
202             sb.append( eh.getClass().getSimpleName() );\r
203         }\r
204         sb.append("]");\r
205         return sb.toString();\r
206     }\r
207 \r
208     public ElementClass newClassWith(ElementHandler... addedHandlers) {\r
209         if (addedHandlers.length == 0)\r
210             return this;\r
211         Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
212         for (ElementHandler h : addedHandlers)\r
213             newHandlers.add(h);\r
214         return ElementClass.compile(newHandlers).setId(id);\r
215     }\r
216 \r
217     public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) {\r
218         if (addedHandlers.length == 0)\r
219             return this;\r
220         Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
221         for (ElementHandler h : addedHandlers)\r
222             newHandlers.add(h);\r
223         return ElementClass.compile(newHandlers, check).setId(id);\r
224     }\r
225 \r
226     public ElementClass newClassWith(Collection<ElementHandler> addedHandlers) {\r
227         if (addedHandlers.isEmpty())\r
228             return this;\r
229         Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());\r
230         newHandlers.addAll(addedHandlers);\r
231         return ElementClass.compile(newHandlers).setId(id);\r
232     }\r
233 \r
234     public ElementClass setId(String id) {\r
235         this.id = id;\r
236         return this;\r
237     }\r
238 \r
239     public String getId() {\r
240         return id;\r
241     }\r
242 \r
243 }\r