001: // Copyright (c) 2003 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.text;
005:
006: import java.util.Hashtable;
007: import java.util.Vector;
008: import java.util.Enumeration;
009:
010: /** Mananges a table of named options,
011: * Can inherit from another table of "default" options. */
012:
013: public class Options {
014: /** Bit indicating option value is a boolean. */
015: public static final int BOOLEAN_OPTION = 1;
016:
017: public static final int STRING_OPTION = 2;
018:
019: /** The option table contain defaults, that we "inherit" from. */
020: Options previous;
021:
022: OptionInfo first;
023: OptionInfo last;
024:
025: public Options() {
026: }
027:
028: public Options(Options previous) {
029: this .previous = previous;
030: }
031:
032: /** Maps property keys to options values. */
033: Hashtable valueTable;
034:
035: /** Maps property keys to OptionInfo. */
036: Hashtable infoTable;
037:
038: /** Create a new option and enters it in this table.
039: * A duplicate option throws a RuntimeException.
040: * @param key the options name (key).
041: * @param kind type and other flag bits of the option.
042: * @param documentation a String describing what the option does. */
043: public void add(String key, int kind, String documentation) {
044: if (infoTable == null)
045: infoTable = new Hashtable();
046: else if (infoTable.get(key) != null)
047: throw new RuntimeException("duplicate option key: " + key);
048: OptionInfo info = new OptionInfo();
049: info.key = key;
050: info.kind = kind;
051: info.documentation = documentation;
052: if (first == null)
053: first = info;
054: else
055: last.next = info;
056: last = info;
057: infoTable.put(key, info);
058: }
059:
060: static Object valueOf(OptionInfo info, String argument) {
061: if ((info.kind & BOOLEAN_OPTION) != 0) {
062: if (argument == null || argument.equals("1")
063: || argument.equals("on") || argument.equals("yes")
064: || argument.equals("true"))
065: return Boolean.TRUE;
066: if (argument.equals("0") || argument.equals("off")
067: || argument.equals("no")
068: || argument.equals("false"))
069: return Boolean.FALSE;
070: return null;
071: }
072: return argument;
073: }
074:
075: private void error(String message, SourceMessages messages) {
076: if (messages == null)
077: throw new RuntimeException(message);
078: else
079: messages.error('e', message);
080: }
081:
082: /** Set the value of a named option. */
083: public void set(String key, Object value) {
084: set(key, value, null);
085: }
086:
087: /** Set the value of a named option. */
088: public void set(String key, Object value, SourceMessages messages) {
089: OptionInfo info = getInfo(key);
090: if (info == null) {
091: error("invalid option key: " + key, messages);
092: return;
093: }
094: if ((info.kind & BOOLEAN_OPTION) != 0) {
095: if (value instanceof String)
096: value = valueOf(info, (String) value);
097: if (!(value instanceof Boolean)) {
098: error(
099: "value for option "
100: + key
101: + " must be boolean or yes/no/true/false/on/off/1/0",
102: messages);
103: return;
104: }
105: } else if (value == null)
106: value = "";
107: if (valueTable == null)
108: valueTable = new Hashtable();
109: valueTable.put(key, value);
110: }
111:
112: /** Reset the value of a named option. */
113: public void reset(String key, Object oldValue) {
114: if (valueTable == null)
115: valueTable = new Hashtable();
116: if (oldValue == null)
117: valueTable.remove(key);
118: else
119: valueTable.put(key, oldValue);
120: }
121:
122: public static final String UNKNOWN = "unknown option name";
123:
124: /** Set the value of the key to the argument, appropriate parsed.
125: * return null on success or a String error message.
126: * If the option key is invalid, return UNKNOWN. */
127: public String set(String key, String argument) {
128: OptionInfo info = getInfo(key);
129: if (info == null)
130: return UNKNOWN;
131: Object value = valueOf(info, argument);
132: if (value == null) {
133: if ((info.kind & BOOLEAN_OPTION) != 0)
134: return "value of option " + key
135: + " must be yes/no/true/false/on/off/1/0";
136: }
137: if (valueTable == null)
138: valueTable = new Hashtable();
139: valueTable.put(key, value);
140: return null;
141: }
142:
143: public OptionInfo getInfo(String key) {
144: Object info = infoTable == null ? null : infoTable.get(key);
145: if (info == null && previous != null)
146: info = previous.getInfo(key);
147: return (OptionInfo) info;
148: }
149:
150: /** Get the value for the option.
151: * Throws an except if there is no option by that name,
152: * Returns defaultValue if there is such an option, but it
153: * hasn't been set. */
154: public Object get(String key, Object defaultValue) {
155: Object val = valueTable == null ? null : valueTable.get(key);
156: if (val != null)
157: return val;
158: if (previous != null)
159: return previous.get(key, defaultValue);
160: OptionInfo info = getInfo(key);
161: if (info == null)
162: throw new RuntimeException("invalid option key: " + key);
163: return defaultValue;
164: }
165:
166: /** Get current option value.
167: * Only look in local table, not in inherited Options.
168: * Return null if there is no binding (even when get would
169: * throw an except on an unknonw option).
170: */
171: public Object getLocal(String key) {
172: return valueTable == null ? null : valueTable.get(key);
173: }
174:
175: public boolean getBoolean(String key) {
176: return ((Boolean) get(key, Boolean.FALSE)).booleanValue();
177: }
178:
179: public boolean getBoolean(String key, boolean defaultValue) {
180: Boolean defaultObject = defaultValue ? Boolean.TRUE
181: : Boolean.FALSE;
182: return ((Boolean) get(key, defaultObject)).booleanValue();
183: }
184:
185: /** Set a list of options, remember the old value.
186: * @param options is vector of triples, echo of which is consisting of:
187: * a String option key;
188: * an entry whose valus is ignores and is used to store the old value; and
189: * a new value for the options.
190: */
191: public void pushOptionValues(Vector options) {
192: int len = options.size();
193: for (int i = 0; i < len;) {
194: String key = (String) options.elementAt(i++);
195: Object newValue = options.elementAt(i);
196: options.setElementAt(newValue, i++);
197: set(key, options.elementAt(i++));
198: }
199: }
200:
201: /** Restore a list of options, as set by pushOptionValues
202: */
203: public void popOptionValues(Vector options) {
204: for (int i = options.size(); (i -= 3) >= 0;) {
205: String key = (String) options.elementAt(i);
206: Object oldValue = options.elementAt(i + 1);
207: options.setElementAt(null, i + 1);
208: reset(key, oldValue);
209: }
210: }
211:
212: /** Return the list of option keys.
213: */
214: public Vector keys() {
215: Vector allKeys = new Vector();
216: for (Options options = this ; options != null; options = options.previous) {
217: if (options.infoTable != null) {
218: Enumeration e = options.infoTable.keys();
219: while (e.hasMoreElements()) {
220: Object k = e.nextElement();
221: if (!allKeys.contains(k))
222: allKeys.add(k);
223: }
224: }
225: }
226: return allKeys;
227: }
228:
229: public String getDoc(String key) {
230: OptionInfo info = getInfo(key);
231: if (key == null)
232: return null;
233: return info.documentation;
234: }
235:
236: }
237:
238: final class OptionInfo {
239: OptionInfo next;
240: String key;
241: int kind;
242: String documentation;
243: }
|