All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.internal.serialize.DefaultSerializerRegistry Maven / Gradle / Ivy

/*
 * Copyright 2014 the original author or 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 org.gradle.internal.serialize;

import java.util.*;

public class DefaultSerializerRegistry implements SerializerRegistry {
    private final Map, Serializer> serializerMap = new TreeMap, Serializer>(new Comparator>() {
        public int compare(Class o1, Class o2) {
            return o1.getName().compareTo(o2.getName());
        }
    });
    private final Set> javaSerialization = new HashSet>();

    @Override
    public  void register(Class implementationType, Serializer serializer) {
        serializerMap.put(implementationType, serializer);
    }

    @Override
    public  void useJavaSerialization(Class implementationType) {
        javaSerialization.add(implementationType);
    }

    @Override
    public  Serializer build(Class baseType) {
        Map, Serializer> matches = new LinkedHashMap, Serializer>();
        for (Map.Entry, Serializer> entry : serializerMap.entrySet()) {
            if (baseType.isAssignableFrom(entry.getKey())) {
                matches.put(entry.getKey(), entry.getValue());
            }
        }
        Set> matchingJavaSerialization = new LinkedHashSet>();
        for (Class candidate : javaSerialization) {
            if (baseType.isAssignableFrom(candidate)) {
                matchingJavaSerialization.add(candidate);
            }
        }
        if (matches.isEmpty() && matchingJavaSerialization.isEmpty()) {
            throw new IllegalArgumentException(String.format("Don't know how to serialize objects of type %s.", baseType.getName()));
        }
        if (matches.size() == 1 && matchingJavaSerialization.isEmpty()) {
            return (Serializer) matches.values().iterator().next();
        }
        return new TaggedTypeSerializer(matches, matchingJavaSerialization);
    }

    private static class TypeInfo {
        final int tag;
        final boolean useForSubtypes;
        final Serializer serializer;

        private TypeInfo(int tag, boolean useForSubtypes, Serializer serializer) {
            this.tag = tag;
            this.useForSubtypes = useForSubtypes;
            this.serializer = serializer;
        }
    }

    private static class TaggedTypeSerializer implements Serializer {
        private static final int JAVA_TYPE = 1; // Reserve 0 for null (to be added later)
        private static final TypeInfo JAVA_SERIALIZATION = new TypeInfo(JAVA_TYPE, true, new DefaultSerializer());
        private final Map, TypeInfo> serializersByType = new HashMap, TypeInfo>();
        private final Map, TypeInfo> typeHierarchies = new HashMap, TypeInfo>();
        private final TypeInfo[] serializersByTag;

        public TaggedTypeSerializer(Map, Serializer> serializerMap, Set> javaSerialization) {
            serializersByTag = new TypeInfo[2 + serializerMap.size()];
            serializersByTag[JAVA_TYPE] = JAVA_SERIALIZATION;
            int nextTag = 2;
            for (Map.Entry, Serializer> entry : serializerMap.entrySet()) {
                add(nextTag, entry.getKey(), entry.getValue());
                nextTag++;
            }
            for (Class type : javaSerialization) {
                serializersByType.put(type, JAVA_SERIALIZATION);
                typeHierarchies.put(type, JAVA_SERIALIZATION);
            }
        }

        private void add(int tag, Class type, Serializer serializer) {
            TypeInfo typeInfo = new TypeInfo(tag, type.equals(Throwable.class), serializer);
            serializersByType.put(type, typeInfo);
            serializersByTag[typeInfo.tag] = typeInfo;
            if (typeInfo.useForSubtypes) {
                typeHierarchies.put(type, typeInfo);
            }
        }

        public T read(Decoder decoder) throws Exception {
            int tag = decoder.readSmallInt();
            TypeInfo typeInfo = tag >= serializersByTag.length ? null : serializersByTag[tag];
            if (typeInfo == null) {
                throw new IllegalArgumentException(String.format("Unexpected type tag %d found.", tag));
            }
            return (T) typeInfo.serializer.read(decoder);
        }

        public void write(Encoder encoder, T value) throws Exception {
            TypeInfo typeInfo = map(value.getClass());
            encoder.writeSmallInt(typeInfo.tag);
            typeInfo.serializer.write(encoder, value);
        }

        private TypeInfo map(Class valueType) {
            TypeInfo typeInfo = serializersByType.get(valueType);
            if (typeInfo != null) {
                return typeInfo;
            }
            for (Map.Entry, TypeInfo> entry : typeHierarchies.entrySet()) {
                if (entry.getKey().isAssignableFrom(valueType)) {
                    return entry.getValue();
                }
            }
            throw new IllegalArgumentException(String.format("Don't know how to serialize an object of type %s.", valueType.getName()));
        }
    }
}