mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			666 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			666 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Java
		
	
	
	
/* FileHandler.java -- a class for publishing log messages to log files
 | 
						|
   Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath 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, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath 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 GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package java.util.logging;
 | 
						|
 | 
						|
import gnu.java.lang.CPStringBuilder;
 | 
						|
 | 
						|
import java.io.File;
 | 
						|
import java.io.FileOutputStream;
 | 
						|
import java.io.FilterOutputStream;
 | 
						|
import java.io.IOException;
 | 
						|
import java.io.OutputStream;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.ListIterator;
 | 
						|
 | 
						|
/**
 | 
						|
 * A <code>FileHandler</code> publishes log records to a set of log
 | 
						|
 * files.  A maximum file size can be specified; as soon as a log file
 | 
						|
 * reaches the size limit, it is closed and the next file in the set
 | 
						|
 * is taken.
 | 
						|
 *
 | 
						|
 * <p><strong>Configuration:</strong> Values of the subsequent
 | 
						|
 * <code>LogManager</code> properties are taken into consideration
 | 
						|
 * when a <code>FileHandler</code> is initialized.  If a property is
 | 
						|
 * not defined, or if it has an invalid value, a default is taken
 | 
						|
 * without an exception being thrown.
 | 
						|
 *
 | 
						|
 * <ul>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.level</code> - specifies
 | 
						|
 *     the initial severity level threshold. Default value:
 | 
						|
 *     <code>Level.ALL</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.filter</code> - specifies
 | 
						|
 *     the name of a Filter class. Default value: No Filter.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.formatter</code> - specifies
 | 
						|
 *     the name of a Formatter class. Default value:
 | 
						|
 *     <code>java.util.logging.XMLFormatter</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.encoding</code> - specifies
 | 
						|
 *     the name of the character encoding. Default value:
 | 
						|
 *     the default platform encoding.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.limit</code> - specifies the number
 | 
						|
 *     of bytes a log file is approximately allowed to reach before it
 | 
						|
 *     is closed and the handler switches to the next file in the
 | 
						|
 *     rotating set.  A value of zero means that files can grow
 | 
						|
 *     without limit.  Default value: 0 (unlimited growth).</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.count</code> - specifies the number
 | 
						|
 *     of log files through which this handler cycles.  Default value:
 | 
						|
 *     1.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.pattern</code> - specifies a
 | 
						|
 *     pattern for the location and name of the produced log files.
 | 
						|
 *     See the section on <a href="#filePatterns">file name
 | 
						|
 *     patterns</a> for details.  Default value:
 | 
						|
 *     <code>"%h/java%u.log"</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>java.util.FileHandler.append</code> - specifies
 | 
						|
 *     whether the handler will append log records to existing
 | 
						|
 *     files, or whether the handler will clear log files
 | 
						|
 *     upon switching to them. Default value: <code>false</code>,
 | 
						|
 *     indicating that files will be cleared.</li>
 | 
						|
 *
 | 
						|
 * </ul>
 | 
						|
 *
 | 
						|
 * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
 | 
						|
 * The name and location and log files are specified with pattern
 | 
						|
 * strings. The handler will replace the following character sequences
 | 
						|
 * when opening log files:
 | 
						|
 *
 | 
						|
 * <p><ul>
 | 
						|
 * <li><code>/</code> - replaced by the platform-specific path name
 | 
						|
 *     separator.  This value is taken from the system property
 | 
						|
 *     <code>file.separator</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%t</code> - replaced by the platform-specific location of
 | 
						|
 *     the directory intended for temporary files.  This value is
 | 
						|
 *     taken from the system property <code>java.io.tmpdir</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%h</code> - replaced by the location of the home
 | 
						|
 *     directory of the current user.  This value is taken from the
 | 
						|
 *     system property <code>user.home</code>.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%g</code> - replaced by a generation number for
 | 
						|
 *     distinguisthing the individual items in the rotating set
 | 
						|
 *     of log files.  The generation number cycles through the
 | 
						|
 *     sequence 0, 1, ..., <code>count</code> - 1.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%u</code> - replaced by a unique number for
 | 
						|
 *     distinguisthing the output files of several concurrently
 | 
						|
 *     running processes.  The <code>FileHandler</code> starts
 | 
						|
 *     with 0 when it tries to open a log file.  If the file
 | 
						|
 *     cannot be opened because it is currently in use,
 | 
						|
 *     the unique number is incremented by one and opening
 | 
						|
 *     is tried again.  These steps are repeated until the
 | 
						|
 *     opening operation succeeds.
 | 
						|
 *
 | 
						|
 *     <p>FIXME: Is the following correct? Please review.  The unique
 | 
						|
 *     number is determined for each log file individually when it is
 | 
						|
 *     opened upon switching to the next file.  Therefore, it is not
 | 
						|
 *     correct to assume that all log files in a rotating set bear the
 | 
						|
 *     same unique number.
 | 
						|
 *
 | 
						|
 *     <p>FIXME: The Javadoc for the Sun reference implementation
 | 
						|
 *     says: "Note that the use of unique ids to avoid conflicts is
 | 
						|
 *     only guaranteed to work reliably when using a local disk file
 | 
						|
 *     system." Why? This needs to be mentioned as well, in case
 | 
						|
 *     the reviewers decide the statement is true.  Otherwise,
 | 
						|
 *     file a bug report with Sun.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%%</code> - replaced by a single percent sign.</li>
 | 
						|
 * </ul>
 | 
						|
 *
 | 
						|
 * <p>If the pattern string does not contain <code>%g</code> and
 | 
						|
 * <code>count</code> is greater than one, the handler will append
 | 
						|
 * the string <code>.%g</code> to the specified pattern.
 | 
						|
 *
 | 
						|
 * <p>If the handler attempts to open a log file, this log file
 | 
						|
 * is being used at the time of the attempt, and the pattern string
 | 
						|
 * does not contain <code>%u</code>, the handler will append
 | 
						|
 * the string <code>.%u</code> to the specified pattern. This
 | 
						|
 * step is performed after any generation number has been
 | 
						|
 * appended.
 | 
						|
 *
 | 
						|
 * <p><em>Examples for the GNU platform:</em>
 | 
						|
 *
 | 
						|
 * <p><ul>
 | 
						|
 *
 | 
						|
 * <li><code>%h/java%u.log</code> will lead to a single log file
 | 
						|
 *     <code>/home/janet/java0.log</code>, assuming <code>count</code>
 | 
						|
 *     equals 1, the user's home directory is
 | 
						|
 *     <code>/home/janet</code>, and the attempt to open the file
 | 
						|
 *     succeeds.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%h/java%u.log</code> will lead to three log files
 | 
						|
 *     <code>/home/janet/java0.log.0</code>,
 | 
						|
 *     <code>/home/janet/java0.log.1</code>, and
 | 
						|
 *     <code>/home/janet/java0.log.2</code>,
 | 
						|
 *     assuming <code>count</code> equals 3, the user's home
 | 
						|
 *     directory is <code>/home/janet</code>, and all attempts
 | 
						|
 *     to open files succeed.</li>
 | 
						|
 *
 | 
						|
 * <li><code>%h/java%u.log</code> will lead to three log files
 | 
						|
 *     <code>/home/janet/java0.log.0</code>,
 | 
						|
 *     <code>/home/janet/java1.log.1</code>, and
 | 
						|
 *     <code>/home/janet/java0.log.2</code>,
 | 
						|
 *     assuming <code>count</code> equals 3, the user's home
 | 
						|
 *     directory is <code>/home/janet</code>, and the attempt
 | 
						|
 *     to open <code>/home/janet/java0.log.1</code> fails.</li>
 | 
						|
 *
 | 
						|
 * </ul>
 | 
						|
 *
 | 
						|
 * @author Sascha Brawer (brawer@acm.org)
 | 
						|
 */
 | 
						|
