package antlr.build;

import antlr.Utils;

import java.io.*;
import java.lang.reflect.*;

/** An application-specific build tool for ANTLR.  Old build
 *  was a shell script that of course is not portable.  All it
 *  did though was to compile; this Java program is a portable
 *  compile script. :)
 *
 *  This class knows how to portable invoke a system command with
 *  wildcard expansion.  It also knows to how dynamically load a project
 *  description based upon the name.  Actions are just method names
 *  within that object.  The ANTLR object defines build and jar methods
 *  that use Tool to portably compile and jar--it is the replacement
 *  the script.
 *
 *  I almost used Python for this, but I hate forcing users to download
 *  yet another tool just to compile this one.  (Another reason not
 *  to use Ant).
 *
 *  I am using a class to describe the build (e.g., ANTLR) rather than
 *  a file format at the moment since I am not sure how all this will
 *  progress.
 *
 *  Properties antlr.build.compiler and antlr.build.root may be set
 *  according to your needs.  They default to "javac" and ".".  The
 *  root is the dir containing the antlr directory associated with
 *  the antlr Java package.
 *
 *  To build this builder, run "javac *.java" in this dir.
 *
 *  To build ANTLR, just run
 *
 *    java antlr.build.Tool build"
 *
 *  from the
 *  directory containing package antlr's "antlr" dir.  Or, use
 *  something like:
 *
 *  java -Dantlr.build.root=/usr/local/antlr-2.7.2 antlr.build.Tool build
 *
 *  To make the antlr.jar file, use
 *
 *  java antlr.build.Tool jar
 */
public class Tool {
    public String os = null;

    public Tool() {
        os = System.getProperty("os.name");
    }

    public static void main(String[] args) {
        if ( args.length!=1 ) {
            System.err.println("usage: java antlr.build.Tool action");
            return;
        }
        String app = "antlr.build.ANTLR"; // hardcode to build ANTLR app
        String action = args[0];
        Tool t = new Tool();
        t.perform(app, action);
    }

    /** Find a class named after the app and then find a method within
     *  that is named after the action.
     */
    public void perform(String app, String action) {
        if ( app==null || action==null ) {
            error("missing app or action");
            return;
        }
        Class c = null;
        Method m = null;
        Object appObj = null;
        try {
            appObj = Utils.createInstanceOf(app);
        }
        catch (Exception e) {
            // try again with antlr.build.app
            try {
                if ( !app.startsWith("antlr.build.") ) {
                    c = Utils.loadClass("antlr.build."+app);
                }
                error("no such application "+app, e);
            }
            catch (Exception e2) {
                error("no such application "+app, e2);
            }
        }
        if ( c==null || appObj==null ) {
            return;
        }
        try {
            m = c.getMethod(action, new Class[] {antlr.build.Tool.class});
            //log("begin "+action+" on "+app);
            m.invoke(appObj, new Object[] {this});
            //log("end "+action+" on "+app);
        }
        catch (Exception e) {
            error("no such action for application "+app, e);
        }
    }

    /** A portable system command execution routine that logs stdout/stderr
     *  and handles cmd-line wildcards properly.
     */ 
    public void system(String cmd) {
        Runtime rt = Runtime.getRuntime();
        try {
            log(cmd);
            Process proc = null;
            if ( !os.startsWith("Windows") ) {
                // assume unixen if not windoze (windoze tool expands *)
                proc = rt.exec(new String[] {"sh","-c",cmd});
            }
            else {
                proc = rt.exec(cmd);
            }
            StreamScarfer stderrScarfer =
                    new StreamScarfer(proc.getErrorStream(), "stderr", this);
            StreamScarfer stdoutScarfer =
                    new StreamScarfer(proc.getInputStream(), "stdout", this);
            stderrScarfer.start();
            stdoutScarfer.start();
            int exitVal = proc.waitFor();
        }
        catch (Exception e) {
            error("cannot exec "+cmd, e);
        }
    }

    /** Run ANTLR on a grammar file, leaving the output in the directory of the
     *  grammar file.
     */
    public void antlr(String fullyQualifiedFilename) {
        String path = null;
        try {
            path = new File(fullyQualifiedFilename).getParent();
            if ( path!=null ) {
                path = new File(path).getCanonicalPath();
            }
        }
        catch (IOException ioe) {
            error("Invalid grammar file: "+fullyQualifiedFilename);
        }
        if ( path!=null ) {
            log("java antlr.Tool -o "+path+" "+fullyQualifiedFilename);
            antlr.Tool theTool = new antlr.Tool();
            theTool.doEverything(new String[] {"-o", path, fullyQualifiedFilename});
        }
    }

    /** Stdout from executing sub tools */
    public void stdout(String s) {
        System.out.println(s);
    }

    /** Stderr from executing sub tools */
    public void stderr(String s) {
        System.err.println(s);
    }

    public void error(String msg) {
        System.err.println("antlr.build.Tool: "+msg);
    }

    public void log(String cmd) {
        System.out.println("executing: "+cmd);
    }

    public void error(String msg, Exception e) {
        System.err.println("antlr.build.Tool: "+msg);
        e.printStackTrace(System.err);
    }
}
