info.archinnov.achilles.internals.codegen.dsl.select.SelectDSLCodeGen.java Source code

Java tutorial

Introduction

Here is the source code for info.archinnov.achilles.internals.codegen.dsl.select.SelectDSLCodeGen.java

Source

/*
 * Copyright (C) 2012-2016 DuyHai DOAN
 *
 * 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 info.archinnov.achilles.internals.codegen.dsl.select;

import static info.archinnov.achilles.internals.codegen.dsl.AbstractDSLCodeGen.ReturnType.NEW;
import static info.archinnov.achilles.internals.codegen.dsl.AbstractDSLCodeGen.ReturnType.THIS;
import static info.archinnov.achilles.internals.parser.TypeUtils.*;

import java.util.StringJoiner;
import javax.lang.model.element.Modifier;

import org.apache.commons.lang3.ArrayUtils;

import com.squareup.javapoet.*;

import info.archinnov.achilles.internals.codegen.dsl.AbstractDSLCodeGen;
import info.archinnov.achilles.internals.codegen.meta.EntityMetaCodeGen.EntityMetaSignature;
import info.archinnov.achilles.internals.metamodel.columns.ColumnType;
import info.archinnov.achilles.internals.metamodel.columns.ComputedColumnInfo;
import info.archinnov.achilles.internals.metamodel.columns.PartitionKeyInfo;
import info.archinnov.achilles.internals.parser.FieldParser.FieldMetaSignature;
import info.archinnov.achilles.type.tuples.Tuple2;

public class SelectDSLCodeGen extends AbstractDSLCodeGen {

    public static TypeSpec buildSelectClass(EntityMetaSignature signature) {

        final String firstPartitionKey = signature.fieldMetaSignatures.stream()
                .filter(x -> x.context.columnType == ColumnType.PARTITION)
                .map(x -> Tuple2.of(x.context.fieldName, (PartitionKeyInfo) x.context.columnInfo))
                .sorted(TUPLE2_PARTITION_KEY_SORTER).map(Tuple2::_1).findFirst().get();

        TypeName selectFromTypeName = ClassName.get(DSL_PACKAGE, signature.selectFromReturnType());
        TypeName selectColumnsTypeName = ClassName.get(DSL_PACKAGE, signature.selectColumnsReturnType());

        final TypeSpec.Builder selectClassBuilder = TypeSpec.classBuilder(signature.selectClassName())
                .superclass(ABSTRACT_SELECT).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(buildSelectConstructor(signature)).addField(buildExactEntityMetaField(signature))
                .addField(buildEntityClassField(signature)).addType(buildSelectColumns(signature))
                .addType(buildSelectFrom(signature, firstPartitionKey));

        signature.fieldMetaSignatures.stream().filter(x -> x.context.columnType != ColumnType.COMPUTED)
                .forEach(x -> selectClassBuilder
                        .addMethod(buildSelectColumnMethod(selectColumnsTypeName, x, "select", NEW)));

        signature.fieldMetaSignatures.stream().filter(x -> x.context.columnType == ColumnType.COMPUTED)
                .forEach(x -> selectClassBuilder
                        .addMethod(buildSelectComputedColumnMethod(selectColumnsTypeName, x, "select", NEW)));

        selectClassBuilder.addMethod(buildSelectFunctionCallMethod(selectColumnsTypeName, "select", NEW));

        selectClassBuilder.addMethod(buildAllColumns(selectFromTypeName, SELECT_WHERE, "select"));
        selectClassBuilder.addMethod(buildAllColumnsWithSchemaProvider(selectFromTypeName, SELECT_WHERE, "select"));

        SelectWhereDSLCodeGen.buildWhereClasses(signature).forEach(selectClassBuilder::addType);

        return selectClassBuilder.build();
    }

    private static MethodSpec buildSelectConstructor(EntityMetaSignature signature) {
        String metaClassName = signature.className + META_SUFFIX;
        TypeName metaClassType = ClassName.get(ENTITY_META_PACKAGE, metaClassName);

        final MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
                .addParameter(RUNTIME_ENGINE, "rte").addParameter(metaClassType, "meta").addStatement("super(rte)")
                .addStatement("this.meta = meta");

        return builder.build();
    }

    private static TypeSpec buildSelectColumns(EntityMetaSignature signature) {

        TypeName selectColumnsTypeName = ClassName.get(DSL_PACKAGE, signature.selectColumnsReturnType());

        TypeName selectFromTypeName = ClassName.get(DSL_PACKAGE, signature.selectFromReturnType());

        final TypeSpec.Builder builder = TypeSpec.classBuilder(signature.className + SELECT_COLUMNS_DSL_SUFFIX)
                .superclass(ABSTRACT_SELECT_COLUMNS).addModifiers(Modifier.PUBLIC)
                .addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
                        .addParameter(SELECT_COLUMNS, "selection").addStatement("super(selection)").build());

        signature.fieldMetaSignatures.stream().filter(x -> x.context.columnType != ColumnType.COMPUTED).forEach(
                x -> builder.addMethod(buildSelectColumnMethod(selectColumnsTypeName, x, "selection", THIS)));

        signature.fieldMetaSignatures.stream().filter(x -> x.context.columnType == ColumnType.COMPUTED)
                .forEach(x -> builder
                        .addMethod(buildSelectComputedColumnMethod(selectColumnsTypeName, x, "selection", THIS)));

        builder.addMethod(buildSelectFunctionCallMethod(selectColumnsTypeName, "selection", THIS));

        builder.addMethod(buildFrom(selectFromTypeName, SELECT_WHERE, "selection"));
        builder.addMethod(buildFromWithSchemaProvider(selectFromTypeName, SELECT_WHERE, "selection"));

        return builder.build();
    }

    private static TypeSpec buildSelectFrom(EntityMetaSignature signature, String firstPartitionKey) {
        TypeName selectWhereTypeName = ClassName.get(DSL_PACKAGE,
                signature.selectWhereReturnType(firstPartitionKey));

        TypeName selectEndTypeName = ClassName.get(DSL_PACKAGE, signature.selectEndReturnType());

        return TypeSpec.classBuilder(signature.className + SELECT_FROM_DSL_SUFFIX).superclass(ABSTRACT_SELECT_FROM)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(MethodSpec.constructorBuilder().addParameter(SELECT_WHERE, "where")
                        .addStatement("super(where)").build())
                .addMethod(MethodSpec.methodBuilder("where")
                        .addJavadoc("Generate a SELECT ... FROM ... <strong>WHERE</strong> ...")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addStatement("return new $T(where)", selectWhereTypeName).returns(selectWhereTypeName)
                        .build())
                .addMethod(MethodSpec.methodBuilder("without_WHERE_Clause").addJavadoc(
                        "Generate a SELECT statement <strong>without</strong> the <strong>WHERE</strong> clause")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addStatement("return new $T(where)", selectEndTypeName).returns(selectEndTypeName).build())
                .build();
    }

    private static MethodSpec buildSelectColumnMethod(TypeName newTypeName, FieldMetaSignature parsingResult,
            String fieldName, ReturnType returnType) {

        final MethodSpec.Builder builder = MethodSpec.methodBuilder(parsingResult.context.fieldName)
                .addJavadoc("Generate a SELECT ... <strong>$L</strong> ...", parsingResult.context.cqlColumn)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addStatement("$L.column($S)", fieldName, parsingResult.context.cqlColumn).returns(newTypeName);

        if (returnType == NEW) {
            return builder.addStatement("return new $T(select)", newTypeName).build();
        } else {
            return builder.addStatement("return this").build();
        }
    }

    private static MethodSpec buildSelectFunctionCallMethod(TypeName newTypeName, String fieldName,
            ReturnType returnType) {
        final MethodSpec.Builder builder = MethodSpec.methodBuilder("function")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(newTypeName)
                .addJavadoc("Use this method to call a system or user-defined function.").addJavadoc("<br/>")
                .addJavadoc(
                        "All the system functions are accessible from the <strong>{@link info.archinnov.achilles.generated.function.SystemFunctions}</strong> class")
                .addJavadoc("<br/>")
                .addJavadoc(
                        "All the user-defined functions and aggregates are accessible from the <strong>{@link info.archinnov.achilles.generated.function.FunctionsRegistry}</strong> class")
                .addJavadoc("<br/>")
                .addJavadoc(
                        "System and user-defined functions accept only appropriate type. To pass in an entity field as function argument, use ")
                .addJavadoc(
                        "the generated <strong>manager.COLUMNS</strong> class which exposes all columns with their appropriate type")
                .addJavadoc("<br/>").addJavadoc("Example: ").addJavadoc("<pre class=\"code\"><code class=\"java\">")
                .addJavadoc("\n").addJavadoc("  {@literal @}Table\n").addJavadoc("  public class MyEntity {\n")
                .addJavadoc("\n").addJavadoc("      ...\n").addJavadoc("\n")
                .addJavadoc("      {@literal @}Column(\"value_column\")\n")
                .addJavadoc("      private String value;\n").addJavadoc("\n")
                .addJavadoc("      {@literal @}Column(\"list_of_string\")\n")
                .addJavadoc("      private List<String> strings;\n").addJavadoc("\n").addJavadoc("      ...\n")
                .addJavadoc("\n").addJavadoc("  }\n").addJavadoc("\n")
                .addJavadoc("  {@literal @}FunctionsRegistry\n").addJavadoc("  public interface MyFunctions {\n")
                .addJavadoc("\n").addJavadoc("       String convertListToJson(List<String> strings);\n")
                .addJavadoc("\n").addJavadoc("  }\n").addJavadoc("\n").addJavadoc("\n").addJavadoc("  ...\n")
                .addJavadoc("\n").addJavadoc("\n").addJavadoc("  manager\n").addJavadoc("     .dsl()\n")
                .addJavadoc("     .select()\n")
                .addJavadoc(
                        "     // This call will generate SELECT cast(writetime(value_column) as text) AS writetimeOfValueAsString, ...\n")
                .addJavadoc(
                        "     .function(SystemFunctions.castAsText(SystemFunctions.writetime(manager.COLUMNS.VALUE)), \"writetimeOfValueAsString\")\n")
                .addJavadoc("     ...\n").addJavadoc("\n").addJavadoc("  manager \n").addJavadoc("     .dsl()\n")
                .addJavadoc("     .select()\n")
                .addJavadoc(
                        "     // This call will generate SELECT convertlisttojson(list_of_string) AS strings_as_json, ...\n")
                .addJavadoc(
                        "     .function(FunctionsRegistry.convertListToJson(manager.COLUMNS.STRINGS), \"strings_as_json\")\n")
                .addJavadoc("     ...\n").addJavadoc("\n").addJavadoc("</code></pre>\n").addJavadoc("<br/>")
                .addJavadoc("\n").addJavadoc("@param functionCall the function call object \n")
                .addJavadoc(
                        "@param alias mandatory alias for this function call for easier retrieval from the ResultSet \n")
                .addJavadoc("@return a built-in function call passed to the QueryBuilder object \n")
                .addParameter(FUNCTION_CALL, "functionCall", Modifier.FINAL)
                .addParameter(STRING, "alias", Modifier.FINAL)
                .addStatement("functionCall.addToSelect($L, alias)", fieldName);

        if (returnType == NEW) {
            return builder.addStatement("return new $T(select)", newTypeName).build();
        } else {
            return builder.addStatement("return this").build();
        }
    }

    private static MethodSpec buildSelectComputedColumnMethod(TypeName newTypeName,
            FieldMetaSignature parsingResult, String fieldName, ReturnType returnType) {

        final ComputedColumnInfo columnInfo = (ComputedColumnInfo) parsingResult.context.columnInfo;
        StringJoiner joiner = new StringJoiner(",", fieldName + ".fcall($S,", ").as($S)");
        columnInfo.functionArgs.forEach(x -> joiner.add("$L"));

        final Object[] functionName = new Object[] { columnInfo.functionName };
        final Object[] functionArgs = columnInfo.functionArgs.stream()
                .map(arg -> CodeBlock.builder().add("$T.column($S)", QUERY_BUILDER, arg).build()).toArray();
        final Object[] alias = new Object[] { columnInfo.alias };

        final Object[] varargs = ArrayUtils.addAll(functionName, ArrayUtils.addAll(functionArgs, alias));
        final MethodSpec.Builder builder = MethodSpec.methodBuilder(parsingResult.context.fieldName)
                .addJavadoc("Generate a SELECT ... <strong>$L($L) AS $L</strong> ...", varargs)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addStatement(joiner.toString(), varargs)
                .returns(newTypeName);

        if (returnType == NEW) {
            return builder.addStatement("return new $T(select)", newTypeName).build();
        } else {
            return builder.addStatement("return this").build();
        }
    }
}