View Javadoc

1   /*
2    * @(#)CIHashtable.java					0.3-3 06/05/2001
3    *
4    *  This file is part of the HTTPClient package
5    *  Copyright (C) 1996-2001 Ronald Tschalär
6    *
7    *  This library is free software; you can redistribute it and/or
8    *  modify it under the terms of the GNU Lesser General Public
9    *  License as published by the Free Software Foundation; either
10   *  version 2 of the License, or (at your option) any later version.
11   *
12   *  This library is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   *  Lesser General Public License for more details.
16   *
17   *  You should have received a copy of the GNU Lesser General Public
18   *  License along with this library; if not, write to the Free
19   *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20   *  MA 02111-1307, USA
21   *
22   *  For questions, suggestions, bug-reports, enhancement-requests etc.
23   *  I may be contacted at:
24   *
25   *  ronald@innovation.ch
26   *
27   *  The HTTPClient's home page is located at:
28   *
29   *  http://www.innovation.ch/java/HTTPClient/ 
30   *
31   * This file contains modifications for use with "The Grinder"
32   * (http://grinder.sourceforge.net) under the terms of the LGPL. They
33   * are marked below with the comment "GRINDER MODIFICATION".
34   */
35  
36  package HTTPClient;
37  
38  import java.util.Hashtable;
39  import java.util.Enumeration;
40  
41  /**
42   * This class implements a Hashtable with case-insensitive Strings as keys.
43   *
44   * @version	0.3-3  06/05/2001
45   * @author	Ronald Tschalär
46   */
47  class CIHashtable extends Hashtable
48  {
49      // Constructors
50  
51      /**
52       * Create a new CIHashtable with the specified initial capacity and the
53       * specified load factor.
54       *
55       * @param intialCapacity the initial number of buckets
56       * @param loadFactor a number between 0.0 and 1.0
57       * @see java.util.Hashtable(int, float)
58       */
59      public CIHashtable(int initialCapacity, float loadFactor)
60      {
61  	super(initialCapacity, loadFactor);
62      }
63  
64  
65      /**
66       * Create a new CIHashtable with the specified initial capacity.
67       *
68       * @param intialCapacity the initial number of buckets
69       * @see java.util.Hashtable(int)
70       */
71      public CIHashtable(int initialCapacity)
72      {
73  	super(initialCapacity);
74      }
75  
76  
77      /**
78       * Create a new CIHashtable with a default initial capacity.
79       *
80       * @see java.util.Hashtable()
81       */
82      public CIHashtable()
83      {
84  	super();
85      }
86  
87  
88      // Methods
89  
90      /**
91       * Retrieves the object associated with the specified key. The key lookup
92       * is case-insensitive.
93       *
94       * @param key the key
95       * @return the object associated with the key, or null if none found.
96       * @see java.util.Hashtable.get(Object)
97       */
98      public Object get(String key)
99      {
100 	return super.get(new CIString(key));
101     }
102 
103 
104     /**
105      * Stores the specified object with the specified key.
106      *
107      * @param key the key
108      * @param value the object to be associated with the key
109      * @return the object previously associated with the key, or null if
110      *         there was none.
111      * @see java.util.Hashtable.put(Object, Object)
112      */
113     public Object put(String key, Object value)
114     {
115 	return super.put(new CIString(key), value);
116     }
117 
118 
119     /**
120      * Looks whether any object is associated with the specified key. The
121      * key lookup is case insensitive.
122      *
123      * @param key the key
124      * @return true is there is an object associated with key, false otherwise
125      * @see java.util.Hashtable.containsKey(Object)
126      */
127     public boolean containsKey(String key)
128     {
129 	return super.containsKey(new CIString(key));
130     }
131 
132 
133     /**
134      * Removes the object associated with this key from the Hashtable. The
135      * key lookup is case insensitive.
136      *
137      * @param key the key
138      * @return the object associated with this key, or null if there was none.
139      * @see java.util.Hashtable.remove(Object)
140      */
141     public Object remove(String key)
142     {
143 	return super.remove(new CIString(key));
144     }
145 
146 
147     /**
148      * Returns an enumeration of all the keys in the Hashtable.
149      *
150      * @return the requested Enumerator
151      * @see java.util.Hashtable.keys(Object)
152      */
153     public Enumeration keys()
154     {
155 	return new CIHashtableEnumeration(super.keys());
156     }
157 }
158 
159 
160 /**
161  * A simple enumerator which delegates everything to the real enumerator.
162  * If a CIString element is returned, then the string it represents is
163  * returned instead.
164  */
165 final class CIHashtableEnumeration implements Enumeration
166 {
167     Enumeration HTEnum;
168 
169     /** ++GRINDER MODIFICIATION **/
170 //    public CIHashtableEnumeration(Enumeration enum)
171 //    {
172 //   	HTEnum = enum;
173 //    }
174     public CIHashtableEnumeration(Enumeration e)
175     {
176     HTEnum = e;
177     }
178     /** --GRINDER MODIFICIATION **/
179 
180     public boolean hasMoreElements()
181     {
182 	return HTEnum.hasMoreElements();
183     }
184 
185     public Object nextElement()
186     {
187 	Object tmp = HTEnum.nextElement();
188 	if (tmp instanceof CIString)
189 	    return ((CIString) tmp).getString();
190 
191 	return tmp;
192     }
193 }
194 
195 
196 /**
197  * This class' raison d'etre is that I want to use a Hashtable using
198  * Strings as keys and I want the lookup be case insensitive, but I
199  * also want to be able retrieve the keys with original case (otherwise
200  * I could just use toLowerCase() in the get() and put()). Since the
201  * class String is final we create a new class that holds the string
202  * and overrides the methods hashCode() and equals().
203  */
204 final class CIString
205 {
206     /** the string */
207     private String string;
208 
209     /** the hash code */
210     private int hash;
211 
212 
213     /** the constructor */
214     public CIString(String string)
215     {
216 	this.string = string;
217 	this.hash   = calcHashCode(string);
218     }
219 
220     /** return the original string */
221     public final String getString()
222     {
223 	return string;
224     }
225 
226     /** the hash code was precomputed */
227     public int hashCode()
228     {
229 	return hash;
230     }
231 
232 
233     /**
234      * We smash case before calculation so that the hash code is
235      * "case insensitive". This is based on code snarfed from
236      * java.lang.String.hashCode().
237      */
238     private static final int calcHashCode(String str)
239     {
240 	int  hash  = 0;
241 	char llc[] = lc;
242 	int  len   = str.length();
243 
244 	for (int idx= 0; idx<len; idx++)
245 	    hash = 31*hash + llc[str.charAt(idx)];
246 
247 	return hash;
248     }
249 
250 
251     /**
252      * Uses the case insensitive comparison.
253      */
254     public boolean equals(Object obj)
255     {
256 	if (obj != null)
257 	{
258 	    if (obj instanceof CIString)
259 		return string.equalsIgnoreCase(((CIString) obj).string);
260 
261 	    if (obj instanceof String)
262 		return string.equalsIgnoreCase((String) obj);
263 	}
264 
265 	return false;
266     }
267 
268     /**
269      * Just return the internal string.
270      */
271     public String toString()
272     {
273 	return string;
274     }
275 
276 
277     private static final char[] lc = new char[256];
278 
279     static
280     {
281 	// just ISO-8859-1
282 	for (char idx=0; idx<256; idx++)
283 	    lc[idx] = Character.toLowerCase(idx);
284     }
285 }