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