Java Tutorial/Development/Debug

Материал из Java эксперт
Версия от 15:30, 31 мая 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

A bean that can be used to keep track of a counter

/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
import java.io.Serializable;

/**
 * A bean that can be used to keep track of a counter.
 * <p/>
 * Since it is an Iterator it can be used by the iterator tag
 *
 * @author Rickard &Ouml;berg (rickard@middleware-company.ru)
 * @version $Revision: 1282 $
 * @see <related>
 */
public class Counter implements java.util.Iterator, Serializable {
    boolean wrap = false;
    // Attributes ----------------------------------------------------
    long first = 1;
    long current = first;
    long interval = 1;
    long last = -1;

    public void setAdd(long addition) {
        current += addition;
    }
    public void setCurrent(long current) {
        this.current = current;
    }
    public long getCurrent() {
        return current;
    }
    public void setFirst(long first) {
        this.first = first;
        current = first;
    }
    public long getFirst() {
        return first;
    }
    public void setInterval(long interval) {
        this.interval = interval;
    }
    public long getInterval() {
        return interval;
    }
    public void setLast(long last) {
        this.last = last;
    }
    public long getLast() {
        return last;
    }
    // Public --------------------------------------------------------
    public long getNext() {
        long next = current;
        current += interval;
        if (wrap && (current > last)) {
            current -= ((1 + last) - first);
        }
        return next;
    }
    public long getPrevious() {
        current -= interval;
        if (wrap && (current < first)) {
            current += (last - first + 1);
        }
        return current;
    }
    public void setWrap(boolean wrap) {
        this.wrap = wrap;
    }
    public boolean isWrap() {
        return wrap;
    }
    public boolean hasNext() {
        return ((last == -1) || wrap) ? true : (current <= last);
    }
    public Object next() {
        return new Long(getNext());
    }
    public void remove() {
        // Do nothing
    }
}
///////////////////
/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
package com.opensymphony.webwork.util;
import junit.framework.TestCase;

/**
 * User: plightbo
 * Date: Jan 7, 2004
 * Time: 7:55:35 PM
 */
public class CounterTest extends TestCase {
    Counter c = new Counter();

    public void testCurrentAfterNext() {
        long next = c.getNext();
        long current = c.getCurrent();
        assertEquals(next + 1, current);
    }
    public void testCurrentBeforeNext() {
        long current = c.getCurrent();
        long next = c.getNext();
        assertEquals(current, next);
    }
    public void testWrap() {
        c.setWrap(true);
        c.setLast(1);
        long a = c.getNext();
        long b = c.getNext();
        assertEquals(1, a);
        assertEquals(1, b);
    }
}





A helper class for printing indented text

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.PrintWriter;
/**
 * A helper class for printing indented text
 *
 * @version $Revision: 1.2 $
 */
public class IndentPrinter {
    private int indentLevel;
    private String indent;
    private PrintWriter out;
    public IndentPrinter() {
        this(new PrintWriter(System.out), "  ");
    }
    public IndentPrinter(PrintWriter out) {
        this(out, "  ");
    }
    public IndentPrinter(PrintWriter out, String indent) {
        this.out = out;
        this.indent = indent;
    }
    public void println(Object value) {
        out.print(value.toString());
        out.println();
    }
    public void println(String text) {
        out.print(text);
        out.println();
    }
    public void print(String text) {
        out.print(text);
    }
    public void printIndent() {
        for (int i = 0; i < indentLevel; i++) {
            out.print(indent);
        }
    }
    public void println() {
        out.println();
    }
    public void incrementIndent() {
        ++indentLevel;
    }
    public void decrementIndent() {
        --indentLevel;
    }
    public int getIndentLevel() {
        return indentLevel;
    }
    public void setIndentLevel(int indentLevel) {
        this.indentLevel = indentLevel;
    }
    public void flush() {
        out.flush();
    }
}





A simple logging facility.

/* 
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 *
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info:  http://www.jfree.org/jcommon/index.html
 *
 * 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 
 * (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 * USA.  
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 * 
 * --------
 * Log.java
 * --------
 * (C)opyright 2002-2004, by Thomas Morgner and Contributors.
 *
 * Original Author:  Thomas Morgner (taquera@sherito.org);
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 *
 * $Id: Log.java,v 1.5 2006/06/08 17:42:20 taqua Exp $
 *
 * Changes
 * -------
 * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
 * 11-Jun-2003 : Removing LogTarget did not work. 
 * 
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
/**
 * A simple logging facility. Create a class implementing the
 * {@link org.jfree.util.LogTarget} interface to use this feature.
 * 
 * @author Thomas Morgner
 */
public class Log {
  /**
   * A simple message class.
   */
  public static class SimpleMessage {
    /**
     * The message.
     */
    private String message;
    /**
     * The parameters.
     */
    private Object[] param;
    /**
     * Creates a new message.
     * 
     * @param message
     *          the message text.
     * @param param1
     *          parameter 1.
     */
    public SimpleMessage(final String message, final Object param1) {
      this.message = message;
      this.param = new Object[] { param1 };
    }
    /**
     * Creates a new message.
     * 
     * @param message
     *          the message text.
     * @param param1
     *          parameter 1.
     * @param param2
     *          parameter 2.
     */
    public SimpleMessage(final String message, final Object param1, final Object param2) {
      this.message = message;
      this.param = new Object[] { param1, param2 };
    }
    /**
     * Creates a new message.
     * 
     * @param message
     *          the message text.
     * @param param1
     *          parameter 1.
     * @param param2
     *          parameter 2.
     * @param param3
     *          parameter 3.
     */
    public SimpleMessage(final String message, final Object param1, final Object param2,
        final Object param3) {
      this.message = message;
      this.param = new Object[] { param1, param2, param3 };
    }
    /**
     * Creates a new message.
     * 
     * @param message
     *          the message text.
     * @param param1
     *          parameter 1.
     * @param param2
     *          parameter 2.
     * @param param3
     *          parameter 3.
     * @param param4
     *          parameter 4.
     */
    public SimpleMessage(final String message, final Object param1, final Object param2,
        final Object param3, final Object param4) {
      this.message = message;
      this.param = new Object[] { param1, param2, param3, param4 };
    }
    /**
     * Creates a new message.
     * 
     * @param message
     *          the message text.
     * @param param
     *          the parameters.
     */
    public SimpleMessage(final String message, final Object[] param) {
      this.message = message;
      this.param = param;
    }
    /**
     * Returns a string representation of the message (useful for debugging).
     * 
     * @return the string.
     */
    public String toString() {
      final StringBuffer b = new StringBuffer();
      b.append(this.message);
      if (this.param != null) {
        for (int i = 0; i < this.param.length; i++) {
          b.append(this.param[i]);
        }
      }
      return b.toString();
    }
  }
  /**
   * The logging threshold.
   */
  private int debuglevel;
  /**
   * Storage for the log targets.
   */
  private LogTarget[] logTargets;
  /** The log contexts. */
  private HashMap logContexts;
  /**
   * the singleton instance of the Log system.
   */
  private static Log singleton;
  /**
   * Creates a new Log instance. The Log is used to manage the log targets.
   */
  protected Log() {
    this.logContexts = new HashMap();
    this.logTargets = new LogTarget[0];
    this.debuglevel = 100;
  }
  /**
   * Returns the singleton Log instance. A new instance is created if necessary.
   * 
   * @return the singleton instance.
   */
  public static synchronized Log getInstance() {
    if (singleton == null) {
      singleton = new Log();
    }
    return singleton;
  }
  /**
   * Redefines or clears the currently used log instance.
   * 
   * @param log
   *          the new log instance or null, to return to the default
   *          implementation.
   */
  protected static synchronized void defineLog(final Log log) {
    singleton = log;
  }
  /**
   * Returns the currently defined debug level. The higher the level, the more
   * details are printed.
   * 
   * @return the debug level.
   */
  public int getDebuglevel() {
    return this.debuglevel;
  }
  /**
   * Defines the debug level for the log system.
   * 
   * @param debuglevel
   *          the new debug level
   * @see #getDebuglevel()
   */
  protected void setDebuglevel(final int debuglevel) {
    this.debuglevel = debuglevel;
  }
  /**
   * Adds a log target to this facility. Log targets get informed, via the
   * LogTarget interface, whenever a message is logged with this class.
   * 
   * @param target
   *          the target.
   */
  public synchronized void addTarget(final LogTarget target) {
    if (target == null) {
      throw new NullPointerException();
    }
    final LogTarget[] data = new LogTarget[this.logTargets.length + 1];
    System.arraycopy(this.logTargets, 0, data, 0, this.logTargets.length);
    data[this.logTargets.length] = target;
    this.logTargets = data;
  }
  /**
   * Removes a log target from this facility.
   * 
   * @param target
   *          the target to remove.
   */
  public synchronized void removeTarget(final LogTarget target) {
    if (target == null) {
      throw new NullPointerException();
    }
    final ArrayList l = new ArrayList();
    l.addAll(Arrays.asList(this.logTargets));
    l.remove(target);
    final LogTarget[] targets = new LogTarget[l.size()];
    this.logTargets = (LogTarget[]) l.toArray(targets);
  }
  /**
   * Returns the registered logtargets.
   * 
   * @return the logtargets.
   */
  public LogTarget[] getTargets() {
    return (LogTarget[]) this.logTargets.clone();
  }
  /**
   * Replaces all log targets by the given target.
   * 
   * @param target
   *          the new and only logtarget.
   */
  public synchronized void replaceTargets(final LogTarget target) {
    if (target == null) {
      throw new NullPointerException();
    }
    this.logTargets = new LogTarget[] { target };
  }
  /**
   * A convenience method for logging a "debug" message.
   * 
   * @param message
   *          the message.
   */
  public static void debug(final Object message) {
    log(LogTarget.DEBUG, message);
  }
  /**
   * A convenience method for logging a "debug" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public static void debug(final Object message, final Exception e) {
    log(LogTarget.DEBUG, message, e);
  }
  /**
   * A convenience method for logging an "info" message.
   * 
   * @param message
   *          the message.
   */
  public static void info(final Object message) {
    log(LogTarget.INFO, message);
  }
  /**
   * A convenience method for logging an "info" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public static void info(final Object message, final Exception e) {
    log(LogTarget.INFO, message, e);
  }
  /**
   * A convenience method for logging a "warning" message.
   * 
   * @param message
   *          the message.
   */
  public static void warn(final Object message) {
    log(LogTarget.WARN, message);
  }
  /**
   * A convenience method for logging a "warning" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public static void warn(final Object message, final Exception e) {
    log(LogTarget.WARN, message, e);
  }
  /**
   * A convenience method for logging an "error" message.
   * 
   * @param message
   *          the message.
   */
  public static void error(final Object message) {
    log(LogTarget.ERROR, message);
  }
  /**
   * A convenience method for logging an "error" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public static void error(final Object message, final Exception e) {
    log(LogTarget.ERROR, message, e);
  }
  /**
   * Logs a message to the main log stream. All attached log targets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done.
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   */
  protected void doLog(int level, final Object message) {
    if (level > 3) {
      level = 3;
    }
    if (level <= this.debuglevel) {
      for (int i = 0; i < this.logTargets.length; i++) {
        final LogTarget t = this.logTargets[i];
        t.log(level, message);
      }
    }
  }
  /**
   * Logs a message to the main log stream. All attached log targets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done.
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   */
  public static void log(final int level, final Object message) {
    getInstance().doLog(level, message);
  }
  /**
   * Logs a message to the main log stream. All attached logTargets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done. <p/> The
   * exception"s stacktrace will be appended to the log-stream
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   * @param e
   *          the exception, which should be logged.
   */
  public static void log(final int level, final Object message, final Exception e) {
    getInstance().doLog(level, message, e);
  }
  /**
   * Logs a message to the main log stream. All attached logTargets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done. <p/> The
   * exception"s stacktrace will be appended to the log-stream
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   * @param e
   *          the exception, which should be logged.
   */
  protected void doLog(int level, final Object message, final Exception e) {
    if (level > 3) {
      level = 3;
    }
    if (level <= this.debuglevel) {
      for (int i = 0; i < this.logTargets.length; i++) {
        final LogTarget t = this.logTargets[i];
        t.log(level, message, e);
      }
    }
  }
  /**
   * Initializes the logging system. Implementors should override this method to
   * supply their own log configuration.
   */
  public void init() {
    // this method is intentionally empty.
  }
  /**
   * Returns true, if the log level allows debug messages to be printed.
   * 
   * @return true, if messages with an log level of DEBUG are allowed.
   */
  public static boolean isDebugEnabled() {
    return getInstance().getDebuglevel() >= LogTarget.DEBUG;
  }
  /**
   * Returns true, if the log level allows informational messages to be printed.
   * 
   * @return true, if messages with an log level of INFO are allowed.
   */
  public static boolean isInfoEnabled() {
    return getInstance().getDebuglevel() >= LogTarget.INFO;
  }
  /**
   * Returns true, if the log level allows warning messages to be printed.
   * 
   * @return true, if messages with an log level of WARN are allowed.
   */
  public static boolean isWarningEnabled() {
    return getInstance().getDebuglevel() >= LogTarget.WARN;
  }
  /**
   * Returns true, if the log level allows error messages to be printed.
   * 
   * @return true, if messages with an log level of ERROR are allowed.
   */
  public static boolean isErrorEnabled() {
    return getInstance().getDebuglevel() >= LogTarget.ERROR;
  }
  /**
   * Creates a log context.
   * 
   * @param context
   *          the class (<code>null</code> not permitted).
   * 
   * @return A log context.
   */
  public static LogContext createContext(final Class context) {
    return createContext(context.getName());
  }
  /**
   * Creates a log context.
   * 
   * @param context
   *          the label for the context.
   * 
   * @return A log context.
   */
  public static LogContext createContext(final String context) {
    return getInstance().internalCreateContext(context);
  }
  /**
   * Creates a log context.
   * 
   * @param context
   *          the name of the logging context (a common prefix).
   * 
   * @return A log context.
   */
  protected LogContext internalCreateContext(final String context) {
    synchronized (this) {
      LogContext ctx = (LogContext) this.logContexts.get(context);
      if (ctx == null) {
        ctx = new LogContext(context);
        this.logContexts.put(context, ctx);
      }
      return ctx;
    }
  }
}
/*
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 * 
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info: http://www.jfree.org/jcommon/index.html
 * 
 * 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 (at your option)
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. in the
 * United States and other countries.]
 * 
 * -------------- LogTarget.java -------------- (C)opyright 2002-2004, by Object
 * Refinery Limited.
 * 
 * $Id: LogTarget.java,v 1.3 2005/11/14 10:55:59 mungady Exp $
 * 
 * Changes ------- 11-May-2002 : Initial version 06-Dec-2002 : LogTargets now
 * use Object-Parameters instead of Strings. 05-Feb-2003 : Removed unnecessary
 * methods. 29-Apr-2003 : Distilled from the JFreeReport project and moved into
 * JCommon
 * 
 */
/**
 * An interface that defines a log target (a consumer of log messages). Classes
 * which implement this interface can be registered with the
 * {@link org.jfree.util.Log} class and will then receive logging messages
 * generated by the code.
 * 
 * @author Thomas Morgner
 */
interface LogTarget {
  /**
   * Loglevel ERROR.
   */
  public static final int ERROR = 0;
  /**
   * Loglevel WARN.
   */
  public static final int WARN = 1;
  /**
   * Loglevel INFO.
   */
  public static final int INFO = 2;
  /**
   * Loglevel DEBUG.
   */
  public static final int DEBUG = 3;
  /** Strings for the log levels. */
  public static final String[] LEVELS = { "ERROR: ", "WARN:  ", "INFO:  ", "DEBUG: " };
  /**
   * Logs a message at a specified log level.
   * 
   * @param level
   *          the log level.
   * @param message
   *          the log message.
   */
  public void log(int level, Object message);
  /**
   * Logs a message at a specified log level.
   * 
   * @param level
   *          the log level.
   * @param message
   *          the log message.
   * @param e
   *          the exception
   */
  public void log(int level, Object message, Exception e);
}
/*
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 * 
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info: http://www.jfree.org/jcommon/index.html
 * 
 * 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 (at your option)
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. in the
 * United States and other countries.]
 * 
 * --------------- LogContext.java --------------- (C)opyright 2004, by Thomas
 * Morgner and Contributors.
 * 
 * Original Author: Thomas Morgner; Contributor(s): David Gilbert (for Object
 * Refinery Limited);
 * 
 * $Id: LogContext.java,v 1.3 2005/10/18 13:24:19 mungady Exp $
 * 
 * Changes ------- 26-Apr-2004 : Initial version (TM);
 * 
 */
/**
 * A log context.
 * 
 * @author Thomas Morgner
 */
class LogContext {
  /** The prefix string. */
  private String contextPrefix;
  /**
   * Creates a new log context.
   * 
   * @param contextPrefix
   *          the prefix.
   */
  public LogContext(final String contextPrefix) {
    this.contextPrefix = contextPrefix;
  }
  /**
   * Returns true, if the log level allows debug messages to be printed.
   * 
   * @return true, if messages with an log level of DEBUG are allowed.
   */
  public boolean isDebugEnabled() {
    return Log.isDebugEnabled();
  }
  /**
   * Returns true, if the log level allows informational messages to be printed.
   * 
   * @return true, if messages with an log level of INFO are allowed.
   */
  public boolean isInfoEnabled() {
    return Log.isInfoEnabled();
  }
  /**
   * Returns true, if the log level allows warning messages to be printed.
   * 
   * @return true, if messages with an log level of WARN are allowed.
   */
  public boolean isWarningEnabled() {
    return Log.isWarningEnabled();
  }
  /**
   * Returns true, if the log level allows error messages to be printed.
   * 
   * @return true, if messages with an log level of ERROR are allowed.
   */
  public boolean isErrorEnabled() {
    return Log.isErrorEnabled();
  }
  /**
   * A convenience method for logging a "debug" message.
   * 
   * @param message
   *          the message.
   */
  public void debug(final Object message) {
    log(LogTarget.DEBUG, message);
  }
  /**
   * A convenience method for logging a "debug" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public void debug(final Object message, final Exception e) {
    log(LogTarget.DEBUG, message, e);
  }
  /**
   * A convenience method for logging an "info" message.
   * 
   * @param message
   *          the message.
   */
  public void info(final Object message) {
    log(LogTarget.INFO, message);
  }
  /**
   * A convenience method for logging an "info" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public void info(final Object message, final Exception e) {
    log(LogTarget.INFO, message, e);
  }
  /**
   * A convenience method for logging a "warning" message.
   * 
   * @param message
   *          the message.
   */
  public void warn(final Object message) {
    log(LogTarget.WARN, message);
  }
  /**
   * A convenience method for logging a "warning" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public void warn(final Object message, final Exception e) {
    log(LogTarget.WARN, message, e);
  }
  /**
   * A convenience method for logging an "error" message.
   * 
   * @param message
   *          the message.
   */
  public void error(final Object message) {
    log(LogTarget.ERROR, message);
  }
  /**
   * A convenience method for logging an "error" message.
   * 
   * @param message
   *          the message.
   * @param e
   *          the exception.
   */
  public void error(final Object message, final Exception e) {
    log(LogTarget.ERROR, message, e);
  }
  /**
   * Logs a message to the main log stream. All attached log targets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done.
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   */
  public void log(final int level, final Object message) {
    if (this.contextPrefix != null) {
      Log.getInstance().doLog(level, new Log.SimpleMessage(this.contextPrefix, ":", message));
    } else {
      Log.getInstance().doLog(level, message);
    }
  }
  /**
   * Logs a message to the main log stream. All attached logTargets will also
   * receive this message. If the given log-level is higher than the given
   * debug-level in the main config file, no logging will be done. <p/> The
   * exception"s stacktrace will be appended to the log-stream
   * 
   * @param level
   *          log level of the message.
   * @param message
   *          text to be logged.
   * @param e
   *          the exception, which should be logged.
   */
  public void log(final int level, final Object message, final Exception e) {
    if (this.contextPrefix != null) {
      Log.getInstance().doLog(level, new Log.SimpleMessage(this.contextPrefix, ":", message), e);
    } else {
      Log.getInstance().doLog(level, message, e);
    }
  }
  /**
   * Tests this object for equality with an arbitrary object.
   * 
   * @param o
   *          the object to test against (<code>null</code> permitted).
   * 
   * @return A boolean.
   */
  public boolean equals(final Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof LogContext)) {
      return false;
    }
    final LogContext logContext = (LogContext) o;
    if (this.contextPrefix != null) {
      if (!this.contextPrefix.equals(logContext.contextPrefix)) {
        return false;
      }
    } else {
      if (logContext.contextPrefix != null) {
        return false;
      }
    }
    return true;
  }
  /**
   * Returns a hashcode.
   * 
   * @return The hashcode.
   */
  public int hashCode() {
    return (this.contextPrefix != null ? this.contextPrefix.hashCode() : 0);
  }
}





Count Up Down Latch

/* Copyright (c) 2001-2009, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.util.concurrent.CountDownLatch;
public class CountUpDownLatch {
    CountDownLatch latch;
    int            count;
    public CountUpDownLatch() {
        latch      = new CountDownLatch(1);
        this.count = count;
    }
    public void await() throws InterruptedException {
        if (count == 0) {
            return;
        }
        latch.await();
    }
    public void countDown() {
        count--;
        if (count == 0) {
            latch.countDown();
        }
    }
    public long getCount() {
        return count;
    }
    public void countUp() {
        if (latch.getCount() == 0) {
            latch = new CountDownLatch(1);
        }
        count++;
    }
    public void setCount(int count) {
        if (count == 0) {
            if (latch.getCount() != 0) {
                latch.countDown();
            }
        } else if (latch.getCount() == 0) {
            latch = new CountDownLatch(1);
        }
        this.count = count;
    }
}





Debug Utility

/*
 * Copyright (c) 1998-2002 Carnegie Mellon University.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS"" AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public abstract class Debug {
    public static final Debug VERBOSE = new Verbose ();
    public static final Debug QUIET = new Quiet ();
    public static final Debug NONE = new NoDebug ();
    public static Debug getDebugLevel (Class cls)
        throws NoSuchFieldException {
        try {
            Field fld = cls.getField ("debug");
            if (fld.getType () != Debug.class
                || !Modifier.isStatic (fld.getModifiers ()))
                throw new NoSuchFieldException ();
            return (Debug) fld.get (null);
        } catch (IllegalArgumentException e) {
            throw new NoSuchFieldException ();
        } catch (IllegalAccessException e) {
            throw new NoSuchFieldException ();
        } catch (SecurityException e) {
            throw new NoSuchFieldException ();
        }
    }
    public static void setDebugLevel (Class cls, Debug level) 
        throws NoSuchFieldException {
        try {
            Field fld = cls.getField ("debug");
            if (fld.getType () != Debug.class
                || !Modifier.isStatic (fld.getModifiers ()))
                throw new NoSuchFieldException ();
            fld.set (null, level);
        } catch (IllegalArgumentException e) {
            throw new NoSuchFieldException ();
        } catch (IllegalAccessException e) {
            throw new NoSuchFieldException ();
        } catch (SecurityException e) {
            throw new NoSuchFieldException ();
        }
    }
    public abstract boolean isEnabled ();
    public abstract void print (String message);
    public abstract void println (String message);
    public abstract void print (Object obj);
    public abstract void println (Object obj);
    public abstract void report (Throwable t);
    public abstract void printThreadInfo ();
    public abstract void printStackTrace ();
    public abstract void assertion (boolean f);
    public static class Verbose extends Debug { 
        protected PrintStream out;
        public Verbose () {
            this (System.err);
        }
        public Verbose (PrintStream out) {
            this.out = out;
        }
        public boolean isEnabled () {
            return true;
        }
        public void print (String message) {
            out.print (message);
            out.flush ();
        }
        public void println (String message) {
            out.println (message);
            out.flush ();
        }
        public void print (Object obj) {
            print (obj.toString ());
        }
        public void println (Object obj) {
            println (obj.toString ());
        }
        public void report (Throwable t) {
            t.printStackTrace (out);
            out.flush ();
        }
        public void printThreadInfo () {
            ThreadGroup g = Thread.currentThread().getThreadGroup ();
            Thread[] t = new Thread[g.activeCount ()];
            g.enumerate (t);
            out.println ("Active threads in " + g);
            for (int i=0; i<t.length; ++i)
                out.println (t[i]);
            out.flush ();
        }
        public void printStackTrace () {
            try {
                throw new Exception ();
            } catch (Exception e) {
                e.printStackTrace (out);
                out.flush ();
            }
        }
        public void assertion (boolean f) {
            if (!f)
                throw new RuntimeException ("assertion failure");
        }
    }
    public static class Quiet extends Verbose { 
        public Quiet () {
        }
        public Quiet (PrintStream out) {
            super (out);
        }
        public boolean isEnabled () {
            return false;
        }
        public void print (String message) {
        }
        public void println (String message) {
        }
        public void print (Object message) {
        }
        public void println (Object message) {
        }
        public void report (Throwable t) {
            t.printStackTrace (out);
            out.flush ();
        }
        public void printThreadInfo () {
        }
        public void printStackTrace () {
        }
        public void assertion (boolean f) {
            if (!f) {
                try {
                    throw new RuntimeException ("assertion failure");
                } catch (RuntimeException e) {
                    e.printStackTrace (out);
                    out.flush ();
                }
            }
        }
    }
    public static class NoDebug extends Debug { 
        public boolean isEnabled () {
            return false;
        }
        public void print (String message) {
        }
        public void println (String message) {
        }
        public void print (Object message) {
        }
        public void println (Object message) {
        }
        public void report (Throwable t) {
        }
        public void printThreadInfo () {
        }
        public void printStackTrace () {
        }
        public void assertion (boolean f) {
        }
    }
}





Methods for logging events

/*
 * Log.java - A class for logging events
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1999, 2003 Slava Pestov
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//{{{ Imports
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Writer;
import java.text.DateFormat;
import java.util.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import static java.text.DateFormat.MEDIUM;
//}}}
/**
 * This class provides methods for logging events. In terms of functionality,
 * it is somewhere in between <code>System.out.println()</code> and
 * full-blown logging packages such as log4j.
 *
 * All events are logged to an in-memory buffer and optionally a stream,
 * and those with a high urgency (warnings and errors) are also printed
 * to standard output.
 *
 * Logging of exception tracebacks is supported.
 *
 * This class can also optionally redirect standard output and error to the log.
 *
 * @author Slava Pestov
 * @version $Id: Log.java 12789 2008-06-04 21:23:10Z kpouer $
 */
