Package org.xdoclet.plugin.ejb.entity

Source Code of org.xdoclet.plugin.ejb.entity.PrimaryKeyClassPlugin$PkClass

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import org.generama.MergeableVelocityTemplateEngine;
import org.generama.WriterMapper;

import org.xdoclet.plugin.ejb.EjbConfig;
import org.xdoclet.plugin.ejb.EjbJavaClassBuilder;
import org.xdoclet.plugin.ejb.EjbJavaGeneratingPlugin;
import org.xdoclet.plugin.ejb.EjbJavaType;
import org.xdoclet.plugin.ejb.EjbRuntime;
import org.xdoclet.plugin.ejb.EjbUtils;
import org.xdoclet.plugin.ejb.qtags.EjbPkTag;
import org.xdoclet.plugin.ejb.qtags.TagLibrary;

import com.thoughtworks.qdox.model.BeanProperty;
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;

/**
* Primary key class plugin
*
* @author Diogo Quintela
* @version $Revision: 638 $
*/
public class PrimaryKeyClassPlugin extends EjbJavaGeneratingPlugin implements EjbJavaClassBuilder {
    /** Cache for hierarchy resolver */
    private Map pkHierarchyCache = new HashMap();

    public PrimaryKeyClassPlugin(MergeableVelocityTemplateEngine templateEngine,
            WriterMapper writerMapper, EjbConfig config) {
        super(templateEngine, writerMapper, config);
        EjbRuntime.setPlugin(this);
        setPackageregex("beans");
        setPackagereplace("util");
        super.setFileregex(config.getEjbReplaceRegex());
        setFilereplace("PK");
        super.setMultioutput(true);
    }

    /**
     * Don't let multioutput be changed
     */
    public void setMultioutput(boolean multioutput) {
        throw new RuntimeException("Can't set multioutput for plugin");
    }

    /**
     * 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 String getExtends(JavaClass javaClass) {
        PkMetadata pkHierarchy = getMetaPk(javaClass);

        // We'll go up twice, as the first above is the metadata from the class being generated
        pkHierarchy = (pkHierarchy != null) ? pkHierarchy.getParent() : null;
        return (pkHierarchy != null) ? pkHierarchy.getType().toString() : "java.lang.Object";
    }

    public String[] getImplements(JavaClass javaClass) {
        Collection implementsLst = new ArrayList();
        PkMetadata pkHierarchy = getMetaPk(javaClass);
        implementsLst.addAll(Arrays.asList(pkHierarchy.getImplements()));

        if (!implementsLst.contains("java.io.Serializable")) {
            implementsLst.add("java.io.Serializable");
        }

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

    /**
     * JavaClass that describes the generated artifacts
     *
     * @return The generated artifacts JavaClass otherwise null
     */
    public JavaClass[] buildFor(JavaClass metadata) {
        if (!shouldGenerate(metadata)) {
            return null;
        }

        JavaField field;
        JavaMethod method;
        JavaClass retVal = createDynamicJavaClass(getDestinationClassname(metadata), getDestinationPackage(metadata), null, getMetadataProvider());
        retVal.setModifiers(new String[] { "public" });

        PkMetadata metaPk = getMetaPk(metadata);
        String extendz = getExtends(metadata);

        JavaClass parentBuilded[];
        JavaClass parent = metadata.getSuperJavaClass();
        while (parent != null) {
            parentBuilded = buildFor(parent);
            if ((parentBuilded != null) && (parentBuilded[0].getFullyQualifiedName().equals(extendz))) {
                retVal.setSuperClass(parentBuilded[0].asType());
                break;
            }
            parent = parent.getSuperJavaClass();
        }
        String[] implementz = getImplements(metadata);
        Type[] implementzTypes = new Type[implementz.length];
        for (int i = 0; i < implementz.length; i++) {
            implementzTypes[i] = new Type(implementz[i]);
        }
        retVal.setImplementz(implementzTypes);

        BeanProperty[] properties = metaPk.getProperties();
        for (int i = 0; (properties != null) && (i < properties.length); i++) {
            field = new JavaField(properties[i].getType(), properties[i].getName());
            field.setModifiers(new String[] { "public" });
            retVal.addField(field);
        }

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

        BeanProperty[] parentProperties = metaPk.getParentProperties();
        if ((properties != null && properties.length > 0)
         || (parentProperties != null && parentProperties.length > 0)) {
            method = new JavaMethod(getDestinationClassname(metadata));
            method.setConstructor(true);
            method.setModifiers(new String[] { "public" });

            JavaParameter[] params = new JavaParameter[((properties != null) ? properties.length : 0) ((parentProperties != null) ? parentProperties.length : 0)];
            int idx = 0;
            for (int i = 0; (properties != null) && (i < properties.length); i++) {
                params[idx++] = new JavaParameter(properties[i].getType(), properties[i].getName());
            }
            for (int i = 0; (parentProperties != null) && (i < parentProperties.length); i++) {
                params[idx++] = new JavaParameter(parentProperties[i].getType(), parentProperties[i].getName());
            }
            method.setParameters(params);
            retVal.addMethod(method);
        }

        for (int i = 0; (properties != null) && (i < properties.length); i++) {
            method = new JavaMethod(getSetterName(properties[i]));
            method.setModifiers(new String[] { "public" });
            method.setParameters(new JavaParameter[]{new JavaParameter(properties[i].getType(), properties[i].getName())});
            retVal.addMethod(method);
            method = new JavaMethod(properties[i].getType(), getGetterName(properties[i]));
            method.setModifiers(new String[] { "public" });
            retVal.addMethod(method);
        }

        method = new JavaMethod(new Type("int"), "hashCode");
        method.setModifiers(new String[] { "public" });
        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);

