Package com.asakusafw.compiler.flow

Source Code of com.asakusafw.compiler.flow.FlowElementProcessor$DataObjectMirror

/**
* 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.compiler.flow;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import com.asakusafw.compiler.common.NameGenerator;
import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.runtime.flow.ArrayListBuffer;
import com.asakusafw.runtime.flow.FileMapListBuffer;
import com.asakusafw.runtime.flow.ListBuffer;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.FieldDeclaration;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.Statement;
import com.asakusafw.utils.java.model.syntax.Type;
import com.asakusafw.utils.java.model.util.AttributeBuilder;
import com.asakusafw.utils.java.model.util.ExpressionBuilder;
import com.asakusafw.utils.java.model.util.ImportBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.utils.java.model.util.TypeBuilder;
import com.asakusafw.vocabulary.flow.graph.FlowElementAttribute;
import com.asakusafw.vocabulary.flow.graph.FlowElementAttributeProvider;
import com.asakusafw.vocabulary.flow.graph.FlowElementDescription;
import com.asakusafw.vocabulary.flow.graph.FlowElementPortDescription;
import com.asakusafw.vocabulary.flow.graph.FlowResourceDescription;
import com.asakusafw.vocabulary.flow.graph.OperatorDescription;
import com.asakusafw.vocabulary.flow.processor.InputBuffer;

/**
* フロー要素を処理するプロセッサ。
* <p>
* このインターフェースを直接実装すべきでない。
* </p>
*/
public interface FlowElementProcessor extends FlowCompilingEnvironment.Initializable {

    /**
     * 結果オブジェクトに結果を追加する際のメソッド名。
     */
    String RESULT_METHOD_NAME = "add";

    /**
     * このプロセッサの種類を返す。
     * @return このプロセッサの種類
     */
    FlowElementProcessor.Kind getKind();

    /**
     * このプロセッサが対象とする注釈の型を返す。
     * @return このプロセッサが対象とする注釈の型
     */
    Class<? extends Annotation> getTargetAnnotationType();

    /**
     * 処理の文脈の基底となるクラス。
     * @since 0.1.0
     * @version 0.5.1
     */
    public abstract static class AbstractProcessorContext implements FlowElementAttributeProvider {

        /**
         * コンパイル環境。
         */
        protected final FlowCompilingEnvironment environment;

        private final FlowElementAttributeProvider element;

        /**
         * Javaの構造を表すモデルオブジェクトを生成する。
         */
        protected final ModelFactory factory;

        /**
         * インポート宣言を行う。
         */
        protected final ImportBuilder importer;

        /**
         * 名前を生成する。
         */
        protected final NameGenerator names;

        /**
         * 処理対象の演算子の定義記述。
         */
        protected final OperatorDescription description;

        /**
         * リソースと式の対応表。
         */
        protected final Map<FlowResourceDescription, Expression> resources;

        /**
         * 生成されたフィールドの一覧。
         */
        protected final List<FieldDeclaration> generatedFields;

        /**
         * インスタンスを生成する。
         * @param environment 環境
         * @param element target element
         * @param importer インポート
         * @param names 名前生成
         * @param desc 演算子の定義記述
         * @param resources リソースと式の対応表
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public AbstractProcessorContext(
                FlowCompilingEnvironment environment,
                FlowElementAttributeProvider element,
                ImportBuilder importer,
                NameGenerator names,
                OperatorDescription desc,
                Map<FlowResourceDescription, Expression> resources) {
            Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(importer, "importer"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(element, "element"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(names, "names"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(desc, "desc"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(resources, "resources"); //$NON-NLS-1$
            this.environment = environment;
            this.element = element;
            this.factory = environment.getModelFactory();
            this.importer = importer;
            this.names = names;
            this.description = desc;
            this.resources = resources;
            this.generatedFields = Lists.create();
        }

        /**
         * 処理中の演算子の定義記述を返す。
         * @return 処理中の演算子の定義記述
         */
        public OperatorDescription getOperatorDescription() {
            return description;
        }

        @Override
        public <T extends FlowElementAttribute> T getAttribute(Class<T> attributeClass) {
            if (attributeClass == null) {
                throw new IllegalArgumentException("attributeClass must not be null"); //$NON-NLS-1$
            }
            return element.getAttribute(attributeClass);
        }

        /**
         * 指定の番号に割り振られた入力ポートの定義記述を返す。
         * @param portNumber 対象のポート番号
         * @return 入力ポートの定義記述
         * @throws NoSuchElementException 指定のポートが見つからない場合
         * @throws IllegalArgumentException 存在しないポート番号が指定され他場合
         */
        public FlowElementPortDescription getInputPort(int portNumber) {
            if (portNumber < 0 || portNumber >= description.getInputPorts().size()) {
                throw new IllegalArgumentException("invalid port number"); //$NON-NLS-1$
            }
            return description.getInputPorts().get(portNumber);
        }