public class FileHandler
 | 
						|
  extends StreamHandler
 | 
						|
{
 | 
						|
  /**
 | 
						|
   * A literal that prefixes all file-handler related properties in the
 | 
						|
   * logging.properties file.
 | 
						|
   */
 | 
						|
  private static final String PROPERTY_PREFIX = "java.util.logging.FileHandler";
 | 
						|
  /**
 | 
						|
   * The name of the property to set for specifying a file naming (incl. path)
 | 
						|
   * pattern to use with rotating log files.
 | 
						|
   */
 | 
						|
  private static final String PATTERN_KEY = PROPERTY_PREFIX + ".pattern";
 | 
						|
  /**
 | 
						|
   * The default pattern to use when the <code>PATTERN_KEY</code> property was
 | 
						|
   * not specified in the logging.properties file.
 | 
						|
   */
 | 
						|
  private static final String DEFAULT_PATTERN = "%h/java%u.log";
 | 
						|
  /**
 | 
						|
   * The name of the property to set for specifying an approximate maximum
 | 
						|
   * amount, in bytes, to write to any one log output file. A value of zero
 | 
						|
   * (which is the default) implies a no limit.
 | 
						|
   */
 | 
						|
  private static final String LIMIT_KEY = PROPERTY_PREFIX + ".limit";
 | 
						|
  private static final int DEFAULT_LIMIT = 0;
 | 
						|
  /**
 | 
						|
   * The name of the property to set for specifying how many output files to
 | 
						|
   * cycle through. The default value is 1.
 | 
						|
   */
 | 
						|
  private static final String COUNT_KEY = PROPERTY_PREFIX + ".count";
 | 
						|
  private static final int DEFAULT_COUNT = 1;
 | 
						|
  /**
 | 
						|
   * The name of the property to set for specifying whether this handler should
 | 
						|
   * append, or not, its output to existing files. The default value is
 | 
						|
   * <code>false</code> meaning NOT to append.
 | 
						|
   */
 | 
						|
  private static final String APPEND_KEY = PROPERTY_PREFIX + ".append";
 | 
						|
  private static final boolean DEFAULT_APPEND = false;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The number of bytes a log file is approximately allowed to reach
 | 
						|
   * before it is closed and the handler switches to the next file in
 | 
						|
   * the rotating set.  A value of zero means that files can grow
 | 
						|
   * without limit.
 | 
						|
   */
 | 
						|
  private final int limit;
 | 
						|
 | 
						|
 | 
						|
 /**
 | 
						|
  * The number of log files through which this handler cycles.
 | 
						|
  */
 | 
						|
  private final int count;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * The pattern for the location and name of the produced log files.
 | 
						|
   * See the section on <a href="#filePatterns">file name patterns</a>
 | 
						|
   * for details.
 | 
						|
   */
 | 
						|
  private final String pattern;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates whether the handler will append log records to existing
 | 
						|
   * files (<code>true</code>), or whether the handler will clear log files
 | 
						|
   * upon switching to them (<code>false</code>).
 | 
						|
   */
 | 
						|
  private final boolean append;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * The number of bytes that have currently been written to the stream.
 | 
						|
   * Package private for use in inner classes.
 | 
						|
   */
 | 
						|
  long written;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * A linked list of files we are, or have written to. The entries
 | 
						|
   * are file path strings, kept in the order
 | 
						|
   */
 | 
						|
  private LinkedList logFiles;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Constructs a <code>FileHandler</code>, taking all property values
 | 
						|
   * from the current {@link LogManager LogManager} configuration.
 | 
						|
   *
 | 
						|
   * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
 | 
						|
   *         there are IO problems opening the files."  This conflicts
 | 
						|
   *         with the general principle that configuration errors do
 | 
						|
   *         not prohibit construction. Needs review.
 | 
						|
   *
 | 
						|
   * @throws SecurityException if a security manager exists and
 | 
						|
   *         the caller is not granted the permission to control
 | 
						|
   *         the logging infrastructure.
 | 
						|
   */
 | 
						|
  public FileHandler()
 | 
						|
    throws IOException, SecurityException
 | 
						|
  {
 | 
						|
    this(LogManager.getLogManager().getProperty(PATTERN_KEY),
 | 
						|
         LogManager.getIntProperty(LIMIT_KEY, DEFAULT_LIMIT),
 | 
						|
         LogManager.getIntProperty(COUNT_KEY, DEFAULT_COUNT),
 | 
						|
         LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* FIXME: Javadoc missing. */
 | 
						|
  public FileHandler(String pattern)
 | 
						|
    throws IOException, SecurityException
 | 
						|
  {
 | 
						|
    this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, DEFAULT_APPEND);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* FIXME: Javadoc missing. */
 | 
						|
  public FileHandler(String pattern, boolean append)
 | 
						|
    throws IOException, SecurityException
 | 
						|
  {
 | 
						|
    this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, append);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* FIXME: Javadoc missing. */
 | 
						|
  public FileHandler(String pattern, int limit, int count)
 | 
						|
    throws IOException, SecurityException
 | 
						|
  {
 | 
						|
    this(pattern, limit, count,
 | 
						|
         LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Constructs a <code>FileHandler</code> given the pattern for the
 | 
						|
   * location and name of the produced log files, the size limit, the
 | 
						|
   * number of log files thorough which the handler will rotate, and
 | 
						|
   * the <code>append</code> property.  All other property values are
 | 
						|
   * taken from the current {@link LogManager LogManager}
 | 
						|
   * configuration.
 | 
						|
   *
 | 
						|
   * @param pattern The pattern for the location and name of the
 | 
						|
   *        produced log files.  See the section on <a
 | 
						|
   *        href="#filePatterns">file name patterns</a> for details.
 | 
						|
   *        If <code>pattern</code> is <code>null</code>, the value is
 | 
						|
   *        taken from the {@link LogManager LogManager} configuration
 | 
						|
   *        property
 | 
						|
   *        <code>java.util.logging.FileHandler.pattern</code>.
 | 
						|
   *        However, this is a pecularity of the GNU implementation,
 | 
						|
   *        and Sun's API specification does not mention what behavior
 | 
						|
   *        is to be expected for <code>null</code>. Therefore,
 | 
						|
   *        applications should not rely on this feature.
 | 
						|
   *
 | 
						|
   * @param limit specifies the number of bytes a log file is
 | 
						|
   *        approximately allowed to reach before it is closed and the
 | 
						|
   *        handler switches to the next file in the rotating set.  A
 | 
						|
   *        value of zero means that files can grow without limit.
 | 
						|
   *
 | 
						|
   * @param count specifies the number of log files through which this
 | 
						|
   *        handler cycles.
 | 
						|
   *
 | 
						|
   * @param append specifies whether the handler will append log
 | 
						|
   *        records to existing files (<code>true</code>), or whether the
 | 
						|
   *        handler will clear log files upon switching to them
 | 
						|
   *        (<code>false</code>).
 | 
						|
   *
 | 
						|
   * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
 | 
						|
   *         there are IO problems opening the files."  This conflicts
 | 
						|
   *         with the general principle that configuration errors do
 | 
						|
   *         not prohibit construction. Needs review.
 | 
						|
   *
 | 
						|
   * @throws SecurityException if a security manager exists and
 | 
						|
   *         the caller is not granted the permission to control
 | 
						|
   *         the logging infrastructure.
 | 
						|
   *         <p>FIXME: This seems in contrast to all other handler
 | 
						|
   *         constructors -- verify this by running tests against
 | 
						|
   *         the Sun reference implementation.
 | 
						|
   */
 | 
						|
  public FileHandler(String pattern,
 | 
						|
                     int limit,
 | 
						|
                     int count,
 | 
						|
                     boolean append)
 | 
						|
    throws IOException, SecurityException
 | 
						|
  {
 | 
						|
    super(/* output stream, created below */ null,
 | 
						|
          PROPERTY_PREFIX,
 | 
						|
          /* default level */ Level.ALL,
 | 
						|
          /* formatter */ null,
 | 
						|
          /* default formatter */ XMLFormatter.class);
 | 
						|
 | 
						|
    if ((limit <0) || (count < 1))
 | 
						|
      throw new IllegalArgumentException();
 | 
						|
 | 
						|
    this.pattern = pattern != null ? pattern : DEFAULT_PATTERN;
 | 
						|
    this.limit = limit;
 | 
						|
    this.count = count;
 | 
						|
    this.append = append;
 | 
						|
    this.written = 0;
 | 
						|
    this.logFiles = new LinkedList ();
 | 
						|
 | 
						|
    setOutputStream (createFileStream (this.pattern, limit, count, append,
 | 
						|
                                       /* generation */ 0));
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* FIXME: Javadoc missing. */
 | 
						|
  private OutputStream createFileStream(String pattern,
 | 
						|
                                        int limit,
 | 
						|
                                        int count,
 | 
						|
                                        boolean append,
 | 
						|
                                        int generation)
 | 
						|
  {
 | 
						|
    String  path;
 | 
						|
    int     unique = 0;
 | 
						|
 | 
						|
    /* Throws a SecurityException if the caller does not have
 | 
						|
     * LoggingPermission("control").
 | 
						|
     */
 | 
						|
    LogManager.getLogManager().checkAccess();
 | 
						|
 | 
						|
    /* Default value from the java.util.logging.FileHandler.pattern
 | 
						|
     * LogManager configuration property.
 | 
						|
     */
 | 
						|
    if (pattern == null)
 | 
						|
      pattern = LogManager.getLogManager().getProperty(PATTERN_KEY);
 | 
						|
    if (pattern == null)
 | 
						|
      pattern = DEFAULT_PATTERN;
 | 
						|
 | 
						|
    if (count > 1 && !has (pattern, 'g'))
 | 
						|
      pattern = pattern + ".%g";
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
      path = replaceFileNameEscapes(pattern, generation, unique, count);
 | 
						|
 | 
						|
      try
 | 
						|
      {
 | 
						|
        File file = new File(path);
 | 
						|
        if (!file.exists () || append)
 | 
						|
          {
 | 
						|
            FileOutputStream fout = new FileOutputStream (file, append);
 | 
						|
            // FIXME we need file locks for this to work properly, but they
 | 
						|
            // are not implemented yet in Classpath! Madness!
 | 
						|
//             FileChannel channel = fout.getChannel ();
 | 
						|
//             FileLock lock = channel.tryLock ();
 | 
						|
//             if (lock != null) // We've locked the file.
 | 
						|
//               {
 | 
						|
                if (logFiles.isEmpty ())
 | 
						|
                  logFiles.addFirst (path);
 | 
						|
                return new ostr (fout);
 | 
						|
//               }
 | 
						|
          }
 | 
						|
      }
 | 
						|
      catch (Exception ex)
 | 
						|
      {
 | 
						|
        reportError (null, ex, ErrorManager.OPEN_FAILURE);
 | 
						|
      }
 | 
						|
 | 
						|
      unique = unique + 1;
 | 
						|
      if (!has (pattern, 'u'))
 | 
						|
        pattern = pattern + ".%u";
 | 
						|
    }
 | 
						|
    while (true);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Replaces the substrings <code>"/"</code> by the value of the
 | 
						|
   * system property <code>"file.separator"</code>, <code>"%t"</code>
 | 
						|
   * by the value of the system property
 | 
						|
   * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
 | 
						|
   * the system property <code>"user.home"</code>, <code>"%g"</code>
 | 
						|
   * by the value of <code>generation</code>, <code>"%u"</code> by the
 | 
						|
   * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
 | 
						|
   * single percent character.  If <code>pattern</code> does
 | 
						|
   * <em>not</em> contain the sequence <code>"%g"</code>,
 | 
						|
   * the value of <code>generation</code> will be appended to
 | 
						|
   * the result.
 | 
						|
   *
 | 
						|
   * @throws NullPointerException if one of the system properties
 | 
						|
   *         <code>"file.separator"</code>,
 | 
						|
   *         <code>"java.io.tmpdir"</code>, or
 | 
						|
   *         <code>"user.home"</code> has no value and the
 | 
						|
   *         corresponding escape sequence appears in
 | 
						|
   *         <code>pattern</code>.
 | 
						|
   */
 | 
						|
  private static String replaceFileNameEscapes(String pattern,
 | 
						|
                                               int generation,
 | 
						|
                                               int uniqueNumber,
 | 
						|
                                               int count)
 | 
						|
  {
 | 
						|
    CPStringBuilder buf = new CPStringBuilder(pattern);
 | 
						|
    String       replaceWith;
 | 
						|
    boolean      foundGeneration = false;
 | 
						|
 | 
						|
    int pos = 0;
 | 
						|
    do
 | 
						|
    {
 | 
						|
      // Uncomment the next line for finding bugs.
 | 
						|
      // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
 | 
						|
 | 
						|
      if (buf.charAt(pos) == '/')
 | 
						|
      {
 | 
						|
        /* The same value is also provided by java.io.File.separator. */
 | 
						|
        replaceWith = System.getProperty("file.separator");
 | 
						|
        buf.replace(pos, pos + 1, replaceWith);
 | 
						|
        pos = pos + replaceWith.length() - 1;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (buf.charAt(pos) == '%')
 | 
						|
      {
 | 
						|
        switch (buf.charAt(pos + 1))
 | 
						|
        {
 | 
						|
        case 't':
 | 
						|
          replaceWith = System.getProperty("java.io.tmpdir");
 | 
						|
          break;
 | 
						|
 | 
						|
        case 'h':
 | 
						|
          replaceWith = System.getProperty("user.home");
 | 
						|
          break;
 | 
						|
 | 
						|
        case 'g':
 | 
						|
          replaceWith = Integer.toString(generation);
 | 
						|
          foundGeneration = true;
 | 
						|
          break;
 | 
						|
 | 
						|
        case 'u':
 | 
						|
          replaceWith = Integer.toString(uniqueNumber);
 | 
						|
          break;
 | 
						|
 | 
						|
        case '%':
 | 
						|
          replaceWith = "%";
 | 
						|
          break;
 | 
						|
 | 
						|
        default:
 | 
						|
          replaceWith = "??";
 | 
						|
          break; // FIXME: Throw exception?
 | 
						|
        }
 | 
						|
 | 
						|
        buf.replace(pos, pos + 2, replaceWith);
 | 
						|
        pos = pos + replaceWith.length() - 1;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    while (++pos < buf.length() - 1);
 | 
						|
 | 
						|
    if (!foundGeneration && (count > 1))
 | 
						|
    {
 | 
						|
      buf.append('.');
 | 
						|
      buf.append(generation);
 | 
						|
    }
 | 
						|
 | 
						|
    return buf.toString();
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* FIXME: Javadoc missing. */
 | 
						|
  public void publish(LogRecord record)
 | 
						|
  {
 | 
						|
    if (limit > 0 && written >= limit)
 | 
						|
      rotate ();
 | 
						|
    super.publish(record);
 | 
						|
    flush ();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Rotates the current log files, possibly removing one if we
 | 
						|
   * exceed the file count.
 | 
						|
   */
 | 
						|
  private synchronized void rotate ()
 | 
						|
  {
 | 
						|
    if (logFiles.size () > 0)
 | 
						|
      {
 | 
						|
        File f1 = null;
 | 
						|
        ListIterator lit = null;
 | 
						|
 | 
						|
        // If we reach the file count, ditch the oldest file.
 | 
						|
        if (logFiles.size () == count)
 | 
						|
          {
 | 
						|
            f1 = new File ((String) logFiles.getLast ());
 | 
						|
            f1.delete ();
 | 
						|
            lit = logFiles.listIterator (logFiles.size () - 1);
 | 
						|
          }
 | 
						|
        // Otherwise, move the oldest to a new location.
 | 
						|
        else
 | 
						|
          {
 | 
						|
            String path = replaceFileNameEscapes (pattern, logFiles.size (),
 | 
						|
                                                  /* unique */ 0, count);
 | 
						|
            f1 = new File (path);
 | 
						|
            logFiles.addLast (path);
 | 
						|
            lit = logFiles.listIterator (logFiles.size () - 1);
 | 
						|
          }
 | 
						|
 | 
						|
        // Now rotate the files.
 | 
						|
        while (lit.hasPrevious ())
 | 
						|
          {
 | 
						|
            String s = (String) lit.previous ();
 | 
						|
            File f2 = new File (s);
 | 
						|
            f2.renameTo (f1);
 | 
						|
            f1 = f2;
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    setOutputStream (createFileStream (pattern, limit, count, append,
 | 
						|
                                       /* generation */ 0));
 | 
						|
 | 
						|
    // Reset written count.
 | 
						|
    written = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Tell if <code>pattern</code> contains the pattern sequence
 | 
						|
   * with character <code>escape</code>. That is, if <code>escape</code>
 | 
						|
   * is 'g', this method returns true if the given pattern contains
 | 
						|
   * "%g", and not just the substring "%g" (for example, in the case of
 | 
						|
   * "%%g").
 | 
						|
   *
 | 
						|
   * @param pattern The pattern to test.
 | 
						|
   * @param escape The escape character to search for.
 | 
						|
   * @return True iff the pattern contains the escape sequence with the
 | 
						|
   *  given character.
 | 
						|
   */
 | 
						|
  private static boolean has (final String pattern, final char escape)
 | 
						|
  {
 | 
						|
    final int len = pattern.length ();
 | 
						|
    boolean sawPercent = false;
 | 
						|
    for (int i = 0; i < len; i++)
 | 
						|
      {
 | 
						|
        char c = pattern.charAt (i);
 | 
						|
        if (sawPercent)
 | 
						|
          {
 | 
						|
            if (c == escape)
 | 
						|
              return true;
 | 
						|
            if (c == '%') // Double percent
 | 
						|
              {
 | 
						|
                sawPercent = false;
 | 
						|
                continue;
 | 
						|
              }
 | 
						|
          }
 | 
						|
        sawPercent = (c == '%');
 | 
						|
      }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * An output stream that tracks the number of bytes written to it.
 | 
						|
   */
 | 
						|
  private final class ostr extends FilterOutputStream
 | 
						|
  {
 | 
						|
    private ostr (OutputStream out)
 | 
						|
    {
 | 
						|
      super (out);
 | 
						|
    }
 | 
						|
 | 
						|
    public void write (final int b) throws IOException
 | 
						|
    {
 | 
						|
      out.write (b);
 | 
						|
      FileHandler.this.written++; // FIXME: synchronize?
 | 
						|
    }
 | 
						|
 | 
						|
    public void write (final byte[] b) throws IOException
 | 
						|
    {
 | 
						|
      write (b, 0, b.length);
 | 
						|
    }
 | 
						|
 | 
						|
    public void write (final byte[] b, final int offset, final int length)
 | 
						|
      throws IOException
 | 
						|
    {
 | 
						|
      out.write (b, offset, length);
 | 
						|
      FileHandler.this.written += length; // FIXME: synchronize?
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |