/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 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
 *
 * Initial Developer : Delplanque Xavier & Sauthier Guillaume
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.ws.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.jar.JarFile;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.digester.JDigester;
import org.ow2.jonas.deployment.ws.JaxrpcMappingSchemas;
import org.ow2.jonas.deployment.ws.MappingFile;
import org.ow2.jonas.deployment.ws.WSDeploymentDescException;
import org.ow2.jonas.deployment.ws.rules.JavaWsdlMappingRuleSet;
import org.ow2.jonas.deployment.ws.xml.JavaWsdlMapping;
import org.ow2.jonas.lib.util.Log;




import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;


/**
 * this Class is used to manipulate jaxrpc-mapping-file. This file contains
 * informations for mapping between XML namespaces and java packages. We
 * actually support just a few part of this file. According with JSR 921, this
 * file must contain class mapping information (exceptions/faults,
 * types/classes, portTypes/interfaces ...).
 *
 * @author Guillaume Sauthier
 * @author Xavier Delplanque
 * @author Helene Joanin
 */
public class MappingFileManager {

    /**
     * Digester used to parse jaxrpc_mapping.xml
     */
    private static JDigester mfDigester = null;

    /**
     * Rules to parse the jaxrpc_mapping.xml
     */
    private static JavaWsdlMappingRuleSet mfRuleSet = new JavaWsdlMappingRuleSet();

    /**
     * Flag for parser validation
     */
    private static boolean parsingWithValidation = true;

    /**
     * logger
     */
    private static Logger logger = Log.getLogger("org.ow2.jonas.ws");

    /**
     * Private empty constructor for Utility class.
     */
    private MappingFileManager() { }

    /**
     * Returns an instance of jaxrpc-mapping-file
     *
     * @param module module File containing mapping
     * @param filename jaxrpc-mapping-file filename
     *
     * @return Returns an instance of jaxrpc-mapping-file
     *
     * @throws WSDeploymentDescException when MappingFile cannot be loader or instanciated
     */
    public static MappingFile getInstance(File module, String filename)
        throws WSDeploymentDescException {
        InputStream is = openStream(module, filename);
        return new MappingFile(loadMappingFile(new InputStreamReader(is), filename));
    }

    /**
     * Returns an instance of jaxrpc-mapping-file
     *
     * @param r mapping file reader
     * @param filename mapping filename
     * @param validate validate flag
     *
     * @return Returns an instance of jaxrpc-mapping-file
     *
     * @throws WSDeploymentDescException when MappingFile cannot be loader or instanciated
     */
    public static MappingFile getInstance(Reader r, String filename, boolean validate)
        throws WSDeploymentDescException {
        parsingWithValidation = validate;
        return new MappingFile(loadMappingFile(r, filename));
    }
    /**
     * Returns an instance of jaxrpc-mapping-file
     *
     * @param is jaxrpc-mapping-file InputStream
     * @param filename jaxrpc-mapping-file filename
     *
     * @return Returns an instance of jaxrpc-mapping-file
     *
     * @throws WSDeploymentDescException when MappingFile cannot be loader or instanciated
     */
    public static MappingFile getInstance(InputStream is, String filename)
        throws WSDeploymentDescException {
        return new MappingFile(loadMappingFile(new InputStreamReader(is), filename));
    }


    /**
     * Open the InputStream of the given jaxrpc filename inside module.
     *
     * @param module module filename containing jaxrpc mapping file
     * @param filename jaxrpc mappinf file filename
     *
     * @return Returns the InputStream of the mapping file
     *
     * @throws WSDeploymentDescException When InputStream cannot be open (not found, ioexceptions)
     */
    private static InputStream openStream(File module, String filename)
        throws WSDeploymentDescException {

        InputStream isMapping = null;

        // If we have file stored in Jar
        if (module.exists()) {
            // If we have file stored in Jar
            if (module.isFile()) {
                JarFile jf = null;

                // Get the Mapping file of the jar as InputStream
                try {
                    jf = new JarFile(module.getAbsolutePath());
                    isMapping = jf.getInputStream(jf.getEntry(filename));
                } catch (Exception e) {
                    if (jf != null) {
                        try {
                            jf.close();
                        } catch (IOException i) {
                            logger.log(BasicLevel.WARN, "Can't close '" + module + "'");
                        }
                    }

                    throw new WSDeploymentDescException("Cannot read the jaxrpc-mapping-file "
                                                        + filename + " in " +  module.getAbsolutePath(),
                                                        e);
                }
            } else {
                // filename is a Directory
                try {
                    isMapping =
                        new FileInputStream(new File(module, filename));
                } catch (IOException ioe) {
                    throw new WSDeploymentDescException("Cannot read the jaxrpc-mapping-file "
                                                        + filename + " in " + module.getAbsolutePath(),
                                                        ioe);
                }
            }

        } else {
            // try laoding from ClassLoader
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            isMapping = loader.getResourceAsStream(filename);
        }

        return isMapping;
    }

    /**
     * Load the MappingFile file
     *
     * @param reader The mapping file InputStream
     * @param fileName mapping filename
     *
     * @return Zeus Root Element of the xml file.
     *
     * @throws WSDeploymentDescException When parsing fails.
     */
    private static JavaWsdlMapping loadMappingFile(Reader reader, String fileName)
        throws WSDeploymentDescException {

        JavaWsdlMapping jwm = new JavaWsdlMapping();

        // Create if mfDigester is null
        if (mfDigester == null) {
            try {
                // Create and initialize the digester
                mfDigester = new JDigester(mfRuleSet,
                                           parsingWithValidation,
                                           true,
                                           null,
                                           new JaxrpcMappingSchemas());
            } catch (DeploymentDescException  e) {
                throw new WSDeploymentDescException(e);
            }
        }

        try {
            mfDigester.parse(reader, fileName, jwm);
        } catch (DeploymentDescException  e) {
            throw new WSDeploymentDescException(e);
        } finally {
            mfDigester.push(null);
        }

        return jwm;
    }

    /**
     * @return Returns the parsingWithValidation.
     */
    public static boolean isParsingWithValidation() {
        return parsingWithValidation;
    }

    /**
     * @param parsingWithValidation The parsingWithValidation to set.
     */
    public static void setParsingWithValidation(boolean parsingWithValidation) {
        MappingFileManager.parsingWithValidation = parsingWithValidation;
    }
}
