Package org.xdoclet.plugin.ejb.entity

Source Code of org.xdoclet.plugin.ejb.entity.ValueObjectPlugin

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

import java.io.Serializable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

import org.generama.GeneramaException;
import org.generama.MergeableVelocityTemplateEngine;
import org.generama.QDoxCapableMetadataProvider;
import org.generama.WriterMapper;

import org.xdoclet.plugin.ejb.EjbBeanResolver;
import org.xdoclet.plugin.ejb.EjbConfig;
import org.xdoclet.plugin.ejb.EjbJavaClassBuilder;
import org.xdoclet.plugin.ejb.EjbJavaGeneratingPlugin;
import org.xdoclet.plugin.ejb.EjbRuntime;
import org.xdoclet.plugin.ejb.EjbUtils;
import org.xdoclet.plugin.ejb.Relation;
import org.xdoclet.plugin.ejb.RelationManager;
import org.xdoclet.plugin.ejb.interfaces.LocalInterfacePlugin;
import org.xdoclet.plugin.ejb.interfaces.RemoteInterfacePlugin;
import org.xdoclet.plugin.ejb.qtags.EjbValueObjectFieldTag;
import org.xdoclet.plugin.ejb.qtags.EjbValueObjectTag;
import org.xdoclet.plugin.ejb.qtags.TagLibrary;
import org.xdoclet.plugin.ejb.util.DuplicatedJavaClass;
import org.xdoclet.plugin.ejb.util.QDoxCachedMetadataProvider;

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;

/**
* Plugin for ValueObjects generation for Entity EJBs.
*
* @author Diogo Quintela
*/
public class ValueObjectPlugin extends EjbJavaGeneratingPlugin implements EjbJavaClassBuilder {
    public static final Type SET_TYPE = new Type(Set.class.getName());
    public static final Type COLLECTION_TYPE = new Type(Collection.class.getName());
    private static final Map immutableKnownTypes;

    static {
        Map typeMap = new HashMap();
        typeMap.put(Boolean.class.getName(), Boolean.TRUE);
        typeMap.put(Boolean.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Byte.class.getName(), Boolean.TRUE);
        typeMap.put(Byte.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Character.class.getName(), Boolean.TRUE);
        typeMap.put(Character.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Short.class.getName(), Boolean.TRUE);
        typeMap.put(Short.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Integer.class.getName(), Boolean.TRUE);
        typeMap.put(Integer.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Float.class.getName(), Boolean.TRUE);
        typeMap.put(Float.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Long.class.getName(), Boolean.TRUE);
        typeMap.put(Long.TYPE.getName(), Boolean.TRUE);
        typeMap.put(Double.class.getName(), Boolean.TRUE);
        typeMap.put(Double.TYPE.getName(), Boolean.TRUE);
        typeMap.put(String.class.getName(), Boolean.TRUE);
        immutableKnownTypes = Collections.unmodifiableMap(typeMap);
    }

    /**
     * Resolver of beans by name
     */
    private EjbBeanResolver beanResolver;

    /**
     * Relation manager
     */
    private RelationManager relationManager;

    /** Cache for value object resolver */
    private Map metaVoCache = new HashMap();

    public ValueObjectPlugin(MergeableVelocityTemplateEngine templateEngine,
            WriterMapper writerMapper, EjbConfig config)
            throws ClassNotFoundException {

        super(templateEngine, writerMapper,
                new EjbConfig(
                        new QDoxValueObjectExpanderFilterMetadataProvider(
                            config.getMetadataProvider()), writerMapper)
                );

        // Set the plugin for callback reference, as QDoxValueObjectExpanderFilterMetadataProvider must be static
        getValueObjectMetadataProvider().setPlugin(this);

        // EjbRuntime.setPlugin(this);
        setPackageregex("beans");
        setPackagereplace("util");
        super.setFileregex(config.getEjbReplaceRegex());
        setFilereplace("ValueObject");
        super.setMultioutput(true);

        //try {
        //    ((QDoxValueObjectExpanderFilterMetadataProvider) getMetadataProvider()).getMetadata();
        //    System.out.println("=============================");
        //    System.out.println("ORIGINAL");
        //    System.out.println("=============================");
        //    System.out.println(ejbUtils.createRelationManager(
        //            ((QDoxValueObjectExpanderFilterMetadataProvider) getMetadataProvider()).getOriginalMetadata()));
        //    System.out.println("=============================");
        //    System.out.println("TAMPERED");
        //    System.out.println("=============================");
        //    System.out.println(ejbUtils.createRelationManager(
        //            ((QDoxValueObjectExpanderFilterMetadataProvider) getMetadataProvider()).getTamperedMetadata()));
        //} catch (Throwable t) {
        //    t.printStackTrace(System.out);
        //}
    }

