diff options
Diffstat (limited to 'guava/src/com/google/common/collect/Serialization.java')
-rw-r--r-- | guava/src/com/google/common/collect/Serialization.java | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/guava/src/com/google/common/collect/Serialization.java b/guava/src/com/google/common/collect/Serialization.java new file mode 100644 index 0000000..2541548 --- /dev/null +++ b/guava/src/com/google/common/collect/Serialization.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; + +/** + * Provides static methods for serializing collection classes. + * + * <p>This class assists the implementation of collection classes. Do not use + * this class to serialize collections that are defined elsewhere. + * + * @author Jared Levy + */ +final class Serialization { + private Serialization() {} + + /** + * Reads a count corresponding to a serialized map, multiset, or multimap. It + * returns the size of a map serialized by {@link + * #writeMap(Map, ObjectOutputStream)}, the number of distinct elements in a + * multiset serialized by {@link + * #writeMultiset(Multiset, ObjectOutputStream)}, or the number of distinct + * keys in a multimap serialized by {@link + * #writeMultimap(Multimap, ObjectOutputStream)}. + * + * <p>The returned count may be used to construct an empty collection of the + * appropriate capacity before calling any of the {@code populate} methods. + */ + static int readCount(ObjectInputStream stream) throws IOException { + return stream.readInt(); + } + + /** + * Stores the contents of a map in an output stream, as part of serialization. + * It does not support concurrent maps whose content may change while the + * method is running. + * + * <p>The serialized output consists of the number of entries, first key, + * first value, second key, second value, and so on. + */ + static <K, V> void writeMap(Map<K, V> map, ObjectOutputStream stream) + throws IOException { + stream.writeInt(map.size()); + for (Map.Entry<K, V> entry : map.entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + /** + * Populates a map by reading an input stream, as part of deserialization. + * See {@link #writeMap} for the data format. + */ + static <K, V> void populateMap(Map<K, V> map, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int size = stream.readInt(); + populateMap(map, stream, size); + } + + /** + * Populates a map by reading an input stream, as part of deserialization. + * See {@link #writeMap} for the data format. The size is determined by a + * prior call to {@link #readCount}. + */ + static <K, V> void populateMap(Map<K, V> map, ObjectInputStream stream, + int size) throws IOException, ClassNotFoundException { + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMap + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") // reading data stored by writeMap + V value = (V) stream.readObject(); + map.put(key, value); + } + } + + /** + * Stores the contents of a multiset in an output stream, as part of + * serialization. It does not support concurrent multisets whose content may + * change while the method is running. + * + * <p>The serialized output consists of the number of distinct elements, the + * first element, its count, the second element, its count, and so on. + */ + static <E> void writeMultiset( + Multiset<E> multiset, ObjectOutputStream stream) throws IOException { + int entryCount = multiset.entrySet().size(); + stream.writeInt(entryCount); + for (Multiset.Entry<E> entry : multiset.entrySet()) { + stream.writeObject(entry.getElement()); + stream.writeInt(entry.getCount()); + } + } + + /** + * Populates a multiset by reading an input stream, as part of + * deserialization. See {@link #writeMultiset} for the data format. + */ + static <E> void populateMultiset( + Multiset<E> multiset, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctElements = stream.readInt(); + populateMultiset(multiset, stream, distinctElements); + } + + /** + * Populates a multiset by reading an input stream, as part of + * deserialization. See {@link #writeMultiset} for the data format. The number + * of distinct elements is determined by a prior call to {@link #readCount}. + */ + static <E> void populateMultiset( + Multiset<E> multiset, ObjectInputStream stream, int distinctElements) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctElements; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultiset + E element = (E) stream.readObject(); + int count = stream.readInt(); + multiset.add(element, count); + } + } + + /** + * Stores the contents of a multimap in an output stream, as part of + * serialization. It does not support concurrent multimaps whose content may + * change while the method is running. The {@link Multimap#asMap} view + * determines the ordering in which data is written to the stream. + * + * <p>The serialized output consists of the number of distinct keys, and then + * for each distinct key: the key, the number of values for that key, and the + * key's values. + */ + static <K, V> void writeMultimap( + Multimap<K, V> multimap, ObjectOutputStream stream) throws IOException { + stream.writeInt(multimap.asMap().size()); + for (Map.Entry<K, Collection<V>> entry : multimap.asMap().entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeInt(entry.getValue().size()); + for (V value : entry.getValue()) { + stream.writeObject(value); + } + } + } + + /** + * Populates a multimap by reading an input stream, as part of + * deserialization. See {@link #writeMultimap} for the data format. + */ + static <K, V> void populateMultimap( + Multimap<K, V> multimap, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctKeys = stream.readInt(); + populateMultimap(multimap, stream, distinctKeys); + } + + /** + * Populates a multimap by reading an input stream, as part of + * deserialization. See {@link #writeMultimap} for the data format. The number + * of distinct keys is determined by a prior call to {@link #readCount}. + */ + static <K, V> void populateMultimap( + Multimap<K, V> multimap, ObjectInputStream stream, int distinctKeys) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + K key = (K) stream.readObject(); + Collection<V> values = multimap.get(key); + int valueCount = stream.readInt(); + for (int j = 0; j < valueCount; j++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + V value = (V) stream.readObject(); + values.add(value); + } + } + } + + // Secret sauce for setting final fields; don't make it public. + static <T> FieldSetter<T> getFieldSetter( + final Class<T> clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + return new FieldSetter<T>(field); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); // programmer error + } + } + + // Secret sauce for setting final fields; don't make it public. + static final class FieldSetter<T> { + private final Field field; + + private FieldSetter(Field field) { + this.field = field; + field.setAccessible(true); + } + + void set(T instance, Object value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + + void set(T instance, int value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + } +} |