public class Log
{
  //{{{ Constants
  /**
   * The maximum number of log messages that will be kept in memory.
   * @since jEdit 2.6pre5
   */
  public static final int MAXLINES = 500;
  /**
   * Debugging message urgency. Should be used for messages only
   * useful when debugging a problem.
   * @since jEdit 2.2pre2
   */
  public static final int DEBUG = 1;
  /**
   * Message urgency. Should be used for messages which give more
   * detail than notices.
   * @since jEdit 2.2pre2
   */
  public static final int MESSAGE = 3;
  /**
   * Notice urgency. Should be used for messages that directly
   * affect the user.
   * @since jEdit 2.2pre2
   */
  public static final int NOTICE = 5;
  /**
   * Warning urgency. Should be used for messages that warrant
   * attention.
   * @since jEdit 2.2pre2
   */
  public static final int WARNING = 7;
  /**
   * Error urgency. Should be used for messages that signal a
   * failure.
   * @since jEdit 2.2pre2
   */
  public static final int ERROR = 9;
  //}}}
  //{{{ init() method
  /**
   * Initializes the log.
   * @param stdio If true, standard output and error will be
   * sent to the log
   * @param level Messages with this log level or higher will
   * be printed to the system console
   * @since jEdit 3.2pre4
   */
  public static void init(boolean stdio, int level)
  {
    if(stdio)
    {
      if(System.out == realOut && System.err == realErr)
      {
        System.setOut(createPrintStream(NOTICE,null));
        System.setErr(createPrintStream(ERROR,null));
      }
    }
    Log.level = level;
    // Log some stuff
    log(MESSAGE,Log.class,"When reporting bugs, please"
      + " include the following information:");
    String[] props = {
      "java.version", "java.vm.version", "java.runtime.version",
      "java.vendor", "java.rupiler", "os.name", "os.version",
      "os.arch", "user.home", "java.home",
      "java.class.path",
      };
    for(int i = 0; i < props.length; i++)
    {
      log(MESSAGE,Log.class,
        props[i] + "=" + System.getProperty(props[i]));
    }
  } //}}}
  //{{{ setLogWriter() method
  /**
   * Writes all currently logged messages to this stream if there was no
   * stream set previously, and sets the stream to write future log
   * messages to.
   * @param stream The writer
   * @since jEdit 3.2pre4
   */
  public static void setLogWriter(Writer stream)
  {
    if(Log.stream == null && stream != null)
    {
      try
      {
        if(wrap)
        {
          for(int i = logLineCount; i < log.length; i++)
          {
            stream.write(log[i]);
            stream.write(lineSep);
          }
        }
        for(int i = 0; i < logLineCount; i++)
        {
          stream.write(log[i]);
          stream.write(lineSep);
        }
        stream.flush();
      }
      catch(Exception e)
      {
        // do nothing, who cares
      }
    }
    Log.stream = stream;
  } //}}}
  //{{{ flushStream() method
  /**
   * Flushes the log stream.
   * @since jEdit 2.6pre5
   */
  public static void flushStream()
  {
    if(stream != null)
    {
      try
      {
        stream.flush();
      }
      catch(IOException io)
      {
        io.printStackTrace(realErr);
      }
    }
  } //}}}
  //{{{ closeStream() method
  /**
   * Closes the log stream. Should be done before your program exits.
   * @since jEdit 2.6pre5
   */
  public static void closeStream()
  {
    if(stream != null)
    {
      try
      {
        stream.close();
        stream = null;
      }
      catch(IOException io)
      {
        io.printStackTrace(realErr);
      }
    }
  } //}}}
  //{{{ getLogListModel() method
  /**
   * Returns the list model for viewing the log contents.
   * @since jEdit 4.2pre1
   */
  public static ListModel getLogListModel()
  {
    return listModel;
  } //}}}
  //{{{ log() method
  /**
   * Logs an exception with a message.
   *
   * If an exception is the cause of a call to {@link #log}, then
   * the exception should be explicitly provided so that it can
   * be presented to the (debugging) user in a useful manner
   * (not just the exception message, but also the exception stack trace)
   *
   * @since jEdit 4.3pre5
   */
  public static void log(int urgency, Object source, Object message,
    Throwable exception)
  {
    // We can do nicer here, but this is a start...
    log(urgency,source,message);
    log(urgency,source,exception);
  } //}}}
  //{{{ log() method
  /**
   * Logs a message. This method is thread-safe.
   *
   * The following code sends a typical debugging message to the activity
   * log:
   * <pre>Log.log(Log.DEBUG,this,"counter = " + counter);</pre>
   * The corresponding activity log entry might read as follows:
   * <pre>[debug] JavaParser: counter = 15</pre>
   *
   * @param urgency The urgency; can be one of
   * <code>Log.DEBUG</code>, <code>Log.MESSAGE</code>,
   * <code>Log.NOTICE</code>, <code>Log.WARNING</code>, or
   * <code>Log.ERROR</code>.
   * @param source The source of the message, either an object or a
   * class instance. When writing log messages from macros, set
   * this parameter to <code>BeanShell.class</code> to make macro
   * errors easier to spot in the activity log.
   * @param message The message. This can either be a string or
   * an exception
   *
   * @since jEdit 2.2pre2
   */
  public static void log(int urgency, Object source, Object message)
  {
    String _source;
    if(source == null)
    {
      _source = Thread.currentThread().getName();
      if(_source == null)
      {
        _source = Thread.currentThread().getClass().getName();
      }
    }
    else if(source instanceof Class)
      _source = ((Class)source).getName();
    else
      _source = source.getClass().getName();
    int index = _source.lastIndexOf(".");
    if(index != -1)
      _source = _source.substring(index+1);
    if(message instanceof Throwable)
    {
      _logException(urgency,source,(Throwable)message);
    }
    else
    {
      String _message = String.valueOf(message);
      // If multiple threads log stuff, we don"t want
      // the output to get mixed up
      synchronized(LOCK)
      {
        StringTokenizer st = new StringTokenizer(
          _message,"\r\n");
        int lineCount = 0;
        boolean oldWrap = wrap;
        while(st.hasMoreTokens())
        {
          lineCount++;
          _log(urgency,_source,st.nextToken()
            .replace("\t"," "));
        }
        listModel.update(lineCount,oldWrap);
      }
    }
  } //}}}
  //{{{ Private members
  //{{{ Instance variables
  private static final Object LOCK;
  private static final String[] log;
  private static int logLineCount;
  private static boolean wrap;
  private static int level;
  private static Writer stream;
  private static final String lineSep;
  private static final PrintStream realOut;
  private static final PrintStream realErr;
  private static final LogListModel listModel;
  private static final DateFormat timeFormat;
  private static final int MAX_THROWABLES = 10;
  public static final List<Throwable> throwables;
  //}}}
  //{{{ Class initializer
  static
  {
    LOCK = new Object();
    level = WARNING;
    realOut = System.out;
    realErr = System.err;
    log = new String[MAXLINES];
    lineSep = System.getProperty("line.separator");
    listModel = new LogListModel();
    
    timeFormat = DateFormat.getTimeInstance(MEDIUM);
    throwables = Collections.synchronizedList(new ArrayList<Throwable>(MAX_THROWABLES));
  } //}}}
  //{{{ createPrintStream() method
  private static PrintStream createPrintStream(final int urgency,
    final Object source)
  {
    return new LogPrintStream(urgency, source);
  } //}}}
  //{{{ _logException() method
  private static void _logException(final int urgency,
    final Object source,
    final Throwable message)
  {
    PrintStream out = createPrintStream(urgency,source);
    if (urgency >= level)
    {
      synchronized (throwables)
      {
        if (throwables.size() == MAX_THROWABLES)
        {
          throwables.remove(0);
        }
        throwables.add(message);
      }
    }
    synchronized(LOCK)
    {
      message.printStackTrace(out);
    }
  } //}}}
  //{{{ _log() method
  private static void _log(int urgency, String source, String message)
  {
    String fullMessage = timeFormat.format(new Date()) + " ["+Thread.currentThread().getName()+"] [" + urgencyToString(urgency) + "] " + source
      + ": " + message;
    try
    {
      log[logLineCount] = fullMessage;
      if(++logLineCount >= log.length)
      {
        wrap = true;
        logLineCount = 0;
      }
      if(stream != null)
      {
        stream.write(fullMessage);
        stream.write(lineSep);
      }
    }
    catch(Exception e)
    {
      e.printStackTrace(realErr);
    }
    if(urgency >= level)
    {
      if(urgency == ERROR)
        realErr.println(fullMessage);
      else
        realOut.println(fullMessage);
    }
  } //}}}
  //{{{ urgencyToString() method
  private static String urgencyToString(int urgency)
  {
    switch(urgency)
    {
    case DEBUG:
      return "debug";
    case MESSAGE:
      return "message";
    case NOTICE:
      return "notice";
    case WARNING:
      return "warning";
    case ERROR:
      return "error";
    }
    throw new IllegalArgumentException("Invalid urgency: " + urgency);
  } //}}}
  //}}}
  //{{{ LogListModel class
  static class LogListModel implements ListModel
  {
    final List<ListDataListener> listeners = new ArrayList<ListDataListener>();
    //{{{ fireIntervalAdded() method
    private void fireIntervalAdded(int index1, int index2)
    {
      for(int i = 0; i < listeners.size(); i++)
      {
        ListDataListener listener = listeners.get(i);
        listener.intervalAdded(new ListDataEvent(this,
          ListDataEvent.INTERVAL_ADDED,
          index1,index2));
      }
    } //}}}
    //{{{ fireIntervalRemoved() method
    private void fireIntervalRemoved(int index1, int index2)
    {
      for(int i = 0; i < listeners.size(); i++)
      {
        ListDataListener listener = listeners.get(i);
        listener.intervalRemoved(new ListDataEvent(this,
          ListDataEvent.INTERVAL_REMOVED,
          index1,index2));
      }
    } //}}}
    //{{{ addListDataListener() method
    public void addListDataListener(ListDataListener listener)
    {
      listeners.add(listener);
    } //}}}
    //{{{ removeListDataListener() method
    public void removeListDataListener(ListDataListener listener)
    {
      listeners.remove(listener);
    } //}}}
    //{{{ getElementAt() method
    public Object getElementAt(int index)
    {
      if(wrap)
      {
        if(index < MAXLINES - logLineCount)
          return log[index + logLineCount];
        else
          return log[index - MAXLINES + logLineCount];
      }
      else
        return log[index];
    } //}}}
    //{{{ getSize() method
    public int getSize()
    {
      if(wrap)
        return MAXLINES;
      else
        return logLineCount;
    } //}}}
    //{{{ update() method
    void update(final int lineCount, final boolean oldWrap)
    {
      if(lineCount == 0 || listeners.isEmpty())
        return;
      SwingUtilities.invokeLater(new Runnable()
      {
        public void run()
        {
          if(wrap)
          {
            if(oldWrap)
              fireIntervalRemoved(0,lineCount - 1);
            else
            {
              fireIntervalRemoved(0,
                logLineCount);
            }
            fireIntervalAdded(
              MAXLINES - lineCount + 1,
              MAXLINES);
          }
          else
          {
            fireIntervalAdded(
              logLineCount - lineCount + 1,
              logLineCount);
          }
        }
      });
    } //}}}
  } //}}}
  //{{{ LogPrintStream class
  /**
   * A print stream that uses the "Log" class to output the messages,
   * and has special treatment for the printf() function. Using this
   * stream has one caveat: printing messages that don"t have a line
   * break at the end will have one added automatically...
   */
  private static class LogPrintStream extends PrintStream {
    private final ByteArrayOutputStream buffer;
    private final OutputStream orig;
    //{{{ LogPrintStream constructor
    LogPrintStream(int urgency, Object source)
    {
      super(new LogOutputStream(urgency, source));
      buffer = new ByteArrayOutputStream();
      orig = out;
    } //}}}
    //{{{ printf() method
    /**
     * This is a hack to allow "printf" to not print weird
     * stuff to the output. Since "printf" doesn"t seem to
     * print the whole message in one shot, our output
     * stream above would break a line of log into several
     * lines; so we buffer the result of the printf call and
     * print the whole thing in one shot. A similar hack
     * would be needed for the "other" printf method, but
     * I"ll settle for the common case only.
     */
    public PrintStream printf(String format, Object... args)
    {
      synchronized (orig)
      {
        buffer.reset();
        out = buffer;
        super.printf(format, args);
        try
        {
          byte[] data = buffer.toByteArray();
          orig.write(data, 0, data.length);
          out = orig;
        }
        catch (IOException ioe)
        {
          // don"t do anything?
        }
        finally
        {
          buffer.reset();
        }
      }
      return this;
    } //}}}
  } //}}}
  //{{{ LogOutputStream class
  private static class LogOutputStream extends OutputStream
  {
    private final int   urgency;
    private final Object  source;
    //{{{ LogOutputStream constructor
    LogOutputStream(int urgency, Object source)
    {
      this.urgency  = urgency;
      this.source   = source;
    } //}}}
    //{{{ write() method
    public synchronized void write(int b)
    {
      byte[] barray = { (byte)b };
      write(barray,0,1);
    } //}}}
    //{{{ write() method
    public synchronized void write(byte[] b, int off, int len)
    {
      String str = new String(b,off,len);
      log(urgency,source,str);
    } //}}}
  } //}}}
}





