View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.common.collection;
25  
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.ListIterator;
31  import java.util.NoSuchElementException;
32  import net.jcip.annotations.Immutable;
33  import org.modeshape.common.util.CheckArg;
34  
35  /**
36   * An immutable {@link List} that consists of a single element appended to another existing {@link List}. The result is a list
37   * that contains all of the elements in the parent list as well as the last appended element, but while reusing the existing
38   * parent list and without having to create a copy of the parent list.
39   * 
40   * @param <T> the type of element
41   */
42  @Immutable
43  public class ImmutableAppendedList<T> implements List<T> {
44  
45      private final List<T> parent;
46      private final T element;
47      private final int size;
48      private transient int hc;
49  
50      /**
51       * Create an instance using the supplied parent list and an element to be virtually appended to the parent. Note that the
52       * parent must be immutable (though this is not checked).
53       * 
54       * @param parent the parent list
55       * @param element the child element (may be null)
56       * @throws IllegalArgumentException if the reference to the parent list is null
57       */
58      public ImmutableAppendedList( List<T> parent,
59                                    T element ) {
60          CheckArg.isNotNull(parent, "parent");
61          this.parent = parent;
62          this.element = element;
63          this.size = parent.size() + 1;
64      }
65  
66      /**
67       * {@inheritDoc}
68       * 
69       * @see java.util.List#contains(java.lang.Object)
70       */
71      public boolean contains( Object o ) {
72          return element == o || (element != null && element.equals(o)) || parent.contains(o);
73      }
74  
75      /**
76       * {@inheritDoc}
77       * 
78       * @see java.util.List#containsAll(java.util.Collection)
79       */
80      public boolean containsAll( Collection<?> c ) {
81          Iterator<?> e = c.iterator();
82          while (e.hasNext()) {
83              if (!contains(e.next())) return false;
84          }
85          return true;
86      }
87  
88      /**
89       * {@inheritDoc}
90       * 
91       * @see java.util.List#get(int)
92       */
93      public T get( int index ) {
94          if (index == (size - 1)) return element;
95          return parent.get(index);
96      }
97  
98      /**
99       * {@inheritDoc}
100      * 
101      * @see java.util.List#indexOf(java.lang.Object)
102      */
103     public int indexOf( Object o ) {
104         int index = parent.indexOf(o);
105         if (index == -1) {
106             return (element == o || (element != null && element.equals(o))) ? (size - 1) : -1;
107         }
108         return -1;
109     }
110 
111     /**
112      * {@inheritDoc}
113      * 
114      * @see java.util.List#isEmpty()
115      */
116     public boolean isEmpty() {
117         return false;
118     }
119 
120     /**
121      * {@inheritDoc}
122      * 
123      * @see java.util.List#iterator()
124      */
125     @SuppressWarnings( "synthetic-access" )
126     public Iterator<T> iterator() {
127         final Iterator<T> parentIterator = parent.iterator();
128         return new Iterator<T>() {
129             boolean finished = false;
130 
131             public boolean hasNext() {
132                 return parentIterator.hasNext() || !finished;
133             }
134 
135             public T next() {
136                 if (parentIterator.hasNext()) return parentIterator.next();
137                 if (finished) throw new NoSuchElementException();
138                 finished = true;
139                 return element;
140             }
141 
142             public void remove() {
143                 throw new UnsupportedOperationException();
144             }
145         };
146     }
147 
148     /**
149      * {@inheritDoc}
150      * 
151      * @see java.util.List#lastIndexOf(java.lang.Object)
152      */
153     public int lastIndexOf( Object o ) {
154         if (element == o || (element != null && element.equals(o))) return size - 1;
155         return parent.lastIndexOf(o);
156     }
157 
158     /**
159      * {@inheritDoc}
160      * 
161      * @see java.util.List#listIterator()
162      */
163     public ListIterator<T> listIterator() {
164         return listIterator(0);
165     }
166 
167     /**
168      * {@inheritDoc}
169      * 
170      * @see java.util.List#listIterator(int)
171      */
172     @SuppressWarnings( "synthetic-access" )
173     public ListIterator<T> listIterator( final int index ) {
174         return new ListIterator<T>() {
175             int cursor = index;
176 
177             public boolean hasNext() {
178                 return cursor < size;
179             }
180 
181             public T next() {
182                 try {
183                     T next = get(cursor);
184                     cursor++;
185                     return next;
186                 } catch (IndexOutOfBoundsException e) {
187                     throw new NoSuchElementException();
188                 }
189             }
190 
191             public boolean hasPrevious() {
192                 return cursor != 0;
193             }
194 
195             public int nextIndex() {
196                 return cursor;
197             }
198 
199             public T previous() {
200                 try {
201                     int i = cursor - 1;
202                     T previous = get(i);
203                     cursor = i;
204                     return previous;
205                 } catch (IndexOutOfBoundsException e) {
206                     throw new NoSuchElementException();
207                 }
208             }
209 
210             public int previousIndex() {
211                 return cursor - 1;
212             }
213 
214             public void set( T o ) {
215                 throw new UnsupportedOperationException();
216             }
217 
218             public void remove() {
219                 throw new UnsupportedOperationException();
220             }
221 
222             public void add( T o ) {
223                 throw new UnsupportedOperationException();
224             }
225 
226         };
227     }
228 
229     /**
230      * {@inheritDoc}
231      * 
232      * @see java.util.List#size()
233      */
234     public int size() {
235         return size;
236     }
237 
238     /**
239      * {@inheritDoc}
240      * 
241      * @see java.util.List#subList(int, int)
242      */
243     public List<T> subList( int fromIndex,
244                             int toIndex ) {
245         if (fromIndex == 0 && toIndex == size) {
246             // The bounds are the same as this list, so just return this list ...
247             return this;
248         }
249         if (toIndex == size || fromIndex == (size - 1)) {
250             // The only list is the last element ...
251             return Collections.singletonList(element);
252         }
253         if (toIndex < size) {
254             // It is all within the range of the parent's list, so simply delegate
255             return parent.subList(fromIndex, toIndex);
256         }
257         // Otherwise, the sublist starts within the parent list and ends with the last element.
258         // So, create a sublist starting at the 'fromIndex' until the end of the parent list ...
259         List<T> sublist = parent.subList(fromIndex, toIndex - 1); // will catch out-of-bounds errors
260         // And wrap with another immutable appended list to add the last element ...
261         return new ImmutableAppendedList<T>(sublist, element);
262     }
263 
264     /**
265      * {@inheritDoc}
266      * 
267      * @see java.util.List#toArray()
268      */
269     public Object[] toArray() {
270         Object[] result = new Object[size];
271         int i = 0;
272         for (T e : parent) {
273             result[i++] = e;
274         }
275         result[i] = element;
276         return result;
277     }
278 
279     /**
280      * {@inheritDoc}
281      * 
282      * @see java.util.List#toArray(T[])
283      */
284     @SuppressWarnings( "unchecked" )
285     public <X> X[] toArray( X[] a ) {
286         if (a.length < size) a = (X[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
287         a = parent.toArray(a);
288         a[size - 1] = (X)element;
289         return a;
290     }
291 
292     /**
293      * {@inheritDoc}
294      * 
295      * @see java.lang.Object#hashCode()
296      */
297     @Override
298     public int hashCode() {
299         if (hc == 0) {
300             int hashCode = 1;
301             for (T element : this) {
302                 hashCode = 31 * hashCode + (element == null ? 0 : element.hashCode());
303             }
304             hc = hashCode;
305         }
306         return hc;
307     }
308 
309     /**
310      * {@inheritDoc}
311      * 
312      * @see java.lang.Object#equals(java.lang.Object)
313      */
314     @Override
315     public boolean equals( Object obj ) {
316         if (obj == this) return true;
317         if (obj instanceof List) {
318             List<?> that = (List<?>)obj;
319             if (this.size() != that.size()) return false;
320             Iterator<?> thisIter = this.iterator();
321             Iterator<?> thatIter = that.iterator();
322             while (thisIter.hasNext()) {
323                 Object thisValue = thisIter.next();
324                 Object thatValue = thatIter.next();
325                 if (thisValue == null) {
326                     if (thatValue != null) return false;
327                     // assert thatValue == null;
328                 } else {
329                     if (!thisValue.equals(thatValue)) return false;
330                 }
331             }
332             return true;
333         }
334         return super.equals(obj);
335     }
336 
337     /**
338      * {@inheritDoc}
339      * 
340      * @see java.lang.Object#toString()
341      */
342     @Override
343     public String toString() {
344         StringBuffer buf = new StringBuffer();
345         buf.append("[");
346 
347         Iterator<T> i = iterator();
348         boolean hasNext = i.hasNext();
349         while (hasNext) {
350             T o = i.next();
351             buf.append(o == this ? "(this Collection)" : String.valueOf(o));
352             hasNext = i.hasNext();
353             if (hasNext) buf.append(", ");
354         }
355 
356         buf.append("]");
357         return buf.toString();
358     }
359 
360     // ----------------------------------------------------------------------------------------------------------------
361     // Methods that modify are not supported
362     // ----------------------------------------------------------------------------------------------------------------
363 
364     /**
365      * {@inheritDoc}
366      * 
367      * @see java.util.List#add(int, Object)
368      */
369     public void add( int index,
370                      T element ) {
371         throw new UnsupportedOperationException();
372     }
373 
374     /**
375      * {@inheritDoc}
376      * 
377      * @see java.util.List#add(Object)
378      */
379     public boolean add( T o ) {
380         throw new UnsupportedOperationException();
381     }
382 
383     /**
384      * {@inheritDoc}
385      * 
386      * @see java.util.List#addAll(java.util.Collection)
387      */
388     public boolean addAll( Collection<? extends T> c ) {
389         throw new UnsupportedOperationException();
390     }
391 
392     /**
393      * {@inheritDoc}
394      * 
395      * @see java.util.List#addAll(int, java.util.Collection)
396      */
397     public boolean addAll( int index,
398                            Collection<? extends T> c ) {
399         throw new UnsupportedOperationException();
400     }
401 
402     /**
403      * {@inheritDoc}
404      * 
405      * @see java.util.List#clear()
406      */
407     public void clear() {
408         throw new UnsupportedOperationException();
409     }
410 
411     /**
412      * {@inheritDoc}
413      * 
414      * @see java.util.List#remove(java.lang.Object)
415      */
416     public boolean remove( Object o ) {
417         throw new UnsupportedOperationException();
418     }
419 
420     /**
421      * {@inheritDoc}
422      * 
423      * @see java.util.List#remove(int)
424      */
425     public T remove( int index ) {
426         throw new UnsupportedOperationException();
427     }
428 
429     /**
430      * {@inheritDoc}
431      * 
432      * @see java.util.List#removeAll(java.util.Collection)
433      */
434     public boolean removeAll( Collection<?> c ) {
435         throw new UnsupportedOperationException();
436     }
437 
438     /**
439      * {@inheritDoc}
440      * 
441      * @see java.util.List#retainAll(java.util.Collection)
442      */
443     public boolean retainAll( Collection<?> c ) {
444         throw new UnsupportedOperationException();
445     }
446 
447     /**
448      * {@inheritDoc}
449      * 
450      * @see java.util.List#set(int, java.lang.Object)
451      */
452     public T set( int index,
453                   T element ) {
454         throw new UnsupportedOperationException();
455     }
456 
457 }