View Javadoc

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 }