KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > util > OneToManyMap


1 /*****************************************************************************
2  * Source code information
3  * -----------------------
4  * Original author Ian Dickinson, HP Labs Bristol
5  * Author email Ian.Dickinson@hp.com
6  * Package Jena
7  * Created 5 Jan 2001
8  * Filename $RCSfile: OneToManyMap.java,v $
9  * Revision $Revision: 1.12 $
10  * Release status Preview-release $State: Exp $
11  *
12  * Last modified on $Date: 2005/02/21 12:18:57 $
13  * by $Author: andy_seaborne $
14  *
15  * (c) Copyright 2001, 2002, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
16  * See end of file for details
17  *****************************************************************************/

18
19 // Package
20
///////////////
21
package com.hp.hpl.jena.util;
22
23
24 // Imports
25
///////////////
26
import java.util.*;
27
28 import com.hp.hpl.jena.util.iterator.NullIterator;
29
30
31
32 /**
33  * An extension to a standard map that supports one-to-many mappings: that is, there
34  * may be zero, one or many values corresponding to a given key.
35  *
36  * @author Ian Dickinson, HP Labs (<a HREF="mailto:Ian.Dickinson@hp.com">email</a>)
37  * @version CVS info: $Id: OneToManyMap.java,v 1.12 2005/02/21 12:18:57 andy_seaborne Exp $
38  */

