/** * Copyright 2011-2017 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.operator.flow; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.annotation.Generated; import com.asakusafw.compiler.common.JavaName; import com.asakusafw.compiler.common.NameGenerator; import com.asakusafw.compiler.operator.OperatorCompilingEnvironment; import com.asakusafw.compiler.operator.OperatorPortDeclaration; import com.asakusafw.compiler.operator.util.GeneratorUtil; import com.asakusafw.utils.java.jsr269.bridge.Jsr269; 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.MethodDeclaration; import com.asakusafw.utils.java.model.syntax.ModelFactory; import com.asakusafw.utils.java.model.syntax.ModelKind; 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.Type; import com.asakusafw.utils.java.model.syntax.TypeBodyDeclaration; import com.asakusafw.utils.java.model.syntax.TypeDeclaration; 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; import com.asakusafw.vocabulary.flow.FlowDescription; import com.asakusafw.vocabulary.flow.FlowPart; import com.asakusafw.vocabulary.flow.Operator; import com.asakusafw.vocabulary.flow.graph.FlowElementResolver; import com.asakusafw.vocabulary.flow.graph.FlowPartDescription; import com.asakusafw.vocabulary.flow.graph.Inline; import com.asakusafw.vocabulary.operator.OperatorFactory; import com.asakusafw.vocabulary.operator.OperatorInfo; /** * Generates operator factory classes for flow-part classes. */ public class FlowFactoryClassGenerator { /** * The field name for holding {@link FlowElementResolver}. */ static final String RESOLVER_FIELD_NAME = "$"; //$NON-NLS-1$ private final ModelFactory factory; private final ImportBuilder importer; private final FlowPartClass flowClass; private final GeneratorUtil util; private final OperatorCompilingEnvironment environment; /** * Creates a new instance. * @param environment the current environment * @param factory the Java DOM factory * @param importer the import declaration builder * @param flowClass the target flow-part class * @throws IllegalArgumentException if the parameters are {@code null} */ public FlowFactoryClassGenerator( OperatorCompilingEnvironment environment, ModelFactory factory, ImportBuilder importer, FlowPartClass flowClass) { this.environment = environment; this.factory = factory; this.importer = importer; this.flowClass = flowClass; this.util = new GeneratorUtil(environment, factory, importer); } /** * Generates a declaration of support class. * @return the generated type declaration */ public TypeDeclaration generate() { // escape the current namesapce first SimpleName name = getClassName(); importer.resolvePackageMember(Models.append( factory, name, getObjectClassName())); return factory.newClassDeclaration( new JavadocBuilder(factory) .text(Messages.getString("FlowFactoryClassGenerator.javadocClass"), //$NON-NLS-1$ flowClass.getElement().getSimpleName()) .seeType(new Jsr269(factory).convert(environment.getErasure(flowClass.getElement().asType()))) .toJavadoc(), new AttributeBuilder(factory) .annotation(util.t(Generated.class), util.v("{0}:{1}", //$NON-NLS-1$ FlowOperatorCompiler.class.getSimpleName(), FlowOperatorCompiler.VERSION)) .annotation(util.t(OperatorFactory.class), factory.newClassLiteral(util.t(flowClass.getElement()))) .Public() .toAttributes(), name, Collections.emptyList(), null, Collections.emptyList(), createMembers()); } private List<TypeBodyDeclaration> createMembers() { List<TypeBodyDeclaration> results = new ArrayList<>(); TypeDeclaration objectClass = createObjectClass(); results.add(objectClass); NamedType objectType = (NamedType) importer.resolvePackageMember( Models.append(factory, getClassName(), objectClass.getName())); MethodDeclaration factoryMethod = createFactoryMethod(objectType); if (factoryMethod != null) { results.add(factoryMethod); } return results; } private TypeDeclaration createObjectClass() { SimpleName name = getObjectClassName(); NamedType objectType = (NamedType) importer.resolvePackageMember( Models.append(factory, getClassName(), name)); List<TypeBodyDeclaration> members = createObjectMembers(objectType); return factory.newClassDeclaration( new JavadocBuilder(factory) .inline(flowClass.getDocumentation()) .seeType(new Jsr269(factory).convert(environment.getErasure(flowClass.getElement().asType()))) .toJavadoc(), new AttributeBuilder(factory) .Public() .Static() .Final() .toAttributes(), name, util.toTypeParameters(flowClass.getElement()), null, Collections.singletonList(util.t(Operator.class)), members); } private SimpleName getObjectClassName() { return factory.newSimpleName( JavaName.of(flowClass.getElement().getSimpleName().toString()).toTypeName()); } private List<TypeBodyDeclaration> createObjectMembers(NamedType objectType) { assert objectType != null; NameGenerator names = new NameGenerator(factory); List<TypeBodyDeclaration> results = new ArrayList<>(); results.add(createResolverField()); for (OperatorPortDeclaration var : flowClass.getOutputPorts()) { results.add(createObjectOutputField(var, names)); } results.add(createObjectConstructor(objectType, names)); results.add(createRenamer(objectType, names)); results.add(createInliner(objectType, names)); return results; } private FieldDeclaration createResolverField() { return factory.newFieldDeclaration( null, new AttributeBuilder(factory) .Private() .Final() .toAttributes(), util.t(FlowElementResolver.class), factory.newSimpleName(RESOLVER_FIELD_NAME), null); } private MethodDeclaration createRenamer(NamedType objectType, NameGenerator names) { assert objectType != null; assert names != null; SimpleName newName = names.create("newName"); //$NON-NLS-1$ return factory.newMethodDeclaration( new JavadocBuilder(factory) .text(Messages.getString("FlowFactoryClassGenerator.javadocSetName")) //$NON-NLS-1$ .param(newName) .text(Messages.getString("FlowFactoryClassGenerator.javadocSetNameParameter")) //$NON-NLS-1$ .returns() .text(Messages.getString("FlowFactoryClassGenerator.javadocSetNameReturn")) //$NON-NLS-1$ .exception(util.t(IllegalArgumentException.class)) .text(Messages.getString("FlowFactoryClassGenerator.javadocSetNameNullParameter")) //$NON-NLS-1$ .toJavadoc(), new AttributeBuilder(factory) .Public() .toAttributes(), getType(objectType), factory.newSimpleName("as"), //$NON-NLS-1$ Collections.singletonList(factory.newFormalParameterDeclaration( util.t(String.class), newName)), Arrays.asList(new Statement[] { new ExpressionBuilder(factory, factory.newThis()) .field(RESOLVER_FIELD_NAME) .method("setName", newName) //$NON-NLS-1$ .toStatement(), new ExpressionBuilder(factory, factory.newThis()) .toReturnStatement(), })); } private MethodDeclaration createInliner(NamedType objectType, NameGenerator names) { assert objectType != null; assert names != null; SimpleName optimize = names.create("optimize"); //$NON-NLS-1$ return factory.newMethodDeclaration( new JavadocBuilder(factory) .text(Messages.getString("FlowFactoryClassGenerator.javadocInline")) //$NON-NLS-1$ .param(optimize) .text(Messages.getString("FlowFactoryClassGenerator.javadocInlineParameter")) //$NON-NLS-1$ .returns() .text(Messages.getString("FlowFactoryClassGenerator.javadocInlineReturn")) //$NON-NLS-1$ .toJavadoc(), new AttributeBuilder(factory) .Public() .toAttributes(), getType(objectType), factory.newSimpleName("inlined"), //$NON-NLS-1$ Collections.singletonList(factory.newFormalParameterDeclaration( util.t(boolean.class), optimize)), Arrays.asList(new Statement[] { new ExpressionBuilder(factory, factory.newThis()) .field(RESOLVER_FIELD_NAME) .method("getElement") //$NON-NLS-1$ .method("override", factory.newConditionalExpression(//$NON-NLS-1$ optimize, new TypeBuilder(factory, util.t(Inline.class)) .field(Inline.FORCE_AGGREGATE.name()) .toExpression(), new TypeBuilder(factory, util.t(Inline.class)) .field(Inline.KEEP_SEGREGATED.name()) .toExpression())) .toStatement(), new ExpressionBuilder(factory, factory.newThis()) .toReturnStatement(), })); } private TypeBodyDeclaration createObjectOutputField(OperatorPortDeclaration var, NameGenerator names) { assert var != null; assert names != null; return factory.newFieldDeclaration( new JavadocBuilder(factory) .inline(var.getDocumentation()) .toJavadoc(), new AttributeBuilder(factory) .Public() .Final() .toAttributes(), util.toSourceType(var.getType().getRepresentation()), factory.newSimpleName(names.reserve(var.getName())), null); } private TypeBodyDeclaration createObjectConstructor(NamedType objectType, NameGenerator names) { assert objectType != null; List<FormalParameterDeclaration> parameters = createParametersForConstructor(names); List<Statement> statements = createBodyForConstructor(parameters, names); return factory.newConstructorDeclaration( null, new AttributeBuilder(factory) // Default Package Access .toAttributes(), objectType.getName().getLastSegment(), parameters, statements); } private List<FormalParameterDeclaration> createParametersForConstructor(NameGenerator names) { List<FormalParameterDeclaration> parameters = new ArrayList<>(); for (OperatorPortDeclaration var : flowClass.getInputPorts()) { SimpleName name = factory.newSimpleName(names.reserve(var.getName())); parameters.add(factory.newFormalParameterDeclaration( util.toSourceType(var.getType().getRepresentation()), name)); } for (OperatorPortDeclaration var : flowClass.getParameters()) { SimpleName name = factory.newSimpleName(names.reserve(var.getName())); parameters.add(factory.newFormalParameterDeclaration( util.t(var.getType().getRepresentation()), name)); } return parameters; } private List<Statement> createBodyForConstructor( List<FormalParameterDeclaration> parameters, NameGenerator names) { assert parameters != null; List<Statement> statements = new ArrayList<>(); SimpleName builderName = names.create("builder"); //$NON-NLS-1$ statements.add(new TypeBuilder(factory, util.t(FlowPartDescription.Builder.class)) .newObject(factory.newClassLiteral(util.t(flowClass.getElement()))) .toLocalVariableDeclaration(util.t(FlowPartDescription.Builder.class), builderName)); Expression[] arguments = new Expression[ flowClass.getInputPorts().size() + flowClass.getOutputPorts().size() + flowClass.getParameters().size() ]; for (OperatorPortDeclaration var : flowClass.getInputPorts()) { SimpleName name = names.create(var.getName()); statements.add(new ExpressionBuilder(factory, builderName) .method("addInput", //$NON-NLS-1$ util.v(var.getName()), factory.newSimpleName(var.getType().getReference())) .toLocalVariableDeclaration( util.toInType(var.getType().getRepresentation()), name)); arguments[var.getParameterPosition()] = name; } for (OperatorPortDeclaration var : flowClass.getOutputPorts()) { SimpleName name = names.create(var.getName()); Expression type = toExpression(var); assert type != null; statements.add(new ExpressionBuilder(factory, builderName) .method("addOutput", util.v(var.getName()), type) //$NON-NLS-1$ .toLocalVariableDeclaration( util.toOutType(var.getType().getRepresentation()), name)); arguments[var.getParameterPosition()] = name; } for (OperatorPortDeclaration var : flowClass.getParameters()) { Expression type = toExpression(var); SimpleName name = factory.newSimpleName(var.getName()); statements.add(new ExpressionBuilder(factory, builderName) .method("addParameter", //$NON-NLS-1$ util.v(var.getName()), type, name) .toStatement()); arguments[var.getParameterPosition()] = name; } SimpleName descName = names.create("desc"); //$NON-NLS-1$ statements.add(new TypeBuilder(factory, getType(util.t(flowClass.getElement()))) .newObject(arguments) .toLocalVariableDeclaration(util.t(FlowDescription.class), descName)); Expression resolver = new ExpressionBuilder(factory, factory.newThis()) .field(RESOLVER_FIELD_NAME) .toExpression(); statements.add(new ExpressionBuilder(factory, resolver) .assignFrom(new ExpressionBuilder(factory, builderName) .method("toResolver", descName) //$NON-NLS-1$ .toExpression()) .toStatement()); for (OperatorPortDeclaration var : flowClass.getInputPorts()) { statements.add(new ExpressionBuilder(factory, resolver) .method("resolveInput", //$NON-NLS-1$ util.v(var.getName()), factory.newSimpleName(var.getName())) .toStatement()); } for (OperatorPortDeclaration var : flowClass.getOutputPorts()) { statements.add(new ExpressionBuilder(factory, factory.newThis()) .field(var.getName()) .assignFrom(new ExpressionBuilder(factory, resolver) .method("resolveOutput", util.v(var.getName())) //$NON-NLS-1$ .toExpression()) .toStatement()); } return statements; } private Expression toExpression(OperatorPortDeclaration var) throws AssertionError { Expression type; switch (var.getType().getKind()) { case DIRECT: type = factory.newClassLiteral(util.t(environment.getErasure(var.getType().getDirect()))); break; case REFERENCE: type = factory.newSimpleName(var.getType().getReference()); break; default: throw new AssertionError(var.getType().getKind()); } return type; } private MethodDeclaration createFactoryMethod(NamedType objectType) { assert objectType != null; JavadocBuilder javadoc = new JavadocBuilder(factory); javadoc.inline(flowClass.getDocumentation()); List<FormalParameterDeclaration> parameters = new ArrayList<>(); List<Expression> arguments = new ArrayList<>(); List<Expression> inputMetaData = new ArrayList<>(); for (OperatorPortDeclaration var : flowClass.getInputPorts()) { SimpleName name = factory.newSimpleName(var.getName()); javadoc.param(name).inline(var.getDocumentation()); parameters.add(util.toFactoryMethodInput(var, name)); inputMetaData.add(util.toMetaData(var, arguments.size())); arguments.add(name); } List<Expression> outputMetaData = new ArrayList<>(); for (OperatorPortDeclaration var : flowClass.getOutputPorts()) { outputMetaData.add(util.toMetaData(var, -1)); } List<Expression> parameterMetaData = new ArrayList<>(); for (OperatorPortDeclaration var : flowClass.getParameters()) { SimpleName name = factory.newSimpleName(var.getName()); javadoc.param(name).inline(var.getDocumentation()); parameters.add(factory.newFormalParameterDeclaration( util.t(var.getType().getRepresentation()), name)); parameterMetaData.add(util.toMetaData(var, arguments.size())); arguments.add(name); } Type type = getType(objectType); javadoc.returns().text(Messages.getString("FlowFactoryClassGenerator.javadocFactoryReturn")); //$NON-NLS-1$ javadoc.seeType(util.t(flowClass.getElement())); return factory.newMethodDeclaration( javadoc.toJavadoc(), new AttributeBuilder(factory) .annotation(util.t(OperatorInfo.class), "kind", factory.newClassLiteral(util.t(FlowPart.class)), //$NON-NLS-1$ "input", factory.newArrayInitializer(inputMetaData), //$NON-NLS-1$ "output", factory.newArrayInitializer(outputMetaData), //$NON-NLS-1$ "parameter", factory.newArrayInitializer(parameterMetaData)) //$NON-NLS-1$ .Public() .toAttributes(), util.toTypeParameters(flowClass.getElement()), type, factory.newSimpleName("create"), //$NON-NLS-1$ parameters, 0, Collections.emptyList(), factory.newBlock( new TypeBuilder(factory, type) .newObject(arguments) .toReturnStatement())); } private Type getType(Type objectType) { assert objectType != null; assert objectType.getModelKind() != ModelKind.PARAMETERIZED_TYPE; Type type; if (flowClass.getElement().getTypeParameters().isEmpty()) { type = objectType; } else { type = new TypeBuilder(factory, objectType) .parameterize(util.toTypeVariables(flowClass.getElement())) .toType(); } return type; } private SimpleName getClassName() { return util.getFactoryName(flowClass.getElement()); } }