Java tutorial
// Copyright 2015 Denis Itskovich // Refer to LICENSE.txt for license details package com.slimgears.slimsignal.apt; import com.annimon.stream.Stream; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.slimgears.slimapt.PropertyInfo; import com.slimgears.slimapt.TypeUtils; import com.slimgears.slimsignal.core.interfaces.fields.BlobField; import com.slimgears.slimsignal.core.interfaces.fields.ComparableField; import com.slimgears.slimsignal.core.interfaces.fields.RelationalField; import com.slimgears.slimsignal.core.interfaces.fields.StringField; import com.slimgears.slimsignal.core.interfaces.fields.ValueField; import com.slimgears.slimsignal.core.interfaces.fields.ValueGetter; import com.slimgears.slimsignal.core.interfaces.fields.ValueSetter; import com.slimgears.slimsignal.core.internal.AbstractEntityType; import com.slimgears.slimsignal.core.internal.Fields; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; /** * Created by ditskovi on 12/24/2015. */ @SuppressWarnings("StaticPseudoFunctionalStyleMethod") public class MetaFields { private static final Map<TypeName, AbstractMetaFieldBuilder> META_FIELD_BUILDER_MAP = new HashMap<>(); private final Map<TypeName, AbstractMetaFieldBuilder> metaFieldBuilderMap; public MetaFields(TypeElement type) { TypeName typeName = TypeName.get(type.asType()); if (type.getAnnotation(Entity.class) != null) { META_FIELD_BUILDER_MAP.put(typeName, RelationalEntityMetaFieldBuilder.INSTANCE); } else if (type.getAnnotation(GenerateEntity.class) != null) { META_FIELD_BUILDER_MAP.put(entityTypeFromAbstract(typeName), RelationalGeneratedEntityMetaFieldBuilder.INSTANCE); } metaFieldBuilderMap = new HashMap<>(META_FIELD_BUILDER_MAP); mapFieldTypesFromAnnotation(type, ComparableSemantics.class, TYPES_FROM_COMPARABLE_SEMANTICS, ComparableMetaFieldBuilder.INSTANCE); mapFieldTypesFromAnnotation(type, BlobSemantics.class, TYPES_FROM_BLOB_SEMANTICS, BlobMetaFieldBuilder.INSTANCE); mapFieldTypesFromAnnotation(type, ValueSemantics.class, TYPES_FROM_VALUE_SEMANTICS, ValueMetaFieldBuilder.INSTANCE); } public FieldSpec buildMetaField(TypeName entityType, PropertyInfo prop) { return getMetaFieldBuilder(prop).build(entityType, prop); } public static <P extends PropertyInfo> TypeSpec createMetaType(TypeName entityType, TypeName keyType, Iterable<P> props) { return TypeSpec.classBuilder("MetaType") .superclass(ParameterizedTypeName.get(ClassName.get(AbstractEntityType.class), keyType, entityType)) .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .addMethod(MethodSpec.constructorBuilder().addCode("super($T.class, ", entityType) .addCode(Joiner.on(", ") .join(Iterables.transform(props, prop -> getMetaFieldName(prop.getName())))) .addCode(");\n").build()) .addMethod(MethodSpec.methodBuilder("newInstance").addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC).returns(entityType).addCode("return new $T();\n", entityType) .build()) .build(); } public static String generatedEntityTypeName(TypeName superClass) { return TypeUtils.simpleName(superClass.toString()).replace("Abstract", ""); } public static ClassName generatedMetaEntityClassName(TypeName entityClass) { return ClassName.get(TypeUtils.packageName(entityClass.toString()), TypeUtils.simpleName(entityClass.toString()).concat("Meta")); } public static TypeName entityTypeFromAbstract(TypeName superClass) { String simpleName = generatedEntityTypeName(superClass); String packageName = TypeUtils.packageName(superClass.toString()); return ClassName.get(packageName, simpleName); } public static <P extends PropertyInfo> P getKeyField(TypeName entityTypeName, Iterable<? extends P> fields) { P field = Stream.of(fields).filter(f -> isKeyField(entityTypeName, f)).findFirst().orElseGet(null); if (field == null) { throw new RuntimeException(String.format( "Cannot find key field for entity type: %s. " + "Expected field with name %s or field annotated with @Key", entityTypeName, getEntityIdFieldName(entityTypeName))); } return field; } public static boolean isKeyField(TypeName entityTypeName, PropertyInfo field) { return field.getAnnotation(Key.class) != null || field.getName().equals("id") || field.getName().equals(getEntityIdFieldName(entityTypeName)); } private static String getEntityIdFieldName(TypeName entityTypeName) { return TypeUtils.toCamelCase(entityTypeName.toString().replace("Entity", ""), "Id"); } private static <P extends PropertyInfo> P findFieldByName(Iterable<? extends P> fields, String... names) { final Set<String> nameSet = new HashSet<>(Arrays.asList(names)); return Iterables.find(fields, field -> nameSet.contains(field.getName()), null); } private static <P extends PropertyInfo> P findAnnotatedField(Iterable<? extends P> fields, final Class annotationClass) { return (P) Iterables.find(fields, input -> input.getAnnotation(annotationClass) != null, null); } private static boolean isEnumField(PropertyInfo prop) { TypeElement type = prop.getTypeElement(); return type != null && type.getKind() == ElementKind.ENUM; } private static boolean isRelationalField(PropertyInfo prop, Class<? extends Annotation> annotationClass) { TypeElement type = prop.getTypeElement(); return (type != null) && (type.getAnnotation(annotationClass) != null); } public static String getMetaFieldName(String fieldName) { return TypeUtils.toCamelCase("", fieldName); } private AbstractMetaFieldBuilder getMetaFieldBuilder(PropertyInfo prop) { if (isRelationalField(prop, GenerateEntity.class)) return RelationalGeneratedEntityMetaFieldBuilder.INSTANCE; if (isRelationalField(prop, Entity.class)) return RelationalEntityMetaFieldBuilder.INSTANCE; if (isEnumField(prop)) return ComparableMetaFieldBuilder.INSTANCE; AbstractMetaFieldBuilder builder = metaFieldBuilderMap.get(prop.getType()); return builder != null ? builder : BlobMetaFieldBuilder.INSTANCE; } private <TAnnotation extends Annotation> void mapFieldTypesFromAnnotation(TypeElement typeElement, Class<TAnnotation> annotationType, TypeUtils.AnnotationTypesGetter<TAnnotation> getter, AbstractMetaFieldBuilder builder) { TAnnotation annotation = typeElement.getAnnotation(annotationType); if (annotation == null) return; for (TypeName type : TypeUtils.getTypesFromAnnotation(annotation, getter)) { metaFieldBuilderMap.put(type, builder); } } static { META_FIELD_BUILDER_MAP.put(TypeName.INT, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.SHORT, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.LONG, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.BYTE, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.DOUBLE, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.FLOAT, ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.BOOLEAN, ValueMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.get(Date.class), ComparableMetaFieldBuilder.INSTANCE); META_FIELD_BUILDER_MAP.put(TypeName.get(String.class), StringMetaFieldBuilder.INSTANCE); } private static TypeUtils.AnnotationTypesGetter<ComparableSemantics> TYPES_FROM_COMPARABLE_SEMANTICS = ComparableSemantics::value; private static TypeUtils.AnnotationTypesGetter<ValueSemantics> TYPES_FROM_VALUE_SEMANTICS = ValueSemantics::value; private static TypeUtils.AnnotationTypesGetter<BlobSemantics> TYPES_FROM_BLOB_SEMANTICS = BlobSemantics::value; static abstract class AbstractMetaFieldBuilder { public FieldSpec build(TypeName entityType, PropertyInfo prop) { TypeName type = metaFieldType(entityType, TypeUtils.box(prop.getType())); FieldSpec.Builder builder = FieldSpec.builder(type, getMetaFieldName(prop.getName()), Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL); return initialize(entityType, builder, prop).build(); } protected abstract TypeName metaFieldType(TypeName entityType, TypeName fieldType); protected abstract FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop); } static class ValueMetaFieldBuilder extends AbstractMetaFieldBuilder { public static final ValueMetaFieldBuilder INSTANCE = new ValueMetaFieldBuilder(); @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(ValueField.class), entityType, TypeUtils.box(fieldType)); } @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { TypeName fieldType = TypeUtils.box(prop.getType()); return builder.initializer("$T.valueField(" + "\n $S," + "\n $T.class," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), fieldType, ValueGetter.class, entityType, fieldType, fieldType, entityType, prop.getGetterName(), ValueSetter.class, entityType, fieldType, entityType, fieldType, prop.getSetterName(), prop.isNullable()); } } static class ComparableMetaFieldBuilder extends AbstractMetaFieldBuilder { public static final ComparableMetaFieldBuilder INSTANCE = new ComparableMetaFieldBuilder(); @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(ComparableField.class), entityType, TypeUtils.box(fieldType)); } @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { TypeName fieldType = TypeUtils.box(prop.getType()); return builder.initializer("$T.comparableField(" + "\n $S," + "\n $T.class," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), fieldType, ValueGetter.class, entityType, fieldType, fieldType, entityType, prop.getGetterName(), ValueSetter.class, entityType, fieldType, entityType, fieldType, prop.getSetterName(), prop.isNullable()); } } static class StringMetaFieldBuilder extends AbstractMetaFieldBuilder { public static final StringMetaFieldBuilder INSTANCE = new StringMetaFieldBuilder(); @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(StringField.class), entityType); } @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { return builder.initializer("$T.stringField(" + "\n $S," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), ValueGetter.class, entityType, String.class, String.class, entityType, prop.getGetterName(), ValueSetter.class, entityType, String.class, entityType, String.class, prop.getSetterName(), prop.isNullable() && !isKeyField(entityType, prop)); } } static class BlobMetaFieldBuilder extends AbstractMetaFieldBuilder { public static final BlobMetaFieldBuilder INSTANCE = new BlobMetaFieldBuilder(); @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(BlobField.class), entityType, fieldType); } @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { TypeName fieldType = TypeUtils.box(prop.getType()); return builder.initializer("$T.blobField(" + "\n $S," + "\n $T.class," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), prop.getType(), ValueGetter.class, entityType, fieldType, fieldType, entityType, prop.getGetterName(), ValueSetter.class, entityType, fieldType, entityType, fieldType, prop.getSetterName(), prop.isNullable()); } } static abstract class AbstractRelationalMetaFieldBuilder extends AbstractMetaFieldBuilder { @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(RelationalField.class), entityType, fieldType); } } static class RelationalGeneratedEntityMetaFieldBuilder extends AbstractRelationalMetaFieldBuilder { public static final RelationalGeneratedEntityMetaFieldBuilder INSTANCE = new RelationalGeneratedEntityMetaFieldBuilder(); @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { TypeName relatedEntityType = entityTypeFromAbstract(prop.getType()); return builder.initializer("$T.relationalField(" + "\n $S," + "\n $T.EntityMetaType," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), relatedEntityType, ValueGetter.class, entityType, relatedEntityType, relatedEntityType, entityType, prop.getGetterName(), ValueSetter.class, entityType, relatedEntityType, entityType, relatedEntityType, prop.getSetterName(), prop.isNullable()); } } static class RelationalEntityMetaFieldBuilder extends AbstractRelationalMetaFieldBuilder { public static final RelationalEntityMetaFieldBuilder INSTANCE = new RelationalEntityMetaFieldBuilder(); @Override protected TypeName metaFieldType(TypeName entityType, TypeName fieldType) { return ParameterizedTypeName.get(ClassName.get(RelationalField.class), entityType, fieldType); } @Override protected FieldSpec.Builder initialize(TypeName entityType, FieldSpec.Builder builder, PropertyInfo prop) { TypeName relatedEntityType = entityTypeFromAbstract(prop.getType()); ClassName metaEntityClassName = generatedMetaEntityClassName(relatedEntityType); return builder.initializer("$T.relationalField(" + "\n $S," + "\n $T.EntityMetaType," + "\n new $T<$T, $T>() { @Override public $T getValue($T entity) { return entity.$L(); } }," + "\n new $T<$T, $T>() { @Override public void setValue($T entity, $T value) { entity.$L(value); } }," + "\n $L)", Fields.class, prop.getName(), metaEntityClassName, ValueGetter.class, entityType, relatedEntityType, relatedEntityType, entityType, prop.getGetterName(), ValueSetter.class, entityType, relatedEntityType, entityType, relatedEntityType, prop.getSetterName(), prop.isNullable()); } } }