
jaxx.runtime.DefaultApplicationContext Maven / Gradle / Ivy
The newest version!
package jaxx.runtime;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The default context to be used for an application.
*
* This extends the {@link DefaultJAXXContext} and add a possibility to auto-instanciate
* some classes asked via {@link #getContextValue(java.lang.Class)} and
* {@link #getContextValue(java.lang.Class, java.lang.String)} methods.
*
* To registred a such class, just annotate your class with {@link AutoLoad}.
*
* @author chemit
* @since 1.2
* @see DefaultJAXXContext
*/
public class DefaultApplicationContext extends DefaultJAXXContext {
/**
* A class annotated @AutoLoad is used by context to auto
* instanciate the class when required.
*
* Note : A such class always have a public default constructor.
*
* @author chemit
* @version 1.0, 21/02/09
* @since 1.2
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface AutoLoad {
//TODO use this to add a method to init
String initMethod() default "";
}
/**
* A class annotated @MethodAccess is used by context to obtain the value
* of an entry via a declared method.
*
* Note : A such class must have a method called {@link #methodName()} with
* a single String parameter.
*
* @author chemit
* @version 1.0, 21/02/09
* @since 1.2
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface MethodAccess {
/**
* Define a forward to a target class. When the target class
* will be asked with method {@link #getContextValue(java.lang.Class, java.lang.String)}
* it will be delegating to this class.
*
* @return the forwarded class
*/
Class> target() default Object.class;
/**
* @return the name of the method to access data
*/
String methodName();
}
/**
* Map of forwarded classes (key) to classes (values).
*/
protected Map forwards;
public DefaultApplicationContext() {
super();
forwards = new HashMap();
pcs = new PropertyChangeSupport(this);
}
public DefaultApplicationContext(JAXXObject ui) {
super(ui);
}
/** to use log facility, just put in your code: log.info(\"...\"); */
static private final Log log = LogFactory.getLog(DefaultApplicationContext.class);
/** to manage properties modifications */
protected PropertyChangeSupport pcs;
@SuppressWarnings({"unchecked"})
@Override
public T getContextValue(Class clazz, String name) {
Object value = null;
MethodAccess access;
Class> realClass;
if (forwards.containsKey(clazz)) {
// this is a forward class
realClass = forwards.get(clazz);
// always call the forwarder with no name
value = getContextValue(realClass, null);
if (log.isDebugEnabled()) {
log.debug("detect forward from " + clazz + " to " + realClass + " (" + value + ")");
}
} else {
realClass = clazz;
value = super.getContextValue(realClass, name);
}
if (value == null) {
AutoLoad anno = clazz.getAnnotation(AutoLoad.class);
if (anno == null) {
// no annotation, so do nothing
return null;
}
if (name != null) {
throw new IllegalArgumentException("an " + AutoLoad.class.getName() + " can not have a named context but was call with this one : " + name);
}
value = newInstance(clazz);
if (!anno.initMethod().trim().isEmpty()){
// apply method on class
newAccess(clazz, value, anno.initMethod().trim());
}
if (log.isDebugEnabled()) {
log.debug("new instance " + clazz + " : " + value);
}
// save new instance
setContextValue(value, null);
}
access = realClass.getAnnotation(MethodAccess.class);
if (access != null) {
if (name == null) {
if (access.target() != Object.class) {
// register forward
Class> targetClass = access.target();
if (!forwards.containsKey(targetClass)) {
// register forward
forwards.put(targetClass, clazz);
if (log.isDebugEnabled()) {
log.debug("register forward from " + targetClass + " to " + clazz);
}
}
}
} else {
// specialized access
value = newAccess(realClass, value, access.methodName(), name);
}
}
return (T) value;
}
@Override
public void removeContextValue(Class klazz, String name) {
Entry entry;
if (name == null && forwards.containsValue(klazz)) {
// remove forward
Iterator> itr = forwards.entrySet().iterator();
while (itr.hasNext()) {
entry = itr.next();
if (entry.getValue().equals(klazz)) {
itr.remove();
if (log.isDebugEnabled()) {
log.debug("removed forward from " + entry.getKey() + " to " + klazz);
}
break;
}
}
}
//FIXME should update forwards state
super.removeContextValue(klazz, name);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
pcs.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
pcs.removePropertyChangeListener(propertyName, listener);
}
public synchronized boolean hasListeners(String propertyName) {
return pcs.hasListeners(propertyName);
}
public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
return pcs.getPropertyChangeListeners(propertyName);
}
public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
return pcs.getPropertyChangeListeners();
}
protected Object newInstance(Class> clazz) throws IllegalArgumentException {
Object value = null;
Constructor> constructor = null;
try {
constructor = clazz.getConstructor();
// auto instanciate the class
if (constructor == null) {
throw new IllegalArgumentException(clazz + " has no public constructor");
}
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException(ex);
} catch (SecurityException ex) {
throw new IllegalArgumentException(ex);
}
try {
value = constructor.newInstance();
} catch (InstantiationException ex) {
throw new IllegalArgumentException(ex);
} catch (IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
} catch (InvocationTargetException ex) {
throw new IllegalArgumentException(ex);
}
return value;
}
protected Object newAccess(Class> clazz, Object parent, String methodName, String name) throws IllegalArgumentException {
Object value;
try {
Method m = clazz.getMethod(methodName, String.class);
value = m.invoke(parent, name);
return value;
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException(ex);
} catch (SecurityException ex) {
throw new IllegalArgumentException(ex);
} catch (IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
} catch (InvocationTargetException ex) {
throw new IllegalArgumentException(ex);
}
}
protected Object newAccess(Class> clazz, Object parent, String methodName) throws IllegalArgumentException {
Object value;
try {
Method m = clazz.getMethod(methodName);
value = m.invoke(parent);
return value;
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException(ex);
} catch (SecurityException ex) {
throw new IllegalArgumentException(ex);
} catch (IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
} catch (InvocationTargetException ex) {
throw new IllegalArgumentException(ex);
}
}
protected void firePropertyChange(String name, Object oldValue, Object newValue) {
pcs.firePropertyChange(name, oldValue, newValue);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy