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.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Map;
24  import java.util.Set;
25  
26  /**
27   * Map implementation which allows subsets of entries to be retrieved based on the type of the entry value.
28   * 
29   * @param <KeyType> the type of object used as keys
30   * @param <ValueType> the type of object stored as values
31   */
32  public class ValueTypeIndexedMap<KeyType, ValueType> implements Map<KeyType, ValueType> {
33  
34      /** Class to represent null values. */
35      private static class NullValue {}
36      
37      /** Storage for index of class -> members. */
38      private Map<Class<?>, Map<KeyType, ValueType>> index;
39  
40      /** Storage for map members. */
41      private Map<KeyType, ValueType> map;
42  
43      /** Set of valid types for this map. */
44      private Set<Class> types;
45  
46      /** Constructor. */
47      public ValueTypeIndexedMap() {
48          this(new HashSet<Class>());
49      }
50  
51      /**
52       * Constructor.
53       *
54       * @param newMap existing map to build from.
55       * @param newTypes collection of value types to index
56       */
57      public ValueTypeIndexedMap(Map<KeyType, ValueType> newMap, Collection<Class> newTypes) {
58          map = newMap;
59          types = new HashSet<Class>(newTypes);
60          index = new HashMap<Class<?>, Map<KeyType, ValueType>>();
61          rebuildIndex();
62      }
63      
64      /**
65       * Constructor.
66       *
67       * @param newTypes collection of value types to index
68       */
69      public ValueTypeIndexedMap(Collection<Class> newTypes) {
70          this(new HashMap<KeyType, ValueType>(), newTypes);
71      }
72  
73      /** {@inheritDoc} */
74      public void clear() {
75          map.clear();
76          rebuildIndex();
77      }
78  
79      /** {@inheritDoc} */
80      public boolean containsKey(Object key) {
81          return map.containsKey(key);
82      }
83  
84      /** {@inheritDoc} */
85      public boolean containsValue(Object value) {
86          return map.containsValue(value);
87      }
88  
89      /** {@inheritDoc} */
90      public Set<java.util.Map.Entry<KeyType, ValueType>> entrySet() {
91          return map.entrySet();
92      }
93  
94      /** {@inheritDoc} */
95      public ValueType get(Object key) {
96          return map.get(key);
97      }
98  
99      /**
100      * Get the value types that are indexed.
101      * 
102      * @return which value types are indexed
103      */
104     public Set<Class> getTypes() {
105         return types;
106     }
107 
108     /** {@inheritDoc} */
109     public boolean isEmpty() {
110         return map.isEmpty();
111     }
112 
113     /** {@inheritDoc} */
114     public Set<KeyType> keySet() {
115         return map.keySet();
116     }
117 
118     /**
119      * Check if the object is of the specified type, taking null into account as well.
120      * 
121      * @param type type to check for
122      * @param object object to check
123      * @return true if the object is of the specified type
124      */
125     private Boolean matchType(Class<?> type, Object object) {
126         return type.isInstance(object) || (type == NullValue.class && object == null);
127     }
128 
129     /** {@inheritDoc} */
130     public ValueType put(KeyType key, ValueType value) {
131         ValueType oldValue = map.put(key, value);
132 
133         for (Class<?> type : index.keySet()) {
134             if (type == null) { type = NullValue.class; }
135             if (matchType(type, value)) {
136                 index.get(type).put(key, value);
137             } else if (matchType(type, oldValue)) {
138                 index.get(type).remove(key);
139             }
140         }
141 
142         return oldValue;
143     }
144 
145     /** {@inheritDoc} */
146     public void putAll(Map<? extends KeyType, ? extends ValueType> t) {
147         // this is probably not the most efficient way to do this
148         for (KeyType key : t.keySet()) {
149             put(key, t.get(key));
150         }
151     }
152     
153     /**
154      * Rebuild internal index.
155      */
156     public void rebuildIndex() {
157         index.clear();
158         ValueType value;
159 
160         for (Class<?> type : types) {
161             if (type == null) { type = NullValue.class; }
162             index.put(type, new HashMap<KeyType, ValueType>());
163             for (KeyType key : map.keySet()) {
164                 value = map.get(key);
165                 if (matchType(type, value)) {
166                     index.get(type).put(key, value);
167                 }
168             }
169         }
170     }
171     
172     /** {@inheritDoc} */
173     public ValueType remove(Object key) {
174         ValueType value = map.remove(key);
175 
176         for (Class<?> type : index.keySet()) {
177             if (type.isInstance(value)) {
178                 index.get(type).remove(key);
179             }
180         }
181 
182         return value;
183     }
184     
185     /**
186      * Set which value types are indexed.
187      * 
188      * @param newTypes which value types are indexed
189      */
190     public void setTypes(Collection<Class> newTypes) {
191         this.types = new HashSet<Class>(newTypes);
192     }
193 
194     /** {@inheritDoc} */
195     public int size() {
196         return map.size();
197     }
198 
199     /**
200      * Returns an unmodifiable map of the entries whose value is of the specified type.
201      * 
202      * @param <SubType> type of values to include in the returned map
203      * @param type type of values to return
204      * @return sub map of entries whose value is of type SubType or null if the specified type is not a valid type for
205      *         this map.
206      */
207     @SuppressWarnings("unchecked")
208     public <SubType extends ValueType> Map<KeyType, SubType> subMap(Class<SubType> type) {
209         Class<?> key = type;
210         if (key == null) { key = NullValue.class; }
211         if (index.containsKey(key)) {
212             return Collections.unmodifiableMap((Map<KeyType, SubType>) index.get(key));
213         } else {
214             return Collections.emptyMap();
215         }
216     }
217 
218     /** {@inheritDoc} */
219     public String toString() {
220         return map.toString();
221     }
222     
223     /** {@inheritDoc} */
224     public Collection<ValueType> values() {
225         return map.values();
226     }
227 
228 
229 }