View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.util;
18  
19  import java.util.AbstractSet;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.Set;
24  
25  /**
26   * Set implementation which provides indexed access to set members via their class,
27   * and which allows only one instance of a given class to be present in the set. Null
28   * members are not allowed.
29   * 
30   * @param <T> the type of object stored by this class
31   */
32  public class ClassIndexedSet<T> extends AbstractSet<T> implements Set<T> {
33      
34      /** Storage for set members. */
35      private HashSet<T> set;
36      
37      /** Storage for index of class -> member. */
38      private HashMap<Class<? extends T>, T> index;
39      
40      /**
41       * Constructor.
42       *
43       */
44      public ClassIndexedSet() {
45          set = new HashSet<T>();
46          index = new HashMap<Class<? extends T>, T>();
47      }
48  
49      /** {@inheritDoc} */
50      public boolean add(T o) {
51          return add(o, false);
52      }
53  
54      /**
55       * Add member to set, optionally replacing any existing instance of the 
56       * same class.
57       * 
58       * @param o the object to add
59       * @param replace flag indicating whether to replace an existing class type
60       * @return true if object was added
61       * @throws IllegalArgumentException if set already contained an instance of the object's class
62       *              and replacement was specified as false
63       * @throws NullPointerException if the object to add was null
64       */
65      @SuppressWarnings("unchecked")
66      public boolean add(T o, boolean replace) throws NullPointerException, IllegalArgumentException {
67          if (o == null) {
68              throw new NullPointerException("Null elements are not allowed");
69          }
70          boolean replacing = false;
71          Class<? extends T> indexClass = getIndexClass(o);
72          T existing = get(indexClass);
73          if (existing != null) {
74              replacing = true;
75              if (replace) {
76                  remove(existing);
77              } else {
78                  throw new IllegalArgumentException("Set already contains a member of index class " 
79                          + indexClass.getName());
80              }
81          }
82          index.put(indexClass, o);
83          set.add(o);
84          return replacing;
85      }
86  
87      /** {@inheritDoc} */
88      public void clear() {
89          set.clear();
90          index.clear();
91      }
92  
93      /** {@inheritDoc} */
94      @SuppressWarnings("unchecked")
95      public boolean remove(Object o) {
96          if  (set.contains(o)) {
97              removeFromIndex((T) o);
98              set.remove(o);
99              return true;
100         }
101         return false;
102     }
103 
104     /** {@inheritDoc} */
105     public Iterator<T> iterator() {
106         return new ClassIndexedSetIterator(this, set.iterator()); 
107     }
108 
109     /** {@inheritDoc} */
110     public int size() {
111         return set.size();
112     }
113     
114     /**
115      * Check whether set contains an instance of the specified class.
116      * 
117      * @param clazz the class to check
118      * @return true if set contains an instance of the specified class, false otherwise
119      */
120     public boolean contains(Class<? extends T> clazz) {
121         return get(clazz)!=null;
122     }
123     
124     /**
125      * Get the set element specified by the class parameter.
126      * @param <X> generic parameter which eliminates need for casting by the caller
127      * @param clazz the class to whose instance is to be retrieved
128      * @return the element whose class is of the type specified, or null
129      * 
130      */
131     @SuppressWarnings("unchecked")
132     public <X extends T> X get(Class<X> clazz) {
133         return (X) index.get(clazz);
134     }
135     
136     /**
137      * Get the index class of the specified object. Subclasses may override to use
138      * a class index other than the main runtime class of the object.
139      * 
140      * @param o the object whose class index to determine
141      * @return the class index value associated with the object instance
142      */
143     @SuppressWarnings("unchecked")
144     protected Class<? extends T> getIndexClass(Object o) {
145         return (Class<? extends T>) o.getClass();
146     }
147     
148     /**
149      * Remove the specified object from the index.
150      * 
151      * @param o the object to remove
152      */
153     private void removeFromIndex(T o) {
154        index.remove(getIndexClass(o));
155     }
156     
157     /**
158      * Iterator for set implementation {@link ClassIndexedSet}.
159      * 
160      */
161     protected class ClassIndexedSetIterator implements Iterator<T> {
162         
163         /** The set instance over which this instance is an iterator. */
164         private ClassIndexedSet<T> set;
165         
166         /** The iterator for the owner's underlying storage. */
167         private Iterator<T> iterator;
168         
169         /** Flag which tracks whether next() has been called at least once. */
170         private boolean nextCalled;
171         
172         /** Flag which tracks whether remove can currently be called. */
173         private boolean removeStateValid;;
174         
175         /** The element most recently returned by next(), and the target for any subsequent
176          * remove() operation. */
177         private T current;
178         
179         /**
180          * Constructor.
181          *
182          * @param parentSet the {@link ClassIndexedSet} over which this instance is an iterator
183          * @param parentIterator the iterator for the parent's underlying storage
184          */
185         protected ClassIndexedSetIterator(ClassIndexedSet<T> parentSet, Iterator<T> parentIterator) {
186             set = parentSet;
187             iterator = parentIterator;
188             current = null;
189             nextCalled = false;
190             removeStateValid = false;
191         }
192 
193         /** {@inheritDoc} */
194         public boolean hasNext() {
195             return iterator.hasNext();
196         }
197 
198         /** {@inheritDoc} */
199         public T next() {
200             current = iterator.next();
201             nextCalled = true;
202             removeStateValid = true;
203             return current;
204         }
205 
206         /** {@inheritDoc} */
207         public void remove() {
208             if (! nextCalled) {
209                 throw new IllegalStateException("remove() was called before calling next()");
210             }
211             if (! removeStateValid) {
212                 throw new IllegalStateException("remove() has already been called since the last call to next()");
213             }
214             iterator.remove();
215             set.removeFromIndex(current);
216             removeStateValid = false;
217         }
218         
219     }
220 
221 }