gt: 		return parseDoubles(getCsvSetting(name), name);
context: /* 
   * 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.ArrayList;
  import java.util.HashSet;
  import java.util.Properties;
  import java.util.Scanner;
  import java.util.Set;
  import java.util.Stack;
  
  /**
   * 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.
   * <P> 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 ("/").
   * </P> 
   */
  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_settings0311.txt";
  	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<String> writtenSettings = new HashSet<String>();
  	
  	/** run index for run-specific settings */
  	private static int runIndex = 0;
  	private String namespace = null; // namespace to look the settings from
  	private String secondaryNamespace = null;
  	private Stack<String> oldNamespaces;
  	private Stack<String> secondaryNamespaces;
  	
  	/**
  	 * Creates a setting object with a namespace. Namespace is the prefix
  	 * of the all subsequent setting requests.
  	 * @param namespace Namespace to use
  	 */
  	public Settings(String namespace) {
  		this.oldNamespaces = new Stack<String>();
  		this.secondaryNamespaces = new Stack<String>();
  		setNameSpace(namespace);
  	}
  	
  	/**
  	 * Create a setting object without namespace. All setting requests must
  	 * be prefixed with a valid namespace (e.g. "Report.nrofReports").
  	 */
  	public Settings() {
  		this(null);
  	}
  	
  	/**
  	 * Sets the run index for the settings (only has effect on settings with
  	 * run array). A run array can be defined with syntax<BR>
  	 * <CODE>[settingFor1stRun ; settingFor2ndRun ; SettingFor3rdRun]</CODE>
  	 * <BR>I.e. settings are put in brackets and delimited with semicolon.
  	 * First run's setting is returned when index is 0, second when index is
  	 * 1 etc. If run index is bigger than run array's length, indexing wraps
  	 * around in run array (i.e. return value is the value at index 
  	 * <CODE>runIndex % arrayLength</CODE>). 
  	 * 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 
  	 * <code>first_value <= second_value</code>.
  	 * @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);
  				}
  			}
  		}
  	}
  	
  	/**
  	 * 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) { // found value, check if run setting can be parsed
  			value = parseRunSetting(value.trim());
  		}
  			
  		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(value.trim());
  			}
  		}
  		
  		if (value == null || value.length() == 0) {
  			throw new SettingsError("Can't find setting " + 
  					getPropertyNamesString(name));
  		}
  		
  		outputSetting(fullPropName + " = " + value);
  		return value;
  	}
  	
  	/**
  	 * Parses run-specific settings from a String value
  	 * @param value The String to parse
  	 * @return The runIndex % arrayLength'th value of the run array
  	 */
  	private static String parseRunSetting(String value) {
  		final String RUN_ARRAY_START = "[";
  		final String RUN_ARRAY_END = "]";
  		final String RUN_ARRAY_DELIM = ";";
  		final int MIN_LENGTH = 3; // minimum run is one value. e.g. "[v]"
  		
  		if (!value.startsWith(RUN_ARRAY_START) || 
  			!value.endsWith(RUN_ARRAY_END) || 
  			runIndex < 0 ||
  			value.length() < MIN_LENGTH) {
  			return value; // standard format setting -> return
  		}
  		
  		value = value.substring(1,value.length()-1); // remove brackets
  		String[] valueArr = value.split(RUN_ARRAY_DELIM);
  		int arrIndex = runIndex % valueArr.length;
  		value = valueArr[arrIndex].trim();
  
  		return value;
  	}
  	
  	/**
  	 * Returns the setting name appended to namespace name(s) on a String 
  	 * (for error messages)
  	 * @param name Name of the setting
  	 * @return the setting name appended to namespace name(s) on a String 
  	 */
  	private String getPropertyNamesString(String name) {
  		if (this.secondaryNamespace != null) {
  			return "'"+ this.secondaryNamespace + "." + name + "' nor '" + 
  				this.namespace + "." + name + "'";
  		}
  		else if (this.namespace != null){
  			return "'" + this.namespace + "." + name + "'";
  		}
  		else {
  			return "'" + name + "'";
  		}
  	}
  	
  	/**
  	 * Returns a double-valued setting
  	 * @param name Name of the setting to get
  	 * @return Value of the setting as a double
  	 */
  	public double getDouble(String name) {
  		return parseDouble(getSetting(name), name);
  	}
  	
  	/**
  	 * Parses a double value from a String valued setting. Supports
  	 * kilo (k), mega (M) and giga (G) suffixes.
  	 * @param value String value to parse
  	 * @param setting The setting where this value was from (for error msgs)
  	 * @return The value as a double
  	 * @throws SettingsError if the value wasn't a numeric value 
  	 * (or the suffix wasn't recognized)
  	 */
  	private double parseDouble(String value, String setting) {
  		double number;
  		int multiplier = 1;
  		
  		if (value.endsWith("k")) {
  			multiplier = 1000;
  		}
  		else if (value.endsWith("M")) {
  			multiplier = 1000000;
  		}
  		else if (value.endsWith("G")) {
  			multiplier = 1000000000;
  		}
  		
  		if (multiplier > 1) { // take the suffix away before parsing
  			value = value.substring(0,value.length()-1);
  		}
  		
  		try {
  			number = Double.parseDouble(value) * multiplier;
  		} catch (NumberFormatException e) {
  			throw new SettingsError("Invalid numeric setting '" + value + 
  					"' for '" + setting +"'\n" + e.getMessage());
  		}
  		return number;
  	}
  	
  	/**
  	 * Returns a CSV setting. Value part of the setting must be a list of 
  	 * comma separated values. Whitespace between values is trimmed away.
  	 * @param name Name of the setting
  	 * @return Array of values that were comma-separated
  	 * @throws SettingsError if something went wrong with reading
  	 */
  	public String[] getCsvSetting(String name) {
  		ArrayList<String> values = new ArrayList<String>();
  		String csv = getSetting(name);
  		//System.out.print("settings____values"+values+"\n");
  		//System.out.print("settings____csv"+csv+"\n");
  		Scanner s = new Scanner(csv);
  		//System.out.print("settings____csv___"+s+"\n");
  		s.useDelimiter(",");
  
  		while (s.hasNext()) {
  			values.add(s.next().trim());
  		}
  		
  		return values.toArray(new String[0]);
  	}
  
  	/**
  	 * Returns a CSV setting containing expected amount of values.
  	 * Value part of the setting must be a list of 
  	 * comma separated values. Whitespace between values is trimmed away.
  	 * @param name Name of the setting
  	 * @param expectedCount how many values are expected
  	 * @return Array of values that were comma-separated
  	 * @throws SettingsError if something went wrong with reading or didn't
  	 * read the expected amount of values.
  	 */
  	public String[] getCsvSetting(String name, int expectedCount) {
  		String[] values = getCsvSetting(name);
  
  		if (values.length != expectedCount) {
  			throw new SettingsError("Read unexpected amount (" + values.length +
  					") of comma separated values for setting '" 
  					+ name + "' (expected " + expectedCount + ")");
  		}
  		
  		return values;
  	}
  
  	/**
  	 * Returns an array of CSV setting double values containing expected
  	 * amount of values.
  	 * @param name Name of the setting
  	 * @param expectedCount how many values are expected
  	 * @return Array of values that were comma-separated
  	 * @see #getCsvSetting(String, int)
  	 */
  	public double[] getCsvDoubles(String name, int expectedCount) {
  		return parseDoubles(getCsvSetting(name, expectedCount),name);
  	}
  
  	/**
  	 * Returns an array of CSV setting double values.
  	 * @param name Name of the setting
  	 * @return Array of values that were comma-separated
  	 * @see #getCsvSetting(String)
  	 */
  	public double[] getCsvDoubles(String name) {
  