/*
 * 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): ______________________________________.
 *
 * --------------------------------------------------------------------------
 * $id$
 * --------------------------------------------------------------------------
 */

// JPP.java

package org.objectweb.jpp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.String;
import java.util.Enumeration;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;


/**
 * JPP as Java PreProcessor.
 * This class allows to preprocess a jpp file included the directives
 * <ul>
 * <li>
 * #include
 * <li>
 * #ifdef
 * <li>
 * #ifndef
 * <li>
 * #else
 * <li>
 * #endif
 * </ul>
 */

public class JPP {

    static final String INST_INCLUDE  = new String("#include");
    static final String INST_IFDEF    = new String("#ifdef");
    static final String INST_IFNDEF   = new String("#ifndef");
    static final String INST_ELSE     = new String("#else");
    static final String INST_ENDIF    = new String("#endif");

    ExpandedFile   jpp_fIn;
    BufferedWriter jpp_fOut;
    Vector         jpp_names;

    /**
     * JPP Constructor
     *
     * @param    fileNameIn     path of the jpp file
     * @param    dirOutputName  path of the directory where to place the generated file
     *                          (null if not defined)
     * @param    includeDirs    list of the 'include' directories
     * @param    definedNames   list of the defined names
     *
     */
    public JPP(String fileNameIn, String dirOutputName, Vector includeDirs, Vector definedNames) 
	throws java.io.IOException {

	String fileNameOut = null;
	if (dirOutputName == null) {
	    fileNameOut = new String("");
	} else {
	    fileNameOut = new String(dirOutputName + File.separatorChar);
	}
	String baseName = fileNameIn.substring(0, fileNameIn.length()-3);
	fileNameOut = fileNameOut.concat(baseName+"java");

	jpp_fIn  = new ExpandedFile(fileNameIn, includeDirs);
	jpp_fOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileNameOut)));
	jpp_names = definedNames;

    }


    public void preprocess() 
	throws java.io.IOException {

	Stack   stackDefined = new Stack();
	String  line = null;
	boolean toWrite = true;

	line = jpp_fIn.readLine();

	while (line != null)  {

	    boolean isAnInst = false;

	    if (line.startsWith(INST_IFDEF) || line.startsWith(INST_IFNDEF)) {
		isAnInst = true;
		String nom;
		StringTokenizer stLine = new StringTokenizer(line);
		nom = stLine.nextToken();
		nom = stLine.nextToken();
		int defined;
		if (toWrite) {
		    if ((line.startsWith(INST_IFDEF) && (jpp_names.indexOf(nom) != -1)) 
			|| (line.startsWith(INST_IFNDEF) && (jpp_names.indexOf(nom) == -1))) {
			    defined = 1;
		    } else {
			defined = -1;
			toWrite = false;
		    }
		} else {
		    defined = 0;
		}
		stackDefined.push(new Integer(defined));
	    } else if (line.startsWith(INST_ELSE)) {
		isAnInst = true;
		Integer p = (Integer)stackDefined.peek();
		if (p.intValue() != 0) {
		    toWrite = !toWrite;
		    stackDefined.pop();
		    stackDefined.push(new Integer(-1*p.intValue()));
		}
	    } else if (line.startsWith(INST_ENDIF)) {
		isAnInst = true;
		stackDefined.pop();
		if (stackDefined.empty()) {
		    toWrite = true;
		} else {
		    Integer p = (Integer)stackDefined.peek();
		    if (p.intValue() == 1) {
			toWrite = true;
		    } else {
			toWrite = false;
		    }
		}
	    }

	    if (toWrite && !isAnInst) {
		jpp_fOut.write(line);
		jpp_fOut.newLine();
	    }

	    line = jpp_fIn.readLine();
	}

	jpp_fIn.close();
	jpp_fOut.close();

    }

    String where() {
	return(new String(jpp_fIn.getFileName() + " file at line " + jpp_fIn.getLineNumber()));
    }


    public static void main(String args[]) 
	throws java.io.IOException {

	boolean  isHelp          = false;
	String   fileInputName   = null;
	String   dirOutputName   = null;
	Vector   definedNames    = new Vector();
	Vector   includeDirs     = new Vector();

	includeDirs.addElement(new String("."));

	// Get args
	for (int argn=0; argn<args.length; argn++) {
	    String arg = args[argn];
	    if (arg.equals("-help") || arg.equals("-?")) {
		isHelp = true;
		continue;
	    }
	    if ((argn<args.length) && arg.equals("-d")) {
		dirOutputName = args[argn+1];
		argn++;
		continue;
	    }
	    if ((argn<args.length) && (arg.equals("-D"))) {
		definedNames.addElement(args[argn+1]);
		argn++;
		continue;
	    }
	    if ((argn<args.length) && (arg.equals("-I"))) {
		includeDirs.addElement(args[argn+1]);
		argn++;
		continue;
	    }

	    fileInputName = args[argn];
	}

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

	// Check args
	if (fileInputName == null) {
	    usage();
	    System.exit(2);
	}
	if (! fileInputName.endsWith(".jpp")) {
	    System.err.println("Error: The input file name must ending with .jpp");
	    System.exit(2);
	}

	JPP jpp = null;
	try {
	    jpp = new JPP(fileInputName, dirOutputName, includeDirs, definedNames);
	} catch (java.lang.Exception e) {
	    e.printStackTrace();
	    System.exit(2);
	}
	try {
	    jpp.preprocess();
	} catch (java.lang.Exception e) {
	    System.err.println("Error on " + jpp.where());
	    e.printStackTrace();
	    System.exit(2);
	}

    }


    private static void usage() {
	System.err.println("Usage: java tools.JPP -help");
	System.err.println("");
	System.err.println("  or   java tools.JPP [-d <output_dir>] [-I <dir> ]* [-D <name>]* <Input_File>");
	System.err.println("");
    }


}


