/******************************************************************************* * Copyright (c) 2008-2011 Chair for Applied Software Engineering, * Technische Universitaet Muenchen. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: ******************************************************************************/ package org.eclipse.emf.emfstore.common.observer; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.emfstore.common.Activator; /** * This is a universal observer bus. Better documentation will follow... Example code: * * <pre> * // A is IObserver * A a = new A() { * * public void foo() { * System.out.println("A says: go!"); * } * }; * * // B extends A and is IObserver * B b = new B() { * * public void say(String ja) { * System.out.println("B says: " + ja); * } * * public void foo() { * System.out.println("B says: h??"); * } * }; * * // B is registered first * ObserverBus.register(b); * ObserverBus.register(a); * * ObserverBus.send(A.class).foo(); * * ObserverBus.send(B.class).say("w00t"); * * // Output: * * // B says: h?? * // A says: go! * // * // B says: w00t * * </pre> * * @author wesendon */ public class ObserverBus { private static ObserverBus instance; private HashMap<Class<? extends IObserver>, ProxyHandler> observerProxies; public static ObserverBus getInstance() { if (instance == null) { instance = new ObserverBus(); } return instance; } /** * Default constructor. */ public ObserverBus() { observerProxies = new HashMap<Class<? extends IObserver>, ProxyHandler>(); collectionExtensionPoints(); } /** * This method allows you to notify all observers. * * @param <T> class of observer * @param clazz class of observer * @return call object */ @SuppressWarnings("unchecked") public <T extends IObserver> T notify(Class<T> clazz) { if (clazz == null) { return null; } return (T) getProxyHandler(clazz, true).getProxy(); } private List<IObserver> getObserver(Class<? extends IObserver> clazz, boolean force) { ProxyHandler proxyHandler = getProxyHandler(clazz, force); if (proxyHandler != null) { return proxyHandler.getObservers(); } return null; } /** * Registers an observer for all observer interfaces implemented by the object or its super classes. * * @param observer observer object */ public void register(IObserver observer) { register(observer, getObserverInterfaces(observer)); } /** * Registers an observer for the specified observer interfaces. * * @param observer observer object * @param classes set of classes */ public void register(IObserver observer, Class<? extends IObserver>... classes) { for (Class<? extends IObserver> iface : classes) { if (iface.isInstance(observer)) { getObserver(iface, true).add(observer); } } } /** * Unregisters an observer for all observer interfaces implemented by the object or its super classes. * * @param observer observer object */ public void unregister(IObserver observer) { unregister(observer, getObserverInterfaces(observer)); } /** * Unregisters an observer for the specified observer interfaces. * * @param observer observer object * @param classes set of classes */ public void unregister(IObserver observer, Class<? extends IObserver>... classes) { for (Class<? extends IObserver> iface : classes) { if (iface.isInstance(observer)) { List<IObserver> observers = getObserver(iface, false); if (observers != null) { observers.remove(observer); } } } } private ProxyHandler getProxyHandler(Class<? extends IObserver> clazz, boolean force) { ProxyHandler handler = observerProxies.get(clazz); if (handler == null && force) { handler = new ProxyHandler(); Object observer = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, handler); handler.setProxy(observer); observerProxies.put(clazz, handler); return handler; } return handler; } /** * Proxyobserver which notifies all observers. * * @author wesendon */ private final class ProxyHandler implements InvocationHandler { private Object proxy; private ArrayList<IObserver> observers; public ProxyHandler() { observers = new ArrayList<IObserver>(); } public List<IObserver> getObservers() { return observers; } public void setProxy(Object proxy) { this.proxy = proxy; } public Object getProxy() { return proxy; } // BEGIN SUPRESS CATCH EXCEPTION // TODO: handle exception // TODO: handle return values public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object firstResult = null; for (IObserver observer : observers) { try { if (firstResult == null) { firstResult = method.invoke(observer, args); } else { method.invoke(observer, args); } } catch (Throwable e) { } } if (firstResult==null && method.getReturnType().isPrimitive()) { return getDefaultValueForPrimitive(method.getReturnType()); } return firstResult; } // END SUPRESS CATCH EXCEPTION private Object getDefaultValueForPrimitive(Class<?> returnType) { String simpleName = returnType.getSimpleName(); return primitiveToObjectDefaultValueMap.get(simpleName); } } private static final Map<String, Object> primitiveToObjectDefaultValueMap = new HashMap<String, Object>(); static { primitiveToObjectDefaultValueMap.put("int", new Integer(0)); primitiveToObjectDefaultValueMap.put("boolean", new Boolean(false)); primitiveToObjectDefaultValueMap.put("long", new Long(0)); primitiveToObjectDefaultValueMap.put("float", new Float(0)); primitiveToObjectDefaultValueMap.put("double", new Double(0)); primitiveToObjectDefaultValueMap.put("byte", Byte.MIN_VALUE); primitiveToObjectDefaultValueMap.put("short", Short.MIN_VALUE); } @SuppressWarnings("unchecked") private Class<? extends IObserver>[] getObserverInterfaces(IObserver observer) { HashSet<Class<? extends IObserver>> result = new HashSet<Class<? extends IObserver>>(); getClasses(observer.getClass(), result); return result.toArray(new Class[result.size()]); } @SuppressWarnings("unchecked") private boolean getClasses(Class<?> clazz, HashSet<Class<? extends IObserver>> result) { for (Class<?> iface : clazz.getInterfaces()) { if (iface.equals(IObserver.class) && clazz.isInterface()) { result.add((Class<? extends IObserver>) clazz); return true; } else { if (getClasses(iface, result) && clazz.isInterface()) { result.add((Class<? extends IObserver>) clazz); } } } return false; } public void collectionExtensionPoints() { IConfigurationElement[] confs = Platform.getExtensionRegistry().getConfigurationElementsFor( "org.eclipse.emf.emfstore.common.observer"); for (IConfigurationElement element : confs) { try { String extensionPointName = element.getAttribute("extensionPointName"); String observerAttributeName = element.getAttribute("observerAttributeName"); IConfigurationElement[] extensions = Platform.getExtensionRegistry().getConfigurationElementsFor( extensionPointName); for (IConfigurationElement extension : extensions) { IObserver o = (IObserver) extension.createExecutableExtension(observerAttributeName); register(o); } } catch (CoreException e) { Activator.getDefault().logException(e.getMessage(), e); } } } }