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:[EMAIL PROTECTED]">Stefano Mazzocchi</a> * @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a> * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a> @@ -149,348 +86,188 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Ilkka Priha</a> * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a> * @author <a href="mailto:[EMAIL PROTECTED]">Martin Poeschl</a> + * @author <a href="mailto:[EMAIL PROTECTED]">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: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>