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.Collection; 010import java.util.Iterator; 011import java.util.LinkedHashSet; 012import java.util.Set; 013 014/** 015 * Set capable of lazy loading. 016 */ 017public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd { 018 019 private static final long serialVersionUID = 1L; 020 021 /** 022 * The underlying Set implementation. 023 */ 024 private Set<E> set; 025 026 /** 027 * Create with a specific Set implementation. 028 */ 029 public BeanSet(Set<E> set) { 030 this.set = set; 031 } 032 033 /** 034 * Create using an underlying LinkedHashSet. 035 */ 036 public BeanSet() { 037 this(new LinkedHashSet<>()); 038 } 039 040 public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) { 041 super(loader, ownerBean, propertyName); 042 } 043 044 @Override 045 public void reset(EntityBean ownerBean, String propertyName) { 046 this.ownerBean = ownerBean; 047 this.propertyName = propertyName; 048 this.set = null; 049 } 050 051 @Override 052 public boolean isSkipSave() { 053 return set == null || (set.isEmpty() && !holdsModifications()); 054 } 055 056 @Override 057 @SuppressWarnings("unchecked") 058 public void addEntityBean(EntityBean bean) { 059 set.add((E) bean); 060 } 061 062 @Override 063 @SuppressWarnings("unchecked") 064 public void loadFrom(BeanCollection<?> other) { 065 if (set == null) { 066 set = new LinkedHashSet<>(); 067 } 068 set.addAll((Collection<? extends E>) other.getActualDetails()); 069 } 070 071 @Override 072 public void internalAddWithCheck(Object bean) { 073 // set add() already de-dups so just add it 074 internalAdd(bean); 075 } 076 077 @Override 078 @SuppressWarnings("unchecked") 079 public void internalAdd(Object bean) { 080 if (set == null) { 081 set = new LinkedHashSet<>(); 082 } 083 if (bean != null) { 084 set.add((E) bean); 085 } 086 } 087 088 /** 089 * Returns true if the underlying set has its data. 090 */ 091 @Override 092 public boolean isPopulated() { 093 return set != null; 094 } 095 096 /** 097 * Return true if this is a reference (lazy loading) bean collection. This is 098 * the same as !isPopulated(); 099 */ 100 @Override 101 public boolean isReference() { 102 return set == null; 103 } 104 105 @Override 106 public boolean checkEmptyLazyLoad() { 107 if (set == null) { 108 set = new LinkedHashSet<>(); 109 return true; 110 } else { 111 return false; 112 } 113 } 114 115 private void initClear() { 116 lock.lock(); 117 try { 118 if (set == null) { 119 if (!disableLazyLoad && modifyListening) { 120 lazyLoadCollection(true); 121 } else { 122 set = new LinkedHashSet<>(); 123 } 124 } 125 } finally { 126 lock.unlock(); 127 } 128 } 129 130 private void init() { 131 lock.lock(); 132 try { 133 if (set == null) { 134 if (disableLazyLoad) { 135 set = new LinkedHashSet<>(); 136 } else { 137 lazyLoadCollection(true); 138 } 139 } 140 } finally { 141 lock.unlock(); 142 } 143 } 144 145 /** 146 * Set the underlying set (used for lazy fetch). 147 */ 148 @SuppressWarnings("unchecked") 149 public void setActualSet(Set<?> set) { 150 this.set = (Set<E>) set; 151 } 152 153 /** 154 * Return the actual underlying set. 155 */ 156 public Set<E> getActualSet() { 157 return set; 158 } 159 160 @Override 161 public Collection<E> getActualDetails() { 162 return set; 163 } 164 165 @Override 166 public Collection<?> getActualEntries() { 167 return set; 168 } 169 170 @Override 171 public String toString() { 172 StringBuilder sb = new StringBuilder(50); 173 sb.append("BeanSet "); 174 if (isReadOnly()) { 175 sb.append("readOnly "); 176 } 177 if (set == null) { 178 sb.append("deferred "); 179 } else { 180 sb.append("size[").append(set.size()).append("]"); 181 sb.append(" set").append(set); 182 } 183 return sb.toString(); 184 } 185 186 /** 187 * Equal if obj is a Set and equal in a Set sense. 188 */ 189 @Override 190 public boolean equals(Object obj) { 191 init(); 192 return set.equals(obj); 193 } 194 195 @Override 196 public int hashCode() { 197 init(); 198 return set.hashCode(); 199 } 200 201 @Override 202 public void addBean(E bean) { 203 add(bean); 204 } 205 206 @Override 207 public void removeBean(E bean) { 208 if (set.remove(bean)) { 209 getModifyHolder().modifyRemoval(bean); 210 } 211 } 212 213 // -----------------------------------------------------// 214 // proxy method for map 215 // -----------------------------------------------------// 216 217 @Override 218 public boolean add(E o) { 219 checkReadOnly(); 220 init(); 221 if (modifyListening) { 222 if (set.add(o)) { 223 modifyAddition(o); 224 return true; 225 } else { 226 return false; 227 } 228 } 229 return set.add(o); 230 } 231 232 @Override 233 public boolean addAll(Collection<? extends E> addCollection) { 234 checkReadOnly(); 235 init(); 236 if (modifyListening) { 237 boolean changed = false; 238 for (E bean : addCollection) { 239 if (set.add(bean)) { 240 // register the addition of the bean 241 modifyAddition(bean); 242 changed = true; 243 } 244 } 245 return changed; 246 } 247 return set.addAll(addCollection); 248 } 249 250 @Override 251 public void clear() { 252 checkReadOnly(); 253 initClear(); 254 if (modifyListening) { 255 for (E bean : set) { 256 modifyRemoval(bean); 257 } 258 } 259 set.clear(); 260 } 261 262 @Override 263 public boolean contains(Object o) { 264 init(); 265 return set.contains(o); 266 } 267 268 @Override 269 public boolean containsAll(Collection<?> c) { 270 init(); 271 return set.containsAll(c); 272 } 273 274 @Override 275 public boolean isEmpty() { 276 init(); 277 return set.isEmpty(); 278 } 279 280 @Override 281 public Iterator<E> iterator() { 282 init(); 283 if (isReadOnly()) { 284 return new ReadOnlyIterator<>(set.iterator()); 285 } 286 if (modifyListening) { 287 return new ModifyIterator<>(this, set.iterator()); 288 } 289 return set.iterator(); 290 } 291 292 @Override 293 public boolean remove(Object o) { 294 checkReadOnly(); 295 init(); 296 if (modifyListening) { 297 if (set.remove(o)) { 298 modifyRemoval(o); 299 return true; 300 } 301 return false; 302 } 303 return set.remove(o); 304 } 305 306 @Override 307 public boolean removeAll(Collection<?> beans) { 308 checkReadOnly(); 309 init(); 310 if (modifyListening) { 311 boolean changed = false; 312 for (Object bean : beans) { 313 if (set.remove(bean)) { 314 modifyRemoval(bean); 315 changed = true; 316 } 317 } 318 return changed; 319 } 320 return set.removeAll(beans); 321 } 322 323 @Override 324 public boolean retainAll(Collection<?> beans) { 325 checkReadOnly(); 326 init(); 327 if (modifyListening) { 328 boolean changed = false; 329 Iterator<?> it = set.iterator(); 330 while (it.hasNext()) { 331 Object bean = it.next(); 332 if (!beans.contains(bean)) { 333 // not retaining this bean so add it to the removal list 334 it.remove(); 335 modifyRemoval(bean); 336 changed = true; 337 } 338 } 339 return changed; 340 } 341 return set.retainAll(beans); 342 } 343 344 @Override 345 public int size() { 346 init(); 347 return set.size(); 348 } 349 350 @Override 351 public Object[] toArray() { 352 init(); 353 return set.toArray(); 354 } 355 356 @Override 357 public <T> T[] toArray(T[] a) { 358 init(); 359 //noinspection SuspiciousToArrayCall 360 return set.toArray(a); 361 } 362 363 private static class ReadOnlyIterator<E> implements Iterator<E>, Serializable { 364 365 private static final long serialVersionUID = 2577697326745352605L; 366 367 private final Iterator<E> it; 368 369 ReadOnlyIterator(Iterator<E> it) { 370 this.it = it; 371 } 372 373 @Override 374 public boolean hasNext() { 375 return it.hasNext(); 376 } 377 378 @Override 379 public E next() { 380 return it.next(); 381 } 382 383 @Override 384 public void remove() { 385 throw new IllegalStateException("This collection is in ReadOnly mode"); 386 } 387 } 388 389 @Override 390 public BeanCollection<E> getShallowCopy() { 391 BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set)); 392 copy.setFromOriginal(this); 393 return copy; 394 } 395}