/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* 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 jetbrains.mps.generator.impl;
import jetbrains.mps.generator.GenerationCanceledException;
import jetbrains.mps.generator.impl.FastRuleFinder.BlockedReductionsData;
import jetbrains.mps.generator.runtime.GenerationException;
import jetbrains.mps.generator.runtime.TemplateExecutionEnvironment;
import jetbrains.mps.generator.runtime.TemplateWeavingRule;
import jetbrains.mps.generator.template.ITemplateGenerator;
import jetbrains.mps.generator.template.QueryExecutionContext;
import jetbrains.mps.smodel.FastNodeFinder;
import jetbrains.mps.smodel.FastNodeFinderManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.ArrayList;
import java.util.List;
/**
* Evgeny Gryaznov, Feb 15, 2010
*/
public class WeavingProcessor {
private final TemplateGenerator myGenerator;
private final List<ArmedWeavingRule> myReadyRules;
public WeavingProcessor(TemplateGenerator generator) {
myGenerator = generator;
myReadyRules = new ArrayList<ArmedWeavingRule>();
}
public void prepareWeavingRules(SModel inputModel) throws GenerationCanceledException, GenerationFailureException {
Iterable<TemplateWeavingRule> rules = myGenerator.getRuleManager().getWeaving_MappingRules();
myReadyRules.clear();
final BlockedReductionsData ruleBlocks = myGenerator.getBlockedReductionsData();
final FastNodeFinder nodeFinder = FastNodeFinderManager.get(inputModel);
for (TemplateWeavingRule rule : rules) {
boolean includeInheritors = rule.applyToInheritors();
for (SNode applicableNode : nodeFinder.getNodes(rule.getApplicableConcept(), includeInheritors)) {
if (ruleBlocks.isWeavingBlocked(applicableNode, rule)) {
continue;
}
QueryExecutionContext executionContext = myGenerator.getExecutionContext(applicableNode);
if (executionContext == null) {
continue;
}
TemplateExecutionEnvironment environment = new TemplateExecutionEnvironmentImpl(new TemplateProcessor(myGenerator), executionContext, new ReductionTrack(myGenerator.getBlockedReductionsData()));
DefaultTemplateContext context = new DefaultTemplateContext(environment, applicableNode, null);
if (executionContext.isApplicable(rule, context)) {
// if there are too many ArmedWeavingRule instances (i.e. a lot of applicable SNode),
// it's easy to refactor AWR to keep list of applicable nodes and to recreate TEE on demand
myReadyRules.add(new ArmedWeavingRule(rule, environment, applicableNode));
ruleBlocks.blockWeaving(applicableNode, rule);
}
}
}
}
public boolean hasWeavingRulesToApply() {
return !myReadyRules.isEmpty();
}
public void apply() throws GenerationFailureException, GenerationCanceledException {
for (ArmedWeavingRule rule : myReadyRules) {
if (rule.apply()) {
myGenerator.setChanged();
}
}
}
private static class ArmedWeavingRule {
@NotNull
private final TemplateWeavingRule myRule;
@NotNull
private final TemplateExecutionEnvironment myEnv;
@NotNull
private final SNode myApplicableNode;
public ArmedWeavingRule(@NotNull TemplateWeavingRule rule, @NotNull TemplateExecutionEnvironment env, @NotNull SNode applicableNode) {
myRule = rule;
myEnv = env;
myApplicableNode = applicableNode;
}
public boolean apply() throws GenerationFailureException, GenerationCanceledException {
try {
DefaultTemplateContext context = new DefaultTemplateContext(myEnv, myApplicableNode, null);
final QueryExecutionContext queryExecutor = myEnv.getQueryExecutor();
SNode outputContextNode = queryExecutor.getContextNode(myRule, context);
if (!checkContext(outputContextNode)) {
return false;
}
try {
queryExecutor.applyRule(myRule, context, outputContextNode);
} catch (DismissTopMappingRuleException e) {
myEnv.getLogger().error(myRule.getRuleNode(), "wrong template: dismiss in weaving rule is not supported", GeneratorUtil.describeInput(context));
} catch (TemplateProcessingFailureException e) {
myEnv.getLogger().error(myRule.getRuleNode(), "weaving rule: error processing template fragment", GeneratorUtil.describeInput(context));
}
} catch (GenerationCanceledException ex) {
throw ex;
} catch (GenerationFailureException ex) {
throw ex;
} catch (GenerationException e) {
myEnv.getLogger().error(myRule.getRuleNode(), "internal error: " + e.toString());
}
return true; // original code did myGenerator.setChanged once checkContext had passed.
}
private boolean checkContext(SNode contextNode) {
TemplateGenerator generator = myEnv.getGenerator();
if (contextNode == null) {
myEnv.getLogger().error(myRule.getRuleNode(), "weaving rule: cannot find context node", GeneratorUtil.describe(myApplicableNode, "input node"));
return false;
}
// Additional check - context should be generated from the same root
SNode contextRoot = contextNode.getContainingRoot();
SModel model = contextRoot.getModel();
if (model == null) {
return reportErrorIfStrict("bad context for weaving rule: no root for context " + contextNode);
}
if (model != generator.getOutputModel()) {
String msg = "Bad context for weaving rule: context node shall belong to output model.";
if (model == generator.getInputModel()) {
msg += "Context specified comes from the input model. You may use 'genContext.get copied output for' operation to obtain its output model counterpart.";
}
myEnv.getLogger().error(myRule.getRuleNode(), msg, GeneratorUtil.describe(myApplicableNode, "input node"));
return false;
}
SNode originalContextRoot = generator.getOriginalRootByGenerated(contextRoot);
if (originalContextRoot == null) {
return reportErrorIfStrict(String.format("bad context for weaving rule: %s is generated by 'create root' rule", contextRoot));
}
if (myApplicableNode.getModel() == null) return true;
SNode inputRoot = myApplicableNode.getContainingRoot();
if (originalContextRoot != inputRoot) {
String msg = "bad context for weaving rule: %s is generated from %s , while input node is from %s";
return reportErrorIfStrict(String.format(msg, contextRoot, originalContextRoot, inputRoot));
}
return true;
}
private boolean reportErrorIfStrict(String msg) {
ITemplateGenerator generator = myEnv.getGenerator();
if (generator.isStrict()) {
generator.getLogger().error(myRule.getRuleNode(), String.format("Strict generation mode failure: %s", msg));
return false;
} else {
generator.getLogger().warning(myRule.getRuleNode(), msg);
return true;
}
}
@Override
public String toString() {
return String.format("waving rule for: %s; node: %s", myRule.getApplicableConcept().getQualifiedName(), myApplicableNode);
}
}
}