/**
 * java_cup.anttask.CUPTask.java
 *
 * @author Michael Petter, 2003
 *
 * Ant-Task for CUP Parser Generator for Java
 * -- tested with Ant 1.5.1;
 * -- compiles with javac -classpath .:${ANT_HOME}/lib/ant.jar java_cup.anttask.CUPTask.java
 * -- overrides org.apache.tools.ant.taskdefs.Java
 * -- providing cool interface to CUP
 * -- mapping all existing parameters to attributes
 * -- trys to add new useful features to CUP, like 
 *     - automatic package discovery
 *     - re-generate .java only when necessary
 *     - possibility to generate into a dest-directory
 *
 * my code is not perfect (in some cases it is pretty
 * ugly :-) ), but i didn't encounter any major error
 * until now
 */

package java_cup.anttask;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;

import java.util.List;
import java.util.LinkedList;
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

import java_cup.version;

public class CUPTask extends Task {
    private String srcfile = null;
    private String parser = null;
    private String _package = null;
    private String symbols = null;
    private String destdir = null;
    private boolean _interface = false;
    private boolean nonterms = false;
    private String expect = null;
    private boolean compact_red = false;
    private boolean nowarn = false;
    private boolean nosummary = false;
    private boolean progress = false;
    private boolean dump_grammar = false;
    private boolean dump_states = false;
    private boolean dump_tables = false;
    private boolean dump = false;
    private boolean time = false;
    private boolean debug = false;
    private boolean debugsymbols = false;
    private boolean nopositions = false;
    private boolean xmlactions = false;
    private boolean genericlabels = false;
    private boolean locations = true;
    private boolean noscanner = false;
    private boolean force = false;
    private boolean quiet = false;

    /**
     * executes the task parses all attributes and validates options...
     *
     */

    public void execute() throws BuildException {
        List<String> sc = new LinkedList<>(); // sc = simulated commandline
        // here, we parse our elements
        if (parser != null) {
            sc.add("-parser");
            sc.add(parser);
        } else
            parser = "parser"; // set the default name to check actuality
        if (_package != null) {
            sc.add("-package");
            sc.add(_package);
        }
        if (symbols != null) {
            sc.add("-symbols");
            sc.add(symbols);
        } else
            symbols = "sym";
        if (expect != null) {
            sc.add("-expect");
            sc.add(expect);
        }
        if (_interface) {
            sc.add("-interface");
        }
        if (nonterms) {
            sc.add("-nonterms");
        }
        if (compact_red) {
            sc.add("-compact_red");
        }
        if (nowarn) {
            sc.add("-nowarn");
        }
        if (nosummary) {
            sc.add("-nosummary");
        }
        if (progress) {
            sc.add("-progress");
        }
        if (dump_grammar) {
            sc.add("-dump_grammar");
        }
        if (dump_states) {
            sc.add("-dump_states");
        }
        if (dump_tables) {
            sc.add("-dump_tables");
        }
        if (dump) {
            sc.add("-dump");
        }
        if (time) {
            sc.add("-time");
        }
        if (debug) {
            sc.add("-debug");
        }
        if (debugsymbols) {
            sc.add("-debugsymbols");
        }
        if (nopositions) {
            sc.add("-nopositions");
        }
        if (locations) {
            sc.add("-locations");
        }
        if (genericlabels) {
            sc.add("-genericlabels");
        }
        if (xmlactions) {
            sc.add("-xmlactions");
        }
        if (noscanner) {
            sc.add("-noscanner");
        }
        if (!quiet)
            log("This is " + version.title_str);
        if (!quiet)
            log("Authors : " + version.author_str);
        if (!quiet)
            log("Bugreports to petter@cs.tum.edu");

        // look for package name and add to destdir
        String packagename = inspect(srcfile);

        // now, that's sweet:
        if (destdir == null) {
            destdir = System.getProperty("user.dir");
            if (!quiet)
                log("No destination directory specified; using working directory: " + destdir);
        }
        File dest = new File(destdir + packagename);
        if (!(dest).exists()) {
            if (!quiet)
                log("Destination directory didn't exist; creating new one: " + destdir + packagename);
            dest.mkdirs();
            force = true;
        } else {
            if (force && !quiet) {
                log("anyway, this generation will be processed because of option force set to \"true\"");
            } else {
                if (!quiet)
                    log("checking, whether this run is necessary");
            }
            // let's check, whether there exists any Parser fragment...
            File parserfile = new File(destdir + packagename, parser + ".java");
            File symfile = new File(destdir + packagename, symbols + ".java");
            File cupfile = new File(srcfile);

            if (!parserfile.exists() || !symfile.exists()) {
                if (!quiet)
                    log("Either Parserfile or Symbolfile didn't exist");
                force = true;
            } else {
                if (!quiet)
                    log("Parserfile and symbolfile are existing");
            }

            if (parserfile.lastModified() <= cupfile.lastModified()) {
                if (!quiet)
                    log("Parserfile " + parserfile + " isn't actual");
                force = true;
            } else {
                if (!quiet)
                    log("Parserfile " + parserfile + " is actual");
            }

            if (symfile.lastModified() <= cupfile.lastModified()) {
                if (!quiet)
                    log("Symbolfile " + symfile + " isn't actual");
                force = true;
            } else {
                if (!quiet)
                    log("Symbolfile" + symfile + " is actual");
            }

            if (!force) {
                if (!quiet)
                    log("skipping generation of " + srcfile);
                if (!quiet)
                    log("use option force=\"true\" to override");
                return;
            }
        }

        sc.add("-destdir");
        sc.add(dest.getAbsolutePath());

        // also catch the not existing input file
        if (srcfile == null)
            throw new BuildException("Input file needed: Specify <cup srcfile=\"myfile.cup\"> ");
        if (!(new File(srcfile)).exists())
            throw new BuildException("Input file not found: srcfile=\"" + srcfile + "\" ");

        sc.add(srcfile);
        String[] args = new String[sc.size()];
        for (int i = 0; i < args.length; i++)
            args[i] = sc.get(i);

        try {
            java_cup.Main.main(args);
        } catch (Exception e) {
            log("CUP error occured int CUP task: " + e);
        }

    }

