📄 extendedproperties.java
字号:
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* 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.
*/
package org.apache.commons.collections;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* 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. 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>
*
* <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!
*
*
* @since 1.0
* @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>
* @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
* @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a>
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
* @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
* @version $Id: ExtendedProperties.java,v 1.7.2.1 2004/05/22 12:14:02 scolebourne Exp $
*/
public class ExtendedProperties extends Hashtable
{
/**
* Default configurations repository.
*/
private ExtendedProperties defaults;
/**
* The file connected to this repository (holding comments and
* such).
*
* @serial
*/
protected String file;
/**
* Base path of the configuration file used to create
* this ExtendedProperties object.
*/
protected String basePath;
/**
* File separator.
*/
protected String fileSeparator = System.getProperty("file.separator");
/**
* Has this configuration been intialized.
*/
protected boolean isInitialized = false;
/**
* This is the name of the property that can point to other
* properties file for including other properties files.
*/
protected static String include = "include";
/**
* These are the keys in the order they listed
* in the configuration file. This is useful when
* you wish to perform operations with configuration
* information in a particular order.
*/
protected ArrayList keysAsListed = new ArrayList();
protected final static String START_TOKEN="${";
protected final static String END_TOKEN="}";
protected String interpolate(String base)
{
if (base == null)
{
return null;
}
int begin = -1;
int end = -1;
int prec = 0 - END_TOKEN.length();
String variable = null;
StringBuffer result = new StringBuffer();
// FIXME: we should probably allow the escaping of the start token
while ( ((begin=base.indexOf(START_TOKEN,prec+END_TOKEN.length()))>-1)
&& ((end=base.indexOf(END_TOKEN,begin))>-1) )
{
result.append(base.substring(prec+END_TOKEN.length(),begin));
variable = base.substring(begin+START_TOKEN.length(),end);
if (get(variable)!=null)
{
result.append(get(variable));
}
prec=end;
}
result.append(base.substring(prec+END_TOKEN.length(),base.length()));
return result.toString();
}
/**
* 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
{
/**
* The property delimiter used while parsing (a comma).
*/
static final String DELIMITER = ",";
/**
* Constructor.
*
* @param string A String.
*/
public PropertiesTokenizer(String string)
{
super(string, DELIMITER);
}
/**
* 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(DELIMITER);
}
else
{
buffer.append(token);
break;
}
}
return buffer.toString().trim();
}
}
/**
* Creates an empty extended properties object.
*/
public ExtendedProperties()
{
super();
}
/**
* Creates and loads the extended properties from the specified
* file.
*
* @param file A String.
* @exception IOException.
*/
public ExtendedProperties(String file) throws IOException
{
this(file,null);
}
/**
* Creates and loads the extended properties from the specified
* file.
*
* @param file A String.
* @exception IOException.
*/
public ExtendedProperties(String file, String defaultFile)
throws IOException
{
this.file = file;
basePath = new File(file).getAbsolutePath();
basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1);
this.load(new FileInputStream(file));
if (defaultFile != null)
{
defaults = new ExtendedProperties(defaultFile);
}
}
/**
* Private initializer method that sets up the generic
* resources.
*
* @exception IOException, if there was an I/O problem.
*/
private void init( ExtendedProperties exp ) throws IOException
{
isInitialized = true;
}
/**
* Indicate to client code whether property
* resources have been initialized or not.
*/
public boolean isInitialized()
{
return isInitialized;
}
/**
* 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 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
{
PropertiesReader reader = null;
if (enc != null)
{
try
{
reader =
new PropertiesReader(new InputStreamReader(input,enc));
}
catch (UnsupportedEncodingException e)
{
// Get one with the default encoding...
}
}
if (reader == null)
{
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -