Package org.xdoclet.plugin.ejb

Source Code of org.xdoclet.plugin.ejb.EjbUtils$TagReference

/*
* Copyright (c) 2005
* XDoclet Team
* All rights reserved.
*/
package org.xdoclet.plugin.ejb;

import java.text.MessageFormat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.generama.ConfigurableDocletTagFactory;

import org.xdoclet.plugin.ejb.qtags.EjbActivationConfigPropertyTag;
import org.xdoclet.plugin.ejb.qtags.EjbBeanTag;
import org.xdoclet.plugin.ejb.qtags.EjbEjbExternalRefTag;
import org.xdoclet.plugin.ejb.qtags.EjbEjbRefTag;
import org.xdoclet.plugin.ejb.qtags.EjbEjbServiceRefTag;
import org.xdoclet.plugin.ejb.qtags.EjbFinderTag;
import org.xdoclet.plugin.ejb.qtags.EjbPermissionTag;
import org.xdoclet.plugin.ejb.qtags.EjbPkTag;
import org.xdoclet.plugin.ejb.qtags.EjbRelationTag;
import org.xdoclet.plugin.ejb.qtags.EjbResourceEnvRefTag;
import org.xdoclet.plugin.ejb.qtags.EjbResourceRefTag;
import org.xdoclet.plugin.ejb.qtags.EjbTransactionMethodTag;
import org.xdoclet.plugin.ejb.qtags.EjbTransactionTag;
import org.xdoclet.plugin.ejb.qtags.TagLibrary;
import org.xdoclet.plugin.ejb.qtags.parameter.RoleList;
import org.xdoclet.plugin.ejb.util.DuplicatedJavaMethod;

import com.thoughtworks.qdox.model.AbstractJavaEntity;
import com.thoughtworks.qdox.model.BeanProperty;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.Type;

/**
* This class holds common functionality used by all EJB plugin.
* These methods will mostly be called by templates (including
* vendor specific templates). Reusable code should be placed here.
*
* @author Diogo Quintela
* @author Aslak Hellesøy
* @version $Revision: 534 $
*
* TODO: Centralize some logic in findByPrimaryKey / EjbPkTag
* TODO: Refactor this into smaller units
* TODO: Try not to centralize all methods in EjbUtils. Move some into EjbJarXmlPlugin.
*/
public class EjbUtils {
    // Bean / Methods / Interfaces flags
    public static final int REMOTE = 1 << 0;
    public static final int REMOTE_HOME = 1 << 1;
    public static final int LOCAL = 1 << 2;
    public static final int LOCAL_HOME = 1 << 3;
    public static final int SERVICE_END_POINT = 1 << 4;

    // Bean flags
    public static final int BEAN_EJB = 1 << 0;
    public static final int BEAN_ENTITY = 1 << 1;
    public static final int BEAN_BMP = 1 << 2;
    public static final int BEAN_CMP = 1 << 3;
    public static final int BEAN_SESSION = 1 << 4;
    public static final int BEAN_STATE_FULL = 1 << 5;
    public static final int BEAN_STATE_LESS = 1 << 6;
    public static final int BEAN_MESSAGE_DRIVEN = 1 << 7;

    // Tag find mechanism flags
    public static final int TAG_FIND_CLASS = 1 << 0;
    public static final int TAG_FIND_METHOD = 1 << 1;
    public static final int TAG_FIND_FIELD = 1 << 2;
    public static final int TAG_FIND_RECURSIVE = 1 << 3;

    // Methods flags
    public static final int IFACE_METHOD_COMPONENT = 1 << 0;
    public static final int IFACE_METHOD_CREATE = 1 << 1;
    public static final int IFACE_METHOD_HOME = 1 << 2;
    public static final int IFACE_METHOD_REMOVE = 1 << 3;
    public static final int IFACE_METHOD_FINDER = 1 << 4;
    public static final int IFACE_METHOD_SELECT = 1 << 5;

    // Extra method flags
    public static final int METADATA_METHOD_PERSISTENCE_FIELD = 1 << 6;
    public static final int METADATA_METHOD_PRIMARY_KEY_FIELD = 1 << 7;
    public static final int METADATA_METHOD_RELATION_FIELD = 1 << 8;

    // Interface type auxiliar constants
    public static final String REMOTE_INTERFACE = "Remote";
    public static final String REMOTE_HOME_INTERFACE = "Home";
    public static final String LOCAL_INTERFACE = "Local";
    public static final String LOCAL_HOME_INTERFACE = "LocalHome";
    public static final String SERVICE_END_POINT_INTERFACE = "ServiceEndpoint";

    /** The logging object */
    protected static Log log = LogFactory.getLog(EjbUtils.class);

    /**
     * Maps primitive types to their wrapper classes
     */
    private final static Map wrappers;

    static {
        Map wps = new HashMap();
        wps.put("boolean", new Type(Boolean.class.getName()));
        wps.put("byte", new Type(Byte.class.getName()));
        wps.put("char", new Type(Character.class.getName()));
        wps.put("short", new Type(Short.class.getName()));
        wps.put("int", new Type(Integer.class.getName()));
        wps.put("float", new Type(Float.class.getName()));
        wps.put("long", new Type(Long.class.getName()));
        wps.put("double", new Type(Double.class.getName()));
        wrappers = Collections.unmodifiableMap(wps);
    }

    // Patterns for method name matching
    private static final Pattern methodPattern;
    private static final Pattern paramPattern;
    private static final Pattern dimensionPattern;

    static {
        // Defines a small BNF grammar for parsing method signatures
        // See @ejb.finder signature
        String space = "[ \\t]";
        String spaces = "(?:" + space + ")*";
        String typeIdentifier = "[a-zA-Z_][a-zA-Z0-9_]+";
        String typeDimension = "\\[" + spaces + "\\]";
        String typeDimensions = "(?:" + spaces + typeDimension + ")*";
        String typeDefination = "(?:" + typeIdentifier + "(?:\\." + typeIdentifier + ")*" + typeDimensions + ")";
        String paramWithName = "(" + typeDefination + ")" + space + spaces + "(" + typeIdentifier + ")" + "(" +
            typeDimensions + ")";
        String paramWithoutName = "(" + typeDefination + ")";
        String paramDefinition = "(?:(" + paramWithName + ")|(" + paramWithoutName + "))";
        String paramsDefinition = "(?:(?:" + spaces + ")|(?:" + spaces + paramDefinition + spaces + ")|(?:" + spaces +
            paramDefinition + spaces + "(?:," + spaces + paramDefinition + spaces + ")+))";
        String methodDefination = spaces + "(" + typeDefination + ")" + space + spaces + "(" + typeIdentifier + ")" +
            spaces + "\\((" + paramsDefinition + ")\\)" + spaces;

        // Let's define the patterns
        methodPattern = Pattern.compile(methodDefination);
        paramPattern = Pattern.compile(paramDefinition);
        dimensionPattern = Pattern.compile(typeDimension);
    }

    private final EjbConfig config;

    //    private Map ejbCache = new HashMap();
    public EjbUtils() {
        this(EjbRuntime.getConfig());
    }

    public EjbUtils(EjbConfig config) {
        this.config = config;
    }

    public String getEjbName(JavaClass clazz) {
        String result = clazz.getNamedParameter(TagLibrary.EJB_BEAN, "name");

        if (result == null) {
            result = clazz.getName();
            result = result.replaceAll(config.getEjbReplaceRegex(), "");
        }

        return result;
    }

    public String expandPattern(String pattern, String arg) {
        return expandPattern(pattern, new String[] {arg});
    }

    public String expandPattern(String pattern, String[] args) {
        if (pattern == null) {
            return null;
        }

        String formattedName = MessageFormat.format(pattern, args);

        if (log.isDebugEnabled()) {
            log.debug("Expanded value is '" + formattedName + "'");
        }

        return formattedName;
    }

    public String expandPattern(String pattern, JavaClass clazz) {
        if (pattern == null) {
            return null;
        }

        String ejbName = getEjbName(clazz);

        if (log.isDebugEnabled()) {
            log.debug("Expanding pattern '" + pattern + "' with ejb name as argument '" + ejbName + "'");
        }

        return expandPattern(pattern, ejbName);
    }

    public int getMethodType(JavaMethod method) {
        int retVal = 0;

        if (method.getTagByName(TagLibrary.EJB_INTERFACE_METHOD) != null) {
            retVal = IFACE_METHOD_COMPONENT;
        } else if (method.getName().equals("ejbCreate") && (method.getTagByName(TagLibrary.EJB_CREATE_METHOD) != null)) {
            retVal = IFACE_METHOD_CREATE;
        } else if (method.getName().startsWith("ejbHome") && (method.getName().length() > "ejbHome".length()) &&
                (method.getTagByName(TagLibrary.EJB_HOME_METHOD) != null)) {
            retVal = IFACE_METHOD_HOME;
        } else if (method.getName().equals("ejbRemove")) {
            retVal = IFACE_METHOD_REMOVE;
        } else if (method.getName().startsWith("ejbFind") && (method.getName().length() > "ejbFind".length())) {
            if ((method.getReturns() != null) && !method.getReturns().isVoid()) {
                retVal = IFACE_METHOD_FINDER;
            } else {
                if (log.isWarnEnabled()) {
                    log.warn("Inconsistent return type for finder method. Ignoring method. " +
                        method.getDeclarationSignature(true));
                }
            }
        } else if (method.getName().startsWith("ejbSelect") && (method.getName().length() > "ejbSelect".length())) {
            if ((method.getReturns() != null) && !method.getReturns().isVoid()) {
                retVal = IFACE_METHOD_SELECT;
            } else {
                if (log.isWarnEnabled()) {
                    log.warn("Inconsistent return type for select method. Ignoring method. Return-type=" +
                        method.getDeclarationSignature(true));
                }
            }
        }

        return retVal;
    }

    /**
     * Extension of getMethodType that gathers extra method information using method class
     *
     * @param method The method to gather info for
     *
     * @return The metadata bits
     */
    public int getMethodMetadata(JavaMethod method) {
        return getMethodMetadata(method.getParentClass(), method);
    }

