1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jelly.tags.bean;
18
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.Map;
22
23 import org.apache.commons.beanutils.MethodUtils;
24
25 import org.apache.commons.jelly.JellyTagException;
26 import org.apache.commons.jelly.XMLOutput;
27
28 /***
29 * Creates a nested property via calling a beans createFoo() method then
30 * either calling the setFoo(value) or addFoo(value) methods in a similar way
31 * to how Ant tags construct themselves.
32 *
33 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
34 * @author Christian Sell
35 * @version $Revision: 155420 $
36 */
37 public class BeanPropertyTag extends BeanTag {
38
39 /*** empty arguments constant */
40 private static final Object[] EMPTY_ARGS = {};
41
42 /*** empty argument types constant */
43 private static final Class[] EMPTY_ARG_TYPES = {};
44
45 /*** the name of the create method */
46 private String createMethodName;
47
48
49 public BeanPropertyTag(String tagName) {
50 super(Object.class, tagName);
51
52 if (tagName.length() > 0) {
53 createMethodName = "create"
54 + tagName.substring(0,1).toUpperCase()
55 + tagName.substring(1);
56 }
57 }
58
59 /***
60 * Creates a new instance by calling a create method on the parent bean
61 */
62 protected Object newInstance(Class theClass, Map attributes, XMLOutput output) throws JellyTagException {
63 Object parentObject = getParentObject();
64 if (parentObject != null) {
65
66 Class parentClass = parentObject.getClass();
67 Method method = findCreateMethod(parentClass);
68 if (method != null) {
69 try {
70 return method.invoke(parentObject, EMPTY_ARGS);
71 }
72 catch (Exception e) {
73 throw new JellyTagException( "failed to invoke method: " + method + " on bean: " + parentObject + " reason: " + e, e );
74 }
75 }
76 else {
77 Class tagClass = theClass;
78 if(tagClass == Object.class)
79 tagClass = findAddMethodClass(parentClass);
80 if(tagClass == null)
81 throw new JellyTagException("unable to infer element class for tag "+getTagName());
82
83 return super.newInstance(tagClass, attributes, output) ;
84 }
85 }
86 throw new JellyTagException("The " + getTagName() + " tag must be nested within a tag which maps to a BeanSource implementor");
87 }
88
89 /***
90 * finds the parameter type of the first public method in the parent class whose name
91 * matches the add{tag name} pattern, whose return type is void and which takes
92 * one argument only.
93 * @param parentClass
94 * @return the class of the first and only parameter
95 */
96 protected Class findAddMethodClass(Class parentClass) {
97 Method[] methods = parentClass.getMethods();
98 for (int i = 0; i < methods.length; i++) {
99 Method method = methods[i];
100 if(Modifier.isPublic(method.getModifiers())) {
101 Class[] args = method.getParameterTypes();
102 if (method.getName().equals(addMethodName)
103 && java.lang.Void.TYPE.equals(method.getReturnType())
104 && args.length == 1
105 && !java.lang.String.class.equals(args[0])
106 && !args[0].isArray()
107 && !args[0].isPrimitive())
108 return args[0];
109 }
110 }
111 return null;
112 }
113
114 /***
115 * Finds the Method to create a new property object
116 */
117 protected Method findCreateMethod(Class theClass) {
118 if (createMethodName == null) {
119 return null;
120 }
121 return MethodUtils.getAccessibleMethod(
122 theClass, createMethodName, EMPTY_ARG_TYPES
123 );
124 }
125 }