//package edu.toronto.psi.vincent.util;

import java.io.*;
import java.util.*;

/**
 * Wrapper for the Properties class to make reading data from and
 * writing data to a properties file easier.  In particular, numbers
 * and arrays can be parsed from and written to a properties file.
 *
 * <pre>
 * Copyright (C) 2005  Vincent Cheung (vincent@psi.toronto.edu, http://www.psi.toronto.edu/~vincent/)
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.</pre>
 * 
 * @author <a href="mailto:vincent@psi.toronto.edu">Vincent Cheung</a>
 * @version 1.3 12/02/05
 */
public class EasyProperties extends Properties {
	
	/**
	 * Creates an empty property list with no default values.
	 */
	public EasyProperties() {
		super();
	}

	/**
	 * Creates an empty property list with the specified defaults.
	 *
	 * @param defaults the defaults.
	 */
	public EasyProperties(Properties defaults) {
		super(defaults);
	}

	/**
	 * Creates a property list from the specified properties file.
	 *
	 * @param filename the name of the properties file.
	 */
	public EasyProperties(String filename) {
		super();

		// load the properties file
		FileInputStream fin = null;
		
		try {
			fin = new FileInputStream(filename);
			load(fin);
		} catch (IOException e) {
			System.out.println("Error reading properties from file.");
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
				}
			}
		}
	}

	/**
	 * Load the properties file.
	 *
	 * @param filename the name of the properties file.
	 */
	public void load(String filename) {
		// load the properties file
		FileInputStream fin = null;
		
		try {
			fin = new FileInputStream(filename);
			load(fin);
		} catch (IOException e) {
			System.out.println("Error reading properties from file.");
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
				}
			}
		}
	}
	
	/**
	 * Writes this property list (key and element pairs) in this Properties table to a file
	 * in a format suitable for loading into a Properties table using the load method.
	 *
	 * If the file already exists, it will be overwritten.
	 *
	 * If the header argument is not null, then an ASCII # character, the header string, 
	 * and a line separator are first written to the output stream. Thus, the header can 
	 * serve as an identifying comment.
	 *
	 * Next, a comment line is always written, consisting of an ASCII # character, the current 
	 * date and time (as if produced by the toString method of Date for the current time), and 
	 * a line separator as generated by the Writer.
	 *
	 * Then every entry in this Properties table is written out, one per line. For each entry 
	 * the key string is written, then an ASCII =, then the associated element string. Each 
	 * character of the key and element strings is examined to see whether it should be rendered 
	 * as an escape sequence.
	 *
	 * @param filename the name of the properties file.
	 * @param header a description of the property list.
	 */
	public void store(String filename, String header) {
		
		File file = new File(filename);
		
		// create the necessary directories
		if (file.getParentFile() != null)
			file.getParentFile().mkdirs();
		
		FileOutputStream fout = null;
		
		try {
			fout = new FileOutputStream(file);
			store(fout, header);
		} catch (IOException e) {
			System.out.println("Error writing properties to file.");
		} finally {
			if (fout != null) {
				try {
					fout.close();
				} catch (IOException e) {
				}
			}
		}
	}	

	/**
	 * Prints this property list to the default output stream (System.out).
	 */
	public void list() {
		list(new PrintStream(System.out));
	}
	
	/**
	 * Returns the property as a boolean (property is true if != "0" or == "true").
	 *
	 * @param key the property key.
	 * @return the property as a boolean.
	 */
	public boolean getBooleanProperty(String key) {
		String property = getProperty(key, null);
		
		if (property == null)
			throw new NumberFormatException("null");
		
		try {
			return (Integer.parseInt(property) != 0);
		} catch (NumberFormatException e) {
			return (Boolean.valueOf(property).booleanValue());
		}
	}

	/**
	 * Returns the property as a boolean (property is true if != "0" or == "true").
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a boolean.
	 */
	public boolean getBooleanProperty(String key, boolean defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		try {
			return (Integer.parseInt(getProperty(key)) != 0);
		} catch (NumberFormatException e) {
			return (Boolean.valueOf(getProperty(key)).booleanValue());
		}
	}

	/**
	 * Returns the property as an integer.
	 *
	 * @param key the property key.
	 * @return the property as an integer.
	 */
	public int getIntProperty(String key) {
		return (Integer.parseInt(getProperty(key)));
	}

	/**
	 * Returns the property as an integer.
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as an integer.
	 */
	public int getIntProperty(String key, int defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return Integer.parseInt(property);
	}
	
	/**
	 * Returns the property as a double.
	 *
	 * @param key the property key.
	 * @return the property as a double.
	 */
	public double getDoubleProperty(String key) {
		return Double.parseDouble(getProperty(key));
	}

	/**
	 * Returns the property as a double.
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a double.
	 */
	public double getDoubleProperty(String key, double defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;

		return Double.parseDouble(property);
	}

	/**
	 * Returns the property as an integer array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the property key.
	 * @return the property as an integer array.
	 */
	public int[] getIntArrayProperty(String key) {
		return getIntArrayProperty(key, null);
	}

	/**
	 * Returns the property as an integer array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as an integer array.
	 */
	public int[] getIntArrayProperty(String key, int[] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return parseIntArray(property);
	}
	
	/**
	 * Parses a comma delimited string to an integer array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param s the comma delimited string.
	 * @return an integer array.
	 */
	public int[] parseIntArray(String s) {

		// remove braces
		int startIndex = s.lastIndexOf('{');
		int endIndex = s.indexOf('}');
		
		if (endIndex == -1)
			endIndex = s.length();

		if (startIndex > endIndex)
			return null;
		
		s = s.substring(startIndex+1, endIndex);

		if (s.length() == 0)
			return null;

		// convert the comma delimited numbers into an array
		String[] temp = s.split(",");
		
		int[] array = new int[temp.length];
		
		for (int i = 0; i < array.length; i++)
			array[i] = Integer.parseInt(temp[i].trim());
		
		return array;
	}
	
	/**
	 * Returns the property as a two-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the property key.
	 * @return the property as a two-dimensional integer array.
	 */
	public int[][] get2DIntArrayProperty(String key) {
		return get2DIntArrayProperty(key, null);
	}

	/**
	 * Returns the property as a two-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a two-dimensional integer array.
	 */
	public int[][] get2DIntArrayProperty(String key, int[][] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return (parse2DIntArray(property));
	}
			
	/**
	 * Parses a comma delimited string to a two-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param s the comma delimited string.
	 * @return a two-dimensional integer array.
	 */
	public int[][] parse2DIntArray(String s) {
	
		// split into strings corresponding to arrays within the 2D array, i.e. "}}, {{"
		String[] split = s.split("\\}\\s*,\\s*\\{");
		
		if (split.length == 0)
			return null;
		
		// create the integer array
		int[][] array = new int[split.length][];
		
		for (int i = 0; i < array.length; i++)
			array[i] = parseIntArray(split[i]);

		return array;
	}

	/**
	 * Returns the property as a three-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the property key.
	 * @return the property as a three-dimensional integer array.
	 */
	public int[][][] get3DIntArrayProperty(String key) {
		return get3DIntArrayProperty(key, null);
	}

	/**
	 * Returns the property as a three-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a three-dimensional integer array.
	 */
	public int[][][] get3DIntArrayProperty(String key, int[][][] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return parse3DIntArray(property);
	}
			
	/**
	 * Parses a comma delimited string to a three-dimensional integer array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param s the comma delimited string.
	 * @return a three-dimensional integer array.
	 */
	public int[][][] parse3DIntArray(String s) {
	
		// split into strings corresponding to 2D arrays within the 3D array, i.e. "}}}, {{{"
		String[] split = s.split("\\}\\s*\\}\\s*,\\s*\\{\\s*\\{");
		
		if (split.length == 0)
			return null;

		// create the integer array
		int[][][] array = new int[split.length][][];
		
		for (int i = 0; i < array.length; i++)
			array[i] = parse2DIntArray(split[i]);

		return array;
	}


	/**
	 * Returns the property as a double array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the property key.
	 * @return the property as a double array.
	 */
	public double[] getDoubleArrayProperty(String key) {
		return getDoubleArrayProperty(key, null);
	}

	/**
	 * Returns the property as a double array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a double array.
	 */
	public double[] getDoubleArrayProperty(String key, double[] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return parseDoubleArray(property);
	}
	
	/**
	 * Parses a comma delimited string to a double array.
	 *
	 * The property is in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param s the comma delimited string.
	 * @return a double array.
	 */
	public double[] parseDoubleArray(String s) {

		// remove braces
		int startIndex = s.lastIndexOf('{');
		int endIndex = s.indexOf('}');
		
		if (endIndex == -1)
			endIndex = s.length();

		if (startIndex > endIndex)
			return null;
		
		s = s.substring(startIndex+1, endIndex);

		if (s.length() == 0)
			return null;

		// convert the comma delimited numbers into an array
		String[] temp = s.split(",");
		
		double[] array = new double[temp.length];
		
		for (int i = 0; i < array.length; i++)
			array[i] = Double.parseDouble(temp[i].trim());
		
		return array;
	}
	
	/**
	 * Returns the property as a two-dimensional double array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the property key.
	 * @return the property as a two-dimensional double array.
	 */
	public double[][] get2DDoubleArrayProperty(String key) {
		return (get2DDoubleArrayProperty(key, null));
	}

	/**
	 * Returns the property as a two-dimensional double array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a two-dimensional double array.
	 */
	public double[][] get2DDoubleArrayProperty(String key, double[][] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return (parse2DDoubleArray(property));
	}
			
	/**
	 * Parses a comma delimited string to a two-dimensional double array.
	 *
	 * The property is in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param s the comma delimited string.
	 * @return a two-dimensional double array.
	 */
	public double[][] parse2DDoubleArray(String s) {
	
		// split into strings corresponding to arrays within the 2D array, i.e. "}}, {{"
		String[] split = s.split("\\}\\s*,\\s*\\{");
		
		if (split.length == 0)
			return null;
		
		// create the double array
		double[][] array = new double[split.length][];
		
		for (int i = 0; i < array.length; i++)
			array[i] = parseDoubleArray(split[i]);

		return array;
	}

	/**
	 * Returns the property as a three-dimensional double array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the property key.
	 * @return the property as a three-dimensional double array.
	 */
	public double[][][] get3DDoubleArrayProperty(String key) {
		return get3DDoubleArrayProperty(key, null);
	}

	/**
	 * Returns the property as a three-dimensional double array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a three-dimensional double array.
	 */
	public double[][][] get3DDoubleArrayProperty(String key, double[][][] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return parse3DDoubleArray(property);
	}
			
	/**
	 * Parses a comma delimited string to a three-dimensional double array.
	 *
	 * The property is in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param s the comma delimited string.
	 * @return a three-dimensional double array.
	 */
	public double[][][] parse3DDoubleArray(String s) {
	
		// split into strings corresponding to 2D arrays within the 3D array, i.e. "}}}, {{{"
		String[] split = s.split("\\}\\s*\\}\\s*,\\s*\\{\\s*\\{");
		
		if (split.length == 0)
			return null;

		// create the double array
		double[][][] array = new double[split.length][][];
		
		for (int i = 0; i < array.length; i++)
			array[i] = parse2DDoubleArray(split[i]);

		return array;
	}

	/**
	 * Returns the property as a string array.
	 *
	 * The property is in the form:
	 * {"one", "two", "three", "this has \\"quotes\\""}
	 *
	 * @param key the property key.
	 * @return the property as a string array.
	 */
	public String[] getStringArrayProperty(String key) {
		return (getStringArrayProperty(key, null));
	}

	/**
	 * Returns the property as a string array.
	 *
	 * The property is in the form:
	 * {"one", "two", "three", "this has \\"quotes\\""}
	 *
	 * @param key the property key.
	 * @param defaultValue the default value.
	 * @return the property as a string array.
	 */
	public String[] getStringArrayProperty(String key, String[] defaultValue) {
		String property = getProperty(key, null);
		
		if (property == null)
			return defaultValue;
		
		return parseStringArray(property);
	}
	
	/**
	 * Parses a ", " delimited string to a string array.
	 *
	 * The property is in the form:
	 * {"one", "two", "three", "this has \\"quotes\\""}
	 *
	 * @param s the ", " delimited string.
	 * @return a string array.
	 */
	public String[] parseStringArray(String s) {

		// replace \" in the string with a random string to prevent the following problem:
		// {"text \\"quote with trailing comma\\", ", "2nd element"}
		final String QUOTE = "1e\\$9gKV0s3\\^r9oN0pQ8!n7d";

		// remove leading and trailing "s
		int startIndex = s.indexOf('"');
		int endIndex = s.lastIndexOf('"');

		if (endIndex == -1)
			endIndex = s.length();

		if (startIndex > endIndex)
			return null;
		
		s = s.substring(startIndex+1, endIndex);

		if (s.length() == 0)
			return null;

		// replace all instances of \" with QUOTE to avoid the trailing quote-comma problem
		s = s.replaceAll("\\\\\"", QUOTE);

		// convert the comma delimited numbers into an array
		String[] array = s.split("\"\\s*,\\s*\"");
//		String[] array = s.split("[^\\\\]?\"\\s*,\\s*\"");

		for (int i = 0; i < array.length; i++)
//			array[i] = array[i].replaceAll("\\\\\"", "\"");
			array[i] = array[i].replaceAll(QUOTE, "\"");

		return array;
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, boolean value) {
		return setProperty(key, "" + value);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, int value) {
		return setProperty(key, "" + value);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, double value) {
		return setProperty(key, "" + value);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, int[] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			if (i == 0)
				s += value[i];
			else
				s += ", " + value[i];
		}
		
		s += "}";
		
		return setProperty(key, s);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, int[][] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			
			if (i == 0)
				s += "{";
			else
				s += ", {";
			
			for (int j = 0; j < value[i].length; j++) {
				if (j == 0)
					s += value[i][j];
				else
					s += ", " + value[i][j];
			}
		
			s += "}";
		}
		
		s += "}";
		
		return setProperty(key, s);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, int[][][] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			
			if (i == 0)
				s += "{";
			else
				s += ", {";
			
			for (int j = 0; j < value[i].length; j++) {
				
				if (j == 0)
					s += "{";
				else
					s += ", {";
				
				for (int k = 0; k < value[i][j].length; k++) {
					if (k == 0)
						s += value[i][j][k];
					else
						s += ", " + value[i][j][k];
				}
				
				s += "}";
			}
		
			s += "}";
		}
		
		s += "}";
		
		return setProperty(key, s);
	}

	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {1, 2, 3, 4, 5}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, double[] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			if (i == 0)
				s += value[i];
			else
				s += ", " + value[i];
		}
		
		s += "}";
		
		return (setProperty(key, s));
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, double[][] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			
			if (i == 0)
				s += "{";
			else
				s += ", {";
			
			for (int j = 0; j < value[i].length; j++) {
				if (j == 0)
					s += value[i][j];
				else
					s += ", " + value[i][j];
			}
		
			s += "}";
		}
		
		s += "}";
		
		return setProperty(key, s);
	}
	
	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {{{1, 2, 3, 4, 5}, {6, 7, 8}, {9, 10}}, {{-1, -2}, {-3, -4}}}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, double[][][] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			
			if (i == 0)
				s += "{";
			else
				s += ", {";
			
			for (int j = 0; j < value[i].length; j++) {
				
				if (j == 0)
					s += "{";
				else
					s += ", {";
				
				for (int k = 0; k < value[i][j].length; k++) {
					if (k == 0)
						s += value[i][j][k];
					else
						s += ", " + value[i][j][k];
				}
				
				s += "}";
			}
		
			s += "}";
		}
		
		s += "}";
		
		return setProperty(key, s);
	}

	/**
	 * Calls the Hashtable method put. Provided for parallelism with the getProperty method.
	 * Conversion to a string is performed. The value returned is the result of the Hashtable call to put.
	 *
	 * The property is written in the form:
	 * {"one", "two", "three", "this has \\"quotes\\""}
	 *
	 * @param key the key to be placed into this property list.
	 * @param value the value corresponding to key.
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key, String[] value) {
		String s = "{";
		
		for (int i = 0; i < value.length; i++) {
			if (i == 0)
				s += "\"" + value[i].replaceAll("\"", "\\\\\"") + "\"";
			else
				s += ", \"" + value[i].replaceAll("\"", "\\\\\"") + "\"";
		}
		
		s += "}";
		
		return setProperty(key, s);
	}
} // end EasyProperties class