    /**
     * Extension of getMethodType that gathers extra method information
     *
     * @param javaClass The associated JavaClass
     * @param method The method to gather info for
     *
     * @return The metadata bits
     */
    public int getMethodMetadata(JavaClass javaClass, JavaMethod method) {
        int retVal = getMethodType(method);
        EjbBeanTag beanTag = (EjbBeanTag) javaClass.getTagByName(TagLibrary.EJB_BEAN);
        DocletTag tag;

        if ((tag = method.getTagByName(TagLibrary.EJB_PERSISTENCE_FIELD)) != null) {
            if ((method.isPropertyAccessor() /*|| method.isPropertyMutator()*/)) {
                retVal |= METADATA_METHOD_PERSISTENCE_FIELD;
            } else {
                if (log.isWarnEnabled()) {
                    log.warn("Ignoring " + TagLibrary.EJB_PERSISTENCE_FIELD + " tag. It should be place on property accessor. " + //  or property mutator." +
                        method.getDeclarationSignature(true) + " - " + EjbUtils.tagToString(tag));
                }
            }
        } else if ((tag = method.getTagByName(TagLibrary.EJB_RELATION)) != null) {
            // METADATA_METHOD_RELATION_FIELD isn't compatible with METADATA_METHOD_PERSISTENCE_FIELD ??
            if (method.isPropertyAccessor()) {
                retVal |= METADATA_METHOD_RELATION_FIELD;

                // TODO: Is METADATA_METHOD_RELATION_FIELD compatible with METADATA_METHOD_PERSISTENCE_FIELD ??
            } else {
                if (log.isWarnEnabled()) {
                    log.warn("Ignoring " + TagLibrary.EJB_RELATION + " tag. It should be place on property accessor." +
                        method.getDeclarationSignature(true) + " - " + EjbUtils.tagToString(tag));
                }
            }
        }

        if (beanTag != null && beanTag.getPrimkeyField() != null) {
            BeanProperty prop = javaClass.getBeanProperty(beanTag.getPrimkeyField());

            try {
                if (prop == null || prop.getAccessor() == null) {
                    throw getErrorWithTagLocation(beanTag,
                        "Could not find getter for prim-key-field: " + beanTag.getPrimkeyField());
                }

                // Ok, now this must be that accessor for the key
                if (method == prop.getAccessor()) {
                    retVal |= METADATA_METHOD_PRIMARY_KEY_FIELD;
                }
            } catch (Error e) {
                log.error(e.getMessage());
                throw e;
            }
        } else if ((tag = method.getTagByName(TagLibrary.EJB_PK_FIELD)) != null) {
            //            try {
            // method must be a property acessor
            if (method.isPropertyAccessor()) {
                retVal |= METADATA_METHOD_PRIMARY_KEY_FIELD;

                // throw getErrorWithTagLocation(tag, "Tag must be used on a property getter");
            } else {
                if (log.isWarnEnabled()) {
                    log.warn("Ignoring " + TagLibrary.EJB_PK_FIELD + " tag. It should be place on property accessor." +
                        method.getDeclarationSignature(true) + " - " + EjbUtils.tagToString(tag));
                }
            }

            // retVal |= METADATA_METHOD_PRIMARY_KEY_FIELD;
            //            } catch (Error e) {
            //                log.error(e.getMessage());
            //                throw e;
            //            }
        }

        return retVal;
    }

    public Collection getBeans(Collection metadata) {
        return getBeans(metadata, BEAN_EJB);
    }

    public Collection getCMPBeans(Collection metadata) {
        return getBeans(metadata, BEAN_CMP);
    }

    public Collection getBMPBeans(Collection metadata) {
        return getBeans(metadata, BEAN_BMP);
    }

    public Collection getEntityBeans(Collection metadata) {
        return getBeans(metadata, BEAN_ENTITY);
    }

    public Collection getSessionBeans(Collection metadata) {
        return getBeans(metadata, BEAN_SESSION);
    }

    public Collection getMessageDrivenBeans(Collection metadata) {
        return getBeans(metadata, BEAN_MESSAGE_DRIVEN);
    }

    public Collection getStateFullBeans(Collection metadata) {
        return getBeans(metadata, BEAN_STATE_FULL);
    }

    public Collection getStateLessBeans(Collection metadata) {
        return getBeans(metadata, BEAN_STATE_LESS);
    }

    public Collection getBeans(final Collection metadata, final int beanType) {
        return CollectionUtils.select(metadata,
            new Predicate() {
                public boolean evaluate(Object meta) {
                    JavaClass javaClass = (JavaClass) meta;
                    return hasFlag(getBeanType(javaClass), beanType);
                }
            });
    }

    public Collection getSelectMethods(final JavaClass clazz) {
        if (!isCMP(clazz)) {
            return null;
        }

        // Select all public abstract methods started by "ejbSelect" with a non-void
        // return type
        return CollectionUtils.select(Arrays.asList(clazz.getMethods(true)),
            new Predicate() {
                public boolean evaluate(Object object) {
                    JavaMethod method = (JavaMethod) object;
                    int methodType = getMethodType(method);
                    boolean retVal = hasFlag(methodType, IFACE_METHOD_SELECT);
                    retVal = retVal && method.isAbstract();
                    retVal = retVal && method.isPublic();
                    return retVal;
                }
            });
    }

    public static boolean hasFlag(int fullFlag, int checkFlag) {
        return (fullFlag & checkFlag) == checkFlag;
    }

    public Collection getCMPFields(final JavaClass clazz) {
        if (clazz == null) {
            throw new Error();
        }

        if (!isCMP(clazz)) {
            return null;
        }

        return CollectionUtils.select(Arrays.asList(clazz.getMethods(true)),
            new Predicate() {
                public boolean evaluate(Object object) {
                    int methodFlags = getMethodMetadata((JavaMethod) object);
                    return hasFlag(methodFlags, METADATA_METHOD_PERSISTENCE_FIELD);

                    /*
                       JavaMethod method = (JavaMethod) object;
                       EjbPersistenceFieldTag persistenceTag = (EjbPersistenceFieldTag) method.getTagByName(
                               "ejb.persistence-field");
                       return (method.isPropertyAccessor() && persistenceTag != null);
                     */
                }
            });
    }

    public boolean shouldGenerate(Object metadata) {
        JavaClass javaClass = (JavaClass) metadata;
        boolean isEjb = isEJB(javaClass);
        EjbBeanTag beanTag = (EjbBeanTag) javaClass.getTagByName(TagLibrary.EJB_BEAN);
        boolean ignore = (beanTag != null) && !beanTag.isGenerate();
        return isEjb && !ignore;
    }

    public String firstSentence(String comment) {
        if (comment == null) {
            return null;
        }

        int idx = comment.indexOf('\n');
        return idx >= 0 ? comment.substring(0, idx) : comment;
    }

    public String description(JavaClass javaClass) {
        String description = javaClass.getNamedParameter(TagLibrary.EJB_BEAN, "description");

        if (description == null) {
            description = firstSentence(javaClass.getComment());
        }

        if (description == null) {
            description = "No description";
        }

        return description;
    }

    public JavaMethod getFinderMethodBySignature(String finderSignature) {
        return getMethodBySignature(finderSignature);
    }

    public JavaMethod getMethodBySignature(String signature) {
        Matcher matcher = methodPattern.matcher(signature);

        if (matcher.matches()) {
            JavaMethod method = new JavaMethod(new Type(matcher.group(1)), matcher.group(2));

            // Now let's find out the arguments
            matcher = paramPattern.matcher(matcher.group(3));
            int beginIdx = 0;
            int count = 0;
            String paramType = null;
            String paramName = null;
            List paramLst = new ArrayList();

            while (matcher.find(beginIdx)) {
                int paramDim = 0;

                if (matcher.group(1) != null) {
                    paramType = matcher.group(2) + (matcher.group(4) != null ? matcher.group(4) : "");
                    paramName = matcher.group(3);
                } else {
                    paramType = matcher.group(6);
                    paramName = "_arg" + (count++);
                }

                // Now we need to parse paramType for it's dimensions
                Matcher dMatcher = dimensionPattern.matcher(paramType);

                if (dMatcher.find()) {
                    paramType = paramType.substring(0, dMatcher.start());
                    paramDim = 1;

                    while (dMatcher.find(dMatcher.end())) {
                        paramDim++;
                    }
                }

                paramLst.add(new JavaParameter(new Type(paramType, paramDim), paramName));
                beginIdx = matcher.end();
            }

            method.setParameters((JavaParameter[]) paramLst.toArray(new JavaParameter[0]));
            return method;
        }

        throw new Error("Invalid finder signature '" + signature + "'");
    }

    /**
     * Returns true if the given JavaClass has a Remote Interface
     * (independend of whether one is generated or not).
     */
    public boolean hasRemoteInterface(JavaClass javaClass) {
        return hasFlag(getViewType(javaClass), REMOTE)
        && !isMessageDrivenBean(javaClass);
    }

    /**
     * Returns true if the given JavaClass has a Remote Home Interface
     * (independend of whether one is generated or not).
     */
    public boolean hasRemoteHomeInterface(JavaClass javaClass) {
        return hasFlag(getViewType(javaClass), REMOTE_HOME)
        && !isMessageDrivenBean(javaClass);
    }

    /**
     * Returns true if the given JavaClass has a Local Interface
     * (independend of whether one is generated or not).
     */
    public boolean hasLocalInterface(JavaClass javaClass) {
        return hasFlag(getViewType(javaClass), LOCAL)
            && !isMessageDrivenBean(javaClass);
    }

    /**
     * Returns true if the given JavaClass has a Local Home Interface
     * (independend of whether one is generated or not).
     */
    public boolean hasLocalHomeInterface(JavaClass javaClass) {
        return hasFlag(getViewType(javaClass), LOCAL_HOME)
        && !isMessageDrivenBean(javaClass);
    }
   
    /**
     * Returns true if the given JavaClass has a Service Endpoint Interface
     * (independend of whether one is generated or not).
     */
    public boolean hasServiceEndPoint(JavaClass javaClass) {
        return hasFlag(getViewType(javaClass), SERVICE_END_POINT);
    }

