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 180 } else { 181 sb.append("size[").append(set.size()).append("]"); 182 sb.append(" set").append(set); 183 } 184 return sb.toString(); 185 } 186 187 /** 188 * Equal if obj is a Set and equal in a Set sense. 189 */ 190 @Override 191 public boolean equals(Object obj) { 192 init(); 193 return set.equals(obj); 194 } 195 196 @Override 197 public int hashCode() { 198 init(); 199 return set.hashCode(); 200 } 201 202 @Override 203 public void addBean(E bean) { 204 add(bean); 205 } 206 207 @Override 208 public void removeBean(E bean) { 209 if (set.remove(bean)) { 210 getModifyHolder().modifyRemoval(bean); 211 } 212 } 213 214 // -----------------------------------------------------// 215 // proxy method for map 216 // -----------------------------------------------------// 217 218 @Override 219 public boolean add(E o) { 220 checkReadOnly(); 221 init(); 222 if (modifyListening) { 223 if (set.add(o)) { 224 modifyAddition(o); 225 return true; 226 } else { 227 return false; 228 } 229 } 230 return set.add(o); 231 } 232 233 @Override 234 public boolean addAll(Collection<? extends E> addCollection) { 235 checkReadOnly(); 236 init(); 237 if (modifyListening) { 238 boolean changed = false; 239 for (E bean : addCollection) { 240 if (set.add(bean)) { 241 // register the addition of the bean 242 modifyAddition(bean); 243 changed = true; 244 } 245 } 246 return changed; 247 } 248 return set.addAll(addCollection); 249 } 250 251 @Override 252 public void clear() { 253 checkReadOnly(); 254 initClear(); 255 if (modifyListening) { 256 for (E bean : set) { 257 modifyRemoval(bean); 258 } 259 } 260 set.clear(); 261 } 262 263 @Override 264 public boolean contains(Object o) { 265 init(); 266 return set.contains(o); 267 } 268 269 @Override 270 public boolean containsAll(Collection<?> c) { 271 init(); 272 return set.containsAll(c); 273 } 274 275 @Override 276 public boolean isEmpty() { 277 init(); 278 return set.isEmpty(); 279 } 280 281 @Override 282 public Iterator<E> iterator() { 283 init(); 284 if (isReadOnly()) { 285 return new ReadOnlyIterator<>(set.iterator()); 286 } 287 if (modifyListening) { 288 return new ModifyIterator<>(this, set.iterator()); 289 } 290 return set.iterator(); 291 } 292 293 @Override 294 public boolean remove(Object o) { 295 checkReadOnly(); 296 init(); 297 if (modifyListening) { 298 if (set.remove(o)) { 299 modifyRemoval(o); 300 return true; 301 } 302 return false; 303 } 304 return set.remove(o); 305 } 306 307 @Override 308 public boolean removeAll(Collection<?> beans) { 309 checkReadOnly(); 310 init(); 311 if (modifyListening) { 312 boolean changed = false; 313 for (Object bean : beans) { 314 if (set.remove(bean)) { 315 modifyRemoval(bean); 316 changed = true; 317 } 318 } 319 return changed; 320 } 321 return set.removeAll(beans); 322 } 323 324 @Override 325 public boolean retainAll(Collection<?> beans) { 326 checkReadOnly(); 327 init(); 328 if (modifyListening) { 329 boolean changed = false; 330 Iterator<?> it = set.iterator(); 331 while (it.hasNext()) { 332 Object bean = it.next(); 333 if (!beans.contains(bean)) { 334 // not retaining this bean so add it to the removal list 335 it.remove(); 336 modifyRemoval(bean); 337 changed = true; 338 } 339 } 340 return changed; 341 } 342 return set.retainAll(beans); 343 } 344 345 @Override 346 public int size() { 347 init(); 348 return set.size(); 349 } 350 351 @Override 352 public Object[] toArray() { 353 init(); 354 return set.toArray(); 355 } 356 357 @Override 358 public <T> T[] toArray(T[] a) { 359 init(); 360 //noinspection SuspiciousToArrayCall 361 return set.toArray(a); 362 } 363 364 private static class ReadOnlyIterator<E> implements Iterator<E>, Serializable { 365 366 private static final long serialVersionUID = 2577697326745352605L; 367 368 private final Iterator<E> it; 369 370 ReadOnlyIterator(Iterator<E> it) { 371 this.it = it; 372 } 373 374 @Override 375 public boolean hasNext() { 376 return it.hasNext(); 377 } 378 379 @Override 380 public E next() { 381 return it.next(); 382 } 383 384 @Override 385 public void remove() { 386 throw new IllegalStateException("This collection is in ReadOnly mode"); 387 } 388 } 389 390 @Override 391 public BeanCollection<E> getShallowCopy() { 392 BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set)); 393 copy.setFromOriginal(this); 394 return copy; 395 } 396}