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      /** {@inheritDoc} */
64      public void clear() {
65          super.clear();
66          objectIndex.clear();
67      }
68  
69      /**
70       * Retrieves all the SAMLObjects that have given schema type or element name.
71       * 
72       * @param typeOrName the schema type or element name
73       * 
74       * @return list of SAMLObjects that have given schema type or element name or null
75       */
76      public List<ElementType> get(QName typeOrName) {
77          return objectIndex.get(typeOrName);
78      }
79  
80      /**
81       * Replaces the element at the specified position in this list with the specified element.
82       * 
83       * @param index index of element to replace
84       * @param element element to be stored at the specified position
85       * 
86       * @return the element previously at the specified position
87       */
88      public ElementType set(int index, ElementType element) {
89          ElementType returnValue = super.set(index, element);
90  
91          removeElementFromIndex(returnValue);
92  
93          indexElement(element);
94          return returnValue;
95      }
96  
97      /**
98       * Inserts the specified element at the specified position in this list. Shifts the element currently at that
99       * position (if any) and any subsequent elements to the right (adds one to their indices).
100      * 
101      * @param index index of element to add
102      * @param element element to be stored at the specified position
103      */
104     public void add(int index, ElementType element) {
105         super.add(index, element);
106         indexElement(element);
107     }
108 
109     /**
110      * Removes the element at the specified position in this list. Shifts any subsequent elements to the left (subtracts
111      * one from their indices). Returns the element that was removed from the list
112      * 
113      * @param index the index of the element to remove
114      * 
115      * @return the element removed from the list
116      */
117     public ElementType remove(int index) {
118         ElementType returnValue = super.remove(index);
119 
120         removeElementFromIndex(returnValue);
121 
122         return returnValue;
123     }
124 
125     /**
126      * Removes a given element from the list and index.
127      * 
128      * @param element the element to be removed
129      * 
130      * @return true if the element was in the list and removed, false if not
131      */
132     public boolean remove(ElementType element) {
133         boolean elementRemoved = false;
134 
135         elementRemoved = super.remove(element);
136         if (elementRemoved) {
137             removeElementFromIndex(element);
138         }
139 
140         return elementRemoved;
141     }
142 
143     /**
144      * Returns a view of the list that only contains elements stored under the given index. The returned list is backed
145      * by this list so and supports all optional operations, so changes made to the returned list are reflected in this
146      * list.
147      * 
148      * @param index index of the elements returned in the list view
149      * 
150      * @return a view of this list that contains only the elements stored under the given index
151      */
152     public List<? extends ElementType> subList(QName index) {
153         if (!objectIndex.containsKey(index)) {
154             objectIndex.put(index, new LazyList<ElementType>());
155         }
156 
157         return new ListView<ElementType>(this, index);
158     }
159 
160     /**
161      * Indexes the given SAMLObject by type and element name.
162      * 
163      * @param element the SAMLObject to index
164      */
165     protected void indexElement(ElementType element) {
166         if (element == null) {
167             return;
168         }
169 
170         QName type = element.getSchemaType();
171         if (type != null) {
172             indexElement(type, element);
173         }
174 
175         indexElement(element.getElementQName(), element);
176     }
177 
178     /**
179      * Indexes the given SAMLobject by the given index.
180      * 
181      * @param index the index for the element
182      * @param element the element to be indexed
183      */
184     protected void indexElement(QName index, ElementType element) {
185         List<ElementType> objects = objectIndex.get(index);
186         if (objects == null) {
187             objects = new LazyList<ElementType>();
188             objectIndex.put(index, objects);
189         }
190 
191         objects.add(element);
192     }
193 
194     /**
195      * Removes the given element from the schema type and element qname index.
196      * 
197      * @param element the element to remove from the index
198      */
199     protected void removeElementFromIndex(ElementType element) {
200         if (element == null) {
201             return;
202         }
203 
204         QName type = element.getSchemaType();
205         if (type != null) {
206             removeElementFromIndex(type, element);
207         }
208 
209         removeElementFromIndex(element.getElementQName(), element);
210     }
211 
212     /**
213      * Removes an object from the given index id.
214      * 
215      * @param index the id of the index
216      * @param element the element to be removed from that index
217      */
218     protected void removeElementFromIndex(QName index, ElementType element) {
219         List<ElementType> objects = objectIndex.get(index);
220         if (objects != null) {
221             objects.remove(element);
222         }
223 
224         if (objects.size() == 0) {
225             objectIndex.remove(index);
226         }
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.
233  * 
234  * @param <ElementType> the XMLObject type that this list operates on
235  */
236 class ListView<ElementType extends XMLObject> extends AbstractList<ElementType> {
237 
238     /** List that backs this view. */
239     private IndexedXMLObjectChildrenList<ElementType> backingList;
240 
241     /** Index that points to the list, in the backing list, that this view operates on. */
242     private QName index;
243 
244     /** List, in the backing list, that the given index points to. */
245     private List<ElementType> indexList;
246 
247     /**
248      * Constructor.
249      * 
250      * @param newBackingList the list that backs this view
251      * @param newIndex the element schema type or name of the sublist this view operates on
252      */
253     public ListView(IndexedXMLObjectChildrenList<ElementType> newBackingList, QName newIndex) {
254         backingList = newBackingList;
255         index = newIndex;
256         indexList = backingList.get(index);
257     }
258 
259     /**
260      * Checks to see if the given element is contained in this list.
261      * 
262      * @param element the element to check for
263      * 
264      * @return true if the element is in this list, false if not
265      */
266     public boolean contains(ElementType element) {
267         return indexList.contains(element);
268     }
269 
270     /** {@inheritDoc} */
271     public ElementType get(int newIndex) {
272         return indexList.get(newIndex);
273     }
274 
275     /** {@inheritDoc} */
276     public int size() {
277         return indexList.size();
278     }
279 
280     /** {@inheritDoc} */
281     public ElementType set(int newIndex, ElementType element) {
282 
283         if (newIndex < 0 && newIndex > indexList.size()) {
284             throw new IndexOutOfBoundsException();
285         }
286 
287         ElementType replacedElement;
288         int elementIndex;
289 
290         replacedElement = indexList.get(newIndex);
291         elementIndex = backingList.indexOf(replacedElement);
292         backingList.set(elementIndex, element);
293 
294         return replacedElement;
295     }
296 
297     /** {@inheritDoc} */
298     public void add(int newIndex, ElementType element) {
299         indexCheck(element);
300         backingList.add(element);
301     }
302 
303     /** {@inheritDoc} */
304     public ElementType remove(int newIndex) {
305         return backingList.remove(newIndex);
306     }
307 
308     /**
309      * Checks to make sure the given element schema type or name matches the index given at construction time.
310      * 
311      * @param element the element to check
312      * 
313      * @throws IllegalArgumentException thrown if the element schema type or name does not match the index
314      */
315     protected void indexCheck(ElementType element) throws IllegalArgumentException {
316         if (index.equals(element.getSchemaType()) || index.equals(element.getElementQName())) {
317             return;
318         } else {
319             throw new IllegalArgumentException("Element " + element.getElementQName() + " is not of type " + index);
320         }
321     }
322 }