/**
* 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.batch;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.utils.graph.Graph;
import com.asakusafw.utils.graph.Graphs;
import com.asakusafw.vocabulary.batch.BatchDescription;
import com.asakusafw.vocabulary.batch.Work;
import com.asakusafw.vocabulary.batch.WorkDescription;
/**
* Compiles batch classes described in the batch DSL.
*/
public class BatchCompiler {
private final BatchCompilingEnvironment environment;
/**
* Creates a new instance.
* @param configuration the compiler settings
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public BatchCompiler(BatchCompilerConfiguration configuration) {
Precondition.checkMustNotBeNull(configuration, "configuration"); //$NON-NLS-1$
this.environment = new BatchCompilingEnvironment(configuration).bless();
}
/**
* Compiles the target batch description.
* @param description the target batch description
* @return the compiled result
* @throws IOException if failed to compile
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public Workflow compile(BatchDescription description) throws IOException {
Precondition.checkMustNotBeNull(description, "description"); //$NON-NLS-1$
Workflow workflow = createWorkflow(description);
processUnits(workflow.getGraph().getNodeSet());
if (environment.hasError()) {
throw new IOException(MessageFormat.format(
Messages.getString("BatchCompiler.errorFailedToAnalyze"), //$NON-NLS-1$
environment.getErrorMessage()));
}
processWorkflow(workflow);
if (environment.hasError()) {
throw new IOException(MessageFormat.format(
Messages.getString("BatchCompiler.errorFailedToEmit"), //$NON-NLS-1$
environment.getErrorMessage()));
}
return workflow;
}
private void processWorkflow(Workflow workflow) throws IOException {
assert workflow != null;
Set<WorkDescription> descriptions = new HashSet<>();
for (Workflow.Unit unit : workflow.getGraph().getNodeSet()) {
descriptions.add(unit.getDescription());
}
WorkflowProcessor.Repository repo = environment.getWorkflows();
Set<WorkflowProcessor> procs = repo.findWorkflowProcessors(descriptions);
for (WorkflowProcessor proc : procs) {
proc.process(workflow);
}
}
private void processUnits(Set<Workflow.Unit> units) throws IOException {
assert units != null;
WorkflowProcessor.Repository repo = environment.getWorkflows();
for (Workflow.Unit unit : units) {
WorkDescriptionProcessor<?> proc = repo.findDescriptionProcessor(unit.getDescription());
if (proc == null) {
environment.error(Messages.getString("BatchCompiler.errorMissingProcessor"), //$NON-NLS-1$
unit.getClass().getName());
continue;
}
processUnit(unit, proc);
}
}
private <T extends WorkDescription> void processUnit(
Workflow.Unit unit,
WorkDescriptionProcessor<T> proc) throws IOException {
assert unit != null;
assert proc != null;
assert proc.getTargetType().isInstance(unit.getDescription());
T desc = proc.getTargetType().cast(unit.getDescription());
Object result = proc.process(desc);
unit.setProcessed(result);
}
private Workflow createWorkflow(BatchDescription description) {
assert description != null;
Collection<Work> works = description.getWorks();
Map<Work, Workflow.Unit> units = new HashMap<>();
for (Work work : works) {
units.put(work, new Workflow.Unit(work.getDescription()));
}
Graph<Workflow.Unit> graph = Graphs.newInstance();
for (Map.Entry<Work, Workflow.Unit> entry : units.entrySet()) {
Workflow.Unit unit = entry.getValue();
graph.addNode(unit);
for (Work dependency : entry.getKey().getDependencies()) {
Workflow.Unit predecessor = units.get(dependency);
assert predecessor != null;
graph.addEdge(unit, predecessor);
}
}
Workflow workflow = new Workflow(description, graph);
return workflow;
}
}