/* * Copyright 2010 Aalto University, ComNet * Released under GPLv3. See LICENSE.txt for details. */ package core; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; /** * Interface for simulation settings stored in setting file(s). Settings * class should be initialized before using (with {@link #init(String)}). If * Settings isn't initialized, only settings in {@link #DEF_SETTINGS_FILE} * are read. Normally, after initialization, settings in the given file can * override any settings defined in the default settings file and/or define * new settings. *
All settings are key-value pairs. For parsing details see * {@link java.util.Properties#getProperty(String)}. Value can be a single * value or comma separated list of values. With CSV values, CSV methods * must be used (e.g. {@link #getCsvInts(String, int)}). Setting value should * not start and end with a bracket since those are reserved for run-specific * values (see {@link #setRunIndex(int)}). In file paths directory separator * should always be forward slash ("/"). *
*/ public class Settings { /** properties object where the setting files are read into */ protected static Properties props; /** file name of the default settings file ({@value}) */ public static final String DEF_SETTINGS_FILE ="default_settings.txt"; /** * Setting to define the file name where all read settings are written * ({@value}. If set to an empty string, standard output is used. * By default setting are not written anywhere. */ public static final String SETTING_OUTPUT_S = "Settings.output"; /** delimiter for requested values in strings ({@value}) * @see #valueFillString(String) */ public static final String FILL_DELIMITER = "%%"; /** Stream where all read settings are written to */ private static PrintStream out = null; private static Set[settingFor1stRun ; settingFor2ndRun ; SettingFor3rdRun]
* runIndex % arrayLength).
* To disable whole run-index-thing, set index to value smaller than
* zero (e.g. -1). When disabled, run-arrays are returned as normal values,
* including the brackets.
* @param index The run index to use for subsequent settings calls, or
* -1 to disable run indexing
*/
public static void setRunIndex(int index) {
runIndex = index;
writtenSettings.clear();
}
/**
* Checks that the given integer array contains a valid range. I.e.,
* the length of the array must be two and
* first_value <= second_value.
* @param range The range array
* @param sname Name of the setting (for error messages)
* @throws SettingsError If the given array didn't qualify as a range
*/
public void assertValidRange(int range[], String sname)
throws SettingsError {
if (range.length != 2) {
throw new SettingsError("Range setting " +
getFullPropertyName(sname) +
" should contain only two comma separated integer values");
}
if (range[0] > range[1]) {
throw new SettingsError("Range setting's " +
getFullPropertyName(sname) +
" first value should be smaller or equal to second value");
}
}
/**
* Sets the namespace to something else than the current namespace.
* This change can be reverted using {@link #restoreNameSpace()}
* @param namespace The new namespace
*/
public void setNameSpace(String namespace) {
this.oldNamespaces.push(this.namespace);
this.namespace = namespace;
}
/**
* Returns full (namespace prefixed) property name for a setting.
* @param setting The name of the setting
* @return The setting name prefixed with fully qualified name of the
* namespace where the requested setting would be retrieved from or null
* if that setting is not found from any of the current namespace(s)
*/
public String getFullPropertyName(String setting) {
if (!contains(setting)) {
return null;
}
if (props.getProperty(getFullPropertyName(setting, false)) != null) {
return getFullPropertyName(setting, false);
}
// not found from primary, but Settings contains -> must be from 2ndary
else return getFullPropertyName(setting, true);
}
/**
* Returns the namespace of the settings object
* @return the namespace of the settings object
*/
public String getNameSpace() {
return this.namespace;
}
/**
* Sets a secondary namespace where a setting is searched from if it
* isn't found from the primary namespace. Secondary namespace can
* be used e.g. as a "default" space where the settings are looked from
* if no specific setting is set.
* This change can be reverted using {@link #restoreSecondaryNamespace()}
* @param namespace The new secondary namespace or null if secondary
* namespace is not used (default behavior)
*/
public void setSecondaryNamespace(String namespace) {
this.secondaryNamespaces.push(this.secondaryNamespace);
this.secondaryNamespace = namespace;
}
/**
* Restores the namespace that was in use before a call to setNameSpace
* @see #setNameSpace(String)
*/
public void restoreNameSpace() {
this.namespace = this.oldNamespaces.pop();
}
/**
* Restores the secondary namespace that was in use before a call to
* setSecondaryNameSpace
* @see #setSecondaryNamespace(String)
*/
public void restoreSecondaryNamespace() {
this.secondaryNamespace = this.secondaryNamespaces.pop();
}
/**
* Initializes the settings all Settings objects will use. This should be
* called before any setting requests. Subsequent calls replace all
* old settings and then Settings contains only the new settings.
* The file {@link #DEF_SETTINGS_FILE}, if exists, is always read.
* @param propFile Path to the property file where additional settings
* are read from or null if no additional settings files are needed.
* @throws SettingsError If loading the settings file(s) didn't succeed
*/
public static void init(String propFile) throws SettingsError {
String outFile;
try {
if (new File(DEF_SETTINGS_FILE).exists()) {
Properties defProperties = new Properties();
defProperties.load(new FileInputStream(DEF_SETTINGS_FILE));
props = new Properties(defProperties);
}
else {
props = new Properties();
}
if (propFile != null) {
props.load(new FileInputStream(propFile));
}
} catch (IOException e) {
throw new SettingsError(e);
}
outFile = props.getProperty(SETTING_OUTPUT_S);
if (outFile != null) {
if (outFile.trim().length() == 0) {
out = System.out;
} else {
try {
out = new PrintStream(new File(outFile));
} catch (FileNotFoundException e) {
throw new SettingsError("Can't open Settings output file:" +
e);
}
}
}
}
public static void addSetting(String name, String value)
{
props.put(name, value);
}
/**
* Reads another settings file and adds the key-value pairs to the current
* settings overriding any values that already existed with the same keys.
* @param propFile Path to the property file
* @throws SettingsError If loading the settings file didn't succeed
* @see #init(String)
*/
public static void addSettings(String propFile) throws SettingsError {
try {
props.load(new FileInputStream(propFile));
} catch (IOException e) {
throw new SettingsError(e);
}
}
/**
* Writes the given setting string to the settings output (if any)
* @param setting The string to write
*/
private static void outputSetting(String setting) {
if (out != null && !writtenSettings.contains(setting)) {
if (writtenSettings.size() == 0) {
out.println("# Settings for run " + (runIndex + 1));
}
out.println(setting);
writtenSettings.add(setting);
}
}
/**
* Returns true if a setting with defined name (in the current namespace
* or secondary namespace if such is set) exists and has some value
* (not just white space)
* @param name Name of the setting to check
* @return True if the setting exists, false if not
*/
public boolean contains(String name) {
try {
String value = getSetting(name);
if (value == null) {
return false;
}
else return value.trim().length() > 0;
}
catch (SettingsError e) {
return false; // didn't find the setting
}
}
/**
* Returns full (namespace prefixed) property name for setting.
* @param name Name of the settings
* @param secondary If true, the secondary namespace is used.
* @return full (prefixed with current namespace) property name for setting
*/
private String getFullPropertyName(String name, boolean secondary) {
String usedNamespace = (secondary ? secondaryNamespace : namespace);
if (usedNamespace != null) {
return usedNamespace + "." + name;
}
else {
return name;
}
}
/**
* Returns a String-valued setting. Setting is first looked from the
* namespace that is set (if any) and then from the secondary namespace
* (if any). All other getters use this method as their first step too
* (so all getters may throw SettingsError and look from both namespaces).
* @param name Name of the setting to get
* @return The contents of the setting in a String
* @throws SettingsError if the setting is not found from either one of
* the namespaces
*/
public String getSetting(String name) {
String fullPropName;
if (props == null) {
init(null);
}
fullPropName = getFullPropertyName(name, false);
String value = props.getProperty(fullPropName);
/*if ((value == null || value.length() == 0) &&
this.secondaryNamespace != null) {
// try secondary namespace if the value wasn't found from primary
fullPropName = getFullPropertyName(name, true);
value = props.getProperty(fullPropName);
if (value != null) {
value = parseRunSetting(valueFillString(value.trim()));
}
}*/
if((value == null || value.length() == 0) && this.secondaryNamespace != null)
{
value = props.getProperty(this.secondaryNamespace + '.' + name);
}
if(value == null || value.length() == 0)
{
Iterator