You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by he...@apache.org on 2002/12/14 12:36:11 UTC

cvs commit: jakarta-commons-sandbox/configuration/src/java/org/apache/commons/configuration PropertiesConfiguration.java

henning     2002/12/14 03:36:11

  Modified:    configuration/src/java/org/apache/commons/configuration
                        PropertiesConfiguration.java
  Log:
  - Factored out lots of the code into BasePropertyConfiguration. Here are
    only the code to open up a file and allow access to a property stream.
  - Also moved all code here that deals specifically with files.
  - Style and comment cleanups
  
  Revision  Changes    Path
  1.6       +130 -353  jakarta-commons-sandbox/configuration/src/java/org/apache/commons/configuration/PropertiesConfiguration.java
  
  Index: PropertiesConfiguration.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/configuration/src/java/org/apache/commons/configuration/PropertiesConfiguration.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- PropertiesConfiguration.java	3 Dec 2002 14:13:55 -0000	1.5
  +++ PropertiesConfiguration.java	14 Dec 2002 11:36:11 -0000	1.6
  @@ -56,89 +56,26 @@
   
   import java.io.File;
   import java.io.FileInputStream;
  -import java.io.FileWriter;
  +import java.io.FileNotFoundException;
   import java.io.IOException;
   import java.io.InputStream;
  -import java.io.InputStreamReader;
  -import java.io.LineNumberReader;
  -import java.io.Reader;
  -import java.io.UnsupportedEncodingException;
  -import java.util.Date;
  -import java.util.Iterator;
  +
   import org.apache.commons.lang.StringUtils;
   
   /**
  - * loads the configuration from a properties file. <p>
  - *
  - * <p>The properties file 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. You can also pull in files relative
  - *   to the parent configuration file. So if you have something
  - *   like the following:
  - *
  - *   include = additional.properties
  - *
  - *   Then "additional.properties" is expected to be in the same
  - *   directory as the parent configuration file.
  - *
  - *   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>
  - *
  + * This is the "classic" Properties loader which loads the values from
  + * a single or multiple files (which can be chained with "include =".
  + * All given path references are either absolute or relative to the 
  + * file name supplied in the Constructor.
  + * <p>
  + * In this class, empty PropertyConfigurations can be built, properties
  + * added and later saved. include statements are (obviously) not supported
  + * if you don't construct a PropertyConfiguration from a file.
  + * <p>
  + * If you want to use the getResourceAsStream() trick to load your 
  + * resources without an absolute path, please take a look at the
  + * ClassPropertiesConfiguration which is intended to be used for this.
  + * 
    * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
    * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
    * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a>
  @@ -149,348 +86,188 @@
    * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
    * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
    * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
  + * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    * @version $Id$
    */
  -public class PropertiesConfiguration extends BaseConfiguration
  +public class PropertiesConfiguration
  +    extends BasePropertiesConfiguration
  +    implements Configuration
   {
  -
       /** File separator. */
       protected String fileSeparator = System.getProperty("file.separator");
   
  -    /**
  -     * Base path of the configuration file used to create this Configuration
  -     * object.
  -     */
  -    protected String basePath;
  -
  -    /**
  -     * This is the name of the property that can point to other
  -     * properties file for including other properties files.
  -     */
  -    protected static String include = "include";
  -
  +    /** 
  +     * Base path of the configuration file used to 
  +     * create this Configuration object. Might be null, then a 
  +     * "synthetic" PropertyConfiguration has been created which
  +     * is not loaded from a file
  +     **/
  +    protected String basePath = null;
   
       /**
  -     * Creates an empty Configuration
  +     * Creates an empty PropertyConfiguration object which can be
  +     * used to synthesize a new Properties file by adding values and
  +     * then saving(). An object constructed by this C'tor can not be 
  +     * tickled into loading included files because it cannot supply a
  +     * base for relative includes.
        */
       public PropertiesConfiguration()
       {
  +        setIncludesAllowed(false);
       }
   
       /**
  -     * Creates and loads the extended properties from the specified file.
  +     * Creates an empty PropertyConfiguration object with
  +     * a Super-Object which is queries for every key.
        *
  -     * @param file A String.
  -     * @throws IOException
  +     * @param defaults Configuration defaults to use if key not in file
  +     * @throws IOException Error while loading the properties file
        */
  -    public PropertiesConfiguration(String file) throws IOException
  +    public PropertiesConfiguration(Configuration defaults)
  +        throws IOException
       {
  -        basePath = new File(file).getAbsolutePath();
  -        basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1);
  -
  -        load(new FileInputStream(file));
  +        this();
  +        this.defaults = defaults;
       }
   
       /**
        * Creates and loads the extended properties from the specified file.
  +     * The specified file can contain "include = " properties which then
  +     * are loaded and merged into the properties.
        *
  -     * @param file A String.
  -     * @param defaultConfig Configuration defaults to use if key not in file
  -     * @throws IOException
  +     * @param fileName The name of the Properties File to load.
  +     * @throws IOException Error while loading the properties file
        */
  -    public PropertiesConfiguration(String file, Configuration defaultConfig)
  +    public PropertiesConfiguration(String fileName)
           throws IOException
       {
  -        this(file);
  -        defaults = defaultConfig;
  +        File file = new File(fileName);
  +        File baseFile = file.getParentFile();
  +
  +        if (baseFile != null)
  +        {
  +            basePath = baseFile.getAbsolutePath();
  +            setIncludesAllowed(true);
  +        }
  +        else
  +        {
  +            // For some unknown reason we have no path to load includes
  +            // from. So, don't allow includes.
  +            setIncludesAllowed(false);
  +        }
  +
  +        load(getPropertyStream(file.getName()));
       }
   
       /**
  -     * Creates the extended properties with the specified defaults
  +     * Creates and loads the extended properties from the specified file.
        *
  -     * @param defaultConfig Configuration defaults to use if key not in file
  -     * @throws IOException
  +     * @param file The name of the Properties File to load.
  +     * @param defaults Configuration defaults to use if key not in file
  +     * @throws IOException Error while loading the properties file
        */
  -    public PropertiesConfiguration(Configuration defaultConfig)
  +    public PropertiesConfiguration(String file, Configuration defaults)
           throws IOException
       {
  -        defaults = defaultConfig;
  +        this(file);
  +        this.defaults = defaults;
       }
   
       /**
        * Creates and loads the extended properties from the specified file.
        *
  -     * @param file A String.
  -     * @param defaultFile A String.
  -     * @throws IOException
  +     * @param file The name of the Properties File to load.
  +     * @param defaultFile The name of a properties file whose values
  +     *                    should be used if a key is not in the file.
  +     * @throws IOException Error while loading the properties file
        */
       public PropertiesConfiguration(String file, String defaultFile)
           throws IOException
       {
           this(file);
  -        if (defaultFile != null)
  +        if (StringUtils.isNotEmpty(defaultFile))
           {
  -            defaults = new PropertiesConfiguration(defaultFile);
  +            this.defaults = new PropertiesConfiguration(defaultFile);
           }
       }
   
       /**
  -     * Load the properties from the given input stream.
  +     * Gets a resource relative to the supplied base class or
  +     * from the class loader if its an absolute reference (starting
  +     * with a "/"
        *
  -     * @param input An InputStream.
  -     * @throws IOException
  +     * @param resourceName The resource Name
  +     * @return An Input Stream
  +     * @throws IOException Error while loading the properties file
        */
  -    public void load(InputStream input) throws IOException
  -    {
  -        load(input, null);
  -    }
  -
  -    /**
  -     * Load the properties from the given input stream and using the specified
  -     * encoding.
  -     *
  -     * @param input An InputStream.
  -     * @param enc An encoding.
  -     * @exception IOException
  -     */
  -    public synchronized void load(InputStream input, String enc)
  -            throws IOException
  +    public InputStream getPropertyStream(String resourceName)
  +        throws IOException
       {
  -        PropertiesReader reader = null;
  -        if (enc != null)
  -        {
  -            try
  -            {
  -                reader =
  -                    new PropertiesReader(new InputStreamReader(input, enc));
  -            }
  -            catch (UnsupportedEncodingException e)
  -            {
  -                // Get one with the default encoding...
  -            }
  -        }
  +        InputStream resource = null;
  +        File file = null;
   
  -        if (reader == null)
  +        if (resourceName.startsWith(fileSeparator))
           {
  -            reader =
  -                new PropertiesReader(new InputStreamReader(input));
  +            /*
  +             * We have an absolute path so we'll
  +             * use this.
  +             */
  +            file = new File(resourceName);
           }
  -
  -        try
  +        else
           {
  -            while (true)
  +            if (StringUtils.isEmpty(basePath))
  +            {
  +                // Good luck... This will fail 99 out of 100 times.
  +                file = new File(resourceName);
  +            }
  +            else
               {
  -                String line = reader.readProperty();
  -                int equalSign = line.indexOf('=');
  +                StringBuffer fileName = new StringBuffer();
  +                fileName.append(basePath);
   
  -                if (equalSign > 0)
  +                // My best friend. Paranoia.
  +                if (!basePath.endsWith(fileSeparator))
                   {
  -                    String key = line.substring(0, equalSign).trim();
  -                    String value = line.substring(equalSign + 1).trim();
  +                    fileName.append(fileSeparator);
  +                }
   
  -                    /*
  -                     * Configure produces lines like this ... just
  -                     * ignore them.
  -                     */
  -                    if ("".equals(value))
  -                    {
  -                        continue;
  -                    }
  -
  -                    if (getInclude() != null &&
  -                        key.equalsIgnoreCase(getInclude()))
  -                    {
  -                        /*
  -                         * Recursively load properties files.
  -                         */
  -                        File file = null;
  -
  -                        if (value.startsWith(fileSeparator))
  -                        {
  -                            /*
  -                             * We have an absolute path so we'll
  -                             * use this.
  -                             */
  -                            file = new File(value);
  -                        }
  -                        else
  -                        {
  -                            /*
  -                             * We have a relative path, and we have
  -                             * two possible forms here. If we have the
  -                             * "./" form then just strip that off first
  -                             * before continuing.
  -                             */
  -                            if (value.startsWith("." + fileSeparator))
  -                            {
  -                                value = value.substring(2);
  -                            }
  -
  -                            file = new File(basePath + value);
  -                        }
  -
  -                        if (file != null && file.exists() && file.canRead())
  -                        {
  -                            load(new FileInputStream(file));
  -                        }
  -                    }
  -                    else
  -                    {
  -                        addProperty(key, value);
  -                    }
  +                //
  +                // We have a relative path, and we have
  +                // two possible forms here. If we have the
  +                // "./" form then just strip that off first
  +                // before continuing.
  +                //
  +                if (resourceName.startsWith("." + fileSeparator))
  +                {
  +                    fileName.append(resourceName.substring(2));
                   }
  +                else
  +                {
  +                    fileName.append(resourceName);
  +                }
  +                
  +                file = new File(fileName.toString());
               }
           }
  -        catch (NullPointerException e)
  -        {
  -            /*
  -             * Should happen only when EOF is reached.
  -             */
  -            return;
  -        }
  -    }
  -
  -    /**
  -     * save properties to a file.
  -     * properties with multiple values are saved comma seperated.
  -     *
  -     * @param filename name of the properties file
  -     * @throws IOException
  -     */
  -    public void save(String filename) throws IOException
  -    {
  -        File file = new File(filename);
  -        PropertiesWriter out = new PropertiesWriter(file);
   
  -        out.writeComment("written by PropertiesConfiguration");
  -        out.writeComment(new Date().toString());
  -        for (Iterator i = this.getKeys(); i.hasNext();)
  +        if (file == null || !file.exists())
           {
  -            String key = (String) i.next();
  -            String value = StringUtils.join(this.getStringArray(key), ", ");
  -            out.writeProperty(key, value);
  +            throw new FileNotFoundException("Could not open File " 
  +                                            + resourceName);
           }
  -        out.flush();
  -        out.close();
  -    }
  -
  -    /**
  -     * 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;
  -    }
  -
  -
  -    /**
  -     * 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
  +        else
           {
  -            StringBuffer buffer = new StringBuffer();
  -
  -            try
  +            if (file.canRead())
               {
  -                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;
  -                        }
  -                    }
  -                }
  +                resource = new FileInputStream(file);
               }
  -            catch (NullPointerException e)
  +            else
               {
  -                return null;
  +                throw new IOException("File " + resourceName 
  +                                      + " exists but could not be read.");
               }
  -
  -            return buffer.toString();
  -        }
  -    } // class PropertiesReader
  -
  -    /**
  -     * This class is used to write properties lines.
  -     */
  -    class PropertiesWriter extends FileWriter
  -    {
  -        /**
  -         * Constructor.
  -         *
  -         * @param file the proerties file
  -         * @throws IOException
  -         */
  -        public PropertiesWriter(File file) throws IOException
  -        {
  -            super(file);
           }
  -
  -        /**
  -         * Write a property.
  -         *
  -         * @param key
  -         * @param value
  -         * @exception IOException
  -         */
  -        public void writeProperty(String key, String value) throws IOException
  -        {
  -            write(key + " = " + value + "\n");
  -        }
  -
  -        /**
  -         * Write a comment.
  -         *
  -         * @param comment
  -         * @exception IOException
  -         */
  -        public void writeComment(String comment) throws IOException
  -        {
  -            write("# " + comment + "\n");
  -        }
  -    } // class PropertiesWriter
  +        return resource;
  +    }
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>