blob: ff0e3116b132f5c1e334d5de23aa64a61c92d5db [file] [log] [blame]
/*
* Copyright 2002,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jelly.tags.bean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Tag;
import org.apache.commons.jelly.impl.BeanSource;
import org.apache.commons.jelly.impl.CollectionTag;
import org.apache.commons.jelly.tags.core.UseBeanTag;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Creates a bean for the given tag which is then either output as a variable
* or can be added to a parent tag.
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
* @author Christian Sell
* @version $Revision: 1.11 $
*/
public class BeanTag extends UseBeanTag {
/** The Log to which logging calls will be made. */
private static final Log log = LogFactory.getLog(BeanTag.class);
protected static final Object[] EMPTY_ARGUMENTS = {};
/** the name of the property to create */
private String tagName;
/** the name of the adder method */
protected String addMethodName;
/** if present this is used to call a doit method when the bean is constructed */
private Method invokeMethod;
public BeanTag() {
this(null, "bean", null);
}
public BeanTag(Class defaultClass, String tagName) {
this(defaultClass, tagName, null);
}
public BeanTag(Class defaultClass, String tagName, Method invokeMethod) {
super(defaultClass);
this.tagName = tagName;
this.invokeMethod = invokeMethod;
if (tagName.length() > 0) {
addMethodName = "add"
+ tagName.substring(0,1).toUpperCase()
+ tagName.substring(1);
}
}
/**
* @return the local name of the XML tag to which this tag is bound
*/
public String getTagName() {
return tagName;
}
/**
* Output the tag as a named variable. If the parent bean has an adder or setter
* method then invoke that to register this bean with its parent.
*/
protected void processBean(String var, Object bean) throws JellyTagException {
if (var != null) {
context.setVariable(var, bean);
}
// now lets try set the parent property via calling the adder or the setter method
if (bean != null) {
Tag parent = this;
while (true) {
parent = parent.getParent();
if (parent == null) {
break;
}
if (parent instanceof BeanSource) {
BeanSource source = (BeanSource) parent;
Object parentObject = source.getBean();
if (parentObject != null) {
if (parentObject instanceof Collection) {
Collection collection = (Collection) parentObject;
collection.add(bean);
}
else {
// lets see if there's a setter method...
Method method = findAddMethod(parentObject.getClass(), bean.getClass());
if (method != null) {
Object[] args = { bean };
try {
method.invoke(parentObject, args);
}
catch (Exception e) {
throw new JellyTagException( "failed to invoke method: " + method + " on bean: " + parentObject + " reason: " + e, e );
}
}
else {
try {
BeanUtils.setProperty(parentObject, tagName, bean);
} catch (IllegalAccessException e) {
throw new JellyTagException(e);
} catch (InvocationTargetException e) {
throw new JellyTagException(e);
}
}
}
}
else {
log.warn("Cannot process null bean for tag: " + parent);
}
}
else if (parent instanceof CollectionTag) {
CollectionTag tag = (CollectionTag) parent;
tag.addItem(bean);
}
else {
continue;
}
break;
}
if (invokeMethod != null) {
Object[] args = { bean };
try {
invokeMethod.invoke(bean, EMPTY_ARGUMENTS);
}
catch (Exception e) {
throw new JellyTagException( "failed to invoke method: " + invokeMethod + " on bean: " + bean + " reason: " + e, e );
}
}
else {
if (parent == null && var == null) {
//warn if the bean gets lost in space
log.warn( "Could not add bean to parent for bean: " + bean );
}
}
}
}
/**
* Finds the Method to add the new bean
*/
protected Method findAddMethod(Class beanClass, Class valueClass) {
if (addMethodName == null) {
return null;
}
Class[] argTypes = { valueClass };
return MethodUtils.getAccessibleMethod(
beanClass, addMethodName, argTypes
);
}
/**
* @return the parent bean object
*/
protected Object getParentObject() throws JellyTagException {
BeanSource tag = (BeanSource) findAncestorWithClass(BeanSource.class);
if (tag != null) {
return tag.getBean();
}
return null;
}
}