KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > joram > shared > util > Properties


1 /*
2  * JORAM: Java(TM) Open Reliable Asynchronous Messaging
3  * Copyright (C) 2006 ScalAgent Distributed Technologies
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA.
19  *
20  * Initial developer(s): ScalAgent Distributed Technologies
21  * Contributor(s):
22  */

23 package org.objectweb.joram.shared.util;
24
25 import java.io.*;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.NoSuchElementException JavaDoc;
29 import java.util.ConcurrentModificationException JavaDoc;
30
31 import org.objectweb.joram.shared.stream.*;
32
33 import org.objectweb.joram.shared.JoramTracing;
34 import org.objectweb.util.monolog.api.BasicLevel;
35
36 /**
37  * This class implements a set of properties, which maps keys to values.
38  * Only string object can be used as a key, all primitives type can be used
39  * as a value. <p>
40  */

41 public class Properties implements Cloneable JavaDoc {
42   /** The total number of entries in the hash table. */
43   private transient int count;
44   /** The hash table data. */
45   private transient Entry table[];
46
47   /**
48    * The table is rehashed when its size exceeds this threshold. (The
49    * value of this field is (int)(capacity * loadFactor).)
50    */

51   private transient int threshold;
52                              
53   /** The load factor for the hashtable. */
54   private transient float loadFactor;
55
56   /**
57    * The number of times this Properties has been structurally modified
58    * Structural modifications are those that change the number of entries in
59    * the Properties or otherwise modify its internal structure (e.g.,
60    * rehash). This field is used to make iterators on Collection-views of
61    * the Properties fail-fast. (See ConcurrentModificationException).
62    */

63   private transient int modCount = 0;
64
65   /**
66    * Constructs a new, empty hashtable with the specified initial
67    * capacity and the specified load factor.
68    *
69    * @param initialCapacity the initial capacity of the hashtable.
70    * @param loadFactor the load factor of the hashtable.
71    * @exception IllegalArgumentException if the initial capacity is less
72    * than zero, or if the load factor is nonpositive.
73    */

74   public Properties(int initialCapacity, float loadFactor) {
75     if (initialCapacity < 0)
76       throw new IllegalArgumentException JavaDoc("Illegal Capacity: "+
77                                          initialCapacity);
78     if (loadFactor <= 0 || Float.isNaN(loadFactor))
79       throw new IllegalArgumentException JavaDoc("Illegal Load: "+loadFactor);
80
81     if (initialCapacity==0)
82       initialCapacity = 1;
83     this.loadFactor = loadFactor;
84     table = new Entry[initialCapacity];
85     threshold = (int)(initialCapacity * loadFactor);
86   }
87
88   /**
89    * Constructs a new, empty hashtable with the specified initial capacity
90    * and default load factor, which is <tt>0.75</tt>.
91    *
92    * @param initialCapacity the initial capacity of the hashtable.
93    * @exception IllegalArgumentException if the initial capacity is less
94    * than zero.
95    */

96   public Properties(int initialCapacity) {
97     this(initialCapacity, 0.75f);
98   }
99
100   /**
101    * Constructs a new, empty hashtable with a default initial capacity (11)
102    * and load factor, which is <tt>0.75</tt>.
103    */

104   public Properties() {
105     this(11, 0.75f);
106   }
107
108   /**
109    * Returns the number of keys in this hashtable.
110    *
111    * @return the number of keys in this hashtable.
112    */

113   public synchronized int size() {
114     return count;
115   }
116
117   /**
118    * Tests if this hashtable maps no keys to values.
119    *
120    * @return <code>true</code> if this hashtable maps no keys to values;
121    * <code>false</code> otherwise.
122    */

123   public synchronized boolean isEmpty() {
124     return count == 0;
125   }
126
127   /**
128    * Returns an enumeration of the keys in this hashtable.
129    *
130    * @return an enumeration of the keys in this hashtable.
131    * @see Enumeration
132    * @see #elements()
133    * @see #keySet()
134    * @see Map
135    */

136   public synchronized Enumeration JavaDoc keys() {
137     return getEnumeration(KEYS);
138   }
139
140   /**
141    * Returns an enumeration of the values in this hashtable.
142    * Use the Enumeration methods on the returned object to fetch the elements
143    * sequentially.
144    *
145    * @return an enumeration of the values in this hashtable.
146    * @see java.util.Enumeration
147    * @see #keys()
148    * @see #values()
149    * @see Map
150    */

151   public synchronized Enumeration JavaDoc elements() {
152     return getEnumeration(VALUES);
153   }
154
155   /**
156    * Tests if the specified object is a key in this hashtable.
157    *
158    * @param key possible key.
159    * @return <code>true</code> if and only if the specified object
160    * is a key in this hashtable, as determined by the
161    * <tt>equals</tt> method; <code>false</code> otherwise.
162    * @throws NullPointerException if the key is <code>null</code>.
163    * @see #contains(Object)
164    */

165   public synchronized boolean containsKey(String JavaDoc key) {
166     Entry tab[] = table;
167     int hash = key.hashCode();
168     int index = (hash & 0x7FFFFFFF) % tab.length;
169     for (Entry e = tab[index] ; e != null ; e = e.next) {
170       if ((e.hash == hash) && e.key.equals(key)) {
171         return true;
172       }
173     }
174     return false;
175   }
176
177   /**
178    * Returns the value to which the specified key is mapped in this hashtable.
179    *
180    * @param key a key in the hashtable.
181    * @return the value to which the key is mapped in this hashtable;
182    * <code>null</code> if the key is not mapped to any value in
183    * this hashtable.
184    * @throws NullPointerException if the key is <code>null</code>.
185    * @see #put(Object, Object)
186    */

187   public synchronized Object JavaDoc get(String JavaDoc key) {
188     Entry tab[] = table;
189     int hash = key.hashCode();
190     int index = (hash & 0x7FFFFFFF) % tab.length;
191     for (Entry e = tab[index] ; e != null ; e = e.next) {
192       if ((e.hash == hash) && e.key.equals(key)) {
193         return e.value;
194       }
195     }
196     return null;
197   }
198
199   /**
200    * Increases the capacity of and internally reorganizes this
201    * hashtable, in order to accommodate and access its entries more
202    * efficiently. This method is called automatically when the
203    * number of keys in the hashtable exceeds this hashtable's capacity
204    * and load factor.
205    */

206   protected void rehash() {
207     int oldCapacity = table.length;
208     Entry oldMap[] = table;
209
210     int newCapacity = oldCapacity * 2 + 1;
211     Entry newMap[] = new Entry[newCapacity];
212
213     modCount++;
214     threshold = (int)(newCapacity * loadFactor);
215     table = newMap;
216
217     for (int i = oldCapacity ; i-- > 0 ;) {
218       for (Entry old = oldMap[i] ; old != null ; ) {
219         Entry e = old;
220         old = old.next;
221
222         int index = (e.hash & 0x7FFFFFFF) % newCapacity;
223         e.next = newMap[index];
224         newMap[index] = e;
225       }
226     }
227   }
228
229   /**
230    * Maps the specified <code>key</code> to the specified
231    * <code>value</code> in this hashtable. Neither the key nor the
232    * value can be <code>null</code>. <p>
233    *
234    * The value can be retrieved by calling the <code>get</code> method
235    * with a key that is equal to the original key.
236    *
237    * @param key the hashtable key.
238    * @param value the value.
239    * @return the previous value of the specified key in this hashtable,
240    * or <code>null</code> if it did not have one.
241    * @exception NullPointerException if the key or value is
242    * <code>null</code>.
243    * @see Object#equals(Object)
244    * @see #get(Object)
245    */

246   public synchronized Object JavaDoc put(String JavaDoc key, Object JavaDoc value) {
247     // Make sure the value is not null
248
if (value == null) {
249       throw new NullPointerException JavaDoc();
250     }
251
252     // Makes sure the key is not already in the hashtable.
253
Entry tab[] = table;
254     int hash = key.hashCode();
255     int index = (hash & 0x7FFFFFFF) % tab.length;
256     for (Entry e = tab[index] ; e != null ; e = e.next) {
257       if ((e.hash == hash) && e.key.equals(key)) {
258         Object JavaDoc old = e.value;
259         e.value = value;
260         return old;
261       }
262     }
263
264     modCount++;
265     if (count >= threshold) {
266       // Rehash the table if the threshold is exceeded
267
rehash();
268
269       tab = table;
270       index = (hash & 0x7FFFFFFF) % tab.length;
271     }
272
273     // Creates the new entry.
274
Entry e = new Entry(hash, key, value, tab[index]);
275     tab[index] = e;
276     count++;
277     return null;
278   }
279
280   /**
281    * Removes the key (and its corresponding value) from this
282    * hashtable. This method does nothing if the key is not in the hashtable.
283    *
284    * @param key the key that needs to be removed.
285    * @return the value to which the key had been mapped in this hashtable,
286    * or <code>null</code> if the key did not have a mapping.
287    * @throws NullPointerException if the key is <code>null</code>.
288    */

289   public synchronized Object JavaDoc remove(String JavaDoc key) {
290     Entry tab[] = table;
291     int hash = key.hashCode();
292     int index = (hash & 0x7FFFFFFF) % tab.length;
293     for (Entry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
294       if ((e.hash == hash) && e.key.equals(key)) {
295         modCount++;
296         if (prev != null) {
297           prev.next = e.next;
298         } else {
299           tab[index] = e.next;
300         }
301         count--;
302         Object JavaDoc oldValue = e.value;
303         e.value = null;
304         return oldValue;
305       }
306     }
307     return null;
308   }
309
310   /**
311    * Clears this hashtable so that it contains no keys.
312    */

313   public synchronized void clear() {
314     Entry tab[] = table;
315     modCount++;
316     for (int index = tab.length; --index >= 0; )
317       tab[index] = null;
318     count = 0;
319   }
320
321   /**
322    * Creates a shallow copy of this hashtable. All the structure of the
323    * hashtable itself is copied, but the keys and values are not cloned.
324    * This is a relatively expensive operation.
325    *
326    * @return a clone of the hashtable.
327    */

328   public synchronized Object JavaDoc clone() {
329     try {
330       Properties t = (Properties) super.clone();
331       t.table = new Entry[table.length];
332       for (int i = table.length ; i-- > 0 ; ) {
333         t.table[i] = (table[i] != null)
334           ? (Entry)table[i].clone() : null;
335       }
336       t.modCount = 0;
337       return t;
338     } catch (CloneNotSupportedException JavaDoc e) {
339       // this shouldn't happen, since we are Cloneable
340
throw new InternalError JavaDoc();
341     }
342   }
343
344   /**
345    * Returns a string representation of this <tt>Properties</tt> object
346    * in the form of a set of entries, enclosed in braces and separated
347    * by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space). Each
348    * entry is rendered as the key, an equals sign <tt>=</tt>, and the
349    * associated element, where the <tt>toString</tt> method is used to
350    * convert the key and element to strings. <p>Overrides to
351    * <tt>toString</tt> method of <tt>Object</tt>.
352    *
353    * @return a string representation of this hashtable.
354    */

355   public synchronized String JavaDoc toString() {
356     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
357     buf.append("(").append(super.toString());
358     buf.append("}");
359     return buf.toString();
360   }
361
362
363   private Enumeration JavaDoc getEnumeration(int type) {
364     if (count == 0) {
365       return emptyEnumerator;
366     } else {
367       return new Enumerator(type, false);
368     }
369   }
370
371   // Comparison and hashing
372

373   /**
374    * Returns the hash code value for this Map as per the definition in the
375    * Map interface.
376    *
377    * @see Map#hashCode()
378    * @since 1.2
379    */

380   public synchronized int hashCode() {
381     /*
382      * This code detects the recursion caused by computing the hash code
383      * of a self-referential hash table and prevents the stack overflow
384      * that would otherwise result. This allows certain 1.1-era
385      * applets with self-referential hash tables to work. This code
386      * abuses the loadFactor field to do double-duty as a hashCode
387      * in progress flag, so as not to worsen the space performance.
388      * A negative load factor indicates that hash code computation is
389      * in progress.
390      */

391     int h = 0;
392     if (count == 0 || loadFactor < 0)
393       return h; // Returns zero
394

395     loadFactor = -loadFactor; // Mark hashCode computation in progress
396
Entry tab[] = table;
397     for (int i = 0; i < tab.length; i++)
398       for (Entry e = tab[i]; e != null; e = e.next)
399         h += e.key.hashCode() ^ e.value.hashCode();
400     loadFactor = -loadFactor; // Mark hashCode computation complete
401

402     return h;
403   }
404
405 // /**
406
// * Save the state of the Properties to a stream (i.e., serialize it).
407
// *
408
// * @serialData The <i>capacity</i> of the Properties (the length of the
409
// * bucket array) is emitted (int), followed by the
410
// * <i>size</i> of the Properties (the number of key-value
411
// * mappings), followed by the key (Object) and value (Object)
412
// * for each key-value mapping represented by the Properties
413
// * The key-value mappings are emitted in no particular order.
414
// */
415
// private synchronized void writeObject(java.io.ObjectOutputStream s)
416
// throws IOException
417
// {
418
// // Write out the length, threshold, loadfactor
419
// s.defaultWriteObject();
420

421 // // Write out length, count of elements and then the key/value objects
422
// s.writeInt(table.length);
423
// s.writeInt(count);
424
// for (int index = table.length-1; index >= 0; index--) {
425
// Entry entry = table[index];
426

427 // while (entry != null) {
428
// s.writeObject(entry.key);
429
// s.writeObject(entry.value);
430
// entry = entry.next;
431
// }
432
// }
433
// }
434

435 // /**
436
// * Reconstitute the Properties from a stream (i.e., deserialize it).
437
// */
438
// private void readObject(java.io.ObjectInputStream s)
439
// throws IOException, ClassNotFoundException
440
// {
441
// // Read in the length, threshold, and loadfactor
442
// s.defaultReadObject();
443

444 // // Read the original length of the array and number of elements
445
// int origlength = s.readInt();
446
// int elements = s.readInt();
447

448 // // Compute new size with a bit of room 5% to grow but
449
// // No larger than the original size. Make the length
450
// // odd if it's large enough, this helps distribute the entries.
451
// // Guard against the length ending up zero, that's not valid.
452
// int length = (int)(elements * loadFactor) + (elements / 20) + 3;
453
// if (length > elements && (length & 1) == 0)
454
// length--;
455
// if (origlength > 0 && length > origlength)
456
// length = origlength;
457

458 // table = new Entry[length];
459
// count = 0;
460

461 // // Read the number of elements and then all the key/value objects
462
// for (; elements > 0; elements--) {
463
// String key = (String) s.readObject();
464
// Object value = s.readObject();
465
// put(key, value); // synch could be eliminated for performance
466
// }
467
// }
468

469
470   /**
471    * Properties collision list.
472    */

473   private static class Entry {
474     int hash;
475     String JavaDoc key;
476     Object JavaDoc value;
477     Entry next;
478
479     protected Entry(int hash, String JavaDoc key, Object JavaDoc value, Entry next) {
480       this.hash = hash;
481       this.key = key;
482       this.value = value;
483       this.next = next;
484     }
485
486     protected Object JavaDoc clone() {
487       return new Entry(hash, key, value,
488                        (next==null ? null : (Entry)next.clone()));
489     }
490
491     // Map.Entry Ops
492

493     public String JavaDoc getKey() {
494       return key;
495     }
496
497     public Object JavaDoc getValue() {
498       return value;
499     }
500
501     public Object JavaDoc setValue(Object JavaDoc value) {
502       if (value == null)
503         throw new NullPointerException JavaDoc();
504
505       Object JavaDoc oldValue = this.value;
506       this.value = value;
507       return oldValue;
508     }
509
510     public boolean equals(Object JavaDoc o) {
511       if (!(o instanceof Entry))
512         return false;
513       Entry e = (Entry) o;
514
515       return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
516         (value==null ? e.getValue()==null : value.equals(e.getValue()));
517     }
518
519     public int hashCode() {
520       return hash ^ (value==null ? 0 : value.hashCode());
521     }
522
523     public String JavaDoc toString() {
524       return key + "=" + value.toString();
525     }
526   }
527
528   // Types of Enumerations/Iterations
529
private static final int KEYS = 0;
530   private static final int VALUES = 1;
531   private static final int ENTRIES = 2;
532
533   /**
534    * A hashtable enumerator class. This class implements both the
535    * Enumeration and Iterator interfaces, but individual instances
536    * can be created with the Iterator methods disabled. This is necessary
537    * to avoid unintentionally increasing the capabilities granted a user
538    * by passing an Enumeration.
539    */

540   private class Enumerator implements Enumeration JavaDoc {
541     Entry[] table = Properties.this.table;
542     int index = table.length;
543     Entry entry = null;
544     Entry lastReturned = null;
545     int type;
546
547     /**
548      * Indicates whether this Enumerator is serving as an Iterator
549      * or an Enumeration. (true -> Iterator).
550      */

551     boolean iterator;
552
553     /**
554      * The modCount value that the iterator believes that the backing
555      * List should have. If this expectation is violated, the iterator
556      * has detected concurrent modification.
557      */

558     protected int expectedModCount = modCount;
559
560     Enumerator(int type, boolean iterator) {
561       this.type = type;
562       this.iterator = iterator;
563     }
564
565     public boolean hasMoreElements() {
566       Entry e = entry;
567       int i = index;
568       Entry t[] = table;
569       /* Use locals for faster loop iteration */
570       while (e == null && i > 0) {
571         e = t[--i];
572       }
573       entry = e;
574       index = i;
575       return e != null;
576     }
577
578     public Object JavaDoc nextElement() {
579       Entry et = entry;
580       int i = index;
581       Entry t[] = table;
582       /* Use locals for faster loop iteration */
583       while (et == null && i > 0) {
584         et = t[--i];
585       }
586       entry = et;
587       index = i;
588       if (et != null) {
589         Entry e = lastReturned = entry;
590         entry = e.next;
591         return type == KEYS ? e.key : (type == VALUES ? e.value : e);
592       }
593       throw new NoSuchElementException JavaDoc("Properties Enumerator");
594     }
595
596     // Iterator methods
597
public boolean hasNext() {
598       return hasMoreElements();
599     }
600
601     public Object JavaDoc next() {
602       if (modCount != expectedModCount)
603         throw new ConcurrentModificationException JavaDoc();
604       return nextElement();
605     }
606
607     public void remove() {
608       if (!iterator)
609         throw new UnsupportedOperationException JavaDoc();
610       if (lastReturned == null)
611         throw new IllegalStateException JavaDoc("Properties Enumerator");
612       if (modCount != expectedModCount)
613         throw new ConcurrentModificationException JavaDoc();
614
615       synchronized(Properties.this) {
616         Entry[] tab = Properties.this.table;
617         int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
618
619         for (Entry e = tab[index], prev = null; e != null;
620              prev = e, e = e.next) {
621           if (e == lastReturned) {
622             modCount++;
623             expectedModCount++;
624             if (prev == null)
625               tab[index] = e.next;
626             else
627               prev.next = e.next;
628             count--;
629             lastReturned = null;
630             return;
631           }
632         }
633         throw new ConcurrentModificationException JavaDoc();
634       }
635     }
636   }
637    
638   private static EmptyEnumerator emptyEnumerator = new EmptyEnumerator();
639
640   /**
641    * A hashtable enumerator class for empty hash tables, specializes
642    * the general Enumerator
643    */

644   private static class EmptyEnumerator implements Enumeration JavaDoc {
645
646     EmptyEnumerator() {
647     }
648
649     public boolean hasMoreElements() {
650       return false;
651     }
652
653     public Object JavaDoc nextElement() {
654       throw new NoSuchElementException JavaDoc("Properties Enumerator");
655     }
656   }
657
658   public void copyInto(Map JavaDoc h) {
659     if (count > 0) {
660       for (int index = table.length-1; index >= 0; index--) {
661         Entry entry = table[index];
662
663         while (entry != null) {
664           h.put(entry.key, entry.value);
665           entry = entry.next;
666         }
667       }
668     }
669   }
670
671   /* ***** ***** ***** ***** *****
672    * Streamable interface
673    * ***** ***** ***** ***** ***** */

674
675   /**
676    * The object implements the writeTo method to write its contents to
677    * the output stream.
678    *
679    * @param os the stream to write the object to
680    */

681   public void writeTo(OutputStream os) throws IOException {
682     StreamUtil.writeTo(count, os);
683     for (int index = table.length-1; index >= 0; index--) {
684       Entry entry = table[index];
685       
686       while (entry != null) {
687         StreamUtil.writeTo(entry.key, os);
688         StreamUtil.writeObjectTo(entry.value, os);
689         entry = entry.next;
690       }
691     }
692   }
693
694   /**
695    * The object implements the readFrom method to restore its contents from
696    * the input stream.
697    *
698    * @param is the stream to read data from in order to restore the object
699    */

700   public static Properties readFrom(InputStream is) throws IOException {
701     int count = StreamUtil.readIntFrom(is);
702     if (count == -1) return null;
703
704     Properties p = new Properties(((4*count)/3) +1);
705
706     String JavaDoc key;
707     Object JavaDoc value;
708     for (int i=0; i<count; i++) {
709       key = StreamUtil.readStringFrom(is);
710       value = StreamUtil.readObjectFrom(is);
711       p.put(key, value);
712     }
713
714     return p;
715   }
716 }
717
Popular Tags