Package com.asakusafw.modelgen.emitter

Source Code of com.asakusafw.modelgen.emitter.SummarizedModelEntityEmitter

/**
* Copyright 2011-2014 Asakusa Framework Team.
*
* 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 com.asakusafw.modelgen.emitter;

import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.asakusafw.modelgen.Constants;
import com.asakusafw.modelgen.model.Aggregator;
import com.asakusafw.modelgen.model.ModelProperty;
import com.asakusafw.modelgen.model.ModelReference;
import com.asakusafw.modelgen.model.Source;
import com.asakusafw.modelgen.model.SummarizedModelDescription;
import com.asakusafw.utils.java.model.syntax.Annotation;
import com.asakusafw.utils.java.model.syntax.AnnotationElement;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.Javadoc;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.NamedType;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.Statement;
import com.asakusafw.utils.java.model.syntax.TypeBodyDeclaration;
import com.asakusafw.utils.java.model.util.AttributeBuilder;
import com.asakusafw.utils.java.model.util.ExpressionBuilder;
import com.asakusafw.utils.java.model.util.JavadocBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.vocabulary.model.DataModel;
import com.asakusafw.vocabulary.model.Key;
import com.asakusafw.vocabulary.model.ModelRef;
import com.asakusafw.vocabulary.model.Property;
import com.asakusafw.vocabulary.model.SummarizedModel;

/**
* {@link SummarizedModelDescription}に対するエンティティ情報をファイル上に出力するエミッタ。
*/
public class SummarizedModelEntityEmitter
        extends ModelEntityEmitter<SummarizedModelDescription> {

    /**
     * インスタンスを生成する。
     * @param factory ソースコードを生成するファクトリ
     * @param output 出力先のベースディレクトリ
     * @param packageName 出力先のパッケージ名
     * @param headerComment ファイルのヘッダコメント、不要の場合は{@code null}
     */
    public SummarizedModelEntityEmitter(
            ModelFactory factory,
            File output,
            String packageName,
            List<String> headerComment) {
        super(factory, output, packageName, headerComment);
    }

    @Override
    public Class<SummarizedModelDescription> getEmitTargetType() {
        return SummarizedModelDescription.class;
    }

    @Override
    protected List<TypeBodyDeclaration> createMembers(
            SummarizedModelDescription model) {
        List<TypeBodyDeclaration> members = super.createMembers(model);
        members.add(createStartSummarize(model));
        members.add(createProcessSummarize(model));
        return members;
    }

    private TypeBodyDeclaration createStartSummarize(
            SummarizedModelDescription model) {
        SimpleName param = common.getVariableNameOf(model, "original");
        List<Statement> statements = new ArrayList<Statement>();
        for (ModelProperty property : model.getProperties()) {
            statements.add(createStartSummarizeFor(param, property));
        }
        return f.newMethodDeclaration(
                new JavadocBuilder(f)
                    .text("指定のモデルを最初の要素として、このモデルの集計結果を初期化する。")
                    .param(param)
                        .text("最初の要素となるモデル")
                    .toJavadoc(),
                new AttributeBuilder(f)
                    .Public()
                    .toAttributes(),
                Models.toType(f, void.class),
                common.getStartSummarizerName(),
                Collections.singletonList(f.newFormalParameterDeclaration(
                        bless(common.getModelType(model.getOriginalModel())),
                        param)),
                statements);
    }

    private Statement createStartSummarizeFor(
            SimpleName parameterName,
            ModelProperty property) {
        Source source = property.getFrom();
        Expression to = f.newFieldAccessExpression(
                f.newThis(),
                common.getFieldNameOf(property.getName(), property.getType()));
        switch (source.getAggregator()) {
        case IDENT:
        case MAX:
        case MIN:
        case SUM:
            return new ExpressionBuilder(f, to)
                .method(Constants.NAME_OPTION_MODIFIER,
                        new ExpressionBuilder(f, parameterName)
                            .method(common.getGetterNameOf(source.getName(), source.getType()))
                            .toExpression())
                .toStatement();
        case COUNT:
            return new ExpressionBuilder(f, to)
                .method(Constants.NAME_OPTION_MODIFIER, Models.toLiteral(f, 1))
                .toStatement();
        default:
            throw new AssertionError();
        }
    }

    private TypeBodyDeclaration createProcessSummarize(
            SummarizedModelDescription model) {
        SimpleName param = common.getVariableNameOf(model, "original");
        List<Statement> statements = new ArrayList<Statement>();
        for (ModelProperty property : model.getProperties()) {
            // IDENTは一度だけ
            if (property.getFrom().getAggregator() == Aggregator.IDENT) {
                continue;
            }
            statements.add(createAddSummarizeFor(param.getToken(), property));
        }
        return f.newMethodDeclaration(
                new JavadocBuilder(f)
                    .text("このモデルに、指定のモデルの集計結果を合成する。")
                    .param(param)
                        .text("合成するモデル")
                    .toJavadoc(),
                new AttributeBuilder(f)
                    .Public()
                    .toAttributes(),
                Models.toType(f, void.class),
                common.getCombineSummarizerName(),
                Collections.singletonList(f.newFormalParameterDeclaration(
                        bless(common.getModelType(model.getReference())),
                        param)),
                statements);
    }

    private Statement createAddSummarizeFor(
            String parameterName,
            ModelProperty property) {
        Source source = property.getFrom();
        Expression self = f.newFieldAccessExpression(
                f.newThis(),
                common.getFieldNameOf(property.getName(), property.getType()));
        Expression other = f.newFieldAccessExpression(
                f.newSimpleName(parameterName),
                common.getFieldNameOf(property.getName(), property.getType()));
        Aggregator aggregator = source.getAggregator();
        switch (aggregator) {
        case MAX:
        case MIN:
            return new ExpressionBuilder(f, self)
                .method(aggregator.name().toLowerCase(), other)
                .toStatement();
        case SUM:
        case COUNT:
            return new ExpressionBuilder(f, self)
                .method(Constants.NAME_OPTION_ADDER, other)
                .toStatement();
        default:
            throw new AssertionError();
        }
    }

    @Override
    protected List<Annotation> createAnnotationsForModel(
            SummarizedModelDescription model) {
        return new AttributeBuilder(f)
            .annotation(bless(DataModel.class))
            .annotation(bless(SummarizedModel.class),
                "from",
                createModelRefAnnotation(
                        model.getOriginalModel(),
                        model.getGroupBy()))
            .annotation(bless(SuppressWarnings.class), Models.toLiteral(f, "deprecation"))
            .toAnnotations();
    }

    @Override
    protected List<Annotation> createAnnotationsForField(ModelProperty property) {
        return new AttributeBuilder(f)
            .annotation(bless(Property.class),
                    "from", createSourceAnnotation(property.getFrom()),
                    "aggregator", Models.append(
                            f,
                            ((NamedType) bless(Property.Aggregator.class)).getName(),
                            property.getFrom().getAggregator().name()))
            .toAnnotations();
    }

    private Annotation createModelRefAnnotation(
            ModelReference reference,
            List<Source> groupSources) {
        List<String> groupKeys = new ArrayList<String>();
        for (Source s : groupSources) {
            groupKeys.add(common.getFieldNameOf(
                    s.getName(),
                    s.getType()).getToken());
        }
        String[] group = groupKeys.toArray(new String[groupKeys.size()]);
        Annotation key = f.newNormalAnnotation(
                (NamedType) bless(Key.class),
                Collections.singletonList(f.newAnnotationElement(
                        f.newSimpleName("group"),
                        Models.toArrayInitializer(f, group))));
        return f.newNormalAnnotation(
                (NamedType) bless(ModelRef.class),
                Arrays.asList(new AnnotationElement[] {
                        f.newAnnotationElement(
                                f.newSimpleName("type"),
                                f.newClassLiteral(bless(
                                        common.getModelType(reference)))),
                        f.newAnnotationElement(
                                f.newSimpleName("key"),
                                key),
                }));
    }

    private Annotation createSourceAnnotation(Source source) {
        AnnotationElement declaring = f.newAnnotationElement(
                f.newSimpleName("declaring"),
                f.newClassLiteral(bless(common.getModelType(source.getDeclaring()))));
        AnnotationElement name = f.newAnnotationElement(
                f.newSimpleName("name"),
                Models.toLiteral(f, common.getFieldNameOf(
                        source.getName(),
                        source.getType()).getToken()));
        return f.newNormalAnnotation(
                (NamedType) bless(Property.Source.class),
                Arrays.asList(declaring, name));
    }

    @Override
    protected Javadoc createJavadocForModel(SummarizedModelDescription model) {
        List<String> groupByNames = new ArrayList<String>();
        for (Source source : model.getGroupBy()) {
            groupByNames.add(source.getName());
        }
        String groupByComment;
        if (groupByNames.isEmpty()) {
            groupByComment = "<p>グループ化は指定されておらず、全てのレコードが同一グループになる。</p>";
        } else {
            groupByComment = MessageFormat.format(
                    "<p>グループ化はそれぞれ <code>{0}</code> で行っている。</p>",
                    groupByNames);
        }
        return new JavadocBuilder(f)
            .text("テーブル<code>{0}</code>を集計した結果のモデルクラス。",
                    model.getReference().getSimpleName())
            .text(groupByComment)
            .toJavadoc();
    }

    @Override
    protected Javadoc createJavadocForField(ModelProperty property) {
        return new JavadocBuilder(f)
            .text("{0}のフィールド。", getColumnDescription(property))
            .toJavadoc();
    }

    @Override
    protected Javadoc createJavadocForGetter(ModelProperty property) {
        return new JavadocBuilder(f)
            .text("{0}を返す。", getColumnDescription(property))
            .returns()
                .text(getColumnDescription(property))
            .exception(bless(NullPointerException.class))
                .text("値に")
                .code("null")
                .text("が格納されていた場合")
            .toJavadoc();
    }

    @Override
    protected Javadoc createJavadocForSetter(ModelProperty property) {
        return new JavadocBuilder(f)
            .text("{0}を変更する。", getColumnDescription(property))
            .param(createNameForParameter(property))
                .text("設定する値")
            .toJavadoc();
    }

    private String getColumnDescription(ModelProperty property) {
        Source source = property.getFrom();
        if (source.getAggregator() == Aggregator.IDENT) {
            return MessageFormat.format(
                    "グループ化したカラム<code>{0}</code>の内容",
                    source.getName());
        } else {
            return MessageFormat.format(
                    "カラム<code>{0}</code>を<code>{1}()</code>で集約した結果",
                    source.getName(),
                    source.getAggregator());
        }
    }
}
TOP

Related Classes of com.asakusafw.modelgen.emitter.SummarizedModelEntityEmitter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.