
/*
 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
 *
 * $Id$
 */

package org.dellroad.stuff.main;

import java.io.EOFException;
import java.io.File;

import org.gnu.readline.Readline;
import org.gnu.readline.ReadlineLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Support superclass for command line classes.
 */
public abstract class MainClass {

    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected MainClass() {
    }

    /**
     * Subclass main implementation. This method is free to throw exceptions; these will
     * be displayed on standard error and converted into non-zero exit values.
     *
     * @return exit value
     */
    protected abstract int run(String[] args) throws Exception;

    /**
     * Enter command loop. Commands are read using GNU libreadline and handed off
     * to {@link #handleCommand} for processing.
     */
    protected void commandLoop(String appName, String prompt) throws Exception {

        // Setup readline
        try {
            Readline.load(ReadlineLibrary.GnuReadline);
        } catch (UnsatisfiedLinkError e) {
            // ignore
        }
        Readline.initReadline(appName);
        Readline.setThrowExceptionOnUnsupportedMethod(false);

        // Read init file(s)
        String[] files = new String[] { System.getenv("INPUTRC"),
          new File(new File(System.getProperty("user.home")), ".inputrc").getAbsolutePath(), "/etc/.inputrc" };
        for (String file : files) {
            try {
                Readline.readInitFile(file);
            } catch (Exception e) {
                // ignore
            }
        }

        // Read history file
        String historyFile = new File(new File(System.getProperty("user.home")), "." + appName + "_history").getAbsolutePath();
        try {
            Readline.readHistoryFile(historyFile);
        } catch (Exception e) {
            // ignore
        }

        // Main loop
        try {
            while (true) {

                // Read next line
                String line;
                try {
                    line = Readline.readline(prompt);
                } catch (EOFException e) {
                    break;
                }

                // No input?
                if (line == null)
                    continue;

                // Update history
                Readline.addToHistory(line);

                // Execute line
                if (!this.handleCommand(line))
                    break;
            }
        } finally {

            // Save history
            try {
                Readline.writeHistoryFile(historyFile);
            } catch (Exception e) {
                // ignore
            }

            // Clean up
            Readline.cleanup();
        }
    }

    /**
     * Callback used by {@link #commandLoop}.
     *
     * <p>
     * The implementation in {@link MainClass} just returns {@code false}.
     *
     * @return true to continue reading the next command, false to exit
     */
    protected boolean handleCommand(String line) throws Exception {
        return false;
    }

    /**
     * Display the usage message to standard error.
     */
    protected abstract void usageMessage();

    /**
     * Print the usage message and exit with exit value 1.
     */
    protected void usageError() {
        usageMessage();
        System.exit(1);
    }

    /**
     * Emit an error message an exit with exit value 1.
     */
    protected final void errout(String message) {
        System.err.println(getClass().getSimpleName() + ": " + message);
        System.exit(1);
    }

    /**
     * Invokes {@link #run}, catching any exceptions thrown and exiting with a non-zero
     * value if and only if an exception was caught.
     * <p/>
     * <p>
     * The concrete class' {@code main()} method should invoke this method.
     * </p>
     */
    protected void doMain(String[] args) {
        int exitValue = 1;
        try {
            exitValue = run(args);
        } catch (Throwable t) {
            t.printStackTrace(System.err);
        } finally {
            System.exit(exitValue);
        }
    }
}


@@ -156,16 +156,44 @@
 io.File;
+%0Aimport java.util.ArrayList;
 %0A%0Aimport
@@ -3665,32 +3665,1154 @@
 exit(1);%0A    %7D%0A%0A
+    /**%0A     * Parse command line flags of the form %7B@code -Dname=value%7D and set the corresponding system properties.%0A     * Parsing stops at the first argument not starting with a dash (or %7B@code --%7D).%0A     *%0A     * @return command line with all the property-setting flags removed%0A     */%0A    protected String%5B%5D parsePropertyFlags(String%5B%5D args) %7B%0A        ArrayList%3CString%3E list = new ArrayList%3CString%3E(args.length);%0A        boolean done = false;%0A        for (String arg : args) %7B%0A            if (done) %7B%0A                list.add(arg);%0A                continue;%0A            %7D%0A            if (arg.equals(%22--%22) %7C%7C arg.length() == 0 %7C%7C arg.charAt(0) != '-') %7B%0A                list.add(arg);%0A                done = true;%0A                continue;%0A            %7D%0A            if (arg.startsWith(%22-D%22)) %7B%0A                int eq = arg.indexOf('=');%0A                if (eq %3C 3)%0A                    usageError();%0A                System.setProperty(arg.substring(2, eq), arg.substring(eq + 1));%0A                continue;%0A            %7D%0A            list.add(arg);%0A        %7D%0A        return list.toArray(new String%5Blist.size()%5D);%0A    %7D%0A%0A
     /**%0A     * I

Add MainClass.parsePropertyFlags().