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