/*
* Copyright (c) 1999 Dustin Sallings <dustin@spy.net>
*/
package net.spy.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* SpyConfig - an abstracted config file maintainer.
*
* The current config file format is that of a java Properties file. This
* class makes it easier to load them, caches them, and gives a good base
* for extensions that load default config files for projects that are hard
* to pass config filepaths into.
*/
public class SpyConfig extends Properties {
private static Map<File, ConfigInfo> configStore=null;
/**
* Construct a new SpyConfig object describing a config file.
*
* @param conffile The config file we are describing.
*/
public SpyConfig(File conffile) {
super();
confInit();
loadConfig(conffile);
}
/**
* Construct a new SpyConfig object without a config file.
*/
public SpyConfig() {
super();
confInit();
}
private static synchronized void confInit() {
if(configStore==null) {
configStore=Collections.synchronizedMap(
new HashMap<File, ConfigInfo>());
}
}
/**
* Try to load a config file. This function exists primarily for
* classes that extend SpyConfig and want to have multiple default
* locations for config files.
*
* @param conffile Path to the configuration file to load.
*
* @return true if the config loaded successfully.
*/
public boolean loadConfig(File conffile) {
boolean loaded=false;
// See whether we've cached the config file or not.
if(isUpToDate(conffile)) {
// We've already generated this, set it here.
ConfigInfo ci=configStore.get(conffile);
set(ci.getConfig());
loaded=true;
} else {
try {
Map<String, String> h = mapConfig(conffile);
record(conffile, h);
set(h);
loaded=true;
} catch(IOException e) {
// Didn't load, this is not considered a failure.
}
}
return(loaded);
}
/**
* Try to load a config file. This function allows an object to load a
* config file from a list of files. Only the first file in the list
* that works is actually loaded.
*
* @param confFiles an array of config file paths to attempt to load
*
* @return true if a config file was loaded
*/
public boolean loadConfig(File confFiles[]) {
boolean gotit=false;
for(int i=0; i<confFiles.length && gotit==false; i++) {
gotit=loadConfig(confFiles[i]);
}
return(gotit);
}
// Check to see if we have current data on this file.
private boolean isUpToDate(File file) {
boolean r = false;
ConfigInfo ci=configStore.get(file);
if(ci!=null) {
if(ci.getTimestamp() == file.lastModified()) {
r=true;
}
}
return(r);
}
// record stuff to keep up with config file status
private void record(File file, Map<String, String> h) {
configStore.put(file, new ConfigInfo(h, file.lastModified()));
}
private void set(Map<String,String> h) {
for(Map.Entry<String, String> me : h.entrySet()) {
put(me.getKey(), me.getValue());
}
}
/**
* Get the value for a given config entry.
*
* @param key which config entry to return.
*/
public String get(String key) {
return((String)super.get(key));
}
/**
* Get the value for a given config entry, with default.
*
* @param key which config entry to return.
* @param def default in case the entry doesn't exist.
*/
public String get(String key, String def) {
String ret=get(key);
if(ret==null) {
ret=def;
}
return(ret);
}
/**
* Get an int value for a given config entry. Please note, a default
* is *required* because undefined ints suck.
*
* @param key which config entry to return.
* @param def default in case the entry doesn't exist.
*/
public int getInt(String key, int def) {
String value=get(key);
int r=def;
if(value!=null) {
r=Integer.parseInt(value);
}
return(r);
}
/**
* Assign a value to the config only if it doesn't already exist.
* Useful for setting defaults.
*
* @param key config key
* @param value config value
*
*/
public void orput(String key, String value) {
if(!containsKey(key)) {
put(key, value);
}
}
// Unchecked because Properties is a Map<Object, Object>, but should only
// contain String, String.
@SuppressWarnings("unchecked")
private Map<String, String> mapConfig(File file) throws IOException {
Properties p = new Properties();
FileInputStream fis=new FileInputStream(file);
try {
p.load(fis);
} finally {
CloseUtil.close(fis);
}
return(new HashMap(p));
}
// Inner class for storing configuration information
private static class ConfigInfo extends Object {
private long timestamp=0;
private Map<String,String> config=null;
public ConfigInfo(Map<String, String> m, long ts) {
super();
this.config=m;
this.timestamp=ts;
}
public long getTimestamp() {
return(timestamp);
}
public Map<String, String> getConfig() {
return(config);
}
}
}