/*
* Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org
* Use is subject to license terms. See license.txt.
*/
// TODO javadoc - remove this comment only when the class and all non-public
// methods and fields are documented
package org.beanfabrics.support;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.beanfabrics.Path;
import org.beanfabrics.PathObservation;
import org.beanfabrics.model.PresentationModel;
import org.beanfabrics.util.ReflectionUtil;
/**
* @author Michael Karneim
*/
// TODO (mk) UNIT TEST
public class OnChangeSupport implements Support {
public static OnChangeSupport get(PresentationModel model) {
Supportable s = (Supportable)model;
OnChangeSupport support = s.getSupportMap().get(OnChangeSupport.class);
if (support == null) {
support = new OnChangeSupport(model);
s.getSupportMap().put(OnChangeSupport.class, support);
}
return support;
}
private final PresentationModel model;
private Map<Method, OnChangeMethodSupport> map = new HashMap<Method, OnChangeMethodSupport>();
public OnChangeSupport(PresentationModel model) {
this.model = model;
}
public void setup(Method method) {
if (map.containsKey(method) == false) {
OnChangeMethodSupport support = support(model, method);
map.put(method, support);
}
}
private static OnChangeMethodSupport support(PresentationModel pModel, Method m) {
OnChange anno = m.getAnnotation(OnChange.class);
Path[] paths = new Path[anno.path() == null ? 0 : anno.path().length];
for (int i = 0; i < paths.length; ++i) {
paths[i] = Path.parse(anno.path()[i]);
}
OnChangeMethodSupport result = new OnChangeMethodSupport(pModel, paths, m);
//result.callAnnotatedMethod();
return result;
}
private static class OnChangeMethodSupport {
private final PresentationModel owner;
private final Path[] paths;
private final List<PathObservation> obervations = new LinkedList<PathObservation>();
private final Method annotatedMethod;
private final PropertyChangeListener pcListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
callAnnotatedMethod();
}
};
public OnChangeMethodSupport(PresentationModel owner, Path[] paths, Method annotatedMethod) {
super();
if (annotatedMethod.getParameterTypes() != null && annotatedMethod.getParameterTypes().length > 0) {
throw new IllegalArgumentException("method '" + annotatedMethod.getName() + "' must not declare parameters");
}
this.owner = owner;
this.paths = paths;
this.annotatedMethod = annotatedMethod;
for (Path path : paths) {
if (path.length() == 0) {
owner.addPropertyChangeListener(this.pcListener);
} else if (path.length() == 1) {
owner.addPropertyChangeListener(path.getLastElement(), this.pcListener);
} else {
Path parent = path.getParent();
this.obervations.add(new TargetObservation(owner, parent, path.getLastElement()));
}
}
}
private void callAnnotatedMethod() {
try {
ReflectionUtil.invokeMethod(owner, annotatedMethod, (Object[])null);
} catch (IllegalArgumentException e) {
throw new UndeclaredThrowableException(e, "" + annotatedMethod.getName());
} catch (IllegalAccessException e) {
throw new UndeclaredThrowableException(e, "" + annotatedMethod.getName());
} catch (InvocationTargetException e) {
throw new UndeclaredThrowableException(e, "" + annotatedMethod.getName());
}
}
private class TargetObservation extends PathObservation {
private PresentationModel currentTarget = null;
private String propertyName;
public TargetObservation(PresentationModel root, Path path, String propertyName) {
super(root, path);
this.propertyName = propertyName;
this.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
addPCLToTarget();
}
});
this.addPCLToTarget();
}
public void addPCLToTarget() {
PresentationModel newTarget = this.getTarget();
if (newTarget == this.currentTarget) {
return; // nothing to do
}
if (this.currentTarget != null) {
this.currentTarget.removePropertyChangeListener(propertyName, pcListener);
}
this.currentTarget = newTarget;
if (this.currentTarget != null) {
this.currentTarget.addPropertyChangeListener(propertyName, pcListener);
}
}
}
}
}