39 public class OneToManyMap
40     implements Map
41 {
42     // Constants
43
//////////////////////////////////
44

45
46     // Static variables
47
//////////////////////////////////
48

49
50     // Instance variables
51
//////////////////////////////////
52

53     /** Encapsulated hash table stores the values */
54     private Map m_table = new HashMap();
55
56
57     // Constructors
58
//////////////////////////////////
59

60     /**
61      * <p>Construct a new empty one-to-many map</p>
62      */

63     public OneToManyMap() {
64     }
65     
66     
67     /**
68      * <p>Construct a new one-to-many map whose contents are
69      * initialised from the existing map.</p>
70      *
71      * @param map An existing one-to-many map
72      */

73     public OneToManyMap( OneToManyMap map ) {
74         // copy the contents of the existing map
75
// note we can't just use the copying constructor for hashmap
76
// as we don't want to share the arraylists that are the key values
77
for (Iterator i = map.keySet().iterator(); i.hasNext(); ) {
78             Object JavaDoc key = i.next();
79
80             for (Iterator j = map.getAll( key ); j.hasNext(); ) {
81                 put( key, j.next() );
82             }
83         }
84     }
85
86
87
88
89     // External signature methods
90
//////////////////////////////////
91

92     /**
93      * Clear all entries from the map.
94      */

95     public void clear() {
96         m_table.clear();
97     }
98
99
100     /**
101      * Answer true if the map contains the given value as a key.
102      *
103      * @param key The key object to test for
104      * @return True or false
105      */

106     public boolean containsKey( Object JavaDoc key ) {
107         return m_table.containsKey( key );
108     }
109
110
111     /**
112      * Answer true if the map contains the given object as a value
113      * stored against any key. Note that this is quite an expensive
114      * operation in the current implementation.
115      *
116      * @param value The value to test for
117      * @return True if the value is in the map
118      */

119     public boolean containsValue( Object JavaDoc value ) {
120         for (Iterator values = m_table.values().iterator(); values.hasNext(); ) {
121             Object JavaDoc x = values.next();
122
123             if (x.equals( value )) {
124                 return true;
125             }
126             else if (x instanceof List && ((List) x).contains( value )) {
127                 return true;
128             }
129         }
130
131         return false;
132     }
133
134
135     /**
136      * <p>Answer true if this mapping contains the pair
137      * <code>(key,&nbsp;value)</code>.</p>
138      * @param key A key object
139      * @param value A value object
140      * @return True if <code>key</code> has <code>value</code>
141      * as one of its values in this mapping
142      */

143     public boolean contains( Object JavaDoc key, Object JavaDoc value ) {
144         for (Iterator i = getAll( key ); i.hasNext(); ) {
145             if (i.next().equals( value )) {
146                 return true;
147             }
148         }
149         return false;
150     }
151     
152     
153     /**
154      * Answer a set of the mappings in this map. Each member of the set will
155      * be a Map.Entry value.
156      *
157      * @return A Set of the mappings as Map.Entry values.
158      */

159     public Set entrySet() {
160         Set s = CollectionFactory.createHashedSet();
161
162         for (Iterator e0 = m_table.keySet().iterator(); e0.hasNext(); ) {
163             Object JavaDoc key = e0.next();
164             List values = (List) m_table.get( key );
165
166             // add each key-value pair to the result set
167
for (ListIterator e1 = values.listIterator(); e1.hasNext(); ) {
168                 s.add( new Entry( key, e1.next() ) );
169             }
170         }
171
172         return s;
173     }
174
175
176     /**
177      * Compares the specified object with this map for equality.
178      * Returns true if the given object is also a map and the two Maps
179      * represent the same mappings. More formally, two maps t1 and t2 represent
180      * the same mappings if t1.entrySet().equals(t2.entrySet()).
181      *
182      * This ensures that the equals method works properly across different
183      * implementations of the Map interface.
184      *
185      * @param o The object to be compared for equality with this map.
186      * @return True if the specified object is equal to this map.
187      */

188     public boolean equals( Object JavaDoc o ) {
189         if (o instanceof java.util.Map JavaDoc) {
190             return entrySet().equals( ((Map) o).entrySet() );
191         }
192         else
193             return false;
194     }
195
196
197     /**
198      * Get a value for this key. Since this map is explicitly designed to
199      * allow there to be more than one mapping per key, this method will return
200      * an undetermined instance of the mapping. If no mapping exists, or the
201      * selected value is null, null is returned.
202      *
203      * @param key The key to access the map.
204      * @return One of the values this key corresponds to, or null.
205      * @see #getAll
206      */

207     public Object JavaDoc get( Object JavaDoc key ) {
208         ArrayList entry = (ArrayList) m_table.get( key );
209
210         if (entry != null) {
211             if (!entry.isEmpty()) {
212                 return entry.get( 0 );
213             }
214         }
215
216         // not present
217
return null;
218     }
219
220
221     /**
222      * Answer an iterator over all of the values for the given key. An iterator
223      * is always supplied, even if the key is not present.
224      *
225      * @param key The key object
226      * @return An iterator over all of the values for this key in the map
227      */

228     public Iterator getAll( Object JavaDoc key ) {
229         ArrayList entry = (ArrayList) m_table.get( key );
230         return (entry != null) ? entry.iterator() : NullIterator.instance;
231     }
232
233
234     /**
235      * Returns the hash code value for this map. The hash code of a map is
236      * defined to be the sum of the hashCodes of each entry in the map's
237      * entrySet view. This ensures that t1.equals(t2) implies
238      * that t1.hashCode()==t2.hashCode() for any two maps t1 and t2,
239      * as required by the general contract of Object.hashCode
240      */

241     public int hashCode() {
242         int hc = 0;
243
244         for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
245             hc ^= i.next().hashCode();
246         }
247
248         return hc;
249     }
250
251
252     /**
253      * Answer true if the map is empty of key-value mappings.
254      *
255      * @return True if there are no entries.
256      */

257     public boolean isEmpty() {
258         return m_table.isEmpty();
259     }
260
261
262     /**
263      * Answer a set of the keys in this map
264      *
265      * @return The keys of the map as a Set
266      */

267     public Set keySet() {
268         return m_table.keySet();
269     }
270
271
272     /**
273      * Associates the given value with the given key. Since this map formulation
274      * allows many values for one key, previous associations with the key are not
275      * lost. Consequently, the method always returns null (since the replaced value
276      * is not defined).
277      *
278      * @param key The key object
279      * @param value The value object
280      * @return Null.
281      */