        /**
         * 指定の番号に割り振られた出力ポートの定義記述を返す。
         * @param portNumber 対象のポート番号
         * @return 出力ポートの定義記述
         * @throws NoSuchElementException 指定のポートが見つからない場合
         * @throws IllegalArgumentException 存在しないポート番号が指定され他場合
         */
        public FlowElementPortDescription getOutputPort(int portNumber) {
            if (portNumber < 0 || portNumber >= description.getOutputPorts().size()) {
                throw new IllegalArgumentException("invalid port number"); //$NON-NLS-1$
            }
            return description.getOutputPorts().get(portNumber);
        }

        /**
         * 指定の番号に割り振られたリソースを表すオブジェクトを返す。
         * @param resourceNumber 対象のリソース番号
         * @return リソースを表す式
         * @throws IllegalArgumentException 存在しないリソース番号が指定された場合
         */
        public FlowResourceDescription getResourceDescription(int resourceNumber) {
            if (resourceNumber < 0 || resourceNumber >= description.getResources().size()) {
                throw new IllegalArgumentException("invalid resource number"); //$NON-NLS-1$
            }
            FlowResourceDescription resource = description.getResources().get(resourceNumber);
            return resource;
        }

        /**
         * 指定のリソースを表す式を返す。
         * @param resource 対象のリソース
         * @return リソースを表す式
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public Expression getResource(FlowResourceDescription resource) {
            Precondition.checkMustNotBeNull(resource, "resource"); //$NON-NLS-1$
            Expression expression = resources.get(resource);
            assert expression != null;
            return expression;
        }

        /**
         * JavaのDOM構造を構築するためのファクトリーを返す。
         * @return DOM構造を構築するためのファクトリー
         */
        public ModelFactory getModelFactory() {
            return factory;
        }

        private Expression addField(Type type, String name, Expression init) {
            assert type != null;
            assert name != null;
            SimpleName fieldName = createName(name);
            FieldDeclaration field = factory.newFieldDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .Private()
                        .toAttributes(),
                    type,
                    fieldName,
                    init);
            generatedFields.add(field);
            return factory.newFieldAccessExpression(
                    factory.newThis(),
                    fieldName);
        }

        /**
         * ここまでにこの文脈で生成されたフィールド宣言の一覧を返す。
         * @return この文脈で生成されたフィールド宣言の一覧
         */
        public List<FieldDeclaration> getGeneratedFields() {
            return generatedFields;
        }

        /**
         * 指定のヒント名を含む衝突しない新しい名前を返す。
         * @param hint ヒント名
         * @return 衝突しない新しい名前
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public SimpleName createName(String hint) {
            Precondition.checkMustNotBeNull(hint, "hint"); //$NON-NLS-1$
            return names.create(hint);
        }

        /**
         * 演算子実装クラスのインスタンスを生成し、それを参照するための式を返す。
         * @return 生成した式
         */
        public Expression createImplementation() {
            Class<?> implementing = description.getDeclaration().getImplementing();
            Type type = convert(implementing);
            return addField(type, "op", new TypeBuilder(factory, type)
                .newObject()
                .toExpression());
        }

