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