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