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