/*
* Copyright (c) 2001, 2002 The XDoclet team
* All rights reserved.
*/
package xdoclet.modules.java.javabean;
import java.beans.Introspector;
import java.text.MessageFormat;
import java.util.Properties;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import xjavadoc.XClass;
import xjavadoc.XMethod;
import xjavadoc.XParameter;
import xjavadoc.XTag;
import xjavadoc.XType;
import xdoclet.XDocletException;
import xdoclet.XDocletTagSupport;
import xdoclet.util.LogUtil;
/**
* Specific tags handler to make the template easy.
*
* @author Laurent Etiemble (letiemble@users.sourceforge.net)
* @author Ryan Ovrevik
* @created June 20, 2002
* @version $Revision: 1.5 $
* @xdoclet.taghandler namespace="JavaBean"
*/
public class JavaBeanTagsHandler extends XDocletTagSupport
{
public static String getBeanInfoClassFor(XClass clazz) throws XDocletException
{
XTag beanTag = clazz.getDoc().getTag("javabean.class");
String className = null;
if (beanTag != null) {
// be paranoid...
className = beanTag.getAttributeValue("class");
}
if (className == null) {
className = clazz.getQualifiedName();
}
return MessageFormat.format(BeanInfoSubTask.GENERATED_BEANINFO_CLASS_NAME, new Object[]{className});
}
/**
* Returns true is the current method meets the minimum requirements for a property accessor. Essentially this means
* that it takes no arguments and does not return void. This different from XMethod.getAccessor() like functionality
* in that stricly speeking the XMethod is dealling stricly with the "Simple" naming convention.
*
* @param method
* @return
*/
private static boolean isPossiblePropertyAccessor(XMethod method)
{
return (method.getParameters().size() == 0 && !method.getReturnType().getType().isA("void"));
}
private static boolean isPossiblePropertyAccessor(XMethod method, XType propertyType)
{
return isPossiblePropertyAccessor(method) && method.getReturnType().getType().equals(propertyType);
}
/**
* Returns true is the current method meets the minimum requirements for a property mutator. Essentially this means
* that it takes one and only one argument and returns void.
*
* @param method
* @return
*/
private static boolean isPossiblePropertyMutator(XMethod method)
{
return (method.getParameters().size() == 1);
}
private static boolean isPossiblePropertyMutator(XMethod method, XType propertyType)
{
if (isPossiblePropertyMutator(method)) {
XParameter firstParam = (XParameter) method.getParameters().get(0);
return firstParam.getType().equals(propertyType);
}
return false;
}
private static boolean isExplicitlyReadOnlyProperty(XMethod method)
{
XTag tag = method.getDoc().getTag("javabean.property");
return "true".equalsIgnoreCase(tag.getAttributeValue("readonly"));
}
/**
* The current method must be a valid property accessor (return the property type takes not args) or mutator (return
* void and takes the property type as the single arg)
*
* @param method
* @return
*/
private static XType getPropertyType(XMethod method)
{
XType result = null;
//does the method have an explicit name attribute
String name = method.getDoc().getTag("javabean.property").getAttributeValue("name");
if ((name == null) || (name.length() <= 0)) {
if (isPossiblePropertyMutator(method)) {
XParameter parameter = (XParameter) method.getParameters().get(0);
result = parameter.getType();
}
else if (isPossiblePropertyAccessor(method)) {
result = method.getReturnType().getType();
}
}
else {
result = method.getPropertyType().getType();
}
return result;
}
/**
* Get the getter method for the current method. Will return null if this method is not a property (determined by
* the getPropertyName method). Will return null if this is a write only property. A property is write only if an
* accessor method does not exist. Attempt to find a property accessor that matches an explicitly named getter
* method. The named method must satisfy the requirements of a property accessor (take no params and return the
* correct type) The current method could be an accessor or a mutator attempt the use the current method if it
* satisfies Simple property accessor requirements.
*
* @param propertyName
* @param propertyType
* @param method
* @return the method that will serve as the getter method for this property Will return null if the
* current method is not a property method
*/
private static XMethod getGetterMethod(final String propertyName, final XType propertyType, XMethod method)
{
Log log = LogUtil.getLog(JavaBeanTagsHandler.class, "getGetterMethod");
//do some parameter checking
if ((propertyName == null) || (propertyName.length() == 0)) {
log.error("invalid property name: " + propertyName);
return null;
}
if (propertyType == null) {
log.error("invalid property type: " + propertyType);
return null;
}
if (method == null) {
log.error("invalid method: " + method);
return null;
}
//get on with the real business
if (log.isInfoEnabled()) {
log.info("===find getter method===");
log.info("method name: " + method.getName());
log.info("property name: " + propertyName);
log.info("property type: " + propertyType);
}
XMethod getterMethod = null;
//attempt to find an explicitly named getter method
final String explicitMethodName = method.getDoc().getTag("javabean.property").getAttributeValue("getter");
if ((explicitMethodName != null) && (explicitMethodName.length() > 0)) {
//the method has an explicit attribute
//there must be a bean property method with the given name
java.util.List methodList = getCurrentClass().getMethods(
new Predicate()
{
public boolean evaluate(Object obj)
{
XMethod method = (XMethod) obj;
return isPossiblePropertyAccessor(method, propertyType) &&
method.getName().equals(explicitMethodName);
}
}, false);
//the method name, return, and arguments are known... there can be [0, 1]
if (methodList.size() == 1) {
getterMethod = (XMethod) methodList.get(0);
if (log.isInfoEnabled()) {
log.info("found explicit getter " + getterMethod.getName());
}
if (isPossiblePropertyAccessor(method, propertyType)
&& (getterMethod != method)) {
log.warn("explicit getter " + getterMethod.getName() + " (should be passed method)");
}
}
else {
//they gave an explicit method name but it could not be found
log.warn("no explicit getter " + explicitMethodName);
}
}
else if (isPossiblePropertyAccessor(method, propertyType)) {
//this was put on the an accessor assume it was what they wanted
getterMethod = method;
log.info("using the passed method");
}
else {
//attempt to find the standard bean method (Simple property JavaBeans API specification 8.3.1)
//takes no params (isPropertyAccessor)
java.util.List methodList = getCurrentClass().getMethods(
new Predicate()
{
public boolean evaluate(Object obj)
{
XMethod method = (XMethod) obj;
return isPossiblePropertyAccessor(method, propertyType) &&
method.getPropertyName().equals(propertyName);
}
}, false);
if (methodList.size() == 1) {
getterMethod = (XMethod) methodList.get(0);
if (log.isInfoEnabled()) {
log.info("found standard getter " + getterMethod.getName());
}
}
else {
log.warn("no standard getter");
}
}
return getterMethod;
}
private static XMethod getSetterMethod(final String propertyName, final XType propertyType, XMethod method)
{
Log log = LogUtil.getLog(JavaBeanTagsHandler.class, "getSetterMethod");
//do some parameter checking
if ((propertyName == null) || (propertyName.length() == 0)) {
log.error("invalid property name: " + propertyName);
return null;
}
if (propertyType == null) {
log.error("invalid property type: " + propertyType);
return null;
}
if (method == null) {
log.error("invalid method: " + method);
return null;
}
//get on with the real business
XMethod setterMethod = null;
if (log.isInfoEnabled()) {
log.info("===find setter method===");
log.info("method name: " + method.getName());
log.info("property name: " + propertyName);
log.info("property type: " + propertyType);
}
//attempt to find an explicitly named setter method
final String explicitMethodName = method.getDoc().getTag("javabean.property").getAttributeValue("setter");
if (isExplicitlyReadOnlyProperty(method)) {
log.info("explicit read only");
}
else if ((explicitMethodName != null) && (explicitMethodName.length() > 0)) {
//the method has an explicit attribute
//there must be a bean property method with the given name
java.util.List methodList = getCurrentClass().getMethods(
new Predicate()
{
public boolean evaluate(Object obj)
{
XMethod method = (XMethod) obj;
return isPossiblePropertyMutator(method, propertyType) &&
method.getName().equals(explicitMethodName);
}
}, false);
if (methodList.size() == 1) {
setterMethod = (XMethod) methodList.get(0);
if (log.isInfoEnabled()) {
log.info("found explicit setter " + setterMethod.getName());
}
if (isPossiblePropertyMutator(method, propertyType)
&& (setterMethod != method)) {
log.warn("explicit setter " + setterMethod.getName() + " (should be passed method)");
}
}
else {
//they gave an explicit method name but it could not be found
log.warn("no explicit setter " + explicitMethodName);
}
}
else if (isPossiblePropertyMutator(method, propertyType)) {
//this was put on a mutator assume it was what they wanted?
setterMethod = method;
log.info("using the passed method");
}
else {
//attempt to find the standard bean method (Simple property JavaBeans API specification 8.3.1)
//takes one param
java.util.List methodList = getCurrentClass().getMethods(
new Predicate()
{
public boolean evaluate(Object obj)
{
XMethod method = (XMethod) obj;
return isPossiblePropertyMutator(method, propertyType) &&
propertyName.equals(method.getPropertyName());
}
}, false);
if (methodList.size() == 1) {
setterMethod = (XMethod) methodList.get(0);
if (log.isInfoEnabled()) {
log.info("found standard setter " + setterMethod.getName());
}
}
else {
log.info("no standard setter (not tagged readonly)");
}
}
return setterMethod;
}
/**
* Return the getter prefix according to the class tag that contains a class.
*
* @param attributes XDoclet attributes
* @return The getter prefix
* @exception XDocletException Thrown in case of problem
*/
public String getterPrefix(Properties attributes) throws XDocletException
{
String name = getTagValue(attributes, FOR_CLASS);
if ("boolean".equals(name)) {
return "is";
}
if ("java.lang.Boolean".equals(name)) {
return "is";
}
return "get";
}
/**
* Get the getter method for the current method
*
* @return
*/
public String getGetterMethodNameQuoted()
{
XMethod currentMethod = getCurrentMethod();
String propertyName = getPropertyName(currentMethod);
XType propertyType = getPropertyType(currentMethod);
if (propertyName != null) {
}
XMethod getterMethod = getGetterMethod(propertyName, propertyType, currentMethod);
if (getterMethod == null) {
return "null";
}
else {
return "\"" + getterMethod.getName() + "\"";
}
}
/**
* Get the setter method for the current method
*
* @return
*/
public String getSetterMethodNameQuoted()
{
XMethod currentMethod = getCurrentMethod();
String propertyName = getPropertyName(currentMethod);
XType propertyType = getPropertyType(currentMethod);
XMethod setterMethod = getSetterMethod(propertyName, propertyType, currentMethod);
if (setterMethod == null) {
return "null";
}
else {
return "\"" + setterMethod.getName() + "\"";
}
}
/**
* Get the property name for the current method xxx rlo the is fucked
*
* @return
*/
public String getPropertyNameQuoted()
{
String name = getPropertyName(getCurrentMethod());
if (name == null) {
return "null";
}
else {
return "\"" + name + "\"";
}
}
/**
* return configured bean class name or current class name
*
* @param attributes XDoclet attributes
* @return The getter prefix
* @exception XDocletException Thrown in case of problem
*/
public String beanClass(Properties attributes) throws XDocletException
{
if (getTagValue(FOR_CLASS, "javabean.class", "class", null, null, false, false) != null) {
return getTagValue(FOR_CLASS, "javabean.class", "class", null, null, false, false);
}
else {
return getCurrentClass().getQualifiedName();
}
}
/**
* Capitalize the first letter of a class tag (i.e countToken => CountToken)
*
* @param attributes XDoclet attributes
* @return The class tag capitalized
* @exception XDocletException Thrown in case of problem
*/
public String capitalizeClassTag(Properties attributes) throws XDocletException
{
String name = getTagValue(attributes, FOR_CLASS);
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
return new String(chars);
}
/**
* Get the property name for the current method xxx rlo the is fucked
*
* @param currentMethod
* @return
*/
private String getPropertyName(XMethod currentMethod)
{
String name = null;
XTag tag = currentMethod.getDoc().getTag("javabean.property");
if (tag != null) {
//the method is a property
name = tag.getAttributeValue("name");
//does the method have an explicit attribute
if ((name == null) || (name.length() <= 0)) {
//figure out the name from the method
//must be a standard bean method
name = getCurrentMethod().getPropertyName();
}
else {
name = Introspector.decapitalize(name);
}
}
return name;
}
}