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