001package com.avaje.ebean.common; 002 003import com.avaje.ebean.bean.BeanCollection; 004import com.avaje.ebean.bean.BeanCollectionAdd; 005import com.avaje.ebean.bean.BeanCollectionLoader; 006import com.avaje.ebean.bean.EntityBean; 007 008import java.io.Serializable; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.Iterator; 013import java.util.List; 014import java.util.ListIterator; 015 016/** 017 * List capable of lazy loading. 018 */ 019public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd { 020 021 private static final long serialVersionUID = 1L; 022 023 /** 024 * The underlying List implementation. 025 */ 026 private List<E> list; 027 028 /** 029 * Specify the underlying List implementation. 030 */ 031 public BeanList(List<E> list) { 032 super(); 033 this.list = list; 034 } 035 036 /** 037 * Uses an ArrayList as the underlying List implementation. 038 */ 039 public BeanList() { 040 this(new ArrayList<E>()); 041 } 042 043 /** 044 * Used to create deferred fetch proxy. 045 */ 046 public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) { 047 super(loader, ownerBean, propertyName); 048 } 049 050 @Override 051 public void reset(EntityBean ownerBean, String propertyName) { 052 this.ownerBean = ownerBean; 053 this.propertyName = propertyName; 054 this.list = null; 055 this.touched = false; 056 } 057 058 @Override 059 public boolean isEmptyAndUntouched() { 060 return !touched && (list == null || list.isEmpty()); 061 } 062 063 @SuppressWarnings("unchecked") 064 public void addEntityBean(EntityBean bean) { 065 list.add((E) bean); 066 } 067 068 @Override 069 @SuppressWarnings("unchecked") 070 public void loadFrom(BeanCollection<?> other) { 071 if (list == null) { 072 list = new ArrayList<E>(); 073 } 074 list.addAll((Collection<? extends E>) other.getActualDetails()); 075 } 076 077 @SuppressWarnings("unchecked") 078 public void internalAdd(Object bean) { 079 if (list == null) { 080 list = new ArrayList<E>(); 081 } 082 if (bean != null) { 083 list.add((E) bean); 084 } 085 } 086 087 @Override 088 public void internalAddWithCheck(Object bean) { 089 if (list == null || !list.contains(bean)) { 090 internalAdd(bean); 091 } 092 } 093 094 public boolean checkEmptyLazyLoad() { 095 if (list == null) { 096 list = new ArrayList<E>(); 097 return true; 098 } else { 099 return false; 100 } 101 } 102 103 private void initClear() { 104 synchronized (this) { 105 if (list == null) { 106 if (modifyListening) { 107 lazyLoadCollection(true); 108 } else { 109 list = new ArrayList<E>(); 110 } 111 } 112 touched(true); 113 } 114 } 115 116 private void initAsUntouched() { 117 init(false); 118 } 119 120 private void init() { 121 init(true); 122 } 123 124 private void init(boolean setTouched) { 125 synchronized (this) { 126 if (list == null) { 127 lazyLoadCollection(false); 128 } 129 touched(setTouched); 130 } 131 } 132 133 /** 134 * Set the actual underlying list. 135 * <p> 136 * This is primarily for the deferred fetching function. 137 * </p> 138 */ 139 @SuppressWarnings("unchecked") 140 public void setActualList(List<?> list) { 141 this.list = (List<E>) list; 142 } 143 144 /** 145 * Return the actual underlying list. 146 */ 147 public List<E> getActualList() { 148 return list; 149 } 150 151 public Collection<E> getActualDetails() { 152 return list; 153 } 154 155 @Override 156 public Collection<?> getActualEntries() { 157 return list; 158 } 159 160 /** 161 * Return true if the underlying list is populated. 162 */ 163 public boolean isPopulated() { 164 return list != null; 165 } 166 167 /** 168 * Return true if this is a reference (lazy loading) bean collection. This is 169 * the same as !isPopulated(); 170 */ 171 public boolean isReference() { 172 return list == null; 173 } 174 175 public String toString() { 176 StringBuilder sb = new StringBuilder(50); 177 sb.append("BeanList "); 178 if (isReadOnly()) { 179 sb.append("readOnly "); 180 } 181 if (list == null) { 182 sb.append("deferred "); 183 184 } else { 185 sb.append("size[").append(list.size()).append("] "); 186 sb.append("list").append(list).append(""); 187 } 188 return sb.toString(); 189 } 190 191 /** 192 * Equal if obj is a List and equal in a list sense. 193 * <p> 194 * Specifically obj does not need to be a BeanList but any list. This does not 195 * use the FindMany, fetchedMaxRows or finishedFetch properties in the equals 196 * test. 197 * </p> 198 */ 199 public boolean equals(Object obj) { 200 init(); 201 return list.equals(obj); 202 } 203 204 public int hashCode() { 205 init(); 206 return list.hashCode(); 207 } 208 209 // -----------------------------------------------------// 210 // The additional methods are here 211 // -----------------------------------------------------// 212 213 // -----------------------------------------------------// 214 // proxy method for List 215 // -----------------------------------------------------// 216 217 public void add(int index, E element) { 218 checkReadOnly(); 219 init(); 220 if (modifyAddListening) { 221 modifyAddition(element); 222 } 223 list.add(index, element); 224 } 225 226 @Override 227 public void addBean(E bean) { 228 add(bean); 229 } 230 231 public boolean add(E o) { 232 checkReadOnly(); 233 init(); 234 if (modifyAddListening) { 235 if (list.add(o)) { 236 modifyAddition(o); 237 return true; 238 } else { 239 return false; 240 } 241 } 242 return list.add(o); 243 } 244 245 public boolean addAll(Collection<? extends E> c) { 246 checkReadOnly(); 247 init(); 248 if (modifyAddListening) { 249 // all elements in c are added (no contains checking) 250 getModifyHolder().modifyAdditionAll(c); 251 } 252 return list.addAll(c); 253 } 254 255 public boolean addAll(int index, Collection<? extends E> c) { 256 checkReadOnly(); 257 init(); 258 if (modifyAddListening) { 259 // all elements in c are added (no contains checking) 260 getModifyHolder().modifyAdditionAll(c); 261 } 262 return list.addAll(index, c); 263 } 264 265 public void clear() { 266 checkReadOnly(); 267 // TODO: when clear() and not initialised could be more clever 268 // and fetch just the Id's 269 initClear(); 270 if (modifyRemoveListening) { 271 for (int i = 0; i < list.size(); i++) { 272 getModifyHolder().modifyRemoval(list.get(i)); 273 } 274 } 275 list.clear(); 276 } 277 278 public boolean contains(Object o) { 279 init(); 280 return list.contains(o); 281 } 282 283 public boolean containsAll(Collection<?> c) { 284 init(); 285 return list.containsAll(c); 286 } 287 288 public E get(int index) { 289 init(); 290 return list.get(index); 291 } 292 293 public int indexOf(Object o) { 294 init(); 295 return list.indexOf(o); 296 } 297 298 public boolean isEmpty() { 299 initAsUntouched(); 300 return list.isEmpty(); 301 } 302 303 public Iterator<E> iterator() { 304 init(); 305 if (isReadOnly()) { 306 return new ReadOnlyListIterator<E>(list.listIterator()); 307 } 308 if (modifyListening) { 309 Iterator<E> it = list.iterator(); 310 return new ModifyIterator<E>(this, it); 311 } 312 return list.iterator(); 313 } 314 315 public int lastIndexOf(Object o) { 316 init(); 317 return list.lastIndexOf(o); 318 } 319 320 public ListIterator<E> listIterator() { 321 init(); 322 if (isReadOnly()) { 323 return new ReadOnlyListIterator<E>(list.listIterator()); 324 } 325 if (modifyListening) { 326 ListIterator<E> it = list.listIterator(); 327 return new ModifyListIterator<E>(this, it); 328 } 329 return list.listIterator(); 330 } 331 332 public ListIterator<E> listIterator(int index) { 333 init(); 334 if (isReadOnly()) { 335 return new ReadOnlyListIterator<E>(list.listIterator(index)); 336 } 337 if (modifyListening) { 338 ListIterator<E> it = list.listIterator(index); 339 return new ModifyListIterator<E>(this, it); 340 } 341 return list.listIterator(index); 342 } 343 344 @Override 345 public void removeBean(E bean) { 346 if (list.remove(bean)) { 347 getModifyHolder().modifyRemoval(bean); 348 } 349 } 350 351 public E remove(int index) { 352 checkReadOnly(); 353 init(); 354 if (modifyRemoveListening) { 355 E o = list.remove(index); 356 modifyRemoval(o); 357 return o; 358 } 359 return list.remove(index); 360 } 361 362 public boolean remove(Object o) { 363 checkReadOnly(); 364 init(); 365 if (modifyRemoveListening) { 366 boolean isRemove = list.remove(o); 367 if (isRemove) { 368 modifyRemoval(o); 369 } 370 return isRemove; 371 } 372 return list.remove(o); 373 } 374 375 public boolean removeAll(Collection<?> beans) { 376 checkReadOnly(); 377 init(); 378 if (modifyRemoveListening) { 379 boolean changed = false; 380 for (Object bean : beans) { 381 if (list.remove(bean)) { 382 // register this bean as having been removed 383 modifyRemoval(bean); 384 changed = true; 385 } 386 } 387 return changed; 388 } 389 return list.removeAll(beans); 390 } 391 392 public boolean retainAll(Collection<?> retainBeans) { 393 checkReadOnly(); 394 init(); 395 if (modifyRemoveListening) { 396 boolean changed = false; 397 Iterator<E> it = list.iterator(); 398 while (it.hasNext()) { 399 Object bean = it.next(); 400 if (!retainBeans.contains(bean)) { 401 // removing this bean 402 it.remove(); 403 modifyRemoval(bean); 404 changed = true; 405 } 406 } 407 return changed; 408 } 409 return list.retainAll(retainBeans); 410 } 411 412 public E set(int index, E element) { 413 checkReadOnly(); 414 init(); 415 if (modifyListening) { 416 E o = list.set(index, element); 417 modifyAddition(element); 418 modifyRemoval(o); 419 return o; 420 } 421 return list.set(index, element); 422 } 423 424 public int size() { 425 init(); 426 return list.size(); 427 } 428 429 public List<E> subList(int fromIndex, int toIndex) { 430 init(); 431 if (isReadOnly()) { 432 return Collections.unmodifiableList(list.subList(fromIndex, toIndex)); 433 } 434 if (modifyListening) { 435 return new ModifyList<E>(this, list.subList(fromIndex, toIndex)); 436 } 437 return list.subList(fromIndex, toIndex); 438 } 439 440 public Object[] toArray() { 441 init(); 442 return list.toArray(); 443 } 444 445 public <T> T[] toArray(T[] a) { 446 init(); 447 //noinspection SuspiciousToArrayCall 448 return list.toArray(a); 449 } 450 451 private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable { 452 453 private static final long serialVersionUID = 3097271091406323699L; 454 455 private final ListIterator<E> i; 456 457 ReadOnlyListIterator(ListIterator<E> i) { 458 this.i = i; 459 } 460 461 public void add(E o) { 462 throw new IllegalStateException("This collection is in ReadOnly mode"); 463 } 464 465 public void remove() { 466 throw new IllegalStateException("This collection is in ReadOnly mode"); 467 } 468 469 public void set(E o) { 470 throw new IllegalStateException("This collection is in ReadOnly mode"); 471 } 472 473 public boolean hasNext() { 474 return i.hasNext(); 475 } 476 477 public boolean hasPrevious() { 478 return i.hasPrevious(); 479 } 480 481 public E next() { 482 return i.next(); 483 } 484 485 public int nextIndex() { 486 return i.nextIndex(); 487 } 488 489 public E previous() { 490 return i.previous(); 491 } 492 493 public int previousIndex() { 494 return i.previousIndex(); 495 } 496 497 } 498}