/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.navigation.annotation.processor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.riena.core.singleton.SingletonProvider;
import org.eclipse.riena.navigation.INavigationNodeController;
import org.eclipse.riena.navigation.ISimpleNavigationNodeListener;
import org.eclipse.riena.navigation.annotation.OnNavigationNodeEvent;
/**
* Annotation processor for {@code INavigationNodeController} annotations.
*
* @since 3.0
*/
public final class NavigationNodeControllerAnnotationProcessor {
private static final SingletonProvider<NavigationNodeControllerAnnotationProcessor> NNCAP = new SingletonProvider<NavigationNodeControllerAnnotationProcessor>(
NavigationNodeControllerAnnotationProcessor.class);
/**
* Answer the singleton
* <code>NavigationNodeControllerAnnotationProcessor</code>
*
* @return the NavigationNodeControllerAnnotationProcessor singleton
*/
public static NavigationNodeControllerAnnotationProcessor getInstance() {
return NNCAP.getInstance();
}
private final Map<INavigationNodeController, ?> alreadyProcessed = new WeakHashMap<INavigationNodeController, Object>();
private NavigationNodeControllerAnnotationProcessor() {
// singleton
}
/**
* Process the {@link OnNavigationNodeEvent} annotations for the given
* {@link INavigationNodeController}.
*
* @param navigationNodeController
* the {@link INavigationNodeController} to process
*/
public void processAnnotations(final INavigationNodeController navigationNodeController) {
synchronized (alreadyProcessed) {
if (alreadyProcessed.containsKey(navigationNodeController)) {
return;
}
alreadyProcessed.put(navigationNodeController, null);
}
processAnnotations(navigationNodeController, navigationNodeController.getClass());
}
private void processAnnotations(final INavigationNodeController navigationNodeController, final Class<?> targetClazz) {
if (!INavigationNodeController.class.isAssignableFrom(targetClazz)) {
return;
}
processAnnotations(navigationNodeController, targetClazz.getSuperclass());
final Map<String, Method> events = getEventMethodMap(navigationNodeController, targetClazz);
if (!events.isEmpty()) {
navigationNodeController.getNavigationNode().addSimpleListener(
createListener(events, navigationNodeController));
}
}
private Map<String, Method> getEventMethodMap(final INavigationNodeController navigationNodeController,
final Class<?> targetClazz) {
final Map<String, Method> events = new HashMap<String, Method>();
for (final Method method : targetClazz.getDeclaredMethods()) {
final OnNavigationNodeEvent onNavigationNodeEvent = method.getAnnotation(OnNavigationNodeEvent.class);
if (onNavigationNodeEvent != null) {
if (!method.isAccessible()) {
method.setAccessible(true);
}
events.put(onNavigationNodeEvent.event().getMethodName(), method);
}
}
return events;
}
private ISimpleNavigationNodeListener createListener(final Map<String, Method> events,
final INavigationNodeController navigationNodeController) {
return (ISimpleNavigationNodeListener) Proxy.newProxyInstance(navigationNodeController.getClass()
.getClassLoader(), new Class[] { ISimpleNavigationNodeListener.class }, new ListenerHandler(events,
navigationNodeController));
}
private static class ListenerHandler implements InvocationHandler {
private final Map<String, Method> events;
private final INavigationNodeController navigationNodeController;
public ListenerHandler(final Map<String, Method> events,
final INavigationNodeController navigationNodeController) {
this.events = events;
this.navigationNodeController = navigationNodeController;
}
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
if (methodName.equals("hashCode")) { //$NON-NLS-1$
return System.identityHashCode(proxy);
} else if (methodName.equals("equals")) { //$NON-NLS-1$
return proxy == args[0];
} else if (methodName.equals("toString")) { //$NON-NLS-1$
return "Proxy for " + ISimpleNavigationNodeListener.class.getName() + " " + proxy.getClass().getName() + '@' //$NON-NLS-1$ //$NON-NLS-2$
+ Integer.toHexString(proxy.hashCode());
}
}
final Method targetMethod = events.get(methodName);
if (targetMethod != null) {
if (targetMethod.getParameterTypes().length == 0) {
return targetMethod.invoke(navigationNodeController);
} else {
return targetMethod.invoke(navigationNodeController, args);
}
}
return null;
}
}
}