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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.element;
\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
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
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
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
41 * The minimum requirement is implementation for the following interfaces:
\r
45 * Typical element class has the following implementations:
\r
50 * @see InternalSize or @see Scale
\r
53 * Interactive element classes have implementation to:
\r
54 * @see HandleMouseEvent
\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
66 * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images).
\r
68 * @author Toni Kalajainen
\r
70 public final class ElementClass extends AbstractHandlerClass<ElementHandler> {
\r
72 private static final long serialVersionUID = -446421782709451743L;
\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
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
84 private static final Class<?>[] REQUIRED_HANDLERS =
\r
85 new Class<?>[] {Transform.class, InternalSize.class};
\r
87 private String id = "";
\r
90 * Compile new element class from a set of handler
\r
91 * @param contributions
\r
94 public static ElementClass compile(Collection<ElementHandler> contributions)
\r
96 assertClassValid(contributions);
\r
97 return new ElementClass(contributions);
\r
100 public static ElementClass compile(Collection<ElementHandler> contributions, boolean check)
\r
102 if(check) assertClassValid(contributions);
\r
103 return new ElementClass(contributions, check);
\r
107 * Compile new element class from a set of handler
\r
108 * @param contributions
\r
111 public static ElementClass compile(ElementHandler... contributions)
\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
118 return new ElementClass(al);
\r
121 ElementClass(Collection<ElementHandler> contributions) {
\r
122 super(contributions);
\r
123 assertClassValid(contributions);
\r
126 ElementClass(Collection<ElementHandler> contributions, boolean check) {
\r
127 super(contributions);
\r
128 if(check) assertClassValid(contributions);
\r
132 * Validates that handler is valid.
\r
134 * @param contributions
\r
137 public static void assertClassValid(Collection<ElementHandler> contributions)
\r
139 // 1. Verify requirements
\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
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
154 // 2.2. Verify singletons are implemented only once
\r
155 for (Class<ElementHandler> ehc : implementedHandlerInterfaces)
\r
157 if (!_isSingletonHandler(ehc)) continue;
\r
158 int implementationCount = 0;
\r
159 for (ElementHandler eh : contributions)
\r
161 if (!ehc.isInstance(eh)) continue;
\r
162 implementationCount++;
\r
164 if (implementationCount>1)
\r
165 throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions);
\r
169 private static boolean _isSingletonHandler(Class<ElementHandler> elementHandlerClass)
\r
171 Single s = elementHandlerClass.getAnnotation(Single.class);
\r
175 @SuppressWarnings("unchecked")
\r
176 private static void _traverseElementHandlerInterfaces(Class<?> clazz, Collection<Class<ElementHandler>> result)
\r
178 // Add implemented interfaces (that are inherited from ElementHandler)
\r
179 for (Class<?> inf : clazz.getInterfaces())
\r
181 if (!ElementHandler.class.isAssignableFrom(inf)) continue;
\r
182 result.add((Class<ElementHandler>)inf);
\r
186 Class<?> superType = clazz.getSuperclass();
\r
187 if (superType!=null)
\r
188 _traverseElementHandlerInterfaces(superType, result);
\r
192 public String toString() {
\r
196 StringBuilder sb = new StringBuilder();
\r
199 for (ElementHandler eh : super.getAll())
\r
201 if (i++>0) sb.append(", ");
\r
202 sb.append( eh.getClass().getSimpleName() );
\r
205 return sb.toString();
\r
208 public ElementClass newClassWith(ElementHandler... addedHandlers) {
\r
209 if (addedHandlers.length == 0)
\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
217 public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) {
\r
218 if (addedHandlers.length == 0)
\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
226 public ElementClass newClassWith(Collection<ElementHandler> addedHandlers) {
\r
227 if (addedHandlers.isEmpty())
\r
229 Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());
\r
230 newHandlers.addAll(addedHandlers);
\r
231 return ElementClass.compile(newHandlers).setId(id);
\r
234 public ElementClass setId(String id) {
\r
239 public String getId() {
\r