Package com.asakusafw.compiler.directio.emitter

Source Code of com.asakusafw.compiler.directio.emitter.NamingClassEmitter

/**
* 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.directio.emitter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.directio.OutputPattern.CompiledResourcePattern;
import com.asakusafw.compiler.directio.OutputPattern.RandomNumber;
import com.asakusafw.compiler.directio.OutputPattern.SourceKind;
import com.asakusafw.compiler.flow.DataClass;
import com.asakusafw.compiler.flow.FlowCompilingEnvironment;
import com.asakusafw.runtime.stage.directio.StringTemplate;
import com.asakusafw.runtime.stage.directio.StringTemplate.Format;
import com.asakusafw.runtime.stage.directio.StringTemplate.FormatSpec;
import com.asakusafw.runtime.value.IntOption;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.model.syntax.Comment;
import com.asakusafw.utils.java.model.syntax.CompilationUnit;
import com.asakusafw.utils.java.model.syntax.ConstructorDeclaration;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.FieldDeclaration;
import com.asakusafw.utils.java.model.syntax.FormalParameterDeclaration;
import com.asakusafw.utils.java.model.syntax.InfixOperator;
import com.asakusafw.utils.java.model.syntax.Javadoc;
import com.asakusafw.utils.java.model.syntax.MethodDeclaration;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.Name;
import com.asakusafw.utils.java.model.syntax.QualifiedName;
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.syntax.TypeBodyDeclaration;
import com.asakusafw.utils.java.model.syntax.TypeDeclaration;
import com.asakusafw.utils.java.model.syntax.TypeParameterDeclaration;
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.JavadocBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.utils.java.model.util.TypeBuilder;

/**
* Emits {@link StringTemplate} subclasses.
* @since 0.2.5
*/
@SuppressWarnings("deprecation")
public class NamingClassEmitter {

    static final Logger LOG = LoggerFactory.getLogger(NamingClassEmitter.class);

    private final FlowCompilingEnvironment environment;

    private final String moduleId;

    /**
     * Creates a new instance.
     * @param environment current environment
     * @param moduleId target module ID
     * @throws IllegalArgumentException if some parameters were {@code null}
     */
    public NamingClassEmitter(FlowCompilingEnvironment environment, String moduleId) {
        Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(moduleId, "moduleId"); //$NON-NLS-1$
        this.environment = environment;
        this.moduleId = moduleId;
    }

    /**
     * Emits a name generator class.
     * @param outputName the output name
     * @param index the output index
     * @param dataType output data type
     * @param namingInfo naming information
     * @return the generated class name
     * @throws IOException if failed to emit class
     * @throws IllegalArgumentException if some parameters were {@code null}
     */
    public Name emit(
            String outputName,
            int index,
            DataClass dataType,
            List<CompiledResourcePattern> namingInfo) throws IOException {
        if (outputName == null) {
            throw new IllegalArgumentException("outputName must not be null"); //$NON-NLS-1$
        }
        if (dataType == null) {
            throw new IllegalArgumentException("dataType must not be null"); //$NON-NLS-1$
        }
        if (namingInfo == null) {
            throw new IllegalArgumentException("namingInfo must not be null"); //$NON-NLS-1$
        }
        LOG.debug("Start preparing output file name template: batch={}, flow={}, output={}", new Object[] {
                environment.getBatchId(),
                environment.getFlowId(),
                outputName,
        });

        Engine engine = new Engine(environment, moduleId, outputName, index, dataType, namingInfo);
        CompilationUnit source = engine.generate();
        environment.emit(source);
        Name packageName = source.getPackageDeclaration().getName();
        SimpleName simpleName = source.getTypeDeclarations().get(0).getName();
        QualifiedName name = environment
            .getModelFactory()
            .newQualifiedName(packageName, simpleName);
        LOG.debug("Finish preparing output file name template: batch={}, flow={}, output={}, class={}", new Object[] {
                environment.getBatchId(),
                environment.getFlowId(),
                outputName,
                name.toNameString(),
        });
        return name;
    }


    private static final class Engine {

        private static final String FIELD_RANDOM_HOLDER = "randomValue";

        private static final String FIELD_RANDOMIZER = "randomizer";

        private final String moduleId;

        private final String outputName;

        private final int index;

        private final DataClass dataType;

        private final List<CompiledResourcePattern> namingInfo;

        private final ModelFactory factory;

        private final ImportBuilder importer;

        Engine(
                FlowCompilingEnvironment environment,
                String moduleId,
                String outputName,
                int index,
                DataClass dataType,
                List<CompiledResourcePattern> namingInfo) {
            assert environment != null;
            assert moduleId != null;
            assert outputName != null;
            assert dataType != null;
            assert namingInfo != null;
            this.moduleId = moduleId;
            this.outputName = outputName;
            this.index = index;
            this.dataType = dataType;
            this.namingInfo = namingInfo;
            this.factory = environment.getModelFactory();
            Name packageName = environment.getEpiloguePackageName(moduleId);
            this.importer = new ImportBuilder(
                    factory,
                    factory.newPackageDeclaration(packageName),
                    ImportBuilder.Strategy.TOP_LEVEL);
        }

        public CompilationUnit generate() {
            TypeDeclaration type = createType();
            return factory.newCompilationUnit(
                    importer.getPackageDeclaration(),
                    importer.toImportDeclarations(),
                    Collections.singletonList(type),
                    Collections.<Comment>emptyList());
        }