    public boolean isViewType(JavaClass javaClass, String viewType) {
        return hasFlag(getViewType(javaClass), getViewType(viewType));
    }

    public int getInterfaceType(String ifaceType) {
        EjbVersion version = config.getEjbVersion();
        int retVal = 0;

        if (REMOTE_INTERFACE.equals(ifaceType)) {
            retVal |= REMOTE;
        } else if (REMOTE_HOME_INTERFACE.equals(ifaceType)) {
            retVal |= REMOTE_HOME;
        } else if (LOCAL_INTERFACE.equals(ifaceType)) {
            retVal |= LOCAL;
        } else if (LOCAL_HOME_INTERFACE.equals(ifaceType)) {
            retVal |= LOCAL_HOME;
        } else if (version.greaterOrEquals(EjbVersion.EJB_2_1) && SERVICE_END_POINT_INTERFACE.equals(ifaceType)) {
            retVal |= SERVICE_END_POINT;
        }

        return retVal;
    }

    public int getViewType(String viewType) {
        EjbVersion version = config.getEjbVersion();
        int retVal = 0;
        viewType = viewType.toLowerCase();

        if (version.greaterOrEquals(EjbVersion.EJB_2_1)) {
            if ("all".equals(viewType)) {
                // retVal |= REMOTE | REMOTE_HOME;
                // retVal |= LOCAL | LOCAL_HOME;
                retVal |= SERVICE_END_POINT;
            } else if (viewType.indexOf("service-endpoint") != -1) {
                retVal |= SERVICE_END_POINT;
            }
        }

        if ("both".equals(viewType) || "all".equals(viewType)) {
            retVal |= REMOTE | REMOTE_HOME;
            retVal |= LOCAL | LOCAL_HOME;
        }

        if (viewType.indexOf("remote") != -1) {
            retVal |= REMOTE | REMOTE_HOME;
        }

        if (viewType.indexOf("local") != -1) {
            retVal |= LOCAL | LOCAL_HOME;
        }

        return retVal;
    }

    public int getViewType(JavaClass javaClass) {
        EjbVersion version = config.getEjbVersion();
        String viewType = javaClass.getNamedParameter(TagLibrary.EJB_BEAN, "view-type");

        if (!version.greaterOrEquals(EjbVersion.EJB_2_0)) {
            viewType = "remote";
        }

        if (viewType == null) {
            viewType = "both";
        }

        return getViewType(viewType);
    }

    public int getViewType(JavaMethod method, JavaClass javaClass) {
        // We should mask method view type with bean's view type
        // ie, A method cannot have a view type not supported by the bean
        int beanViewType = getViewType(javaClass);
        int retVal = 0;
        int filterMask = beanViewType;
        String viewType = null;
        int methodType = getMethodType(method);

        switch (methodType) {
            case IFACE_METHOD_COMPONENT:
                viewType = method.getNamedParameter(TagLibrary.EJB_INTERFACE_METHOD, "view-type");
                retVal = (viewType != null) ? getViewType(viewType) : beanViewType;

                // If it's a interface method it can only live in business-interfaces
                // Let's remove home interfaces if inherited
                filterMask &= ~(REMOTE_HOME | LOCAL_HOME);
                break;

            case IFACE_METHOD_CREATE:
                viewType = method.getNamedParameter(TagLibrary.EJB_CREATE_METHOD, "view-type");
                retVal = (viewType != null) ? getViewType(viewType) : beanViewType;

                // If it's a create method, it can live only in home-interfaces
                // Let's set mask to guarantee only homes
                filterMask &= (REMOTE_HOME | LOCAL_HOME);
                break;

            case IFACE_METHOD_HOME:
                viewType = method.getNamedParameter(TagLibrary.EJB_HOME_METHOD, "view-type");
                retVal = (viewType != null) ? getViewType(viewType) : beanViewType;

                // If it's a home method, it can live only in home-interfaces
                // Let's set mask to guarantee only homes
                filterMask &= (REMOTE_HOME | LOCAL_HOME);
                break;

            case IFACE_METHOD_REMOVE:
            case IFACE_METHOD_FINDER:
            case IFACE_METHOD_SELECT:

                // TODO: remove methods are only in home interfaces ? Or can be in business ?
                // TODO: finder methods are only in home interfaces ? Or can be in business ?
                // TODO: select methods are only in home interfaces ? Or can be in business ?
                retVal = (REMOTE_HOME | LOCAL_HOME);
                break;

            default:

                // By default a method will not be included, so will not have a view type
                break;
        }

        // Let's mask by bitwise filter
        retVal &= filterMask;
        return retVal;
    }

