/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.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
 *
 * Initial developer(s): ____________________________________.
 * Contributor(s): Lutris Technologies Inc http://www.lutris.com
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
*/


package org.objectweb.jonas_ejb.deployment.api;

import java.lang.String;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Iterator;

import org.objectweb.jonas_ejb.deployment.xml.AssemblyDescriptor;
import org.objectweb.jonas_ejb.deployment.xml.Entity;
import org.objectweb.jonas_ejb.deployment.xml.CmpField;
import org.objectweb.jonas_ejb.deployment.xml.JonasEntity;

/**
 * Base class for descriptor of an entity bean with container managed persistence 
 * @author Christophe Ney
 */
public abstract class EntityCmpDesc extends EntityDesc {

    protected Hashtable fieldDesc = new Hashtable();
    protected Field primaryKeyField = null;    
    protected Method isModifiedMethod = null;
    
    /**
     * Package protected constructor used by API
     */
    EntityCmpDesc(ClassLoader classLoader,Entity ent, AssemblyDescriptor asd,JonasEntity jEnt)
            throws DeploymentDescException {
        super(classLoader,ent,asd,jEnt);

        // isModifiedMethod
        if (jEnt.getIsModifiedMethodName() != null) {
            String mName = jEnt.getIsModifiedMethodName().getValue();
            try {
                isModifiedMethod = this.ejbClass.getMethod(mName, new Class[0]);
            } catch (NoSuchMethodException e) {
                isModifiedMethod = null;
                throw new DeploymentDescException(mName+" is not a method of "+this.ejbClass.getName());
            } catch (SecurityException e) {
                throw new DeploymentDescException("Cannot use java reflexion on "+this.ejbClass.getName());
            }
        }

        // primary key field
        if (ent.getPrimkeyField()!=null) {
            String fn = ent.getPrimkeyField().getValue();
            try {
                primaryKeyField=ejbClass.getField(fn);
            } catch (NoSuchFieldException e){
                throw new DeploymentDescException(fn+" is not a field of "+this.ejbClass.getName());
            } catch (SecurityException e){
                throw new DeploymentDescException("Cannot use java reflexion on "+this.ejbClass.getName());
            }
        }

        // field descriptors for persistent fields
        for (Iterator i = ent.getCmpFieldList().iterator();i.hasNext();) {
            String fn = ((CmpField)i.next()).getFieldName().getValue();
            // check for valid field name
            try {
                Field f = ejbClass.getField(fn);
            } catch (NoSuchFieldException e) {
                throw new DeploymentDescException("Invalid field name in field-name in bean "+this.ejbName,e);
            } catch (SecurityException e) {
                throw new DeploymentDescException("Cannot use java reflexion on "+this.ejbClass.getName());
            }
            fieldDesc.put(fn,new FieldDesc(false));
        }
         
        // primary key fields
        if (primaryKeyField!=null) {
            // primary key field
            if (!fieldDesc.containsKey(primaryKeyField.getName())) 
                throw new DeploymentDescException("primkey-field is not listed as cmp-field in bean "+this.ejbName);
            fieldDesc.put(primaryKeyField.getName(),new FieldDesc(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$")) {
                        if (!fieldDesc.containsKey(pn)) 
                            throw new DeploymentDescException("public field "+pn+" of primkey-class is not listed as cmp-field in bean "+this.ejbName);
                        fieldDesc.put(pn,new FieldDesc(true));
                    }
                }
            }
        }
    }
    
    /**
     * Assessor for a CMP field
     * @return Descriptor for the given field
     */
    public boolean hasCmpFieldDesc(Field field) {
        return fieldDesc.containsKey(field.getName());
    }
    
    /**
     * Get descriptor for a given field
     * Used by GenIC
     * @param field of the bean class
     * @return Descriptor for the given field
     */
    public FieldDesc getCmpFieldDesc(Field field) {
        FieldDesc ret = (FieldDesc)fieldDesc.get(field.getName());
        if (ret==null)
            throw new Error("Field "+field.getName()+" is not a cmp field of class "+this.ejbClass.getName());
        return ret;
    }
    

    /**
     * Assessor for primary key being a field
     * @return true if primary key is a field
     */
    public boolean hasPrimaryKeyField(){
        return primaryKeyField!=null;
    }    
    
    /**
     * Get the primary key field for the entity.
     * Used by GenIC
     * @return Field for the primary key
     */
    public Field getPrimaryKeyField(){
        if (primaryKeyField==null)
            throw new Error("No primary key field defined for bean "+this.ejbName);
        return primaryKeyField;
    }

    /**
     * Assessor for existence of a isModified methoe
     * @return true of isModified method exist for the bean
     */
    public boolean hasIsModifiedMethod(){
        return isModifiedMethod!=null;
    }


    /**
     * Get the 'isModified' method name implemented in the bean class.
     * (This information is JOnAS specific).
     * @return Name of the isModified method
     */
    public Method getIsModifiedMethod(){
        if (isModifiedMethod==null)
            throw new Error("No isModified method defined for bean "+this.ejbName);
        return isModifiedMethod;
    }
        

    /**
     * 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());
        Enumeration e = fieldDesc.keys();
        while (e.hasMoreElements()) {
            String f = (String)e.nextElement();
            FieldDesc fd = (FieldDesc)fieldDesc.get(f);
            ret.append("\ngetCmpFieldDesc("+f+")="+fd.getClass().getName());
            ret.append(fd.toString());
        }
        if (hasPrimaryKeyField())
            ret.append("\ngetPrimaryKeyField()="+getPrimaryKeyField().toString());
        if (hasIsModifiedMethod())
            ret.append("getIsModifiedMethod()="+getIsModifiedMethod().toString());
        return ret.toString();
    }
}