        /**
         * 任意の型を持つフィールドを生成する。
         * @param type 対象の型
         * @param name 名前のヒント
         * @return 生成したフィールドを参照するための式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Expression createField(java.lang.reflect.Type type, String name) {
            return createField(type, name, null);
        }

        /**
         * Put a new field declaration into the current context.
         * @param type field type
         * @param name field name
         * @param init field initialization expression (nullable)
         * @return an expression to access the created field
         * @throws IllegalArgumentException if some parameters were {@code null}
         * @since 0.5.1
         */
        public Expression createField(java.lang.reflect.Type type, String name, Expression init) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(name, "name"); //$NON-NLS-1$
            return addField(
                    importer.toType(type),
                    name,
                    init);
        }

        /**
         * モデルのキャッシュインスタンスを生成し、それを参照するための式を返す。
         * @param type モデルの型
         * @return 生成した式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public DataObjectMirror createModelCache(java.lang.reflect.Type type) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            DataClass data = environment.getDataClasses().load(type);
            if (data == null) {
                environment.error("{0}のデータモデルを解析できませんでした", type);
                data = new DataClass.Unresolved(factory, type);
            }
            Type domType = importer.toType(type);
            Expression cache = addField(
                    domType,
                    "cache",
                    data.createNewInstance(domType));
            return new DataObjectMirror(factory, cache, data);
        }

        /**
         * {@link ListBuffer}のインスタンスを生成し、それを参照するための式を返す。
         * @param type リストの要素型
         * @param bufferKind the input buffer kind
         * @return 生成した式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public ListBufferMirror createListBuffer(java.lang.reflect.Type type, InputBuffer bufferKind) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(bufferKind, "bufferKind"); //$NON-NLS-1$
            Type elementType = importer.toType(type);
            Class<?> bufferType = inputBufferTypeFromKind(bufferKind);
            Type listType = importer.resolve(factory.newParameterizedType(
                    Models.toType(factory, bufferType),
                    Collections.singletonList(elementType)));
            Expression list = addField(
                    listType,
                    "list",
                    new TypeBuilder(factory, listType)
                        .newObject()
                        .toExpression());
            DataClass component = environment.getDataClasses().load(type);
            if (component == null) {
                environment.error("{0}のデータモデルを解析できませんでした", type);
                component = new DataClass.Unresolved(factory, type);
            }
            return new ListBufferMirror(factory, list, component, elementType);
        }

        private Class<?> inputBufferTypeFromKind(InputBuffer kind) {
            assert kind != null;
            switch (kind) {
            case EXPAND:
                return ArrayListBuffer.class;
            case ESCAPE:
                return FileMapListBuffer.class;
            default:
                throw new AssertionError(kind);
            }
        }

        /**
         * 指定の型をインポートし、JavaのDOMの表現に変換して返す。
         * @param type 対象の型
         * @return 変換後の型
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Type convert(java.lang.reflect.Type type) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            return importer.toType(type);
        }

        /**
         * 指定の型をインポートして返す。
         * @param type 対象の型
         * @return 変換後の型
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Type simplify(Type type) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            return importer.resolve(type);
        }
    }

    /**
     * データオブジェクトを操作するためのミラー。
     */
    public static class DataObjectMirror {

        private final Expression object;

        private final DataClass dataClass;

        /**
         * インスタンスを生成する。
         * @param factory ファクトリ
         * @param object データオブジェクトを参照するための式
         * @param dataClass データオブジェクトの型
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public DataObjectMirror(
                ModelFactory factory,
                Expression object,
                DataClass dataClass) {
            Precondition.checkMustNotBeNull(factory, "factory"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(object, "object"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(dataClass, "dataClass"); //$NON-NLS-1$
            this.object = object;
            this.dataClass = dataClass;
        }

        /**
         * 操作対象のオブジェクトを表す式を返す。
         * @return 操作対象のオブジェクトを表す式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Expression get() {
            return object;
        }

        /**
         * このデータオブジェクトに、別のデータオブジェクトの内容を設定する文を返す。
         * @param value 別のデータオブジェクトを表す式
         * @return 生成した文
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Statement createSet(Expression value) {
            Precondition.checkMustNotBeNull(value, "value"); //$NON-NLS-1$
            return dataClass.assign(object, value);
        }

        /**
         * このデータオブジェクトの内容を消去する文を返す。
         * @return 生成した文
         */
        public Statement createReset() {
            return dataClass.reset(object);
        }
    }

    /**
     * 結果オブジェクトを操作するミラー。
     */
    public static class ResultMirror {

        private final ModelFactory factory;

        private final Expression object;

        /**
         * インスタンスを生成する。
         * @param factory ファクトリ
         * @param object 結果オブジェクトを参照するための式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public ResultMirror(ModelFactory factory, Expression object) {
            Precondition.checkMustNotBeNull(factory, "factory"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(object, "object"); //$NON-NLS-1$
            this.factory = factory;
            this.object = object;
        }

        /**
         * 操作対象のオブジェクトを表す式を返す。
         * @return 操作対象のオブジェクトを表す式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Expression get() {
            return object;
        }

        /**
         * この結果オブジェクトに、指定の式を追加する文を返す。
         * @param value 追加する式
         * @return 生成した文
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Statement createAdd(Expression value) {
            Precondition.checkMustNotBeNull(value, "value"); //$NON-NLS-1$
            return new ExpressionBuilder(factory, object)
                .method(RESULT_METHOD_NAME, value)
                .toStatement();
        }
    }

    /**
     * {@link ListBuffer}を操作するミラー。
     */
    public static class ListBufferMirror {

        private static final String BEGIN = "begin";

        private static final String ADVANCE = "advance";

        private static final String END = "end";

        private static final String EXPAND = "expand";

        private static final String IS_EXPAND_REQUIRED = "isExpandRequired";

        private static final String SHRINK = "shrink";

        private final ModelFactory factory;

        private final Expression object;

        private final DataClass dataClass;

        private final Type elementType;

        /**
         * インスタンスを生成する。
         * @param factory ファクトリ
         * @param object 結果オブジェクトを参照するための式
         * @param dataClass リスト要素のデータ型
         * @param elementType リスト要素のDOMでの型表現
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public ListBufferMirror(
                ModelFactory factory,
                Expression object,
                DataClass dataClass,
                Type elementType) {
            Precondition.checkMustNotBeNull(factory, "factory"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(object, "object"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(dataClass, "dataClass"); //$NON-NLS-1$
            this.factory = factory;
            this.object = object;
            this.dataClass = dataClass;
            this.elementType = elementType;
        }

        /**
         * 操作対象のオブジェクトを表す式を返す。
         * @return 操作対象のオブジェクトを表す式
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Expression get() {
            return object;
        }

        /**
         * {@link ListBuffer}の開始処理を行う文を返す。
         * @return 生成した文
         * @see ListBuffer#begin()
         */
        public Statement createBegin() {
            return new ExpressionBuilder(factory, object)
                .method(BEGIN)
                .toStatement();
        }

        /**
         * {@link ListBuffer}へのデータ追加処理を行う文を返す。
         * <p>
         * バッファの拡張などは自動的に行う。
         * </p>
         * @param value 追加するデータを表す式
         * @return 生成した文
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         * @see ListBuffer#advance()
         * @see ListBuffer#expand(Object)
         * @see ListBuffer#isExpandRequired()
         */
        public Statement createAdvance(Expression value) {
            Precondition.checkMustNotBeNull(value, "value"); //$NON-NLS-1$
            List<Statement> thenBlock = Arrays.asList(new Statement[] {
                    new ExpressionBuilder(factory, object)
                        .method(EXPAND, dataClass.createNewInstance(elementType))
                        .toStatement(),
                    dataClass.assign(
                            new ExpressionBuilder(factory, object)
                                .method(ADVANCE)
                                .toExpression(),
                            value),
            });
            List<Statement> elseBlock = Arrays.asList(new Statement[] {
                    dataClass.assign(
                            new ExpressionBuilder(factory, object)
                                .method(ADVANCE)
                                .toExpression(),
                            value),
            });
            return factory.newIfStatement(
                    new ExpressionBuilder(factory, object)
                        .method(IS_EXPAND_REQUIRED)
                        .toExpression(),
                    factory.newBlock(thenBlock),
                    factory.newBlock(elseBlock));
        }

        /**
         * {@link ListBuffer}の更新終了処理を行う文を返す。
         * @return 生成した文
         * @see ListBuffer#end()
         */
        public Statement createEnd() {
            return new ExpressionBuilder(factory, object)
                .method(END)
                .toStatement();
        }

        /**
         * {@link ListBuffer}の参照終了処理を行う文を返す。
         * @return 生成した文
         * @see ListBuffer#end()
         */
        public Statement createShrink() {
            return new ExpressionBuilder(factory, object)
                .method(SHRINK)
                .toStatement();
        }
    }

    /**
     * {@link FlowElementProcessor}を取得するためのリポジトリ。
     */
    interface Repository extends FlowCompilingEnvironment.Initializable {

        /**
         * 空要素に対するプロセッサを返す。
         * @return 空要素に対するプロセッサ
         */
        LinePartProcessor getEmptyProcessor();

        /**
         * 指定の要素に関するプロセッサを返す。
         * @param description 対象の要素記述
         * @return 対応するプロセッサ、存在しない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        FlowElementProcessor findProcessor(FlowElementDescription description);

        /**
         * 指定のライン要素に関するプロセッサを返す。
         * @param description 対象の要素記述
         * @return 対応するプロセッサ、存在しない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        LineProcessor findLineProcessor(FlowElementDescription description);

        /**
         * 指定の合流要素に関するプロセッサを返す。
         * @param description 対象の要素記述
         * @return 対応するプロセッサ、存在しない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        RendezvousProcessor findRendezvousProcessor(FlowElementDescription description);
    }

    /**
     * プロセッサの種類。
     */
    enum Kind {

        /**
         * {@link LinePartProcessor}として利用される。
         */
        LINE_PART,

        /**
         * {@link LineEndProcessor}として利用される。
         */
        LINE_END,

        /**
         * {@link RendezvousProcessor}として利用される。
         */
        RENDEZVOUS,
    }

}
TOP

Related Classes of com.asakusafw.compiler.flow.FlowElementProcessor$DataObjectMirror

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.