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 202 } else { 203 sb.append("size[").append(list.size()).append("] "); 204 sb.append("list").append(list).append(""); 205 } 206 return sb.toString(); 207 } 208 209 /** 210 * Equal if obj is a List and equal in a list sense. 211 * <p> 212 * Specifically obj does not need to be a BeanList but any list. This does not 213 * use the FindMany, fetchedMaxRows or finishedFetch properties in the equals 214 * test. 215 * </p> 216 */ 217 @Override 218 public boolean equals(Object obj) { 219 init(); 220 return list.equals(obj); 221 } 222 223 @Override 224 public int hashCode() { 225 init(); 226 return list.hashCode(); 227 } 228 229 // -----------------------------------------------------// 230 // The additional methods are here 231 // -----------------------------------------------------// 232 233 // -----------------------------------------------------// 234 // proxy method for List 235 // -----------------------------------------------------// 236 237 @Override 238 public void add(int index, E element) { 239 checkReadOnly(); 240 init(); 241 if (modifyListening) { 242 modifyAddition(element); 243 } 244 list.add(index, element); 245 } 246 247 @Override 248 public void addBean(E bean) { 249 add(bean); 250 } 251 252 @Override 253 public boolean add(E o) { 254 checkReadOnly(); 255 init(); 256 if (modifyListening) { 257 if (list.add(o)) { 258 modifyAddition(o); 259 return true; 260 } else { 261 return false; 262 } 263 } 264 return list.add(o); 265 } 266 267 @Override 268 public boolean addAll(Collection<? extends E> c) { 269 checkReadOnly(); 270 init(); 271 if (modifyListening) { 272 // all elements in c are added (no contains checking) 273 getModifyHolder().modifyAdditionAll(c); 274 } 275 return list.addAll(c); 276 } 277 278 @Override 279 public boolean addAll(int index, Collection<? extends E> c) { 280 checkReadOnly(); 281 init(); 282 if (modifyListening) { 283 // all elements in c are added (no contains checking) 284 getModifyHolder().modifyAdditionAll(c); 285 } 286 return list.addAll(index, c); 287 } 288 289 @Override 290 public void clear() { 291 checkReadOnly(); 292 // TODO: when clear() and not initialised could be more clever 293 // and fetch just the Id's 294 initClear(); 295 if (modifyListening) { 296 for (E aList : list) { 297 getModifyHolder().modifyRemoval(aList); 298 } 299 } 300 list.clear(); 301 } 302 303 @Override 304 public boolean contains(Object o) { 305 init(); 306 return list.contains(o); 307 } 308 309 @Override 310 public boolean containsAll(Collection<?> c) { 311 init(); 312 return list.containsAll(c); 313 } 314 315 @Override 316 public E get(int index) { 317 init(); 318 return list.get(index); 319 } 320 321 @Override 322 public int indexOf(Object o) { 323 init(); 324 return list.indexOf(o); 325 } 326 327 @Override 328 public boolean isEmpty() { 329 init(); 330 return list.isEmpty(); 331 } 332 333 @Override 334 public Iterator<E> iterator() { 335 init(); 336 if (isReadOnly()) { 337 return new ReadOnlyListIterator<>(list.listIterator()); 338 } 339 if (modifyListening) { 340 Iterator<E> it = list.iterator(); 341 return new ModifyIterator<>(this, it); 342 } 343 return list.iterator(); 344 } 345 346 @Override 347 public int lastIndexOf(Object o) { 348 init(); 349 return list.lastIndexOf(o); 350 } 351 352 @Override 353 public ListIterator<E> listIterator() { 354 init(); 355 if (isReadOnly()) { 356 return new ReadOnlyListIterator<>(list.listIterator()); 357 } 358 if (modifyListening) { 359 ListIterator<E> it = list.listIterator(); 360 return new ModifyListIterator<>(this, it); 361 } 362 return list.listIterator(); 363 } 364 365 @Override 366 public ListIterator<E> listIterator(int index) { 367 init(); 368 if (isReadOnly()) { 369 return new ReadOnlyListIterator<>(list.listIterator(index)); 370 } 371 if (modifyListening) { 372 ListIterator<E> it = list.listIterator(index); 373 return new ModifyListIterator<>(this, it); 374 } 375 return list.listIterator(index); 376 } 377 378 @Override 379 public void removeBean(E bean) { 380 if (list.remove(bean)) { 381 getModifyHolder().modifyRemoval(bean); 382 } 383 } 384 385 @Override 386 public E remove(int index) { 387 checkReadOnly(); 388 init(); 389 if (modifyListening) { 390 E o = list.remove(index); 391 modifyRemoval(o); 392 return o; 393 } 394 return list.remove(index); 395 } 396 397 @Override 398 public boolean remove(Object o) { 399 checkReadOnly(); 400 init(); 401 if (modifyListening) { 402 boolean isRemove = list.remove(o); 403 if (isRemove) { 404 modifyRemoval(o); 405 } 406 return isRemove; 407 } 408 return list.remove(o); 409 } 410 411 @Override 412 public boolean removeAll(Collection<?> beans) { 413 checkReadOnly(); 414 init(); 415 if (modifyListening) { 416 boolean changed = false; 417 for (Object bean : beans) { 418 if (list.remove(bean)) { 419 // register this bean as having been removed 420 modifyRemoval(bean); 421 changed = true; 422 } 423 } 424 return changed; 425 } 426 return list.removeAll(beans); 427 } 428 429 @Override 430 public boolean retainAll(Collection<?> retainBeans) { 431 checkReadOnly(); 432 init(); 433 if (modifyListening) { 434 boolean changed = false; 435 Iterator<E> it = list.iterator(); 436 while (it.hasNext()) { 437 Object bean = it.next(); 438 if (!retainBeans.contains(bean)) { 439 // removing this bean 440 it.remove(); 441 modifyRemoval(bean); 442 changed = true; 443 } 444 } 445 return changed; 446 } 447 return list.retainAll(retainBeans); 448 } 449 450 @Override 451 public E set(int index, E element) { 452 checkReadOnly(); 453 init(); 454 if (modifyListening) { 455 E o = list.set(index, element); 456 modifyAddition(element); 457 modifyRemoval(o); 458 return o; 459 } 460 return list.set(index, element); 461 } 462 463 @Override 464 public int size() { 465 init(); 466 return list.size(); 467 } 468 469 @Override 470 public List<E> subList(int fromIndex, int toIndex) { 471 init(); 472 if (isReadOnly()) { 473 return Collections.unmodifiableList(list.subList(fromIndex, toIndex)); 474 } 475 if (modifyListening) { 476 return new ModifyList<>(this, list.subList(fromIndex, toIndex)); 477 } 478 return list.subList(fromIndex, toIndex); 479 } 480 481 @Override 482 public Object[] toArray() { 483 init(); 484 return list.toArray(); 485 } 486 487 @Override 488 public <T> T[] toArray(T[] a) { 489 init(); 490 //noinspection SuspiciousToArrayCall 491 return list.toArray(a); 492 } 493 494 private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable { 495 496 private static final long serialVersionUID = 3097271091406323699L; 497 498 private final ListIterator<E> i; 499 500 ReadOnlyListIterator(ListIterator<E> i) { 501 this.i = i; 502 } 503 504 @Override 505 public void add(E o) { 506 throw new IllegalStateException("This collection is in ReadOnly mode"); 507 } 508 509 @Override 510 public void remove() { 511 throw new IllegalStateException("This collection is in ReadOnly mode"); 512 } 513 514 @Override 515 public void set(E o) { 516 throw new IllegalStateException("This collection is in ReadOnly mode"); 517 } 518 519 @Override 520 public boolean hasNext() { 521 return i.hasNext(); 522 } 523 524 @Override 525 public boolean hasPrevious() { 526 return i.hasPrevious(); 527 } 528 529 @Override 530 public E next() { 531 return i.next(); 532 } 533 534 @Override 535 public int nextIndex() { 536 return i.nextIndex(); 537 } 538 539 @Override 540 public E previous() { 541 return i.previous(); 542 } 543 544 @Override 545 public int previousIndex() { 546 return i.previousIndex(); 547 } 548 549 } 550 551 @Override 552 public BeanCollection<E> getShallowCopy() { 553 BeanList<E> copy = new BeanList<>(new CopyOnFirstWriteList<>(list)); 554 copy.setFromOriginal(this); 555 return copy; 556 } 557}