UberProperties Javadoc/*
* Copyright (C) 2002-2011 Stephen Ostermiller
* http://ostermiller.org/contact.pl?regarding=Java+Utilities
*
* Copyright (C) 2003 Carlo Magnaghi <software at tecnosoft dot net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* See LICENSE.txt for details.
*/
package com.Ostermiller.util;
import java.io.*;
import java.util.*;
/**
* The Properties class represents a persistent set of properties. The
* Properties can be saved to a stream or loaded from a stream. Each key and
* its corresponding value in the property list is a string.
* More information about this class is available from <a target="_top" href=
* "http://ostermiller.org/utils/UberProperties.html">ostermiller.org</a>.
* <p>
* A property list can contain another property list as its "defaults"; this
* second property list is searched if the property key is not found in the
* original property list.
* <p>
* When saving properties to a stream or loading them from a stream, the ISO
* 8859-1 character encoding is used. For characters that cannot be directly
* represented in this encoding, Unicode escapes are used; however, only a
* single 'u' character is allowed in an escape sequence. The native2ascii tool
* can be used to convert property files to and from other character encodings.
* <p>
* Unlike the java.util.Properties, UberProperties does not inherit from
* java.util.Hashtable, so Objects other than strings cannot be stored in it.
* Also, comments from a files are preserved, and there can be several
* properties for a given name.
* <p>
* This class is not synchronized, so it should not be used in a
* multi-threaded environment without external synchronization.
* <p>
* The file format that UberProperties uses is as follows:
* <blockquote>
* The file is assumed to be using the ISO 8859-1 character encoding. All of the
* comment lines (starting with a '#' or '!') at the beginning of the file before the
* first line that is not a comment, are the comment associated with the file.
* After that, each comment will be associated with the next property. If there
* is more than one property with the same name, the first comment will be the
* only one that is loaded.
* <p>
* Every property occupies one line of the input stream. Each line is terminated
* by a line terminator (\n or \r or \r\n).
* <p>
* A line that contains only whitespace or whose first non-whitespace character
* is an ASCII # or ! is ignored (thus, # or ! indicate comment lines).
* <p>
* Every line other than a blank line or a comment line describes one property
* to be added to the table (except that if a line ends with \, then the
* following line, if it exists, is treated as a continuation line,
* as described below). The key consists of all the characters in the line
* starting with the first non-whitespace character and up to, but not
* including, the first ASCII =, :, or whitespace character. All of the key
* termination characters may be included in the key by preceding them with a \.
* Any whitespace after the key is skipped; if the first non-whitespace
* character after the key is = or :, then it is ignored and any whitespace
* characters after it are also skipped. All remaining characters on the line
* become part of the associated element string. Within the element string, the
* ASCII escape sequences \t, \n, \r, \\, \", \', \ (a backslash and a space),
* and \\uxxxx are recognized and converted to single characters. Moreover, if
* the last character on the line is \, then the next line is treated as a
* continuation of the current line; the \ and line terminator are simply
* discarded, and any leading whitespace characters on the continuation line are
* also discarded and are not part of the element string.
* <p>
* As an example, each of the following four lines specifies the key "Truth"
* and the associated element value "Beauty":<br>
* <pre>Truth = Beauty
* Truth:Beauty
* Truth :Beauty</pre>
* <p>
* As another example, the following three lines specify a single property:<br>
* <pre>fruits apple, banana, pear, \
* cantaloupe, watermelon, \
* kiwi, mango</pre>
* <p>
* The key is "fruits" and the associated element is:<br>
* "apple, banana, pear, cantaloupe, watermelon, kiwi, mango"<br>
* Note that a space appears before each \ so that a space will appear after
* each comma in the final result; the \, line terminator, and leading
* whitespace on the continuation line are merely discarded and are not replaced
* by one or more other characters.
* <p>
* As a third example, the line:<br>
* cheeses<br>
* specifies that the key is "cheeses" and the associated element is the empty
* string.
* </blockquote>
*
* @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
* @since ostermillerutils 1.00.00
*/
public class UberProperties {
/**
* A hash map that contains all the properties.
* This should never be null, but may be empty.
* This should hold objects of type Property.
*
* @since ostermillerutils 1.00.00
*/
private HashMap<String,Property> properties = new HashMap<String,Property>();
/**
* Comment for this set of properties.
* This may be either null or empty.
*
* @since ostermillerutils 1.00.00
*/
private String comment = null;
/**
* The object type that goes in the HashMap.
*
* @since ostermillerutils 1.00.00
*/
private class Property {
/**
* List of values for this property.
* This should never be null or empty.
*
* @since ostermillerutils 1.00.00
*/
private ArrayList<String> list;
/**
* Comment for this set of properties.
* This may be either null or empty.
*
* @since ostermillerutils 1.00.00
*/
private String comment = null;
/**
* Set the comment associated with this property.
*
* @param comment the comment for this property, or null to clear.
*
* @since ostermillerutils 1.00.00
*/
public void setComment(String comment){
this.comment = comment;
}
/**
* Get the comment associated with this property.
*
* @return comment for this property, or null if none is set.
*
* @since ostermillerutils 1.00.00
*/
public String getComment(){
return this.comment;
}
/**
* Construct a new property with the given value.
*
* @param value initial value for this property.
*
* @since ostermillerutils 1.00.00
*/
public Property(String value){
list = new ArrayList<String>(1);
add(value);
}
/**
* Construct a new property with the given values.
*
* @param values initial values for this property.
*
* @since ostermillerutils 1.00.00
*/
public Property(String[] values){
list = new ArrayList<String>(values.length);
add(values);
}
/**
* Set this property to have this single value.
*
* @param value lone value for this property.
*
* @since ostermillerutils 1.00.00
*/
public void set(String value){
list.clear();
add(value);
}
/**
* Set this property to have only these values.
*
* @param values lone values for this property.
*
* @since ostermillerutils 1.00.00
*/
public void set(String[] values){
list.clear();
add(values);
}
/**
* Add this value to the list of values for this property.
*
* @param value another value for this property.
*
* @since ostermillerutils 1.00.00
*/
public void add(String value){
list.add(value);
}
/**
* Add these values to the list of values for this property.
*
* @param values other values for this property.
*
* @since ostermillerutils 1.00.00
*/
public void add(String[] values){
list.ensureCapacity(list.size() + values.length);
for (String element: values) {
add(element);
}
}
/**
* Get the last value for this property.
*
* @return the last value.
*
* @since ostermillerutils 1.00.00
*/
public String getValue(){
return list.get(list.size() - 1);
}
/**
* Get all the values for this property.
*
* @return a list of all the values.
*
* @since ostermillerutils 1.00.00
*/
public String[] getValues(){
return list.toArray(new String[list.size()]);
}
}
/**
* Creates an empty property list with no default values.
*
* @since ostermillerutils 1.00.00
*/
public UberProperties(){
// Create empty properties
}
/**
* Creates an empty property list with the specified defaults.
*
* @param defaults the defaults.
* @throws NullPointerException if defaults is null.
*
* @since ostermillerutils 1.00.00
*/
public UberProperties(UberProperties defaults){
merge(defaults);
}
/**
* Put all the properties from the defaults in this.
* Calling this from a constructor will clone (deep)
* the default properties.
*
* @since ostermillerutils 1.00.00
*/
private void merge(UberProperties defaults){
setComment(defaults.getComment());
String[] names = defaults.propertyNames();
for (String element: names) {
setProperties(element, defaults.getProperties(element));
setComment(element, defaults.getComment(element));
}
}
/**
* Test to see if a property with the given name exists.
*
* @param name the name of the property.
* @return true if the property existed and was removed, false if it did not exist.
* @throws NullPointerException if name is null.
*
* @since ostermillerutils 1.00.00
*/
public boolean contains(String name){
if (name == null) throw new NullPointerException();
return properties.containsKey(name);
}
/**
* Remove any property with the given name.
*
* @param name the name of the property.
* @return true if the property existed and was removed, false if it did not exist.
* @throws NullPointerException if name is null.
*
* @since ostermillerutils 1.00.00
*/
public boolean remove(String name){
if (!contains(name)) return false;
properties.remove(name);
return true;
}
/**
* Replaces all properties of the given name with
* a single property with the given value.
*
* @param name the name of the property.
* @param value the value of the property, or null to remove it.
* @throws NullPointerException if name is null.
*
* @since ostermillerutils 1.00.00
*/
public void setProperty(String name, String value){
if (name == null) throw new NullPointerException();
if (value == null){
properties.remove(name);
} else {
Property property;
if (properties.containsKey(name)){
property = properties.get(name);
property.set(value);
} else {
property = new Property(value);
properties.put(name, property);
}
}
}
/**
* Replaces all properties of the given name with
* properties with the given values.
*
* @param name the name of the property.
* @param values for the property.
* @throws NullPointerException if name is null.
* @throws NullPointerException if values is null.
* @throws IllegalArgumentException if values is empty.
*
* @since ostermillerutils 1.00.00
*/
public void setProperties(String name, String[] values){
if (name == null) throw new NullPointerException();
if (values.length == 0) throw new IllegalArgumentException();
Property property;
if (properties.containsKey(name)){
property = properties.get(name);
property.set(values);
} else {
property = new Property(values);
properties.put(name, property);
}
}
/**
* Replaces all properties of the given name with
* a single property with the given value.
*
* @param name the name of the property.
* @param value the value of the property or null to remove it.
* @param comment the comment for the property, or null to remove it.
* @throws NullPointerException if name is null.
* @throws NullPointerException if comment is null.
*
* @since ostermillerutils 1.00.00
*/
public void setProperty(String name, String value, String comment){
if (name == null) throw new NullPointerException();
if (value == null){
properties.remove(name);
} else {
setProperty(name, value);
setComment(name, comment);
}
}
/**
* Replaces all properties of the given name with
* properties with the given values.
*
* @param name the name of the property.
* @param values value of the property.
* @param comment the comment for the property, or null to remove it.
* @throws NullPointerException if name is null.
* @throws NullPointerException if values is null.
* @throws IllegalArgumentException if values is empty.
*
* @since ostermillerutils 1.00.00
*/
public void setProperties(String name, String[] values, String comment){
if (name == null) throw new NullPointerException();
if (values.length == 0) throw new IllegalArgumentException();
setProperties(name, values);
setComment(name, comment);
}
/**
* Set the comment on the property of the given name.
* The property must exist before this method is called.
*
* @param name the name of the property.
* @param comment the comment for the property.
* @param comment the comment for the property, or null to remove it.
* @throws NullPointerException if name is null.
* @throws IllegalArgumentException if name is not a known key.
*
* @since ostermillerutils 1.00.00
*/
private void setComment(String name, String comment){
if (name == null) throw new NullPointerException();
if (!properties.containsKey(name)) throw new IllegalArgumentException();
(properties.get(name)).setComment(comment);
}
/**
* Adds a value to the list of properties with the
* given name.
*
* @param name the name of the property.
* @param value the values for the property, or null to remove.
* @param comment the comment for the property, or null to remove it.
* @throws NullPointerException if name is null.
* @throws NullPointerException if value is null.
*
* @since ostermillerutils 1.00.00
*/
public void addProperty(String name, String value, String comment){
if (name == null) throw new NullPointerException();
if (value == null) throw new NullPointerException();
addProperty(name, value);
setComment(name, comment);
}
/**
* Adds the values to the list of properties with the
* given name.
*
* @param name the name of the property.
* @param values the values for the property.
* @param comment the comment for the property, or null to remove it.
* @throws NullPointerException if name is null.
* @throws NullPointerException if values is null.
*
* @since ostermillerutils 1.00.00
*/
public void addProperties(String name, String[] values, String comment){
if (name == null) throw new NullPointerException();
if (values == null) throw new NullPointerException();
addProperties(name, values);
setComment(name, comment);
}
/**
* Adds a value to the list of properties with the
* given name.
*
* @param name the name of the property.
* @param value the values for the property.
* @throws NullPointerException if name is null.
* @throws NullPointerException if value is null.
*
* @since ostermillerutils 1.00.00
*/
public void addProperty(String name, String value){
if (name == null) throw new NullPointerException();
if (value == null) throw new NullPointerException();
Property property;
if (properties.containsKey(name)){
property = properties.get(name);
property.add(value);
} else {
property = new Property(value);
properties.put(name, property);
}
}
/**
* Adds the values to the list of properties with the
* given name.
*
* @param name the name of the property.
* @param values the values for the property.
* @throws NullPointerException if name is null.
* @throws NullPointerException if values is null.
*
* @since ostermillerutils 1.00.00
*/
public void addProperties(String name, String[] values){
if (name == null) throw new NullPointerException();
if (values == null) throw new NullPointerException();
Property property;
if (properties.containsKey(name)){
property = properties.get(name);
property.add(values);
} else {
property = new Property(values);
properties.put(name, property);
}
}
private static int hexDigitValue(char c){
switch (c){
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: return -1;
}
}
private static String unescape(String s){
StringBuffer sb = new StringBuffer(s.length());
for(int i=0; i<s.length(); i++){
char c = s.charAt(i);
if (c == '\\'){
i++;
if (i < s.length()){
c = s.charAt(i);
switch (c){
case 'n': {
sb.append('\n');
} break;
case 'r': {
sb.append('\r');
} break;
case 't': {
sb.append('\t');
} break;
case 'f': {
sb.append('\f');
} break;
case 'u': {
boolean foundUnicode = false;
if (i+4 < s.length()){
int unicodeValue = 0;
for (int j = 3; unicodeValue >= 0 && j >= 0; j--){
int val = hexDigitValue(s.charAt(i+(4-j)));
if (val == -1){
unicodeValue = -1;
} else {
unicodeValue |= (val << (j << 2));
}
}
if (unicodeValue >= 0) {
i+=4;
foundUnicode = true;
sb.append((char)unicodeValue);
}
}
if (!foundUnicode) sb.append(c);
} break;
default: {
sb.append(c);
} break;
}
}
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Load these properties from a user file with default properties
* from a system resource.
* <p>
* Example:
* <pre>load(
* new String(){".java","tld","company","package","component.properties"}
* "tld/company/package/component.properties",
* )</pre>
* This will load the properties file relative to the classpath as the
* defaults and the file <%userhome%>/.java/tld/company/package/component.properties
* if the file exists. The .java directory is recommended as it is a common,
* possibly hidden, directory in the users home directory commonly used by
* Java programs.
*
* This method is meant to be used with the save(String systemResource) method
* which will save modified properties back to the user directory.
*
* @param userFile array of Strings representing a path and file name relative to the user home directory.
* @param systemResource name relative to classpath of default properties, or null to ignore.
* @throws IOException if an error occurs when reading.
* @throws NullPointerException if userFile is null.
* @throws IllegalArgumentException if userFile is empty.
*
* @since ostermillerutils 1.00.00
*/
public void load(String[] userFile, String systemResource) throws IOException {
int length = userFile.length;
if (userFile.length == 0) throw new IllegalArgumentException();
InputStream in = ClassLoader.getSystemResourceAsStream(systemResource);
if (in==null) throw new FileNotFoundException(systemResource);
if (systemResource != null) load(in);
File f = new File(System.getProperty("user.home"));
for (int i=0; f.exists() && i<length; i++){
f = new File(f, userFile[i]);
}
if (f.exists()) load(new FileInputStream(f));
}
/**
* Add the properties from the input stream to this
* UberProperties.
* <p>
* The input stream in parsed as ISO-8859-1 (Latin 1) text.
*
* @param in InputStream containing properties.
* @param add whether parameters should add to parameters with the same name or replace them.
* @throws IOException if an error occurs when reading.
*
* @since ostermillerutils 1.00.00
*/
public void load(InputStream in, boolean add) throws IOException {
load(new InputStreamReader(in, "ISO-8859-1"), add);
}
/**
* Add the properties from the reader to this
* UberProperties.
*
* @param reader Reader containing properties.
* @param add whether parameters should add to parameters with the same name or replace them.
* @throws IOException if an error occurs when reading.
*
* @since ostermillerutils 1.07.01
*/
public void load(Reader reader, boolean add) throws IOException {
PropertiesLexer lex = new PropertiesLexer(reader);
PropertiesToken t;
HashSet<String> names = new HashSet<String>();
StringBuffer comment = new StringBuffer();
boolean foundComment = false;
StringBuffer name = new StringBuffer();
StringBuffer value = new StringBuffer();
boolean atStart = true;
String lastSeparator = null;
while ((t = lex.getNextToken()) != null){
if (t.getID() == PropertiesToken.COMMENT){
int start = 1;
String commentText = t.getContents();
if (commentText.startsWith("# ")) start = 2;
comment.append(commentText.substring(start, commentText.length()));
comment.append("\n");
lex.getNextToken();
foundComment = true;
} else if (t.getID() == PropertiesToken.NAME){
if (atStart){
setComment(comment.toString());
comment.setLength(0);
atStart = false;
}
name.append(t.getContents());
} else if (t.getID() == PropertiesToken.VALUE){
if (atStart){
setComment(comment.toString());
comment.setLength(0);
atStart = false;
}
value.append(t.getContents());
} else if (t.getID() == PropertiesToken.SEPARATOR){
lastSeparator = t.getContents();
} else if (t.getID() == PropertiesToken.END_LINE_WHITE_SPACE){
if (atStart){
setComment(comment.toString());
comment.setLength(0);
atStart = false;
}
String stName = unescape(name.toString());
String stValue = unescape(value.toString());
if (lastSeparator != null || stName.length() > 0 || stValue.length() > 0 ){
if (add || names.contains(stName)){
addProperty(stName, stValue);
} else {
setProperty(stName, stValue);
names.add(stName);
}
if (foundComment) setComment(stName, unescape(comment.toString()));
}
comment.setLength(0);
name.setLength(0);
value.setLength(0);
foundComment = false;
lastSeparator = null;
}
}
}
/**
* Add the properties from the input stream to this
* UberProperties.
* <p>
* The input stream in parsed as ISO-8859-1 (Latin 1) text.
* <p>
* Properties that are found replace any properties that
* were there before.
*
* @param in InputStream containing properties.
* @throws IOException if an error occurs when reading.
*
* @since ostermillerutils 1.00.00
*/
public void load(InputStream in) throws IOException {
load(in, false);
}
/**
* Add the properties from the reader to this
* UberProperties.
* <p>
* Properties that are found replace any properties that
* were there before.
*
* @param in Reader containing properties.
* @throws IOException if an error occurs when reading.
*
* @since ostermillerutils 1.00.00
*/
public void load(Reader in) throws IOException {
load(in, false);
}
/**
* Save these properties from a user file.
* <p>
* Example:
* <pre>save(
* new String(){"tld","company","package","component.properties"}
* )</pre>
* This will save the properties file relative to the user directory:
* <%userhome%>/tld/company/package/component.properties
* Directories will be created as needed.
*
* @param userFile array of Strings representing a path and file name relative to the user home directory.
* @throws IOException if an error occurs when reading.
* @throws NullPointerException if userFile is null.
* @throws IllegalArgumentException if userFile is empty.
*
* @since ostermillerutils 1.00.00
*/
public void save(String[] userFile) throws IOException {
int length = userFile.length;
if (length == 0) throw new IllegalArgumentException();
File f = new File(System.getProperty("user.home"));
for (int i=0; i<length; i++){
f = new File(f, userFile[i]);
if (i == length - 2 && !f.exists()){
f.mkdirs();
}
}
OutputStream out = new FileOutputStream(f);
save(out);
out.close();
}
/**
* Save these properties to the given writer. The properties are saved without escaping
* high byte characters with backslash u style escape sequences. This method is intended
* for those who wish to store properties in UTF-8 (or other high byte capable) text file.
* When saved via this method, the properties can only be read back using the read(Reader)
* method, as the read(InputStream) and read(File) methods assume ISO-8859-1 encoded bytes.
* <p>
* Note that this method in NOT compatible with the java.util.Properties class. This method
* will store unicode characters un-escaped.
*
* @param out writer
* @throws IOException if an input/output error occurs
* @since ostermillerutils 1.07.01
*/
public void save(Writer out) throws IOException {
writeComment(out, comment);
out.write('\n');
String[] names = propertyNames();
Arrays.sort(names);
for (String element: names) {
writeComment(out, getComment(element));
String[] values = getProperties(element);
for (String element2: values) {
writeProperty(out, element, element2);
}
}
out.flush();
}
/**
* Save these properties to the given stream.
*
* @param out OutputStream to which these properties should be written.
* @throws IOException if an error occurs when writing.
*
* @since ostermillerutils 1.00.00
*/
public void save(OutputStream out) throws IOException {
writeComment(out, comment);
out.write('\n');
String[] names = propertyNames();
Arrays.sort(names);
for (String element: names) {
writeComment(out, getComment(element));
String[] values = getProperties(element);
for (String element2: values) {
writeProperty(out, element, element2);
}
}
out.flush();
}
private static void writeProperty(OutputStream out, String name, String value) throws IOException {
writeEscapedISO88591(out, name, TYPE_NAME);
out.write('=');
writeEscapedISO88591(out, value, TYPE_VALUE);
out.write('\n');
}
private static void writeProperty(Writer out, String name, String value) throws IOException {
writeEscapedText(out, name, TYPE_NAME);
out.write('=');
writeEscapedText(out, value, TYPE_VALUE);
out.write('\n');
}
private static void writeComment(OutputStream out, String comment) throws IOException {
if (comment != null){
java.util.StringTokenizer tok = new java.util.StringTokenizer(comment, "\r\n");
while (tok.hasMoreTokens()){
out.write('#');
out.write(' ');
writeEscapedISO88591(out, tok.nextToken(), TYPE_COMMENT);
out.write('\n');
}
}
}
private static void writeComment(Writer out, String comment) throws IOException {
if (comment != null){
java.util.StringTokenizer tok = new java.util.StringTokenizer(comment, "\r\n");
while (tok.hasMoreTokens()){
out.write('#');
out.write(' ');
writeEscapedText(out, tok.nextToken(), TYPE_COMMENT);
out.write('\n');
}
}
}
private static final int TYPE_COMMENT = 0;
private static final int TYPE_NAME = 1;
private static final int TYPE_VALUE = 2;
private static void writeEscapedISO88591(OutputStream out, String s, int type) throws IOException {
for (int i=0; i<s.length(); i++){
int c = s.charAt(i);
if (c < 0x100){
boolean escape = false;
if (c == '\r' || c == '\n' || c == '\\'){
escape = true;
} else if (c == ' ' || c == '\t' || c == '\f'){
if(type == TYPE_NAME){
escape = true;
} else if (type == TYPE_VALUE && (i==0 || i == s.length() - 1)){
escape = true;
}
} else if (type == TYPE_NAME && (c == '=' || c == ':')){
escape = true;
}
if (escape){
switch (c){
case '\n': {
switch (type){
case TYPE_COMMENT: {
out.write('\n');
out.write('#');
out.write(' ');
} break;
case TYPE_NAME: {
out.write('\\');
out.write('n');
out.write('\\');
out.write('\n');
out.write('\t');
} break;
case TYPE_VALUE: {
out.write('\\');
out.write('n');
out.write('\\');
out.write('\n');
out.write('\t');
out.write('\t');
} break;
}
} break;
case '\\': {
out.write('\\');
out.write('\\');
} break;
case '\r': {
out.write('\\');
out.write('r');
} break;
case '\t': {
out.write('\\');
out.write('t');
} break;
case '\f': {
out.write('\\');
out.write('f');
} break;
default : {
out.write('\\');
out.write((byte)c);
} break;
}
} else {
out.write((byte)c);
}
} else {
out.write('\\');
out.write('u');
out.write(StringHelper.prepad(Integer.toHexString(c), 4, '0').getBytes("ISO-8859-1"));
}
}
}
private static void writeEscapedText(Writer out, String s, int type) throws IOException {
for (int i=0; i<s.length(); i++){
int c = s.charAt(i);
boolean escape = false;
if (c == '\r' || c == '\n' || c == '\\'){
escape = true;
} else if (c == ' ' || c == '\t' || c == '\f'){
if(type == TYPE_NAME){
escape = true;
} else if (type == TYPE_VALUE && (i==0 || i == s.length() - 1)){
escape = true;
}
} else if (type == TYPE_NAME && (c == '=' || c == ':')){
escape = true;
}
if (escape){
switch (c){
case '\n': {
switch (type){
case TYPE_COMMENT: {
out.write("\n# ");
} break;
case TYPE_NAME: {
out.write("\\n\\\n\t");
} break;
case TYPE_VALUE: {
out.write("\\n\\\n\t\t");
} break;
}
} break;
case '\\': {
out.write("\\\\");
} break;
case '\r': {
out.write("\\r");
} break;
case '\t': {
out.write("\\t");
} break;
case '\f': {
out.write("\\f");
} break;
default : {
out.write('\\');
out.write(c);
} break;
}
} else {
out.write(c);
}
}
}
/**
* Get the first property with the given name.
* If the property is not specified in this UberProperties
* but it is in the default UberProperties, the default is
* used. If no default is found, null is returned.
*
* @param name Parameter name
* @return the first value of this property, or null if the property does not exist.
*
* @since ostermillerutils 1.00.00
*/
public String getProperty(String name){
String value = null;
if (properties.containsKey(name)){
value = (properties.get(name)).getValue();
}
return value;
}
/**
* Get the first property with the given name.
* If the property is not specified in this UberProperties
* but it is in the default UberProperties, the default
* UberProperties is consulted, otherwise, the supplied
* default is used.
*
* @param name Parameter name
* @param defaultValue Value to use when property not present
* @return the first value of this property.
*
* @since ostermillerutils 1.00.00
*/
public String getProperty(String name, String defaultValue){
String value = getProperty(name);
if (value == null) value = defaultValue;
return value;
}
/**
* Get the values for a property.
* Properties returned in the same order in which
* they were added.
* <p>
* If the property is not specified in this UberProperties
* but it is in the default UberProperties, the default is
* used. If no default is found, null is returned.
*
* @param name Parameter name
* @return all the values associated with the given key, or null if the property does not exist.
*
* @since ostermillerutils 1.00.00
*/
public String[] getProperties(String name){
String[] values = null;
if (properties.containsKey(name)){
values = (properties.get(name)).getValues();
}
return values;
}
/**
* Convert this UberProperties into a java.util.Properties.
*
* @return java properties object.
* @since ostermillerutils 1.07.01
*/
public Properties toJavaUtilProperties(){
Properties p = new Properties();
for(String name: propertyNames()){
p.put(name, getProperty(name));
}
return p;
}
/**
* Get the values for a property.
* Properties returned in the same order in which
* they were added.
* <p>
* If the property is not specified in this UberProperties
* but it is in the default UberProperties, the default
* UberProperties is consulted, otherwise, the supplied
* defaults are used.
*
* @param name Parameter name
* @param defaultValues Values to use when property not present
* @return all the values associated with the given key, or null if the property does not exist.
*
* @since ostermillerutils 1.00.00
*/
public String[] getProperties(String name, String[] defaultValues){
String[] values = getProperties(name);
if (values == null) values = defaultValues;
return values;
}
/**
* Get the comment associated with this property.
* <p>
* If the property is not specified in this UberProperties
* but it is in the default UberProperties, the default is
* used. If no default is found, null is returned.
*
* @param name Parameter name
* @return the comment for this property, or null if there is no comment or the property does not exist.
*
* @since ostermillerutils 1.00.00
*/
public String getComment(String name){
String comment = null;
if (properties.containsKey(name)){
comment = (properties.get(name)).getComment();
}
return comment;
}
/**
* Returns an enumeration of all the keys in this property list, including
* distinct keys in the default property list if a key of the same name has
* not already been found from the main properties list.
*
* @return an enumeration of all the keys in this property list, including the keys in the default property list.
*
* @since ostermillerutils 1.00.00
*/
public String[] propertyNames(){
Set<String> names = properties.keySet();
return names.toArray(new String[names.size()]);
}
/**
* Set the comment associated with this set of properties.
*
* @param comment the comment for entire set of properties, or null to clear.
*
* @since ostermillerutils 1.00.00
*/
public void setComment(String comment){
this.comment = comment;
}
/**
* Get the comment associated with this set of properties.
*
* @return comment for entire set of properties, or null if there is no comment.
*
* @since ostermillerutils 1.00.00
*/
public String getComment(){
return this.comment;
}
/**
* Get the number of unique names for properties stored
* in this UberProperties.
*
* @return number of names.
*
* @since ostermillerutils 1.00.00
*/
public int getPropertyNameCount(){
return properties.keySet().size();
}
/**
* Save these properties to a string.
*
* @return Serialized String version of these properties.
*
* @since ostermillerutils 1.02.23
*/
@Override public String toString(){
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
this.save(out);
} catch (IOException iox){
throw new Error("IO constructed on memory, this shouldn't happen.", iox);
}
String s = null;
try {
s = new String(out.toByteArray(), "ISO-8859-1");
} catch (UnsupportedEncodingException uee){
throw new Error("ISO-8859-1 should be recognized.", uee);
}
return s;
}
/**
* Liberally parse the property value as an integer.
* Uses StringHelper.parseInteger() to parse the integer.
*
* @param name the property name
* @return the parsed property value, or null if the property does not exist or cannot be parsed.
* @see com.Ostermiller.util.StringHelper#parseInteger(String)
* @since ostermillerutils 1.07.01
*/
public Integer getIntegerProperty(String name){
return StringHelper.parseInteger(getProperty(name));
}
/**
* Liberally parse the property value as an integer.
* Uses StringHelper.parseInt() to parse the integer.
*
* @param name the property name
* @param defaultValue default value to be return in case of error
* @return the parsed property value, or the default if the property does not exist or cannot be parsed.
* @see com.Ostermiller.util.StringHelper#parseInt(String, int)
* @since ostermillerutils 1.07.01
*/
public int getIntProperty(String name, int defaultValue){
return StringHelper.parseInt(getProperty(name), defaultValue);
}
/**
* Liberally parse the property value as a boolean.
* Uses StringHelper.parseBoolean() to parse the boolean.
*
* @param name the property name
* @return the parsed property value, or null if the property does not exist or cannot be parsed.
* @see com.Ostermiller.util.StringHelper#parseBoolean(String)
* @since ostermillerutils 1.07.01
*/
public Boolean getBooleanProperty(String name){
return StringHelper.parseBoolean(getProperty(name));
}
/**
* Liberally parse the property value as a boolean.
* Uses StringHelper.parseBoolean() to parse the boolean.
*
* @param name the property name
* @param defaultValue default value to be return in case of error
* @return the parsed property value, or the default if the property does not exist or cannot be parsed.
* @see com.Ostermiller.util.StringHelper#parseBoolean(String, boolean)
* @since ostermillerutils 1.07.01
*/
public boolean getBooleanProperty(String name, boolean defaultValue){
return StringHelper.parseBoolean(getProperty(name), defaultValue);
}
}