/*
 * 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): Helene Joanin (jonas-teams).
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */



package org.objectweb.jonas_ejb.genidl;

import java.io.File;
import java.net.URLClassLoader;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

import org.objectweb.common.Cmd;
import org.objectweb.common.Env;
import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
import org.objectweb.jonas_ejb.deployment.api.DeploymentDesc;
import org.objectweb.jonas_ejb.deployment.api.MessageDrivenDesc;
import org.objectweb.jonas_ejb.deployment.lib.EjbDeploymentDescManager;
import org.objectweb.jonas_ejb.lib.BeanNaming;
import org.objectweb.jonas_lib.version.Version;
import org.objectweb.jonas_lib.deployment.api.DeploymentDescException;


/**
 *  This class allows to generate:
 * <ul>
 * <li>
 *  the IDLs of the Home and Remote interfaces
 * </ul>
 * of all the Enterprise Java Beans defined in the given Deployment Descriptor.
 */

public class GenIDL {

    private static final int EXIT_SUCCESS = 0;
    private static final int EXIT_FAILURE = 2;

    // Is the command is verbose ?
    private boolean verbose = false;
    
    // Name of the directory where to place the generated file
    private String directoryOutputName = null;

    // A BeanDesc for each bean for which IDL have to be generated
    private Vector vBeanDD = null;

    // Names of the remote classes (package name included)
    private Vector remoteClasses = null;


    /**
     * GenIDL allows to generate the IDLs of the Home and Remote interfaces of the
     * beans described in the given EJB deployment descriptor.
     * <p>
     *
     * Usage: java org.objectweb.jonas_ejb.genidl.GenIDL -help
     * <br>
     *         to print this help message
     * <p>
     *
     * or    java org.objectweb.jonas_ejb.genidl.GenIDL <Options> <Input_File>
     * <br>
     *         to generate the IDLs of the Home and Remote interfaces of the
     *         beans described in the given EJB deployment descriptor.
     * <p>
     *           Options include:
     * <ul>
     * <li>
     *             -d <output_dir>  specify where to place the generated files
     * <li>
     *             -novalidation    parse the XML deployment descriptors without validation
     * <li>
     *             -rmicopts <opt>  specify the options to pass to the rmic compiler
     * <li>
     *             -verbose
     * <li>
     * </ul>
     * <p>
     *           Input_File         file name of the standard deployment descriptor
     *                              (.xml ended), or
     *                              file name of the EJB-jar (.jar ended).
     */

    public static void main(String args[]) {

	// First of all, check if the used JDK is at least an JDK 1.3
	// Indeed, 'rmic -idl' only available from JDK 1.3
	if (Env.getJavaVersion() < Env.JAVA_1_3) {
	    GenIDL.fatalError("'rmic -idl' not available on a JDK version less than 1.3.");
	}

	boolean  error = false;

	boolean  isHelp          = false;
	boolean  isVerbose       = false; 
	boolean  parseWithValidation = true;

	String   fileInputName = null;
	String   dirOutputName = null;
	Vector   optionsRmiC   = new Vector();

	// Get args
	for (int argn=0; argn<args.length; argn++) {
	    String arg = args[argn];
	    if (arg.equals("-help") || arg.equals("-?")) {
		isHelp = true;
		continue;
	    }
	    if (arg.equals("-novalidation")) {
		parseWithValidation = false;
		continue;
	    }
	    if (arg.equals("-verbose")) {
		isVerbose = true;
		continue;
	    }
	    if (arg.equals("-rmicopts")) {
		argn++;
		if (argn<args.length) {
		    StringTokenizer st = new StringTokenizer(args[argn]);
		    while (st.hasMoreTokens()) {
			optionsRmiC.addElement(st.nextToken());
		    }
		} else {
		    error = true;
		}
		continue;
	    }
	    if (arg.equals("-d")) {
		argn++;
		if (argn<args.length) {
		    dirOutputName = args[argn];
		} else {
		    error = true;
		}
		continue;
	    }
	    fileInputName = args[argn];
	}

	// Usage ?
	if (isHelp) {
	    usage();
	    System.exit(EXIT_SUCCESS);
	}

	// Check args
	if (error || (fileInputName == null)) {
	    usage();
	    System.exit(EXIT_FAILURE);
	}
	if (dirOutputName == null) {
	    dirOutputName = new String("");
	}

	// Calculate the classpath for rmic
	String classpath;
	if (fileInputName.endsWith(".jar")) {
	    classpath = fileInputName +
		File.pathSeparator +
		System.getProperty("java.class.path", "");
	} else {
	    classpath = System.getProperty("java.class.path", "");
	}
	optionsRmiC.addElement("-classpath");
	optionsRmiC.addElement(classpath);

	// Set the parsing mode (with or without validation)
	if (!parseWithValidation) {
	    EjbDeploymentDescManager.setParsingWithValidation(false);
	}

	// Generates the IDL for the set of the beans
	try {
	    DeploymentDesc ejbJarDD = null;
	    if (fileInputName.endsWith(".jar")) {
		// ejb-jar file
		URL url[] = new URL[1];
		url[0] = (new File(fileInputName)).toURL();
		URLClassLoader cl = new URLClassLoader(url);
		ejbJarDD = EjbDeploymentDescManager.getDeploymentDesc(fileInputName, cl);
	    } else {
		// xml file
		ejbJarDD = EjbDeploymentDescManager.getDeploymentDesc(fileInputName,
								      BeanNaming.getJonasXmlName(fileInputName),
								      BeanNaming.getParentName(fileInputName));
	    }

	    GenIDL gwc = new GenIDL(ejbJarDD, dirOutputName, isVerbose);
	    gwc.generate(optionsRmiC);

	} catch (MalformedURLException e) {
	    GenIDL.fatalError("Invalid ejb-jar file name (" + e.getMessage() + ")");
	} catch (GenIDLException e) {
	    GenIDL.fatalError(e.getMessage());
	} catch (DeploymentDescException e) {
	    GenIDL.fatalError("Cannot read the Deployment Descriptors from "
			      +fileInputName+": "+e.getMessage());
	}
	// End
    }

    /**
     * GenIDL Constructor
     *
     * @param     ejbJarDescr    deployment descriptors of the beans
     * @param     dirOutputName  path of the directory where to place the generated files
     *                           (empty string "" if the output directory is the current directory)
     * @param     isVerbose      if true, some traces are print
     * @exception GenIDLException In error case
     */
    public GenIDL(DeploymentDesc ejbJarDesc, String dirOutputName, boolean isVerbose)
	throws GenIDLException {
	
	verbose = isVerbose;
	directoryOutputName = dirOutputName;
	remoteClasses = new Vector();

	vBeanDD = new Vector();
	BeanDesc[] beansDD = ejbJarDesc.getBeanDesc();
	for (int i=0; i<beansDD.length; i++) {
	    if (!(beansDD[i] instanceof MessageDrivenDesc)) {
		// Nothing to generate in case of MessageDriven Bean
		vBeanDD.addElement(beansDD[i]);
	    }
	}

	// Display the bean's names
	StringBuffer message = new StringBuffer();
	message.append("GenIDL for JOnAS "+Version.getNumber()+": ");
	if (vBeanDD.size()!=0) {
	    message.append("IDL generation for beans ");
	    for (int i=0; i < vBeanDD.size(); i++) {
		BeanDesc dd = (BeanDesc)vBeanDD.elementAt(i);
		message.append((i==0?"":", ") + "'" + dd.getEjbName() + "'");
	    }
	    message.append(" ...");
	} else {
	    message.append("No generation to do (only message driven beans)");
	}
	GenIDL.info(message.toString());

	// Init the list of the remote classes
	for (Enumeration e = vBeanDD.elements() ; e.hasMoreElements() ;) {
	    BeanDesc dd = (BeanDesc)e.nextElement();
	    if (dd.getHomeClass() != null) {
		remoteClasses.addElement(dd.getHomeClass().getName());
	    }
	    if (dd.getRemoteClass() != null) {
		remoteClasses.addElement(dd.getRemoteClass().getName());
	    }
	}
    }

    /**
     * Generate the IDLs for all Beans.
     *
     * @param     optionsRmiC    options for the rmic tool
     * @exception GenIDLException In error case
     */
    public void generate(Vector optionsRmiC)
	throws GenIDLException {
	
	String optDirOutput;
	Cmd cmd;

	if (vBeanDD.size()==0) {
	    return;
	}

	// Generate the IDLs by the way of the rmic -idl command
	cmd = new Cmd("rmic");
	cmd.addArgument("-idl");
	cmd.addArgument("-always");
	if (directoryOutputName.length() != 0) {
	    cmd.addArgument("-d");
	    cmd.addArgument(directoryOutputName);
	}
	cmd.addArguments(optionsRmiC);
	for (Enumeration e = remoteClasses.elements() ; e.hasMoreElements() ;) {
	    String className = (String)e.nextElement();
	    cmd.addArgument(className);
	}
	
	trace("Running '" + cmd.toString() + "'");
	if (cmd.run()) {
	    trace("The IDL of the Home and Remote interfaces of the beans"
		  +" are successfully generated via rmic -idl.");
	} else {
	    throw new GenIDLException("Failed when generating the IDL of the Home and Remote interfaces of the beans via rmic -idl.");
	}

    }

    /**
     * Display the usage
     */
    private static void usage() {
	StringBuffer msg = new StringBuffer();
	msg.append("Usage: java org.objectweb.jonas_ejb.genidl.GenIDL -help \n");
	msg.append("         to print this help message \n");
	msg.append(" or    java org.objectweb.jonas_ejb.genidl.GenIDL <Options> <Input_File> \n");
	msg.append("         to generate the IDLs for given EJB(s). \n");
	msg.append(" \n");
	msg.append("Options include: \n");
        msg.append("       -d <output_dir>  specify where to place the generated files \n");
	msg.append("       -novalidation    parse the XML deployment descriptors without \n");
	msg.append("                        validation \n");      
	msg.append("       -rmicopts  <opt> specify the options to pass to the rmi compiler \n");
	msg.append("       -verbose \n");
	msg.append(" \n");
	msg.append("Input_File              standard deployment descriptor file's name or \n");
	msg.append("                        ejb-jar file's name \n");
	GenIDL.info(msg.toString());
    }

    /**
     * Display the specified message only if verbose.
     * @param msg the message to display
     */
    void trace(String msg) {
	if (verbose) {
	    info("-- "+msg);
	}
    }

    /**
     * Display the specified message.
     * @param msg the message to display
     */
    static void info(String msg) {
        System.out.println(msg);
    }

    /**
     * Display the specified error message.
     * @param errMsg the error message to display
     */
    static void error(String errMsg) {
        System.err.println("GenIDL error: " + errMsg);
    }


    /**
     * Display the specified error message and exits with an
     * EXIT_FAILURE status.
     * @param errMsg the error message to display
     */
    static void fatalError(String errMsg) {
	System.err.println("GenIDL fatal error: " + errMsg);
	System.exit(EXIT_FAILURE);
    }
}

