package net.sourceforge.javautil.groovy.builder.interceptor.objectfactory;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassMethod;
import net.sourceforge.javautil.common.reflection.cache.ClassProperty;
import net.sourceforge.javautil.groovy.builder.GroovyBuilder;
import net.sourceforge.javautil.groovy.builder.interceptor.annotation.AddChildNode;
import net.sourceforge.javautil.groovy.builder.interceptor.annotation.Node;
import net.sourceforge.javautil.groovy.builder.interceptor.annotation.Parent;
/**
* This will map node relationships for child/parent nodes.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: AnnotatedNodeMapper.java 1844 2010-02-19 05:21:05Z ponderator $
*/
public class AnnotatedNodeMapper extends StandardNodeMapper {
protected Map<Class, ClassMethod> addChild = new HashMap<Class, ClassMethod>();
protected Map<Class, ClassMethod> setParent = new HashMap<Class, ClassMethod>();
protected Map<Class, ClassMethod> nodeType = new HashMap<Class, ClassMethod>();
/**
* Parent nodes should use the {@link AddChildNode} annotation in order to
* specify which method should be used, if any, to add children to some internal
* collection. Child nodes should use the {@link Parent} annotation in order to
* specify which method should be used, if any, to set the parent to child node.
*/
public void associate(GroovyBuilder builder, ObjectFactoryInterceptor ofi, Object child, Object parent) {
Node.Type type = null;
boolean parentRequired = true;
Node cnode = child.getClass().getAnnotation(Node.class);
if (cnode != null) {
type = cnode.value();
parentRequired = cnode.parentRequired();
} else {
ClassMethod method = this.getTypeIndicator(child.getClass());
if (method != null) {
cnode = method.getAnnotation(Node.class);
parentRequired = cnode.parentRequired();
type = (Node.Type) method.invoke(child);
} else {
type = Node.Type.Branch;
parentRequired = false;
}
}
switch (type) {
case Root:
if (parent != null) throw new RuntimeException("Cannot add root nodes to type: " + parent);
break;
case Branch:
if (parent == null && parentRequired) throw new RuntimeException("No parent for a branch node: " + child);
if (parent != null) this.addChild(parent, child);
break;
case Leaf:
if (parent == null) throw new RuntimeException("No parent for a leaf node: " + child);
this.addChild(parent, child);
break;
case Dynamic:
throw new RuntimeException("Dynamic is not a valid type: " + child);
}
if (parent != null) this.setParent(child, parent);
}
protected void setParent (Object child, Object parent) {
ClassMethod property = this.getParentSetter(child.getClass());
if (property == null) return;
property.invoke(child, parent);
}
protected void addChild (Object parent, Object child) {
ClassMethod method = this.getChildAggregator(parent.getClass());
if (method == null) return;
method.invoke(parent, child);
}
protected ClassMethod getParentSetter (Class clazz) {
if (!this.setParent.containsKey(clazz)) {
this.setParent.put(clazz, ClassCache.getFor(clazz).getMethod(Parent.class));
}
return this.setParent.get(clazz);
}
protected ClassMethod getChildAggregator (Class clazz) {
if (!this.addChild.containsKey(clazz)) {
this.addChild.put(clazz, ClassCache.getFor(clazz).getMethod(AddChildNode.class));
}
return this.addChild.get(clazz);
}
protected ClassMethod getTypeIndicator (Class clazz) {
if (!this.nodeType.containsKey(clazz)) {
this.nodeType.put(clazz, ClassCache.getFor(clazz).getMethod(Node.class));
}
return this.nodeType.get(clazz);
}
}