001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of the Granite Data Services Platform.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or
008     *   modify it under the terms of the GNU Lesser General Public
009     *   License as published by the Free Software Foundation; either
010     *   version 2.1 of the License, or (at your option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful,
013     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015     *   General Public License for more details.
016     *
017     *   You should have received a copy of the GNU Lesser General Public
018     *   License along with this library; if not, write to the Free Software
019     *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020     *   USA, or see <http://www.gnu.org/licenses/>.
021     */
022    package org.granite.messaging.jmf.persistence;
023    
024    import java.io.IOException;
025    import java.io.ObjectInput;
026    import java.io.ObjectOutput;
027    import java.lang.reflect.InvocationTargetException;
028    import java.util.Arrays;
029    import java.util.Collection;
030    import java.util.Comparator;
031    import java.util.Iterator;
032    import java.util.Map;
033    import java.util.Map.Entry;
034    import java.util.NoSuchElementException;
035    import java.util.Set;
036    import java.util.SortedMap;
037    import java.util.SortedSet;
038    
039    import org.granite.messaging.jmf.ExtendedObjectInput;
040    import org.granite.messaging.persistence.PersistentCollectionSnapshot;
041    
042    /**
043     * @author Franck WOLFF
044     */
045    public class JMFPersistentCollectionSnapshot implements PersistentCollectionSnapshot {
046            
047            private static final long serialVersionUID = 1L;
048            
049            protected boolean initialized = false;
050            protected boolean dirty = false;
051            protected Object[] elements = null;
052            protected boolean sorted = false;
053            protected String comparatorClassName = null;
054            
055            public JMFPersistentCollectionSnapshot(String detachedState) {
056            }
057            
058            public JMFPersistentCollectionSnapshot(boolean sorted, String detachedState) {
059                    this.sorted = sorted;
060            }
061    
062            public JMFPersistentCollectionSnapshot(boolean initialized, String detachedState, boolean dirty, Collection<?> collection) {
063                    this.initialized = initialized;
064                    if (initialized) {
065                            this.dirty = dirty;
066                            this.elements = collection.toArray();
067                            
068                            if (collection instanceof SortedSet) {
069                                    this.sorted = true;
070                                    
071                                    Comparator<?> comparator = ((SortedSet<?>)collection).comparator();
072                                    if (comparator != null)
073                                            this.comparatorClassName = comparator.getClass().getName();
074                            }
075                    }
076            }
077    
078            public JMFPersistentCollectionSnapshot(boolean initialized, String detachedState, boolean dirty, Map<?, ?> collection) {
079                    this.initialized = initialized;
080                    if (initialized) {
081                            this.dirty = dirty;
082                            
083                            Object[] entries = collection.entrySet().toArray();
084                            this.elements = new Object[entries.length * 2];
085                            
086                            int elementIndex = 0;
087                            for (int entryIndex = 0; entryIndex < entries.length; entryIndex++) {
088                                    Map.Entry<?, ?> entry = (Map.Entry<?, ?>)entries[entryIndex];
089                                    this.elements[elementIndex++] = entry.getKey();
090                                    this.elements[elementIndex++] = entry.getValue();
091                            }
092                            
093                            if (collection instanceof SortedMap) {
094                                    this.sorted = true;
095                                    
096                                    Comparator<?> comparator = ((SortedMap<?, ?>)collection).comparator();
097                                    if (comparator != null)
098                                            this.comparatorClassName = comparator.getClass().getName();
099                            }
100                    }
101            }
102            
103            public boolean isInitialized() {
104                    return initialized;
105            }
106            
107            public String getDetachedState() {
108                    return null;
109            }
110    
111            public boolean isDirty() {
112                    return dirty;
113            }
114    
115            public boolean isSorted() {
116                    return sorted;
117            }
118    
119            public String getComparatorClassName() {
120                    return comparatorClassName;
121            }
122            
123            public <T> Comparator<T> newComparator(ObjectInput in)
124                    throws ClassNotFoundException, InstantiationException, IllegalAccessException,
125                    InvocationTargetException, SecurityException, NoSuchMethodException {
126                    
127                    if (comparatorClassName == null)
128                            return null;
129                    
130                    return ((ExtendedObjectInput)in).getReflection().newInstance(comparatorClassName);
131            }
132    
133            @SuppressWarnings("unchecked")
134            public <T> Collection<T> getElementsAsCollection() {
135                    return (Collection<T>)Arrays.asList(elements);
136            }
137            
138            public <K, V> Map<K, V> getElementsAsMap() {
139                    return new SnapshotMap<K, V>(elements);
140            }
141            
142            public void writeExternal(ObjectOutput out) throws IOException {
143                    out.writeBoolean(initialized);
144                    if (initialized) {
145                            if (sorted)
146                                    out.writeUTF(comparatorClassName);
147                            out.writeBoolean(dirty);
148                            out.writeObject(elements);
149                    }
150            }
151    
152            public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
153                    readInitializationData(in);
154                    if (initialized)
155                            readCoreData(in);
156            }
157            
158            public void readInitializationData(ObjectInput in) throws IOException {
159                    initialized = in.readBoolean();
160                    
161                    if (initialized && sorted)
162                            comparatorClassName = in.readUTF();
163            }
164            
165            public void readCoreData(ObjectInput in) throws IOException, ClassNotFoundException {
166                    this.dirty = in.readBoolean();
167                    this.elements = (Object[])in.readObject();
168            }
169            
170            static class SnapshotMap<K, V> implements Map<K, V> {
171                    
172                    private final Object[] elements;
173                    
174                    public SnapshotMap(Object[] elements) {
175                            if ((elements.length % 2) != 0)
176                                    throw new IllegalArgumentException("Elements must have an even length: " + elements.length);
177                            this.elements = elements;
178                    }
179                    
180                    public int size() {
181                            return elements.length / 2;
182                    }
183    
184                    public boolean isEmpty() {
185                            return elements.length == 0;
186                    }
187    
188                    public Set<Entry<K, V>> entrySet() {
189                            return new Set<Entry<K, V>>() {
190    
191                                    public int size() {
192                                            return elements.length / 2;
193                                    }
194    
195                                    public boolean isEmpty() {
196                                            return elements.length == 0;
197                                    }
198    
199                                    public Iterator<Entry<K, V>> iterator() {
200                                            
201                                            return new Iterator<Entry<K, V>>() {
202    
203                                                    private int cursor = 0;
204                                                    
205                                                    public boolean hasNext() {
206                                                            return cursor < elements.length;
207                                                    }
208    
209                                                    @SuppressWarnings("unchecked")
210                                                    public Entry<K, V> next() {
211                                                            if (cursor >= elements.length)
212                                                                    throw new NoSuchElementException();
213                                                            
214                                                            K key = (K)elements[cursor++];
215                                                            V value = (V)elements[cursor++];
216                                                            return new SnapshotMapEntry<K, V>(key, value);
217                                                    }
218    
219                                                    public void remove() {
220                                                            throw new UnsupportedOperationException();
221                                                    }
222                                            };
223                                    }
224    
225                                    public boolean contains(Object o) {
226                                            throw new UnsupportedOperationException();
227                                    }
228    
229                                    public Object[] toArray() {
230                                            throw new UnsupportedOperationException();
231                                    }
232    
233                                    public <T> T[] toArray(T[] a) {
234                                            throw new UnsupportedOperationException();
235                                    }
236    
237                                    public boolean add(Entry<K, V> e) {
238                                            throw new UnsupportedOperationException();
239                                    }
240    
241                                    public boolean remove(Object o) {
242                                            throw new UnsupportedOperationException();
243                                    }
244    
245                                    public boolean containsAll(Collection<?> c) {
246                                            throw new UnsupportedOperationException();
247                                    }
248    
249                                    public boolean addAll(Collection<? extends Entry<K, V>> c) {
250                                            throw new UnsupportedOperationException();
251                                    }
252    
253                                    public boolean retainAll(Collection<?> c) {
254                                            throw new UnsupportedOperationException();
255                                    }
256    
257                                    public boolean removeAll(Collection<?> c) {
258                                            throw new UnsupportedOperationException();
259                                    }
260    
261                                    public void clear() {
262                                            throw new UnsupportedOperationException();
263                                    }
264    
265                                    @Override
266                                    public int hashCode() {
267                                            throw new UnsupportedOperationException();
268                                    }
269    
270                                    @Override
271                                    public boolean equals(Object obj) {
272                                            throw new UnsupportedOperationException();
273                                    }
274                            };
275                    }
276    
277                    public boolean containsKey(Object key) {
278                            throw new UnsupportedOperationException();
279                    }
280    
281                    public boolean containsValue(Object value) {
282                            throw new UnsupportedOperationException();
283                    }
284    
285                    public V get(Object key) {
286                            throw new UnsupportedOperationException();
287                    }
288    
289                    public V put(K key, V value) {
290                            throw new UnsupportedOperationException();
291                    }
292    
293                    public V remove(Object key) {
294                            throw new UnsupportedOperationException();
295                    }
296    
297                    public void putAll(Map<? extends K, ? extends V> m) {
298                            throw new UnsupportedOperationException();
299                    }
300    
301                    public void clear() {
302                            throw new UnsupportedOperationException();
303                    }
304    
305                    public Set<K> keySet() {
306                            throw new UnsupportedOperationException();
307                    }
308    
309                    public Collection<V> values() {
310                            throw new UnsupportedOperationException();
311                    }
312    
313                    @Override
314                    public int hashCode() {
315                            throw new UnsupportedOperationException();
316                    }
317    
318                    @Override
319                    public boolean equals(Object obj) {
320                            throw new UnsupportedOperationException();
321                    }
322            }
323            
324            static class SnapshotMapEntry<K, V> implements Entry<K, V> {
325    
326                    private final K key;
327                    private final V value;
328                    
329                    public SnapshotMapEntry(K key, V value) {
330                            this.key = key;
331                            this.value = value;
332                    }
333    
334                    public K getKey() {
335                            return key;
336                    }
337    
338                    public V getValue() {
339                            return value;
340                    }
341    
342                    public V setValue(V value) {
343                            throw new UnsupportedOperationException();
344                    }
345    
346                    @Override
347                    public int hashCode() {
348                            return (key == null   ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
349                    }
350    
351                    @Override
352                    public boolean equals(Object obj) {
353                            if (!(obj instanceof Entry))
354                                    return false;
355                            Entry<?, ?> e = (Entry<?, ?>)obj;
356                            return (
357                                    (key == null ? e.getKey() == null : key.equals(e.getKey()))  &&
358                                    (value == null ? e.getValue() == null : value.equals(e.getValue()))
359                            );
360                    }
361            }
362    }