001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors. 
008     *
009     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     *
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.common.collection;
025    
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.ListIterator;
031    import java.util.NoSuchElementException;
032    import net.jcip.annotations.Immutable;
033    import org.jboss.dna.common.util.CheckArg;
034    
035    /**
036     * An immutable {@link List} that consists of a single element appended to another existing {@link List}. The result is a list
037     * that contains all of the elements in the parent list as well as the last appended element, but while reusing the existing
038     * parent list and without having to create a copy of the parent list.
039     * 
040     * @author Randall Hauch
041     * @param <T> the type of element
042     */
043    @Immutable
044    public class ImmutableAppendedList<T> implements List<T> {
045    
046        private final List<T> parent;
047        private final T element;
048        private final int size;
049        private transient int hc;
050    
051        /**
052         * Create an instance using the supplied parent list and an element to be virtually appended to the parent. Note that the
053         * parent must be immutable (though this is not checked).
054         * 
055         * @param parent the parent list
056         * @param element the child element (may be null)
057         * @throws IllegalArgumentException if the reference to the parent list is null
058         */
059        public ImmutableAppendedList( List<T> parent,
060                                      T element ) {
061            CheckArg.isNotNull(parent, "parent");
062            this.parent = parent;
063            this.element = element;
064            this.size = parent.size() + 1;
065        }
066    
067        /**
068         * {@inheritDoc}
069         * 
070         * @see java.util.List#contains(java.lang.Object)
071         */
072        public boolean contains( Object o ) {
073            return element == o || (element != null && element.equals(o)) || parent.contains(o);
074        }
075    
076        /**
077         * {@inheritDoc}
078         * 
079         * @see java.util.List#containsAll(java.util.Collection)
080         */
081        public boolean containsAll( Collection<?> c ) {
082            Iterator<?> e = c.iterator();
083            while (e.hasNext()) {
084                if (!contains(e.next())) return false;
085            }
086            return true;
087        }
088    
089        /**
090         * {@inheritDoc}
091         * 
092         * @see java.util.List#get(int)
093         */
094        public T get( int index ) {
095            if (index == (size - 1)) return element;
096            return parent.get(index);
097        }
098    
099        /**
100         * {@inheritDoc}
101         * 
102         * @see java.util.List#indexOf(java.lang.Object)
103         */
104        public int indexOf( Object o ) {
105            int index = parent.indexOf(o);
106            if (index == -1) {
107                return (element == o || (element != null && element.equals(o))) ? (size - 1) : -1;
108            }
109            return -1;
110        }
111    
112        /**
113         * {@inheritDoc}
114         * 
115         * @see java.util.List#isEmpty()
116         */
117        public boolean isEmpty() {
118            return false;
119        }
120    
121        /**
122         * {@inheritDoc}
123         * 
124         * @see java.util.List#iterator()
125         */
126        @SuppressWarnings( "synthetic-access" )
127        public Iterator<T> iterator() {
128            final Iterator<T> parentIterator = parent.iterator();
129            return new Iterator<T>() {
130                boolean finished = false;
131    
132                public boolean hasNext() {
133                    return parentIterator.hasNext() || !finished;
134                }
135    
136                public T next() {
137                    if (parentIterator.hasNext()) return parentIterator.next();
138                    if (finished) throw new NoSuchElementException();
139                    finished = true;
140                    return element;
141                }
142    
143                public void remove() {
144                    throw new UnsupportedOperationException();
145                }
146            };
147        }
148    
149        /**
150         * {@inheritDoc}
151         * 
152         * @see java.util.List#lastIndexOf(java.lang.Object)
153         */
154        public int lastIndexOf( Object o ) {
155            if (element == o || (element != null && element.equals(o))) return size - 1;
156            return parent.lastIndexOf(o);
157        }
158    
159        /**
160         * {@inheritDoc}
161         * 
162         * @see java.util.List#listIterator()
163         */
164        public ListIterator<T> listIterator() {
165            return listIterator(0);
166        }
167    
168        /**
169         * {@inheritDoc}
170         * 
171         * @see java.util.List#listIterator(int)
172         */
173        @SuppressWarnings( "synthetic-access" )
174        public ListIterator<T> listIterator( final int index ) {
175            return new ListIterator<T>() {
176                int cursor = index;
177    
178                public boolean hasNext() {
179                    return cursor < size;
180                }
181    
182                public T next() {
183                    try {
184                        T next = get(cursor);
185                        cursor++;
186                        return next;
187                    } catch (IndexOutOfBoundsException e) {
188                        throw new NoSuchElementException();
189                    }
190                }
191    
192                public boolean hasPrevious() {
193                    return cursor != 0;
194                }
195    
196                public int nextIndex() {
197                    return cursor;
198                }
199    
200                public T previous() {
201                    try {
202                        int i = cursor - 1;
203                        T previous = get(i);
204                        cursor = i;
205                        return previous;
206                    } catch (IndexOutOfBoundsException e) {
207                        throw new NoSuchElementException();
208                    }
209                }
210    
211                public int previousIndex() {
212                    return cursor - 1;
213                }
214    
215                public void set( T o ) {
216                    throw new UnsupportedOperationException();
217                }
218    
219                public void remove() {
220                    throw new UnsupportedOperationException();
221                }
222    
223                public void add( T o ) {
224                    throw new UnsupportedOperationException();
225                }
226    
227            };
228        }
229    
230        /**
231         * {@inheritDoc}
232         * 
233         * @see java.util.List#size()
234         */
235        public int size() {
236            return size;
237        }
238    
239        /**
240         * {@inheritDoc}
241         * 
242         * @see java.util.List#subList(int, int)
243         */
244        public List<T> subList( int fromIndex,
245                                int toIndex ) {
246            if (fromIndex == 0 && toIndex == size) {
247                // The bounds are the same as this list, so just return this list ...
248                return this;
249            }
250            if (toIndex == size || fromIndex == (size - 1)) {
251                // The only list is the last element ...
252                return Collections.singletonList(element);
253            }
254            if (toIndex < size) {
255                // It is all within the range of the parent's list, so simply delegate
256                return parent.subList(fromIndex, toIndex);
257            }
258            // Otherwise, the sublist starts within the parent list and ends with the last element.
259            // So, create a sublist starting at the 'fromIndex' until the end of the parent list ...
260            List<T> sublist = parent.subList(fromIndex, toIndex - 1); // will catch out-of-bounds errors
261            // And wrap with another immutable appended list to add the last element ...
262            return new ImmutableAppendedList<T>(sublist, element);
263        }
264    
265        /**
266         * {@inheritDoc}
267         * 
268         * @see java.util.List#toArray()
269         */
270        public Object[] toArray() {
271            Object[] result = new Object[size];
272            int i = 0;
273            for (T e : parent) {
274                result[i++] = e;
275            }
276            result[i] = element;
277            return result;
278        }
279    
280        /**
281         * {@inheritDoc}
282         * 
283         * @see java.util.List#toArray(T[])
284         */
285        @SuppressWarnings( "unchecked" )
286        public <X> X[] toArray( X[] a ) {
287            if (a.length < size) a = (X[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
288            a = parent.toArray(a);
289            a[size - 1] = (X)element;
290            return a;
291        }
292    
293        /**
294         * {@inheritDoc}
295         * 
296         * @see java.lang.Object#hashCode()
297         */
298        @Override
299        public int hashCode() {
300            if (hc == 0) {
301                int hashCode = 1;
302                for (T element : this) {
303                    hashCode = 31 * hashCode + (element == null ? 0 : element.hashCode());
304                }
305                hc = hashCode;
306            }
307            return hc;
308        }
309    
310        /**
311         * {@inheritDoc}
312         * 
313         * @see java.lang.Object#equals(java.lang.Object)
314         */
315        @Override
316        public boolean equals( Object obj ) {
317            if (obj == this) return true;
318            if (obj instanceof List) {
319                List<?> that = (List<?>)obj;
320                if (this.size() != that.size()) return false;
321                Iterator<?> thisIter = this.iterator();
322                Iterator<?> thatIter = that.iterator();
323                while (thisIter.hasNext()) {
324                    Object thisValue = thisIter.next();
325                    Object thatValue = thatIter.next();
326                    if (thisValue == null) {
327                        if (thatValue != null) return false;
328                        // assert thatValue == null;
329                    } else {
330                        if (!thisValue.equals(thatValue)) return false;
331                    }
332                }
333                return true;
334            }
335            return super.equals(obj);
336        }
337    
338        /**
339         * {@inheritDoc}
340         * 
341         * @see java.lang.Object#toString()
342         */
343        @Override
344        public String toString() {
345            StringBuffer buf = new StringBuffer();
346            buf.append("[");
347    
348            Iterator<T> i = iterator();
349            boolean hasNext = i.hasNext();
350            while (hasNext) {
351                T o = i.next();
352                buf.append(o == this ? "(this Collection)" : String.valueOf(o));
353                hasNext = i.hasNext();
354                if (hasNext) buf.append(", ");
355            }
356    
357            buf.append("]");
358            return buf.toString();
359        }
360    
361        // ----------------------------------------------------------------------------------------------------------------
362        // Methods that modify are not supported
363        // ----------------------------------------------------------------------------------------------------------------
364    
365        /**
366         * {@inheritDoc}
367         * 
368         * @see java.util.List#add(int, Object)
369         */
370        public void add( int index,
371                         T element ) {
372            throw new UnsupportedOperationException();
373        }
374    
375        /**
376         * {@inheritDoc}
377         * 
378         * @see java.util.List#add(Object)
379         */
380        public boolean add( T o ) {
381            throw new UnsupportedOperationException();
382        }
383    
384        /**
385         * {@inheritDoc}
386         * 
387         * @see java.util.List#addAll(java.util.Collection)
388         */
389        public boolean addAll( Collection<? extends T> c ) {
390            throw new UnsupportedOperationException();
391        }
392    
393        /**
394         * {@inheritDoc}
395         * 
396         * @see java.util.List#addAll(int, java.util.Collection)
397         */
398        public boolean addAll( int index,
399                               Collection<? extends T> c ) {
400            throw new UnsupportedOperationException();
401        }
402    
403        /**
404         * {@inheritDoc}
405         * 
406         * @see java.util.List#clear()
407         */
408        public void clear() {
409            throw new UnsupportedOperationException();
410        }
411    
412        /**
413         * {@inheritDoc}
414         * 
415         * @see java.util.List#remove(java.lang.Object)
416         */
417        public boolean remove( Object o ) {
418            throw new UnsupportedOperationException();
419        }
420    
421        /**
422         * {@inheritDoc}
423         * 
424         * @see java.util.List#remove(int)
425         */
426        public T remove( int index ) {
427            throw new UnsupportedOperationException();
428        }
429    
430        /**
431         * {@inheritDoc}
432         * 
433         * @see java.util.List#removeAll(java.util.Collection)
434         */
435        public boolean removeAll( Collection<?> c ) {
436            throw new UnsupportedOperationException();
437        }
438    
439        /**
440         * {@inheritDoc}
441         * 
442         * @see java.util.List#retainAll(java.util.Collection)
443         */
444        public boolean retainAll( Collection<?> c ) {
445            throw new UnsupportedOperationException();
446        }
447    
448        /**
449         * {@inheritDoc}
450         * 
451         * @see java.util.List#set(int, java.lang.Object)
452         */
453        public T set( int index,
454                      T element ) {
455            throw new UnsupportedOperationException();
456        }
457    
458    }