class ExpandedFile {

    Vector iDirs = null;
    Stack fStack = null;
    Stack nameStack = null;
    Stack nbStack  = null;
    BufferedReader fCur = null;
    String nameCur = null;
    int nbCur = -1;

    ExpandedFile (String fileName, Vector includeDirs)
	throws java.io.IOException {
	  fCur = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
	  iDirs = includeDirs;
	  fStack = new Stack();
	  nameStack = new Stack();
	  nbStack = new Stack();
	  nameCur = new String(fileName);
	  nbCur = 0;
    }

    String readLine()
	throws java.io.IOException {
	String line = null;

	line = fCur.readLine();
	nbCur++;

	if (line == null) {
	    if (!fStack.empty()) {
		fCur.close();
		fCur = (BufferedReader)fStack.pop();
		nameCur = (String)nameStack.pop();
		nbCur = ((Integer)nbStack.pop()).intValue();
		line = this.readLine();
	    }
	} else if (line.startsWith(JPP.INST_INCLUDE)) {
	    fStack.push(fCur);
	    nameStack.push(nameCur);
	    nbStack.push(new Integer(nbCur));
	    String fiName;
	    StringTokenizer stLine = new StringTokenizer(line);
	    fiName = stLine.nextToken();
	    fiName = stLine.nextToken();
	    fiName = this.includeFileName(fiName);
	    fCur = new BufferedReader(new InputStreamReader(new FileInputStream(fiName)));
	    nameCur = new String(fiName);
	    nbCur = 0;
	    line = this.readLine();
	}

	return(line);
    }

    private String includeFileName(String fName) {

	String fiName = null;
	Enumeration dirs = iDirs.elements();
	boolean found = false;
	while (dirs.hasMoreElements() && !found) {
	    fiName = new String((String)dirs.nextElement()+File.separatorChar+fName);
	    File f = new File(fiName);
	    if (f.exists()) {
		found = true;
	    }
	}
	
	if (!found) {
	    fiName = fName;
	}

	return(fiName);
    }

    String getFileName() {
	return(nameCur);
    }

    int getLineNumber() {
	return(nbCur);
    }

    void close()
	throws java.io.IOException {
	fCur.close();
    }

}

