diff src/main/java/org/msgpack/template/TemplateRegistry.java @ 0:cb825acd883a

first commit
author sugi
date Sat, 18 Oct 2014 15:06:15 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/msgpack/template/TemplateRegistry.java	Sat Oct 18 15:06:15 2014 +0900
@@ -0,0 +1,623 @@
+//
+// 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));
+        }
+    }
+}