Mercurial > hg > Members > sugi > MessagePack-java
diff src/main/java/org/msgpack/template/builder/AbstractTemplateBuilder.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/builder/AbstractTemplateBuilder.java Sat Oct 18 15:06:15 2014 +0900 @@ -0,0 +1,293 @@ +// +// 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.builder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.msgpack.annotation.Beans; +import org.msgpack.annotation.Ignore; +import org.msgpack.annotation.Index; +import org.msgpack.annotation.Message; +import org.msgpack.annotation.MessagePackBeans; +import org.msgpack.annotation.MessagePackMessage; +import org.msgpack.annotation.MessagePackOrdinalEnum; +import org.msgpack.annotation.NotNullable; +import org.msgpack.annotation.Optional; +import org.msgpack.annotation.OrdinalEnum; +import org.msgpack.template.FieldList; +import org.msgpack.template.FieldOption; +import org.msgpack.template.Template; +import org.msgpack.template.TemplateRegistry; +import org.msgpack.template.builder.TemplateBuildException; + +public abstract class AbstractTemplateBuilder implements TemplateBuilder { + + protected TemplateRegistry registry; + + protected AbstractTemplateBuilder(TemplateRegistry registry) { + this.registry = registry; + } + + @Override + public <T> Template<T> buildTemplate(final Type targetType) + throws TemplateBuildException { + @SuppressWarnings("unchecked") + Class<T> targetClass = (Class<T>) targetType; + checkClassValidation(targetClass); + FieldOption fieldOption = getFieldOption(targetClass); + FieldEntry[] entries = toFieldEntries(targetClass, fieldOption); + return buildTemplate(targetClass, entries); + } + + @Override + public <T> Template<T> buildTemplate(final Class<T> targetClass, final FieldList fieldList) + throws TemplateBuildException { + checkClassValidation(targetClass); + FieldEntry[] entries = toFieldEntries(targetClass, fieldList); + return buildTemplate(targetClass, entries); + } + + protected abstract <T> Template<T> buildTemplate(Class<T> targetClass, FieldEntry[] entries); + + protected void checkClassValidation(final Class<?> targetClass) { + if (targetClass.isInterface()) { + throw new TemplateBuildException( + "Cannot build template for interface: " + targetClass.getName()); + } + if (Modifier.isAbstract(targetClass.getModifiers())) { + throw new TemplateBuildException( + "Cannot build template for abstract class: " + targetClass.getName()); + } + if (targetClass.isArray()) { + throw new TemplateBuildException( + "Cannot build template for array class: " + targetClass.getName()); + } + if (targetClass.isPrimitive()) { + throw new TemplateBuildException( + "Cannot build template of primitive type: " + targetClass.getName()); + } + } + + protected FieldOption getFieldOption(Class<?> targetClass) { + Message m = targetClass.getAnnotation(Message.class); + if (m == null) { + return FieldOption.DEFAULT; + } + MessagePackMessage mpm = targetClass + .getAnnotation(MessagePackMessage.class); + if (mpm == null) { + return FieldOption.DEFAULT; + } + // TODO #MN + return m.value(); + } + + private FieldEntry[] toFieldEntries(final Class<?> targetClass, final FieldList flist) { + List<FieldList.Entry> src = flist.getList(); + FieldEntry[] entries = new FieldEntry[src.size()]; + for (int i = 0; i < src.size(); i++) { + FieldList.Entry s = src.get(i); + if (s.isAvailable()) { + try { + entries[i] = new DefaultFieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption()); + } catch (SecurityException e) { + throw new TemplateBuildException(e); + } catch (NoSuchFieldException e) { + throw new TemplateBuildException(e); + } + } else { + entries[i] = new DefaultFieldEntry(); + } + } + return entries; + } + + protected FieldEntry[] toFieldEntries(final Class<?> targetClass, final FieldOption from) { + Field[] fields = getFields(targetClass); + + /* + * index: + * + * @Index(0) + * int field_a; // 0 + * int field_b; // 1 + * @Index(3) + * int field_c; // 3 + * int field_d; // 4 + * @Index(2) + * int field_e; // 2 + * int field_f; // 5 + */ + List<FieldEntry> indexed = new ArrayList<FieldEntry>(); + int maxIndex = -1; + for (Field f : fields) { + FieldOption opt = getFieldOption(f, from); + if (opt == FieldOption.IGNORE) { + // skip + continue; + } + + int index = getFieldIndex(f, maxIndex); + if (indexed.size() > index && indexed.get(index) != null) { + throw new TemplateBuildException("duplicated index: " + index); + } + if (index < 0) { + throw new TemplateBuildException("invalid index: " + index); + } + + while (indexed.size() <= index) { + indexed.add(null); + } + indexed.set(index, new DefaultFieldEntry(f, opt)); + + if (maxIndex < index) { + maxIndex = index; + } + } + + FieldEntry[] entries = new FieldEntry[maxIndex + 1]; + for (int i = 0; i < indexed.size(); i++) { + FieldEntry e = indexed.get(i); + if (e == null) { + entries[i] = new DefaultFieldEntry(); + } else { + entries[i] = e; + } + } + return entries; + } + + private Field[] getFields(Class<?> targetClass) { + // order: [fields of super class, ..., fields of this class] + List<Field[]> succ = new ArrayList<Field[]>(); + int total = 0; + for (Class<?> c = targetClass; c != Object.class; c = c.getSuperclass()) { + Field[] fields = c.getDeclaredFields(); + total += fields.length; + succ.add(fields); + } + Field[] result = new Field[total]; + int off = 0; + for (int i = succ.size() - 1; i >= 0; i--) { + Field[] fields = succ.get(i); + System.arraycopy(fields, 0, result, off, fields.length); + off += fields.length; + } + return result; + } + + private FieldOption getFieldOption(Field field, FieldOption from) { + int mod = field.getModifiers(); + // default mode: + // transient, static, final : Ignore + // primitive type : NotNullable + // reference type : Ignore + if (Modifier.isStatic(mod) || Modifier.isFinal(mod) + || Modifier.isTransient(mod)) { + return FieldOption.IGNORE; + } + + if (isAnnotated(field, Ignore.class)) { + return FieldOption.IGNORE; + } else if (isAnnotated(field, Optional.class)) { + return FieldOption.OPTIONAL; + } else if (isAnnotated(field, NotNullable.class)) { + return FieldOption.NOTNULLABLE; + } + + if (from != FieldOption.DEFAULT) { + return from; + } + + if (field.getType().isPrimitive()) { + return FieldOption.NOTNULLABLE; + } else { + return FieldOption.OPTIONAL; + } + } + + private int getFieldIndex(final Field field, int maxIndex) { + Index a = field.getAnnotation(Index.class); + if (a == null) { + return maxIndex + 1; + } else { + return a.value(); + } + } + + @Override + public void writeTemplate(Type targetType, String directoryName) { + throw new UnsupportedOperationException(targetType.toString()); + } + + @Override + public <T> Template<T> loadTemplate(Type targetType) { + return null; + } + + public static boolean isAnnotated(Class<?> targetClass, + Class<? extends Annotation> with) { + return targetClass.getAnnotation(with) != null; + } + + public static boolean isAnnotated(AccessibleObject accessibleObject, Class<? extends Annotation> with) { + return accessibleObject.getAnnotation(with) != null; + } + + public static boolean matchAtClassTemplateBuilder(Class<?> targetClass, boolean hasAnnotation) { + if (hasAnnotation) { + return AbstractTemplateBuilder.isAnnotated(targetClass, Message.class) + || AbstractTemplateBuilder.isAnnotated(targetClass, MessagePackMessage.class); + } else { + return !targetClass.isEnum() && !targetClass.isInterface(); + } + } + + public static boolean matchAtBeansClassTemplateBuilder(Type targetType, boolean hasAnnotation) { + Class<?> targetClass = (Class<?>) targetType; + if (hasAnnotation) { + return AbstractTemplateBuilder.isAnnotated((Class<?>) targetType, Beans.class) + || AbstractTemplateBuilder.isAnnotated((Class<?>) targetType, MessagePackBeans.class); + } else { + return !targetClass.isEnum() || !targetClass.isInterface(); + } + } + + public static boolean matchAtArrayTemplateBuilder(Class<?> targetClass, boolean hasAnnotation) { + if (((Type) targetClass) instanceof GenericArrayType) { + return true; + } + return targetClass.isArray(); + } + + public static boolean matchAtOrdinalEnumTemplateBuilder(Class<?> targetClass, boolean hasAnnotation) { + if (hasAnnotation) { + return AbstractTemplateBuilder.isAnnotated(targetClass, OrdinalEnum.class) + || AbstractTemplateBuilder.isAnnotated(targetClass, MessagePackOrdinalEnum.class); + } else { + return targetClass.isEnum(); + } + } +}