    /**
     * Let's search for package name
     *
     * @param cupfile where we have to search for the package name
     *
     * @return the package folder structure
     */
    protected String inspect(String cupfile) {
        try {
            BufferedReader br = new BufferedReader(new FileReader(cupfile));
            while (br.ready()) {
                String line = br.readLine();
                if ((line.startsWith("package")) && (line.indexOf(";") != -1)) {
                    String result = line.substring(8, line.indexOf(";"));
                    result = result.replace('.', System.getProperty("file.separator").charAt(0));
                    br.close();
                    return System.getProperty("file.separator") + result;
                }

            }
            br.close();
        } catch (IOException ioe) {
        }
        return "";
    }

    /**
     * Gets the value of quiet
     *
     * @return the value of quiet
     */
    public boolean getQuiet() {
        return this.quiet;
    }

    /**
     * Sets the value of quiet
     *
     * @param arg_quiet Value to assign to this.quiet
     */
    public void setQuiet(boolean argquiet) {
        this.quiet = argquiet;
    }

    /**
     * Gets the value of force
     *
     * @return the value of force
     */
    public boolean getForce() {
        return this.force;
    }

    /**
     * Sets the value of force
     *
     * @param arg_package Value to assign to this.force
     */
    public void setForce(boolean argforce) {
        this.force = argforce;
    }

    /**
     * Gets the value of _package
     *
     * @return the value of _package
     */
    public String getPackage() {
        return this._package;
    }

    /**
     * Sets the value of _package
     *
     * @param arg_package Value to assign to this._package
     */
    public void setPackage(String arg_package) {
        this._package = arg_package;
    }

    /**
     * Gets the value of destdir
     *
     * @return the value of destdir
     */
    public String getDestdir() {
        return this.destdir;
    }

    /**
     * Sets the value of destdir
     *
     * @param arg_package Value to assign to this.destdir
     */
    public void setDestdir(String destdir) {
        this.destdir = destdir;
    }

    /**
     * Gets the value of _interface
     *
     * @return the value of _interface
     */
    public boolean isInterface() {
        return this._interface;
    }

    /**
     * Sets the value of _interface
     *
     * @param arg_interface Value to assign to this._interface
     */
    public void setInterface(boolean arg_interface) {
        this._interface = arg_interface;
    }

    /**
     * Get the Srcfile value.
     * 
     * @return the Srcfile value.
     */
    public String getSrcfile() {
        return srcfile;
    }

    /**
     * Set the Srcfile value.
     * 
     * @param newSrcfile The new Srcfile value.
     */
    public void setSrcfile(String newSrcfile) {
        this.srcfile = newSrcfile;
    }

    /**
     * Gets the value of parser
     *
     * @return the value of parser
     */
    public String getParser() {
        return this.parser;
    }

    /**
     * Sets the value of parser
     *
     * @param argParser Value to assign to this.parser
     */
    public void setParser(String argParser) {
        this.parser = argParser;
    }

    /**
     * Gets the value of symbols
     *
     * @return the value of symbols
     */
    public String getSymbols() {
        return this.symbols;
    }

    /**
     * Sets the value of symbols
     *
     * @param argSymbols Value to assign to this.symbols
     */
    public void setSymbols(String argSymbols) {
        this.symbols = argSymbols;
    }

    /**
     * Gets the value of nonterms
     *
     * @return the value of nonterms
     */
    public boolean isNonterms() {
        return this.nonterms;
    }

    /**
     * Sets the value of nonterms
     *
     * @param argNonterms Value to assign to this.nonterms
     */
    public void setNonterms(boolean argNonterms) {
        this.nonterms = argNonterms;
    }

    /**
     * Gets the value of expect
     *
     * @return the value of expect
     */
    public String getExpect() {
        return this.expect;
    }

    /**
     * Sets the value of expect
     *
     * @param argExpect Value to assign to this.expect
     */
    public void setExpect(String argExpect) {
        this.expect = argExpect;
    }

    /**
     * Gets the value of compact_red
     *
     * @return the value of compact_red
     */
    public boolean isCompact_red() {
        return this.compact_red;
    }

