/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */


package com.sun.jdo.api.persistence.enhancer.meta;

import java.io.PrintWriter;

import com.sun.jdo.api.persistence.enhancer.util.Support;
import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.jdo.PersistenceFieldElement;
import com.sun.jdo.api.persistence.model.jdo.PersistenceClassElement;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;


/**
 * Provides the JDO meta information based on a JDO meta model.
 */
//@olsen: new class
public class JDOMetaDataModelImpl extends Support
    implements JDOMetaData
{
    // misc
    protected final PrintWriter out;

    // model
    protected Model model;

    /**
     * Creates an instance.
     */
    public JDOMetaDataModelImpl(Model model) {
        this(model, null);
    }

    public JDOMetaDataModelImpl(Model model,
                                PrintWriter out)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        // check arguments
        if (model == null) {
            final String msg
                = "Initializing meta data: model == null";//NOI18N
            throw new JDOMetaDataFatalError(msg);
        }
        this.model = model;
        this.out = out;
    }

    /**
     * Tests whether a class is known to be transient.
     */
    public boolean isTransientClass(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //^olsen: delegate to Model once supported
        if (classPath.startsWith("java/"))//NOI18N
            return true;
        if (classPath.startsWith("javax/"))//NOI18N
            return true;
        if (classPath.startsWith("com/sun/jdo/"))//NOI18N
            return true;
        return false;
    }

    /**
     * Tests whether a class is known to be persistence-capable.
     */
    public boolean isPersistenceCapableClass(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //^olsen: delegate to Model once supported
        if (isTransientClass(classPath))
            return false;
        final String className = pathToName(classPath);
        return model.isPersistent(className);
    }

    /**
     * Tests whether a class is known as a persistence-capable root class.
     */
    public boolean isPersistenceCapableRootClass(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //@olsen: 4631388 - do not attempt to support inheritance right now
        final String className = pathToName(classPath);
        return model.isPersistent(className);
        //return (model.isPersistent(className)
        //        && !model.hasPersistentSuperclass(className));
    }

    /**
     * Returns the name of the persistence-capable root class of a class.
     */
    public String getPersistenceCapableRootClass(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //^olsen: exchange dummy implementation once supported by Model
        return (isPersistenceCapableClass(classPath) ? classPath : null);
    }

    /**
     * Returns the name of the superclass of a class.
     * <P>
     * @param classPath the JVM-qualified name of the class
     * @return the name of the superclass.
     */
    public String getSuperClass(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //throw new UnsupportedOperationException ("not implemented yet");
        return null;
    }

    /**
     * Tests whether a type is known for Second Class Objects.
     */
    public boolean isSecondClassObjectType(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        return model.isSecondClassObject(className);
    }

    /**
     * Tests whether a type is known for Mutable Second Class Objects.
     */
    public boolean isMutableSecondClassObjectType(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        return model.isMutableSecondClassObject(className);
    }

    /**
     * Tests whether a field of a class is known to be persistent.
     */
    public boolean isPersistentField(String classPath, String fieldName)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        return model.isPersistent(className, fieldName);
    }

    /**
     * Tests whether a field of a class is known to be transactional.
     */
    public boolean isTransactionalField(String classPath, String fieldName)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        //throw new UnsupportedOperationException ("not implemented yet");
        return false;
    }

    /**
     * Tests whether a field of a class is known to be Primary Key.
     */
    public boolean isPrimaryKeyField(String classPath, String fieldName)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        return model.isKey(className, fieldName);
    }

    /**
     * Tests whether a field of a class is known to be part of the
     * Default Fetch Group. Please note that for a relationship field, this
     * method always returns false.
     */
    public boolean isDefaultFetchGroupField(String classPath, String fieldName)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        boolean isdfgField = model.isDefaultFetchGroup(className, fieldName);
        if(isdfgField) {
            final PersistenceFieldElement pfe
                = model.getPersistenceField(className, fieldName);
            if (pfe instanceof RelationshipElement) {
                // This is a relationship field. Flag it as not belonging
                // to dfg.
                // Relationship fields are always flaged as not belonging to dfg
                // This assures that access to a relationship fields is always
                // mediated.
                // Please see call to this method from following for more details.
                // 1. EJBMetaDataModelImpl#getFieldFlags()
                // 2. MethodAnnotater#notePutFieldAnnotation()
                // 3. MethodAnnotater#noteGetFieldAnnotation()
                isdfgField = false;
            }
        }
        return isdfgField;
    }

    /**
     * Returns the unique field index of a declared, persistent field of a
     * class.
     */
    public int getFieldNo(String classPath, String fieldName)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        final PersistenceFieldElement pfe
            = model.getPersistenceField(className, fieldName);
        if (pfe == null
            || pfe.getPersistenceType() != PersistenceFieldElement.PERSISTENT)
            return -1;

        return pfe.getFieldNumber();
    }

    /**
     * Returns an array of field names of all declared, persistent fields
     * of a class.
     */
    private final String[] getPersistentFields(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        final String className = pathToName(classPath);
        final PersistenceClassElement pce
            = model.getPersistenceClass(className);
        if (pce == null)
            return new String[]{};

        // exctract field names into result array
        final PersistenceFieldElement[] pfes = pce.getFields();
        final int nofFields = (pfes != null  ?  pfes.length  :  0);
        final String[] names = new String[nofFields];
        for (int i = 0; i < nofFields; i++) {
            final PersistenceFieldElement pfe = pfes[i];
            names[i] = pfe.getName();

            //@olsen: debugging check
            if (false) {
                if (pfe.getPersistenceType()
                    != PersistenceFieldElement.PERSISTENT) {
                    final String msg
                        = ("Getting persistent field names: " //NOI18N
                           + "Encountered non-persistent field '"//NOI18N
                           + names[i] + "' for class " + classPath);//NOI18N
                    throw new JDOMetaDataFatalError(msg);
                    //out.println(msg);
                    //names[i] = null;
                }
            }
        }
        return names;
    }

    /**
     * Returns an array of field names of all declared persistent and
     * transactional fields of a class.
     */
    public String[] getManagedFields(String classPath)
        throws JDOMetaDataUserException, JDOMetaDataFatalError
    {
        return getPersistentFields(classPath);
    }

    static protected String pathToName(String classPath) {
        if (classPath != null) {
            return classPath.replace('/', '.');
        } else {
            return null;
        }
    }

    static protected String nameToPath(String className) {
        if (className != null) {
            return className.replace('.', '/');
        } else {
            return null;
        }
    }
}
