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