view src/main/java/org/msgpack/template/TemplateRegistry.java @ 7:e7e55c455e39 default tip

refactor
author sugi
date Fri, 09 Jan 2015 16:02:44 +0900
parents cb825acd883a
children
line wrap: on
line source

//
// MessagePack for Java
//
// Copyright (C) 2009 - 2013 FURUHASHI Sadayuki
//
//    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.msgpack.template;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;

import org.msgpack.MessagePackable;
import org.msgpack.MessageTypeException;
import org.msgpack.template.BigIntegerTemplate;
import org.msgpack.template.BooleanTemplate;
import org.msgpack.template.ByteArrayTemplate;
import org.msgpack.template.ByteTemplate;
import org.msgpack.template.DoubleArrayTemplate;
import org.msgpack.template.DoubleTemplate;
import org.msgpack.template.FieldList;
import org.msgpack.template.FloatArrayTemplate;
import org.msgpack.template.FloatTemplate;
import org.msgpack.template.GenericTemplate;
import org.msgpack.template.IntegerArrayTemplate;
import org.msgpack.template.IntegerTemplate;
import org.msgpack.template.LongArrayTemplate;
import org.msgpack.template.LongTemplate;
import org.msgpack.template.ShortArrayTemplate;
import org.msgpack.template.ShortTemplate;
import org.msgpack.template.StringTemplate;
import org.msgpack.template.Template;
import org.msgpack.template.ValueTemplate;
import org.msgpack.template.builder.ArrayTemplateBuilder;
import org.msgpack.template.builder.TemplateBuilder;
import org.msgpack.template.builder.TemplateBuilderChain;
import org.msgpack.type.Value;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class TemplateRegistry {

    private TemplateRegistry parent = null;

    private TemplateBuilderChain chain;

    Map<Type, Template<Type>> cache;

    private Map<Type, GenericTemplate> genericCache;

    /**
     * create <code>TemplateRegistry</code> object of root.
     */
    private TemplateRegistry() {
        parent = null;
        chain = createTemplateBuilderChain();
        genericCache = new HashMap<Type, GenericTemplate>();
        cache = new HashMap<Type, Template<Type>>();
        registerTemplates();
        cache = Collections.unmodifiableMap(cache);
    }

    /**
     *
     * @param registry
     */
    public TemplateRegistry(TemplateRegistry registry) {
        if (registry != null) {
            parent = registry;
        } else {
            parent = new TemplateRegistry();
        }
        chain = createTemplateBuilderChain();
        cache = new HashMap<Type, Template<Type>>();
        genericCache = new HashMap<Type, GenericTemplate>();
        registerTemplatesWhichRefersRegistry();
    }

    protected TemplateBuilderChain createTemplateBuilderChain() {
        return new TemplateBuilderChain(this);
    }

    public void setClassLoader(final ClassLoader cl) {
        chain = new TemplateBuilderChain(this, cl);
    }

    private void registerTemplates() {
        register(boolean.class, BooleanTemplate.getInstance());
        register(Boolean.class, BooleanTemplate.getInstance());
        register(byte.class, ByteTemplate.getInstance());
        register(Byte.class, ByteTemplate.getInstance());
        register(short.class, ShortTemplate.getInstance());
        register(Short.class, ShortTemplate.getInstance());
        register(int.class, IntegerTemplate.getInstance());
        register(Integer.class, IntegerTemplate.getInstance());
        register(long.class, LongTemplate.getInstance());
        register(Long.class, LongTemplate.getInstance());
        register(float.class, FloatTemplate.getInstance());
        register(Float.class, FloatTemplate.getInstance());
        register(double.class, DoubleTemplate.getInstance());
        register(Double.class, DoubleTemplate.getInstance());
        register(BigInteger.class, BigIntegerTemplate.getInstance());
        register(char.class, CharacterTemplate.getInstance());
        register(Character.class, CharacterTemplate.getInstance());
        register(boolean[].class, BooleanArrayTemplate.getInstance());
        register(short[].class, ShortArrayTemplate.getInstance());
        register(int[].class, IntegerArrayTemplate.getInstance());
        register(long[].class, LongArrayTemplate.getInstance());
        register(float[].class, FloatArrayTemplate.getInstance());
        register(double[].class, DoubleArrayTemplate.getInstance());
        register(String.class, StringTemplate.getInstance());
        register(byte[].class, ByteArrayTemplate.getInstance());
        register(ByteBuffer.class, ByteBufferTemplate.getInstance());
        register(Value.class, ValueTemplate.getInstance());
        register(BigDecimal.class, BigDecimalTemplate.getInstance());
        register(Date.class, DateTemplate.getInstance());

        registerTemplatesWhichRefersRegistry();

    }

    protected void registerTemplatesWhichRefersRegistry() {
        AnyTemplate anyTemplate = new AnyTemplate(this);

        register(List.class, new ListTemplate(anyTemplate));
        register(Set.class, new SetTemplate(anyTemplate));
        register(Collection.class, new CollectionTemplate(anyTemplate));
        register(Map.class, new MapTemplate(anyTemplate, anyTemplate));
        registerGeneric(List.class, new GenericCollectionTemplate(this, ListTemplate.class));
        registerGeneric(Set.class, new GenericCollectionTemplate(this, SetTemplate.class));
        registerGeneric(Collection.class, new GenericCollectionTemplate(this, CollectionTemplate.class));
        registerGeneric(Map.class, new GenericMapTemplate(this, MapTemplate.class));
    }

    public void register(final Class<?> targetClass) {
        buildAndRegister(null, targetClass, false, null);
    }

    public void register(final Class<?> targetClass, final FieldList flist) {
        if (flist == null) {
            throw new NullPointerException("FieldList object is null");
        }

        buildAndRegister(null, targetClass, false, flist);
    }

    public synchronized void register(final Type targetType, final Template tmpl) {
        if (tmpl == null) {
            throw new NullPointerException("Template object is null");
        }

        if (targetType instanceof ParameterizedType) {
            cache.put(((ParameterizedType) targetType).getRawType(), tmpl);
        } else {
            cache.put(targetType, tmpl);
        }
    }

    public synchronized void registerGeneric(final Type targetType, final GenericTemplate tmpl) {
        if (targetType instanceof ParameterizedType) {
            genericCache.put(((ParameterizedType) targetType).getRawType(),
                    tmpl);
        } else {
            genericCache.put(targetType, tmpl);
        }
    }

    public synchronized boolean unregister(final Type targetType) {
        Template<Type> tmpl = cache.remove(targetType);
        return tmpl != null;
    }

    public synchronized void unregister() {
        cache.clear();
    }

    public synchronized Template lookup(Type targetType) {
        Template tmpl;

        if (targetType instanceof ParameterizedType) {
            // ParameterizedType is not a Class<?>
            ParameterizedType paramedType = (ParameterizedType) targetType;
            tmpl = lookupGenericType(paramedType);
            if (tmpl != null) {
                return tmpl;
            }
            targetType = paramedType.getRawType();
        }

        tmpl = lookupGenericArrayType(targetType);
        if (tmpl != null) {
            return tmpl;
        }

        tmpl = lookupCache(targetType);
        if (tmpl != null) {
            return tmpl;
        }

        if (targetType instanceof WildcardType ||
                targetType instanceof TypeVariable) {
            // WildcardType is not a Class<?>
            tmpl = new AnyTemplate<Object>(this);
            register(targetType, tmpl);
            return tmpl;
        }

        Class<?> targetClass = (Class<?>) targetType;

        // MessagePackable interface is implemented
        if (MessagePackable.class.isAssignableFrom(targetClass)) {
            // FIXME #MN
            // following processing should be merged into lookAfterBuilding
            // or lookupInterfaceTypes method in next version
            tmpl = new MessagePackableTemplate(targetClass);
            register(targetClass, tmpl);
            return tmpl;
        }

        if (targetClass.isInterface()) {
            // writing interfaces will succeed
            // reading into interfaces will fail
            tmpl = new AnyTemplate<Object>(this);
            register(targetType, tmpl);
            return tmpl;
        }

        // find matched template builder and build template
        tmpl = lookupAfterBuilding(targetClass);
        if (tmpl != null) {
            return tmpl;
        }

        // lookup template of interface type
        tmpl = lookupInterfaceTypes(targetClass);
        if (tmpl != null) {
            return tmpl;
        }

        // lookup template of superclass type
        tmpl = lookupSuperclasses(targetClass);
        if (tmpl != null) {
            return tmpl;
        }

        // lookup template of interface type of superclasss
        tmpl = lookupSuperclassInterfaceTypes(targetClass);
        if (tmpl != null) {
            return tmpl;
        }

        throw new MessageTypeException(
                "Cannot find template for " + targetClass + " class.  " +
                "Try to add @Message annotation to the class or call MessagePack.register(Type).");
    }

    private Template<Type> lookupGenericType(ParameterizedType paramedType) {
        Template<Type> tmpl = lookupGenericTypeImpl(paramedType);
        if (tmpl != null) {
            return tmpl;
        }

        try {
            tmpl = parent.lookupGenericTypeImpl(paramedType);
            if (tmpl != null) {
                return tmpl;
            }
        } catch (NullPointerException e) { // ignore
        }

        tmpl = lookupGenericInterfaceTypes(paramedType);
        if (tmpl != null) {
            return tmpl;
        }

        tmpl = lookupGenericSuperclasses(paramedType);
        if (tmpl != null) {
            return tmpl;
        }

        return null;
    }

    private Template lookupGenericTypeImpl(ParameterizedType targetType) {
        Type rawType = targetType.getRawType();
        return lookupGenericTypeImpl0(targetType, rawType);
    }

    private Template lookupGenericTypeImpl0(ParameterizedType targetType, Type rawType) {
        GenericTemplate gtmpl = genericCache.get(rawType);
        if (gtmpl == null) {
            return null;
        }

        Type[] types = targetType.getActualTypeArguments();
        Template[] tmpls = new Template[types.length];
        for (int i = 0; i < types.length; ++i) {
            tmpls[i] = lookup(types[i]);
        }

        return gtmpl.build(tmpls);
    }

    private <T> Template<T> lookupGenericInterfaceTypes(ParameterizedType targetType) {
        Type rawType = targetType.getRawType();
        Template<T> tmpl = null;

        try {
            Class<?>[] infTypes = ((Class) rawType).getInterfaces();
            for (Class<?> infType : infTypes) {
                tmpl = lookupGenericTypeImpl0(targetType, infType);
                if (tmpl != null) {
                    return tmpl;
                }
            }
        } catch (ClassCastException e) { // ignore
        }

        return tmpl;
    }

    private <T> Template<T> lookupGenericSuperclasses(ParameterizedType targetType) {
        Type rawType = targetType.getRawType();
        Template<T> tmpl = null;

        try {
            Class<?> superClass = ((Class) rawType).getSuperclass();
            if (superClass == null) {
                return null;
            }

            for (; superClass != Object.class; superClass = superClass.getSuperclass()) {
                tmpl = lookupGenericTypeImpl0(targetType, superClass);
                if (tmpl != null) {
                    register(targetType, tmpl);
                    return tmpl;
                }
            }
        } catch (ClassCastException e) { // ignore
        }

        return tmpl;
    }

    private Template<Type> lookupGenericArrayType(Type targetType) {
        // TODO GenericArrayType is not a Class<?> => buildArrayTemplate
        if (! (targetType instanceof GenericArrayType)) {
            return null;
        }

        GenericArrayType genericArrayType = (GenericArrayType) targetType;
        Template<Type> tmpl = lookupGenericArrayTypeImpl(genericArrayType);
        if (tmpl != null) {
            return tmpl;
        }

        try {
            tmpl = parent.lookupGenericArrayTypeImpl(genericArrayType);
            if (tmpl != null) {
                return tmpl;
            }
        } catch (NullPointerException e) { // ignore
        }

        return null;
    }

    private Template lookupGenericArrayTypeImpl(GenericArrayType genericArrayType) {
        String genericArrayTypeName = "" + genericArrayType;
        int dim = genericArrayTypeName.split("\\[").length - 1;
        if (dim <= 0) {
            throw new MessageTypeException(
                    String.format("fatal error: type=", genericArrayTypeName));
        } else if (dim > 1) {
            throw new UnsupportedOperationException(String.format(
                    "Not implemented template generation of %s", genericArrayTypeName));
        }

        String genericCompTypeName = "" + genericArrayType.getGenericComponentType();
        boolean isPrimitiveType = isPrimitiveType(genericCompTypeName);
        StringBuffer sbuf = new StringBuffer();
        for (int i = 0; i < dim; i++) {
            sbuf.append('[');
        }
        if (!isPrimitiveType) {
            sbuf.append('L');
            sbuf.append(toJvmReferenceTypeName(genericCompTypeName));
            sbuf.append(';');
        } else {
            sbuf.append(toJvmPrimitiveTypeName(genericCompTypeName));
        }

        String jvmArrayClassName = sbuf.toString();
        Class jvmArrayClass = null;
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
            if (cl != null) {
                jvmArrayClass = cl.loadClass(jvmArrayClassName);
                if (jvmArrayClass != null) {
                    return lookupAfterBuilding(jvmArrayClass);
                }
            }
        } catch (ClassNotFoundException e) {} // ignore

        try {
            cl = getClass().getClassLoader();
            if (cl != null) {
                jvmArrayClass = cl.loadClass(jvmArrayClassName);
                if (jvmArrayClass != null) {
                    return lookupAfterBuilding(jvmArrayClass);
                }
            }
        } catch (ClassNotFoundException e) {} // ignore

        try {
            jvmArrayClass = Class.forName(jvmArrayClassName);
            if (jvmArrayClass != null) {
                return lookupAfterBuilding(jvmArrayClass);
            }
        } catch (ClassNotFoundException e) {} // ignore

        throw new MessageTypeException(String.format(
                "cannot find template of %s", jvmArrayClassName));
    }

    private Template<Type> lookupCache(Type targetType) {
        Template<Type> tmpl = cache.get(targetType);
        if (tmpl != null) {
            return tmpl;
        }

        try {
            tmpl = parent.lookupCache(targetType);
        } catch (NullPointerException e) { // ignore
        }
        return tmpl;
    }

    private <T> Template<T> lookupAfterBuilding(Class<T> targetClass) {
        TemplateBuilder builder = chain.select(targetClass, true);
        Template<T> tmpl = null;
        if (builder != null) {
            // TODO #MN for Android, we should modify here
            tmpl = chain.getForceBuilder().loadTemplate(targetClass);
            if (tmpl != null) {
                register(targetClass, tmpl);
                return tmpl;
            }
            tmpl = buildAndRegister(builder, targetClass, true, null);
        }
        return tmpl;
    }

    private <T> Template<T> lookupInterfaceTypes(Class<T> targetClass) {
        Class<?>[] infTypes = targetClass.getInterfaces();
        Template<T> tmpl = null;
        for (Class<?> infType : infTypes) {
            tmpl = (Template<T>) cache.get(infType);
            if (tmpl != null) {
                register(targetClass, tmpl);
                return tmpl;
            } else {
                try {
                    tmpl = (Template<T>) parent.lookupCache(infType);
                    if (tmpl != null) {
                        register(targetClass, tmpl);
                        return tmpl;
                    }
                } catch (NullPointerException e) { // ignore
                }
            }
        }
        return tmpl;
    }

    private <T> Template<T> lookupSuperclasses(Class<T> targetClass) {
        Class<?> superClass = targetClass.getSuperclass();
        Template<T> tmpl = null;
        if (superClass != null) {
            for (; superClass != Object.class; superClass = superClass
                    .getSuperclass()) {
                tmpl = (Template<T>) cache.get(superClass);
                if (tmpl != null) {
                    register(targetClass, tmpl);
                    return tmpl;
                } else {
                    try {
                        tmpl = (Template<T>) parent.lookupCache(superClass);
                        if (tmpl != null) {
                            register(targetClass, tmpl);
                            return tmpl;
                        }
                    } catch (NullPointerException e) { // ignore
                    }
                }
            }
        }
        return tmpl;
    }

    private <T> Template<T> lookupSuperclassInterfaceTypes(Class<T> targetClass) {
        Class<?> superClass = targetClass.getSuperclass();
        Template<T> tmpl = null;
        if (superClass != null) {
            for (; superClass != Object.class; superClass = superClass.getSuperclass()) {
                tmpl = (Template<T>) lookupInterfaceTypes(superClass);
                if (tmpl != null) {
                    register(targetClass, tmpl);
                    return tmpl;
                } else {
                    try {
                        tmpl = (Template<T>) parent.lookupCache(superClass);
                        if (tmpl != null) {
                            register(targetClass, tmpl);
                            return tmpl;
                        }
                    } catch (NullPointerException e) { // ignore
                    }
                }
            }
        }
        return tmpl;
    }

    private synchronized Template buildAndRegister(TemplateBuilder builder,
            final Class targetClass, final boolean hasAnnotation,
            final FieldList flist) {
        Template newTmpl = null;
        Template oldTmpl = null;
        try {
            if (cache.containsKey(targetClass)) {
                oldTmpl = cache.get(targetClass);
            }
            newTmpl = new TemplateReference(this, targetClass);
            cache.put(targetClass, newTmpl);
            if (builder == null) {
                builder = chain.select(targetClass, hasAnnotation);
            }
            newTmpl = flist != null ?
                    builder.buildTemplate(targetClass, flist) : builder.buildTemplate(targetClass);
            return newTmpl;
        } catch (Exception e) {
            if (oldTmpl != null) {
                cache.put(targetClass, oldTmpl);
            } else {
                cache.remove(targetClass);
            }
            newTmpl = null;
            if (e instanceof MessageTypeException) {
                throw (MessageTypeException) e;
            } else {
                throw new MessageTypeException(e);
            }
        } finally {
            if (newTmpl != null) {
                cache.put(targetClass, newTmpl);
            }
        }
    }

    private static boolean isPrimitiveType(String genericCompTypeName) {
        return (genericCompTypeName.equals("byte")
                || genericCompTypeName.equals("short")
                || genericCompTypeName.equals("int")
                || genericCompTypeName.equals("long")
                || genericCompTypeName.equals("float")
                || genericCompTypeName.equals("double")
                || genericCompTypeName.equals("boolean")
                || genericCompTypeName.equals("char"));
    }

    private static String toJvmReferenceTypeName(String typeName) {
        // delete "class " from class name
        // e.g. "class Foo" to "Foo" by this method
        return typeName.substring(6);
    }

    private static String toJvmPrimitiveTypeName(String typeName) {
        if (typeName.equals("byte")) {
            return "B";
        } else if (typeName.equals("short")) {
            return "S";
        } else if (typeName.equals("int")) {
            return "I";
        } else if (typeName.equals("long")) {
            return "J";
        } else if (typeName.equals("float")) {
            return "F";
        } else if (typeName.equals("double")) {
            return "D";
        } else if (typeName.equals("boolean")) {
            return "Z";
        } else if (typeName.equals("char")) {
            return "C";
        } else {
            throw new MessageTypeException(String.format(
                    "fatal error: type=%s", typeName));
        }
    }
}