content: package com.aizistral.omniconfig;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.regex.Pattern;
  
  public class Property
  {
  	public enum Type
  	{
  		STRING,
  		INTEGER,
  		BOOLEAN,
  		DOUBLE,
  		COLOR,
  		MOD_ID;
  
  		public static Type tryParse(char id)
  		{
  			for (int x = 0; x < values().length; x++)
  			{
  				if (values()[x].getID() == id)
  					return values()[x];
  			}
  
  			return STRING;
  		}
  
  		public char getID()
  		{
  			return this.name().charAt(0);
  		}
  	}
  
  	private String name;
  	private String value;
  	private String defaultValue;
  	public String comment;
  	private String[] values;
  	private String[] defaultValues;
  	private String[] validValues;
  	private String langKey;
  	private String minValue;
  	private String maxValue;
  
  	private boolean requiresWorldRestart = false;
  	private boolean showInGui = true;
  	private boolean requiresMcRestart = false;
  	private Pattern validationPattern;
  	private final boolean wasRead;
  	private final boolean isList;
  	private boolean isListLengthFixed = false;
  	private int maxListLength = -1;
  	private final Type type;
  	private boolean changed = false;
  	public boolean initialized = false;
  	private boolean allowReload = true;
  
  	public Property(String name, String value, Type type)
  	{
  		this(name, value, type, false, new String[0], name);
  	}
  
  	public Property(String name, String value, Type type, boolean read)
  	{
  		this(name, value, type, read, new String[0], name);
  	}
  
  	public Property(String name, String value, Type type, String[] validValues)
  	{
  		this(name, value, type, false, validValues, name);
  	}
  
  	public Property(String name, String value, Type type, String langKey)
  	{
  		this(name, value, type, false, new String[0], langKey);
  	}
  
  	public Property(String name, String value, Type type, boolean read, String langKey)
  	{
  		this(name, value, type, read, new String[0], langKey);
  	}
  
  	public Property(String name, String value, Type type, String[] validValues, String langKey)
  	{
  		this(name, value, type, false, validValues, langKey);
  	}
  
  	Property(String name, String value, Type type, boolean read, String[] validValues, String langKey)
  	{
  		this.setName(name);
  		this.value = value;
  		this.values = new String[0];
  		this.type  = type;
  		this.wasRead    = read;
  		this.isList     = false;
  		this.defaultValue = value;
  		this.defaultValues = new String[0];
  		this.validValues = validValues;
  		this.isListLengthFixed = false;
  		this.maxListLength = -1;
  		this.minValue = String.valueOf(Integer.MIN_VALUE);
  		this.maxValue = String.valueOf(Integer.MAX_VALUE);
  		this.langKey = langKey;
  		this.comment = "";
  	}
  
  	public Property(String name, String[] values, Type type)
  	{
  		this(name, values, type, false);
  	}
  
  	Property(String name, String[] values, Type type, boolean read)
  	{
  		this(name, values, type, read, new String[0], name);
  	}
  
  	public Property(String name, String[] values, Type type, String langKey)
  	{
  		this(name, values, type, false, langKey);
  	}
  
  	Property(String name, String[] values, Type type, boolean read, String langKey)
  	{
  		this(name, values, type, read, new String[0], langKey);
  	}
  
  	Property(String name, String[] values, Type type, boolean read, String[] validValues, String langKey)
  	{
  		this.setName(name);
  		this.type   = type;
  		this.values = Arrays.copyOf(values, values.length);
  		this.wasRead     = read;
  		this.isList      = true;
  		this.value = "";
  		this.defaultValue = "";
  		for (String s : values) {
  			this.defaultValue += ", [" + s + "]";
  		}
  		this.defaultValue = this.defaultValue.replaceFirst(", ", "");
  		this.defaultValues = Arrays.copyOf(values, values.length);
  		this.validValues = validValues;
  		this.isListLengthFixed = false;
  		this.maxListLength = -1;
  		this.minValue = String.valueOf(Integer.MIN_VALUE);
  		this.maxValue = String.valueOf(Integer.MAX_VALUE);
  		this.langKey = langKey;
  		this.comment = "";
  	}
  
  	/**
  	 * Returns whether or not this Property is defaulted.
  	 *
  	 * @return true if the current value(s) is(are) deeply equal to the default value(s)
  	 */
  	public boolean isDefault()
  	{
  		if (this.isBooleanList())
  		{
  			if (this.values.length == this.defaultValues.length)
  			{
  				for (int i = 0; i < this.values.length; i++)
  					if (Boolean.parseBoolean(this.values[i]) != Boolean.parseBoolean(this.defaultValues[i]))
  						return false;
  
  				return true;
  			}
  			else
  				return false;
  		}
  
  		if (this.isIntList())
  		{
  			if (this.values.length == this.defaultValues.length)
  			{
  				for (int i = 0; i < this.values.length; i++)
  					if (Integer.parseInt(this.values[i]) != Integer.parseInt(this.defaultValues[i]))
  						return false;
  
  				return true;
  			}
  			else
  				return false;
  		}
  
  		if (this.isDoubleList())
  		{
  			if (this.values.length == this.defaultValues.length)
  			{
  				for (int i = 0; i < this.values.length; i++)
  					if (Double.parseDouble(this.values[i]) != Double.parseDouble(this.defaultValues[i]))
  						return false;
  
  				return true;
  			}
  			else
  				return false;
  		}
  
  		if (this.isList())
  		{
  			if (this.values.length == this.defaultValues.length)
  			{
  				for (int i = 0; i < this.values.length; i++)
  					if (!this.values[i].equals(this.defaultValues[i]))
  						return false;
  
  				return true;
  			}
  			else
  				return false;
  		}
  
  		if (this.type == Type.BOOLEAN && this.isBooleanValue())
  			return Boolean.parseBoolean(this.value) == Boolean.parseBoolean(this.defaultValue);
  
  		if (this.type == Type.INTEGER && this.isIntValue())
  			return Integer.parseInt(this.value) == Integer.parseInt(this.defaultValue);
  
  		if (this.type == Type.DOUBLE && this.isDoubleValue())
  			return Double.parseDouble(this.value) == Double.parseDouble(this.defaultValue);
  
  		return this.value.equals(this.defaultValue);
  	}
  
  	/**
  	 * Sets the current value(s) of this Property to the default value(s).
  	 */
  	public Property setToDefault()
  	{
  		this.value = this.defaultValue;
  		this.values = Arrays.copyOf(this.defaultValues, this.defaultValues.length);
  		return this;
  	}
  
  	/**
  	 * Gets the raw String default value of this Property. Check for isList() == false first.
  	 *
  	 * @return the default value String
  	 */
  	public String getDefault()
  	{
  		return this.defaultValue;
  	}
  
  	/**
  	 * Gets the raw String[] default values of this Property. Check for isList() == true first.
  	 *
  	 * @return the default values String[]
  	 */
  	public String[] getDefaults()
  	{
  		return Arrays.copyOf(this.defaultValues, this.defaultValues.length);
  	}
  
  	/**
  	 * Sets the flag for whether or not this Property can be edited while a world is running. Care should be taken to ensure
  	 * that only properties that are truly dynamic can be changed from the in-game options menu. When set to false the Property will be
  	 * editable from both the main menu Mods list config screen and the in-game Mod Options config screen. When set to true the Property
  	 * will only be editable from the main menu Mods list config screen.
  	 */
  	public Property setRequiresWorldRestart(boolean requiresWorldRestart)
  	{
  		this.requiresWorldRestart = requiresWorldRestart;
  		return this;
  	}
  
  	/**
  	 * Returns whether or not this Property is able to be edited while a world is running using the in-game Mod Options screen
  	 * as well as the Mods list screen, or only from the Mods list screen. Setting this flag to true will disable editing of
  	 * this property while a world is running.
  	 */
  	public boolean requiresWorldRestart()
  	{
  		return this.requiresWorldRestart;
  	}
  
  	/**
  	 * Sets whether or not this Property should be allowed to show on config GUIs.
  	 * Defaults to true.
  	 */
  	public Property setShowInGui(boolean showInGui)
  	{
  		this.showInGui = showInGui;
  		return this;
  	}
  
  	/**
  	 * Gets whether or not this Property should be allowed to show on config GUIs.
  	 * Defaults to true unless set to false.
  	 */
  	public boolean showInGui()
  	{
  		return this.showInGui;
  	}
  
  	/**
  	 * Sets whether or not this Property requires Minecraft to be restarted when changed.
  	 * Defaults to false. Setting this flag to true will also disable editing of
  	 * this property while a world is running.
  	 */
  	public Property setRequiresMcRestart(boolean requiresMcRestart)
  	{
  		this.requiresMcRestart = this.requiresWorldRestart = requiresMcRestart;
  		return this;
  	}
  
  	/**
  	 * Gets whether or not this Property requires Minecraft to be restarted when changed.
  	 * Defaults to false unless set to true.
  	 */
  	public boolean requiresMcRestart()
  	{
  		return this.requiresMcRestart;
  	}
  
  	/**
  	 * Sets the maximum length of this list/array Property. Only important if isList() == true. If the current values array or default
  	 * values array is longer than the new maximum it will be resized. If calling both this method and setIsListLengthFixed(true), this
  	 * method should be called afterwards (but is not required).
  	 */
  	public Property setMaxListLength(int max)
  	{
  		this.maxListLength = max;
  		if (this.maxListLength != -1)
  		{
  			if (this.values != null && this.values.length != this.maxListLength)
  				if (this.isListLengthFixed || this.values.length > this.maxListLength) {
  					this.values = Arrays.copyOf(this.values, this.maxListLength);
  				}
  
  			if (this.defaultValues != null && this.defaultValues.length != this.maxListLength)
  				if (this.isListLengthFixed || this.defaultValues.length > this.maxListLength) {
  					this.defaultValues = Arrays.copyOf(this.defaultValues, this.maxListLength);
  				}
  		}
  		return this;
  	}
  
  	/**
  	 * Gets the maximum length of this list/array Property. Only important if isList() == true.
  	 */
  	public int getMaxListLength()
  	{
  		return this.maxListLength;
  	}
  
  	/**
  	 * Sets the flag for whether this list/array Property has a fixed length. Only important if isList() == true. If calling both this
  	 * method and setMaxListLength(), this method should be called first (but is not required).
  	 */
  	public Property setIsListLengthFixed(boolean isListLengthFixed)
  	{
  		this.isListLengthFixed = isListLengthFixed;
  		return this;
  	}
  
  	/**
  	 * Returns whether or not this list/array has a fixed length. Only important if isList() == true.
  	 */
  	public boolean isListLengthFixed()
  	{
  		return this.isListLengthFixed;
  	}
  
  	/**
  	 * Sets a regex Pattern object used to validate user input for formatted String or String[] properties.
  	 *
  	 * @param validationPattern
  	 */
  	public Property setValidationPattern(Pattern validationPattern)
  	{
  		this.validationPattern = validationPattern;
  		return this;
  	}
  
  	/**
  	 * Gets the Pattern object used to validate user input for this Property.
  	 *
  	 * @return the user input validation Pattern object, or null if none is set
  	 */
  	public Pattern getValidationPattern()
  	{
  		return this.validationPattern;
  	}
  
  	/**
  	 * Sets the localization language key for this Property so that the config GUI screens are nice and pretty <3. The string languageKey +
  	 * ".tooltip" is used for tooltips when a user hovers the mouse over a GUI property label.
  	 *
  	 * @param langKey a string language key such as myawesomemod.config.myPropName
  	 */
  	public Property setLanguageKey(String langKey)
  	{
  		this.langKey = langKey;
  		return this;
  	}
  
  	/**
  	 * Gets the language key string for this Property.
  	 *
  	 * @return the language key
  	 */
  	public String getLanguageKey()
  	{
  		return this.langKey;
  	}
  
  	/**
  	 * Sets the default string value of this Property.
  	 *
  	 * @param defaultValue a String value
  	 */
  	public Property setDefaultValue(String defaultValue)
  	{
  		this.defaultValue = defaultValue;
  		return this;
  	}
  
  	/**
  	 * Sets the default String[] values of this Property.
  	 *
  	 * @param defaultValues an array of String values
  	 */
  	public Property setDefaultValues(String[] defaultValues)
  	{
  		this.defaultValue = "";
  		for (String s : defaultValues) {
  			this.defaultValue += ", [" + s + "]";
  		}
  		this.defaultValue = this.defaultValue.replaceFirst(", ", "");
  		this.defaultValues = Arrays.copyOf(defaultValues, defaultValues.length);
  		return this;
  	}
  
  	/**
  	 * Sets the default int value of this Property.
  	 *
  	 * @param defaultValue an int value
  	 */
  	public Property setDefaultValue(int defaultValue)
  	{
  		this.setDefaultValue(Integer.toString(defaultValue));
  		return this;
  	}
  
  	/**
  	 * Sets the default int[] values of this Property.
  	 *
  	 * @param defaultValues an array of int values
  	 */
  	public Property setDefaultValues(int[] defaultValues)
  	{
  		String[] temp = new String[defaultValues.length];
  		for (int i = 0; i < defaultValues.length; i++) {
  			temp[i] = Integer.toString(defaultValues[i]);
  		}
  
  		this.setDefaultValues(temp);
  		return this;
  	}
  
  	/**
  	 * Sets the default double value of this Property.
  	 *
  	 * @param defaultValue a double value
  	 */
  	public Property setDefaultValue(double defaultValue)
  	{
  		this.setDefaultValue(Double.toString(defaultValue));
  		return this;
  	}
  
  	/**
  	 * Sets the default double[] values of this Property
  	 *
  	 * @param defaultValues an array of double values
  	 */
  	public Property setDefaultValues(double[] defaultValues)
  	{
  		String[] temp = new String[defaultValues.length];
  		for (int i = 0; i < defaultValues.length; i++) {
  			temp[i] = Double.toString(defaultValues[i]);
  		}
  
  		this.setDefaultValues(temp);
  		return this;
  	}
  
  	/**
  	 * Sets the default boolean value of this Property.
  	 *
  	 * @param defaultValue a boolean value
  	 */
  	public Property setDefaultValue(boolean defaultValue)
  	{
  		this.setDefaultValue(Boolean.toString(defaultValue));
  		return this;
  	}
  
  	/**
  	 * Sets the default boolean[] values of this Property.
  	 *
  	 * @param defaultValues an array of boolean values
  	 */
  	public Property setDefaultValues(boolean[] defaultValues)
  	{
  		String[] temp = new String[defaultValues.length];
  		for (int i = 0; i < defaultValues.length; i++) {
  			temp[i] = Boolean.toString(defaultValues[i]);
  		}
  
  		this.setDefaultValues(temp);
  		return this;
  	}
  
  	/**
  	 * Sets the minimum int value of this Property.
  	 *
  	 * @param minValue an int value
  	 */
  	public Property setMinValue(int minValue)
  	{
  		this.minValue = Integer.toString(minValue);
  		return this;
  	}
  
  	/**
  	 * Sets the maximum int value of this Property.
  	 *
  	 * @param maxValue an int value
  	 */
  	public Property setMaxValue(int maxValue)
  	{
  		this.maxValue = Integer.toString(maxValue);
  		return this;
  	}
  
  	/**
  	 * Sets the minimum double value of this Property.
  	 *
  	 * @param minValue a double value
  	 */
  	public Property setMinValue(double minValue)
  	{
  		this.minValue = Double.toString(minValue);
  		return this;
  	}
  
  	/**
  	 * Sets the maximum double value of this Property.
  	 *
  	 * @param maxValue a double value
  	 */
  	public Property setMaxValue(double maxValue)
  	{
  		this.maxValue = Double.toString(maxValue);
  		return this;
  	}
  
  	/**
  	 * Gets the minimum value.
  	 *
  	 * @return the minimum value bound
  	 */
  	public String getMinValue()
  	{
  		return this.minValue;
  	}
  
  	/**
  	 * Gets the maximum value.
  	 *
  	 * @return the maximum value bound
  	 */
  	public String getMaxValue()
  	{
  		return this.maxValue;
  	}
  
  	/**
  	 * Returns the value in this property as it's raw string.
  	 *
  	 * @return current value
  	 */
  	public String getString()
  	{
  		return this.value;
  	}
  
  	/**
  	 * Sets the array of valid values that this String Property can be set to. When an array of valid values is defined for a Property the
  	 * GUI control for that property will be a value cycle button.
  	 *
  	 * @param validValues a String array of valid values
  	 */
  	public Property setValidValues(String[] validValues)
  	{
  		this.validValues = validValues;
  		return this;
  	}
  
  	/**
  	 * Gets the array of valid values that this String Property can be set to, or null if not defined.
  	 *
  	 * @return a String array of valid values
  	 */
  	public String[] getValidValues()
  	{
  		return this.validValues;
  	}
  
  	/**
  	 * Returns the value in this property as an integer,
  	 * if the value is not a valid integer, it will return the initially provided default.
  	 *
  	 * @return The value
  	 */
  	public int getInt()
  	{
  		try
  		{
  			return Integer.parseInt(this.value);
  		}
  		catch (NumberFormatException e)
  		{
  			return Integer.parseInt(this.defaultValue);
  		}
  	}
  
  	/**
  	 * Returns the value in this property as an integer,
  	 * if the value is not a valid integer, it will return the
  	 * provided default.
  	 *
  	 * @param _default The default to provide if the current value is not a valid integer
  	 * @return The value
  	 */
  	public int getInt(int _default)
  	{
  		try
  		{
  			return Integer.parseInt(this.value);
  		}
  		catch (NumberFormatException e)
  		{
  			return _default;
  		}
  	}
  
  	/**
  	 * Checks if the current value stored in this property can be converted to an integer.
  	 * @return True if the type of the Property is an Integer
  	 */
  	public boolean isIntValue()
  	{
  		try
  		{
  			Integer.parseInt(this.value);
  			return true;
  		}
  		catch (NumberFormatException e)
  		{
  			return false;
  		}
  	}
  
  	/**
  	 * Returns the value in this property as a boolean,
  	 * if the value is not a valid boolean, it will return the
  	 * provided default.
  	 *
  	 * @param _default The default to provide
  	 * @return The value as a boolean, or the default
  	 */
  	public boolean getBoolean(boolean _default)
  	{
  		if (this.isBooleanValue())
  			return Boolean.parseBoolean(this.value);
  		else
  			return _default;
  	}
  
  	/**
  	 * Returns the value in this property as a boolean, if the value is not a valid boolean, it will return the provided default.
  	 *
  	 * @return The value as a boolean, or the default
  	 */
  	public boolean getBoolean()
  	{
  		if (this.isBooleanValue())
  			return Boolean.parseBoolean(this.value);
  		else
  			return Boolean.parseBoolean(this.defaultValue);
  	}
  
  	/**
  	 * Checks if the current value held by this property is a valid boolean value.
  	 *
  	 * @return True if it is a boolean value
  	 */
  	public boolean isBooleanValue()
  	{
  		return ("true".equals(this.value.toLowerCase()) || "false".equals(this.value.toLowerCase()));
  	}
  
  	/**
  	 * Checks if the current value held by this property is a valid double value.
  	 * @return True if the value can be converted to an double
  	 */
  	public boolean isDoubleValue()
  	{
  		try
  		{
  			Double.parseDouble(this.value);
  			return true;
  		}
  		catch (NumberFormatException e)
  		{
  			return false;
  		}
  	}
  
  	/**
  	 * Returns the value in this property as a double,
  	 * if the value is not a valid double, it will return the
  	 * provided default.
  	 *
  	 * @param _default The default to provide if the current value is not a valid double
  	 * @return The value
  	 */
  	public double getDouble(double _default)
  	{
  		try
  		{
  			return Double.parseDouble(this.value);
  		}
  		catch (NumberFormatException e)
  		{
  			return _default;
  		}
  	}
  
  	/**
  	 * Returns the value in this property as a double, if the value is not a valid double, it will return the provided default.
  	 *
  	 * @param _default The default to provide if the current value is not a valid double
  	 * @return The value
  	 */
  	public double getDouble()
  	{
  		try
  		{
  			return Double.parseDouble(this.value);
  		}
  		catch (NumberFormatException e)
  		{
  			return Double.parseDouble(this.defaultValue);
  		}
  	}
  
  	public String[] getStringList()
  	{
  		return this.values;
  	}
  
  	/**
  	 * Returns the integer value of all values that can
  	 * be parsed in the list.
  	 *
  	 * @return Array of length 0 if none of the values could be parsed.
  	 */
  	public int[] getIntList()
  	{
  		ArrayList<Integer> nums = new ArrayList<Integer>();
  
  		for (String value : this.values)
  		{
  			try
  			{
  				nums.add(Integer.parseInt(value));
  			}
  			catch (NumberFormatException e){}
  		}
  
  		int[] primitives = new int[nums.size()];
  
  		for (int i = 0; i < nums.size(); i++)
  		{
  			primitives[i] = nums.get(i);
  		}
  
  		return primitives;
  	}
  
  	/**
  	 * Checks if all of the current values stored in this property can be converted to an integer.
  	 * @return True if the type of the Property is an Integer List
  	 */
  	public boolean isIntList()
  	{
  		if (this.isList && this.type == Type.INTEGER) {
  			for (String value : this.values)
  			{
  				try
  				{
  					Integer.parseInt(value);
  				}
  				catch (NumberFormatException e)
  				{
  					return false;
  				}
  			}
  		}
  		return this.isList && this.type == Type.INTEGER;
  	}
  
  	/**
  	 * Returns the boolean value of all values that can
  	 * be parsed in the list.
  	 *
  	 * @return Array of length 0 if none of the values could be parsed.
  	 */
  	public boolean[] getBooleanList()
  	{
  		ArrayList<Boolean> tmp = new ArrayList<Boolean>();
  		for (String value : this.values)
  		{
  			try
  			{
  				tmp.add(Boolean.parseBoolean(value));
  			}
  			catch (NumberFormatException e){}
  		}
  
  		boolean[] primitives = new boolean[tmp.size()];
  
  		for (int i = 0; i < tmp.size(); i++)
  		{
  			primitives[i] = tmp.get(i);
  		}
  
  		return primitives;
  	}
  
  	/**
  	 * Checks if all of current values stored in this property can be converted to a boolean.
  	 * @return True if it is a boolean value
  	 */
  	public boolean isBooleanList()
  	{
  		if (this.isList && this.type == Type.BOOLEAN) {
  			for (String value : this.values)
  			{
  				if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value))
  					return false;
  			}
  		}
  
  		return this.isList && this.type == Type.BOOLEAN;
  	}
  
  	/**
  	 * Returns the double value of all values that can
  	 * be parsed in the list.
  	 *
  	 * @return Array of length 0 if none of the values could be parsed.
  	 */
  	public double[] getDoubleList()
  	{
  		ArrayList<Double> tmp = new ArrayList<Double>();
  		for (String value : this.values)
  		{
  			try
  			{
  				tmp.add(Double.parseDouble(value));
  			}
  			catch (NumberFormatException e) {}
  		}
  
  		double[] primitives = new double[tmp.size()];
  
  		for (int i = 0; i < tmp.size(); i++)
  		{
  			primitives[i] = tmp.get(i);
  		}
  
  		return primitives;
  	}
  
  	/**
  	 * Checks if all of the current values stored in this property can be converted to a double.
  	 * @return True if the type of the Property is a double List
  	 */
  	public boolean isDoubleList()
  	{
  		if (this.isList && this.type == Type.DOUBLE) {
  			for (String value : this.values)
  			{
  				try
  				{
  					Double.parseDouble(value);
  				}
  				catch (NumberFormatException e)
  				{
  					return false;
  				}
  			}
  		}
  
  		return this.isList && this.type == Type.DOUBLE;
  	}
  
  	/**
  	 * Gets the name/key for this Property.
  	 *
  	 * @return the Property name
  	 */
  	public String getName()
  	{
  		return this.name;
  	}
  
  	/**
  	 * Sets the name/key for this Property.
  	 *
  	 * @param name a name
  	 */
  	public void setName(String name)
  	{
  		this.name = name;
  	}
  
  	/**
  	 * Determines if this config value was just created, or if it was read from the config file.
  	 * This is useful for mods who auto-assign their blocks to determine if the ID returned is
  	 * a configured one, or a automatically generated one.
  	 *
  	 * @return True if this property was loaded from the config file with a value
  	 */
  	public boolean wasRead()
  	{
  		return this.wasRead;
  	}
  
  	/**
  	 * Gets the Property.Type enum value for this Property.
  	 *
  	 * @return the Property's type
  	 */
  	public Type getType()
  	{
  		return this.type;
  	}
  
  	/**
  	 * Returns whether or not this Property is a list/array.
  	 *
  	 * @return true if this Property is a list/array, false otherwise
  	 */
  	public boolean isList()
  	{
  		return this.isList;
  	}
  
  	/**
  	 * Whether or not this property is permitted to be reloaded from config file
  	 * when it is watched by beholder and is automatically reloaded from file
  	 * system upon being changed.
  	 *
  	 * @return true if so, false otherwise
  	 */
  
  	public boolean isReloadAllowed() {
  		return this.allowReload;
  	}
  
  	/**
  	 * Set whether or not this property should be allowed to reload upon config file
  	 * being automatically reloaded from file system when change is detected.
  	 * @param allowReload
  	 */
  
  	public void setAllowReload(boolean allowReload) {
  		this.allowReload = allowReload;
  	}
  
  	/**
  	 * Gets the changed status of this Property.
  	 *
  	 * @return true if this Property has changed, false otherwise
  	 */
  	public boolean hasChanged(){ return this.changed; }
  	void resetChangedState(){ this.changed = false; }
  
  	/**
  	 * Sets the value of this Property to the provided String value.
  	 */
  	public Property setValue(String value)
  	{
  		this.value = value;
  		this.changed = true;
  		return this;
  	}
  
  	public void set(String value)
  	{
  		this.setValue(value);
  	}
  
  	/**
  	 * Sets the values of this Property to the provided String[] values.
  	 */
  	public Property setValues(String[] values)
  	{
  		this.values = Arrays.copyOf(values, values.length);
  		this.changed = true;
  		return this;
  	}
  
  	public void set(String[] values)
  	{
  		this.setValues(values);
  	}
  
  	/**
  	 * Sets the value of this Property to the provided int value.
  	 */
  	public Property setValue(int value)
  	{
  		this.setValue(Integer.toString(value));
  		return this;
  	}
  
  	/**
  	 * Sets the value of this Property to the provided boolean value.
  	 */
  	public Property setValue(boolean value)
  	{
  		this.setValue(Boolean.toString(value));
  		return this;
  	}
  
  	/**
  	 * Sets the value of this Property to the provided double value.
  	 */
  	public Property setValue(double value)
  	{
  		this.setValue(Double.toString(value));
  		return this;
  	}
  
  	/**
  	 * Sets the values of this Property to the provided boolean[] values.
  	 */
  	public Property setValues(boolean[] values)
  	{
  		this.values = new String[values.length];
  		for (int i = 0; i < values.length; i++) {
  			this.values[i] = String.valueOf(values[i]);
  		}
  		this.changed = true;
  		return this;
  	}
  
  	public void set(boolean[] values)
  	{
  		this.setValues(values);
  	}
  
  	/**
  	 * Sets the values of this Property to the provided int[] values.
  	 */
  	public Property setValues(int[] values)
  	{
  		this.values = new String[values.length];
  		for (int i = 0; i < values.length; i++) {
  			this.values[i] = String.valueOf(values[i]);
  		}
  		this.changed = true;
  		return this;
  	}
  
  	public void set(int[] values)
  	{
  		this.setValues(values);
  	}
  
  	/**
  	 * Sets the values of this Property to the provided double[] values.
  	 */
  	public Property setValues(double[] values)
  	{
  		this.values = new String[values.length];
  		for (int i = 0; i < values.length; i++) {
  			this.values[i] = String.valueOf(values[i]);
  		}
  		this.changed = true;
  		return this;
  	}
  
  	public void set(double[] values)
  	{
  		this.setValues(values);
  	}
  	public void set(int     value){ this.set(Integer.toString(value)); }
  	public void set(boolean value){ this.set(Boolean.toString(value)); }
  	public void set(double  value){ this.set(Double.toString(value));  }
  }