        private TypeDeclaration createType() {
            SimpleName name = getClassName();
            importer.resolvePackageMember(name);
            List<TypeBodyDeclaration> members = Lists.create();
            if (requireRandomNumber()) {
                members.add(createRandomHolder());
                members.add(createRandomizer());
            }
            members.add(createConstructor());
            members.add(createSetMethod());
            return factory.newClassDeclaration(
                    createJavadoc(),
                    new AttributeBuilder(factory)
                        .annotation(
                                importer.toType(SuppressWarnings.class),
                                Models.toLiteral(factory, "deprecation"))
                        .Public()
                        .Final()
                        .toAttributes(),
                    name,
                    Collections.<TypeParameterDeclaration>emptyList(),
                    t(StringTemplate.class),
                    Collections.<Type>emptyList(),
                    members);
        }

        private boolean requireRandomNumber() {
            for (CompiledResourcePattern naming : namingInfo) {
                if (naming.getKind() == SourceKind.RANDOM) {
                    return true;
                }
            }
            return false;
        }

        private FieldDeclaration createRandomHolder() {
            new IntOption().modify(index);
            return factory.newFieldDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .Private()
                        .Final()
                        .toAttributes(),
                    importer.toType(IntOption.class),
                    factory.newSimpleName(FIELD_RANDOM_HOLDER),
                    new TypeBuilder(factory, importer.toType(IntOption.class))
                        .newObject()
                        .toExpression());
        }

        private FieldDeclaration createRandomizer() {
            return factory.newFieldDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .Private()
                        .Final()
                        .toAttributes(),
                    importer.toType(Random.class),
                    factory.newSimpleName(FIELD_RANDOMIZER),
                    new TypeBuilder(factory, importer.toType(Random.class))
                        .newObject(Models.toLiteral(factory, 12345))
                        .toExpression());
        }

        private ConstructorDeclaration createConstructor() {
            List<Expression> arguments = Lists.create();
            for (CompiledResourcePattern naming : namingInfo) {
                arguments.add(new TypeBuilder(factory, t(FormatSpec.class))
                    .newObject(
                            new TypeBuilder(factory, t(Format.class))
                                .field(naming.getFormat().name())
                                .toExpression(),
                            naming.getArgument() == null
                                ? Models.toNullLiteral(factory)
                                : Models.toLiteral(factory, naming.getArgument()))
                    .toExpression());
            }
            List<Statement> statements = Lists.create();
            statements.add(factory.newSuperConstructorInvocation(arguments));
            return factory.newConstructorDeclaration(
                    new JavadocBuilder(factory)
                        .text("Creates a new instance.")
                        .toJavadoc(),
                    new AttributeBuilder(factory)
                        .Public()
                        .toAttributes(),
                    getClassName(),
                    Collections.<FormalParameterDeclaration>emptyList(),
                    statements);
        }

        private MethodDeclaration createSetMethod() {
            SimpleName raw = factory.newSimpleName("rawObject");
            SimpleName object = factory.newSimpleName("object");
            List<Statement> statements = Lists.create();
            statements.add(new ExpressionBuilder(factory, raw)
                .castTo(t(dataType.getType()))
                .toLocalVariableDeclaration(t(dataType.getType()), object));
            int position = 0;
            for (CompiledResourcePattern naming : namingInfo) {
                switch (naming.getKind()) {
                case NOTHING:
                    break;
                case PROPERTY: {
                    DataClass.Property property = naming.getTarget();
                    statements.add(new ExpressionBuilder(factory, factory.newThis())
                        .method("setProperty", Models.toLiteral(factory, position), property.createGetter(object))
                        .toStatement());
                    break;
                }
                case RANDOM: {
                    RandomNumber rand = naming.getRandomNumber();
                    statements.add(new ExpressionBuilder(factory, factory.newThis())
                        .field(FIELD_RANDOM_HOLDER)
                        .method("modify", new ExpressionBuilder(factory, factory.newThis())
                            .field(FIELD_RANDOMIZER)
                            .method(
                                    "nextInt",
                                    Models.toLiteral(factory, rand.getUpperBound() - rand.getLowerBound() + 1))
                            .apply(InfixOperator.PLUS, Models.toLiteral(factory, rand.getLowerBound()))
                            .toExpression())
                        .toStatement());
                    statements.add(new ExpressionBuilder(factory, factory.newThis())
                        .method("setProperty",
                                Models.toLiteral(factory, position),
                                new ExpressionBuilder(factory, factory.newThis())
                                    .field(FIELD_RANDOM_HOLDER)
                                    .toExpression())
                        .toStatement());
                    break;
                }
                default:
                    throw new AssertionError();
                }
                position++;
            }
            return factory.newMethodDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .annotation(t(Override.class))
                        .Public()
                        .toAttributes(),
                    t(void.class),
                    factory.newSimpleName("set"),
                    Collections.singletonList(factory.newFormalParameterDeclaration(t(Object.class), raw)),
                    statements);
        }

        private SimpleName getClassName() {
            return factory.newSimpleName(String.format("%s%04d", "Naming", index));
        }

        private Javadoc createJavadoc() {
            return new JavadocBuilder(factory)
                .text("A naming output \"{1}\" class for \"{0}\".", moduleId, outputName)
                .toJavadoc();
        }

        private Type t(java.lang.reflect.Type type, Type...typeArgs) {
            assert type != null;
            assert typeArgs != null;
            Type raw = importer.toType(type);
            if (typeArgs.length == 0) {
                return raw;
            }
            return factory.newParameterizedType(raw, Arrays.asList(typeArgs));
        }
    }
}
TOP

Related Classes of com.asakusafw.compiler.directio.emitter.NamingClassEmitter

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.