/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.ejb;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Iterator;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.xml.JLinkedList;
import org.ow2.jonas.deployment.ejb.xml.AssemblyDescriptor;
import org.ow2.jonas.deployment.ejb.xml.CmpField;
import org.ow2.jonas.deployment.ejb.xml.Entity;
import org.ow2.jonas.deployment.ejb.xml.JdbcMapping;
import org.ow2.jonas.deployment.ejb.xml.JonasEntity;
import org.ow2.jonas.lib.util.BeanNaming;



/**
 * Base class to hold meta-information related to an entity of type CMP.
 * @author Christophe Ney [cney@batisseurs.com] : Initial developer
 * @author Helene Joanin
 * @author Jerome Camilleri : automatic PK
 */
public abstract class EntityCmpDesc extends EntityDesc {

    protected HashMap fieldDesc = new HashMap();
    protected String primaryKeyField = null;
    protected String jdbcAutomaticPkFieldName = null;

    /**
     * constructor to be used by parent node.
     * @param classLoader class loader to use to laod bean's classes
     * @param ent entity in the standard deployment descriptor
     * @param asd assembly-descriptor in the standard deployment descriptor
     * @param jEnt jonas-entity in the JOnAS deployment descriptor
     * @param fileName jar or directory containing ejb
     * @throws DeploymentDescException in error case.
     */
    public EntityCmpDesc(ClassLoader classLoader, Entity ent,
             AssemblyDescriptor asd, JonasEntity jEnt,
             JLinkedList jMDRList, String fileName)
        throws DeploymentDescException {

        super(classLoader, ent, asd, jEnt, jMDRList, fileName);

        // field descriptors for persistent fields
        for (Iterator i = ent.getCmpFieldList().iterator(); i.hasNext();) {
            String fn = ((CmpField) i.next()).getFieldName();
            FieldDesc fd = this.newFieldDescInstance();
            fd.setName(fn);
            fd.setPrimaryKey(false);
            fieldDesc.put(fn, fd);

        }

        // jdbc pk auto field name
        JdbcMapping jm = jEnt.getJdbcMapping();
        if ((jm != null) && (jm.getJdbcAutomaticPkFieldName() != null)) {
            if (jm.getJdbcAutomaticPkFieldName().length() != 0) {
                jdbcAutomaticPkFieldName = jm.getJdbcAutomaticPkFieldName();
            }
        }
        if (jdbcAutomaticPkFieldName == null) {
            // Default value
            jdbcAutomaticPkFieldName =  "JPK_";
        }

        if (isUndefinedPK()) { // Automatic PK
           // If Primary Field is not declared (auto generated key field)
            primaryKeyField = "JONASAUTOPKFIELD";
        } else if (ent.getPrimkeyField() != null) {
            // primary key field
            primaryKeyField = ent.getPrimkeyField();
            FieldDesc fd = (FieldDesc) fieldDesc.get(primaryKeyField);
            if (fd == null) {
                throw new DeploymentDescException("primkey-field " + primaryKeyField + " is not listed as cmp-field in bean " + this.ejbName);
            }
            fd.setPrimaryKey(true);
        } else {
            // public fields of primary key class
            Field[] pcf = primaryKeyClass.getFields();
            for (int i = 0; i < pcf.length; i++) {
                if (Modifier.isPublic(pcf[i].getModifiers())) {
                    String pn = pcf[i].getName();
                    // exclude serialVersionUID field for jdk1.2.1 on solaris and
                    // exclude JProbe$ for JProbe using
                    if (!pn.equals("serialVersionUID") && !pn.startsWith("JProbe$")) {
                        FieldDesc fd = (FieldDesc) fieldDesc.get(pn);
                        if (fd == null) {
                            throw new DeploymentDescException("public field " + pn + " of primkey-class is not listed as cmp-field in bean " + this.ejbName);
                        }
                        fd.setPrimaryKey(true);
                    }
                }
            }
        }
        String packageName = BeanDesc.GENERATED_PREFIX + BeanNaming.getPackageName(getEjbClass().getName());
        derivedBeanName = new String("JOnAS" + getIdentifier() + "Bean");
        fullDerivedBeanName = BeanNaming.getClassName(packageName, derivedBeanName);

        // automatic-pk
        // used of specific tag automatic-pk is deprecated so nothing in this code was about jdbc-mapping
        String primkeytype = ent.getPrimKeyClass();
        if ((jm != null) && (jm.getAutomaticPk() != null)) {
            // optional parameter automatic-pk
            jdbcAutomaticPk = jm.getAutomaticPk().equalsIgnoreCase("true");
            if (jdbcAutomaticPk && pkObjectType) {
                // You can't use specific tag 'automatic-pk' with prim-key-type=java.lang.Object
                throw new DeploymentDescException("Don't use specific tag 'automatic-pk' with prim-key-type=java.lang.Object in bean " + ent.getEjbName());
            }
        }
        if (pkObjectType && ent.getPrimkeyField() != null) {
             throw new DeploymentDescException("'prim-key-field' must not be set if your prim-key-type was java.lang.Object in bean " + ent.getEjbName());
        }
        if (this.isAutomaticPk()) { // Check if prim-key-class type is Integer or java.lang.Object
            if (!(primkeytype.equalsIgnoreCase("java.lang.Integer")
                    || primkeytype.equalsIgnoreCase("Integer"))) {
                throw new DeploymentDescException("You must used java.lang.Integer type for your auto-generate primary key field in bean " + ent.getEjbName());
            }
        }
    }

