// // @(#)RuleOptions.java 10/2002 // // Copyright 2002 Zachary DelProposto. All rights reserved. // Use is subject to license terms. // // // 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. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Or from http://www.gnu.org/ // package dip.world; import dip.misc.Utils; import dip.world.variant.data.Variant; import java.io.Serializable; import java.io.ObjectStreamException; import java.io.InvalidObjectException; import java.util.HashMap; import java.util.Set; import java.util.Iterator; import java.lang.reflect.*; /** * RuleOptions is an object for storing Options and OptionValues that * describe rule variants. * <p> * Internationalization notes: * <p> <pre> OptionValue: getNameI18N() gets internationalized name (key is getName()) getDescriptionI18N() gets internationalized description (name + "_description") Option: getNameI18N() gets internationalized name getDescriptionI18N() gets internationalized description (name + "_description") </pre> * * * * * */ public class RuleOptions implements Serializable { /** * */ private static final long serialVersionUID = -6922232860462500876L; // internal constnats private static final String DESCRIPTION = "_description"; // il8n constants private static final String RO_BAD_OPTIONVALUE = "RuleOpts.parser.badoptionvalue"; private static final String RO_BAD_NVP = "RuleOpts.parser.badnvp"; // DO NOT change the names of these!! // pre-defined option values that are shared between multiple options /** TRUE (Boolean) OptionValue */ public static final OptionValue VALUE_TRUE = new OptionValue("OptionValue.true"); /** FALSE (Boolean) OptionValue */ public static final OptionValue VALUE_FALSE = new OptionValue("OptionValue.false"); // DO NOT change the names of these!! // defined options, and their values // BUILD options public static final OptionValue VALUE_BUILDS_HOME_ONLY = new OptionValue("OptionValue.home-only"); public static final OptionValue VALUE_BUILDS_ANY_OWNED = new OptionValue("OptionValue.any-owned"); public static final OptionValue VALUE_BUILDS_ANY_IF_HOME_OWNED = new OptionValue("OptionValue.any-if-home-owned"); public static final Option OPTION_BUILDS = new Option("Option.builds", VALUE_BUILDS_HOME_ONLY, new OptionValue[] {VALUE_BUILDS_HOME_ONLY, VALUE_BUILDS_ANY_OWNED, VALUE_BUILDS_ANY_IF_HOME_OWNED }); public static final OptionValue VALUE_WINGS_ENABLED = new OptionValue("OptionValue.wings-enabled"); public static final OptionValue VALUE_WINGS_DISABLED = new OptionValue("OptionValue.wings-disabled"); public static final Option OPTION_WINGS = new Option("Option.wings", VALUE_WINGS_DISABLED, new OptionValue[] { VALUE_WINGS_ENABLED, VALUE_WINGS_DISABLED }); public static final OptionValue VALUE_PATHS_EXPLICIT = new OptionValue("OptionValue.explicit-paths"); public static final OptionValue VALUE_PATHS_IMPLICIT = new OptionValue("OptionValue.implicit-paths"); public static final OptionValue VALUE_PATHS_EITHER = new OptionValue("OptionValue.either-path"); public static final Option OPTION_CONVOYED_MOVES = new Option("Option.move.convoyed", VALUE_PATHS_EITHER, new OptionValue[] { VALUE_PATHS_EITHER, VALUE_PATHS_EXPLICIT, VALUE_PATHS_IMPLICIT }); // array of default options, that are always set for every variant. private static final Option[] DEFAULT_RULE_OPTIONS = { OPTION_BUILDS, OPTION_WINGS, OPTION_CONVOYED_MOVES }; // NOTE: we must include all options / optionvalues in these arrays // OptionList -- for serialization/deserialization private static final Option[] ALL_OPTIONS = {OPTION_BUILDS, OPTION_WINGS, OPTION_CONVOYED_MOVES}; // OptionValue -- for serialization/deserialization private static final OptionValue[] ALL_OPTIONVALUES = { VALUE_BUILDS_HOME_ONLY, VALUE_BUILDS_ANY_OWNED, VALUE_BUILDS_ANY_IF_HOME_OWNED, VALUE_WINGS_ENABLED, VALUE_WINGS_DISABLED, VALUE_PATHS_EXPLICIT, VALUE_PATHS_IMPLICIT, VALUE_PATHS_EITHER }; // instance variables protected HashMap optionMap = null; /** * An Option is created for each type of Rule that may have more than * one allowable option. The name of each Option must be unique. * * */ public static class Option implements Serializable { // instance variables protected final String name; protected final OptionValue[] allowed; protected final OptionValue defaultValue; /** Create an Option. */ public Option(String name, OptionValue defaultValue, OptionValue[] allowed) { if(defaultValue == null || name == null || allowed == null) { throw new IllegalArgumentException(); } this.name = name; this.defaultValue = defaultValue; this.allowed = allowed; }// Option() /** Returns the Option name. */ public String getName() { return name; } /** Returns the default Option value. */ public OptionValue getDefault() { return defaultValue; } /** Returns the allowed Option values. */ public OptionValue[] getAllowed() { return allowed; } /** Checks if the given optionValue is allowed. */ public boolean isAllowed(OptionValue optionValue) { for(int i=0; i<allowed.length; i++) { if(optionValue == allowed[i]) { return true; } } return false; }// isAllowed() /** Gets the internationalized ("display") version of the name. */ public String getNameI18N() { return Utils.getLocalString(getName()); } /** Gets the internationalized ("display") version of the description. */ public String getDescriptionI18N() { return Utils.getLocalString(getName()+DESCRIPTION); } /** Checks if the given OptionValue is permitted; if so, returns true. */ public boolean checkValue(OptionValue value) { for(int i=0; i<allowed.length; i++) { if(allowed[i].equals(value)) { return true; } } return false; }// checkValue() public boolean equals(Object obj) { return name.equals( ((Option) obj).name ); }// equals() public int hashCode() { return name.hashCode(); }// hashCode() protected Object readResolve() throws java.io.ObjectStreamException { // slow but easy for(int i=0; i<ALL_OPTIONS.length; i++) { if( name.equals(ALL_OPTIONS[i].name) ) { return ALL_OPTIONS[i]; } } throw new InvalidObjectException("RuleOptions: ALL_OPTIONS internal error"); }// readResolve() /** For debugging only */ public String toString() { return name; } }// nested class Option /** * OptionValues are the pre-defined values that an Option may have. * <p> * OptionValue names need not be unique, and may be shared between * options. */ public static class OptionValue implements Serializable { // instance variables final String name; /** Create an OptionValue. */ public OptionValue(String name) { if(name == null) { throw new IllegalArgumentException(); } this.name = name; }// OptionValue() /** Returns the OptionValue name. */ public String getName() { return name; }// getName() /** Gets the internationalized ("display") version of the name. */ public String getNameI18N() { return Utils.getLocalString(getName()); } /** Gets the internationalized ("display") version of the description. */ public String getDescriptionI18N() { return Utils.getLocalString(getName()+DESCRIPTION); } public boolean equals(Object obj) { return name.equals( ((OptionValue) obj).name ); }// equals() public int hashCode() { return name.hashCode(); }// hashCode() protected Object readResolve() throws java.io.ObjectStreamException { // slow but easy for(int i=0; i<ALL_OPTIONVALUES.length; i++) { if( name.equals(ALL_OPTIONVALUES[i].name) ) { return ALL_OPTIONVALUES[i]; } } throw new InvalidObjectException("RuleOptions: ALL_OPTIONVALUES internal error"); }// readResolve() /** For debugging only */ public String toString() { return name; } }// nested class OptionValue() /** Creates a new RuleOptions object, which stores various Rule options. */ public RuleOptions() { optionMap = new HashMap(31); }// RuleOptions() /** * Sets the OptionValue for an Option. * <p> * Null Options or OptionValues are not permitted. If an invalid OptionValue * is given, an IllegalArgumentException is thrown. * */ public void setOption(Option option, OptionValue value) { if(option == null || value == null) { throw new IllegalArgumentException("null Option or OptionValue"); } if(!option.checkValue(value)) { throw new IllegalArgumentException("invalid OptionValue for Option"); } optionMap.put(option, value); }// setOption() /** * Obtains the value for an Option. If the Option is not found, or its OptionValue * not set, the default OptionValue is returned. * <p> * A null Option is not permitted. */ public OptionValue getOptionValue(Option option) { if(option == null) { throw new IllegalArgumentException("null Option"); } OptionValue value = (OptionValue) optionMap.get(option); if(value == null) { return option.getDefault(); } return value; }// getOption() /** Returns a Set of all Options. */ public Set getAllOptions() { return optionMap.keySet(); }// getAllOptions() /** For debugging only; print the rule options */ public String toString() { StringBuffer sb = new StringBuffer(256); sb.append(this.getClass().getName()); sb.append('\n'); Set set = getAllOptions(); Iterator iter = set.iterator(); while(iter.hasNext()) { Option opt = (Option) iter.next(); OptionValue ov = getOptionValue(opt); sb.append(" "); sb.append(opt); sb.append(" : "); sb.append(ov); sb.append('\n'); } return sb.toString(); }// toString() /** * * Create a RuleOptions from a Variant. * <p> * An InvalidWorldException is thrown if the passed data * is invalid. * */ public static RuleOptions createFromVariant(Variant variant) throws InvalidWorldException { // create ruleoptions // set rule options RuleOptions ruleOpts = new RuleOptions(); // set default rule options for(int i=0; i<DEFAULT_RULE_OPTIONS.length; i++) { ruleOpts.setOption(DEFAULT_RULE_OPTIONS[i], DEFAULT_RULE_OPTIONS[i].getDefault()); } // this class Class clazz = ruleOpts.getClass(); // look up all name-value pairs via reflection. Variant.NameValuePair[] nvps = variant.getRuleOptionNVPs(); for(int i=0; i<nvps.length; i++) { Option option = null; OptionValue optionValue = null; // first, check the name try { Field field = clazz.getField(nvps[i].getName()); option = (Option) field.get(null); field = clazz.getField(nvps[i].getValue()); optionValue = (OptionValue) field.get(null); } catch(Exception e) { throw new InvalidWorldException( Utils.getLocalString(RO_BAD_NVP, nvps[i].getName(), nvps[i].getValue(), e.getMessage()) ); } // ensure that optionValue is valid for option if( !option.isAllowed(optionValue) ) { throw new InvalidWorldException( Utils.getLocalString(RO_BAD_OPTIONVALUE, nvps[i].getValue(), nvps[i].getName()) ); } // set option ruleOpts.setOption(option, optionValue); } // done. return ruleOpts; }// createFromVariant() }// class RuleOptions