/*
* Copyright 2003-2017 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.query;
import jetbrains.mps.generator.impl.GenerationFailureException;
import jetbrains.mps.generator.template.CreateRootRuleContext;
import jetbrains.mps.generator.template.DropAttributeRuleContext;
import jetbrains.mps.generator.template.DropRootRuleContext;
import jetbrains.mps.generator.template.IfMacroContext;
import jetbrains.mps.generator.template.InlineSwitchCaseContext;
import jetbrains.mps.generator.template.InsertMacroContext;
import jetbrains.mps.generator.template.MapRootRuleContext;
import jetbrains.mps.generator.template.MapSrcMacroContext;
import jetbrains.mps.generator.template.MapSrcMacroPostProcContext;
import jetbrains.mps.generator.template.MappingScriptContext;
import jetbrains.mps.generator.template.PatternRuleContext;
import jetbrains.mps.generator.template.PropertyMacroContext;
import jetbrains.mps.generator.template.ReductionRuleQueryContext;
import jetbrains.mps.generator.template.ReferenceMacroContext;
import jetbrains.mps.generator.template.SourceSubstituteMacroNodeContext;
import jetbrains.mps.generator.template.SourceSubstituteMacroNodesContext;
import jetbrains.mps.generator.template.TemplateArgumentContext;
import jetbrains.mps.generator.template.TemplateQueryContext;
import jetbrains.mps.generator.template.TemplateVarContext;
import jetbrains.mps.generator.template.WeavingAnchorContext;
import jetbrains.mps.generator.template.WeavingMappingRuleContext;
import jetbrains.mps.lang.pattern.GeneratedMatchingPattern;
import jetbrains.mps.util.annotation.ToRemove;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import java.util.Collection;
import java.util.Collections;
/**
* This is a base implementation of {@link jetbrains.mps.generator.impl.query.GeneratorQueryProvider} which generated
* class with queries shall extend to protect itself from future modifications of the interface.
*
* @author Artem Tikhomirov
*/
public abstract class QueryProviderBase implements GeneratorQueryProvider {
protected QueryProviderBase() {
// this cons is invoked from previous version of QueriesGenerated that implements GeneratorQueryProvider
}
protected QueryProviderBase(int versionNowUnused) {
// this one is invoked from newly generated implementations to indicate new methods were generated
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public CreateRootCondition getCreateRootRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public CreateRootCondition getCreateRootRuleCondition(@NotNull QueryKey identity) {
return getCreateRootRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public MapRootRuleCondition getMapRootRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public MapRootRuleCondition getMapRootRuleCondition(@NotNull QueryKey identity) {
return getMapRootRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public ReductionRuleCondition getReductionRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public ReductionRuleCondition getReductionRuleCondition(@NotNull QueryKey identity) {
return getReductionRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public PatternRuleQuery getPatternRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public PatternRuleQuery getPatternRuleCondition(@NotNull QueryKey identity) {
return getPatternRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public DropRuleCondition getDropRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public DropRuleCondition getDropRuleCondition(@NotNull QueryKey identity) {
return getDropRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public DropAttributeRuleCondition getDropAttributeRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public DropAttributeRuleCondition getDropAttributeRuleCondition(@NotNull QueryKey identity) {
return getDropAttributeRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public WeaveRuleCondition getWeaveRuleCondition(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public WeaveRuleCondition getWeaveRuleCondition(@NotNull QueryKey identity) {
return getWeaveRuleCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public WeaveRuleQuery getWeaveRuleQuery(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public WeaveRuleQuery getWeaveRuleQuery(@NotNull QueryKey identity) {
return getWeaveRuleQuery(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public WeaveAnchorQuery getWeaveAnchorQuery(@NotNull SNode rule) {
return new Defaults();
}
@NotNull
@Override
public WeaveAnchorQuery getWeaveAnchorQuery(@NotNull QueryKey identity) {
return getWeaveAnchorQuery(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public ScriptCodeBlock getScriptCodeBlock(@NotNull SNode script) {
return new Defaults();
}
@NotNull
@Override
public ScriptCodeBlock getScriptCodeBlock(@NotNull QueryKey identity) {
return getScriptCodeBlock(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public MapConfigurationCondition getMapConfigurationCondition(@NotNull SNode mapCfg) {
return new Defaults();
}
@NotNull
@Override
public MapConfigurationCondition getMapConfigurationCondition(@NotNull QueryKey identity) {
return getMapConfigurationCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public SourceNodeQuery getSourceNodeQuery(@NotNull SNode query) {
return new Defaults();
}
@NotNull
@Override
public SourceNodeQuery getSourceNodeQuery(@NotNull QueryKey identity) {
return getSourceNodeQuery(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public SourceNodesQuery getSourceNodesQuery(@NotNull SNode query) {
return new Defaults();
}
@NotNull
@Override
public SourceNodesQuery getSourceNodesQuery(@NotNull QueryKey identity) {
return getSourceNodesQuery(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public PropertyValueQuery getPropertyValueQuery(@NotNull SNode propertyMacro) {
// XXX propertyMacro.getNodeId() is wrong identity for PM's query function, we shall rather use
// RuleUtil.getPropertyMacro_ValueFunction(propertyMacro).getNodeId(), but as long as it's 'fail-only' implementation, who cares?
return new PropertyQuery(new QueryKeyImpl(propertyMacro.getReference(), propertyMacro.getNodeId()));
}
@NotNull
@Override
public PropertyValueQuery getPropertyValueQuery(@NotNull QueryKey identity) {
return getPropertyValueQuery(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public IfMacroCondition getIfMacroCondition(@NotNull SNode ifMacro) {
return new Missing(ifMacro);
}
@NotNull
@Override
public IfMacroCondition getIfMacroCondition(@NotNull QueryKey identity) {
return getIfMacroCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@ToRemove(version = 2017.1)
@Override
public InlineSwitchCaseCondition getInlineSwitchCaseCondition(@NotNull SNode caseNode) {
return new Missing(caseNode);
}
@NotNull
@Override
public InlineSwitchCaseCondition getInlineSwitchCaseCondition(@NotNull QueryKey identity) {
return getInlineSwitchCaseCondition(((QueryKeyImpl) identity).getAPITransitionNode());
}
@NotNull
@Override
public ReferenceTargetQuery getReferenceTargetQuery(@NotNull QueryKey identity) {
return new RefQuery(identity);
}
@NotNull
@Override
public CallArgumentQuery getTemplateCallArgumentQuery(@NotNull QueryKey identity) {
// DefaultQueryExecutionContext used to evaluate to null if no method was found.
// It is reasonable for scenarios like bootstrap of the generator itself (e.g. to generate a new CALL inside QueriesGenerate)
// but for any other generator there's no reason to continue generation if QG is broken that's why we fail here with Missing.
// For lang.generator generator, we handle missing methods in ReflectiveQueryProvider, and unless we decide to switch to non-reflective
// QG in lang.generator itself, we shall not bother. QPB subclass delegates to super (i.e. this method) when it has been asked for non-existent query.
return new Missing(identity);
}
@NotNull
@Override
public VariableValueQuery getVariableValueQuery(@NotNull QueryKey identity) {
// Same as above, no reason to default to null.
return new Missing(identity);
}
@NotNull
@Override
public InsertMacroQuery getInsertMacroQuery(@NotNull QueryKey identity) {
// used to evaluate to null if missing in DQEC. Why not do the same here?
return new Missing(identity);
}
@NotNull
@Override
public MapNodeQuery getMapNodeQuery(@NotNull QueryKey identity) {
return new Missing(identity);
}
@NotNull
@Override
public MapPostProcessor getMapPostProcessor(@NotNull QueryKey identity) {
return new Missing(identity);
}
/**
* Reasonable default values for all conditions and queries.
* Note, these default values represent the case when no condition/query was specified. There's
* another set of defaults for cases when condition failed to evaluate ({@link jetbrains.mps.generator.impl.interpreted.ReflectiveQueryProvider.Impl}.
*/
public static class Defaults implements CreateRootCondition, MapRootRuleCondition, ReductionRuleCondition, PatternRuleQuery,
DropRuleCondition, WeaveRuleCondition, WeaveRuleQuery, ScriptCodeBlock, MapConfigurationCondition, SourceNodeQuery, SourceNodesQuery,
WeaveAnchorQuery, DropAttributeRuleCondition {
@Override
public boolean check(@NotNull CreateRootRuleContext ctx) {
return true;
}
@Override
public boolean check(@NotNull MapRootRuleContext ctx) {
return true;
}
@Override
public boolean check(@NotNull DropRootRuleContext ctx) {
return true;
}
@Override
public boolean check(@NotNull DropAttributeRuleContext ctx) {
return true;
}
@Override
public boolean check(@NotNull ReductionRuleQueryContext ctx) {
return true;
}
@Override
public boolean check(@NotNull WeavingMappingRuleContext ctx) {
return true;
}
@Override
public SNode contextNode(WeavingMappingRuleContext ctx) {
return null;
}
@Override
public GeneratedMatchingPattern pattern(@NotNull PatternRuleContext ctx) {
return null;
}
@Override
public boolean check(@NotNull TemplateQueryContext ctx) {
return true;
}
@Override
public void invoke(MappingScriptContext ctx) {
}
@Nullable
@Override
public SNode evaluate(@NotNull SourceSubstituteMacroNodeContext context) throws GenerationFailureException {
return context.getInputNode(); // use input node if no query is specified
}
@NotNull
@Override
public Collection<SNode> evaluate(@NotNull SourceSubstituteMacroNodesContext context) throws GenerationFailureException {
return Collections.emptyList();
}
@Override
@Nullable
public SNode anchorNode(WeavingAnchorContext ctx) throws GenerationFailureException {
// null is legitimate value, indicates 'just append'
return null;
}
}
private static class PropertyQuery implements PropertyValueQuery {
private final QueryKey myQueryKey;
PropertyQuery(QueryKey identity) {
myQueryKey = identity;
}
@NotNull
@Override
public SProperty getProperty() {
throw new IllegalStateException("evaluate() shall had failed with exception");
}
@Override
public Object getTemplateValue() {
return null;
}
@NotNull
@Override
public SNodeReference getMacro() {
// FIXME getTemplateNode is not necessarily macro node, but we don't need this method anyway, and will remove it soon.
// OTOH, may utilize myQueryKey.getTemplateNode() in evaluate() to log location
return myQueryKey.getTemplateNode();
}
@Nullable
@Override
public Object evaluate(@NotNull PropertyMacroContext context) throws GenerationFailureException {
// XXX why not
// ctx.getGenerator().getLogger().error(myMacro,...); and some reasonable message?
context.showErrorMessage(null, "cannot evaluate property macro");
throw new GenerationFailureException("cannot evaluate property macro");
}
}
private static class RefQuery implements ReferenceTargetQuery {
private final QueryKey myIdentity;
RefQuery(QueryKey identity) {
myIdentity = identity;
}
@Nullable
@Override
public Object evaluate(@NotNull ReferenceMacroContext ctx) throws GenerationFailureException {
ctx.getGenerator().getLogger().error(myIdentity.getTemplateNode(), "missing reference macro");
throw new GenerationFailureException("missing reference macro");
}
}
// unlike Defaults, complains about missing query
public static class Missing implements IfMacroCondition, InlineSwitchCaseCondition, CallArgumentQuery, VariableValueQuery, InsertMacroQuery, MapNodeQuery, MapPostProcessor {
private final SNodeReference myTemplate;
public Missing(QueryKey identity) {
myTemplate = identity.getTemplateNode();
}
public Missing(SNode identity) {
myTemplate = identity.getReference();
}
@Override
public boolean check(@NotNull IfMacroContext context) throws GenerationFailureException {
String msg = "no required condition for IF macro";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Override
public boolean check(@NotNull InlineSwitchCaseContext context) throws GenerationFailureException {
// here comes the logic that used to live in DefaultQueryExecutionContext
String msg = "condition required for case in inline switch";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Nullable
@Override
public Object evaluate(@NotNull TemplateArgumentContext context) throws GenerationFailureException {
String msg = "call argument query is missing";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Nullable
@Override
public Object evaluate(@NotNull TemplateVarContext context) throws GenerationFailureException {
String msg = "variable value query is missing";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Nullable
@Override
public SNode evaluate(@NotNull InsertMacroContext context) throws GenerationFailureException {
String msg = "insert node query is missing";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Nullable
@Override
public SNode evaluate(@NotNull MapSrcMacroContext context) throws GenerationFailureException {
String msg = "mapping function is missing";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
@Override
public void invoke(@NotNull MapSrcMacroPostProcContext context) throws GenerationFailureException {
String msg = "post-processing function is missing";
reportError(context, msg);
throw new GenerationFailureException(msg);
}
private void reportError(TemplateQueryContext context, String message) throws GenerationFailureException {
context.getGenerator().getLogger().error(myTemplate, message);
}
}
}