Printing indented text

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.PrintWriter;
/**
 * A helper class for printing indented text
 *
 * @version $Revision: 1.2 $
 */
public class IndentPrinter {
    private int indentLevel;
    private String indent;
    private PrintWriter out;
    public IndentPrinter() {
        this(new PrintWriter(System.out), "  ");
    }
    public IndentPrinter(PrintWriter out) {
        this(out, "  ");
    }
    public IndentPrinter(PrintWriter out, String indent) {
        this.out = out;
        this.indent = indent;
    }
    public void println(Object value) {
        out.print(value.toString());
        out.println();
    }
    public void println(String text) {
        out.print(text);
        out.println();
    }
    public void print(String text) {
        out.print(text);
    }
    public void printIndent() {
        for (int i = 0; i < indentLevel; i++) {
            out.print(indent);
        }
    }
    public void println() {
        out.println();
    }
    public void incrementIndent() {
        ++indentLevel;
    }
    public void decrementIndent() {
        --indentLevel;
    }
    public int getIndentLevel() {
        return indentLevel;
    }
    public void setIndentLevel(int indentLevel) {
        this.indentLevel = indentLevel;
    }
    public void flush() {
        out.flush();
    }
}





Prints messages formatted for a specific line width.

/*
 * $RCSfile: MsgPrinter.java,v $
 * $Revision: 1.1 $
 * $Date: 2005/02/11 05:02:26 $
 * $State: Exp $
 *
 * Class:                   MsgPrinter
 *
 * Description:             Prints messages formatted for a specific
 *                          line width.
 *
 *
 *
 * COPYRIGHT:
 *
 * This software module was originally developed by Rapha&euml;l Grosbois and
 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
 * Askel&ouml;f (Ericsson Radio Systems AB); and Bertrand Berthelot, David
 * Bouchard, F&eacute;lix Henry, Gerard Mozelle and Patrice Onno (Canon Research
 * Centre France S.A) in the course of development of the JPEG2000
 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
 * software module is an implementation of a part of the JPEG 2000
 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
 * Partners) agree not to assert against ISO/IEC and users of the JPEG
 * 2000 Standard (Users) any of their rights under the copyright, not
 * including other intellectual property rights, for this software module
 * with respect to the usage by ISO/IEC and Users of this software module
 * or modifications thereof for use in hardware or software products
 * claiming conformance to the JPEG 2000 Standard. Those intending to use
 * this software module in hardware or software products are advised that
 * their use may infringe existing patents. The original developers of
 * this software module, JJ2000 Partners and ISO/IEC assume no liability
 * for use of this software module or modifications thereof. No license
 * or right to this software module is granted for non JPEG 2000 Standard
 * conforming products. JJ2000 Partners have full right to use this
 * software module for his/her own purpose, assign or donate this
 * software module to any third party and to inhibit third parties from
 * using this software module for non JPEG 2000 Standard conforming
 * products. This copyright notice must be included in all copies or
 * derivative works of this software module.
 *
 * Copyright (c) 1999/2000 JJ2000 Partners.
 *
 *
 *
 */


import java.io.*;
/**
 * This utility class formats messages to the specified line width, by
 * inserting line-breaks between words, and printing the resulting
 * lines.
 * */
public class MsgPrinter {
    /** The line width to use */
    public int lw;
    /** Signals that a newline was found */
    private static final int IS_NEWLINE = -2;
    /** Signals that the end-of-string was reached */
    private static final int IS_EOS = -1;
    /**
     * Creates a new message printer with the specified line width and
     * with the default locale.
     *
     * @param linewidth The line width for which to format (in
     * characters)
     *
     *
     * */
    public MsgPrinter(int linewidth) {
        lw = linewidth;
    }
    /**
     * Returns the line width that is used for formatting.
     *
     * @return The line width used for formatting
     *
     *
     * */
    public int getLineWidth() {
        return lw;
    }
    /**
     * Sets the line width to the specified value. This new value will
     * be used in subsequent calls to the print() message.
     *
     * @param linewidth The new line width to use (in cahracters)
     *
     *
     * */
    public void setLineWidth(int linewidth) {
        if (linewidth <1) {
            throw new IllegalArgumentException();
        }
        lw = linewidth;
    }
    /**
     * Formats the message to print in the current line width, by
     * breaking the message into lines between words. The number of
     * spaces to indent the first line is specified by "flind" and the
     * number of spaces to indent each of the following lines is
     * specified by "ind". Newlines in "msg" are respected. A newline is
     * always printed at the end.
     *
     * @param out Where to print the message.
     *
     * @param flind The indentation for the first line.
     *
     * @param ind The indentation for the other lines.
     *
     * @param msg The message to format and print.
     *
     *
     * */
    public void print(PrintWriter out, int flind, int ind,
                      String msg) {
        int start,end,pend,efflw,lind,i;
        start = 0;
        end = 0;
        pend = 0;
        efflw = lw-flind;
        lind = flind;
        while ((end = nextLineEnd(msg,pend)) != IS_EOS) {
            if (end == IS_NEWLINE) { // Forced line break
                for (i=0; i<lind; i++) {
                    out.print(" ");
                }
                out.println(msg.substring(start,pend));
                if (nextWord(msg,pend) == msg.length()) {
                    // Traling newline => print it and done
                    out.println("");
                    start = pend;
                    break;
                }
            }
            else {
                if (efflw > end-pend) { // Room left on current line
                    efflw -= end-pend;
                    pend = end;
                    continue;
                }
                else { // Filled-up current line => print it
                    for (i=0; i<lind; i++) {
                        out.print(" ");
                    }
                    if (start == pend) { // Word larger than line width
                        // Print anyways
                        out.println(msg.substring(start,end));
                        pend = end;
                    }
                    else {
                        out.println(msg.substring(start,pend));
                    }
                }
            }
            // Initialize for next line
            lind = ind;
            efflw = lw-ind;
            start = nextWord(msg,pend);
            pend = start;
            if (start == IS_EOS) {
                break; // Did all the string
            }
        }
        if (pend != start) { // Part of a line left => print it
            for (i=0; i<lind; i++) {
                out.print(" ");
            }
            out.println(msg.substring(start,pend));
        }
    }
    /**
     * Returns the index of the last character of the next word, plus 1, or
     * IS_NEWLINE if a newline character is encountered before the next word,
     * or IS_EOS if the end of the string is ecnounterd before the next
     * word. The method first skips all whitespace characters at or after
     * "from", except newlines. If a newline is found IS_NEWLINE is
     * returned. Then it skips all non-whitespace characters and returns the
     * position of the last non-whitespace character, plus 1. The returned
     * index may be greater than the last valid index in the tsring, but it is
     * always suitable to be used in the String.substring() method.
     *
     * Non-whitespace characters are defined as in the
     * Character.isWhitespace method (that method is used).
     *
     * @param str The string to parse
     *
     * @param from The index of the first position to search from
     *
     * @return The index of the last character in the next word, plus 1,
     * IS_NEWLINE, or IS_EOS if there are no more words.
     *
     *
     * */
    private int nextLineEnd(String str, int from) {
        final int len = str.length();
        char c = "\0";
        // First skip all whitespace, except new line
        while (from < len && (c = str.charAt(from)) != "\n" &&
               Character.isWhitespace(c)) {
            from++;
        }
        if (c == "\n") {
            return IS_NEWLINE;
        }
        if (from >= len) {
            return IS_EOS;
        }
        // Now skip word characters
        while (from < len && !Character.isWhitespace(str.charAt(from))) {
            from++;
        }
        return from;
    }
    /**
     * Returns the position of the first character in the next word, starting
     * from "from", if a newline is encountered first then the index of the
     * newline character plus 1 is returned. If the end of the string is
     * encountered then IS_EOS is returned. Words are defined as any
     * concatenation of 1 or more characters which are not
     * whitespace. Whitespace characters are those for which
     * Character.isWhitespace() returns true (that method is used).
     *
     * Non-whitespace characters are defined as in the
     * Character.isWhitespace method (that method is used).
     *
     * @param str The string to parse
     *
     * @param from The index where to start parsing
     *
     * @return The index of the first character of the next word, or the index
     * of the newline plus 1, or IS_EOS.
     *
     *
     * */
    private int nextWord(String str, int from) {
        final int len = str.length();
        char c = "\0";
        // First skip all whitespace, but new lines
        while (from < len && (c = str.charAt(from)) != "\n" &&
               Character.isWhitespace(c)) {
            from++;
        }
        if (from >= len) {
            return IS_EOS;
        }
        else if (c == "\n") {
            return from+1;
        }
        else {
            return from;
        }
    }
}