    /**
     * Sets the value of compact_red
     *
     * @param argCompact_red Value to assign to this.compact_red
     */
    public void setCompact_red(boolean argCompact_red) {
        this.compact_red = argCompact_red;
    }

    /**
     * Gets the value of nowarn
     *
     * @return the value of nowarn
     */
    public boolean isNowarn() {
        return this.nowarn;
    }

    /**
     * Sets the value of nowarn
     *
     * @param argNowarn Value to assign to this.nowarn
     */
    public void setNowarn(boolean argNowarn) {
        this.nowarn = argNowarn;
    }

    /**
     * Gets the value of nosummary
     *
     * @return the value of nosummary
     */
    public boolean isNosummary() {
        return this.nosummary;
    }

    /**
     * Sets the value of nosummary
     *
     * @param argNosummary Value to assign to this.nosummary
     */
    public void setNosummary(boolean argNosummary) {
        this.nosummary = argNosummary;
    }

    /**
     * Gets the value of progress
     *
     * @return the value of progress
     */
    public boolean isProgress() {
        return this.progress;
    }

    /**
     * Sets the value of progress
     *
     * @param argProgress Value to assign to this.progress
     */
    public void setProgress(boolean argProgress) {
        this.progress = argProgress;
    }

    /**
     * Gets the value of dump_grammar
     *
     * @return the value of dump_grammar
     */
    public boolean isDump_grammar() {
        return this.dump_grammar;
    }

    /**
     * Sets the value of dump_grammar
     *
     * @param argDump_grammar Value to assign to this.dump_grammar
     */
    public void setDump_grammar(boolean argDump_grammar) {
        this.dump_grammar = argDump_grammar;
    }

    /**
     * Gets the value of dump_states
     *
     * @return the value of dump_states
     */
    public boolean isDump_states() {
        return this.dump_states;
    }

    /**
     * Sets the value of dump_states
     *
     * @param argDump_states Value to assign to this.dump_states
     */
    public void setDump_states(boolean argDump_states) {
        this.dump_states = argDump_states;
    }

    /**
     * Gets the value of dump_tables
     *
     * @return the value of dump_tables
     */
    public boolean isDump_tables() {
        return this.dump_tables;
    }

    /**
     * Sets the value of dump_tables
     *
     * @param argDump_tables Value to assign to this.dump_tables
     */
    public void setDump_tables(boolean argDump_tables) {
        this.dump_tables = argDump_tables;
    }

    /**
     * Gets the value of dump
     *
     * @return the value of dump
     */
    public boolean isDump() {
        return this.dump;
    }

    /**
     * Sets the value of dump
     *
     * @param argDump Value to assign to this.dump
     */
    public void setDump(boolean argDump) {
        this.dump = argDump;
    }

    /**
     * Gets the value of time
     *
     * @return the value of time
     */
    public boolean isTime() {
        return this.time;
    }

    /**
     * Sets the value of time
     *
     * @param argTime Value to assign to this.time
     */
    public void setTime(boolean argTime) {
        this.time = argTime;
    }

    /**
     * Gets the value of debug
     *
     * @return the value of debug
     */
    public boolean isDebug() {
        return this.debug;
    }

    /**
     * Sets the value of debug
     *
     * @param argDebug Value to assign to this.debug
     */
    public void setDebug(boolean argDebug) {
        this.debug = argDebug;
    }

    /**
     * Gets the value of debug
     *
     * @return the value of debug
     */
    public boolean isDebugSymbols() {
        return this.debugsymbols;
    }

    /**
     * Sets the value of debug
     *
     * @param argDebug Value to assign to this.debug
     */
    public void setDebugSymbols(boolean argDebug) {
        this.debugsymbols = argDebug;
    }

    /**
     * Gets the value of nopositions
     *
     * @return the value of nopositions
     */
    public boolean isNopositions() {
        return this.nopositions;
    }

    /**
     * Sets the value of nopositions
     *
     * @param argNopositions Value to assign to this.nopositions
     */
    public void setNopositions(boolean argNopositions) {
        this.nopositions = argNopositions;
    }

    /**
     * Gets the value of locations
     *
     * @return the value of locations
     */
    public boolean isLocations() {
        return this.locations;
    }

    /**
     * Sets the value of locations
     *
     * @param argLocations Value to assign to this.locations
     */
    public void setLocations(boolean argLocations) {
        this.locations = argLocations;
    }

    /**
     * Gets the value of noscanner
     *
     * @return the value of noscanner
     */
    public boolean isNoscanner() {
        return this.noscanner;
    }

    /**
     * Sets the value of noscanner
     *
     * @param argNoscanner Value to assign to this.noscanner
     */
    public void setNoscanner(boolean argNoscanner) {
        this.noscanner = argNoscanner;
    }

    public boolean isXmlactions() {
        return xmlactions;
    }

    public void setXmlactions(boolean xmlactions) {
        this.xmlactions = xmlactions;
    }

    public boolean isGenericlabels() {
        return genericlabels;
    }

    public void setGenericlabels(boolean genericlabels) {
        this.genericlabels = genericlabels;
    }

}
