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