    /**
     * Get descriptor for a given field name
     * Used by GenIC
     * @param fieldName Name of the field
     * @return Descriptor for the given field or null
     */
    public FieldDesc getCmpFieldDesc(String fieldName) {
        return (FieldDesc) fieldDesc.get(fieldName);
    }

    /**
     * Indicate if the primary key field is only one field with a primary-key-field
     * defined in the DD.
     * @return true if the primary key field is only one field
     *         with a primary-key-field defined in the DD.
     */
    public boolean hasSimplePkField() {
        return primaryKeyField != null;
    }

    // TODO remove this method and keep only the hasSimplePkField method
    public boolean hasPrimaryKeyField() {
        return hasSimplePkField();
    }

    /**
     * Get the primary key field for the entity.
     * @return Field for the primary key
     */
    public FieldDesc getSimplePkField() {
        FieldDesc fd = (FieldDesc) fieldDesc.get(getSimplePkFieldName());
        return fd;
    }

    /**
     * Get the primary key field name for the entity.
     * @return Field for the primary key
     */
    public String getSimplePkFieldName() {
        if (primaryKeyField == null) {
            throw new Error("No primary key field defined for bean " + this.ejbName);
        }
        return primaryKeyField;
    }

    // TODO remove this method and keep only the getSimplePkFieldName() method
    public String getPrimaryKeyFieldName() {
        return getSimplePkFieldName();
    }

    /**
     * Get the associated field for auto genarated pk field specify by user
     * @return Name of the field where automatic pk bean is stored
     */
    public String getJdbcAutomaticPkFieldName() {
        return jdbcAutomaticPkFieldName;
    }

    public Iterator getCmpFieldDescIterator() {
        return fieldDesc.values().iterator();
    }
    /**
     * String representation of the object for test purpose
     * @return String representation of this object
     */
    public String toString() {
        StringBuffer ret = new StringBuffer();
        ret.append(super.toString());
        for (Iterator i = fieldDesc.keySet().iterator(); i.hasNext();) {
            String f = (String) i.next();
            FieldDesc fd = (FieldDesc) fieldDesc.get(f);
            ret.append("\ngetCmpFieldDesc(" + f + ")=" + fd.getClass().getName());
            ret.append(fd.toString());
        }
        if (hasPrimaryKeyField()) {
            ret.append("\ngetPrimaryKeyField()=" + getPrimaryKeyFieldName());
        }
        return ret.toString();
    }

    /**
     * factory method for field descriptors
     * @return a FieldDesc.
     */
    protected FieldDesc newFieldDescInstance() {
        return new FieldJdbcDesc();
    }
}

