001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of Granite Data Services.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or modify
008     *   it under the terms of the GNU Library General Public License as published by
009     *   the Free Software Foundation; either version 2 of the License, or (at your
010     *   option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful, but
013     *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014     *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015     *   for more details.
016     *
017     *   You should have received a copy of the GNU Library General Public License
018     *   along with this library; if not, see <http://www.gnu.org/licenses/>.
019     */
020    package org.granite.client.persistence.collection;
021    
022    import java.io.IOException;
023    import java.io.ObjectInput;
024    import java.io.ObjectOutput;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Comparator;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.ListIterator;
031    import java.util.Map;
032    import java.util.Set;
033    import java.util.SortedMap;
034    import java.util.SortedSet;
035    
036    import org.granite.client.persistence.LazyInitializationException;
037    import org.granite.client.persistence.Loader;
038    import org.granite.logging.Logger;
039    import org.granite.messaging.persistence.PersistentCollectionSnapshot;
040    import org.granite.util.TypeUtil;
041    
042    
043    /**
044     * @author Franck WOLFF
045     */
046    public abstract class AbstractPersistentCollection<C> implements PersistentCollection {
047            
048            private static final Logger log = Logger.getLogger(AbstractPersistentCollection.class);
049    
050            private volatile C collection = null;
051            private volatile boolean dirty = false;
052            private volatile String detachedState = null;
053            private Loader<PersistentCollection> loader = new DefaultCollectionLoader();
054        private List<ChangeListener> changeListeners = new ArrayList<ChangeListener>();
055        private List<InitializationListener> initializationListeners = new ArrayList<InitializationListener>();
056            
057            protected AbstractPersistentCollection() {
058            }
059            
060            protected void init(C collection, String detachedState, boolean dirty) {
061                    this.collection = collection;
062                    if (detachedState != null)
063                            this.detachedState = detachedState;
064                    this.dirty = dirty;
065            }
066            
067            public Loader<PersistentCollection> getLoader() {
068                    return this.loader;
069            }
070            public void setLoader(Loader<PersistentCollection> loader) {
071                    this.loader = loader;
072            }
073            
074            protected boolean checkInitializedRead() {
075                    if (wasInitialized())
076                            return true;
077                    loader.load(this, null);
078                    return false;
079            }
080            
081            protected void checkInitializedWrite() {
082                    if (!wasInitialized())
083                            throw new LazyInitializationException(getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this)));
084            }
085            
086            protected C getCollection() {
087                    return collection;
088            }
089            
090            public String getDetachedState() {
091                    return detachedState;
092            }
093            
094            protected ClassLoader getClassLoader() {
095                    return Thread.currentThread().getContextClassLoader();
096            }
097    
098            public boolean wasInitialized() {
099                    return collection != null;
100            }
101    
102            public boolean isDirty() {
103                    return dirty;
104            }
105    
106            public void dirty() {
107                    dirty = true;
108            for (ChangeListener listener : changeListeners)
109                listener.changed(this);
110            }
111    
112            public void clearDirty() {
113                    dirty = false;
114            }
115            
116            
117            public PersistentCollection clone(boolean uninitialize) {
118                    try {
119                        @SuppressWarnings("unchecked")
120                            AbstractPersistentCollection<C> collection = TypeUtil.newInstance(getClass(), AbstractPersistentCollection.class);
121                    if (wasInitialized() && !uninitialize)
122                            collection.init(getCollection(), getDetachedState(), isDirty());
123                    return collection;
124                    }
125                    catch (Exception e) {
126                            throw new RuntimeException("Could not clone collection " + this.getClass().getName(), e);
127                    }
128        }
129    
130            
131            protected abstract PersistentCollectionSnapshot createSnapshot(Object io, boolean forReading);
132            protected abstract void updateFromSnapshot(ObjectInput in, PersistentCollectionSnapshot snapshot);
133    
134            public void writeExternal(ObjectOutput out) throws IOException {
135                    PersistentCollectionSnapshot snapshot = createSnapshot(out, false);
136                    snapshot.writeExternal(out);
137            }
138    
139            public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
140                    PersistentCollectionSnapshot snapshot = createSnapshot(in, true);
141                    snapshot.readExternal(in);
142                    updateFromSnapshot(in, snapshot);
143            }
144            
145            @Override
146            public String toString() {
147                    return getClass().getName() + " {initialized=" + wasInitialized() + ", dirty=" + isDirty() + "}" +
148                            (collection != null ? ": " + collection.toString() : "");
149            }
150    
151            class IteratorProxy<E> implements Iterator<E> {
152                    
153                    private final Iterator<E> iterator;
154                    
155                    public IteratorProxy(Iterator<E> iterator) {
156                            this.iterator = iterator;
157                    }
158    
159                    public boolean hasNext() {
160                            return iterator.hasNext();
161                    }
162    
163                    public E next() {
164                            if (!checkInitializedRead())
165                                    return null;
166                            return iterator.next();
167                    }
168                    
169                    public void remove() {
170                            checkInitializedWrite();
171                            iterator.remove();
172                            dirty();
173                    }
174                    
175                    @Override
176                    public int hashCode() {
177                            return iterator.hashCode();
178                    }
179    
180                    @Override
181                    public boolean equals(Object obj) {
182                            return iterator.equals(obj);
183                    }
184            }
185            
186            class ListIteratorProxy<E> implements ListIterator<E> {
187                    
188                    private final ListIterator<E> iterator;
189    
190                    private E lastNextOrPrevious = null;
191                    
192                    public ListIteratorProxy(ListIterator<E> iterator) {
193                            this.iterator = iterator;
194                    }
195                    
196                    public boolean hasNext() {
197                            return iterator.hasNext();
198                    }
199    
200                    public E next() {
201                            if (!checkInitializedRead())
202                                    return null;
203                            return (lastNextOrPrevious = iterator.next());
204                    }
205    
206                    public boolean hasPrevious() {
207                            return iterator.hasPrevious();
208                    }
209    
210                    public E previous() {
211                            if (!checkInitializedRead())
212                                    return null;
213                            return (lastNextOrPrevious = iterator.previous());
214                    }
215    
216                    public int nextIndex() {
217                            return iterator.nextIndex();
218                    }
219    
220                    public int previousIndex() {
221                            return iterator.previousIndex();
222                    }
223    
224                    public void remove() {
225                            checkInitializedWrite();
226                            iterator.remove();
227                            lastNextOrPrevious = null;
228                            dirty();
229                    }
230    
231                    public void set(E e) {
232                            checkInitializedWrite();
233                            iterator.set(e);
234                            if (e == null ? lastNextOrPrevious != null : !e.equals(lastNextOrPrevious))
235                                    dirty();
236                    }
237    
238                    public void add(E e) {
239                            checkInitializedWrite();
240                            iterator.add(e);
241                            lastNextOrPrevious = null;
242                            dirty();
243                    }
244    
245                    @Override
246                    public int hashCode() {
247                            return iterator.hashCode();
248                    }
249    
250                    @Override
251                    public boolean equals(Object obj) {
252                            return iterator.equals(obj);
253                    }
254            }
255            
256            class CollectionProxy<E> implements Collection<E> {
257                    
258                    protected final Collection<E> collection;
259    
260                    public CollectionProxy(Collection<E> collection) {
261                            this.collection = collection;
262                    }
263    
264                    public int size() {
265                            return collection.size();
266                    }
267    
268                    public boolean isEmpty() {
269                            return collection.isEmpty();
270                    }
271    
272                    public boolean contains(Object o) {
273                            return collection.contains(o);
274                    }
275    
276                    public Iterator<E> iterator() {
277                            return new IteratorProxy<E>(collection.iterator());
278                    }
279    
280                    public Object[] toArray() {
281                            return collection.toArray();
282                    }
283    
284                    public <T> T[] toArray(T[] a) {
285                            return collection.toArray(a);
286                    }
287    
288                    public boolean add(E e) {
289                            if (collection.add(e)) {
290                                    dirty();
291                                    return true;
292                            }
293                            return false;
294                    }
295    
296                    public boolean remove(Object o) {
297                            if (collection.remove(o)) {
298                                    dirty();
299                                    return true;
300                            }
301                            return false;
302                    }
303    
304                    public boolean containsAll(Collection<?> c) {
305                            return collection.containsAll(c);
306                    }
307    
308                    public boolean addAll(Collection<? extends E> c) {
309                            if (collection.addAll(c)) {
310                                    dirty();
311                                    return true;
312                            }
313                            return false;
314                    }
315    
316                    public boolean removeAll(Collection<?> c) {
317                            if (collection.removeAll(c)) {
318                                    dirty();
319                                    return true;
320                            }
321                            return false;
322                    }
323    
324                    public boolean retainAll(Collection<?> c) {
325                            if (collection.retainAll(c)) {
326                                    dirty();
327                                    return true;
328                            }
329                            return false;
330                    }
331    
332                    public void clear() {
333                            if (!collection.isEmpty()) {
334                                    collection.clear();
335                                    dirty();
336                            }
337                    }
338    
339                    @Override
340                    public int hashCode() {
341                            return collection.hashCode();
342                    }
343    
344                    @Override
345                    public boolean equals(Object obj) {
346                            return collection.equals(obj);
347                    }
348            }
349            
350            class SetProxy<E> extends CollectionProxy<E> implements Set<E> {
351    
352                    public SetProxy(Set<E> collection) {
353                            super(collection);
354                    }
355            }
356            
357            class ListProxy<E> extends CollectionProxy<E> implements List<E> {
358    
359                    public ListProxy(List<E> collection) {
360                            super(collection);
361                    }
362    
363                    public boolean addAll(int index, Collection<? extends E> c) {
364                            if (((List<E>)collection).addAll(index, c)) {
365                                    dirty();
366                                    return true;
367                            }
368                            return false;
369                    }
370    
371                    public E get(int index) {
372                            return ((List<E>)collection).get(index);
373                    }
374                    
375                    public E set(int index, E element) {
376                            E previousElement = ((List<E>)collection).set(index, element);
377                            if (previousElement == null ? element != null : !previousElement.equals(element))
378                                    dirty();
379                            return previousElement;
380                    }
381    
382                    public void add(int index, E element) {
383                            ((List<E>)collection).add(index, element);
384                            dirty();
385                    }
386    
387                    public E remove(int index) {
388                            E removedElement = ((List<E>)collection).remove(index);
389                            dirty();
390                            return removedElement;
391                    }
392    
393                    public int indexOf(Object o) {
394                            return ((List<E>)collection).indexOf(o);
395                    }
396    
397                    public int lastIndexOf(Object o) {
398                            return ((List<E>)collection).lastIndexOf(o);
399                    }
400    
401                    public ListIterator<E> listIterator() {
402                            return listIterator(0);
403                    }
404    
405                    public ListIterator<E> listIterator(int index) {
406                            return new ListIteratorProxy<E>(((List<E>)collection).listIterator(index));
407                    }
408    
409                    public List<E> subList(int fromIndex, int toIndex) {
410                            return new ListProxy<E>(((List<E>)collection).subList(fromIndex, toIndex));
411                    }
412            }
413            
414            class SortedSetProxy<E> extends SetProxy<E> implements SortedSet<E> {
415    
416                    public SortedSetProxy(SortedSet<E> collection) {
417                            super(collection);
418                    }
419    
420                    public Comparator<? super E> comparator() {
421                            return ((SortedSet<E>)collection).comparator();
422                    }
423    
424                    public SortedSet<E> subSet(E fromElement, E toElement) {
425                            return new SortedSetProxy<E>(((SortedSet<E>)collection).subSet(fromElement, toElement));
426                    }
427    
428                    public SortedSet<E> headSet(E toElement) {
429                            return new SortedSetProxy<E>(((SortedSet<E>)collection).headSet(toElement));
430                    }
431    
432                    public SortedSet<E> tailSet(E fromElement) {
433                            return new SortedSetProxy<E>(((SortedSet<E>)collection).tailSet(fromElement));
434                    }
435    
436                    public E first() {
437                            return ((SortedSet<E>)collection).first();
438                    }
439    
440                    public E last() {
441                            return ((SortedSet<E>)collection).last();
442                    }
443            }
444            
445            class SortedMapProxy<K, V> implements SortedMap<K, V> {
446                    
447                    protected final SortedMap<K, V> sortedMap;
448                    
449                    public SortedMapProxy(SortedMap<K, V> sortedMap) {
450                            this.sortedMap = sortedMap;
451                    }
452    
453                    public int size() {
454                            return sortedMap.size();
455                    }
456    
457                    public boolean isEmpty() {
458                            return sortedMap.isEmpty();
459                    }
460    
461                    public boolean containsKey(Object key) {
462                            return sortedMap.containsKey(key);
463                    }
464    
465                    public boolean containsValue(Object value) {
466                            return sortedMap.containsValue(value);
467                    }
468    
469                    public V get(Object key) {
470                            return sortedMap.get(key);
471                    }
472    
473                    public V put(K key, V value) {
474                            boolean containsKey = sortedMap.containsKey(key);
475                            V previousValue = sortedMap.put(key, value);
476                            if (!containsKey || (previousValue == null ? value != null : !previousValue.equals(value)))
477                                    dirty();
478                            return previousValue;
479                    }
480    
481                    public V remove(Object key) {
482                            boolean containsKey = sortedMap.containsKey(key);
483                            V removedValue = sortedMap.remove(key);
484                            if (containsKey)
485                                    dirty();
486                            return removedValue;
487                    }
488    
489                    public void putAll(Map<? extends K, ? extends V> m) {
490                            for (Map.Entry<? extends K, ? extends V> entry : m.entrySet())
491                                    put(entry.getKey(), entry.getValue());
492                    }
493    
494                    public void clear() {
495                            if (!sortedMap.isEmpty()) {
496                                    sortedMap.clear();
497                                    dirty();
498                            }
499                    }
500    
501                    public Comparator<? super K> comparator() {
502                            return sortedMap.comparator();
503                    }
504    
505                    public SortedMap<K, V> subMap(K fromKey, K toKey) {
506                            return new SortedMapProxy<K, V>(sortedMap.subMap(fromKey, toKey));
507                    }
508    
509                    public SortedMap<K, V> headMap(K toKey) {
510                            return new SortedMapProxy<K, V>(sortedMap.headMap(toKey));
511                    }
512    
513                    public SortedMap<K, V> tailMap(K fromKey) {
514                            return new SortedMapProxy<K, V>(sortedMap.tailMap(fromKey));
515                    }
516    
517                    public K firstKey() {
518                            return sortedMap.firstKey();
519                    }
520    
521                    public K lastKey() {
522                            return sortedMap.lastKey();
523                    }
524    
525                    public Set<K> keySet() {
526                            return new SetProxy<K>(sortedMap.keySet());
527                    }
528    
529                    public Collection<V> values() {
530                            return new CollectionProxy<V>(sortedMap.values());
531                    }
532    
533                    public Set<Entry<K, V>> entrySet() {
534                            return new SetProxy<Entry<K, V>>(sortedMap.entrySet());
535                    }
536    
537                    @Override
538                    public int hashCode() {
539                            return sortedMap.hashCode();
540                    }
541    
542                    @Override
543                    public boolean equals(Object obj) {
544                            return sortedMap.equals(obj);
545                    }
546            }
547    
548            
549        
550        public void addListener(ChangeListener listener) {
551            if (!changeListeners.contains(listener))
552                changeListeners.add(listener);
553        }
554        
555        public void removeListener(ChangeListener listener) {
556            changeListeners.remove(listener);
557        }
558            
559            
560            private static class DefaultCollectionLoader implements Loader<PersistentCollection> {
561                    
562                    public void load(PersistentCollection collection, InitializationCallback callback) {
563                            throw new LazyInitializationException(collection.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(collection)));
564                    }
565                    
566                    public void onInitializing() {
567                    }
568                    
569                    public void onInitialize() {
570                    }
571                    
572                    public void onUninitialize() {                  
573                    }
574            }
575        
576        public void addListener(InitializationListener listener) {
577            if (!initializationListeners.contains(listener))
578                initializationListeners.add(listener);
579        }
580        
581        public void removeListener(InitializationListener listener) {
582            initializationListeners.remove(listener);
583        }
584        
585        public void initializing() {
586            loader.onInitializing();
587        }
588        
589            public void initialize() {
590            loader.onInitialize();
591            
592            for (InitializationListener listener : initializationListeners)
593                listener.initialized(this);
594            
595            doInitialize();
596            
597            log.debug("initialized");
598        }
599        
600        protected abstract void doInitialize();
601        
602        public void uninitialize() {
603            loader.onUninitialize();
604            
605            for (InitializationListener listener : initializationListeners)
606                listener.uninitialized(this);
607            
608            log.debug("uninitialized");
609        }
610        
611        public void withInitialized(InitializationCallback callback) {
612            if (wasInitialized())
613                callback.call(this);
614            else
615                    loader.load(this, callback);
616        }
617    }