Skip to content

Package: ECPObserverBusImpl

ECPObserverBusImpl

nameinstructionbranchcomplexitylinemethod
ECPObserverBusImpl()
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
addObserver(ECPObserver, Class)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
createProxy(Class, boolean)
M: 0 C: 23
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getAllInterfaces(Class, Set)
M: 0 C: 32
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
getClasses(Class, HashSet)
M: 4 C: 39
91%
M: 2 C: 8
80%
M: 2 C: 4
67%
M: 1 C: 6
86%
M: 0 C: 1
100%
getObserverByClass(Class)
M: 0 C: 15
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
getObserverInterfaces(ECPObserver)
M: 0 C: 17
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
initObserverList(Class)
M: 0 C: 20
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
isPrioritizedObserver(Class, Method)
M: 32 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
notify(Class)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
notify(Class, boolean)
M: 2 C: 7
78%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 2
67%
M: 0 C: 1
100%
register(ECPObserver)
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
register(ECPObserver, Class[])
M: 0 C: 25
100%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 0 C: 4
100%
M: 0 C: 1
100%
removeObserver(ECPObserver, Class)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
sortObservers(List)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
unregister(ECPObserver)
M: 0 C: 7
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
unregister(ECPObserver, Class[])
M: 0 C: 25
100%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 0 C: 4
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2012 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * wesendon - initial API and implementation
13: *
14: *******************************************************************************/
15:
16: package org.eclipse.emf.ecp.internal.core.util.observer;
17:
18: import java.lang.reflect.InvocationHandler;
19: import java.lang.reflect.InvocationTargetException;
20: import java.lang.reflect.Method;
21: import java.lang.reflect.Proxy;
22: import java.util.ArrayList;
23: import java.util.Arrays;
24: import java.util.Collection;
25: import java.util.Collections;
26: import java.util.Comparator;
27: import java.util.HashMap;
28: import java.util.HashSet;
29: import java.util.List;
30: import java.util.Set;
31:
32: import org.eclipse.emf.ecp.core.util.observer.ECPObserver;
33: import org.eclipse.emf.ecp.core.util.observer.ECPObserverBus;
34:
35: /**
36: * This is a universal observer bus. This class follows the publish/subscribe pattern, it is a central dispatcher for
37: * observers and makes use of generics in order to allow type safety. It can be used as singleton or be injected through
38: * DI.
39: * Observers have to implement the {@link ECPObserver} interface, which is only used as a marker. Future use of
40: * Annotations is possible.
41: * by using {@link #notify(Class)} (e.g. <code>bus.notify(MyObserver.class).myObserverMethod()</code>) all registered
42: * Observers are notified.
43: * This is implemented by using the java {@link Proxy} class. By calling {@link #notify(Class)} a proxy is returned,
44: * which then calls all registered observers.
45: * The proxy can also be casted into {@link ECPObserverCall}, which allows to access all results by the different
46: * observers.
47: *
48: *
49: * Example code:
50: *
51: * <pre>
52: * // A is ESObserver
53: * A a = new A() {
54: *
55: *         public void foo() {
56: *                 System.out.println("A says: go!");
57: *         }
58: * };
59: *
60: * // B extends A and is IObserver
61: * B b = new B() {
62: *
63: *         public void say(String ja) {
64: *                 System.out.println("B says: " + ja);
65: *         }
66: *
67: *         public void foo() {
68: *                 System.out.println("B says: h??");
69: *         }
70: * };
71: *
72: * // B is registered first
73: * ObserverBus.register(b);
74: * ObserverBus.register(a);
75: *
76: * ObserverBus.notify(A.class).foo();
77: *
78: * ObserverBus.notify(B.class).say("w00t");
79: *
80: * // Output:
81: *
82: * // B says: h??
83: * // A says: go!
84: * //
85: * // B says: w00t
86: *
87: * </pre>
88: *
89: * @author wesendon
90: */
91: public class ECPObserverBusImpl implements ECPObserverBus {
92:
93:         private final HashMap<Class<? extends ECPObserver>, List<ECPObserver>> observerMap;
94:
95:         /**
96:          * Default constructor.
97:          */
98:         public ECPObserverBusImpl() {
99:                 observerMap = new HashMap<Class<? extends ECPObserver>, List<ECPObserver>>();
100:         }
101:
102:         /**
103:          * This method allows you to notify all observers.
104:          *
105:          * @param <T> class of observer
106:          * @param clazz class of observer
107:          * @return call object
108:          */
109:         @Override
110:         public <T extends ECPObserver> T notify(Class<T> clazz) {
111:                 return notify(clazz, false);
112:         }
113:
114:         /**
115:          * This method allows you to notify all observers.
116:          *
117:          * @param <T> class of observer
118:          * @param clazz class of observer
119:          * @param prioritized sort observer after {@link ECPPrioritizedIObserver}
120:          *
121:          * @return call object
122:          */
123:         public <T extends ECPObserver> T notify(Class<T> clazz, boolean prioritized) {
124:•                if (clazz == null) {
125:                         return null;
126:                 }
127:                 return createProxy(clazz, false);
128:         }
129:
130:         /**
131:          * Registers an observer for all observer interfaces implemented by the object or its super classes.
132:          *
133:          * @param observer observer object
134:          */
135:         @Override
136:         public void register(ECPObserver observer) {
137:                 register(observer, getObserverInterfaces(observer));
138:         }
139:
140:         /**
141:          * Registers an observer for the specified observer interfaces.
142:          *
143:          * @param observer observer object
144:          * @param classes set of classes
145:          */
146:         public void register(ECPObserver observer, Class<? extends ECPObserver>... classes) {
147:•                for (final Class<? extends ECPObserver> iface : classes) {
148:•                        if (iface.isInstance(observer)) {
149:                                 addObserver(observer, iface);
150:                         }
151:                 }
152:         }
153:
154:         /**
155:          * Unregisters an observer for all observer interfaces implemented by the object or its super classes.
156:          *
157:          * @param observer observer object
158:          */
159:         @Override
160:         public void unregister(ECPObserver observer) {
161:                 unregister(observer, getObserverInterfaces(observer));
162:         }
163:
164:         /**
165:          * Unregisters an observer for the specified observer interfaces.
166:          *
167:          * @param observer observer object
168:          * @param classes set of classes
169:          */
170:         public void unregister(ECPObserver observer, Class<? extends ECPObserver>... classes) {
171:•                for (final Class<? extends ECPObserver> iface : classes) {
172:•                        if (iface.isInstance(observer)) {
173:                                 removeObserver(observer, iface);
174:                         }
175:                 }
176:         }
177:
178:         private void addObserver(ECPObserver observer, Class<? extends ECPObserver> iface) {
179:                 final List<ECPObserver> observers = initObserverList(iface);
180:                 observers.add(observer);
181:         }
182:
183:         private void removeObserver(ECPObserver observer, Class<? extends ECPObserver> iface) {
184:                 final List<ECPObserver> observers = initObserverList(iface);
185:                 observers.remove(observer);
186:         }
187:
188:         private List<ECPObserver> initObserverList(Class<? extends ECPObserver> iface) {
189:                 List<ECPObserver> list = observerMap.get(iface);
190:•                if (list == null) {
191:                         list = new ArrayList<ECPObserver>();
192:                         observerMap.put(iface, list);
193:                 }
194:                 return list;
195:         }
196:
197:         private List<ECPObserver> getObserverByClass(Class<? extends ECPObserver> clazz) {
198:                 List<ECPObserver> list = observerMap.get(clazz);
199:•                if (list == null) {
200:                         list = Collections.emptyList();
201:                 }
202:                 return new ArrayList<ECPObserver>(list);
203:         }
204:
205:         private boolean isPrioritizedObserver(Class<?> clazz, Method method) {
206:                 // Only prioritize if requested class extends PrioritizedIObserver and method is part of this class and not part
207:                 // of some super class
208:•                if (!clazz.equals(method.getDeclaringClass())) {
209:                         return false;
210:                 }
211:•                for (final Class<?> interfaceClass : clazz.getInterfaces()) {
212:•                        if (ECPPrioritizedIObserver.class.equals(interfaceClass)) {
213:                                 return true;
214:                         }
215:                 }
216:                 return false;
217:         }
218:
219:         @SuppressWarnings("unchecked")
220:         private <T extends ECPObserver> T createProxy(Class<T> clazz, boolean prioritized) {
221:                 final ProxyHandler handler = new ProxyHandler((Class<ECPObserver>) clazz, prioritized);
222:                 return (T) Proxy
223:                         .newProxyInstance(clazz.getClassLoader(), new Class[] { clazz, ECPObserverCall.class }, handler);
224:
225:         }
226:
227:         /**
228:          * Proxyobserver which notifies all observers.
229:          *
230:          * @author wesendon
231:          */
232:         private final class ProxyHandler implements InvocationHandler, ECPObserverCall {
233:
234:                 private final Class<ECPObserver> clazz;
235:                 private List<ECPObserverCall.Result> lastResults;
236:                 private final boolean prioritized;
237:
238:                 ProxyHandler(Class<ECPObserver> clazz, boolean prioritized) {
239:                         this.clazz = clazz;
240:                         this.prioritized = prioritized;
241:                         lastResults = new ArrayList<ECPObserverCall.Result>();
242:                 }
243:
244:                 // BEGIN SUPRESS CATCH EXCEPTION
245:                 @Override
246:                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
247:                         // END SUPRESS CATCH EXCEPTION
248:                         // fork for calls to ObserverCall.class
249:                         if (ECPObserverCall.class.equals(method.getDeclaringClass())) {
250:                                 return accessObserverCall(method, args);
251:                         }
252:
253:                         final List<ECPObserver> observers = getObserverByClass(clazz);
254:
255:                         if (prioritized && isPrioritizedObserver(clazz, method)) {
256:                                 sortObservers(observers);
257:                         }
258:
259:                         // return default value if no observers are registered
260:                         if (observers.size() == 0) {
261:                                 lastResults = new ArrayList<ECPObserverCall.Result>();
262:                                 return Result.getDefaultValue(method);
263:                         }
264:
265:                         lastResults = notifiyObservers(observers, method, args);
266:                         final List<Object> result = new ArrayList<Object>();
267:                         for (final Result resultObject : lastResults) {
268:                                 final Object res = resultObject.getResultOrDefaultValue();
269:                                 if (res instanceof Object[]) {
270:                                         final Object[] arrayRes = (Object[]) res;
271:                                         result.addAll(Arrays.asList(arrayRes));
272:                                 } else if (res instanceof Collection) {
273:                                         result.addAll((Collection) res);
274:                                 } else {
275:                                         result.add(res);
276:                                 }
277:
278:                         }
279:                         return result;
280:                 }
281:
282:                 private Object accessObserverCall(Method method, Object[] args) throws IllegalArgumentException,
283:                         IllegalAccessException, InvocationTargetException {
284:                         return method.invoke(this, args);
285:
286:                 }
287:
288:                 private List<ECPObserverCall.Result> notifiyObservers(List<ECPObserver> observers, Method method,
289:                         Object[] args) {
290:                         final List<ECPObserverCall.Result> results = new ArrayList<ECPObserverCall.Result>(observers.size());
291:                         for (final ECPObserver observer : observers) {
292:                                 try {
293:                                         results.add(new Result(observer, method, method.invoke(observer, args)));
294:                                         // BEGIN SUPRESS CATCH EXCEPTION
295:                                 } catch (final Throwable e) {
296:                                         // END SUPRESS CATCH EXCEPTION
297:                                         results.add(new Result(observer, e, method));
298:                                 }
299:                         }
300:                         return results;
301:                 }
302:
303:                 @Override
304:                 public List<Result> getObserverCallResults() {
305:                         return lastResults;
306:                 }
307:
308:                 // END SUPRESS CATCH EXCEPTION
309:         }
310:
311:         /**
312:          * Sorts Observers. Make sure they are {@link ECPPrioritizedIObserver}!!
313:          *
314:          * @param observers list of observers
315:          */
316:         private void sortObservers(List<ECPObserver> observers) {
317:                 Collections.sort(observers, new Comparator<ECPObserver>() {
318:                         @Override
319:                         public int compare(ECPObserver o1, ECPObserver o2) {
320:                                 final int prio1 = ((ECPPrioritizedIObserver) o1).getPriority();
321:                                 final int prio2 = ((ECPPrioritizedIObserver) o2).getPriority();
322:                                 if (prio1 == prio2) {
323:                                         return 0;
324:                                 }
325:                                 return prio1 > prio2 ? 1 : -1;
326:                         }
327:                 });
328:         }
329:
330:         @SuppressWarnings("unchecked")
331:         private Class<? extends ECPObserver>[] getObserverInterfaces(ECPObserver observer) {
332:                 final HashSet<Class<? extends ECPObserver>> observerInterfacsFound = new HashSet<Class<? extends ECPObserver>>();
333:                 getClasses(observer.getClass(), observerInterfacsFound);
334:                 return observerInterfacsFound.toArray(new Class[observerInterfacsFound.size()]);
335:         }
336:
337:         @SuppressWarnings("unchecked")
338:         private boolean getClasses(Class<?> clazz, HashSet<Class<? extends ECPObserver>> result) {
339:•                for (final Class<?> iface : getAllInterfaces(clazz, new HashSet<Class<?>>())) {
340:•                        if (iface.equals(ECPObserver.class) && clazz.isInterface()) {
341:                                 result.add((Class<? extends ECPObserver>) clazz);
342:                                 return true;
343:                         }
344:•                        if (getClasses(iface, result) && clazz.isInterface()) {
345:                                 result.add((Class<? extends ECPObserver>) clazz);
346:                         }
347:
348:                 }
349:                 return false;
350:         }
351:
352:         private Set<Class<?>> getAllInterfaces(final Class<?> clazz, final Set<Class<?>> interfacesFound) {
353:
354:•                for (final Class<?> iface : clazz.getInterfaces()) {
355:                         interfacesFound.add(iface);
356:                 }
357:
358:•                if (clazz.getSuperclass() == null) {
359:                         return interfacesFound;
360:                 }
361:
362:                 return getAllInterfaces(clazz.getSuperclass(), interfacesFound);
363:         }
364:
365: }