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 */
022package org.granite.messaging.jmf.persistence;
023
024import java.io.IOException;
025import java.io.ObjectInput;
026import java.io.ObjectOutput;
027import java.lang.reflect.InvocationTargetException;
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Comparator;
031import java.util.Iterator;
032import java.util.Map;
033import java.util.Map.Entry;
034import java.util.NoSuchElementException;
035import java.util.Set;
036import java.util.SortedMap;
037import java.util.SortedSet;
038
039import org.granite.messaging.jmf.ExtendedObjectInput;
040import org.granite.messaging.persistence.PersistentCollectionSnapshot;
041
042/**
043 * @author Franck WOLFF
044 */
045public 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}