001package io.ebean.common; 002 003import io.ebean.bean.BeanCollection; 004import io.ebean.bean.BeanCollectionAdd; 005import io.ebean.bean.BeanCollectionLoader; 006import io.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<>()); 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 } 056 057 @Override 058 public boolean isSkipSave() { 059 return list == null || (list.isEmpty() && !holdsModifications()); 060 } 061 062 @Override 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<>(); 073 } 074 list.addAll((Collection<? extends E>) other.getActualDetails()); 075 } 076 077 @Override 078 @SuppressWarnings("unchecked") 079 public void internalAdd(Object bean) { 080 if (list == null) { 081 list = new ArrayList<>(); 082 } 083 if (bean != null) { 084 list.add((E) bean); 085 } 086 } 087 088 @Override 089 public void internalAddWithCheck(Object bean) { 090 if (list == null || bean == null || !containsInstance(bean)) { 091 internalAdd(bean); 092 } 093 } 094 095 /** 096 * Contains using instance equality for List (specifically not .equals() based). 097 */ 098 private boolean containsInstance(Object bean) { 099 for (Object element : list) { 100 if (element == bean) { 101 return true; 102 } 103 } 104 return false; 105 } 106 107 @Override 108 public boolean checkEmptyLazyLoad() { 109 if (list == null) { 110 list = new ArrayList<>(); 111 return true; 112 } else { 113 return false; 114 } 115 } 116 117 private void initClear() { 118 lock.lock(); 119 try { 120 if (list == null) { 121 if (!disableLazyLoad && modifyListening) { 122 lazyLoadCollection(true); 123 } else { 124 list = new ArrayList<>(); 125 } 126 } 127 } finally { 128 lock.unlock(); 129 } 130 } 131 132 private void init() { 133 lock.lock(); 134 try { 135 if (list == null) { 136 if (disableLazyLoad) { 137 list = new ArrayList<>(); 138 } else { 139 lazyLoadCollection(false); 140 } 141 } 142 } finally { 143 lock.unlock(); 144 } 145 } 146 147 /** 148 * Set the actual underlying list. 149 * <p> 150 * This is primarily for the deferred fetching function. 151 * </p> 152 */ 153 @SuppressWarnings("unchecked") 154 public void setActualList(List<?> list) { 155 this.list = (List<E>) list; 156 } 157 158 /** 159 * Return the actual underlying list. 160 */ 161 public List<E> getActualList() { 162 return list; 163 } 164 165 @Override 166 public Collection<E> getActualDetails() { 167 return list; 168 } 169 170 @Override 171 public Collection<?> getActualEntries() { 172 return list; 173 } 174 175 /** 176 * Return true if the underlying list is populated. 177 */ 178 @Override 179 public boolean isPopulated() { 180 return list != null; 181 } 182 183 /** 184 * Return true if this is a reference (lazy loading) bean collection. This is 185 * the same as !isPopulated(); 186 */ 187 @Override 188 public boolean isReference() { 189 return list == null; 190 } 191 192 @Override 193 public String toString() { 194 StringBuilder sb = new StringBuilder(50); 195 sb.append("BeanList "); 196 if (isReadOnly()) { 197 sb.append("readOnly "); 198 } 199 if (list == null) { 200 sb.append("deferred "); 201 } else { 202 sb.append("size[").append(list.size()).append("] "); 203 sb.append("list").append(list); 204 } 205 return sb.toString(); 206 } 207 208 /** 209 * Equal if obj is a List and equal in a list sense. 210 * <p> 211 * Specifically obj does not need to be a BeanList but any list. This does not 212 * use the FindMany, fetchedMaxRows or finishedFetch properties in the equals 213 * test. 214 * </p> 215 */ 216 @Override 217 public boolean equals(Object obj) { 218 init(); 219 return list.equals(obj); 220 } 221 222 @Override 223 public int hashCode() { 224 init(); 225 return list.hashCode(); 226 } 227 228 // -----------------------------------------------------// 229 // The additional methods are here 230 // -----------------------------------------------------// 231 232 // -----------------------------------------------------// 233 // proxy method for List 234 // -----------------------------------------------------// 235 236 @Override 237 public void add(int index, E element) { 238 checkReadOnly(); 239 init(); 240 if (modifyListening) { 241 modifyAddition(element); 242 } 243 list.add(index, element); 244 } 245 246 @Override 247 public void addBean(E bean) { 248 add(bean); 249 } 250 251 @Override 252 public boolean add(E o) { 253 checkReadOnly(); 254 init(); 255 if (modifyListening) { 256 if (list.add(o)) { 257 modifyAddition(o); 258 return true; 259 } else { 260 return false; 261 } 262 } 263 return list.add(o); 264 } 265 266 @Override 267 public boolean addAll(Collection<? extends E> c) { 268 checkReadOnly(); 269 init(); 270 if (modifyListening) { 271 // all elements in c are added (no contains checking) 272 getModifyHolder().modifyAdditionAll(c); 273 } 274 return list.addAll(c); 275 } 276 277 @Override 278 public boolean addAll(int index, Collection<? extends E> c) { 279 checkReadOnly(); 280 init(); 281 if (modifyListening) { 282 // all elements in c are added (no contains checking) 283 getModifyHolder().modifyAdditionAll(c); 284 } 285 return list.addAll(index, c); 286 } 287 288 @Override 289 public void clear() { 290 checkReadOnly(); 291 // TODO: when clear() and not initialised could be more clever 292 // and fetch just the Id's 293 initClear(); 294 if (modifyListening) { 295 for (E aList : list) { 296 getModifyHolder().modifyRemoval(aList); 297 } 298 } 299 list.clear(); 300 } 301 302 @Override 303 public boolean contains(Object o) { 304 init(); 305 return list.contains(o); 306 } 307 308 @Override 309 public boolean containsAll(Collection<?> c) { 310 init(); 311 return list.containsAll(c); 312 } 313 314 @Override 315 public E get(int index) { 316 init(); 317 return list.get(index); 318 } 319 320 @Override 321 public int indexOf(Object o) { 322 init(); 323 return list.indexOf(o); 324 } 325 326 @Override 327 public boolean isEmpty() { 328 init(); 329 return list.isEmpty(); 330 } 331 332 @Override 333 public Iterator<E> iterator() { 334 init(); 335 if (isReadOnly()) { 336 return new ReadOnlyListIterator<>(list.listIterator()); 337 } 338 if (modifyListening) { 339 Iterator<E> it = list.iterator(); 340 return new ModifyIterator<>(this, it); 341 } 342 return list.iterator(); 343 } 344 345 @Override 346 public int lastIndexOf(Object o) { 347 init(); 348 return list.lastIndexOf(o); 349 } 350 351 @Override 352 public ListIterator<E> listIterator() { 353 init(); 354 if (isReadOnly()) { 355 return new ReadOnlyListIterator<>(list.listIterator()); 356 } 357 if (modifyListening) { 358 ListIterator<E> it = list.listIterator(); 359 return new ModifyListIterator<>(this, it); 360 } 361 return list.listIterator(); 362 } 363 364 @Override 365 public ListIterator<E> listIterator(int index) { 366 init(); 367 if (isReadOnly()) { 368 return new ReadOnlyListIterator<>(list.listIterator(index)); 369 } 370 if (modifyListening) { 371 ListIterator<E> it = list.listIterator(index); 372 return new ModifyListIterator<>(this, it); 373 } 374 return list.listIterator(index); 375 } 376 377 @Override 378 public void removeBean(E bean) { 379 if (list.remove(bean)) { 380 getModifyHolder().modifyRemoval(bean); 381 } 382 } 383 384 @Override 385 public E remove(int index) { 386 checkReadOnly(); 387 init(); 388 if (modifyListening) { 389 E o = list.remove(index); 390 modifyRemoval(o); 391 return o; 392 } 393 return list.remove(index); 394 } 395 396 @Override 397 public boolean remove(Object o) { 398 checkReadOnly(); 399 init(); 400 if (modifyListening) { 401 boolean isRemove = list.remove(o); 402 if (isRemove) { 403 modifyRemoval(o); 404 } 405 return isRemove; 406 } 407 return list.remove(o); 408 } 409 410 @Override 411 public boolean removeAll(Collection<?> beans) { 412 checkReadOnly(); 413 init(); 414 if (modifyListening) { 415 boolean changed = false; 416 for (Object bean : beans) { 417 if (list.remove(bean)) { 418 // register this bean as having been removed 419 modifyRemoval(bean); 420 changed = true; 421 } 422 } 423 return changed; 424 } 425 return list.removeAll(beans); 426 } 427 428 @Override 429 public boolean retainAll(Collection<?> retainBeans) { 430 checkReadOnly(); 431 init(); 432 if (modifyListening) { 433 boolean changed = false; 434 Iterator<E> it = list.iterator(); 435 while (it.hasNext()) { 436 Object bean = it.next(); 437 if (!retainBeans.contains(bean)) { 438 // removing this bean 439 it.remove(); 440 modifyRemoval(bean); 441 changed = true; 442 } 443 } 444 return changed; 445 } 446 return list.retainAll(retainBeans); 447 } 448 449 @Override 450 public E set(int index, E element) { 451 checkReadOnly(); 452 init(); 453 if (modifyListening) { 454 E o = list.set(index, element); 455 modifyAddition(element); 456 modifyRemoval(o); 457 return o; 458 } 459 return list.set(index, element); 460 } 461 462 @Override 463 public int size() { 464 init(); 465 return list.size(); 466 } 467 468 @Override 469 public List<E> subList(int fromIndex, int toIndex) { 470 init(); 471 if (isReadOnly()) { 472 return Collections.unmodifiableList(list.subList(fromIndex, toIndex)); 473 } 474 if (modifyListening) { 475 return new ModifyList<>(this, list.subList(fromIndex, toIndex)); 476 } 477 return list.subList(fromIndex, toIndex); 478 } 479 480 @Override 481 public Object[] toArray() { 482 init(); 483 return list.toArray(); 484 } 485 486 @Override 487 public <T> T[] toArray(T[] a) { 488 init(); 489 //noinspection SuspiciousToArrayCall 490 return list.toArray(a); 491 } 492 493 private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable { 494 495 private static final long serialVersionUID = 3097271091406323699L; 496 497 private final ListIterator<E> i; 498 499 ReadOnlyListIterator(ListIterator<E> i) { 500 this.i = i; 501 } 502 503 @Override 504 public void add(E o) { 505 throw new IllegalStateException("This collection is in ReadOnly mode"); 506 } 507 508 @Override 509 public void remove() { 510 throw new IllegalStateException("This collection is in ReadOnly mode"); 511 } 512 513 @Override 514 public void set(E o) { 515 throw new IllegalStateException("This collection is in ReadOnly mode"); 516 } 517 518 @Override 519 public boolean hasNext() { 520 return i.hasNext(); 521 } 522 523 @Override 524 public boolean hasPrevious() { 525 return i.hasPrevious(); 526 } 527 528 @Override 529 public E next() { 530 return i.next(); 531 } 532 533 @Override 534 public int nextIndex() { 535 return i.nextIndex(); 536 } 537 538 @Override 539 public E previous() { 540 return i.previous(); 541 } 542 543 @Override 544 public int previousIndex() { 545 return i.previousIndex(); 546 } 547 548 } 549 550 @Override 551 public BeanCollection<E> getShallowCopy() { 552 BeanList<E> copy = new BeanList<>(new CopyOnFirstWriteList<>(list)); 553 copy.setFromOriginal(this); 554 return copy; 555 } 556}