001: /*
002: * StringMap.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999-2000 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: cstevens.
018: * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.11
024: * Created by cstevens on 99/09/15
025: * Last modified by cstevens on 00/03/29 16:46:29
026: */
027:
028: package sunlabs.brazil.util;
029:
030: import java.util.Dictionary;
031: import java.util.Enumeration;
032: import java.util.Vector;
033:
034: /**
035: * The <code>StringMap</code> class is a substitute for the Hashtable.
036: * The StringMap has the following properties: <ul>
037: * <li> Maps case-insensitive string keys to string values.
038: * <li> The case of the keys is preserved.
039: * <li> Values may be <code>null</code>.
040: * <li> Preserves the relative order of the data.
041: * <li> The same key may appear multiple times in a single map.
042: * <li> This map is implemented via a Vector, and as such, as the number of
043: * keys increases, the time required to search will go up.
044: * </ul>
045: *
046: * @author Colin Stevens (colin.stevens@sun.com)
047: * @version 1.11, 00/03/29
048: */
049: public class StringMap extends Dictionary {
050: Vector keys;
051: Vector values;
052:
053: /**
054: * Creates an empty StringMap.
055: */
056: public StringMap() {
057: keys = new Vector();
058: values = new Vector();
059: }
060:
061: /**
062: * Returns the number of elements in this StringMap. Every occurrence of
063: * keys that appear multiple times is counted.
064: *
065: * @return The number of elements in this StringMap.
066: *
067: * @see #keys
068: *
069: * @implements Dictionary#size
070: */
071: public int size() {
072: return keys.size();
073: }
074:
075: /**
076: * Tests if there are any elements in this StringMap.
077: *
078: * @return Returns <code>true</code> if there are no elements,
079: * <code>false</code> otherwise.
080: *
081: * @implements Dictionary#isEmpty
082: */
083: public boolean isEmpty() {
084: return keys.isEmpty();
085: }
086:
087: /**
088: * Returns an enumeration of the keys in this StringMap. The elements
089: * of the enumeration are strings.
090: * <p>
091: * The same key may appear multiple times in the enumeration, not
092: * necessarily consecutively. Since <code>get</code> always returns
093: * the value associated with the first occurrence of a given key, a
094: * StringMap cannot be enumerated in the same fashion as a Hashtable.
095: * Instead, the caller should use:
096: * <pre>
097: * Enumeration keys = map.keys();
098: * Enumeration values = map.elements();
099: * while (keys.hasMoreElements()) {
100: * String key = (String) keys.nextElement();
101: * String value = (String) values.nextElement();
102: * }
103: * </pre>
104: * or:
105: * <pre>
106: * for (int i = 0; i < map.size(); i++) {
107: * String key = map.getKey(i);
108: * String value = map.get(i);
109: * }
110: * </pre>
111: *
112: * @return An enumeration of the keys.
113: *
114: * @see #elements
115: * @see #size
116: * @see #getKey
117: * @see #get
118: *
119: * @implements Dictionary#keys
120: */
121: public Enumeration keys() {
122: return keys.elements();
123: }
124:
125: /**
126: * Returns an enumeration of the values in this StringMap. The elements
127: * of the enumeration are strings.
128: *
129: * @return An enumeration of the values.
130: *
131: * @see #keys
132: *
133: * @implements Dictionary#elements
134: */
135: public Enumeration elements() {
136: return values.elements();
137: }
138:
139: /**
140: * Returns the key at the specified index. The index ranges from
141: * <code>0</code> to <code>size() - 1</code>.
142: * <p>
143: * This method can be used to iterate over all the keys in this
144: * StringMap in the order in which they were inserted, subject to any
145: * intervening deletions.
146: *
147: * @param index
148: * The index of the key.
149: *
150: * @return The key at the specified index.
151: *
152: * @throws IndexOutOfBoundsException
153: * if the index is out of the allowed range.
154: */
155: public String getKey(int index) throws IndexOutOfBoundsException {
156: return (String) keys.elementAt(index);
157: }
158:
159: /**
160: * Returns the value at the specified index. The index ranges from
161: * <code>0</code> to <code>size() - 1</code>.
162: * <p>
163: * This method can be used to iterate over all the values in this
164: * StringMap in the order in which they were inserted, subject to any
165: * intervening deletions.
166: *
167: * @param index
168: * The index of the key.
169: *
170: * @return The value at the specified index.
171: *
172: * @throws IndexOutOfBoundsException
173: * if the index is out of the allowed range.
174: */
175: public String get(int index) throws IndexOutOfBoundsException {
176: return (String) values.elementAt(index);
177: }
178:
179: /**
180: * Returns the value that the specified case-insensitive key maps to
181: * in this StringMap.
182: * <p>
183: * The same key may appear multiple times in the enumeration; this
184: * method always returns the value associated with the first
185: * occurrence of the specified key. In order to get all the values,
186: * it is necessary to iterate over the entire StringMap to retrieve
187: * all the values associated with a given key.
188: *
189: * @param key
190: * A key in this StringMap. May not be <code>null</code>.
191: *
192: * @return The value to which the specified key is mapped, or
193: * <code>null</code> if the key is not in the StringMap.
194: *
195: * @see #keys
196: */
197: public String get(String key) {
198: int i = indexOf(key);
199: if (i >= 0) {
200: return (String) values.elementAt(i);
201: } else {
202: return null;
203: }
204: }
205:
206: /**
207: * Performs the same job as <code>get(String)</code>. It exists so
208: * this class can extend the <code>Dictionary</code> class.
209: *
210: * @param key
211: * Must be a String.
212: *
213: * @return A String value.
214: *
215: * @throws ClassCastException
216: * if the <code>key</code> is not a String.
217: *
218: * @see #get(String)
219: *
220: * @implements Dictionary#get
221: */
222: public Object get(Object key) {
223: return get((String) key);
224: }
225:
226: /**
227: * Maps the key at the given index to the specified value in this
228: * StringMap. The index ranges from <code>0</code> to
229: * <code>size() - 1</code>.
230: *
231: * @param index
232: * The index of the key.
233: *
234: * @return The value at the specified index.
235: *
236: * @throws IndexOutOfBoundsException
237: * if the index is out of the allowed range.
238: */
239: public void put(int index, String value) {
240: values.setElementAt(value, index);
241: }
242:
243: /**
244: * Maps the given case-insensitive key to the specified value in this
245: * StringMap.
246: * <p>
247: * The value can be retrieved by calling <code>get</code> with a
248: * key that is case-insensitive equal to the given key.
249: * <p>
250: * If this StringMap already contained a mapping for the given key,
251: * the old value is forgotten and the new specified value is used.
252: * The case of the prior key is retained in that case. Otherwise
253: * the case of the new key is used.
254: *
255: * @param key
256: * The new key. May not be <code>null</code>.
257: *
258: * @param value
259: * The new value. May be <code>null</code>.
260: *
261: * @return The previous value to which <code>key</code> was mapped,
262: * or <code>null</code> if the the key did not map to any
263: * value.
264: */
265: public void put(String key, String value) {
266: int i = indexOf(key);
267: if (i < 0) {
268: keys.addElement(key);
269: values.addElement(value);
270: } else {
271: values.setElementAt(value, i);
272: }
273: }
274:
275: /**
276: * Performs the same job as <code>put(String, String)</code>. It exists
277: * so this class can extend the <code>Dictionary</code> class.
278: *
279: * @param key
280: * Must be a String.
281: *
282: * @param value
283: * Must be a String.
284: *
285: * @return The previous value to which <code>key</code> was mapped,
286: * or <code>null</code> if the the key did not map to any
287: * value.
288: *
289: * @throws ClassCastException
290: * if the <code>key</code> or <code>value</code> is not a
291: * String.
292: *
293: * @see #put(String, String)
294: *
295: * @implements Dictionary#put
296: */
297: public Object put(Object key, Object value) {
298: String skey = (String) key;
299: String svalue = (String) value;
300:
301: Object prior;
302:
303: int i = indexOf(skey);
304: if (i < 0) {
305: prior = null;
306: keys.addElement(skey);
307: values.addElement(svalue);
308: } else {
309: prior = values.elementAt(i);
310: values.setElementAt(svalue, i);
311: }
312: return prior;
313: }
314:
315: /**
316: * Maps the given case-insensitive key to the specified value in this
317: * StringMap.
318: * <p>
319: * The new mapping is added to this StringMap even if the given key
320: * already has a mapping. In this way it is possible to create a key
321: * that maps to two or more values.
322: * <p>
323: * Since the same key may appear multiple times in this StringMap, it
324: * is necessary to iterate over the entire StringMap to retrieve all
325: * values associated with a given key.
326: *
327: * @param key
328: * The new key. May not be <code>null</code>.
329: *
330: * @param value
331: * The new value. May be <code>null</code>.
332: *
333: * @see #put(String, String)
334: * @see #keys
335: */
336: public void add(String key, String value) {
337: keys.addElement(key);
338: values.addElement(value);
339: }
340:
341: /**
342: * Removes the given case-insensitive key and its corresponding value
343: * from this StringMap. This method does nothing if the key is not in
344: * this StringMap.
345: * <p>
346: * The same key may appear in multiple times in this StringMap; this
347: * method only removes the first occurrence of the key.
348: *
349: * @param key
350: * The key that needs to be removed. Must not be
351: * <code>null</code>.
352: */
353: public void remove(String key) {
354: int i = indexOf(key);
355: if (i >= 0) {
356: remove(i);
357: }
358: }
359:
360: public void remove(int i) {
361: keys.removeElementAt(i);
362: values.removeElementAt(i);
363: }
364:
365: /**
366: * Performs the same job as <code>remove(String)</code>. It exists so
367: * this class can extend the <code>Dictionary</code> class.
368: *
369: * @param key
370: * Must be a String.
371: *
372: * @return The string value to which the key had been mapped, or
373: * <code>null</code> if the key did not have a mapping.
374: *
375: * @throws ClassCastException
376: * if the <code>key</code> is not a String.
377: *
378: * @implements Dictionary#remove
379: */
380: public Object remove(Object key) {
381: int i = indexOf((String) key);
382: if (i >= 0) {
383: Object prior = values.elementAt(i);
384: remove(i);
385: return prior;
386: }
387: return null;
388: }
389:
390: /**
391: * Removes all the keys and values from this StringMap.
392: */
393: public void clear() {
394: keys.setSize(0);
395: values.setSize(0);
396: }
397:
398: private int indexOf(String key) {
399: int length = keys.size();
400: for (int i = 0; i < length; i++) {
401: String got = (String) keys.elementAt(i);
402: if (key.equalsIgnoreCase(got)) {
403: return i;
404: }
405: }
406: return -1;
407: }
408:
409: /**
410: * Returns a string representation of this <code>StringMap</code> in the
411: * form of a set of entries, enclosed in braces and separated by the
412: * characters ", ". Each entry is rendered as the key, an equals sign
413: * "=", and the associated value.
414: *
415: * @return The string representation of this <code>StringMap</code>.
416: */
417: public String toString() {
418: StringBuffer sb = new StringBuffer();
419:
420: sb.append('{');
421:
422: int length = keys.size();
423: for (int i = 0; i < length; i++) {
424: sb.append(getKey(i));
425: sb.append('=');
426: sb.append(get(i));
427: sb.append(", ");
428: }
429: if (sb.length() > 1) {
430: sb.setLength(sb.length() - 2);
431: }
432: sb.append('}');
433:
434: return sb.toString();
435: }
436: }
|