        method = new JavaMethod(new Type("java.lang.String"), "toString");
        method.setModifiers(new String[] { "public" });
        retVal.addMethod(method);

        return new JavaClass[]{retVal};
    }

    public boolean shouldGenerate(Object metadata) {
        JavaClass javaClass = (JavaClass) metadata;
        PkMetadata pkHierarchy = getMetaPk(javaClass);
        boolean generate = (pkHierarchy != null) && pkHierarchy.getType().equals(getVirtualType(javaClass));
        if (generate) generate = isDestinationDirty(javaClass);
        if (generate && verbose) System.out.println(
                "Generating Primary Key for " + javaClass.getName());
        return generate;
    }

    public PkMetadata getMetaPk(JavaClass javaClass) {
        PkMetadata pkHierarchy = collectPkHierarchy(javaClass);

        if (pkHierarchy.isEmptyWithParent()) {
            return pkHierarchy.getParent();
        }

        return null;
    }

    public List getHierarchy(JavaClass javaClass) {
        List hierarchyLst = new ArrayList();
        hierarchyLst.add(javaClass);

        while ((javaClass = javaClass.getSuperJavaClass()) != null) {
            hierarchyLst.add(javaClass);
        }

        return hierarchyLst;
    }

    private BeanProperty[] getPkProperties(JavaClass javaClass) {
        JavaMethod[] methods = javaClass.getMethods();
        JavaMethod method;
        SortedSet propSet = new TreeSet(new Comparator() {
                    public int compare(Object o1, Object o2) {
                        BeanProperty p1 = (BeanProperty) o1;
                        BeanProperty p2 = (BeanProperty) o2;
                        return p1.getName().compareTo(p2.getName());
                    }
                });

        for (int i = 0; i < methods.length; i++) {
            method = methods[i];
            int metaFlags = ejbUtils.getMethodMetadata(javaClass, method);

            if (EjbUtils.hasFlag(metaFlags, EjbUtils.METADATA_METHOD_PRIMARY_KEY_FIELD)) {
                BeanProperty beanProperty = javaClass.getBeanProperty(method.getPropertyName());

                if (beanProperty != null) {
                    if (propSet.add(beanProperty)) {
                        if (log.isDebugEnabled()) {
                            log.debug(beanProperty.getName() + " was added to the Set");
                        }
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug(beanProperty.getName() +
                                " wasn't added to the Set. There must be already a property with the same name");
                        }
                    }
                } else {
                    String errorMessage = "Unexpected null property for " + method.getPropertyName() + " in " +
                        javaClass.getFullyQualifiedName();
                    log.error(errorMessage);
                    throw new Error(errorMessage);
                }
            }
        }

        return (BeanProperty[]) propSet.toArray(new BeanProperty[0]);
    }

    public PkMetadata collectPkHierarchy(JavaClass javaClass) {
        PkMetadata retVal = null;

        if (pkHierarchyCache.containsKey(javaClass)) {
            retVal = (PkMetadata) pkHierarchyCache.get(javaClass);
        } else {
            List hierarchyLst = getHierarchy(javaClass);
            Collections.reverse(hierarchyLst); // Top down scan
            PkClass current = new PkClass();
            EjbPkTag pkTag;

            for (Iterator iter = hierarchyLst.iterator(); iter.hasNext();) {
                JavaClass clazz = (JavaClass) iter.next();
                pkTag = (EjbPkTag) clazz.getTagByName(TagLibrary.EJB_PK);

                if (pkTag != null) {
                    String pkExtends = pkTag.getExtends();

                    if (pkExtends != null) {
                        // We need to test if so far we haven't collected any properties to allow to set top class
                        // If we don't check this, we could be loosing primary key's properties
                        // NOTE: xdoclet1 allowed this, although it generates non compiling class
                        if (current.isEmpty() && current.getParent() == null) {
                            current.setType(new EjbJavaType(pkExtends));
                            current = new PkClass(current);
                        } else {
                            throw ejbUtils.getErrorWithTagLocation(pkTag,
                                "Can't set \"extends\" property without loosing current primary key properties: " +
                                current);
                        }
                    }

                    String[] implementz = pkTag.getImplements();

                    if (implementz != null) {
                        current.addImplements(implementz);
                    }
                }

                current.addProperties(getPkProperties(clazz));

                if (!current.isSimpleProperty() && !current.isEmpty() && ((pkTag == null) || pkTag.isGenerate())) {
                    current.setType(getVirtualType(clazz));
                    current = new PkClass(current);
                }
            }

            // Set into cache
            pkHierarchyCache.put(javaClass, current);

            // Set return value
            retVal = current;
        }

        return retVal;
    }

    /**
     * Gets primary key field name if exists
     *
     * @param javaClass Entity bean
     *
     * @return The field name if exists
     */
    public String pkField(JavaClass javaClass) {
        PkMetadata pkClassHierarchy = collectPkHierarchy(javaClass);

        if (pkClassHierarchy.isSimpleProperty()) {
            return pkClassHierarchy.getProperties()[0].getName();
        }

        return null;
    }

    /**
     * Get the primary key class specified for a given class. If a primary key field has been specified, using the
     * <code>primkey-field</code> parameter on the <code>ejb.bean</code> tag, this will be the return type of that
     * field's getter method.
     * Otherwise, it will be determined by the various parameters of the <code>ejb.pk</code> tag
     * and the subtask's settings for default pattern, packageSubstitution, etc.
     *
     * @param javaClass Entity bean
     *
     * @return The primary key for the entity
     * @throws ClassNotFoundException
     */
    public String pkClass(JavaClass javaClass) {
        PkMetadata pkClassHierarchy = collectPkHierarchy(javaClass);

        if (pkClassHierarchy.isSimpleProperty()) {
            return pkClassHierarchy.getProperties()[0].getType().toString();
        } else {
            if (pkClassHierarchy.isEmptyWithParent()) {
                pkClassHierarchy = pkClassHierarchy.getParent();
                return pkClassHierarchy.getType().toString();
            } else {
                throw new Error("Cannot resolve primary key class name (" + javaClass.getFullyQualifiedName() +
                    "). Have you forgot to set @ejb.bean \"primkey-field\" or @ejb.pk-field ? (" + pkClassHierarchy +
                    ")");
            }
        }
    }

    /**
     * Get the primary key class specified for a given class. If a primary key field has been specified, using the
     * <code>primkey-field</code> parameter on the <code>ejb.bean</code> tag, this will be the return type of that
     * field's getter method.
     * Otherwise, it will be determined by the various parameters of the <code>ejb.pk</code> tag
     * and the subtask's settings for default pattern, packageSubstitution, etc.
     *
     * @param javaClass Entity bean
     *
     * @return The primary key for the entity
     * @throws ClassNotFoundException
     */
    public Type getPkClassType(JavaClass javaClass) {
        PkMetadata pkClassHierarchy = collectPkHierarchy(javaClass);

        if (pkClassHierarchy.isSimpleProperty()) {
            return pkClassHierarchy.getProperties()[0].getType();
        } else {
            if (pkClassHierarchy.isEmptyWithParent()) {
                pkClassHierarchy = pkClassHierarchy.getParent();
                return new Type(pkClassHierarchy.getType().toString());
            } else {
                throw new Error("Cannot resolve primary key class name (" + javaClass.getFullyQualifiedName() +
                    "). Have you forgot to set @ejb.bean \"primkey-field\" or @ejb.pk-field ? (" + pkClassHierarchy +
                    ")");
            }
        }
    }

    protected String getLocalyDefinedFullClassName(JavaClass clazz) {
        EjbPkTag pkTag = (EjbPkTag) clazz.getTagByName(TagLibrary.EJB_PK);
        return (pkTag != null) ? pkTag.getClass_() : null;
    }

    protected String getPatternBasedUnqualifiedName(JavaClass clazz) {
        EjbPkTag pkTag = (EjbPkTag) clazz.getTagByName(TagLibrary.EJB_PK);
        return (pkTag != null && pkTag.getPattern() != null) ? ejbUtils.expandPattern(pkTag.getPattern(), clazz) : null;
    }

    protected String getLocalyDefinedPackageName(JavaClass clazz) {
        EjbPkTag pkTag = (EjbPkTag) clazz.getTagByName(TagLibrary.EJB_PK);
        return (pkTag != null) ? pkTag.getPackage() : null;
    }

    public final static class PkClass implements PkMetadata {
        private EjbJavaType type;
        private List propertiesLst = new ArrayList();
        private List implementsLst = new ArrayList();
        private PkClass parent;

        public PkClass(PkClass parent) {
            this.parent = parent;
        }

        public PkClass() {
            // no parent
        }

        public void addImplements(String[] implementz) {
            implementsLst.addAll(Arrays.asList(implementz));
        }

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

        public void addProperty(BeanProperty prop) {
            propertiesLst.add(prop);
        }

        public void addProperties(BeanProperty[] prop) {
            propertiesLst.addAll(Arrays.asList(prop));
        }

        public BeanProperty[] getProperties() {
            return (BeanProperty[]) propertiesLst.toArray(new BeanProperty[0]);
        }

        public BeanProperty[] getParentProperties() {
            List retVal = new ArrayList();

            if (parent != null) {
                retVal.addAll(Arrays.asList(parent.getProperties()));
                retVal.addAll(Arrays.asList(parent.getParentProperties()));
            }

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

        public EjbJavaType getType() {
            return type;
        }

        public PkMetadata getParent() {
            return this.parent;
        }

        public void setParent(PkClass parent) {
            this.parent = parent;
        }

        public void setType(EjbJavaType type) {
            this.type = type;
        }

        public boolean isSimpleProperty() {
            return !hasParent() && (propertiesLst.size() == 1) && (implementsLst.size() == 0);
        }

        public boolean isEmpty() {
            return (type == null) && (propertiesLst.size() == 0) && (implementsLst.size() == 0);
        }

        public boolean hasParent() {
            return (parent != null);
        }

        public boolean isEmptyWithParent() {
            return isEmpty() && hasParent();
        }

        public String toString() {
            StringBuffer retBuf = new StringBuffer();
            retBuf.append('[');
            retBuf.append("type=");
            retBuf.append(type);
            retBuf.append(", properties=");
            retBuf.append('{');

            for (Iterator iter = propertiesLst.iterator(); iter.hasNext();) {
                BeanProperty prop = (BeanProperty) iter.next();
                retBuf.append(prop.getName());

                if (iter.hasNext()) {
                    retBuf.append(',');
                }
            }

            retBuf.append('}');
            retBuf.append(", implements=");
            retBuf.append('{');

            for (Iterator iter = implementsLst.iterator(); iter.hasNext();) {
                String implementz = (String) iter.next();
                retBuf.append(implementz);

                if (iter.hasNext()) {
                    retBuf.append(',');
                }
            }

            retBuf.append('}');
            retBuf.append(", parent=");
            retBuf.append(parent);
            retBuf.append(']');
            return retBuf.toString();
        }
    }
}
TOP

Related Classes of org.xdoclet.plugin.ejb.entity.PrimaryKeyClassPlugin$PkClass

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.