Mercurial > hg > Members > sugi > MessagePack-java
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)); + } + } +}