1 /*
2 * Copyright [2005] [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.AbstractList;
20 import java.util.Collection;
21 import java.util.List;
22 import java.util.Map;
23
24 import javax.xml.namespace.QName;
25
26 import net.jcip.annotations.NotThreadSafe;
27
28 import org.opensaml.xml.XMLObject;
29
30 /**
31 * A list which indexes XMLObjects by their schema type and element QName for quick retrival based on those items.
32 *
33 * @param <ElementType> the type of element added to the list
34 */
35 @NotThreadSafe
36 public class IndexedXMLObjectChildrenList<ElementType extends XMLObject> extends XMLObjectChildrenList<ElementType> {
37
38 /** Index of objects by type and name. */
39 private Map<QName, List<ElementType>> objectIndex;
40
41 /**
42 * Constructor.
43 *
44 * @param parent the parent of the {@link XMLObject}s added to the list
45 */
46 public IndexedXMLObjectChildrenList(XMLObject parent) {
47 super(parent);
48 objectIndex = new LazyMap<QName, List<ElementType>>();
49 }
50
51 /**
52 * Constructor.
53 *
54 * @param parent the parent of all elements
55 * @param col collection to add to this list
56 */
57 public IndexedXMLObjectChildrenList(XMLObject parent, Collection<ElementType> col) {
58 super(parent);
59 objectIndex = new LazyMap<QName, List<ElementType>>();
60 addAll(col);
61 }
62
63 /**
64 * Inserts the specified element at the specified position in this list. Shifts the element currently at that
65 * position (if any) and any subsequent elements to the right (adds one to their indices).
66 *
67 * @param index index of element to add
68 * @param element element to be stored at the specified position
69 */
70 public void add(int index, ElementType element) {
71 super.add(index, element);
72 indexElement(element);
73 }
74
75 /** {@inheritDoc} */
76 public void clear() {
77 super.clear();
78 objectIndex.clear();
79 }
80
81 /**
82 * Retrieves all the SAMLObjects that have given schema type or element name.
83 *
84 * @param typeOrName the schema type or element name
85 *
86 * @return list of SAMLObjects that have given schema type or element name or null
87 */
88 public List<ElementType> get(QName typeOrName) {
89 return objectIndex.get(typeOrName);
90 }
91
92 /**
93 * Indexes the given SAMLObject by type and element name.
94 *
95 * @param element the SAMLObject to index
96 */
97 protected void indexElement(ElementType element) {
98 if (element == null) {
99 return;
100 }
101
102 QName type = element.getSchemaType();
103 if (type != null) {
104 indexElement(type, element);
105 }
106
107 indexElement(element.getElementQName(), element);
108 }
109
110 /**
111 * Indexes the given SAMLobject by the given index.
112 *
113 * @param index the index for the element
114 * @param element the element to be indexed
115 */
116 protected void indexElement(QName index, ElementType element) {
117 List<ElementType> objects = objectIndex.get(index);
118 if (objects == null) {
119 objects = new LazyList<ElementType>();
120 objectIndex.put(index, objects);
121 }
122
123 objects.add(element);
124 }
125
126 /**
127 * Removes a given element from the list and index.
128 *
129 * @param element the element to be removed
130 *
131 * @return true if the element was in the list and removed, false if not
132 */
133 public boolean remove(ElementType element) {
134 boolean elementRemoved = false;
135
136 elementRemoved = super.remove(element);
137 if (elementRemoved) {
138 removeElementFromIndex(element);
139 }
140
141 return elementRemoved;
142 }
143
144 /**
145 * Removes the element at the specified position in this list. Shifts any subsequent elements to the left (subtracts
146 * one from their indices). Returns the element that was removed from the list
147 *
148 * @param index the index of the element to remove
149 *
150 * @return the element removed from the list
151 */
152 public ElementType remove(int index) {
153 ElementType returnValue = super.remove(index);
154
155 removeElementFromIndex(returnValue);
156
157 return returnValue;
158 }
159
160 /**
161 * Removes the given element from the schema type and element qname index.
162 *
163 * @param element the element to remove from the index
164 */
165 protected void removeElementFromIndex(ElementType element) {
166 if (element == null) {
167 return;
168 }
169
170 QName type = element.getSchemaType();
171 if (type != null) {
172 removeElementFromIndex(type, element);
173 }
174
175 removeElementFromIndex(element.getElementQName(), element);
176 }
177
178 /**
179 * Removes an object from the given index id.
180 *
181 * @param index the id of the index
182 * @param element the element to be removed from that index
183 */
184 protected void removeElementFromIndex(QName index, ElementType element) {
185 List<ElementType> objects = objectIndex.get(index);
186 if (objects != null) {
187 objects.remove(element);
188 }
189
190 if (objects.size() == 0) {
191 objectIndex.remove(index);
192 }
193 }
194
195 /**
196 * Replaces the element at the specified position in this list with the specified element.
197 *
198 * @param index index of element to replace
199 * @param element element to be stored at the specified position
200 *
201 * @return the element previously at the specified position
202 */
203 public ElementType set(int index, ElementType element) {
204 ElementType returnValue = super.set(index, element);
205
206 removeElementFromIndex(returnValue);
207
208 indexElement(element);
209 return returnValue;
210 }
211
212 /**
213 * Returns a view of the list that only contains elements stored under the given index. The returned list is backed
214 * by this list so and supports all optional operations, so changes made to the returned list are reflected in this
215 * list.
216 *
217 * @param index index of the elements returned in the list view
218 *
219 * @return a view of this list that contains only the elements stored under the given index
220 */
221 public List<? extends ElementType> subList(QName index) {
222 if (!objectIndex.containsKey(index)) {
223 objectIndex.put(index, new LazyList<ElementType>());
224 }
225
226 return new ListView<ElementType>(this, index);
227 }
228 }
229
230 /**
231 * A special list that works as a view of an IndexedXMLObjectChildrenList showing only the sublist associated with a
232 * given index. Operations performed on this sublist are reflected in the backing list. Index-based mutation operations
233 * are not supported.
234 *
235 * @param <ElementType> the XMLObject type that this list operates on
236 */
237 class ListView<ElementType extends XMLObject> extends AbstractList<ElementType> {
238
239 /** List that backs this view. */
240 private IndexedXMLObjectChildrenList<ElementType> backingList;
241
242 /** Index that points to the list, in the backing list, that this view operates on. */
243 private QName index;
244
245 /** List, in the backing list, that the given index points to. */
246 private List<ElementType> indexList;
247
248 /**
249 * Constructor.
250 *
251 * @param newBackingList the list that backs this view
252 * @param newIndex the element schema type or name of the sublist this view operates on
253 */
254 public ListView(IndexedXMLObjectChildrenList<ElementType> newBackingList, QName newIndex) {
255 backingList = newBackingList;
256 index = newIndex;
257 indexList = backingList.get(index);
258 }
259
260 public boolean add(ElementType o) {
261 boolean result = backingList.add(o);
262 indexList = backingList.get(index);
263 return result;
264 }
265
266 /** {@inheritDoc} */
267 public void add(int newIndex, ElementType element) {
268 throw new UnsupportedOperationException();
269 }
270
271 /** {@inheritDoc} */
272 public boolean addAll(Collection<? extends ElementType> c) {
273 boolean result = backingList.addAll(c);
274 indexList = backingList.get(index);
275 return result;
276 }
277
278 /** {@inheritDoc} */
279 public boolean addAll(int index, Collection<? extends ElementType> c) {
280 throw new UnsupportedOperationException();
281 }
282
283 /** {@inheritDoc} */
284 public void clear() {
285 backingList.clear();
286 indexList = backingList.get(index);
287 }
288
289 /**
290 * Checks to see if the given element is contained in this list.
291 *
292 * @param element the element to check for
293 *
294 * @return true if the element is in this list, false if not
295 */
296 public boolean contains(Object element) {
297 return indexList.contains(element);
298 }
299
300 /** {@inheritDoc} */
301 public boolean containsAll(Collection<?> c) {
302 return indexList.containsAll(c);
303 }
304
305 /** {@inheritDoc} */
306 public ElementType get(int newIndex) {
307 return indexList.get(newIndex);
308 }
309
310 /** {@inheritDoc} */
311 public int indexOf(Object o) {
312 return backingList.indexOf(o);
313 }
314
315 /** {@inheritDoc} */
316 public boolean isEmpty() {
317 return indexList.isEmpty();
318 }
319
320 /** {@inheritDoc} */
321 public int lastIndexOf(Object o) {
322 return backingList.lastIndexOf(o);
323 }
324
325 /** {@inheritDoc} */
326 public ElementType remove(int newIndex) {
327 throw new UnsupportedOperationException();
328 }
329
330 /** {@inheritDoc} */
331 public boolean remove(Object o) {
332 boolean result = backingList.remove(o);
333 indexList = backingList.get(index);
334 return result;
335 }
336
337 /** {@inheritDoc} */
338 public boolean removeAll(Collection<?> c) {
339 boolean result = backingList.removeAll(c);
340 indexList = backingList.get(index);
341 return result;
342 }
343
344 /** {@inheritDoc} */
345 public boolean retainAll(Collection<?> c) {
346 boolean result = backingList.retainAll(c);
347 indexList = backingList.get(index);
348 return result;
349 }
350
351 /** {@inheritDoc} */
352 public ElementType set(int newIndex, ElementType element) {
353 throw new UnsupportedOperationException();
354 }
355
356 /** {@inheritDoc} */
357 public int size() {
358 return indexList.size();
359 }
360
361 /** {@inheritDoc} */
362 public Object[] toArray() {
363 return indexList.toArray();
364 }
365
366 public <T extends Object> T[] toArray(T[] a) {
367 return indexList.toArray(a);
368 }
369 }