    /**
     * JavaClass that describes the generated artifacts
     *
     * @return The generated artifacts JavaClass otherwise null
     */
    public JavaClass[] buildFor(JavaClass metadata_) {
        JavaClass[] expandedClasses = expandClass(metadata_);

        List retLst = new ArrayList();

        for (int i = 0; i < expandedClasses.length; i++) {
            JavaClass metadata = expandedClasses[i];
            if (!shouldGenerate(metadata)) {
                continue;
            }
            ValueObjectMetadata metaVo = getMetaVO(metadata);
            BeanProperty pkProperty = metaVo.getPkProperty();

            JavaField field;
            JavaMethod method;
            JavaClass retVal = createDynamicJavaClass(getDestinationClassname(metadata), getDestinationPackage(metadata), null, getMetadataProvider());
            String[] modifiers = new String[isAbstract(metadata) ? 2 : 1];
            modifiers[0] = "public";
            if (isAbstract(metadata)) {
                modifiers[1] = "abstract";
            }
            retVal.setModifiers(modifiers);
            retVal.setSuperClass(new Type(getExtends(metadata)));

            String[] implementz = getImplements(metadata);
            Type[] implementzTypes = new Type[implementz.length];
            for (int j = 0; j < implementz.length; j++) {
                implementzTypes[j] = new Type(implementz[j]);
            }
            retVal.setImplementz(implementzTypes);

            if (getEjbUtils().isUseSoftLocking(metadata)) {
                field = new JavaField(new Type("int"), "_version");
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
            }

            ValueObjectFieldMetadata[] nonPkFields = metaVo.getNonPkFields();
            for (int j = 0; j < nonPkFields.length; j++) {
                ValueObjectFieldMetadata vField = nonPkFields[j];
                field = new JavaField(vField.getProperty().getType(), vField.getProperty().getName());
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
                field = new JavaField(new Type("boolean"), vField.getProperty().getName()+"HasBeenSet");
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
            }

            ValueObjectFieldMetadata[] pkFields = metaVo.getPkFields();
            for (int j = 0; j < pkFields.length; j++) {
                ValueObjectFieldMetadata vField = pkFields[j];
                field = new JavaField(vField.getProperty().getType(), vField.getProperty().getName());
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
                field = new JavaField(new Type("boolean"), vField.getProperty().getName()+"HasBeenSet");
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
            }

            ValueObjectRelationMetadata[] nonCollectionRel = metaVo.getNonCollectionRelations();
            for (int j = 0; j < nonCollectionRel.length; j++) {
                ValueObjectRelationMetadata vRelation = nonCollectionRel[j];
                field = new JavaField(vRelation.getProperty().getType(), vRelation.getProperty().getName());
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
                field = new JavaField(new Type("boolean"), vRelation.getProperty().getName()+"HasBeenSet");
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
            }
            ValueObjectRelationMetadata[] collectionRel = metaVo.getCollectionRelations();
            for (int j = 0; j < collectionRel.length; j++) {
                ValueObjectRelationMetadata vRelation = collectionRel[j];
                field = new JavaField(vRelation.getProperty().getType(), vRelation.getProperty().getName());
                field.setModifiers(new String[] { "private" });
                retVal.addField(field);
            }

            method = new JavaMethod(getDestinationClassname(metadata));
            method.setConstructor(true);
            method.setModifiers(new String[] { "public" });
            retVal.addMethod(method);

            if (metaVo.isGeneratePKConstructor() && metaVo.hasPk()) {
                if(!isEmpty(nonPkFields) || !metaVo.isSimplePkProperty()) {
                    method = new JavaMethod(getDestinationClassname(metadata));
                    method.setConstructor(true);
                    method.setModifiers(new String[] { "public" });
                    method.setParameters(new JavaParameter[]{new JavaParameter(pkProperty.getType(), pkProperty.getName())});
                    retVal.addMethod(method);
                }
                if(!isEmpty(nonPkFields) && !metaVo.isSimplePkProperty()) {
                    method = new JavaMethod(getDestinationClassname(metadata));
                    method.setConstructor(true);
                    method.setModifiers(new String[] { "public" });
                    JavaParameter[] params = new JavaParameter[nonPkFields.length + 1];
                    params[0] = new JavaParameter(pkProperty.getType(), pkProperty.getName());
                    for (int j = 0; j < nonPkFields.length; j++) {
                        ValueObjectFieldMetadata nonPkField = nonPkFields[j];
                        params[j+1] = new JavaParameter(nonPkField.getProperty().getType(), nonPkField.getProperty().getName());
                    }
                    method.setParameters(params);
                    retVal.addMethod(method);
                }
            }

            ValueObjectFieldMetadata[] fields = metaVo.getFields();
            if (!isEmpty(fields)) {
                method = new JavaMethod(getDestinationClassname(metadata));
                method.setConstructor(true);
                method.setModifiers(new String[] { "public" });
                JavaParameter[] params = new JavaParameter[fields.length];
                for (int j = 0; j < fields.length; j++) {
                    ValueObjectFieldMetadata vField = fields[j];
                    params[j] = new JavaParameter(vField.getProperty().getType(), vField.getProperty().getName());
                }
                method.setParameters(params);
                retVal.addMethod(method);
            }

            if (metaVo.hasPk() && !metaVo.isSimplePkProperty()) {
                method = new JavaMethod(pkProperty.getType(), pkProperty.getAccessor().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod(pkProperty.getMutator().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(pkProperty.getType(), pkProperty.getName())});
                retVal.addMethod(method);
            }

            if (getEjbUtils().isUseSoftLocking(metadata)) {
                method = new JavaMethod(new Type("int"), "getVersion");
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod("setVersion");
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type("int"), "version")});
                retVal.addMethod(method);
            }

            for (int j = 0; (fields != null) && (j < fields.length); j++) {
                ValueObjectFieldMetadata vField = fields[j];
                method = new JavaMethod(vField.getProperty().getType(), vField.getProperty().getAccessor().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod(vField.getProperty().getMutator().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(vField.getProperty().getType(), vField.getProperty().getName())});
                retVal.addMethod(method);

                method = new JavaMethod(new Type("boolean"), vField.getProperty().getName()+"HasBeenSet");
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);
            }

            for (int k = 0; k < nonCollectionRel.length; k++) {
                ValueObjectRelationMetadata nonColRel = nonCollectionRel[k];

                method = new JavaMethod(nonColRel.getProperty().getType(), nonColRel.getProperty().getAccessor().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod(nonColRel.getProperty().getMutator().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(nonColRel.getProperty().getType(), nonColRel.getProperty().getName())});
                retVal.addMethod(method);

                method = new JavaMethod(new Type("boolean"), nonColRel.getProperty().getName()+"HasBeenSet");
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);
            }

            for (int k = 0; k < collectionRel.length; k++) {
                ValueObjectRelationMetadata colRel = collectionRel[k];

                field = new JavaField(colRel.getProperty().getType(), "added" + colRel.getProperty().getName());
                field.setModifiers(new String[]{"protected"});
                retVal.addField(field);
                field = new JavaField(colRel.getProperty().getType(), "onceAdded" + colRel.getProperty().getName());
                field.setModifiers(new String[]{"protected"});
                retVal.addField(field);
                field = new JavaField(colRel.getProperty().getType(), "removed" + colRel.getProperty().getName());
                field.setModifiers(new String[]{"protected"});
                retVal.addField(field);
                field = new JavaField(colRel.getProperty().getType(), "updated" + colRel.getProperty().getName());
                field.setModifiers(new String[]{"protected"});
                retVal.addField(field);

                method = new JavaMethod(colRel.getProperty().getType(), "getAdded" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                retVal.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getOnceAdded" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                retVal.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getRemoved" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                retVal.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getUpdated" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                retVal.addMethod(method);

                method = new JavaMethod(colRel.getProperty().getType(), colRel.getProperty().getAccessor().getName()+"Collection");
                method.setModifiers(new String[]{"public"});
                retVal.addMethod(method);

                method = new JavaMethod(new Type(colRel.getAggregate(),1), colRel.getProperty().getAccessor().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod(colRel.getProperty().getMutator().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type(colRel.getAggregate(),1), colRel.getProperty().getName())});
                retVal.addMethod(method);

                method = new JavaMethod("clear"+colRel.getProperty().getName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod("add"+colRel.getAggregateName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type(colRel.getAggregate()), "other")});
                retVal.addMethod(method);

                method = new JavaMethod("remove"+colRel.getAggregateName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type(colRel.getAggregate()), "other")});
                retVal.addMethod(method);

                method = new JavaMethod("update"+colRel.getAggregateName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type(colRel.getAggregate()), "other")});
                retVal.addMethod(method);

                method = new JavaMethod("clean"+colRel.getAggregateName());
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                retVal.addMethod(method);

                method = new JavaMethod("copy"+colRel.getProperty().getName()+"From");
                modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
                modifiers[0] = "public";
                if (metaVo.isFullSynchronization()) {
                    modifiers[1] = "synchronized";
                }
                method.setModifiers(modifiers);
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type(getDestinationFullyQualifiedClassName(metadata)), "other")});
                retVal.addMethod(method);
            }

            method = new JavaMethod(new Type("java.lang.String"), "toString");
            modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
            modifiers[0] = "public";
            if (metaVo.isFullSynchronization()) {
                modifiers[1] = "synchronized";
            }
            method.setModifiers(modifiers);
            retVal.addMethod(method);

            method = new JavaMethod(new Type("boolean"), "hasIdentity");
            modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
            modifiers[0] = "protected";
            if (metaVo.isFullSynchronization()) {
                modifiers[1] = "synchronized";
            }
            method.setModifiers(modifiers);
            retVal.addMethod(method);

            method = new JavaMethod(new Type("boolean"), "equals");
            method.setModifiers(new String[]{"public"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.lang.Object"), "other")});
            retVal.addMethod(method);

            if (metaVo.isStrictOrdering()) {
                method = new JavaMethod(new Type("int"), "compareTo");
                method.setModifiers(new String[]{"public"});
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.lang.Object"), "other")});
                retVal.addMethod(method);
            }

            method = new JavaMethod(new Type("java.lang.Object"), "clone");
            modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
            modifiers[0] = "public";
            if (metaVo.isFullSynchronization()) {
                modifiers[1] = "synchronized";
            }
            method.setModifiers(modifiers);
            retVal.addMethod(method);

            method = new JavaMethod(new Type("int"), "hashCode");
            modifiers = new String[metaVo.isFullSynchronization() ? 2 : 1];
            modifiers[0] = "public";
            if (metaVo.isFullSynchronization()) {
                modifiers[1] = "synchronized";
            }
            method.setModifiers(modifiers);
            retVal.addMethod(method);

            JavaClass innerClass = createDynamicJavaClass("ReadOnly" + getDestinationClassname(metadata), getDestinationPackage(metadata), null, getMetadataProvider());
            innerClass.setModifiers(new String[]{"final","private"});
            retVal.addClass(innerClass);

            method = new JavaMethod(innerClass.asType(), "getReadOnly"+getDestinationClassname(metadata));
            method.setModifiers(new String[]{"public"});
            retVal.addMethod(method);

            method = new JavaMethod(new Type("java.util.Collection"), "wrapCollection");
            method.setModifiers(new String[]{"private", "static"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.util.Collection"), "input")});
            retVal.addMethod(method);

            method = new JavaMethod(new Type("java.util.Set"), "wrapCollection");
            method.setModifiers(new String[]{"private", "static"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.util.Set"), "input")});
            retVal.addMethod(method);

            method = new JavaMethod(new Type("java.util.Collection"), "wrapReadOnly");
            method.setModifiers(new String[]{"private", "static"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.util.Collection"), "input")});
            retVal.addMethod(method);

            method = new JavaMethod(new Type("java.util.Set"), "wrapReadOnly");
            method.setModifiers(new String[]{"private", "static"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.util.Set"), "input")});
            retVal.addMethod(method);

            // handle inner class

            implementz = getImplements(metadata);
            implementzTypes = new Type[implementz.length];
            for (int j = 0; j < implementz.length; j++) {
                implementzTypes[j] = new Type(implementz[j]);
            }
            innerClass.setImplementz(implementzTypes);

            method = new JavaMethod(retVal.asType(), "underlying");
            method.setModifiers(new String[]{"private"});
            innerClass.addMethod(method);

            for (int j = 0; (fields != null) && (j < fields.length); j++) {
                ValueObjectFieldMetadata vField = fields[j];
                method = new JavaMethod(vField.getProperty().getType(), vField.getProperty().getAccessor().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);

                method = new JavaMethod(new Type("boolean"), vField.getProperty().getName()+"HasBeenSet");
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
            }

            for (int k = 0; k < nonCollectionRel.length; k++) {
                ValueObjectRelationMetadata nonColRel = nonCollectionRel[k];

                method = new JavaMethod(nonColRel.getProperty().getType(), nonColRel.getProperty().getAccessor().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);

                method = new JavaMethod(new Type("boolean"), nonColRel.getProperty().getName()+"HasBeenSet");
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
            }

            for (int k = 0; k < collectionRel.length; k++) {
                ValueObjectRelationMetadata colRel = collectionRel[k];

                method = new JavaMethod(colRel.getProperty().getType(), "getAdded" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getOnceAdded" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getRemoved" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
                method = new JavaMethod(colRel.getProperty().getType(), "getUpdated" + colRel.getProperty().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);

                method = new JavaMethod(colRel.getProperty().getType(), colRel.getProperty().getAccessor().getName()+"Collection");
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);

                method = new JavaMethod(new Type(colRel.getAggregate(),1), colRel.getProperty().getAccessor().getName());
                method.setModifiers(new String[]{"public"});
                innerClass.addMethod(method);
            }

            method = new JavaMethod(new Type("int"), "hashCode");
            method.setModifiers(new String[]{"public"});
            innerClass.addMethod(method);

            method = new JavaMethod(new Type("boolean"), "equals");
            method.setModifiers(new String[]{"public"});
            method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.lang.Object"), "other")});
            innerClass.addMethod(method);

            if (metaVo.isStrictOrdering()) {
                method = new JavaMethod(new Type("int"), "compareTo");
                method.setModifiers(new String[]{"public"});
                method.setParameters(new JavaParameter[]{new JavaParameter(new Type("java.lang.Object"), "other")});
                innerClass.addMethod(method);
            }

            // add to list
            retLst.add(retVal);
        }

        return (retLst.size() == 0) ? null : (JavaClass[])retLst.toArray(new JavaClass[0]);
    }

    private QDoxValueObjectExpanderFilterMetadataProvider getValueObjectMetadataProvider() {
        QDoxCachedMetadataProvider cached = (QDoxCachedMetadataProvider) getMetadataProvider();
        return (QDoxValueObjectExpanderFilterMetadataProvider) cached.getFilteredMetadataProvider();
    }

    private Collection getOriginalMetadata() {
        return getValueObjectMetadataProvider().getOriginalMetadata();
    }

    // We are ignoring arrays for this, it's handled at .vm level
    public boolean isImmutable(Type type) {
        return immutableKnownTypes.containsKey(type.getValue());
    }

    public boolean isCloneable(Type type) {
        // JavaMethod cloneMethod = type.getJavaClass().getMethodBySignature("clone", null);
        return type.isA(new Type(Cloneable.class.getName())); // && cloneMethod != null && cloneMethod.isPublic();
    }

    public class ValueObjectRelationMetadata {
        private final Log log = LogFactory.getLog(ValueObjectRelationMetadata.class);
        private final JavaClass relationEjb;
        private final boolean isAggregate;
        private final EjbValueObjectFieldTag vTag;
        private Type relationCollectionType;

        public ValueObjectRelationMetadata(JavaClass relationEjb, Type relationCollectionType, boolean isAggregate,
            EjbValueObjectFieldTag vTag) {
            this.relationEjb = relationEjb;
            this.relationCollectionType = relationCollectionType;
            this.isAggregate = isAggregate;
            this.vTag = vTag;
        }

        public BeanProperty getProperty() {
            BeanProperty retVal;
            String propName;
            Type propType;

            if (isCollection()) {
                propName = getAggregateNamePlural();
                propType = getCollectionType();
            } else {
                propName = getAggregateName();
                propType = new Type(getAggregate());
            }

            retVal = new BeanProperty(propName);
            retVal.setType(propType);
            JavaMethod getter = new JavaMethod(propType, getGetterName(retVal));
            JavaMethod setter = new JavaMethod(propType, getSetterName(retVal));
            setter.setParameters(new JavaParameter[] {new JavaParameter(propType, propName)});
            retVal.setAccessor(getter);
            retVal.setMutator(setter);
            return retVal;
        }

        public boolean isCollection() {
            boolean isCollection = false;
            isCollection |= (relationCollectionType != null) && relationCollectionType.isA(COLLECTION_TYPE);
            isCollection |= (relationCollectionType != null) && relationCollectionType.isA(SET_TYPE);
            return isCollection;
        }

        public Type getCollectionType() {
            Type retVal = null;

            if (relationCollectionType != null) {
                if (relationCollectionType.isA(COLLECTION_TYPE)) {
                    retVal = COLLECTION_TYPE;
                } else if (relationCollectionType.isA(SET_TYPE)) {
                    retVal = SET_TYPE;
                }
            }

            return retVal;
        }

        public Type getCollectionTypeImpl() {
            String concreteType = vTag.getConcreteType();

            if (concreteType == null) {
                Type colType = getCollectionType();

                if ((colType != null) && relationCollectionType.isA(COLLECTION_TYPE)) {
                    concreteType = ArrayList.class.getName();
                } else if ((colType != null) && relationCollectionType.isA(SET_TYPE)) {
                    concreteType = HashSet.class.getName();
                } else if (log.isErrorEnabled()) {
                    log.error(EjbUtils.getMessageWithTagLocation(vTag, "Couldn't resolve concrete type for relation"));
                }
            }

            return (concreteType != null) ? new Type(concreteType) : null;
        }

        public boolean isAggregate() {
            return isAggregate;
        }

        public boolean isCompose() {
            return !isAggregate;
        }

        public String getAggregate() {
            String aggName = isAggregate ? "aggregate" : "compose";
            return vTag.getNamedParameter(aggName);
        }

        public String getAggregateName() {
            String aggName = isAggregate ? "aggregate" : "compose";
            return vTag.getNamedParameter(aggName + "-name");
        }

        public String getAggregateNamePlural() {
            String aggName = isAggregate ? "aggregate" : "compose";
            String aggregateNamePlural = vTag.getNamedParameter(aggName + "s-name");

            if (aggregateNamePlural == null) {
                String aggregateName = getAggregateName();

                if (aggregateName != null) {
                    log.debug("Agregate's s-name not found. Appending 's' aggregate name");
                    aggregateNamePlural = aggregateName + "s";
                }
            }

            return aggregateNamePlural;
        }

        public String toString() {
            StringBuffer retBuf = new StringBuffer();
            retBuf.append("[ValueObjectRelationMetadata: ");
            retBuf.append("relationEjb=" + ((relationEjb != null) ? relationEjb.getFullyQualifiedName() : null));
            retBuf.append(", isAggregate=" + isAggregate);
            retBuf.append(", vTag=" + EjbUtils.tagToString(vTag));
            retBuf.append(", aggregate=" + getAggregate());
            retBuf.append(", aggregateName=" + getAggregateName());
            retBuf.append(", aggregateNamePlural=" + getAggregateNamePlural());
            retBuf.append("]");
            return retBuf.toString();
        }
    }

    public class ValueObjectFieldMetadata {
        private Log log = LogFactory.getLog(ValueObjectFieldMetadata.class);
        private final BeanProperty property;
        private final int metaFlags;

        public ValueObjectFieldMetadata(Type propertyType, String propertyName, int metaFlags) {
            this.metaFlags = metaFlags;
            this.property = new BeanProperty(propertyName);
            this.property.setType(propertyType);
            JavaMethod getter = new JavaMethod(propertyType, getGetterName(this.property));
            JavaMethod setter = new JavaMethod(propertyType, getSetterName(this.property));
            setter.setParameters(new JavaParameter[] {new JavaParameter(propertyType, propertyName)});
            this.property.setAccessor(getter);
            this.property.setMutator(setter);
        }

        public BeanProperty getProperty() {
            return this.property;
        }

        public boolean isPrimaryKeyField() {
            return EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_PRIMARY_KEY_FIELD);
        }
    }

    public class ValueObjectMetadata {
        private Log log = LogFactory.getLog(ValueObjectMetadata.class);
        private Collection fieldLst;
        private Collection relationLst;

        //        private boolean updatable;
        private final PkMetadata pkMetadata;
        private final JavaClass javaClass;
        private final EjbValueObjectTag valueTag;

        public ValueObjectMetadata(JavaClass javaClass, EjbValueObjectTag valueTag, PkMetadata pkMetadata) {
            this.javaClass = javaClass;
            this.fieldLst = new ArrayList();
            this.relationLst = new ArrayList();
            this.valueTag = valueTag;
            this.pkMetadata = pkMetadata;
        }

        public EjbValueObjectTag getValueTag() {
            return valueTag;
        }

        public boolean isStrictOrdering() {
            return "strict".equals(valueTag.getOrdering());
        }

        public boolean isFullSynchronization() {
            return "full".equals(valueTag.getSynchronization());
        }

        public boolean isPartialSynchronization() {
            return "partial".equals(valueTag.getSynchronization());
        }

        public boolean isNoSynchronization() {
            return "none".equals(valueTag.getSynchronization());
        }

        public boolean isGeneratePKConstructor() {
            return valueTag.isGeneratePkConstructor();
        }

        public boolean isFullClone() {
            return valueTag.isFullClone();
        }

        //        public void setUpdatable(boolean updatable) {
        //            this.updatable = updatable;
        //        }
        //
        //        public boolean isUpdatable() {
        //            return this.updatable;
        //        }
        public ValueObjectRelationMetadata[] getRelations() {
            return (ValueObjectRelationMetadata[]) relationLst.toArray(new ValueObjectRelationMetadata[0]);
        }

        public ValueObjectRelationMetadata[] getCollectionRelations() {
            Collection relations = CollectionUtils.select(relationLst,
                    new Predicate() {
                        public boolean evaluate(Object arg0) {
                            ValueObjectRelationMetadata relation = (ValueObjectRelationMetadata) arg0;
                            return relation.isCollection();
                        }
                    });

            return (ValueObjectRelationMetadata[]) relations.toArray(new ValueObjectRelationMetadata[0]);
        }

        public ValueObjectRelationMetadata[] getNonCollectionRelations() {
            Collection relations = CollectionUtils.select(relationLst,
                    new Predicate() {
                        public boolean evaluate(Object arg0) {
                            ValueObjectRelationMetadata relation = (ValueObjectRelationMetadata) arg0;
                            return !relation.isCollection();
                        }
                    });

            return (ValueObjectRelationMetadata[]) relations.toArray(new ValueObjectRelationMetadata[0]);
        }

        public ValueObjectFieldMetadata[] getFields() {
            return (ValueObjectFieldMetadata[]) fieldLst.toArray(new ValueObjectFieldMetadata[0]);
        }

        public ValueObjectFieldMetadata[] getPkFields() {
            Collection fields = CollectionUtils.select(fieldLst,
                    new Predicate() {
                        public boolean evaluate(Object arg0) {
                            ValueObjectFieldMetadata field = (ValueObjectFieldMetadata) arg0;
                            return field.isPrimaryKeyField();
                        }
                    });

            return (ValueObjectFieldMetadata[]) fields.toArray(new ValueObjectFieldMetadata[0]);
        }

        public ValueObjectFieldMetadata[] getNonPkFields() {
            Collection fields = CollectionUtils.select(fieldLst,
                    new Predicate() {
                        public boolean evaluate(Object arg0) {
                            ValueObjectFieldMetadata field = (ValueObjectFieldMetadata) arg0;
                            return !field.isPrimaryKeyField();
                        }
                    });

            return (ValueObjectFieldMetadata[]) fields.toArray(new ValueObjectFieldMetadata[0]);
        }

        public void addField(ValueObjectFieldMetadata field) {
            fieldLst.add(field);
        }

        public void addRelation(ValueObjectRelationMetadata relation) {
            relationLst.add(relation);
        }

        public boolean isSimplePkProperty() {
            return pkMetadata.isSimpleProperty();
        }

        public boolean hasPk() {
            return (getPkProperty() != null);
        }

        public BeanProperty getPkProperty() {
            Type pkType = null;
            String propName = null;

            if (!pkMetadata.isSimpleProperty()) {
                if (pkMetadata.isEmptyWithParent()) {
                    PkMetadata pkMeta = pkMetadata.getParent();
                    pkType = pkMeta.getType().getType();
                    propName = "primaryKey";
                } else {
                    if (log.isErrorEnabled()) {
                        log.error("Cannot resolve primary key class name (" + javaClass.getFullyQualifiedName() +
                            "). Have you forgot to set @ejb.bean \"primkey-field\" or @ejb.pk-field ? (" + pkMetadata +
                            ")");
                    }
                }
            } else {
                BeanProperty singleProp = pkMetadata.getProperties()[0];
                pkType = singleProp.getType();
                propName = singleProp.getName();
            }

            BeanProperty prop = null;

            if (pkType != null && propName != null) {
                prop = new BeanProperty(propName);
                prop.setType(pkType);
                JavaMethod getter = new JavaMethod(pkType, getGetterName(prop));
                JavaMethod setter = new JavaMethod(pkType, getSetterName(prop));
                setter.setParameters(new JavaParameter[] {new JavaParameter(pkType, prop.getName())});
                prop.setAccessor(getter);
                prop.setMutator(setter);
            }

            return prop;
        }
    }

    private JavaClass findBeanByInterface(Collection metadata, Type type) {
        JavaClass retVal = null;
        LocalInterfacePlugin localPlugin = EjbRuntime.getLocalInterfacePlugin();
        RemoteInterfacePlugin remotePlugin = EjbRuntime.getRemoteInterfacePlugin();

        if (log.isDebugEnabled()) {
            log.debug("Looking for bean with local|remote interface named " + type);
        }

        for (Iterator iter = metadata.iterator(); retVal == null && iter.hasNext();) {
            JavaClass javaClass = (JavaClass) iter.next();
            Type localType = localPlugin.getVirtualType(javaClass).getType();
            Type remoteType = remotePlugin.getVirtualType(javaClass).getType();

            if (localType.equals(type) || remoteType.equals(type)) {
                if (log.isDebugEnabled()) {
                    log.debug("Match found for " + javaClass.getFullyQualifiedName());
                    log.debug("Local Interface " + localType);
                    log.debug("Remote Interface " + remoteType);
                }

                retVal = javaClass;
            }
        }

        if ((retVal == null) && log.isDebugEnabled()) {
            log.debug("bean with local|remote interface named " + type + " wasn't found");
        } else if (log.isDebugEnabled()) {
            log.debug("bean with local|remote interface named " + type + " found: " + retVal.getFullyQualifiedName());
        }

        return retVal;
    }

    public ValueObjectMetadata getMetaVO(JavaClass javaClass) {
        ValueObjectMetadata retVal = null;
        DocletTag[] valueTags = javaClass.getTagsByName(TagLibrary.EJB_VALUE_OBJECT);

        if (valueTags.length > 0 && log.isDebugEnabled()) {
            StringBuffer retBuf = new StringBuffer();
            retBuf.append("The list of @ejb.value-object for ").append(javaClass.getFullyQualifiedName()).append(" is {");

            for (int i = 0; i < valueTags.length; i++) {
                if (i > 0) {
                    retBuf.append(";");
                }

                retBuf.append(EjbUtils.tagToString(valueTags[i]));
            }

            retBuf.append("}");
            log.debug(retBuf.toString());
        }

        if (log.isTraceEnabled()) {
            log.trace("getMetaVO is working in " + javaClass.getFullyQualifiedName());
        }

        if (valueTags.length > 0) {

            String cacheKey = javaClass.getFullyQualifiedName() + "-" + getDestinationClassname(javaClass);
            if (log.isDebugEnabled()) {
                log.debug("I'll be using cacheKey='"+cacheKey+"' for VO of "+javaClass.getFullyQualifiedName());
            }
            if (metaVoCache.containsKey(cacheKey)) {
                if (log.isDebugEnabled()) {
                    log.debug("I found cache entry for cacheKey='"+cacheKey+"'");
                }
                retVal = (ValueObjectMetadata) metaVoCache.get(cacheKey);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("I DIDN't found cache entry for cacheKey='"+cacheKey+"'");
                }

                // By the use of MyQDoxFilterMetadataProvider, we are sure that if a ejb.value-object tag exists
                // It can only be one (at least in the normal run of this plugin
                EjbValueObjectTag valueTag = (EjbValueObjectTag) valueTags[0];
                retVal = new ValueObjectMetadata(javaClass, valueTag,
                        EjbRuntime.getPrimaryKeyClassPlugin().collectPkHierarchy(javaClass));

                // Lets start rolling
                //            retVal.setUpdatable(valueTag.isUpdatable());
                // TODO: Are we supporting hierarchaly generated files
                // similar to PrimaryKeys ?
                JavaMethod[] methods = javaClass.getMethods(true);
                JavaMethod method;

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

                    if (log.isTraceEnabled()) {
                        // log.trace(method.getName() + " = " + method);
                        log.trace(method.getName() + " seeing if this is a value-object-field");
                    }

                    // Obtain extra method flags
                    int metaFlags = ejbUtils.getMethodMetadata(javaClass, method);

                    if (EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_PRIMARY_KEY_FIELD) ||
                            EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_PERSISTENCE_FIELD) ||
                            EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_RELATION_FIELD)) {
                        if (log.isDebugEnabled()) {
                            log.debug(method.getName() +
                                " is candidate for checking. It's a persistence field, relation field or primary key field");
                        }

                        // Por aqui o resto das valida��es e l�gica
                        boolean isValueObjectField = isSelectedValueObjectField(valueTag.getMatch(), method);

                        // Primary key field must ALLWAYS be included ! (or then they must be checked if ....)
                        isValueObjectField = isValueObjectField ||
                            EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_PRIMARY_KEY_FIELD);

                        if (isValueObjectField) {
                            EjbValueObjectFieldTag vTag = findMatchedTag(valueTag.getMatch(), method);
                            BeanProperty prop = javaClass.getBeanProperty(method.getPropertyName(), true);

                            if (log.isDebugEnabled()) {
                                log.debug(method.getName() + " is a value-object-field: matches or is a pk-field.");
                            }

                            // XXX: Todo, if we have a relation found by the RelationManager, then we must have
                            // it's treatment as such, or should be ignored
                            // Maybe just check if is a EjbUtils.METADATA_METHOD_RELATION_FIELD
                            Relation relation = getRelationManager().getRelationFor(method, javaClass);

                            if ((relation != null) ||
                                    ((vTag != null) && ((vTag.getAggregate() != null) || (vTag.getCompose() != null)))) {
                                // This is a value-object relation
                                // vTag must be non-null, because we need values from it
                                if ((vTag != null) && ((vTag.getAggregate() != null) ^ (vTag.getCompose() != null))) {
                                    JavaClass relationEjb = null;
                                    Type collectionType = null;

                                    if (relation != null) {
                                        if (!method.equals(relation.getLeftMethod())) {
                                            log.debug(
                                                "Current method doesn't match relations' left method. Reversing relation");
                                            relation = relation.reverse();

                                            if (log.isDebugEnabled()) {
                                                log.debug("Reversed relation is relation=" + relation);
                                            }
                                        }

                                        if (log.isDebugEnabled()) {
                                            log.debug("Using relation information. relation=" + relation);
                                            log.debug("1# Left bean=" +
                                                ((relation.getLeftBean() != null)
                                                ? relation.getLeftBean().getFullyQualifiedName() : null));
                                            log.debug("1# Rigth bean=" +
                                                ((relation.getRightBean() != null)
                                                ? relation.getRightBean().getFullyQualifiedName() : null));
                                        }

                                        String relationEjbName = relation.getRightEJBName();
                                        relationEjb = getBeanResolver().findEjbByName(relationEjbName);
                                        collectionType = relation.getLeftMethod().getReturns();
                                    } else {
                                        log.debug("We don't have relation information for this method");
                                        Type returnType = method.getReturns();
                                        Type relationType = null;

                                        if (returnType.isA(COLLECTION_TYPE) || returnType.isA(SET_TYPE)) {
                                            String members = vTag.getMembers();

                                            if (log.isDebugEnabled()) {
                                                log.debug(returnType + ": Is a Set or Collection. Using members='" +
                                                    members + "'");
                                            }

                                            if (members == null) {
                                                if (log.isWarnEnabled()) {
                                                    log.warn(EjbUtils.getMessageWithTagLocation(vTag,
                                                            "members is null. Skipping."));
                                                }

                                                continue;
                                            }

                                            relationType = new Type(members);
                                            collectionType = returnType.isA(COLLECTION_TYPE) ? COLLECTION_TYPE : SET_TYPE;
                                        } else {
                                            if (log.isDebugEnabled()) {
                                                log.debug(returnType + ": Using it to find it's match!");
                                            }

                                            relationType = returnType;
                                        }

                                        relationEjb = findBeanByInterface(getOriginalMetadata(), relationType);
                                    }

                                    if (relationEjb == null) {
                                        log.warn("Couldn't find the related Ejb! Skipping.");
                                        continue;
                                    }

                                    ValueObjectRelationMetadata vObjRelation = new ValueObjectRelationMetadata(relationEjb,
                                            collectionType, (vTag.getAggregate() != null), vTag);

                                    if (log.isDebugEnabled()) {
                                        log.debug("Relation is " + vObjRelation);
                                    }

                                    if (vObjRelation.getAggregateName() == null) {
                                        String aggName = vObjRelation.isAggregate() ? "aggregate" : "compose";

                                        if (log.isWarnEnabled()) {
                                            log.warn(EjbUtils.getMessageWithTagLocation(vTag,
                                                    aggName + "-name can't be null. Ignoring."));
                                        }

                                        // Ignore field
                                        continue;
                                    }

                                    // add it
                                    retVal.addRelation(vObjRelation);
                                } else {
                                    // It can only be an aggregate or a composition. Not both!
                                    if (vTag != null && log.isWarnEnabled()) {
                                        log.warn(EjbUtils.getMessageWithTagLocation(vTag,
                                                "Can't be an aggregate and a composition simultaneous! Skipping."));
                                    }

                                    // Skip to next method
                                    }
                            } else {
                                // Simple value-object property
                                ValueObjectFieldMetadata vObjField = new ValueObjectFieldMetadata(prop.getType(),
                                        prop.getName(), metaFlags);
                                retVal.addField(vObjField);
                            }
                        }
                    }
                }

                if (log.isDebugEnabled()) {
                    log.debug("I am setting a cache entry for cacheKey='"+cacheKey+"'.");
                }
                metaVoCache.put(cacheKey, retVal);
            }
        }

        return retVal;
    }

    protected String getPatternBasedUnqualifiedName(JavaClass javaClass) {
        EjbValueObjectTag valueObjectTag = (EjbValueObjectTag) javaClass.getTagByName(TagLibrary.EJB_VALUE_OBJECT);
        return ejbUtils.expandPattern(getPattern(valueObjectTag), valueObjectTag.getName_());
    }

    private String getPattern(EjbValueObjectTag valueTag) {
        return (valueTag.getPattern() != null) ? valueTag.getPattern() : "{0}" + filereplace;
    }

    /**
     * Does this method matches as a property for the value object ?
     * @param match The group match ('null'/'*' for any)
     * @param method The method
     * @return true, if it matches, false, otherwise
     */
    private boolean isSelectedValueObjectField(String match, JavaMethod method) {
        // A blind match with 'null' is equals to a match with '*'
        if (null == match) {
            match = "*";
        }

        // The method must be a property accessor
        if (!method.isPropertyAccessor()) {
            if (log.isDebugEnabled()) {
                log.debug(method.getName() +
                    " is a not a value-object-field because method is not a property-accessor");
            }

            return false;
        }

        // It's a value object field if we use a blind match
        if ("*".equals(match)) {
            if (log.isDebugEnabled()) {
                log.debug(method.getName() + " is a value-object-field because we used a blind match '" + match + "'");
            }

            return true;
        }

        // We aren't using a blind match
        // Let's search for match tags
        if (findMatchedTag(match, method) != null) {
            // If we found a match, then it's a value-object-field
            return true;
        }

        if (log.isDebugEnabled()) {
            log.debug(method.getName() + " doesn't have any compatible match. Searching for match '" + match + "'");
        }

        return false;
    }

    private EjbValueObjectFieldTag findMatchedTag(String match, JavaMethod method) {
        // A blind match with 'null' is equals to a match with '*'
        if (null == match) {
            match = "*";
        }

        DocletTag[] tags = method.getTagsByName(TagLibrary.EJB_VALUE_OBJECT_FIELD);

        // Remote duplicate @ejb.value-object-match tags by 'match'
        // It will help warning duplicates
        Map matchValues = new HashMap();

        for (int i = 0; i < tags.length; i++) {
            EjbValueObjectFieldTag valueMethodMatchTag = (EjbValueObjectFieldTag) tags[i];
            String methodMatch = valueMethodMatchTag.getMatch();

            if (matchValues.containsKey(methodMatch)) {
                EjbValueObjectFieldTag savedTag = (EjbValueObjectFieldTag) matchValues.get(methodMatch);

                if (log.isWarnEnabled()) {
                    log.warn(EjbUtils.tagToString(valueMethodMatchTag) +
                        " is being ignored because there is already another tag for same match " +
                        EjbUtils.tagToString(savedTag));
                }
            } else {
                matchValues.put(methodMatch, valueMethodMatchTag);
            }
        }

        // Get matched @ejb.value-object-field tag
        EjbValueObjectFieldTag savedTag = (EjbValueObjectFieldTag) matchValues.get(match);

        // If there wasn't a tag for that match, search for one that matches all value objects
        if (savedTag == null) {
            savedTag = (EjbValueObjectFieldTag) matchValues.get("*");
        }

        return savedTag;
    }

    protected static JavaClass[] expandClass(JavaClass javaClass) {
        List retLst = new ArrayList();
        DocletTag[] valueTags = javaClass.getTagsByName(TagLibrary.EJB_VALUE_OBJECT);
        int countTags;

        if ((countTags = valueTags.length) > 0) {
            for (int i = 0; i < countTags; i++) {
                JavaClass newClass = new DuplicatedJavaClass(javaClass);
                DocletTag[] tags = newClass.getTags();
                List newTags = new ArrayList();
                int tagIdx = 0;

                for (int j = 0; j < tags.length; j++) {
                    DocletTag tag = tags[j];

                    if (TagLibrary.EJB_VALUE_OBJECT.equals(tag.getName())) {
                        if (tagIdx++ == i) {
                            newTags.add(tag);
                        }
                    } else {
                        newTags.add(tag);
                    }
                }

                // Set class tags
                newClass.setTags(newTags);

                // -----------------------------------------------------------------------
                // We'll validate at 'QDoxValueObjectExpanderFilterMetadataProvider' if we
                // have overlapping value objects
                // -----------------------------------------------------------------------
                // Add it to return list
                retLst.add(newClass);
            }
        } else {
            // Add it to return list
            retLst.add(javaClass);
        }

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

    /**
     * Don't let fileregex be changed
     */
    public void setFileregex(String fileregex) {
        throw new RuntimeException("Can't set fileregex for plugin. Try setting it in " + EjbConfig.class.getName());
    }

    public boolean shouldGenerate(Object metadata) {
        JavaClass javaClass = (JavaClass) metadata;
        boolean generate = ejbUtils.shouldGenerate(javaClass);
        generate = generate && ejbUtils.isEntityBean(javaClass);
        generate = generate && (getMetaVO(javaClass) != null);
        if (generate) generate = isDestinationDirty(javaClass);
        if (generate && verbose) System.out.println(
                "Generating Value Object for " + javaClass.getName());
        return generate;
    }

    public boolean isAbstract(JavaClass javaClass) {
        ValueObjectMetadata metaVo = getMetaVO(javaClass);
        return metaVo.getValueTag().isAbstract();
    }

    public String getExtends(JavaClass javaClass) {
        ValueObjectMetadata metaVo = getMetaVO(javaClass);
        String extendz = metaVo.getValueTag().getExtends();

        if (extendz == null) {
            extendz = Object.class.getName();
        }

        return extendz;
    }

    public String[] getImplements(JavaClass javaClass) {
        ValueObjectMetadata metaVo = getMetaVO(javaClass);
        Collection implementsLst = new ArrayList();

        if (metaVo.getValueTag().getImplements() != null) {
            implementsLst.addAll(Arrays.asList(metaVo.getValueTag().getImplements()));
        }

        if (metaVo.isStrictOrdering() && !implementsLst.contains(Comparable.class.getName())) {
            implementsLst.add(Comparable.class.getName());
        }

        if (!implementsLst.contains(Serializable.class.getName())) {
            implementsLst.add(Serializable.class.getName());
        }

        if (!implementsLst.contains(Cloneable.class.getName())) {
            implementsLst.add(Cloneable.class.getName());
        }

        return (String[]) implementsLst.toArray(new String[0]);
    }

    private static class QDoxValueObjectExpanderFilterMetadataProvider extends QDoxCachedMetadataProvider {
        protected final Log log = LogFactory.getLog(QDoxValueObjectExpanderFilterMetadataProvider.class);
        private Collection tamperedMetadata;
        protected ValueObjectPlugin vObjPlugin;

        public QDoxValueObjectExpanderFilterMetadataProvider(QDoxCapableMetadataProvider wrapper) {
            super(wrapper);
        }

        void setPlugin(ValueObjectPlugin vObjPlugin) {
            this.vObjPlugin = vObjPlugin;
        }

        public Collection getMetadata() throws GeneramaException {
            if (log.isTraceEnabled()) {
                log.trace("getMetadata() called");
            }

            // For each JavaClass in source metadata, we may need to generate N classes, where N is the number
            // of "ejb.value-object" class tags
            if (tamperedMetadata == null) {
                Collection meta = getOriginalMetadata();

                if (meta != null) {
                    List newMetadata = new ArrayList();

                    for (Iterator iter = meta.iterator(); iter.hasNext();) {
                        JavaClass javaClass = (JavaClass) iter.next();
                        newMetadata.addAll(Arrays.asList(expandClass(javaClass)));
                    }

                    // We now have to filter the collection to eliminate duplicate value objects

                    /*
                     * A map cointaining a cache of value objects under generation: It will be used for detect when overlapping
                     * value-objects are trying to be generated
                     */
                    final Map vObjsNames = new HashMap();
                    tamperedMetadata = CollectionUtils.select(newMetadata,
                            new Predicate() {
                                public boolean evaluate(Object arg0) {
                                    JavaClass javaClass = (JavaClass) arg0;
                                    boolean retVal = true;

                                    if (vObjPlugin.shouldGenerate(javaClass)) {
                                        // Let's try to detect if overlapping value-objects are being generated
                                        String vObjectType = vObjPlugin.getVirtualType(javaClass).toString();

                                        if (vObjsNames.containsKey(vObjectType)) {
                                            if (log.isWarnEnabled()) {
                                                log.warn("There is already an value-object generate with same type '" +
                                                    vObjectType + "'. Ignoring new one.");
                                            }

                                            retVal = false;
                                        } else {
                                            vObjsNames.put(vObjectType, vObjectType);
                                        }
                                    }

                                    return retVal;
                                }
                            });
                }
            }

            return tamperedMetadata;
        }

        public Collection getOriginalMetadata() {
            if (log.isTraceEnabled()) {
                log.trace("getOriginalMetadata() called");
            }

            return super.getMetadata();
        }

        public String getOriginalFileName(Object meta) {
            JavaClass javaClass = (JavaClass) meta;
            String retVal = super.getOriginalFileName(meta);

            if (log.isTraceEnabled()) {
                log.trace("getOriginalFileName() javaClass=" + javaClass.getFullyQualifiedName() + " ==> " + retVal);
            }

            return retVal;
        }

        public String getOriginalPackageName(Object meta) {
            JavaClass javaClass = (JavaClass) meta;
            String retVal;
            retVal = super.getOriginalPackageName(meta);

            if (log.isTraceEnabled()) {
                log.trace("getOriginalPackageName() javaClass=" + javaClass.getFullyQualifiedName() + " ==> " + retVal);
            }

            return retVal;
        }
    }

    /**
     * Returns the beanResolver, lazily loaded to allow all plugins to register
     * their tags before the beanResolver is created for the first time.
     */
    public final EjbBeanResolver getBeanResolver() {
        if (beanResolver == null) {
            beanResolver = ejbUtils.createEjbBeanResolver(getOriginalMetadata());
        }
        return beanResolver;
    }

    /**
     * Returns the relationManager, lazily loaded to allow all plugins to register
     * their tags before the relationManager is created for the first time.
     */
    public final RelationManager getRelationManager() {
        if (relationManager == null) {
            relationManager = ejbUtils.createRelationManager(getOriginalMetadata());
        }
        return relationManager;
    }

}
TOP

Related Classes of org.xdoclet.plugin.ejb.entity.ValueObjectPlugin

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.