282     public Object JavaDoc put( Object JavaDoc key, Object JavaDoc value ) {
283         ArrayList entries = (ArrayList) m_table.get( key );
284         entries = entries == null ? new ArrayList() : entries;
285
286         // add the new value to the list of values held against this key
287
entries.add( value );
288         m_table.put( key, entries );
289
290         return null;
291     }
292
293
294     /**
295      * <p>Put all entries from one map into this map. Tests for m being a
296      * OneToManyMap, and, if so, copies all of the entries for each key.</p>
297      * @param m The map whose contents are to be copied into this map
298      */

299     public void putAll( Map m ) {
300         boolean many = (m instanceof OneToManyMap);
301         
302         for (Iterator i = m.keySet().iterator(); i.hasNext(); ) {
303             Object JavaDoc key = i.next();
304             if (many) {
305                 for (Iterator j = ((OneToManyMap) m).getAll( key ); j.hasNext(); ) {
306                     put( key, j.next() );
307                 }
308             }
309             else {
310                 put( key, m.get( key ) );
311             }
312         }
313     }
314
315
316     /**
317      * Remove all of the associations for the given key. If only a specific
318      * association is to be removed, use {@link #remove( java.lang.Object, java.lang.Object )}
319      * instead. Has no effect if the key is not present in the map. Since no
320      * single specific association with the key is defined, this method always
321      * returns null.
322      *
323      * @param key All associations with this key will be removed
324      * @return null
325      */

326     public Object JavaDoc remove( Object JavaDoc key ) {
327         m_table.remove( key );
328         return null;
329     }
330
331
332     /**
333      * <p>Remove the specific association between the given key and value. Has
334      * no effect if the association is not present in the map. If all values
335      * for a particular key have been removed post removing this particular
336      * association, the key will no longer appear as a key in the map.</p>
337      *
338      * @param key The key object
339      * @param value The value object
340      */

341     public void remove( Object JavaDoc key, Object JavaDoc value ) {
342         List entries = (List) m_table.get( key );
343
344         if (entries != null) {
345             entries.remove( value );
346             
347             if (entries.isEmpty()) {
348                 m_table.remove( key );
349             }
350         }
351     }
352
353
354     /**
355      * <p>Answer the number of key-value mappings in the map</p>
356      * @return The number of key-value pairs.
357      */

358     public int size() {
359         int size = 0;
360
361         for (Iterator i = m_table.keySet().iterator(); i.hasNext(); ) {
362             size += ((List) m_table.get( i.next() )).size();
363         }
364
365         return size;
366     }
367
368
369     /**
370      * <p>Returns a collection view of the values contained in this map.
371      * Specifically, this will be a set, so duplicate values that appear
372      * for multiple keys are suppressed.</p>
373      * @return A set of the values contained in this map.
374      */

375     public Collection values() {
376         Set s = CollectionFactory.createHashedSet();
377
378         for (Iterator e = m_table.keySet().iterator(); e.hasNext(); ) {
379             s.addAll( (List) m_table.get(e.next()) );
380         }
381
382         return s;
383     }
384
385     /**
386      * <p>Answer a string representation of this map. This can be quite a long string for
387      * large maps.<p>
388      */

389     public String JavaDoc toString() {
390         StringBuffer JavaDoc buf = new StringBuffer JavaDoc( "OneToManyMap{" );
391         String JavaDoc sep = "";
392         
393         for (Iterator i = keySet().iterator(); i.hasNext(); ) {
394             Object JavaDoc key = i.next();
395             buf.append( sep );
396             buf.append( key );
397             buf.append( "={" );
398             
399             String JavaDoc sep1 = "";
400             for (Iterator j = getAll(key); j.hasNext(); ) {
401                 buf.append( sep1 );
402                 buf.append( j.next() );
403                 sep1=",";
404             }
405             buf.append("}");
406             sep=",";
407         }
408         buf.append("}");
409         return buf.toString();
410     }
411
412     // Internal implementation methods
413
//////////////////////////////////////
414

