/**
* 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.jobflow;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.asakusafw.compiler.common.Naming;
import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.flow.FlowCompilingEnvironment;
import com.asakusafw.compiler.flow.Location;
import com.asakusafw.runtime.stage.AbstractCleanupStageClient;
import com.asakusafw.runtime.stage.BaseStageClient;
import com.asakusafw.runtime.stage.StageConstants;
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.Expression;
import com.asakusafw.utils.java.model.syntax.FormalParameterDeclaration;
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.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.ImportBuilder;
import com.asakusafw.utils.java.model.util.JavadocBuilder;
import com.asakusafw.utils.java.model.util.Models;
/**
* Creates a subclass of {@link AbstractCleanupStageClient}.
* @since 0.2.6
*/
public class CleanupStageClientEmitter {
static final Logger LOG = LoggerFactory.getLogger(CleanupStageClientEmitter.class);
private final FlowCompilingEnvironment environment;
/**
* Creates a new instance.
* @param environment current compilation environment
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public CleanupStageClientEmitter(FlowCompilingEnvironment environment) {
Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
this.environment = environment;
}
/**
* Emits a new cleanup stage client.
* @return the compiled class info
* @throws IOException if faled to emit a class
*/
public CompiledStage emit() throws IOException {
LOG.debug("Generating cleanup stage client for {}", environment.getFlowId());
Engine engine = new Engine(environment);
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("Cleanup stage client for {} will be {}", environment.getFlowId(), name);
return new CompiledStage(name, Naming.getCleanupStageName());
}
private static class Engine {
private static final char PATH_SEPARATOR = '/';
private final FlowCompilingEnvironment environment;
private final ModelFactory factory;
private final ImportBuilder importer;
private final QualifiedName fqn;
Engine(FlowCompilingEnvironment environment) {
assert environment != null;
this.environment = environment;
this.factory = environment.getModelFactory();
this.fqn = (QualifiedName) Models.toName(factory, AbstractCleanupStageClient.IMPLEMENTATION);
this.importer = new ImportBuilder(
factory,
factory.newPackageDeclaration(fqn.getQualifier()),
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() {
importer.resolvePackageMember(fqn.getSimpleName());
List<TypeBodyDeclaration> members = Lists.create();
members.addAll(createIdMethods());
members.add(createStageOutputPath());
return factory.newClassDeclaration(
createJavadoc(),
new AttributeBuilder(factory)
.Public()
.Final()
.toAttributes(),
fqn.getSimpleName(),
Collections.<TypeParameterDeclaration>emptyList(),
t(AbstractCleanupStageClient.class),
Collections.<Type>emptyList(),
members);
}
private List<MethodDeclaration> createIdMethods() {
List<MethodDeclaration> results = Lists.create();
results.add(createValueMethod(
BaseStageClient.METHOD_BATCH_ID,
t(String.class),
Models.toLiteral(factory, environment.getBatchId())));
results.add(createValueMethod(
BaseStageClient.METHOD_FLOW_ID,
t(String.class),
Models.toLiteral(factory, environment.getFlowId())));
results.add(createValueMethod(
BaseStageClient.METHOD_STAGE_ID,
t(String.class),
Models.toLiteral(factory, Naming.getCleanupStageName())));
return results;
}
private MethodDeclaration createStageOutputPath() {
Location location = environment.getTargetLocation();
location = getCleanupTarget(location);
String path = location.toPath(PATH_SEPARATOR);
return createValueMethod(
AbstractCleanupStageClient.METHOD_CLEANUP_PATH,
t(String.class),
Models.toLiteral(factory, path));
}
private Location getCleanupTarget(Location location) {
Location candidate = location;
Location current = location;
while (current != null) {
String name = current.getName();
if (name.indexOf(StageConstants.EXPR_EXECUTION_ID) >= 0) {
candidate = current;
}
current = current.getParent();
}
return candidate;
}
private Javadoc createJavadoc() {
return new JavadocBuilder(factory)
.text("A cleanup stage client.")
.toJavadoc();
}
private MethodDeclaration createValueMethod(
String methodName,
Type returnType,
Expression expression) {
return factory.newMethodDeclaration(
null,
new AttributeBuilder(factory)
.annotation(t(Override.class))
.Protected()
.toAttributes(),
returnType,
factory.newSimpleName(methodName),
Collections.<FormalParameterDeclaration>emptyList(),
Collections.singletonList(factory.newReturnStatement(expression)));
}
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));
}
}
}