    public boolean isEJB(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_EJB);
    }

    public boolean isCMP(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_CMP);
    }

    public boolean isBMP(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_BMP);
    }

    public boolean isEntityBean(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_ENTITY);
    }

    public boolean isSessionBean(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_SESSION);
    }

    public boolean isMessageDrivenBean(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_MESSAGE_DRIVEN);
    }

    public boolean isStateFull(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_STATE_FULL);
    }

    public boolean isStateLess(JavaClass javaClass) {
        return hasFlag(getBeanType(javaClass), BEAN_STATE_LESS);
    }

    public int getBeanType(JavaClass javaClass) {
        EjbVersion version = config.getEjbVersion();
        String beanType = javaClass.getNamedParameter(TagLibrary.EJB_BEAN, "type");
        int retVal = 0;

        if (javaClass.isA("javax.ejb.EntityBean")) {
            retVal |= BEAN_EJB;
            retVal |= BEAN_ENTITY;

            if (beanType != null && "bmp".equalsIgnoreCase(beanType)) {
                retVal |= BEAN_BMP;
            } else {
                retVal |= BEAN_CMP;
            }
        } else if (javaClass.isA("javax.ejb.SessionBean")) {
            // TODO: Do session beans need to be concrete (ie, not abstract) ?
            retVal |= BEAN_EJB;
            retVal |= BEAN_SESSION;

            if (beanType != null && "stateful".equalsIgnoreCase(beanType)) {
                retVal |= BEAN_STATE_FULL;
            } else {
                retVal |= BEAN_STATE_LESS;
            }
        } else if (version.greaterOrEquals(EjbVersion.EJB_2_0) && javaClass.isA("javax.ejb.MessageDrivenBean")) {
            retVal |= BEAN_EJB;
            retVal |= BEAN_MESSAGE_DRIVEN;
        }

        return retVal;
    }

    public String reentrant(JavaClass javaClass) {
        EjbVersion version = config.getEjbVersion();
        String reentrant = javaClass.getNamedParameter(TagLibrary.EJB_BEAN, "reentrant");
        String retVal = Boolean.valueOf(reentrant).toString();

        if (!version.greaterOrEquals(EjbVersion.EJB_2_1)) {
            // ejb spec 1.1 and 2.0 require that the first character is upper case
            // ejb spec 2.1+ requires that all characters are lower case
            retVal = retVal.substring(0, 1).toUpperCase() + retVal.substring(1);
        }

        return retVal;
    }

    public String persistenceType(JavaClass javaClass) {
        int beanType = getBeanType(javaClass);
        String retVal = null;

        if (hasFlag(beanType, BEAN_CMP)) {
            retVal = "Container";
        } else if (hasFlag(beanType, BEAN_BMP)) {
            retVal = "Bean";
        }

        return retVal;
    }

    public String entityType(JavaClass javaClass) {
        int beanType = getBeanType(javaClass);

        if (hasFlag(beanType, BEAN_ENTITY)) {
            return "entity";
        } else if (hasFlag(beanType, BEAN_SESSION)) {
            return "session";
        } else if (hasFlag(beanType, BEAN_MESSAGE_DRIVEN)) {
            return "message-driven";
        }

        // This should not eventually happen
        // shouldGenerate should not return true for it
        return null;
    }

    public String beanType(JavaClass javaClass) {
        int beanType = getBeanType(javaClass);

        if (hasFlag(beanType, BEAN_ENTITY)) {
            return "Entity";
        } else if (hasFlag(beanType, BEAN_SESSION)) {
            return "Session";
        } else if (hasFlag(beanType, BEAN_MESSAGE_DRIVEN)) {
            return "Message Driven"; // Not used yet !
        }

        return null;
    }

    public String sessionType(JavaClass javaClass) {
        int beanType = getBeanType(javaClass);
        String retVal = null;

        if (hasFlag(beanType, BEAN_STATE_FULL)) {
            retVal = "Stateful";
        } else if (hasFlag(beanType, BEAN_STATE_LESS)) {
            retVal = "Stateless";
        }

        return retVal;
    }

    public String getNamedParameter(AbstractJavaEntity javaEntity, String tagName, String parameterName, String defValue) {
        String value = javaEntity.getNamedParameter(tagName, parameterName);

        if (value == null) {
            value = defValue;
        }

        return value;
    }

    public String getNamedParameter(DocletTag tag, String parameterName, String defValue) {
        String value = tag.getNamedParameter(parameterName);

        if (value == null) {
            value = defValue;
        }

        return value;
    }

    public boolean hasActivationConfig(JavaClass javaClass) {
        EjbBeanTag beanTag = (EjbBeanTag) javaClass.getTagByName(TagLibrary.EJB_BEAN);
        EjbActivationConfigPropertyTag actTag = (EjbActivationConfigPropertyTag) javaClass.getTagByName(
                TagLibrary.EJB_ACTIVATION_CONFIG_PROPERTY);
        boolean retVal = false;

        if (beanTag != null) {
            retVal |= (beanTag.getDestinationType() != null);
            retVal |= (beanTag.getAcknowledgeMode() != null);
            retVal |= (beanTag.getSubscriptionDurability() != null);
            retVal |= (beanTag.getMessageSelector() != null);
        }

        retVal |= (actTag != null);
        return retVal;
    }

    public boolean isUseSoftLocking(JavaClass javaClass) {
        EjbBeanTag beanTag = (EjbBeanTag) javaClass.getTagByName(TagLibrary.EJB_BEAN);
        return (beanTag != null) && beanTag.isUseSoftLocking();
    }

    public Collection findTagsByName(JavaClass javaClass, String tagName) {
        List list = new ArrayList();
        findTagsByNameRecursive(list, javaClass, tagName,
            TAG_FIND_RECURSIVE | TAG_FIND_CLASS | TAG_FIND_METHOD | TAG_FIND_FIELD);
        return list;
    }

    private void findTagsByNameRecursive(List list, JavaClass javaClass, String tagName, int findFlags) {
        DocletTag[] tags;

        if (hasFlag(findFlags, TAG_FIND_CLASS)) {
            tags = javaClass.getTagsByName(tagName);

            for (int i = 0; i < tags.length; i++) {
                list.add(new TagReference(tags[i], javaClass));
            }

            // list.addAll(Arrays.asList(javaClass.getTagsByName(tagName)));
        }

        if (hasFlag(findFlags, TAG_FIND_METHOD)) {
            JavaMethod[] methods = javaClass.getMethods();

            for (int i = 0; i < methods.length; i++) {
                tags = methods[i].getTagsByName(tagName);

                for (int j = 0; j < tags.length; j++) {
                    list.add(new TagReference(tags[j], methods[i]));
                }

                // list.addAll(Arrays.asList(methods[i].getTagsByName(tagName)));
            }
        }

        if (hasFlag(findFlags, TAG_FIND_FIELD)) {
            JavaField[] fields = javaClass.getFields();

            for (int i = 0; i < fields.length; i++) {
                tags = fields[i].getTagsByName(tagName);

                for (int j = 0; j < tags.length; j++) {
                    list.add(new TagReference(tags[j], fields[i]));
                }

                // list.addAll(Arrays.asList(fields[i].getTagsByName(tagName)));
            }
        }

        if (hasFlag(findFlags, TAG_FIND_RECURSIVE)) {
            JavaClass superclass = javaClass.getSuperJavaClass();

            // THIS IS A HACK AROUND A BUG THAT MUST BE SOLVED!!!
            // SOMETIMES A CLASS RETURNS ITSELF AS SUPER ?!?!?!?!?!
            if ((superclass != null) && (superclass != javaClass)) {
                findTagsByNameRecursive(list, superclass, tagName, findFlags);
            }

            JavaClass[] implementz = javaClass.getImplementedInterfaces();

            for (int h = 0; h < implementz.length; h++) {
                if (implementz[h] != null) {
                    findTagsByNameRecursive(list, implementz[h], tagName, findFlags);
                }
            }
        }
    }

    private Type wrapType(Type type) {
        Type wrapperType = (Type) wrappers.get(type.toString());
        return wrapperType != null ? wrapperType : type;
    }

    public String refType(TagReference reference) {
        DocletTag tag = reference.getTag();
        String refType = null;
        String paramName = null;

        if (tag instanceof EjbEjbServiceRefTag) {
            refType = ((EjbEjbServiceRefTag) tag).getInterface();
            paramName = "interface";
        } else if (tag instanceof EjbResourceRefTag) {
            refType = ((EjbResourceRefTag) tag).getResType();
            paramName = "res-type";
        } else if (tag instanceof EjbResourceEnvRefTag) {
            refType = ((EjbResourceEnvRefTag) tag).getType();
            paramName = "type";
        } else {
            throw getErrorWithTagLocation(tag, "Unexpected tag type");
        }

        if (reference.getClazz() != null) {
            // Class level tag
            if (refType == null) {
                throw getErrorWithTagLocation(tag, "'" + paramName + "' is mandatory for class level tag");
            }
        } else if (reference.getField() != null) {
            // Field level tag
            refType = wrapType(reference.getField().getType()).toString();
        } else if (reference.getMethod() != null) {
            // Method level tag
            if (reference.getMethod().getReturns().isVoid()) {
                throw getErrorWithTagLocation(tag,
                    "Cannot set @" + tag.getName() + " on a method with a void return type");
            }

            refType = wrapType(reference.getMethod().getReturns()).toString();
        }

        if (refType == null) {
            throw getErrorWithTagLocation(tag, "'" + paramName + "' could not be resolved");
        }

        return refType;
    }

    public Error getErrorWithTagLocation(DocletTag tag, String message) {
        return new Error(tagToString(tag) + ": " + message);
    }

    public static String getMessageWithTagLocation(DocletTag tag, String message) {
        return tagToString(tag) + ": " + message;
    }

    public static String getMessageWithMethodLocation(JavaMethod method, String message) {
        return methodToString(method) + ": " + message;
    }

    public static String methodToString(JavaMethod method) {
        StringBuffer locationBuf = new StringBuffer();
        locationBuf.append(method.getParentClass().getFullyQualifiedName());
        locationBuf.append(':');
        locationBuf.append(method.getDeclarationSignature(false));
        return locationBuf.toString();
    }

    public static String tagToString(DocletTag tag) {
        StringBuffer retBuf = new StringBuffer();
        retBuf.append("@");
        retBuf.append(tag.getName());
        retBuf.append(" ");
        retBuf.append(tag.getValue());
        retBuf.append(" (");
        retBuf.append(ConfigurableDocletTagFactory.getLocation(tag));
        retBuf.append(":");
        retBuf.append(tag.getLineNumber());
        retBuf.append(")");
        return retBuf.toString();
    }

    public String refName(TagReference reference) {
        DocletTag tag = reference.getTag();
        String refName = null;
        String paramName = null;

        if (tag instanceof EjbEjbServiceRefTag) {
            refName = ((EjbEjbServiceRefTag) tag).getName_();
            paramName = "name";
        } else if (tag instanceof EjbResourceRefTag) {
            refName = ((EjbResourceRefTag) tag).getResRefName();
            paramName = "res-ref-name";
        } else if (tag instanceof EjbResourceEnvRefTag) {
            refName = ((EjbResourceEnvRefTag) tag).getName_();
            paramName = "name";
        } else {
            throw getErrorWithTagLocation(tag, "Unexpected tag type");
        }

        if (refName == null) {
            if (reference.getClazz() != null) {
                // Class level tag
                throw getErrorWithTagLocation(tag, "'" + paramName + "' is mandatory for class level tag");
            } else if (reference.getField() != null) {
                // Field level tag
                refName = reference.getField().getName();
            } else if (reference.getMethod() != null) {
                // Method level tag
                refName = reference.getMethod().getName();
            }
        }

        if (refName == null) {
            throw getErrorWithTagLocation(tag, "'" + paramName + "' could not be resolved");
        }

        return refName;
    }

    public String ejbRefName(TagReference reference) {
        return ejbRefName(reference, null);
    }

    public String ejbRefName(TagReference reference, JavaClass refClass) {
        DocletTag tag = reference.getTag();
        String ejbRefName = null;
        String viewType = null;
        String paramName = null;

        if (tag instanceof EjbEjbRefTag) {
            ejbRefName = ((EjbEjbRefTag) tag).getRefName();
            viewType = ((EjbEjbRefTag) tag).getViewType();
            paramName = "ref-name";
        } else if (tag instanceof EjbEjbExternalRefTag) {
            ejbRefName = ((EjbEjbExternalRefTag) tag).getRefName();
            viewType = ((EjbEjbExternalRefTag) tag).getViewType();
            paramName = "ref-name";
        } else {
            throw getErrorWithTagLocation(tag, "Unexpected tag type");
        }

        if (ejbRefName == null) {
            if (reference.getClazz() != null) {
                // Class level tag
                if (refClass != null) {
                    ejbRefName = getComponentName(refClass, isRemoteEjbRef(viewType, refClass) ? REMOTE : LOCAL, true);
                } else {
                    throw getErrorWithTagLocation(tag, "'" + paramName + "' is mandatory for class level tag");
                }
            } else if (reference.getField() != null) {
                // Field level tag
                ejbRefName = reference.getField().getName();
            } else if (reference.getMethod() != null) {
                // Method level tag
                ejbRefName = reference.getMethod().getName();
            }
        }

        if (ejbRefName == null) {
            throw getErrorWithTagLocation(tag, "'" + paramName + "' could not be resolved");
        }

        return ejbRefName;
    }

    public String getComponentName(JavaClass clazz, int viewType, boolean prependEjb) {
        StringBuffer retName = new StringBuffer();

        String ejbName = getEjbName(clazz).replace('.', '/');
        if (prependEjb && !ejbName.startsWith("ejb/")) {
            retName.append("ejb/");
        }
        retName.append(ejbName);
        if (viewType == LOCAL) {
            retName.append("Local");
        } else if (viewType == REMOTE) {
            retName.append("Remote");
        }

        return retName.toString();
    }

    public String getJndiName(JavaClass clazz, int viewType) {
        EjbBeanTag beanTag = (EjbBeanTag)clazz.getTagByName(TagLibrary.EJB_BEAN);

        String jndiName = null;
        if ((beanTag != null) && (viewType == LOCAL)) {
            jndiName = beanTag.getLocalJndiName();
        } else if ((beanTag != null) && (viewType == REMOTE)) {
            jndiName = beanTag.getJndiName();
        }
        if (jndiName == null) {
            jndiName = getComponentName(clazz, viewType, false);
        }
        return jndiName;
    }

    public boolean isLocalEjbRef(String viewType, JavaClass refClass) {
        return isEjbRefType(viewType, refClass, LOCAL);
    }

    public boolean isLocalEjbRef(String viewType) {
        return isEjbRefType(viewType, LOCAL);
    }

    public boolean isRemoteEjbRef(String viewType, JavaClass refClass) {
        return isEjbRefType(viewType, refClass, REMOTE) && !isEjbRefType(viewType, refClass, LOCAL);
    }

    public boolean isRemoteEjbRef(String viewType) {
        return isEjbRefType(viewType, REMOTE) && !isEjbRefType(viewType, LOCAL);
    }

    private boolean isEjbRefType(String viewType, JavaClass refClass, int desiredView) {
        //        int beanType = (viewType != null) ? getViewType(viewType) : getViewType(refClass);
        int beanType = (viewType != null) ? getViewType(viewType) & getViewType(refClass) : getViewType(refClass);
        return hasFlag(beanType, desiredView);
    }

    private boolean isEjbRefType(String viewType, int desiredView) {
        return hasFlag(getViewType(viewType), desiredView);
    }

    public JavaClass findEjbRef(String ejbName, EjbBeanResolver beanResolver) {
        JavaClass javaClass = beanResolver.findEjbByName(ejbName);

        if (javaClass == null) {
            throw new Error("Couldn't find named ejb: " + ejbName);
        }

        int beanType = getBeanType(javaClass);

        // We can only reference a session or entity bean
        if (!hasFlag(beanType, BEAN_ENTITY) && !hasFlag(beanType, BEAN_SESSION)) {
            throw new Error("Referenced bean must be a session bean or entity bean: " + ejbName);
        }

        return javaClass;
    }

    public JavaMethod externalizeMethodName(JavaMethod method) {
        int methodType = getMethodType(method);
        String removeString = null;

        switch (methodType) {
            case IFACE_METHOD_FINDER:
            case IFACE_METHOD_CREATE:
            case IFACE_METHOD_SELECT:
                removeString = "ejb";
                break;

            case IFACE_METHOD_HOME:
                removeString = "ejbHome";
                break;
        }

        JavaMethod retVal = method;

        if (removeString != null) {
            retVal = removeFromMethodName(method, removeString);
        }

        return retVal;
    }

    private JavaMethod removeFromMethodName(JavaMethod method, String removeString) {
        if (!method.getName().startsWith(removeString) || method.getName().length() <= removeString.length()) {
            throw new Error("Method name: " + method.getName() + ", must start with " + removeString);
        }

        StringBuffer methodName = new StringBuffer(method.getName());
        methodName.delete(0, removeString.length());
        methodName.setCharAt(0, Character.toLowerCase(methodName.charAt(0)));
        method = new DuplicatedJavaMethod(method);
        method.setName(methodName.toString());
        return method;
    }

    public Collection getSecurityRoles(Collection metadata) {
        Collection roleTags = new ArrayList();
        Collection ejbBeans = getBeans(metadata);

        for (Iterator iter = ejbBeans.iterator(); iter.hasNext();) {
            JavaClass javaClass = (JavaClass) iter.next();
            roleTags.addAll(Arrays.asList(javaClass.getTagsByName(TagLibrary.EJB_PERMISSION)));
            JavaMethod[] methods = javaClass.getMethods();

            for (int i = 0; i < methods.length; i++) {
                if (methods[i].getTagByName(TagLibrary.EJB_PERMISSION) != null) {
                    int methodType = getMethodType(methods[i]);

                    if ((methodType != IFACE_METHOD_CREATE) && (methodType != IFACE_METHOD_COMPONENT)) {
                        throw getErrorWithTagLocation(methods[i].getTagByName(TagLibrary.EJB_PERMISSION),
                            "Can't mark a method permission on a non interface or create method");
                    }
                }

                roleTags.addAll(Arrays.asList(methods[i].getTagsByName(TagLibrary.EJB_PERMISSION)));
            }

            if (isEntityBean(javaClass)) {
                // get roles from finders
                roleTags.addAll(Arrays.asList(javaClass.getTagsByName(TagLibrary.EJB_FINDER)));

                // and from pk field ( if any )
                roleTags.addAll(Arrays.asList(javaClass.getTagsByName(TagLibrary.EJB_PK)));
            }

            // and from extra security roles
            roleTags.addAll(Arrays.asList(javaClass.getTagsByName(TagLibrary.EJB_SECURITY_ROLES)));
        }

        Collection roles = new TreeSet();

        for (Iterator iter = roleTags.iterator(); iter.hasNext();) {
            RoleList roleLst = (RoleList) iter.next();

            if (roleLst != null && roleLst.getRoleNames() != null) {
                roles.addAll(Arrays.asList(roleLst.getRoleNames()));
            }
        }

        return roles;
    }

    private static class RelationEntryHolder {
        private EjbRelationTag relTag;
        private JavaMethod method;
        private List beanLst = new ArrayList();

        public RelationEntryHolder(EjbRelationTag relTag, JavaMethod method) {
            this.relTag = relTag;
            this.method = method;
        }

        public void addSourceBean(JavaClass sourceBean) {
            beanLst.add(sourceBean);
        }

        public int countSourceBeans() {
            return beanLst.size();
        }

        public JavaClass[] getSourceBeans() {
            return (JavaClass[]) beanLst.toArray(new JavaClass[0]);
        }

        public JavaMethod getMethod() {
            return this.method;
        }

        public EjbRelationTag getRelTag() {
            return this.relTag;
        }

        public String toString() {
            StringBuffer retBuf = new StringBuffer();
            retBuf.append("[relTag]={");
            retBuf.append((relTag != null) ? ("@" + relTag.getName() + " " + relTag.getValue()) : null);
            retBuf.append("},[method]={");
            retBuf.append((method != null) ? method.getCallSignature() : null);
            retBuf.append("}");
            return retBuf.toString();
        }
    }

    private class RelationImpl implements Relation {
        /** The logging object */
        protected Log log = LogFactory.getLog(RelationImpl.class);
        private String name;
        private RelationEntryHolder left;
        private JavaClass leftBean;
        private RelationEntryHolder right;
        private JavaClass rightBean;

        public RelationImpl(String name) {
            this.name = name;
        }

        public void addHolder(RelationEntryHolder holder, JavaClass sourceBean) {
            if (left == null) {
                left = holder;
                leftBean = sourceBean;
            } else if (right == null) {
                right = holder;
                rightBean = sourceBean;
            } else {
                IllegalStateException e = new IllegalStateException();

                if (log.isErrorEnabled()) {
                    log.error("This should not happen", e);
                }

                // This should not happen
                throw e;
            }
        }

        private EjbRelationTag getRightTag() {
            return right != null ? right.getRelTag() : null;
        }

        private EjbRelationTag getLeftTag() {
            return left != null ? left.getRelTag() : null;
        }

        public void swap() {
            RelationEntryHolder t1 = left;
            JavaClass t2 = leftBean;
            left = right;
            leftBean = rightBean;
            right = t1;
            rightBean = t2;
        }

        public String toString() {
            StringBuffer retBuf = new StringBuffer();
            retBuf.append("[name]={");
            retBuf.append(name);
            retBuf.append("},[left]={");
            retBuf.append(left);
            retBuf.append("},[leftBean]={");
            retBuf.append((leftBean != null) ? leftBean.getFullyQualifiedName() : null);
            retBuf.append("},[right]={");
            retBuf.append(right);
            retBuf.append("},[rightBean]={");
            retBuf.append((rightBean != null) ? rightBean.getFullyQualifiedName() : null);
            retBuf.append("}");
            return retBuf.toString();
        }

        private String getRoleName(EjbRelationTag lTag, EjbRelationTag rTag) {
            String roleName = null;

            if (lTag != null) {
                roleName = lTag.getRoleName();
            } else {
                roleName = rTag.getTargetRoleName();
            }

            return roleName;
        }

        private boolean isCascadeDelete(EjbRelationTag lTag, EjbRelationTag rTag) {
            boolean retVal = false;

            if (lTag != null) {
                retVal = lTag.isCascadeDelete();
            } else {
                retVal = rTag.isTargetCascadeDelete();
            }

            return retVal;
        }

        private String getEJBName(JavaClass clazz, EjbRelationTag tag) {
            String ejbName = null;

            if (clazz != null) {
                ejbName = getEjbName(clazz);
            } else {
                ejbName = tag.getTargetEjb();

                if (ejbName == null) {
                    if (log.isErrorEnabled()) {
                        log.error("'target-ejb' is mandatory for tag " + tagToString(tag));
                    }

                    throw getErrorWithTagLocation(tag, "'target-ejb' is mandatory for tag");
                }
            }

            return ejbName;
        }

        // --------------------------------------------------------
        // Relation interface
        // --------------------------------------------------------
        public String getName() {
            return name;
        }

        /**
         * Gets relation's left side bean
         */
        public JavaClass getLeftBean() {
            return leftBean;
        }

        /**
         * Gets relation's right side bean
         */
        public JavaClass getRightBean() {
            return rightBean;
        }

        /**
         * Gets relation's right side method
         */
        public JavaMethod getRightMethod() {
            return right != null ? right.getMethod() : null;
        }

        /**
         * Gets relation's left side method
         */
        public JavaMethod getLeftMethod() {
            return (left != null) ? left.getMethod() : null;
        }

        public boolean isLeftMany() {
            boolean retVal = false;
            JavaMethod method = getLeftMethod();
            EjbRelationTag tag = getRightTag();

            if (method != null) {
                retVal |= method.getReturns().isA(new Type(Collection.class.getName()));
                retVal |= method.getReturns().isA(new Type(Set.class.getName()));
            } else {
                retVal = tag.isTargetMultiple();
            }

            return retVal;
        }

        public boolean isRightMany() {
            boolean retVal = false;
            JavaMethod method = getRightMethod();
            EjbRelationTag tag = getLeftTag();

            if (method != null) {
                retVal |= method.getReturns().isA(new Type(Collection.class.getName()));
                retVal |= method.getReturns().isA(new Type(Set.class.getName()));
            } else {
                retVal = tag.isTargetMultiple();
            }

            return retVal;
        }

        public boolean isBidirectional() {
            return (left != null) && (right != null);
        }

        public boolean isOne2Many() {
            return !isLeftMany() && isRightMany();
        }

        public String getLeftRoleName() {
            return getRoleName(getLeftTag(), getRightTag());
        }

        public String getRightRoleName() {
            return getRoleName(getRightTag(), getLeftTag());
        }

        public boolean isLeftCascadeDelete() {
            return isCascadeDelete(getLeftTag(), getRightTag());
        }

        public boolean isRightCascadeDelete() {
            return isCascadeDelete(getRightTag(), getLeftTag());
        }

        public String getLeftEJBName() {
            return getEJBName(getLeftBean(), getRightTag());
        }

        public String getRightEJBName() {
            return getEJBName(getRightBean(), getLeftTag());
        }

        public Relation reverse() {
            RelationImpl retVal = new RelationImpl(name);
            retVal.left = this.right;
            retVal.leftBean = this.rightBean;
            retVal.right = this.left;
            retVal.rightBean = this.leftBean;
            return retVal;
        }
    }

    public class RelationManagerImpl implements RelationManager {
        /** The logging object */
        protected Log log = LogFactory.getLog(RelationManagerImpl.class);

        /** The map of name / LinkedHashMap */
        private SortedMap relations = new TreeMap(); // Strings are Comparable

        /** A cache for all relations */
        private Collection allRelations = null;

        /**
         * Returns a map that maintains list of inserted elements
         * method / RelationEntryHolder
         * @param relationName The relation to look for
         * @return The map
         */
        private LinkedHashMap getMapFor(String relationName) {
            LinkedHashMap retVal;
            retVal = (LinkedHashMap) relations.get(relationName);

            if (retVal == null) {
                retVal = new LinkedHashMap();
                relations.put(relationName, retVal);
            }

            return retVal;
        }

        public void manage(EjbRelationTag relTag, JavaMethod method, JavaClass sourceBean) {
            LinkedHashMap mapFor = getMapFor(relTag.getName_());
            RelationEntryHolder entryHolder;

            if (mapFor.containsKey(method)) {
                entryHolder = (RelationEntryHolder) mapFor.get(method);

                if (log.isDebugEnabled()) {
                    log.debug("Existing relation entry pair for relation named '" + relTag.getName_() + "' found at " +
                        tagToString(relTag));
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("A new relation entry pair for relation named '" + relTag.getName_() +
                        "' is being created at " + tagToString(relTag));
                }

                entryHolder = new RelationEntryHolder(relTag, method);
                mapFor.put(method, entryHolder);
            }

            entryHolder.addSourceBean(sourceBean);
        }

        private Collection validateAndCreateRelations(String relationName, LinkedHashMap mapFor) {
            List relationLst = new ArrayList();
            List entryList = new ArrayList(mapFor.values());

            if (entryList.size() > 2) {
                RelationEntryHolder firstHolder = (RelationEntryHolder) entryList.get(0);
                RelationEntryHolder secondHolder = (RelationEntryHolder) entryList.get(1);

                // RelationEntryHolder erroneousHolder = (RelationEntryHolder) entryList.get(2);
                if (log.isErrorEnabled()) {
                    log.error("The relation named '" + relationName + "' has more entries that it should.");
                    log.error("The first node is declared at " + tagToString(firstHolder.getRelTag()));
                    log.error("The second node is declared at " + tagToString(secondHolder.getRelTag()));

                    for (int i = 2; i < entryList.size(); i++) {
                        RelationEntryHolder holder = (RelationEntryHolder) entryList.get(i);
                        log.error("Invalid/duplicated at " + tagToString(holder.getRelTag()));
                    }
                }

                // throw new Error("The '" + relationName + "' relation is declared in too many places.\n" +
                //                 "It's declared in " + tagToString(firstHolder.getRelTag()) + ",\n" +
                //                 tagToString(secondHolder.getRelTag()) + " and\n" + tagToString(erroneousHolder.getRelTag()));
            } else if (entryList.size() == 1) {
                if (log.isDebugEnabled()) {
                    log.debug("It is a unidirectional relation named '" + relationName + "'");
                }

                RelationEntryHolder holder = (RelationEntryHolder) entryList.get(0);
                JavaClass[] sourceBeans = holder.getSourceBeans();
                RelationImpl relation;

                if (holder.countSourceBeans() > 1) {
                    if (log.isDebugEnabled()) {
                        log.debug("This is an unidirectional relation used in several beans");

                        for (int i = 0; i < sourceBeans.length; i++) {
                            if (log.isDebugEnabled()) {
                                log.debug("Used in " + getEjbName(sourceBeans[i]));
                            }
                        }
                    }

                    if (log.isWarnEnabled()) {
                        log.warn(
                            "To support unidirectional relations used in various beans we are going to extend the relation name");
                    }

                    String genRelationName;

                    for (int i = 0; i < sourceBeans.length; i++) {
                        genRelationName = relationName + "-" + getEjbName(sourceBeans[i]);

                        if (log.isWarnEnabled()) {
                            log.warn("Relation name = '" + relationName + "', extendedName='" + genRelationName + "'");
                        }

                        if (log.isTraceEnabled()) {
                            log.trace("Creating RelationImpl for '" + genRelationName + "'");
                        }

                        relationLst.add(relation = new RelationImpl(genRelationName));
                        relation.addHolder(holder, sourceBeans[i]);
                    }
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace("Creating RelationImpl for '" + relationName + "'");
                    }

                    relationLst.add(relation = new RelationImpl(relationName));
                    relation.addHolder(holder, sourceBeans[0]);
                }
            } else {
                // Bi-directional relations
                // We can't support entries with more that one bean
                RelationEntryHolder holder;
                JavaClass[] sourceBeans;

                if (log.isTraceEnabled()) {
                    log.trace("Creating RelationImpl for '" + relationName + "'");
                }

                RelationImpl relation = new RelationImpl(relationName);
                boolean shouldAdd = true;

                for (int i = 0; i < entryList.size(); i++) {
                    holder = (RelationEntryHolder) entryList.get(i);
                    sourceBeans = holder.getSourceBeans();

                    if (holder.countSourceBeans() != 1) {
                        if (log.isErrorEnabled()) {
                            log.error("The bidirectional relation named '" + relationName +
                                "' is declared in more than one bean.");
                            log.error("It is defined in " + tagToString(holder.getRelTag()));

                            for (int j = 0; j < sourceBeans.length; j++) {
                                log.error("And therefore used in " + getEjbName(sourceBeans[i]) + " - " +
                                    sourceBeans[i].getFullyQualifiedName());
                            }
                        }

                        shouldAdd = false;
                        // throw new Error("The bidirectional relation named '" + relationName + "' is declared in more than one bean.\n" +
                        //                 "It's declared in " + tagToString(holder.getRelTag()));
                    } else {
                        relation.addHolder(holder, sourceBeans[0]);
                    }
                }

                if (shouldAdd) {
                    relationLst.add(relation);
                } else if (log.isWarnEnabled()) {
                    log.warn("The bidirectional relation named '" + relationName +
                        "' is being discarded because is declared in more than one bean.");
                }
            }

            // Now swap relations so that right is always many (if there is a many side).
            // This may result in left being null, in case of a unidirectional relationship
            // Possible cases are (left-right):
            // 1-->1, 1<--1, 1-->N, 1<--N, M-->N, N<--M
            // 1<->1, 1<->N, M<->N
            // Impossible cases are (left-right):
            // N-->1, N<--1
            for (Iterator iter = relationLst.iterator(); iter.hasNext();) {
                RelationImpl relation = (RelationImpl) iter.next();

                if (log.isDebugEnabled()) {
                    log.debug("Working on relation " + relation);
                }

                if (relation.isLeftMany() && !relation.isRightMany()) {
                    if (log.isDebugEnabled()) {
                        log.debug("This is N-1 relation. Swapping into 1-N.");
                    }

                    relation.swap();
                }

                if (!relation.isBidirectional() && relation.isOne2Many() && relation.getLeftMethod() == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("This is 1-N without a left method. Swapping.");
                    }

                    relation.swap();
                }
            }

            return relationLst;
        }

        private Relation selectByBean(Relation[] relationLst, JavaClass bean) {
            Relation retVal = null;

            if (relationLst != null) {
                for (int i = 0; retVal == null && i < relationLst.length; i++) {
                    Relation current = relationLst[i];

                    if ((current.getLeftBean() != null) &&
                            current.getLeftBean().getFullyQualifiedName().equals(bean.getFullyQualifiedName())) {
                        retVal = current;
                    } else if ((current.getRightBean() != null) &&
                            current.getRightBean().getFullyQualifiedName().equals(bean.getFullyQualifiedName())) {
                        retVal = current;
                    }
                }
            }

            return retVal;
        }

        public String toString() {
            StringBuffer retBuf = new StringBuffer();
            Relation[] relLst = getRelations();

            for (int i = 0; i < relLst.length; i++) {
                retBuf.append("[");
                retBuf.append(i);
                retBuf.append("]={");
                retBuf.append(relLst[i]);
                retBuf.append("}\n");
            }

            return retBuf.toString();
        }

        // --------------------------------------------------------
        // RelationManager interface
        // --------------------------------------------------------
        /**
         * Match a relation for a method and a bean
         * @param method The method to look the relation for
         * @param bean The bean in any side of relation
         * @return The found relation or null if not found
         */
        public Relation getRelationFor(JavaMethod method, JavaClass bean) {
            return selectByBean(getRelationsFor(method), bean);
        }

        /**
         * Match relations for a method
         * @param method The method to look the relation for
         * @return The list of found relations (empty array if not found)
         * @return The found relation or null if not found
         */
        public Relation[] getRelationsFor(final JavaMethod method) {
            Collection retLst = CollectionUtils.select(Arrays.asList(getRelations()),
                    new Predicate() {
                        public boolean evaluate(Object arg0) {
                            Relation current = (Relation) arg0;
                            boolean isFound;
                            isFound = (current.getLeftMethod() != null) && current.getLeftMethod().equals(method);
                            isFound = isFound ||
                                (current.getRightMethod() != null) && current.getRightMethod().equals(method);
                            return isFound;
                        }
                    });

            return (Relation[]) retLst.toArray(new Relation[0]);
        }

        /**
         * Match relation for a name and a bean
         * @param relationName The relation name
         * @param bean The bean in any side of relation
         * @return The found relation or null if not found
         */
        public Relation getRelationByName(String relationName, JavaClass bean) {
            return selectByBean(getRelationsByName(relationName), bean);
        }

        /**
         * Match relations for a name
         * @param relationName The relation name
         * @return The list of found relations (empty array if not found)
         */
        public Relation[] getRelationsByName(String relationName) {
            Collection retVal = new ArrayList();
            LinkedHashMap mapFor = (LinkedHashMap) relations.get(relationName);

            if (mapFor != null) {
                retVal.addAll(validateAndCreateRelations(relationName, mapFor));
            }

            return (Relation[]) retVal.toArray(new Relation[0]);
        }

        /**
         * Get all known relations
         * @return The list of found relations (empty array if not found)
         */
        public Relation[] getRelations() {
            if (allRelations == null) {
                allRelations = new ArrayList();

                for (Iterator iter = relations.entrySet().iterator(); iter.hasNext();) {
                    Map.Entry entry = (Entry) iter.next();
                    allRelations.addAll(validateAndCreateRelations((String) entry.getKey(),
                            (LinkedHashMap) entry.getValue()));
                }
            }

            return (Relation[]) allRelations.toArray(new Relation[0]);
        }
    }

    /**
     * Creates a relation manager
     * @param metadata The collection of JavaClass
     * @return An RelationManager
     */
    public RelationManager createRelationManager(Collection metadata) {
        // Select concrete CMP Beans
        Collection cmpBeans = CollectionUtils.select(getBeans(metadata, BEAN_CMP),
                new Predicate() {
                    public boolean evaluate(Object arg0) {
                        JavaClass javaClass = (JavaClass) arg0;
                        boolean retVal = shouldGenerate(javaClass);

                        if (log.isDebugEnabled()) {
                            log.debug(javaClass.getFullyQualifiedName() +
                                (retVal ? " is a concrete bean" : " isn't a concrete bean"));
                        }

                        return retVal;
                    }
                });

        RelationManagerImpl manager = new RelationManagerImpl();

        for (Iterator iter = cmpBeans.iterator(); iter.hasNext();) {
            JavaClass javaClass = (JavaClass) iter.next();

            // Get hierarchy methods
            JavaMethod[] methods = javaClass.getMethods(true);

            for (int i = 0; i < methods.length; i++) {
                if (hasFlag(getMethodMetadata(javaClass, methods[i]), METADATA_METHOD_RELATION_FIELD)) {
                    // We support the same relation 'name' repeated when we have a uni-direccional
                    // relation and for the same method
                    // NOTE: This is to support an uni-direccional relation to be defined
                    // in an abstract bean, used in several beans. Imagine PrivateClient and CompanyClient
                    // having a method getAccounts.
                    // For now, i am not seeing any problem...
                    manager.manage((EjbRelationTag) methods[i].getTagByName(TagLibrary.EJB_RELATION), methods[i], javaClass);
                }
            }
        }

        // At the end verify fields
        return manager;
    }

    public static class TagReference {
        private DocletTag tag;
        private JavaClass clazz;
        private JavaMethod method;
        private JavaField field;

        public TagReference(DocletTag tag, JavaClass clazz) {
            this.tag = tag;
            this.clazz = clazz;
        }

        public TagReference(DocletTag tag, JavaMethod method) {
            this.tag = tag;
            this.method = method;
        }

        public TagReference(DocletTag tag, JavaField field) {
            this.tag = tag;
            this.field = field;
        }

        public JavaClass getClazz() {
            return this.clazz;
        }

        public void setClazz(JavaClass clazz) {
            this.clazz = clazz;
        }

        public JavaField getField() {
            return this.field;
        }

        public void setField(JavaField field) {
            this.field = field;
        }

        public JavaMethod getMethod() {
            return this.method;
        }

        public void setMethod(JavaMethod method) {
            this.method = method;
        }

        public DocletTag getTag() {
            return this.tag;
        }

        public void setTag(DocletTag tag) {
            this.tag = tag;
        }
    }

    public Collection getMethodPermissions(JavaClass javaClass) {
        EjbVersion version = config.getEjbVersion();
        Collection retLst = new ArrayList();
        DocletTag[] tags;

        if (!isEJB(javaClass)) {
            throw new Error();
        }

        // Let dig into class level ejb.permission tags
        tags = javaClass.getTagsByName(TagLibrary.EJB_PERMISSION);

        for (int i = 0; i < tags.length; i++) {
            EjbPermissionTag permTag = (EjbPermissionTag) tags[i];
            int permType = getViewType(javaClass);

            // -------------------------------------------------------
            // Let's "bitwise and" to get only the specied masks
            // that are compatible with the bean
            // HUMM: Is this valid ?
            if (permTag.getViewType() != null) {
                permType &= getViewType(permTag.getViewType());
            } else if (permTag.getMethodIntf() != null) {
                permType &= getInterfaceType(permTag.getMethodIntf());
            }

            if (permType == 0) {
                throw getErrorWithTagLocation(permTag,
                    "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
            }

            // -------------------------------------------------------
            // We are generating an method permission if there is at least
            // one role or unchecked is true
            // NOTE: unchecked is only valid for EJB 2.0+
            boolean canContinue = (permTag.getRoleNames() != null && permTag.getRoleNames().length > 0);

            if (version.greaterOrEquals(EjbVersion.EJB_2_0)) {
                canContinue |= permTag.isUnchecked();
            }

            if (!canContinue) {
                throw getErrorWithTagLocation(permTag,
                    "Couldn't resolve role-names for method permission" +
                    (version.greaterOrEquals(EjbVersion.EJB_2_0) ? " or unchecked is false" : ""));
            }

            // Lets expand by permission for interface type
            retLst.addAll(MethodPermission.unroll(permType, permTag.getRoleNames()));
        }

        // Now let's dig into method level ejb.permission tags
        JavaMethod[] methods = javaClass.getMethods();
        JavaMethod method;

        for (int j = 0; j < methods.length; j++) {
            tags = (method = methods[j]).getTagsByName(TagLibrary.EJB_PERMISSION);

            for (int k = 0; k < tags.length; k++) {
                EjbPermissionTag permTag = (EjbPermissionTag) tags[k];
                int methodType = getMethodType(method);

                if ((methodType != IFACE_METHOD_CREATE) && (methodType != IFACE_METHOD_COMPONENT)) {
                    throw getErrorWithTagLocation(permTag,
                        "Can't mark a method permission on a non interface or create method");
                }

                int permType = getViewType(method, javaClass);

                // -------------------------------------------------------
                // Let's "bitwise and" to get only the specied masks
                // that are compatible with the bean
                // HUMM: Is this valid ?
                if (permTag.getViewType() != null) {
                    permType &= getViewType(permTag.getViewType());
                } else if (permTag.getMethodIntf() != null) {
                    permType &= getInterfaceType(permTag.getMethodIntf());
                }

                if (permType == 0) {
                    throw getErrorWithTagLocation(permTag,
                        "Couldn't resolve a compatible interface type reference.  Maybe bean/view-type/version doesn't support it!");
                }

                // -------------------------------------------------------
                // We are generating an method permission if there is at least
                // one role or unchecked is true
                // NOTE: unchecked is only valid for EJB 2.0+
                boolean canContinue = (permTag.getRoleNames() != null && permTag.getRoleNames().length > 0);

                if (version.greaterOrEquals(EjbVersion.EJB_2_0)) {
                    canContinue |= permTag.isUnchecked();
                }

                if (!canContinue) {
                    throw getErrorWithTagLocation(permTag,
                        "Couldn't resolve role-names for method permission" +
                        (version.greaterOrEquals(EjbVersion.EJB_2_0) ? " or unchecked is false" : ""));
                }

                // Lets expand by permission for interface type
                retLst.addAll(MethodPermission.unroll(permType, externalizeMethodName(method), permTag.getRoleNames()));
            }
        }

        if (isEntityBean(javaClass)) {
            tags = javaClass.getTagsByName(TagLibrary.EJB_FINDER);

            // Let dig into class level ejb.finder tags
            for (int i = 0; i < tags.length; i++) {
                EjbFinderTag finderTag = (EjbFinderTag) tags[i];
                int permType = getViewType(finderTag.getViewType()) & (REMOTE_HOME | LOCAL_HOME);

                // -------------------------------------------------------
                // Let's "bitwise and" to get only the specied masks
                // that are compatible with the bean
                // HUMM: Is this valid ?
                if (finderTag.getMethodIntf() != null) {
                    permType &= getInterfaceType(finderTag.getMethodIntf());
                }

                if (permType == 0) {
                    throw getErrorWithTagLocation(finderTag,
                        "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
                }

                // -------------------------------------------------------
                // We are generating an method permission if there is at least
                // one role or unchecked is true
                // NOTE: unchecked is only valid for EJB 2.0+
                boolean canContinue = (finderTag.getRoleNames() != null && finderTag.getRoleNames().length > 0);

                if (version.greaterOrEquals(EjbVersion.EJB_2_0)) {
                    canContinue |= finderTag.isUnchecked();
                }

                if (canContinue) {
                    // Lets expand by permission for interface type
                    // Method signature should maybe be unrolled by permType, but it's not really relevant
                    // the return type, soo..
                    retLst.addAll(MethodPermission.unroll(permType, getFinderMethodBySignature(finderTag.getSignature()),
                            finderTag.getRoleNames()));
                }
            }

            EjbPkTag pkTag = (EjbPkTag) javaClass.getTagByName(TagLibrary.EJB_PK);

            if (pkTag != null) {
                // -------------------------------------------------------
                // We are generating an method permission if there is at least
                // one role or unchecked is true
                // NOTE: unchecked is only valid for EJB 2.0+
                boolean canContinue = (pkTag.getRoleNames() != null && pkTag.getRoleNames().length > 0);

                if (version.greaterOrEquals(EjbVersion.EJB_2_0)) {
                    canContinue |= pkTag.isUnchecked();
                }

                // We'll not continue this if we do not have to check a security method for
                // "findByPrimaryKey"
                if (canContinue) {
                    int permType = getViewType(javaClass) & (REMOTE_HOME | LOCAL_HOME);

                    // -------------------------------------------------------
                    // Let's "bitwise and" to get only the specied masks
                    // that are compatible with the bean
                    // HUMM: Is this valid ?
                    if (pkTag.getMethodIntf() != null) {
                        permType &= getInterfaceType(pkTag.getMethodIntf());
                    }

                    if (permType == 0) {
                        throw getErrorWithTagLocation(pkTag,
                            "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
                    }

                    // Lets expand by permission for interface type
                    // Method signature should maybe be unrolled by permType, but it's not really relevant
                    // the return type, soo..
                    retLst.addAll(MethodPermission.unroll(permType,
                            getFinderMethodBySignature("void findByPrimaryKey()"), pkTag.getRoleNames()));
                }
            }
        }

        return retLst;
    }

    public Collection getContainerTransactions(JavaClass javaClass) {
        Collection retLst = new ArrayList();
        DocletTag[] tags;

        if (!isEJB(javaClass)) {
            throw new Error();
        }

        // Let dig into class level ejb.transaction tags
        tags = javaClass.getTagsByName(TagLibrary.EJB_TRANSACTION);

        for (int i = 0; i < tags.length; i++) {
            EjbTransactionTag transTag = (EjbTransactionTag) tags[i];
            int permType = 0;
            //int permType = getViewType(javaClass);

            // -------------------------------------------------------
            // Let's "bitwise and" to get only the specied masks
            // that are compatible with the bean
            // HUMM: Is this valid ?
            if (transTag.getMethodIntf() != null) {
                permType = getViewType(javaClass);
                permType &= getInterfaceType(transTag.getMethodIntf());
                if (permType == 0) {
                    throw getErrorWithTagLocation(transTag,
                        "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
                }
                // Lets expand by permission for interface type
                // Only one entry should be present
                retLst.addAll(ContainerTransaction.unroll(permType, transTag.getType()));
            } else {
                // No permission was set
                retLst.add(new ContainerTransaction(transTag.getType()));
            }

//            if (permType == 0) {
//                throw getErrorWithTagLocation(transTag,
//                    "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
//            }


            // Lets expand by permission for interface type
            // retLst.addAll(ContainerTransaction.unroll(permType, transTag.getType()));
        }

        // Now let's dig into method level ejb.transaction tags
        JavaMethod[] methods = javaClass.getMethods();
        JavaMethod method;

        for (int j = 0; j < methods.length; j++) {
            tags = (method = methods[j]).getTagsByName(TagLibrary.EJB_TRANSACTION_METHOD);

            for (int k = 0; k < tags.length; k++) {
                EjbTransactionMethodTag transTag = (EjbTransactionMethodTag) tags[k];
                int methodType = getMethodType(method);

                if ((methodType != IFACE_METHOD_CREATE) && (methodType != IFACE_METHOD_COMPONENT)) {
                    throw getErrorWithTagLocation(transTag,
                        "Can't mark a method transaction on a non interface or create method");
                }

                int permType = getViewType(method, javaClass);

                // -------------------------------------------------------
                // Let's "bitwise and" to get only the specied masks
                // that are compatible with the bean
                // HUMM: Is this valid ?
                if (permType == 0) {
                    throw getErrorWithTagLocation(transTag,
                        "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
                }

                // Lets expand by permission for interface type
                retLst.addAll(ContainerTransaction.unroll(permType, externalizeMethodName(method), transTag.getType()));
            }
        }

        if (isEntityBean(javaClass)) {
            tags = javaClass.getTagsByName(TagLibrary.EJB_FINDER);

            // Let dig into class level ejb.finder tags
            for (int i = 0; i < tags.length; i++) {
                EjbFinderTag finderTag = (EjbFinderTag) tags[i];

                if (finderTag.getTransactionType() == null) {
                    // We are processing method transactions.
                    // So the optional element must be present
                    continue;
                }

                int permType = getViewType(finderTag.getViewType()) & (REMOTE_HOME | LOCAL_HOME);

                // -------------------------------------------------------
                // Let's "bitwise and" to get only the specied masks
                // that are compatible with the bean
                // HUMM: Is this valid ?
                if (finderTag.getMethodIntf() != null) {
                    permType &= getInterfaceType(finderTag.getMethodIntf());
                }

                if (permType == 0) {
                    throw getErrorWithTagLocation(finderTag,
                        "Couldn't resolve a compatible interface type reference. Maybe bean/view-type/version doesn't support it!");
                }

                // Lets expand by permission for interface type
                // Method signature should maybe be unrolled by permType, but it's not really relevant
                // the return type, soo..
                retLst.addAll(ContainerTransaction.unroll(permType,
                        getFinderMethodBySignature(finderTag.getSignature()), finderTag.getTransactionType()));
            }
        }

        return retLst;
    }

    public Collection injectMethodThrowException(final Collection methods, final Class exceptionClass) {
        return CollectionUtils.collect(methods,
            new Transformer() {
                public Object transform(Object method) {
                    SignatureDuplicatedJavaMethod retVal = new SignatureDuplicatedJavaMethod((JavaMethod) method);
                    Type ex = new Type(exceptionClass.getName());

                    if (!retVal.containsException(ex)) {
                        retVal.addException(ex);
                    }

                    return retVal;
                }
            });
    }

    public EjbBeanResolver createEjbBeanResolver(Collection metadata) {
        EjbBeanResolver retVal = null;

        if (metadata != null) {
            retVal = new EjbBeanResolverImpl(metadata);
        }

        return retVal;
    }

    public class EjbBeanResolverImpl implements EjbBeanResolver {
        private final Log log = LogFactory.getLog(EjbBeanResolverImpl.class);
        private Map ejbCache = new HashMap();

        public EjbBeanResolverImpl(Collection metadata) {
            for (Iterator iter = metadata.iterator(); iter.hasNext();) {
                JavaClass javaClass = (JavaClass) iter.next();

                if (isEJB(javaClass)) {
                    String ejbName = getEjbName(javaClass);

                    if (ejbCache.containsKey(ejbName)) {
                        JavaClass otherClass = (JavaClass) ejbCache.get(ejbName);
                        String errorMsg = "We cannot have two diferent ejb with same name: " +
                            javaClass.getFullyQualifiedName() + " and " + otherClass.getFullyQualifiedName();
                        log.error(errorMsg);
                        throw new Error(errorMsg);
                    } else {
                        ejbCache.put(ejbName, javaClass);
                    }
                }
            }
        }

        // --------------------------------------------------------
        // EjbBeanResolver interface
        // --------------------------------------------------------
        public JavaClass findEjbByName(String ejbName) {
            if (log.isTraceEnabled()) {
                log.trace("findEjbByName: " + ejbName);
            }

            return (JavaClass) ejbCache.get(ejbName);
        }
    }

    public static class SignatureDuplicatedJavaMethod extends DuplicatedJavaMethod {
        public SignatureDuplicatedJavaMethod(JavaMethod method) {
            super(method);
        }

        public boolean containsException(Type type) {
            return Arrays.asList(getExceptions()).contains(type);
        }

        public void addException(Type type) {
            List exceptions = new ArrayList(Arrays.asList(getExceptions()));
            exceptions.add(type);
            setExceptions((Type[]) exceptions.toArray(new Type[0]));
        }
    }

    public static class Permission {
        private final String ifaceType;
        private final JavaMethod method;

        // class level - applies to all methods
        public Permission(String ifaceType) {
            this(ifaceType, null);
        }

        // method level - applies to given method (null to all methods)
        public Permission(String ifaceType, JavaMethod method) {
            this.ifaceType = ifaceType;
            this.method = method;
        }

        public String getIfaceType() {
            return this.ifaceType;
        }

        public JavaMethod getMethod() {
            return this.method;
        }
    }

    public static class ContainerTransaction extends Permission {
        private final String transType;

        // class level - applies to all methods
        public ContainerTransaction(String transType) {
            this(null, null, transType);
        }

        // class level - applies to all methods
        public ContainerTransaction(String ifaceType, String transType) {
            this(ifaceType, null, transType);
        }

        // method level - applies to given method (null to all methods)
        public ContainerTransaction(String ifaceType, JavaMethod method, String transType) {
            super(ifaceType, method);
            this.transType = transType;
        }

        public String getTransType() {
            return this.transType;
        }

        public static Collection unroll(int permType, String transType) {
            return unroll(permType, null, transType);
        }

        public static Collection unroll(int permType, JavaMethod method, String transType) {
            Collection retLst = new ArrayList();

            if (hasFlag(permType, REMOTE)) {
                retLst.add(new ContainerTransaction(REMOTE_INTERFACE, method, transType));
            }

            if (hasFlag(permType, REMOTE_HOME)) {
                retLst.add(new ContainerTransaction(REMOTE_HOME_INTERFACE, method, transType));
            }

            if (hasFlag(permType, LOCAL)) {
                retLst.add(new ContainerTransaction(LOCAL_INTERFACE, method, transType));
            }

            if (hasFlag(permType, LOCAL_HOME)) {
                retLst.add(new ContainerTransaction(LOCAL_HOME_INTERFACE, method, transType));
            }

            if (hasFlag(permType, SERVICE_END_POINT)) {
                retLst.add(new ContainerTransaction(SERVICE_END_POINT_INTERFACE, method, transType));
            }

            return retLst;
        }
    }

    public static class MethodPermission extends Permission {
        private final String[] roles;

        // class level - applies to all methods
        public MethodPermission(String ifaceType, String[] roles) {
            this(ifaceType, null, roles);
        }

        // method level - applies to given method (null to all methods)
        public MethodPermission(String ifaceType, JavaMethod method, String[] roles) {
            super(ifaceType, method);
            this.roles = roles;
        }

        public String[] getRoles() {
            return this.roles;
        }

        public static Collection unroll(int permType, String[] roles) {
            return unroll(permType, null, roles);
        }

        public static Collection unroll(int permType, JavaMethod method, String[] roles) {
            Collection retLst = new ArrayList();

            if (hasFlag(permType, REMOTE)) {
                retLst.add(new MethodPermission(REMOTE_INTERFACE, method, roles));
            }

            if (hasFlag(permType, REMOTE_HOME)) {
                retLst.add(new MethodPermission(REMOTE_HOME_INTERFACE, method, roles));
            }

            if (hasFlag(permType, LOCAL)) {
                retLst.add(new MethodPermission(LOCAL_INTERFACE, method, roles));
            }

            if (hasFlag(permType, LOCAL_HOME)) {
                retLst.add(new MethodPermission(LOCAL_HOME_INTERFACE, method, roles));
            }

            if (hasFlag(permType, SERVICE_END_POINT)) {
                retLst.add(new MethodPermission(SERVICE_END_POINT_INTERFACE, method, roles));
            }

            return retLst;
        }
    }
}
TOP

Related Classes of org.xdoclet.plugin.ejb.EjbUtils$TagReference

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.