/*******************************************************************************
* Copyright (c) 2007, 2010 Association for Decentralized Information Management
* in Industry THTH ry.
* 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:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.g2d.canvas.impl;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.simantics.utils.datastructures.hints.IHintListener;
import org.simantics.utils.datastructures.hints.IHintObservable;
import org.simantics.utils.datastructures.hints.IHintContext.Key;
/**
* @author Toni Kalajainen
*/
public class HintReflection {
private final static HintListenerDefinition[] EMPTY = new HintListenerDefinition[0];
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface HintListener {
Class> Class();
String Field();
}
/**
* Scans an object for reflections of hint listeners
*
* Example:
*
* @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
* public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
* ...
* }
* @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
* public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
* ...
* }
*
* @param obj object to scan
* @return an array of painters and their priorities
*/
public static HintListenerDefinition[] getDependencies(final Object obj)
{
Map result = new HashMap();
Class> clazz = obj.getClass();
_add(obj, clazz, result);
//if (result==null) return EMPTY;
return result.values().toArray(EMPTY);
}
private static void _add(final Object obj, Class> clazz, Map result)
{
try {
for (final Method m : clazz.getDeclaredMethods()) {
HintListener anno = (HintListener) m
.getAnnotation(HintListener.class);
if (anno == null)
continue;
Class> keyContainerClass = anno.Class();
assert (keyContainerClass != null);
String fieldName = anno.Field();
assert (fieldName != null);
Field field;
field = keyContainerClass.getField(fieldName);
assert (field != null);
field.setAccessible(true);
Object value = field.get(field != null);
assert (value != null);
assert (value instanceof Key);
Key key = (Key) value;
Class> returnType = m.getReturnType();
assert (returnType == void.class);
Class>[] params = m.getParameterTypes();
assert (params.length == 4 || params.length == 3);
assert (params[0].equals(IHintObservable.class));
assert (params[1].equals(Key.class));
assert (params[2].equals(Object.class));
if (params.length == 4)
assert (params[3].equals(Object.class));
HintListenerDefinition def = result.get(key);
if (def==null) {
def = new HintListenerDefinition(key, obj);
result.put(key, def);
}
m.setAccessible(true);
if (params.length == 4)
def.changedHandlerMethod = m;
else
def.removedHandlerMethod = m;
}
} catch (SecurityException e) {
throw new Error(e);
} catch (NoSuchFieldException e) {
throw new Error(e);
} catch (IllegalAccessException e) {
throw new Error(e);
}
/*
for (final Field f : clazz.getDeclaredFields()) {
Class c = f.getType();
Dependency dep = (Dependency) f.getAnnotation(Dependency.class);
Reference ref = (Reference) f.getAnnotation(Reference.class);
if (dep==null && ref==null) continue;
assert(ICanvasParticipant.class.isAssignableFrom( c ));
assert(!(dep!=null && ref!=null));
ReferenceDefinition rd = new ReferenceDefinition();
rd.dependency = dep!=null;
rd.field = f;
rd.requirement = c;
f.setAccessible(true);
result.add(rd);
}
*/
Class> superClass = clazz.getSuperclass();
if (superClass!=null)
_add(obj, superClass, result);
}
public final static class HintListenerDefinition implements IHintListener {
public final Key key;
public final Object object;
public Method changedHandlerMethod;
public Method removedHandlerMethod;
public HintListenerDefinition(Key key, Object object) {
this.key = key;
this.object = object;
}
@Override
public void hintChanged(IHintObservable sender, Key key,
Object oldValue, Object newValue) {
if (changedHandlerMethod==null) return;
try {
changedHandlerMethod.invoke(object, sender, key, oldValue, newValue);
} catch (IllegalArgumentException e) {
throw new Error(e);
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
}
@Override
public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
if (removedHandlerMethod==null) return;
try {
removedHandlerMethod.invoke(object, sender, key, oldValue);
} catch (IllegalArgumentException e) {
throw new Error(e);
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
}
}
}