Random data for test

/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.Random;
/**
 * @author Joe Walker [joe at getahead dot ltd dot uk]
 */
public class RandomData
{
    /**
     * @param isUS US numbers look different to UK ones
     * @return A phone number
     */
    public static String getPhoneNumber(boolean isUS)
    {
        String phoneNumber;
        if (isUS)
        {
            // US
            phoneNumber = "+1 (" + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + ") "
                + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + " - "
                + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9);
        }
        else
        {
            // UK
            phoneNumber = "+44 (0) 1" + random.nextInt(9) + random.nextInt(9) + random.nextInt(9)
                + " " + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9)
                + random.nextInt(9) + random.nextInt(9);
        }
        return phoneNumber;
    }
    public static String getFirstName()
    {
        return FIRSTNAMES[random.nextInt(FIRSTNAMES.length)];
    }
    public static String getSurname()
    {
        return SURNAMES[random.nextInt(SURNAMES.length)];
    }
    public static String getFullName()
    {
        return getFirstName() + " " + getSurname();
    }
    public static String getAddress()
    {
        String housenum = (random.nextInt(99) + 1) + " ";
        String road1 = ROADS1[random.nextInt(ROADS1.length)];
        String road2 = ROADS2[random.nextInt(ROADS2.length)];
        int townNum = random.nextInt(TOWNS.length);
        String town = TOWNS[townNum];
        return housenum + road1 + " " + road2 + ", " + town;
    }
    public static String[] getAddressAndNumber()
    {
        String[] reply = new String[2];
        String housenum = (random.nextInt(99) + 1) + " ";
        String road1 = ROADS1[random.nextInt(ROADS1.length)];
        String road2 = ROADS2[random.nextInt(ROADS2.length)];
        int townNum = random.nextInt(TOWNS.length);
        String town = TOWNS[townNum];
        reply[0] = housenum + road1 + " " + road2 + ", " + town;
        reply[1] = getPhoneNumber(townNum < 5);
        return reply;
    }
    public static float getSalary()
    {
        return Math.round(10 + 90 * random.nextFloat()) * 1000;
    }
    private static final Random random = new Random();
    private static final String[] FIRSTNAMES =
    {
        "Fred", "Jim", "Shiela", "Jack", "Betty", "Jacob", "Martha", "Kelly",
        "Luke", "Matt", "Gemma", "Joe", "Ben", "Jessie", "Leanne", "Becky",
        "William", "Jo"
    };
    private static final String[] SURNAMES =
    {
        "Sutcliffe", "MacDonald", "Duckworth", "Smith", "Wisner", 
        "Nield", "Turton", "Trelfer", "Wilson", "Johnson", "Daniels",
        "Jones", "Wilkinson", "Wilton"
    };
    private static final String[] ROADS1 =
    {
        "Green", "Red", "Yellow", "Brown", "Blue", "Black", "White",
    };
    private static final String[] ROADS2 =
    {
        "Close", "Drive", "Street", "Avenue", "Crescent", "Road", "Place",
    };
    private static final String[] TOWNS =
    {
        "San Mateo", "San Francisco", "San Diego", "New York", "Atlanta",
        "Sandford", "York", "London", "Coventry", "Exeter", "Knowle",
    };
}





Swing Console

/*
 * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
 * 
 * http://izpack.org/
 * http://izpack.codehaus.org/
 * 
 * Copyright 2002 Jan Blok
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import javax.swing.text.Segment;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.util.Vector;
public final class Console
{
    public static final int INITIAL_WIDTH = 800;
    public static final int INITIAL_HEIGHT = 600;
    public static void main(String[] args)
    {
        Runtime rt = Runtime.getRuntime();
        Process p = null;
        try
        {
            /*
             * Start a new process in which to execute the commands in cmd, using the environment in
             * env and use pwd as the current working directory.
             */
            p = rt.exec(args);// , env, pwd);
            new Console(p);
            System.exit(p.exitValue());
        }
        catch (IOException e)
        {
            /*
             * Couldn"t even get the command to start. Most likely it couldn"t be found because of a
             * typo.
             */
            System.out.println("Error starting: " + args[0]);
            System.out.println(e);
        }
    }
    private StdOut so;
    private StdOut se;
    public String getOutputData()
    {
        if (so != null)
        {
            return so.getData();
        }
        else
        {
            return "";
        }
    }
    public String getErrorData()
    {
        if (se != null)
        {
            return se.getData();
        }
        else
        {
            return "";
        }
    }
    public Console(Process p)
    {
        JFrame frame = new JFrame();
        frame.setTitle("Console");
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setLocation(screenSize.width / 2 - INITIAL_WIDTH / 2, screenSize.height / 2
                - INITIAL_HEIGHT / 2);
        ConsoleTextArea cta = new ConsoleTextArea();
        JScrollPane scroll = new JScrollPane(cta);
        scroll.setPreferredSize(new Dimension(INITIAL_WIDTH, INITIAL_HEIGHT));
        frame.getContentPane().add(scroll);
        frame.pack();
        // From here down your shell should be pretty much
        // as it is written here!
        /*
         * Start up StdOut, StdIn and StdErr threads that write the output generated by the process
         * p to the screen, and feed the keyboard input into p.
         */
        so = new StdOut(p, cta);
        se = new StdOut(p, cta);
        StdIn si = new StdIn(p, cta);
        so.start();
        se.start();
        si.start();
        // Wait for the process p to complete.
        try
        {
            frame.setVisible(true);
            p.waitFor();
        }
        catch (InterruptedException e)
        {
            /*
             * Something bad happened while the command was executing.
             */
            System.out.println("Error during execution");
            System.out.println(e);
        }
        /*
         * Now signal the StdOut, StdErr and StdIn threads that the process is done, and wait for
         * them to complete.
         */
        try
        {
            so.done();
            se.done();
            si.done();
            so.join();
            se.join();
            si.join();
        }
        catch (InterruptedException e)
        {
            // Something bad happend to one of the Std threads.
            System.out.println("Error in StdOut, StdErr or StdIn.");
            System.out.println(e);
        }
        frame.setVisible(false);
    }
}
class StdIn extends Thread
{
    private BufferedReader kb;
    private boolean processRunning;
    private PrintWriter op;
    public StdIn(Process p, ConsoleTextArea cta)
    {
        setDaemon(true);
        InputStreamReader ir = new InputStreamReader(cta.getIn());
        kb = new BufferedReader(ir);
        BufferedOutputStream os = new BufferedOutputStream(p.getOutputStream());
        op = new PrintWriter((new OutputStreamWriter(os)), true);
        processRunning = true;
    }
    public void run()
    {
        try
        {
            while (kb.ready() || processRunning)
            {
                if (kb.ready())
                {
                    op.println(kb.readLine());
                }
            }
        }
        catch (IOException e)
        {
            System.err.println("Problem reading standard input.");
            System.err.println(e);
        }
    }
    public void done()
    {
        processRunning = false;
    }
}
class StdOut extends Thread
{
    private InputStreamReader output;
    private boolean processRunning;
    private ConsoleTextArea cta;
    private StringBuffer data;
    public StdOut(Process p, ConsoleTextArea cta)
    {
        setDaemon(true);
        output = new InputStreamReader(p.getInputStream());
        this.cta = cta;
        processRunning = true;
        data = new StringBuffer();
    }
    public void run()
    {
        try
        {
            /*
             * Loop as long as there is output from the process to be displayed or as long as the
             * process is still running even if there is presently no output.
             */
            while (output.ready() || processRunning)
            {
                // If there is output get it and display it.
                if (output.ready())
                {
                    char[] array = new char[255];
                    int num = output.read(array);
                    if (num != -1)
                    {
                        String s = new String(array, 0, num);
                        data.append(s);
                        SwingUtilities.invokeAndWait(new ConsoleWrite(cta, s));
                    }
                }
            }
        }
        catch (Exception e)
        {
            System.err.println("Problem writing to standard output.");
            System.err.println(e);
        }
    }
    public void done()
    {
        processRunning = false;
    }
    public String getData()
    {
        return data.toString();
    }
}
class ConsoleWrite implements Runnable
{
    private ConsoleTextArea textArea;
    private String str;
    public ConsoleWrite(ConsoleTextArea textArea, String str)
    {
        this.textArea = textArea;
        this.str = str;
    }
    public void run()
    {
        textArea.write(str);
    }
}
class ConsoleWriter extends java.io.OutputStream
{
    private ConsoleTextArea textArea;
    private StringBuffer buffer;
    public ConsoleWriter(ConsoleTextArea textArea)
    {
        this.textArea = textArea;
        buffer = new StringBuffer();
    }
    public synchronized void write(int ch)
    {
        buffer.append((char) ch);
        if (ch == "\n")
        {
            flushBuffer();
        }
    }
    public synchronized void write(char[] data, int off, int len)
    {
        for (int i = off; i < len; i++)
        {
            buffer.append(data[i]);
            if (data[i] == "\n")
            {
                flushBuffer();
            }
        }
    }
    public synchronized void flush()
    {
        if (buffer.length() > 0)
        {
            flushBuffer();
        }
    }
    public void close()
    {
        flush();
    }
    private void flushBuffer()
    {
        String str = buffer.toString();
        buffer.setLength(0);
        SwingUtilities.invokeLater(new ConsoleWrite(textArea, str));
    }
}
class ConsoleTextArea extends JTextArea implements KeyListener, DocumentListener
{
    /**
     *
     */
    private static final long serialVersionUID = 3258410625414475827L;
    private ConsoleWriter console1;
    private PrintStream out;
    private PrintStream err;
    private PrintWriter inPipe;
    private PipedInputStream in;
    private Vector<String> history;
    private int historyIndex = -1;
    private int outputMark = 0;
    public void select(int start, int end)
    {
        requestFocus();
        super.select(start, end);
    }
    public ConsoleTextArea()
    {
        super();
        history = new java.util.Vector<String>();
        console1 = new ConsoleWriter(this);
        ConsoleWriter console2 = new ConsoleWriter(this);
        out = new PrintStream(console1);
        err = new PrintStream(console2);
        PipedOutputStream outPipe = new PipedOutputStream();
        inPipe = new PrintWriter(outPipe);
        in = new PipedInputStream();
        try
        {
            outPipe.connect(in);
        }
        catch (IOException exc)
        {
            exc.printStackTrace();
        }
        getDocument().addDocumentListener(this);
        addKeyListener(this);
        setLineWrap(true);
        setFont(new Font("Monospaced", 0, 12));
    }
    void returnPressed()
    {
        Document doc = getDocument();
        int len = doc.getLength();
        Segment segment = new Segment();
        try
        {
            synchronized (doc)
            {
                doc.getText(outputMark, len - outputMark, segment);
            }
        }
        catch (javax.swing.text.BadLocationException ignored)
        {
            ignored.printStackTrace();
        }
        if (segment.count > 0)
        {
            history.addElement(segment.toString());
        }
        historyIndex = history.size();
        inPipe.write(segment.array, segment.offset, segment.count);
        append("\n");
        synchronized (doc)
        {
            outputMark = doc.getLength();
        }
        inPipe.write("\n");
        inPipe.flush();
        console1.flush();
    }
    public void eval(String str)
    {
        inPipe.write(str);
        inPipe.write("\n");
        inPipe.flush();
        console1.flush();
    }
    public void keyPressed(KeyEvent e)
    {
        int code = e.getKeyCode();
        if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT)
        {
            if (outputMark == getCaretPosition())
            {
                e.consume();
            }
        }
        else if (code == KeyEvent.VK_HOME)
        {
            int caretPos = getCaretPosition();
            if (caretPos == outputMark)
            {
                e.consume();
            }
            else if (caretPos > outputMark)
            {
                if (!e.isControlDown())
                {
                    if (e.isShiftDown())
                    {
                        moveCaretPosition(outputMark);
                    }
                    else
                    {
                        setCaretPosition(outputMark);
                    }
                    e.consume();
                }
            }
        }
        else if (code == KeyEvent.VK_ENTER)
        {
            returnPressed();
            e.consume();
        }
        else if (code == KeyEvent.VK_UP)
        {
            historyIndex--;
            if (historyIndex >= 0)
            {
                if (historyIndex >= history.size())
                {
                    historyIndex = history.size() - 1;
                }
                if (historyIndex >= 0)
                {
                    String str = history.elementAt(historyIndex);
                    int len = getDocument().getLength();
                    replaceRange(str, outputMark, len);
                    int caretPos = outputMark + str.length();
                    select(caretPos, caretPos);
                }
                else
                {
                    historyIndex++;
                }
            }
            else
            {
                historyIndex++;
            }
            e.consume();
        }
        else if (code == KeyEvent.VK_DOWN)
        {
            int caretPos = outputMark;
            if (history.size() > 0)
            {
                historyIndex++;
                if (historyIndex < 0)
                {
                    historyIndex = 0;
                }
                int len = getDocument().getLength();
                if (historyIndex < history.size())
                {
                    String str = history.elementAt(historyIndex);
                    replaceRange(str, outputMark, len);
                    caretPos = outputMark + str.length();
                }
                else
                {
                    historyIndex = history.size();
                    replaceRange("", outputMark, len);
                }
            }
            select(caretPos, caretPos);
            e.consume();
        }
    }
    public void keyTyped(KeyEvent e)
    {
        int keyChar = e.getKeyChar();
        if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */)
        {
            if (outputMark == getCaretPosition())
            {
                e.consume();
            }
        }
        else if (getCaretPosition() < outputMark)
        {
            setCaretPosition(outputMark);
        }
    }
    public void keyReleased(KeyEvent e)
    {
    }
    public synchronized void write(String str)
    {
        insert(str, outputMark);
        int len = str.length();
        outputMark += len;
        select(outputMark, outputMark);
    }
    public synchronized void insertUpdate(DocumentEvent e)
    {
        int len = e.getLength();
        int off = e.getOffset();
        if (outputMark > off)
        {
            outputMark += len;
        }
    }
    public synchronized void removeUpdate(DocumentEvent e)
    {
        int len = e.getLength();
        int off = e.getOffset();
        if (outputMark > off)
        {
            if (outputMark >= off + len)
            {
                outputMark -= len;
            }
            else
            {
                outputMark = off;
            }
        }
    }
    public void postUpdateUI()
    {
        // this attempts to cleanup the damage done by updateComponentTreeUI
        requestFocus();
        setCaret(getCaret());
        synchronized (this)
        {
            select(outputMark, outputMark);
        }
    }
    public void changedUpdate(DocumentEvent e)
    {
    }
    public InputStream getIn()
    {
        return in;
    }
    public PrintStream getOut()
    {
        return out;
    }
    public PrintStream getErr()
    {
        return err;
    }
}