• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.annotations.GwtIncompatible;
21 
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.lang.reflect.Field;
26 import java.util.Collection;
27 import java.util.Map;
28 
29 /**
30  * Provides static methods for serializing collection classes.
31  *
32  * <p>This class assists the implementation of collection classes. Do not use
33  * this class to serialize collections that are defined elsewhere.
34  *
35  * @author Jared Levy
36  */
37 @GwtCompatible(emulated = true) // Accessible but not supported in GWT.
38 final class Serialization {
Serialization()39   private Serialization() {}
40 
41   /**
42    * Reads a count corresponding to a serialized map, multiset, or multimap. It
43    * returns the size of a map serialized by {@link
44    * #writeMap(Map, ObjectOutputStream)}, the number of distinct elements in a
45    * multiset serialized by {@link
46    * #writeMultiset(Multiset, ObjectOutputStream)}, or the number of distinct
47    * keys in a multimap serialized by {@link
48    * #writeMultimap(Multimap, ObjectOutputStream)}.
49    *
50    * <p>The returned count may be used to construct an empty collection of the
51    * appropriate capacity before calling any of the {@code populate} methods.
52    */
53   @GwtIncompatible("java.io.ObjectInputStream")
readCount(ObjectInputStream stream)54   public static int readCount(ObjectInputStream stream) throws IOException {
55     return stream.readInt();
56   }
57 
58   /**
59    * Stores the contents of a map in an output stream, as part of serialization.
60    * It does not support concurrent maps whose content may change while the
61    * method is running.
62    *
63    * <p>The serialized output consists of the number of entries, first key,
64    * first value, second key, second value, and so on.
65    */
66   @GwtIncompatible("java.io.ObjectOutputStream")
writeMap(Map<K, V> map, ObjectOutputStream stream)67   public static <K, V> void writeMap(Map<K, V> map, ObjectOutputStream stream)
68       throws IOException {
69     stream.writeInt(map.size());
70     for (Map.Entry<K, V> entry : map.entrySet()) {
71       stream.writeObject(entry.getKey());
72       stream.writeObject(entry.getValue());
73     }
74   }
75 
76   /**
77    * Populates a map by reading an input stream, as part of deserialization.
78    * See {@link #writeMap} for the data format.
79    */
80   @GwtIncompatible("java.io.ObjectInputStream")
populateMap(Map<K, V> map, ObjectInputStream stream)81   public static <K, V> void populateMap(Map<K, V> map, ObjectInputStream stream)
82       throws IOException, ClassNotFoundException {
83     int size = stream.readInt();
84     populateMap(map, stream, size);
85   }
86 
87   /**
88    * Populates a map by reading an input stream, as part of deserialization.
89    * See {@link #writeMap} for the data format. The size is determined by a
90    * prior call to {@link #readCount}.
91    */
92   @GwtIncompatible("java.io.ObjectInputStream")
populateMap(Map<K, V> map, ObjectInputStream stream, int size)93   public static <K, V> void populateMap(Map<K, V> map, ObjectInputStream stream,
94       int size) throws IOException, ClassNotFoundException {
95     for (int i = 0; i < size; i++) {
96       @SuppressWarnings("unchecked") // reading data stored by writeMap
97       K key = (K) stream.readObject();
98       @SuppressWarnings("unchecked") // reading data stored by writeMap
99       V value = (V) stream.readObject();
100       map.put(key, value);
101     }
102   }
103 
104   /**
105    * Stores the contents of a multiset in an output stream, as part of
106    * serialization. It does not support concurrent multisets whose content may
107    * change while the method is running.
108    *
109    * <p>The serialized output consists of the number of distinct elements, the
110    * first element, its count, the second element, its count, and so on.
111    */
112   @GwtIncompatible("java.io.ObjectOutputStream")
writeMultiset( Multiset<E> multiset, ObjectOutputStream stream)113   public static <E> void writeMultiset(
114       Multiset<E> multiset, ObjectOutputStream stream) throws IOException {
115     int entryCount = multiset.entrySet().size();
116     stream.writeInt(entryCount);
117     for (Multiset.Entry<E> entry : multiset.entrySet()) {
118       stream.writeObject(entry.getElement());
119       stream.writeInt(entry.getCount());
120     }
121   }
122 
123   /**
124    * Populates a multiset by reading an input stream, as part of
125    * deserialization. See {@link #writeMultiset} for the data format.
126    */
127   @GwtIncompatible("java.io.ObjectInputStream")
populateMultiset( Multiset<E> multiset, ObjectInputStream stream)128   public static <E> void populateMultiset(
129       Multiset<E> multiset, ObjectInputStream stream)
130       throws IOException, ClassNotFoundException {
131     int distinctElements = stream.readInt();
132     populateMultiset(multiset, stream, distinctElements);
133   }
134 
135   /**
136    * Populates a multiset by reading an input stream, as part of
137    * deserialization. See {@link #writeMultiset} for the data format. The number
138    * of distinct elements is determined by a prior call to {@link #readCount}.
139    */
140   @GwtIncompatible("java.io.ObjectInputStream")
populateMultiset( Multiset<E> multiset, ObjectInputStream stream, int distinctElements)141   public static <E> void populateMultiset(
142       Multiset<E> multiset, ObjectInputStream stream, int distinctElements)
143       throws IOException, ClassNotFoundException {
144     for (int i = 0; i < distinctElements; i++) {
145       @SuppressWarnings("unchecked") // reading data stored by writeMultiset
146       E element = (E) stream.readObject();
147       int count = stream.readInt();
148       multiset.add(element, count);
149     }
150   }
151 
152   /**
153    * Stores the contents of a multimap in an output stream, as part of
154    * serialization. It does not support concurrent multimaps whose content may
155    * change while the method is running. The {@link Multimap#asMap} view
156    * determines the ordering in which data is written to the stream.
157    *
158    * <p>The serialized output consists of the number of distinct keys, and then
159    * for each distinct key: the key, the number of values for that key, and the
160    * key's values.
161    */
162   @GwtIncompatible("java.io.ObjectOutputStream")
writeMultimap( Multimap<K, V> multimap, ObjectOutputStream stream)163   public static <K, V> void writeMultimap(
164       Multimap<K, V> multimap, ObjectOutputStream stream) throws IOException {
165     stream.writeInt(multimap.asMap().size());
166     for (Map.Entry<K, Collection<V>> entry : multimap.asMap().entrySet()) {
167       stream.writeObject(entry.getKey());
168       stream.writeInt(entry.getValue().size());
169       for (V value : entry.getValue()) {
170         stream.writeObject(value);
171       }
172     }
173   }
174 
175   /**
176    * Populates a multimap by reading an input stream, as part of
177    * deserialization. See {@link #writeMultimap} for the data format.
178    */
179   @GwtIncompatible("java.io.ObjectInputStream")
populateMultimap( Multimap<K, V> multimap, ObjectInputStream stream)180   public static <K, V> void populateMultimap(
181       Multimap<K, V> multimap, ObjectInputStream stream)
182       throws IOException, ClassNotFoundException {
183     int distinctKeys = stream.readInt();
184     populateMultimap(multimap, stream, distinctKeys);
185   }
186 
187   /**
188    * Populates a multimap by reading an input stream, as part of
189    * deserialization. See {@link #writeMultimap} for the data format. The number
190    * of distinct keys is determined by a prior call to {@link #readCount}.
191    */
192   @GwtIncompatible("java.io.ObjectInputStream")
populateMultimap( Multimap<K, V> multimap, ObjectInputStream stream, int distinctKeys)193   public static <K, V> void populateMultimap(
194       Multimap<K, V> multimap, ObjectInputStream stream, int distinctKeys)
195       throws IOException, ClassNotFoundException {
196     for (int i = 0; i < distinctKeys; i++) {
197       @SuppressWarnings("unchecked") // reading data stored by writeMultimap
198       K key = (K) stream.readObject();
199       Collection<V> values = multimap.get(key);
200       int valueCount = stream.readInt();
201       for (int j = 0; j < valueCount; j++) {
202         @SuppressWarnings("unchecked") // reading data stored by writeMultimap
203         V value = (V) stream.readObject();
204         values.add(value);
205       }
206     }
207   }
208 
209   // Secret sauce for setting final fields; don't make it public.
210   @GwtIncompatible("java.lang.reflect.Field")
getFieldSetter( final Class<T> clazz, String fieldName)211   static <T> FieldSetter<T> getFieldSetter(
212       final Class<T> clazz, String fieldName) {
213     try {
214       Field field = clazz.getDeclaredField(fieldName);
215       return new FieldSetter<T>(field);
216     } catch (NoSuchFieldException e) {
217       throw new AssertionError(e); // programmer error
218     }
219   }
220 
221   // Secret sauce for setting final fields; don't make it public.
222   @GwtCompatible(emulated = true) // Accessible but not supported in GWT.
223   static final class FieldSetter<T> {
224     private final Field field;
225 
FieldSetter(Field field)226     private FieldSetter(Field field) {
227       this.field = field;
228       field.setAccessible(true);
229     }
230 
231     @GwtIncompatible("java.lang.reflect.Field")
set(T instance, Object value)232     void set(T instance, Object value) {
233       try {
234         field.set(instance, value);
235       } catch (IllegalAccessException impossible) {
236         throw new AssertionError(impossible);
237       }
238     }
239 
240     @GwtIncompatible("java.lang.reflect.Field")
set(T instance, int value)241     void set(T instance, int value) {
242       try {
243         field.set(instance, value);
244       } catch (IllegalAccessException impossible) {
245         throw new AssertionError(impossible);
246       }
247     }
248   }
249 }
250