415     
416     // Inner classes
417
//////////////////////////////////////
418

419     
420     //////////////////////////////////
421

422
423     //==============================================================================
424
// Inner class definitions
425
//==============================================================================
426

427     /**
428      * Helper class to implement the Map.Entry interface to enumerate entries in the map
429      */

430     public static class Entry
431         implements Map.Entry
432     {
433         /** My key object */
434         private Object JavaDoc m_key = null;
435
436         /** My value object */
437         private Object JavaDoc m_value = null;
438
439
440         /**
441          * Constructor - save the key and value
442          */

443         private Entry( Object JavaDoc key, Object JavaDoc value ) {
444             m_key = key;
445             m_value = value;
446         }
447
448
449         /**
450          * Compares the specified object with this entry for equality. Returns true if the given
451          * object is also a map entry and the two entries represent the same mapping.
452          * More formally, two entries e1 and e2 represent the same mapping if
453          * <code><pre>
454          * (e1.getKey()==null ?
455          * e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
456          * (e1.getValue()==null ?
457          * e2.getValue()==null : e1.getValue().equals(e2.getValue()))
458          * </pre></code>
459          *
460          * This ensures that the equals method works properly across different implementations of the Map.Entry interface.
461          *
462          * @param x The object to compare against
463          * @return True if the given object is equal to this Map.Entry object.
464          */

465         public boolean equals( Object JavaDoc x ) {
466             if (x instanceof java.util.Map.Entry) {
467                 Map.Entry e1 = (Map.Entry) x;
468
469                 return (e1.getKey()==null ?
470                                           m_key==null : e1.getKey().equals(m_key)) &&
471                        (e1.getValue()==null ?
472                                             m_value == null : e1.getValue().equals(m_value));
473             }
474             else
475                 return false;
476         }
477
478
479         /**
480          * Answer the key for the entry
481          *
482          * @return The key object
483          */

484         public Object JavaDoc getKey() {
485             return m_key;
486         }
487
488
489         /**
490          * Answer the value for the entry
491          *
492          * @return The value object
493          */

494         public Object JavaDoc getValue() {
495             return m_value;
496         }
497
498
499         /**
500          * Set the value, which writes through to the map. Not implemented.
501          */

502         public Object JavaDoc setValue( Object JavaDoc value )
503             throws UnsupportedOperationException JavaDoc
504         {
505             throw new UnsupportedOperationException JavaDoc( "not implemented" );
506         }
507
508
509         /**
510          * Returns the hash code value for this map entry.
511          * The hash code of a map entry e is defined to be:
512          * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
513          * (e.getValue()==null ? 0 : e.getValue().hashCode())
514          *
515          * This ensures that e1.equals(e2) implies that e1.hashCode()==e2.hashCode() for any two
516          * Entries e1 and e2, as required by the general contract of Object.hashCode.
517          */

518         public int hashCode() {
519             return (getKey()==null ? 0 : getKey().hashCode()) ^
520                    (getValue()==null ? 0 : getValue().hashCode());
521         }
522
523
524     }
525
526 }
527
528 /*
529 * All rights reserved.
530 *
531 * Redistribution and use in source and binary forms, with or without
532 * modification, are permitted provided that the following conditions
533 * are met:
534 * 1. Redistributions of source code must retain the above copyright
535 * notice, this list of conditions and the following disclaimer.
536 * 2. Redistributions in binary form must reproduce the above copyright
537 * notice, this list of conditions and the following disclaimer in the
538 * documentation and/or other materials provided with the distribution.
539 * 3. The name of the author may not be used to endorse or promote products
540 * derived from this software without specific prior written permission.
541 *
542 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
543 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
544 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
545 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
546 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
547 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
548 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
549 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
550 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
551 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
552 */

553
Popular Tags