/*
* Copyright (c) 2001, 2002 The XDoclet team
* All rights reserved.
*/
package xdoclet.modules.ejb.env;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import xjavadoc.XClass;
import xjavadoc.XField;
import xjavadoc.XMember;
import xjavadoc.XMethod;
import xjavadoc.XTag;
import xdoclet.XDocletException;
import xdoclet.XDocletTagSupport;
import xdoclet.util.TypeConversionUtil;
/**
* Handles field level tag's for configuring a bean's environment.
*
* @author <a href="mailto:matthias@germann.com">Matthias Germann</a>
* @created March 31, 2005
* @xdoclet.taghandler namespace="EjbEnv"
* @version $Revision: 1.3 $
*/
public class EnvTagsHandler extends XDocletTagSupport
{
/**
* Maps primitive types to their wrapper classes
*/
private final static Map wrappers;
protected XTag currentTag;
protected XMember currentMember;
protected int currentTagType;
static {
Map wps = new HashMap();
wps.put("boolean", "java.lang.Boolean");
wps.put("byte", "java.lang.Byte");
wps.put("char", "java.lang.Character");
wps.put("short", "java.lang.Short");
wps.put("int", "java.lang.Integer");
wps.put("float", "java.lang.Float");
wps.put("long", "java.lang.Long");
wps.put("double", "java.lang.Double");
wrappers = Collections.unmodifiableMap(wps);
}
/**
* Executes the template for all class-, method- and field-level tags with the passed name
*
* @param template the template
* @param attributes the attributes
* @throws XDocletException if an error occures
* @doc.param name="tagName" optional="false" description="the tag name"
* @doc.param name="paramName" optional="true" description="the required parameter"
* @doc.param name="paramValue" optional="true" description="the value for the required parameter"
* @doc.tag type="block"
*/
public void forAllTags(String template, Properties attributes) throws XDocletException
{
forTags(template, attributes, true, true, true);
}
/**
* Executes the template for all method- and field-level tags with the passed name
*
* @param template the template
* @param attributes the attributes
* @throws XDocletException if an error occures
* @doc.param name="tagName" optional="false" description="the tag name"
* @doc.param name="paramName" optional="true" description="the required parameter"
* @doc.param name="paramValue" optional="true" description="the value for the required parameter"
* @doc.tag type="block"
*/
public void forAllMemberTags(String template, Properties attributes) throws XDocletException
{
forTags(template, attributes, false, true, true);
}
/**
* Executes the template for all method-level tags with the passed name
*
* @param template the template
* @param attributes the attributes
* @throws XDocletException if an error occures
* @doc.param name="tagName" optional="false" description="the tag name"
* @doc.param name="paramName" optional="true" description="the required parameter"
* @doc.param name="paramValue" optional="true" description="the value for the required parameter"
* @doc.tag type="block"
*/
public void forAllMethodTags(String template, Properties attributes) throws XDocletException
{
forTags(template, attributes, false, true, false);
}
/**
* Returns the name parameter value for the current tag. If the name parameter is not specified for a method- or
* field-level tag, the member's name is returned. If the name parameter is not specified for a class level tag, an
* error is generated.
*
* @param attributes the attributes
* @return the name
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the name parameter"
* @doc.tag type="content"
*/
public String name(Properties attributes) throws XDocletException
{
String paramName = attributes.getProperty("paramName");
if (paramName == null) {
throw new XDocletException("paramName attribute is mandatory");
}
String name = null;
StringTokenizer st = new StringTokenizer(paramName, ",");
while (name == null && st.hasMoreTokens()) {
name = currentTag.getAttributeValue(st.nextToken());
}
if (name == null) {
if (currentMember == null) {
// class level
mandatoryParamNotFound(currentTag.getDoc(), paramName, currentTag.getName());
}
else {
// method or field level
name = currentMember.getName();
}
}
return name;
}
/**
* Returns the type parameter value for the current tag. Returns the field type for field-level tags and the return
* value for method-level tags. For class-level tags, the value of the type parameter is returned. The wrapper class
* is returned for primitive fields an methods with primitive return values.
*
* @param attributes the attributes
* @return the type
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the type parameter"
* @doc.param name="values" description="The valid values for the parameter, comma separated. An
* error message is printed if the parameter value is not one of the values."
* @doc.param name="default" description="The default value is returned if parameter not specified
* by user for the tag."
* @doc.param name="mandatory" values="true,false" description="Generate an error if parameter not
* @doc.tag type="content"
*/
public String type(Properties attributes) throws XDocletException
{
if (currentMember == null) {
// class level tags
return paramValue(attributes);
}
else {
// method and field level tags
String type = currentMemberType();
String wrapper = (String) wrappers.get(type);
return wrapper == null ? type : wrapper;
}
}
/**
* Executes the body only if the current tag is either a method- or fiel-level tag or has a type parameter.
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the name parameter"
* @doc.tag type="block"
*/
public void ifHasType(String template, Properties attributes) throws XDocletException
{
String paramName = attributes.getProperty("paramName");
if (paramName == null) {
throw new XDocletException("paramName attribute is mandatory");
}
if (currentMember == null) {
// class level tags
String type = currentTag.getAttributeValue(paramName);
if (type != null) {
generate(template);
}
}
else {
// method and field level tags
generate(template);
}
}
/**
* Returns the method or field name. Can only be used inside <code>forAllMemberTags</code> or <code>forAllMethodTags</code>
* .
*
* @param attributes the attributes
* @return the memeber's name
* @exception XDocletException if an error occures
* @doc.param name="prefix" optional="true" description="the prefix for the name"
* @doc.tag type="content"
*/
public String memberName(Properties attributes) throws XDocletException
{
if (currentMember == null) {
throw new XDocletException("XDtEjbEnv:memberName can only be used inside forAllMemberTags or forAllMethodTags");
}
String name = currentMember.getName();
String prefix = attributes.getProperty("prefix");
if (prefix != null) {
name = prefix + "/" + name;
}
return name;
}
/**
* Returns the method's return type or the field's type. Can only be used inside <code>forAllMemberTags</code> or
* <code>forAllMethodTags</code>.
*
* @return the member's type
* @exception XDocletException if an error occures
* @doc.tag type="content"
*/
public String memberType() throws XDocletException
{
if (currentMember == null) {
throw new XDocletException("XDtEjbEnv:memberType can only be used inside forAllMemberTags or forAllMethodTags");
}
return currentMemberType();
}
/**
* Returns the method signature for the current method. Can only be used inside <code>forAllMethodTags</code>.
*
* @return the current method's signature
* @exception XDocletException if an error occures
* @doc.tag type="content"
*/
public String methodSignature() throws XDocletException
{
if (currentMember == null || !(currentMember instanceof XMethod)) {
throw new XDocletException("XDtEjbEnv:methodSignature can only be used inside forAllMemberTags or forAllMethodTags");
}
XMethod method = (XMethod) currentMember;
StringBuffer sb = new StringBuffer();
if (Modifier.isProtected(method.getModifierSpecifier())) {
sb.append("protected ");
}
if (Modifier.isPublic(method.getModifierSpecifier())) {
sb.append("public ");
}
sb.append(method.getReturnType().getType().getQualifiedName());
sb.append(' ');
sb.append(method.getNameWithSignature(true));
List exceptions = method.getThrownExceptions();
if (exceptions.size() > 0) {
sb.append(" throws ");
for (Iterator it = exceptions.iterator(); it.hasNext(); ) {
XClass exception = (XClass) it.next();
sb.append(exception.getQualifiedName());
if (it.hasNext()) {
sb.append(", ");
}
}
}
return sb.toString();
}
/**
* Returns the value of a parameter.
*
* @param attributes the attributes
* @return the value
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the parameter"
* @doc.param name="values" description="The valid values for the parameter, comma separated. An
* error message is printed if the parameter value is not one of the values."
* @doc.param name="default" description="The default value is returned if parameter not specified
* by user for the tag."
* @doc.param name="mandatory" values="true,false" description="Generate an error if parameter not
* @doc.tag type="content"
*/
public String paramValue(Properties attributes) throws XDocletException
{
attributes.setProperty("tagName", currentTag.getName());
return getTagValue(attributes, currentTagType);
}
/**
* Executes the body only if the current tag has a specified parameter
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the parameter"
* @doc.tag type="body"
*/
public void ifHasParam(String template, Properties attributes) throws XDocletException
{
if (paramValue(attributes) != null) {
generate(template);
}
}
/**
* Executes the body only if the specified tag's value is equal to the specified value
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the parameter"
* @doc.param name="value" optional="false" description="the value of the parameter"
* @doc.tag type="body"
*/
public void ifParamValueEquals(String template, Properties attributes) throws XDocletException
{
if (isParamValueEqual(attributes)) {
generate(template);
}
}
/**
* Executes the body only if the specified tag's value is equal to the specified value
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.param name="paramName" optional="false" description="the name of the parameter"
* @doc.param name="value" optional="false" description="the value of the parameter"
* @doc.tag type="body"
*/
public void ifParamValueNotEquals(String template, Properties attributes) throws XDocletException
{
if (!isParamValueEqual(attributes)) {
generate(template);
}
}
/**
* Executes the body only if the current field type or method return type is primitive.
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.tag type="block"
*/
public void ifPrimitiveMember(String template, Properties attributes) throws XDocletException
{
if (isPrimitiveMember()) {
generate(template);
}
}
/**
* Executes the body only if the current field type or method return type is not a primitive.
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.tag type="block"
*/
public void ifNotPrimitiveMember(String template, Properties attributes) throws XDocletException
{
if (!isPrimitiveMember()) {
generate(template);
}
}
/**
* Executes the body only if the current class has at least one ot the passed tags at field- or method-level
*
* @param template the template
* @param attributes the attributes
* @exception XDocletException if an error occures
* @doc.tag type="block"
* @doc.param name="tagName" optional="false" description="the tag names (comma separated)"
* @doc.param name="paramName" optional="true" description="tags must have this parameter"
* @doc.param name="paramValue" optional="true" description="tags must have this value for the
* parameter with 'paramName'"
*/
public void ifHasTag(String template, Properties attributes) throws XDocletException
{
String tags = attributes.getProperty("tagName");
if (tags == null) {
throw new XDocletException("tagName is mandatory");
}
String paramName = attributes.getProperty("paramName");
String paramValue = attributes.getProperty("paramValue");
if (hasMemberWithTag(getCurrentClass().getMethods(true), tags, paramName, paramValue)) {
generate(template);
}
else {
if (hasMemberWithTag(getCurrentClass().getFields(true), tags, paramName, paramValue)) {
generate(template);
}
}
}
/**
* Executes the passed template for the passed
*
* @param template the template
* @param attributes the parameters
* @param forClass indicates whether the template should be excuted for class level tags
* @param forMethod indicates whether the template should be excuted for method level tags
* @param forField indicates whether the template should be excuted for field level tags
* @throws XDocletException if an error occures
*/
protected void forTags(String template, Properties attributes, boolean forClass, boolean forMethod, boolean forField) throws XDocletException
{
boolean superclasses = TypeConversionUtil.stringToBoolean(attributes.getProperty("superclasses"), true);
String tagName = attributes.getProperty("tagName");
if (tagName == null) {
throw new XDocletException("tagName is mandatory");
}
StringTokenizer st = new StringTokenizer(tagName, ",");
while (st.hasMoreTokens()) {
attributes.setProperty("tagName", st.nextToken());
forTagsInternal(template, attributes, superclasses, forClass, forMethod, forField);
}
}
/**
* Called for each tag in the <code>forTags</code> loop. The default behaviour is to call <code>generate(template)</code>
*
* @param template the template
* @throws XDocletException if an error occures
*/
protected void doGenerate(String template) throws XDocletException
{
generate(template);
}
/**
* Returns whether the current field type or method return type is primitive.
*
* @return <code>true</code> it the current member is primitive
*/
private boolean isPrimitiveMember()
{
if (currentMember != null) {
String type = currentMemberType();
String wrapper = (String) wrappers.get(type);
if (wrapper != null) {
return true;
}
}
return false;
}
/**
* Returns whether the parameter's value is equal to the specfied value
*
* @param attributes the attributes
* @return <code>true</code> if it is equal
* @throws XDocletException if an error occures
*/
private boolean isParamValueEqual(Properties attributes) throws XDocletException
{
String value = attributes.getProperty("value");
if (value == null) {
throw new XDocletException("value is mandatory");
}
return value.equals(paramValue(attributes));
}
/**
* Executes the passed template for the passed
*
* @param template the template
* @param attributes the attriubtes
* @param superclasses indicates whether the superclasses of the current class should also be searched
* @param forClass indicates whether the template should be excuted for class level tags
* @param forMethod indicates whether the template should be excuted for method level tags
* @param forField indicates whether the template should be excuted for field level tags
* @throws XDocletException if an error occures
*/
private void forTagsInternal(String template, Properties attributes, boolean superclasses, boolean forClass, boolean forMethod, boolean forField) throws XDocletException
{
String tagName = attributes.getProperty("tagName");
String paramName = attributes.getProperty("paramName");
String paramValue = attributes.getProperty("paramValue");
// class level tags
if (forClass) {
currentTagType = FOR_CLASS;
Collection tags = getCurrentClass().getDoc().getTags(tagName, superclasses);
for (Iterator it = tags.iterator(); it.hasNext(); ) {
currentTag = (XTag) it.next();
if (tagMatches(currentTag, paramName, paramValue)) {
setCurrentClassTag(currentTag);
currentMember = null;
doGenerate(template);
setCurrentClassTag(null);
}
}
}
// method level tags
if (forMethod) {
currentTagType = FOR_METHOD;
Collection methods = getCurrentClass().getMethods(superclasses);
for (Iterator it = methods.iterator(); it.hasNext(); ) {
XMethod method = (XMethod) it.next();
setCurrentMethod(method);
Collection tags = method.getDoc().getTags(tagName);
for (Iterator it2 = tags.iterator(); it2.hasNext(); ) {
currentTag = (XTag) it2.next();
if (tagMatches(currentTag, paramName, paramValue)) {
setCurrentMethodTag(currentTag);
currentMember = method;
doGenerate(template);
setCurrentMethodTag(null);
}
}
setCurrentMethod(null);
}
}
// field level tags
if (forField) {
currentTagType = FOR_FIELD;
Collection fields = getCurrentClass().getFields(superclasses);
for (Iterator it = fields.iterator(); it.hasNext(); ) {
XField field = (XField) it.next();
setCurrentField(field);
Collection tags = field.getDoc().getTags(tagName);
for (Iterator it2 = tags.iterator(); it2.hasNext(); ) {
currentTag = (XTag) it2.next();
if (tagMatches(currentTag, paramName, paramValue)) {
setCurrentFieldTag(currentTag);
currentMember = field;
doGenerate(template);
setCurrentFieldTag(null);
}
}
setCurrentField(null);
}
}
currentTagType = 0;
}
/**
* Returns whether the passed tag matches for the passed parameter name and value
*
* @param tag the tag
* @param paramName the parameter name or <code>null</code> for no parameter restriction
* @param paramValue the value for the parameter <code>paramName</code> or <code>null</code> for no parameter value
* restriction
* @return <code>true</code> it the tag matches
*/
private boolean tagMatches(XTag tag, String paramName, String paramValue)
{
if (paramName == null) {
return true;
}
String value = tag.getAttributeValue(paramName);
return value != null && (paramValue == null || value.equals(paramValue));
}
/**
* Returns whether the passed Collection of Members has at least one the passed tags for which a value is bound in
* the java componenet environement jndi namespace
*
* @param members a <code>Collection</code> o {@link XMember}
* @param tagNames the tag names
* @param paramName the required parameter or <code>null</code>
* @param paramValue the required value of the parameter <code>paramName</code> or <code>null</code>
* @return <code>true</code> if the passed Collection of Members has at least one of the tags for which a
* value is bound in the java componenet environement jndi namespace
*/
private boolean hasMemberWithTag(Collection members, String tagNames, String paramName, String paramValue)
{
for (Iterator it = members.iterator(); it.hasNext(); ) {
XMember member = (XMember) it.next();
StringTokenizer st = new StringTokenizer(tagNames, ",");
while (st.hasMoreTokens()) {
Collection tags = member.getDoc().getTags(st.nextToken());
if (tags.size() > 0) {
if (paramName == null) {
return true;
}
else {
for (Iterator it2 = tags.iterator(); it2.hasNext(); ) {
XTag tag = (XTag) it2.next();
String value = tag.getAttributeValue(paramName);
if (value != null && (paramValue == null || paramValue.equals(value))) {
return true;
}
}
}
}
}
}
return false;
}
/**
* Returns the type of the current member
*
* @return the type
*/
private String currentMemberType()
{
if (currentMember instanceof XField) {
return ((XField) currentMember).getType().getQualifiedName();
}
else {
return ((XMethod) currentMember).getReturnType().getType().getQualifiedName();
}
}
}