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 }