Ini.java
/*
* Ini
*
* $Id$
* $URL$
*/
package gov.usgs.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Iterator;
import java.util.Properties;
import java.util.HashMap;
/**
* Ini is a Properties that supports sections.
*
* Format Rules:
* <ul>
* <li>Empty lines are ignored.
* <li>Leading and trailing white space are ignored.
* <li>Comments must be on separate lines, and begin with '#' or ';'.
* <li>Properties are key value pairs delimited by an equals: key = value
* <li>Section Names are on separate lines, begin with '[' and end with ']'. Any
* whitespace around the brackets is ignored.
* <li>Any properties before the first section are in the "null" section
* </ul>
*
* Format Example:
*
* <pre>
* #comment about the global
* global = value
*
* # comment about this section
* ; another comment about this section
* [ Section Name ]
* section = value
* </pre>
*
*/
public class Ini extends Properties {
/** Serialization version. */
private static final long serialVersionUID = 1L;
/** Section names map to Section properties. */
private HashMap<String, Properties> sections = new HashMap<String, Properties>();
/** String for representing a comment start */
public static final String COMMENT_START = ";";
/** String for representing an alternate comment start */
public static final String ALTERNATE_COMMENT_START = "#";
/** String to represent a section start */
public static final String SECTION_START = "[";
/** String to represent a section end */
public static final String SECTION_END = "]";
/** String to delimit properties */
public static final String PROPERTY_DELIMITER = "=";
/**
* Same as new Ini(null).
*/
public Ini() {
this(null);
}
/**
* Construct a new Ini with defaults.
*
* @param properties
* a Properties or Ini object with defaults. If an Ini object,
* also makes a shallow copy of sections.
*/
public Ini(final Properties properties) {
super(properties);
if (properties instanceof Ini) {
sections.putAll(((Ini) properties).getSections());
}
}
/**
* @return the section properties map.
*/
public HashMap<String, Properties> getSections() {
return sections;
}
/**
* Get a section property.
*
* @param section
* the section, if null calls getProperty(key).
* @param key
* the property name.
* @return value or property, or null if no matching property found.
*/
public String getSectionProperty(String section, String key) {
if (section == null) {
return getProperty(key);
} else {
Properties props = sections.get(section);
if (props != null) {
return props.getProperty(key);
} else {
return null;
}
}
}
/**
* Set a section property.
*
* @param section
* the section, if null calls super.setProperty(key, value).
* @param key
* the property name.
* @param value
* the property value.
* @return any previous value for key.
*/
public Object setSectionProperty(String section, String key, String value) {
if (section == null) {
return setProperty(key, value);
} else {
Properties props = sections.get(section);
if (props == null) {
// new section
props = new Properties();
sections.put(section, props);
}
return props.setProperty(key, value);
}
}
/**
* Read an Ini input stream.
*
* @param inStream
* the input stream to read.
* @throws IOException
* if unable to parse input stream.
*/
public void load(InputStream inStream) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
// keep track of current line number
int lineNumber = 0;
// line being parsed
String line;
// section being parsed
Properties section = null;
while ((line = br.readLine()) != null) {
lineNumber = lineNumber + 1;
line = line.trim();
// empty line or comment
if (line.length() == 0 || line.startsWith(COMMENT_START)
|| line.startsWith(ALTERNATE_COMMENT_START)) {
// ignore
continue;
}
// section
else if (line.startsWith(SECTION_START)
&& line.endsWith(SECTION_END)) {
// remove brackets
line = line.replace(SECTION_START, "");
line = line.replace(SECTION_END, "");
line = line.trim();
// store all properties in section
section = new Properties();
getSections().put(line, section);
}
// parse as property
else {
int index = line.indexOf("=");
if (index == -1) {
throw new IOException("Expected " + PROPERTY_DELIMITER
+ " on line " + lineNumber + ": '" + line + "'");
} else {
String[] parts = line.split(PROPERTY_DELIMITER, 2);
String key = parts[0].trim();
String value = parts[1].trim();
if (section != null) {
section.setProperty(key, value);
} else {
setProperty(key, value);
}
}
}
}
br.close();
}
/**
* Write an Ini format to a PrintWriter.
*
* @param props
* properties to write.
* @param writer
* the writer that writes.
* @param header
* an optioal header that will appear in comments at the start of
* the ini format.
* @throws IOException
* if unable to write output.
*/
@SuppressWarnings("unchecked")
public static void write(final Properties props, final PrintWriter writer,
String header) throws IOException {
if (header != null) {
// write the header
writer.write(new StringBuffer(COMMENT_START).append(" ").append(
header.trim().replace("\n", "\n" + COMMENT_START + " "))
.append("\n").toString());
}
// write properties
Iterator<String> iter = (Iterator<String>) Collections.list(
props.propertyNames()).iterator();
while (iter.hasNext()) {
String key = iter.next();
writer.write(new StringBuffer(key).append(PROPERTY_DELIMITER)
.append(props.getProperty(key)).append("\n").toString());
}
// write sections
if (props instanceof Ini) {
Ini ini = (Ini) props;
iter = ini.getSections().keySet().iterator();
while (iter.hasNext()) {
String sectionName = iter.next();
writer.write(new StringBuffer(SECTION_START)
.append(sectionName).append(SECTION_END).append("\n")
.toString());
write(ini.getSections().get(sectionName), writer, null);
}
}
// flush, but don't close
writer.flush();
}
/**
* Calls write(new PrintWriter(out), header).
*/
public void store(OutputStream out, String header) throws IOException {
write(this, new PrintWriter(out), header);
}
/**
* Write properties to an OutputStream.
*
* @param out
* the OutputStream used for writing.
*/
public void save(final OutputStream out) {
try {
store(out, null);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Write properties to a PrintStream.
*
* @param out
* the PrintStream used for writing.
*/
public void list(PrintStream out) {
try {
store(out, null);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Write properties to a PrintWriter.
*
* @param out
* the PrintWriter used for writing.
*/
public void list(PrintWriter out) {
try {
write(this, out, null);
} catch (IOException e) {
e.printStackTrace();
}
}
}