View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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              // now lets try call the create method...
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 }