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