Logo Search packages:      
Sourcecode: aspectj version File versions  Download package

Main.java

/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.tools.ajc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.aspectj.ajdt.internal.core.builder.AjBuildManager;
import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.ICommand;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IMessageHolder;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.ReflectionFactory;
import org.aspectj.bridge.Version;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
import org.aspectj.weaver.Dump;

/**
 * Programmatic and command-line interface to AspectJ compiler. The compiler is an ICommand obtained by reflection. Not thread-safe.
 * By default, messages are printed as they are emitted; info messages go to the output stream, and warnings and errors go to the
 * error stream.
 * <p>
 * Clients can handle all messages by registering a holder:
 * 
 * <pre>
 * Main main = new Main();
 * IMessageHolder holder = new MessageHandler();
 * main.setHolder(holder);
 * </pre>
 * 
 * Clients can get control after each command completes by installing a Runnable:
 * 
 * <pre>
 * main.setCompletionRunner(new Runnable() {..});
 * </pre>
 * 
 */
00061 public class Main {
      /** Header used when rendering exceptions for users */
00063       public static final String THROWN_PREFIX = "Exception thrown from AspectJ " + Version.text + LangUtil.EOL + "" + LangUtil.EOL
                  + "This might be logged as a bug already -- find current bugs at" + LangUtil.EOL
                  + "  http://bugs.eclipse.org/bugs/buglist.cgi?product=AspectJ&component=Compiler" + LangUtil.EOL + "" + LangUtil.EOL
                  + "Bugs for exceptions thrown have titles File:line from the top stack, " + LangUtil.EOL
                  + "e.g., \"SomeFile.java:243\"" + LangUtil.EOL + "" + LangUtil.EOL
                  + "If you don't find the exception below in a bug, please add a new bug" + LangUtil.EOL
                  + "at http://bugs.eclipse.org/bugs/enter_bug.cgi?product=AspectJ" + LangUtil.EOL
                  + "To make the bug a priority, please include a test program" + LangUtil.EOL + "that can reproduce this exception."
                  + LangUtil.EOL;

      private static final String OUT_OF_MEMORY_MSG = "AspectJ " + Version.text + " ran out of memory during compilation:"
                  + LangUtil.EOL + LangUtil.EOL + "Please increase the memory available to ajc by editing the ajc script " + LangUtil.EOL
                  + "found in your AspectJ installation directory. The -Xmx parameter value" + LangUtil.EOL
                  + "should be increased from 64M (default) to 128M or even 256M." + LangUtil.EOL + LangUtil.EOL
                  + "See the AspectJ FAQ available from the documentation link" + LangUtil.EOL
                  + "on the AspectJ home page at http://www.eclipse.org/aspectj";

      private static final String MESSAGE_HOLDER_OPTION = "-messageHolder";

      /** @param args the String[] of command-line arguments */
00083       public static void main(String[] args) throws IOException {
            new Main().runMain(args, true);
      }

      /**
       * Convenience method to run ajc and collect String lists of messages. This can be reflectively invoked with the List collecting
       * parameters supplied by a parent class loader. The String messages take the same form as command-line messages.
       * 
       * @param args the String[] args to pass to the compiler
       * @param useSystemExit if true and errors, return System.exit(errs)
       * @param fails the List sink, if any, for String failure (or worse) messages
       * @param errors the List sink, if any, for String error messages
       * @param warnings the List sink, if any, for String warning messages
       * @param info the List sink, if any, for String info messages
       * @return number of messages reported with level ERROR or above
       * @throws any unchecked exceptions the compiler does
       */
00100       public static int bareMain(String[] args, boolean useSystemExit, List fails, List errors, List warnings, List infos) {
            Main main = new Main();
            MessageHandler holder = new MessageHandler();
            main.setHolder(holder);
            try {
                  main.runMain(args, useSystemExit);
            } finally {
                  readMessages(holder, IMessage.FAIL, true, fails);
                  readMessages(holder, IMessage.ERROR, false, errors);
                  readMessages(holder, IMessage.WARNING, false, warnings);
                  readMessages(holder, IMessage.INFO, false, infos);
            }
            return holder.numMessages(IMessage.ERROR, true);
      }

      /** Read messages of a given kind into a List as String */
00116       private static void readMessages(IMessageHolder holder, IMessage.Kind kind, boolean orGreater, List sink) {
            if ((null == sink) || (null == holder)) {
                  return;
            }
            IMessage[] messages = holder.getMessages(kind, orGreater);
            if (!LangUtil.isEmpty(messages)) {
                  for (int i = 0; i < messages.length; i++) {
                        sink.add(MessagePrinter.render(messages[i]));
                  }
            }
      }

      /**
       * @return String rendering throwable as compiler error for user/console, including information on how to report as a bug.
       * @throws NullPointerException if thrown is null
       */
00132       public static String renderExceptionForUser(Throwable thrown) {
            String m = thrown.getMessage();
            return THROWN_PREFIX + (null != m ? m + "\n" : "") + "\n" + CompilationAndWeavingContext.getCurrentContext()
                        + LangUtil.renderException(thrown, true);
      }

      private static String parmInArgs(String flag, String[] args) {
            int loc = 1 + (null == args ? -1 : Arrays.asList(args).indexOf(flag));
            return ((0 == loc) || (args.length <= loc) ? null : args[loc]);
      }

      private static boolean flagInArgs(String flag, String[] args) {
            return ((null != args) && (Arrays.asList(args).indexOf(flag) != -1));
      }

      /**
       * append nothing if numItems is 0, numItems + label + (numItems > 1? "s" : "") otherwise, prefixing with " " if sink has
       * content
       */
00151       private static void appendNLabel(StringBuffer sink, String label, int numItems) {
            if (0 == numItems) {
                  return;
            }
            if (0 < sink.length()) {
                  sink.append(", ");
            }
            sink.append(numItems + " ");
            if (!LangUtil.isEmpty(label)) {
                  sink.append(label);
            }
            if (1 < numItems) {
                  sink.append("s");
            }
      }

      /** control iteration/continuation for command (compiler) */
00168       protected CommandController controller;

      /** ReflectionFactory identifier for command (compiler) */
00171       protected String commandName;

      /** client-set message sink */
00174       private IMessageHolder clientHolder;

      /** internally-set message sink */
00177       protected final MessageHandler ourHandler;

      private int lastFails;
      private int lastErrors;

      /** if not null, run this synchronously after each compile completes */
00183       private Runnable completionRunner;

      public Main() {
            controller = new CommandController();
            commandName = ReflectionFactory.ECLIPSE;
            CompilationAndWeavingContext.setMultiThreaded(false);
            ourHandler = new MessageHandler(true);
      }

      public MessageHandler getMessageHandler() {
            return ourHandler;
      }

      // for unit testing...
      void setController(CommandController controller) {
            this.controller = controller;
      }

      /**
       * Run without throwing exceptions but optionally using System.exit(..). This sets up a message handler which emits messages
       * immediately, so report(boolean, IMessageHandler) only reports total number of errors or warnings.
       * 
       * @param args the String[] command line for the compiler
       * @param useSystemExit if true, use System.exit(int) to complete unless one of the args is -noExit. and signal result (0 no
       *        exceptions/error, <0 exceptions, >0 compiler errors).
       */
00209       public void runMain(String[] args, boolean useSystemExit) {
            // Urk - default no check for AJDT, enabled here for Ant, command-line
            AjBuildManager.enableRuntimeVersionCheck(this);
            final boolean verbose = flagInArgs("-verbose", args);
            final boolean timers = flagInArgs("-timers", args);
            if (null == this.clientHolder) {
                  this.clientHolder = checkForCustomMessageHolder(args);
            }
            IMessageHolder holder = clientHolder;
            if (null == holder) {
                  holder = ourHandler;
                  if (verbose) {
                        ourHandler.setInterceptor(MessagePrinter.VERBOSE);
                  } else {
                        ourHandler.ignore(IMessage.INFO);
                        ourHandler.setInterceptor(MessagePrinter.TERSE);
                  }
            }

            // make sure we handle out of memory gracefully...
            try {
                  // byte[] b = new byte[100000000]; for testing OoME only!
                  long stime = System.currentTimeMillis();
                  // uncomment next line to pause before startup (attach jconsole)
                  // try {Thread.sleep(5000); }catch(Exception e) {}
                  run(args, holder);
                  long etime = System.currentTimeMillis();
                  if (timers) {
                        System.out.println("Compiler took " + (etime - stime) + "ms");
                  }
                  holder.handleMessage(MessageUtil.info("Compiler took " + (etime - stime) + "ms"));
                  // uncomment next line to pause at end (keeps jconsole alive!)
                  // try { System.in.read(); } catch (Exception e) {}
            } catch (OutOfMemoryError outOfMemory) {
                  IMessage outOfMemoryMessage = new Message(OUT_OF_MEMORY_MSG, null, true);
                  holder.handleMessage(outOfMemoryMessage);
                  systemExit(holder); // we can't reasonably continue from this point.
            } finally {
                  CompilationAndWeavingContext.reset();
                  Dump.reset();
            }

            boolean skipExit = false;
            if (useSystemExit && !LangUtil.isEmpty(args)) { // sigh - pluck -noExit
                  for (int i = 0; i < args.length; i++) {
                        if ("-noExit".equals(args[i])) {
                              skipExit = true;
                              break;
                        }
                  }
            }
            if (useSystemExit && !skipExit) {
                  systemExit(holder);
            }
      }

      // put calls around run() call above to allowing connecting jconsole
      // private void pause(int ms) {
      // try {
      // System.err.println("Pausing for "+ms+"ms");
      // System.gc();
      // Thread.sleep(ms);
      // System.gc();
      // System.err.println("Continuing");
      // } catch (Exception e) {}
      // }

      /**
       * @param args
       */
00279       private IMessageHolder checkForCustomMessageHolder(String[] args) {
            IMessageHolder holder = null;
            final String customMessageHolder = parmInArgs(MESSAGE_HOLDER_OPTION, args);
            if (customMessageHolder != null) {
                  try {
                        holder = (IMessageHolder) Class.forName(customMessageHolder).newInstance();
                  } catch (Exception ex) {
                        holder = ourHandler;
                        throw new AbortException("Failed to create custom message holder of class '" + customMessageHolder + "' : " + ex);
                  }
            }
            return holder;
      }

      /**
       * Run without using System.exit(..), putting all messages in holder:
       * <ul>
       * <li>ERROR: compiler error</li>
       * <li>WARNING: compiler warning</li>
       * <li>FAIL: command error (bad arguments, exception thrown)</li>
       * </ul>
       * This handles incremental behavior:
       * <ul>
       * <li>If args include "-incremental", repeat for every input char until 'q' is entered.
       * <li>
       * <li>If args include "-incrementalTagFile {file}", repeat every time we detect that {file} modification time has changed.</li>
       * <li>Either way, list files recompiled each time if args includes "-verbose".</li>
       * <li>Exit when the commmand/compiler throws any Throwable.</li>
       * </ul>
       * When complete, this contains all the messages of the final run of the command and/or any FAIL messages produced in running
       * the command, including any Throwable thrown by the command itself.
       * 
       * @param args the String[] command line for the compiler
       * @param holder the MessageHandler sink for messages.
       */
00314       public void run(String[] args, IMessageHolder holder) {

            PrintStream logStream = null;
            FileOutputStream fos = null;
            String logFileName = parmInArgs("-log", args);
            if (null != logFileName) {
                  File logFile = new File(logFileName);
                  try {
                        logFile.createNewFile();
                        fos = new FileOutputStream(logFileName, true);
                        logStream = new PrintStream(fos, true);
                  } catch (Exception e) {
                        fail(holder, "Couldn't open log file: " + logFileName, e);
                  }
                  Date now = new Date();
                  logStream.println(now.toString());
                  if (flagInArgs("-verbose", args)) {
                        ourHandler.setInterceptor(new LogModeMessagePrinter(true, logStream));
                  } else {
                        ourHandler.ignore(IMessage.INFO);
                        ourHandler.setInterceptor(new LogModeMessagePrinter(false, logStream));
                  }
                  holder = ourHandler;
            }

            if (LangUtil.isEmpty(args)) {
                  args = new String[] { "-?" };
            } else if (controller.running()) {
                  fail(holder, "already running with controller: " + controller, null);
                  return;
            }
            args = controller.init(args, holder);
            if (0 < holder.numMessages(IMessage.ERROR, true)) {
                  return;
            }
            ICommand command = ReflectionFactory.makeCommand(commandName, holder);
            if (0 < holder.numMessages(IMessage.ERROR, true)) {
                  return;
            }
            try {
                  outer: while (true) {
                        boolean passed = command.runCommand(args, holder);
                        if (report(passed, holder) && controller.incremental()) {
                              while (controller.doRepeatCommand(command)) {
                                    holder.clearMessages();
                                    if (controller.buildFresh()) {
                                          continue outer;
                                    } else {
                                          passed = command.repeatCommand(holder);
                                    }
                                    if (!report(passed, holder)) {
                                          break;
                                    }
                              }
                        }
                        break;
                  }
            } catch (AbortException ae) {
                  if (ae.isSilent()) {
                        quit();
                  } else {
                        IMessage message = ae.getIMessage();
                        Throwable thrown = ae.getThrown();
                        if (null == thrown) { // toss AbortException wrapper
                              if (null != message) {
                                    holder.handleMessage(message);
                              } else {
                                    fail(holder, "abort without message", ae);
                              }
                        } else if (null == message) {
                              fail(holder, "aborted", thrown);
                        } else {
                              String mssg = MessageUtil.MESSAGE_MOST.renderToString(message);
                              fail(holder, mssg, thrown);
                        }
                  }
            } catch (Throwable t) {
                  fail(holder, "unexpected exception", t);
            } finally {
                  if (logStream != null) {
                        logStream.close();
                        logStream = null;
                  }
                  if (fos != null) {
                        try {
                              fos.close();
                        } catch (IOException e) {
                              fail(holder, "unexpected exception", e);
                        }
                        fos = null;
                  }
            }
      }

      /** call this to stop after the next iteration of incremental compile */
00409       public void quit() {
            controller.quit();
      }

      /**
       * Set holder to be passed all messages. When holder is set, messages will not be printed by default.
       * 
       * @param holder the IMessageHolder sink for all messages (use null to restore default behavior)
       */
00418       public void setHolder(IMessageHolder holder) {
            clientHolder = holder;
      }

      public IMessageHolder getHolder() {
            return clientHolder;
      }

      /**
       * Install a Runnable to be invoked synchronously after each compile completes.
       * 
       * @param runner the Runnable to invoke - null to disable
       */
00431       public void setCompletionRunner(Runnable runner) {
            this.completionRunner = runner;
      }

      /**
       * Call System.exit(int) with values derived from the number of failures/aborts or errors in messages.
       * 
       * @param messages the IMessageHolder to interrogate.
       * @param messages
       */
00441       protected void systemExit(IMessageHolder messages) {
            int num = lastFails; // messages.numMessages(IMessage.FAIL, true);
            if (0 < num) {
                  System.exit(-num);
            }
            num = lastErrors; // messages.numMessages(IMessage.ERROR, false);
            if (0 < num) {
                  System.exit(num);
            }
            System.exit(0);
      }

      /** Messages to the user */
00454       protected void outMessage(String message) { // XXX coordinate with MessagePrinter
            System.out.print(message);
            System.out.flush();
      }

      /**
       * Report results from a (possibly-incremental) compile run. This delegates to any reportHandler or otherwise prints summary
       * counts of errors/warnings to System.err (if any errors) or System.out (if only warnings). WARNING: this silently ignores
       * other messages like FAIL, but clears the handler of all messages when returning true. XXX false
       * 
       * This implementation ignores the pass parameter but clears the holder after reporting on the assumption messages were
       * handled/printed already. (ignoring UnsupportedOperationException from holder.clearMessages()).
       * 
       * @param pass true result of the command
       * @param holder IMessageHolder with messages from the command
       * @see reportCommandResults(IMessageHolder)
       * @return false if the process should abort
       */
00472       protected boolean report(boolean pass, IMessageHolder holder) {
            lastFails = holder.numMessages(IMessage.FAIL, true);
            boolean result = (0 == lastFails);
            final Runnable runner = completionRunner;
            if (null != runner) {
                  runner.run();
            }
            if (holder == ourHandler) {
                  lastErrors = holder.numMessages(IMessage.ERROR, false);
                  int warnings = holder.numMessages(IMessage.WARNING, false);
                  StringBuffer sb = new StringBuffer();
                  appendNLabel(sb, "fail|abort", lastFails);
                  appendNLabel(sb, "error", lastErrors);
                  appendNLabel(sb, "warning", warnings);
                  if (0 < sb.length()) {
                        PrintStream out = (0 < (lastErrors + lastFails) ? System.err : System.out);
                        out.println(""); // XXX "wrote class file" messages no eol?
                        out.println(sb.toString());
                  }
            }
            return result;
      }

      /** convenience API to make fail messages (without MessageUtils's fail prefix) */
00496       protected static void fail(IMessageHandler handler, String message, Throwable thrown) {
            handler.handleMessage(new Message(message, IMessage.FAIL, thrown, null));
      }

      /**
       * interceptor IMessageHandler to print as we go. This formats all messages to the user.
       */
00503       public static class MessagePrinter implements IMessageHandler {

            public static final IMessageHandler VERBOSE = new MessagePrinter(true);
            public static final IMessageHandler TERSE = new MessagePrinter(false);

            final boolean verbose;

            protected MessagePrinter(boolean verbose) {
                  this.verbose = verbose;
            }

            /**
             * Print errors and warnings to System.err, and optionally info to System.out, rendering message String only.
             * 
             * @return false always
             */
00519             public boolean handleMessage(IMessage message) {
                  if (null != message) {
                        PrintStream out = getStreamFor(message.getKind());
                        if (null != out) {
                              out.println(render(message));
                        }
                  }
                  return false;
            }

            /**
             * Render message differently. If abort, then prefix stack trace with feedback request. If the actual message is empty, then
             * use toString on the whole. Prefix message part with file:line; If it has context, suffix message with context.
             * 
             * @param message the IMessage to render
             * @return String rendering IMessage (never null)
             */
00536             public static String render(IMessage message) {
                  // IMessage.Kind kind = message.getKind();

                  StringBuffer sb = new StringBuffer();
                  String text = message.getMessage();
                  if (text.equals(AbortException.NO_MESSAGE_TEXT)) {
                        text = null;
                  }
                  boolean toString = (LangUtil.isEmpty(text));
                  if (toString) {
                        text = message.toString();
                  }
                  ISourceLocation loc = message.getSourceLocation();
                  String context = null;
                  if (null != loc) {
                        File file = loc.getSourceFile();
                        if (null != file) {
                              String name = file.getName();
                              if (!toString || (-1 == text.indexOf(name))) {
                                    sb.append(FileUtil.getBestPath(file));
                                    if (loc.getLine() > 0) {
                                          sb.append(":" + loc.getLine());
                                    }
                                    int col = loc.getColumn();
                                    if (0 < col) {
                                          sb.append(":" + col);
                                    }
                                    sb.append(" ");
                              }
                        }
                        context = loc.getContext();
                  }

                  // per Wes' suggestion on dev...
                  if (message.getKind() == IMessage.ERROR) {
                        sb.append("[error] ");
                  } else if (message.getKind() == IMessage.WARNING) {
                        sb.append("[warning] ");
                  }

                  sb.append(text);
                  if (null != context) {
                        sb.append(LangUtil.EOL);
                        sb.append(context);
                  }

                  String details = message.getDetails();
                  if (details != null) {
                        sb.append(LangUtil.EOL);
                        sb.append('\t');
                        sb.append(details);
                  }
                  Throwable thrown = message.getThrown();
                  if (null != thrown) {
                        sb.append(LangUtil.EOL);
                        sb.append(Main.renderExceptionForUser(thrown));
                  }

                  if (message.getExtraSourceLocations().isEmpty()) {
                        return sb.toString();
                  } else {
                        return MessageUtil.addExtraSourceLocations(message, sb.toString());
                  }

            }

00602             public boolean isIgnoring(IMessage.Kind kind) {
                  return (null != getStreamFor(kind));
            }

            /**
             * No-op
             * 
             * @see org.aspectj.bridge.IMessageHandler#isIgnoring(org.aspectj.bridge.IMessage.Kind)
             * @param kind
             */
00612             public void dontIgnore(IMessage.Kind kind) {

            }

            /**
             * @return System.err for FAIL, ABORT, ERROR, and WARNING, System.out for INFO if -verbose and WEAVEINFO if -showWeaveInfo.
             */
00619             protected PrintStream getStreamFor(IMessage.Kind kind) {
                  if (IMessage.WARNING.isSameOrLessThan(kind)) {
                        return System.err;
                  } else if (verbose && IMessage.INFO.equals(kind)) {
                        return System.out;
                  } else if (IMessage.WEAVEINFO.equals(kind)) {
                        return System.out;
                  } else {
                        return null;
                  }
            }

            /**
             * No-op
             * 
             * @see org.aspectj.bridge.IMessageHandler#ignore(org.aspectj.bridge.IMessage.Kind)
             * @param kind
             */
00637             public void ignore(Kind kind) {
            }
      }

      public static class LogModeMessagePrinter extends MessagePrinter {

            protected final PrintStream logStream;

            public LogModeMessagePrinter(boolean verbose, PrintStream logStream) {
                  super(verbose);
                  this.logStream = logStream;
            }

            protected PrintStream getStreamFor(IMessage.Kind kind) {
                  if (IMessage.WARNING.isSameOrLessThan(kind)) {
                        return logStream;
                  } else if (verbose && IMessage.INFO.equals(kind)) {
                        return logStream;
                  } else if (IMessage.WEAVEINFO.equals(kind)) {
                        return logStream;
                  } else {
                        return null;
                  }
            }

      }

      /** controller for repeatable command delays until input or file changed or removed */
00665       public static class CommandController {
            public static String TAG_FILE_OPTION = "-XincrementalFile";
            public static String INCREMENTAL_OPTION = "-incremental";

            /** maximum 10-minute delay between filesystem checks */
00670             public static long MAX_DELAY = 1000 * 600;

            /** default 5-second delay between filesystem checks */
00673             public static long DEFAULT_DELAY = 1000 * 5;

            /** @see init(String[]) */
00676             private static String[][] OPTIONS = new String[][] { new String[] { INCREMENTAL_OPTION },
                        new String[] { TAG_FILE_OPTION, null } };

            /** true between init(String[]) and doRepeatCommand() that returns false */
00680             private boolean running;

            /** true after quit() called */
00683             private boolean quit;

            /** true if incremental mode, waiting for input other than 'q' */
00686             private boolean incremental;

            /** true if incremental mode, waiting for file to change (repeat) or disappear (quit) */
00689             private File tagFile;

            /** last modification time for tagFile as of last command - 0 to start */
00692             private long fileModTime;

            /** delay between filesystem checks for tagFile modification time */
00695             private long delay;

            /** true just after user types 'r' for rebuild */
00698             private boolean buildFresh;

            public CommandController() {
                  delay = DEFAULT_DELAY;
            }

            /**
             * @param argList read and strip incremental args from this
             * @param sink IMessageHandler for error messages
             * @return String[] remainder of args
             */
00709             public String[] init(String[] args, IMessageHandler sink) {
                  running = true;
                  // String[] unused;
                  if (!LangUtil.isEmpty(args)) {
                        String[][] options = LangUtil.copyStrings(OPTIONS);
                        /* unused = */LangUtil.extractOptions(args, options);
                        incremental = (null != options[0][0]);
                        if (null != options[1][0]) {
                              File file = new File(options[1][1]);
                              if (!file.exists()) {
                                    MessageUtil.abort(sink, "tag file does not exist: " + file);
                              } else {
                                    tagFile = file;
                                    fileModTime = tagFile.lastModified();
                              }
                        }
                  }
                  return args;
            }

            /**
             * @return true if init(String[]) called but doRepeatCommand has not returned false
             */
00732             public boolean running() {
                  return running;
            }

            /** @param delay milliseconds between filesystem checks */
00737             public void setDelay(long delay) {
                  if ((delay > -1) && (delay < MAX_DELAY)) {
                        this.delay = delay;
                  }
            }

            /** @return true if INCREMENTAL_OPTION or TAG_FILE_OPTION was in args */
00744             public boolean incremental() {
                  return (incremental || (null != tagFile));
            }

            /** @return true if INCREMENTAL_OPTION was in args */
00749             public boolean commandLineIncremental() {
                  return incremental;
            }

            public void quit() {
                  if (!quit) {
                        quit = true;
                  }
            }

            /** @return true just after user typed 'r' */
00760             boolean buildFresh() {
                  return buildFresh;
            }

            /** @return false if we should quit, true to do another command */
00765             boolean doRepeatCommand(ICommand command) {
                  if (!running) {
                        return false;
                  }
                  boolean result = false;
                  if (quit) {
                        result = false;
                  } else if (incremental) {
                        try {
                              if (buildFresh) { // reset before input request
                                    buildFresh = false;
                              }
                              System.out.println(" press enter to recompile, r to rebuild, q to quit: ");
                              System.out.flush();
                              // boolean doMore = false;
                              // seek for one q or a series of [\n\r]...
                              do {
                                    int input = System.in.read();
                                    if ('q' == input) {
                                          break; // result = false;
                                    } else if ('r' == input) {
                                          buildFresh = true;
                                          result = true;
                                    } else if (('\n' == input) || ('\r' == input)) {
                                          result = true;
                                    } // else eat anything else
                              } while (!result);
                              System.in.skip(Integer.MAX_VALUE);
                        } catch (IOException e) { // XXX silence for error?
                              result = false;
                        }
                  } else if (null != tagFile) {
                        long curModTime;
                        while (true) {
                              if (!tagFile.exists()) {
                                    result = false;
                                    break;
                              } else if (fileModTime == (curModTime = tagFile.lastModified())) {
                                    fileCheckDelay();
                              } else {
                                    fileModTime = curModTime;
                                    result = true;
                                    break;
                              }
                        }
                  } // else, not incremental - false
                  if (!result && running) {
                        running = false;
                  }
                  return result;
            }

            /** delay between filesystem checks, returning if quit is set */
00818             protected void fileCheckDelay() {
                  // final Thread thread = Thread.currentThread();
                  long targetTime = System.currentTimeMillis() + delay;
                  // long curTime;
                  while (targetTime > System.currentTimeMillis()) {
                        if (quit) {
                              return;
                        }
                        try {
                              Thread.sleep(300);
                        } // 1/3-second delta for quit check
                        catch (InterruptedException e) {
                        }
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index