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.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.util.Collection;
22  
23  import org.apache.commons.beanutils.BeanUtils;
24  import org.apache.commons.beanutils.MethodUtils;
25  import org.apache.commons.jelly.JellyTagException;
26  import org.apache.commons.jelly.Tag;
27  import org.apache.commons.jelly.impl.BeanSource;
28  import org.apache.commons.jelly.impl.CollectionTag;
29  import org.apache.commons.jelly.tags.core.UseBeanTag;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  
34  /***
35   * Creates a bean for the given tag which is then either output as a variable
36   * or can be added to a parent tag.
37   *
38   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
39   * @author Christian Sell
40   * @version   $Revision: 155420 $
41   */
42  public class BeanTag extends UseBeanTag {
43  
44      /*** The Log to which logging calls will be made. */
45      private static final Log log = LogFactory.getLog(BeanTag.class);
46  
47      protected static final Object[] EMPTY_ARGUMENTS = {};
48  
49      /*** the name of the property to create */
50      private String tagName;
51  
52      /*** the name of the adder method */
53      protected String addMethodName;
54  
55      /*** if present this is used to call a doit method when the bean is constructed */
56      private Method invokeMethod;
57  
58  
59      public BeanTag() {
60          this(null, "bean", null);
61      }
62  
63      public BeanTag(Class defaultClass, String tagName) {
64          this(defaultClass, tagName, null);
65      }
66  
67      public BeanTag(Class defaultClass, String tagName, Method invokeMethod) {
68          super(defaultClass);
69          this.tagName = tagName;
70          this.invokeMethod = invokeMethod;
71  
72          if (tagName.length() > 0) {
73              addMethodName = "add"
74                  + tagName.substring(0,1).toUpperCase()
75                  + tagName.substring(1);
76          }
77      }
78  
79      /***
80       * @return the local name of the XML tag to which this tag is bound
81       */
82      public String getTagName() {
83          return tagName;
84      }
85  
86      /***
87       * Output the tag as a named variable. If the parent bean has an adder or setter
88       * method then invoke that to register this bean with its parent.
89       */
90      protected void processBean(String var, Object bean) throws JellyTagException {
91          if (var != null) {
92              context.setVariable(var, bean);
93          }
94  
95          // now lets try set the parent property via calling the adder or the setter method
96          if (bean != null) {
97              Tag parent = this;
98  
99              while (true) {
100                 parent = parent.getParent();
101                 if (parent == null) {
102                     break;
103                 }
104 
105                 if (parent instanceof BeanSource) {
106                     BeanSource source = (BeanSource) parent;
107                     Object parentObject = source.getBean();
108                     if (parentObject != null) {
109                         if (parentObject instanceof Collection) {
110                             Collection collection = (Collection) parentObject;
111                             collection.add(bean);
112                         }
113                         else {
114                             // lets see if there's a setter method...
115                             Method method = findAddMethod(parentObject.getClass(), bean.getClass());
116                             if (method != null) {
117                                 Object[] args = { bean };
118                                 try {
119                                     method.invoke(parentObject, args);
120                                 }
121                                 catch (Exception e) {
122                                     throw new JellyTagException( "failed to invoke method: " + method + " on bean: " + parentObject + " reason: " + e, e );
123                                 }
124                             }
125                             else {
126                                 try {
127                                   BeanUtils.setProperty(parentObject, tagName, bean);
128                                 } catch (IllegalAccessException e) {
129                                     throw new JellyTagException(e);
130                                 } catch (InvocationTargetException e) {
131                                     throw new JellyTagException(e);
132                                 }
133                             }
134                         }
135                     }
136                     else {
137                         log.warn("Cannot process null bean for tag: " + parent);
138                     }
139                 }
140                 else if (parent instanceof CollectionTag) {
141                     CollectionTag tag = (CollectionTag) parent;
142                     tag.addItem(bean);
143                 }
144                 else {
145                     continue;
146                 }
147                 break;
148             }
149 
150             if (invokeMethod != null) {
151                 Object[] args = { bean };
152                 try {
153                     invokeMethod.invoke(bean, EMPTY_ARGUMENTS);
154                 }
155                 catch (Exception e) {
156                     throw new JellyTagException( "failed to invoke method: " + invokeMethod + " on bean: " + bean + " reason: " + e, e );
157                 }
158             }
159             else {
160                 if (parent == null && var == null) {
161                     //warn if the bean gets lost in space
162                     log.warn( "Could not add bean to parent for bean: " + bean );
163                 }
164             }
165         }
166     }
167 
168     /***
169      * Finds the Method to add the new bean
170      */
171     protected Method findAddMethod(Class beanClass, Class valueClass) {
172         if (addMethodName == null) {
173             return null;
174         }
175         Class[] argTypes = { valueClass };
176         return MethodUtils.getAccessibleMethod(
177             beanClass, addMethodName, argTypes
178         );
179     }
180 
181 
182     /***
183      * @return the parent bean object
184      */
185     protected Object getParentObject() throws JellyTagException {
186         BeanSource tag = (BeanSource) findAncestorWithClass(BeanSource.class);
187         if (tag != null) {
188             return tag.getBean();
189         }
190         return null;
191     }
192 }