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