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 }