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