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