/*
* codjo.net
*
* Common Apache License 2.0
*/
package net.codjo.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* This class reads a typical configuration file, and stocks configuration domains into an hash table. Each
* configuration domain contains a set of property of configuration item.
*
* <p> The file has the following format :
* <pre>
* ([configuration domain] '{'
* ( [configuration item] '=' [value] )*
* '}')*
* </pre>
* </p> <hr> Example of file:
* <pre>
* GLOBAL {
* Prompt.name = true
* Prompt.fullName = true
* debug
* }
* OUTPUT {
* ALL = "./desTraces.trace"
* }
* </pre>
* <hr> Example of use:
* <pre>
* ConfigurationFile cfr = new ConfigurationFile("trace.conf");
* Properties props = cfr.getDomain("OUTPUT");
* String fileName = props.getProperty("ALL");
* </pre>
* <hr>
*
* @author Boris Gonnot
* @version 1 - 04/02/00 - Creation
*/
public final class ConfigurationFile {
private Map domains = new HashMap();
/**
* Constructor.
*
* @param confFile A stream to the configuration file
*
* @throws IOException If an I/O failure occured.
*/
public ConfigurationFile(Reader confFile) throws IOException {
load(confFile);
}
/**
* Default constructor.
*/
public ConfigurationFile() {
}
/**
* Constructor.
*
* @param fileName file name of the configuration file
*
* @throws IOException If an I/O failure occured.
*/
public ConfigurationFile(String fileName) throws IOException {
load(new FileReader(fileName));
}
/**
* Add a new domain to this configuration file.
*
* @param name domain name
* @param properties the domain's properties
*
* @throws IllegalArgumentException TODO
*/
public void addDomain(String name, Properties properties) {
// Precondition
if (name == null) {
throw new IllegalArgumentException();
}
if (properties == null) {
properties = new Properties();
}
// Add the new Domain
domains.put(name, properties);
}
/**
* Tests if the specified domain is defined in this configuration file.
*
* @param name the domain name.
*
* @return <code>true</code> if the domain is defined
*/
public boolean containsDomain(String name) {
return domains.containsKey(name);
}
/**
* Returns all the read domains name.
*
* @return all the domains name
*/
public Iterator domainsName() {
return domains.keySet().iterator();
}
/**
* Returns the set of property of the specified domain.
*
* @param name domain name
*
* @return the domain definition
*/
public Properties getDomain(String name) {
return (Properties)domains.get(name);
}
/**
* Load a configuration file.
*
* @param confFile Reader on the File.
*
* @throws IOException Read error
* @throws IllegalArgumentException TODO
*/
public void load(Reader confFile) throws IOException {
if (confFile == null) {
throw new IllegalArgumentException();
}
domains = new java.util.HashMap();
StringBuffer buffer;
try {
buffer = loadFile(confFile);
}
finally {
confFile.close();
}
loadAllDomains(buffer.toString());
}
/**
* Load a configuration file.
*
* @param fileName Nom de fichier
*
* @throws IOException Read error
*/
public void load(String fileName) throws IOException {
load(new FileReader(fileName));
}
/**
* Saves this configuration file to the specified stream.
*
* <p> <b>NB:</b> The writer is not closed. </p>
*
* @param out an output stream.
* @param header a description of the property list.
*
* @throws IOException Description of Exception
* @throws IllegalArgumentException TODO
*/
public void save(OutputStream out, String header)
throws IOException {
// Precondition
if (out == null) {
throw new IllegalArgumentException();
}
// Init
PrintWriter prnt = new PrintWriter(out);
// Save - Header
if (header != null) {
prnt.write("// ");
prnt.println(header);
}
prnt.write("//");
prnt.println(new java.util.Date());
// Save - all domains
for (Iterator e = domains.keySet().iterator(); e.hasNext();) {
String key = (String)e.next();
prnt.print(key);
prnt.println(" {");
// Save content
Properties props = getDomain(key);
prnt.flush();
props.store(out, null);
prnt.println("}");
}
// Ensure that everything is spooled
prnt.flush();
}
/**
* Description of the Method
*
* @param file Description of the Parameter
*
* @return Description of the Return Value
*/
private int findDomainClosingBracket(String file) {
int bracket = 0;
for (int i = 0; i < file.length(); i++) {
if ('{' == file.charAt(i)) {
bracket++;
}
else if ('}' == file.charAt(i)) {
bracket--;
if (bracket == 0) {
return i;
}
}
}
return -1;
}
/**
* Description of the Method
*
* @param file Description of the Parameter
* @param fromIndex Description of the Parameter
*
* @return Description of the Return Value
*/
private int findEndOfProperty(String file, int fromIndex) {
int idx = file.indexOf('\n', fromIndex);
if (idx == -1) {
return -1;
}
if (idx == 0 || '\\' != file.charAt(idx - 1)) {
return idx;
}
return findEndOfProperty(file, idx + 1);
}
private String jenAiMarre(String str) {
int idx = str.indexOf('\\');
if (idx == -1) {
return str;
}
String result = str.substring(0, idx);
if (idx + 1 > str.length()) {
return result;
}
result = result + str.charAt(idx + 1);
if (idx + 2 > str.length()) {
return result;
}
else {
return result + jenAiMarre(str.substring(idx + 2));
}
}
/**
* Find the next domains and launch the loadDomain method on.
*
* @param file Description of Parameter
*
* @throws IOException For I/O exception.
*/
private void loadAllDomains(String file) throws IOException {
int startDomainIdx = file.indexOf('{');
int endDomainIdx = findDomainClosingBracket(file);
if (startDomainIdx < 0) {
return;
}
String domainName = file.substring(0, startDomainIdx).trim();
Properties props = loadDomain(file.substring(startDomainIdx + 1, endDomainIdx));
domains.put(domainName, props);
loadAllDomains(file.substring(endDomainIdx + 1));
}
/**
* Load domain as Properties object.
*
* @param file Description of Parameter
*
* @return Description of the Returned Value
*
* @throws IOException For I/O exception.
*/
private Properties loadDomain(String file) throws IOException {
Properties props = new Properties();
loadProperty(props, file);
return props;
}
/**
* Description of the Method
*
* @param r Description of the Parameter
*
* @return Description of the Return Value
*
* @throws IOException Description of the Exception
*/
private StringBuffer loadFile(Reader r) throws IOException {
BufferedReader reader = new BufferedReader(r);
StringBuffer buffer = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("//") == false) {
buffer.append(line);
if (reader.ready()) {
buffer.append("\n");
}
}
}
return buffer;
}
/**
* Description of the Method
*
* @param props Description of the Parameter
* @param file Description of the Parameter
*/
private void loadProperty(Properties props, String file) {
if (file.length() == 0) {
return;
}
int eolIdx = findEndOfProperty(file, 0);
if (eolIdx == -1) {
eolIdx = file.length();
}
int equalIdx = file.indexOf('=');
String name;
String value = "";
if (equalIdx != -1 && eolIdx > equalIdx) {
name = file.substring(0, equalIdx).trim();
value = file.substring(equalIdx + 1, eolIdx).trim();
value = jenAiMarre(value);
}
else {
name = file.substring(0, eolIdx).trim();
}
if (name.length() > 0) {
props.setProperty(name, value);
}
loadProperty(props, file.substring(Math.min(eolIdx + 1, file.length())));
}
/**
* Signals that a file have a bad format.
*
* @author $Author: blazart $
* @version $Revision: 1.3 $
*/
private static class FileFormatException extends java.io.IOException {
/**
* Constructs a <code>FileFormatException</code> with the specified detail message.
*
* @param s the detail message.
*/
public FileFormatException(String s) {
super(s);
}
}
}