You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by da...@locus.apache.org on 2000/11/02 03:53:16 UTC

cvs commit: jakarta-velocity/src/java/org/apache/velocity/runtime/configuration ExtendedProperties.java

daveb       00/11/01 18:53:16

  Added:       src/java/org/apache/velocity/runtime/configuration
                        ExtendedProperties.java
  Log:
  Initial add.
  
  Revision  Changes    Path
  1.1                  jakarta-velocity/src/java/org/apache/velocity/runtime/configuration/ExtendedProperties.java
  
  Index: ExtendedProperties.java
  ===================================================================
  package org.apache.velocity.runtime.configuration;
  
  /*
   * Copyright (c) 1997-2000 The Java Apache Project.  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.
   *
   * 3. All advertising materials mentioning features or use of this
   *    software must display the following acknowledgment:
   *    "This product includes software developed by the Java Apache
   *    Project for use in the Apache JServ servlet engine project
   *    <http://java.apache.org/>."
   *
   * 4. The names "Apache JServ", "Apache JServ Servlet Engine", "Turbine",
   *    "Apache Turbine", "Turbine Project", "Apache Turbine Project" and
   *    "Java Apache Project" must not be used to endorse or promote products
   *    derived from this software without prior written permission.
   *
   * 5. Products derived from this software may not be called "Apache JServ"
   *    nor may "Apache" nor "Apache JServ" appear in their names without
   *    prior written permission of the Java Apache Project.
   *
   * 6. Redistributions of any form whatsoever must retain the following
   *    acknowledgment:
   *    "This product includes software developed by the Java Apache
   *    Project for use in the Apache JServ servlet engine project
   *    <http://java.apache.org/>."
   *
   * THIS SOFTWARE IS PROVIDED BY THE JAVA APACHE PROJECT "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 THE JAVA APACHE PROJECT OR
   * ITS 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.
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Java Apache Group. For more information
   * on the Java Apache Project and the Apache JServ Servlet Engine project,
   * please see <http://java.apache.org/>.
   *
   */
  
  // Java stuff.
  import java.io.*;
  import java.util.*;
  
  /**
   * This class extends normal Java properties by adding the possibility
   * to use the same key many times concatenating the value strings
   * instead of overwriting them.
   *
   * <p>The Extended Properties syntax is explained here:
   *
   * <ul>
   *  <li>
   *   Each property has the syntax <code>key = value</code>
   *  </li>
   *  <li>
   *   The <i>key</i> may use any character but the equal sign '='.
   *  </li>
   *  <li>
   *   <i>value</i> may be separated on different lines if a backslash
   *   is placed at the end of the line that continues below.
   *  </li>
   *  <li>
   *   If <i>value</i> is a list of strings, each token is separated
   *   by a comma ','.
   *  </li>
   *  <li>
   *   Commas in each token are escaped placing a backslash right before
   *   the comma.
   *  </li>
   *  <li>
   *   If a <i>key<i> is used more than once, the values are appended
   *   like if they were on the same line separated with commas.
   *  </li>
   *  <li>
   *   Blank lines and lines starting with character '#' are skipped.
   *  </li>
   *  <li>
   *   If a property is named "include" (or whatever is defined by
   *   setInclude() and getInclude() and the value of that property is
   *   the full path to a file on disk, that file will be included into
   *   the ConfigurationsRepository.  Duplicate name values will be
   *   replaced, so be careful.
   *  </li>
   * </ul>
   *
   * <p>Here is an example of a valid extended properties file:
   *
   * <p><pre>
   *      # lines starting with # are comments
   *
   *      # This is the simplest property
   *      key = value
   *
   *      # A long property may be separated on multiple lines
   *      longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
   *                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
   *
   *      # This is a property with many tokens
   *      tokens_on_a_line = first token, second token
   *
   *      # This sequence generates exactly the same result
   *      tokens_on_multiple_lines = first token
   *      tokens_on_multiple_lines = second token
   *
   *      # commas may be escaped in tokens
   *      commas.excaped = Hi\, what'up?
   * </pre>
   *
   * <p><b>NOTE</b>: this class has <b>not</b> been written for
   * performance nor low memory usage.  In fact, it's way slower than it
   * could be and generates too much memory garbage.  But since
   * performance is not an issue during intialization (and there is not
   * much time to improve it), I wrote it this way.  If you don't like
   * it, go ahead and tune it up!
   *
   * @see org.apache.velocity.util.Configurations
   * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
   * @version $Id: ExtendedProperties.java,v 1.1 2000/11/02 02:53:15 daveb Exp $
   */
  public class ExtendedProperties
      extends ConfigurationsRepository
  {
      /**
       * This is the name of the property that can point to other
       * properties file for including other properties files.
       */
      private static String include = "include";
  
      /**
       * This class is used to read properties lines.  These lines do
       * not terminate with new-line chars but rather when there is no
       * backslash sign a the end of the line.  This is used to
       * concatenate multiple lines for readability.
       */
      class PropertiesReader
          extends LineNumberReader
      {
          /**
           * Constructor.
           *
           * @param reader A Reader.
           */
          public PropertiesReader(Reader reader)
          {
              super(reader);
          }
  
          /**
           * Read a property.
           *
           * @return A String.
           * @exception IOException.
           */
          public String readProperty()
              throws IOException
          {
              StringBuffer buffer = new StringBuffer();
  
              try
              {
                  while (true)
                  {
                      String line = readLine().trim();
                      if ((line.length() != 0) && (line.charAt(0) != '#'))
                      {
                          if (line.endsWith("\\"))
                          {
                              line = line.substring(0, line.length() - 1);
                              buffer.append(line);
                          }
                          else
                          {
                              buffer.append(line);
                              break;
                          }
                      }
                  }
              }
              catch (NullPointerException e)
              {
                  return null;
              }
  
              return buffer.toString();
          }
      }
  
      /**
       * This class divides into tokens a property value.  Token
       * separator is "," but commas into the property value are escaped
       * using the backslash in front.
       */
      class PropertiesTokenizer
          extends StringTokenizer
      {
          /**
           * Constructor.
           *
           * @param string A String.
           */
          public PropertiesTokenizer(String string)
          {
              super(string, ",");
          }
  
          /**
           * Check whether the object has more tokens.
           *
           * @return True if the object has more tokens.
           */
          public boolean hasMoreTokens()
          {
              return super.hasMoreTokens();
          }
  
          /**
           * Get next token.
           *
           * @return A String.
           */
          public String nextToken()
          {
              StringBuffer buffer = new StringBuffer();
  
              while (hasMoreTokens())
              {
                  String token = super.nextToken();
                  if (token.endsWith("\\"))
                  {
                      buffer.append(token.substring(0, token.length() - 1));
                      buffer.append(",");
                  }
                  else
                  {
                      buffer.append(token);
                      break;
                  }
              }
  
              return buffer.toString().trim();
          }
      }
  
      /**
       * Creates an empty extended properties object.
       */
      public ExtendedProperties ()
      {
      }
  
      /**
       * Creates and loads the extended properties from the specified
       * file.
       *
       * @param file A String.
       * @exception IOException.
       */
      public ExtendedProperties (String file)
          throws IOException
      {
          this.load(new FileInputStream(file));
      }
  
      /**
       * Gets the property value for including other properties files.
       * By default it is "include".
       *
       * @return A String.
       */
      public String getInclude()
      {
          return this.include;
      }
  
      /**
       * Sets the property value for including other properties files.
       * By default it is "include".
       *
       * @param inc A String.
       */
      public void setInclude(String inc)
      {
          this.include = inc;
      }
  
      /**
       * Load the properties from the given input stream.
       *
       * @param input An InputStream.
       * @exception IOException.
       */
      public synchronized void load(InputStream input)
          throws IOException
      {
          PropertiesReader reader =
              new PropertiesReader(new InputStreamReader(input));
  
          try
          {
              while (true)
              {
                  String line = reader.readProperty();
                  int equalSign = line.indexOf('=');
  
                  if (equalSign > 0)
                  {
                      String key = line.substring(0, equalSign).trim();
                      String value = line.substring(equalSign + 1).trim();
  
                      // Configure produces lines like this ... just
                      // ignore them.
                      if ("".equals(value))
                          continue;
  
                      // Recursively load properties files.
                      File file = new File(value);
                      if (getInclude() != null &&
                          key.equalsIgnoreCase(getInclude()) &&
                          file != null &&
                          file.exists() &&
                          file.canRead())
                          load ( new FileInputStream(file) );
  
                      PropertiesTokenizer tokenizer =
                          new PropertiesTokenizer(value);
                      while (tokenizer.hasMoreTokens())
                      {
                          String token = tokenizer.nextToken();
                          Object o = this.get(key);
                          if (o instanceof String)
                          {
                              Vector v = new Vector(2);
                              v.addElement(o);
                              v.addElement(token);
                              this.put(key, v);
                          }
                          else if (o instanceof Vector)
                          {
                              ((Vector) o).addElement(token);
                          }
                          else
                          {
                              this.put(key, token);
                          }
                      }
                  }
              }
          }
          catch (NullPointerException e)
          {
              // Should happen only when EOF is reached.
              return;
          }
      }
  
      /**
       * Save the properties to the given outputstream.
       *
       * @param output An OutputStream.
       * @param header A String.
       * @exception IOException.
       */
      public synchronized void save(OutputStream output,
                                    String Header)
          throws IOException
      {
          if(output != null)
          {
              PrintWriter theWrtr = new PrintWriter(output);
              if(Header != null)
              {
                  theWrtr.println(Header);
              }
              Enumeration theKeys = keys();
              while(theKeys.hasMoreElements())
              {
                  String key = (String) theKeys.nextElement();
                  Object value = get((Object) key);
                  if(value != null)
                  {
                      if(value instanceof String)
                      {
                          StringBuffer currentOutput = new StringBuffer();
                          currentOutput.append(key);
                          currentOutput.append("=");
                          currentOutput.append((String) value);
                          theWrtr.println(currentOutput.toString());
                      }
                      else if(value instanceof Vector)
                      {
                          Vector values = (Vector) value;
                          Enumeration valuesEnum = values.elements();
                          while(valuesEnum.hasMoreElements())
                          {
                              String currentElement = 
                                     (String) valuesEnum.nextElement();
                              StringBuffer currentOutput = new StringBuffer();
                              currentOutput.append(key);
                              currentOutput.append("=");
                              currentOutput.append(currentElement);
                              theWrtr.println(currentOutput.toString());
                          }
                      }
                  }    
                  theWrtr.println();
                  theWrtr.flush();
              }    
          }        
      }
  }