/* * 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.template; import jetbrains.mps.generator.impl.ExportsSessionContext; import jetbrains.mps.generator.impl.GeneratorUtil; import jetbrains.mps.generator.runtime.TemplateContext; import jetbrains.mps.textgen.trace.TracingUtil; import jetbrains.mps.util.annotation.ToRemove; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeReference; import org.jetbrains.mps.openapi.module.SRepository; import java.util.Collection; import java.util.List; /** * Context for operations of genContext parameter in generator's concept functions. This is what generated code of template queries (like input nodes query, * property and reference macro, etc) have access to. * Igor Alshannikov * Jul 21, 2008 */ public class TemplateQueryContext { private final SNodeReference myTemplateNode; protected TemplateContext myContext; private final ITemplateGenerator myGenerator; /** * Context for queries when an input node is not known yet (queries of an MC or a script). * @since 3.3 */ public TemplateQueryContext(@NotNull SNodeReference templateNode, @NotNull ITemplateGenerator generator) { myTemplateNode = templateNode; myContext = null; myGenerator = generator; } protected TemplateQueryContext(@NotNull SNodeReference templateNode, @NotNull TemplateContext context) { myContext = context; myTemplateNode = templateNode; myGenerator = context.getEnvironment().getGenerator(); } /** * Cons for internal/tests use, generally subclasses shall not call it. */ protected TemplateQueryContext() { myContext = null; myTemplateNode = null; myGenerator = null; } /** * 'node' mapping */ public SNode getNode() { return getInputNode(); } public SNode getInputNode() { return myContext == null ? null : myContext.getInput(); } public SNode getOutputNode() { return null; //used in ref macros } public SModel getInputModel() { return myGenerator.getInputModel(); } public boolean isDirty(SNode node) { return myGenerator.isDirty(node); } public SModel getOutputModel() { return myGenerator.getOutputModel(); } public SModel getOriginalInputModel() { return myGenerator.getGeneratorSessionContext().getOriginalInputModel(); } /** * 'generator' mapping */ public ITemplateGenerator getGenerator() { return myGenerator; } /** * Find out conditional root with a given ML, created from specified model * @param label generally shall not be null, as it's required in GenerationContextOp_GetOutputByLabel * @param inputModel can be null, which indicates current input model. Otherwise, a model root was created from * @return a node in a transient/checkpoint model if generator has any recorded. * @since 3.4 */ @Nullable public SNode getOutputNodeByMappingLabel(String label, @Nullable SModel inputModel) { if (!myGenerator.areMappingsAvailable()) { myGenerator.getLogger().error(getTemplateNodeRef(), "'get output by label' cannot be used here"); } return myGenerator.findOutputNode(inputModel, label); } public SNode getOutputNodeByInputNodeAndMappingLabel(SNode inputNode, String label) { if (inputNode == null) return null; if (!myGenerator.areMappingsAvailable()) { myGenerator.getLogger().error(getTemplateNodeRef(), "'get output by input and label' cannot be used here"); } return myGenerator.findOutputNodeByInputNodeAndMappingName(inputNode, label); } public List<SNode> getAllOutputNodesByInputNodeAndMappingLabel(SNode inputNode, String label) { if (inputNode == null) return null; if (!myGenerator.areMappingsAvailable()) { myGenerator.getLogger().error(getTemplateNodeRef(), "'get all output by input and label' cannot be used here"); } return myGenerator.findAllOutputNodesByInputNodeAndMappingName(inputNode, label); } public void registerMappingLabel(SNode inputNode, String mappingName, SNode outputNode) { // technically, we could do myGenerator.isStrict() && myGenerator.areMappingsAvailable() -> fail "no more labels once transformation is over" // but this would expose knowledge that areMappingsAvailable is meaningful only in strict mode. // Since we do not restrict registration of mapping labels e.g. in TEEImpl, I decided not to keep a check here myGenerator.registerMappingLabel(inputNode, mappingName, outputNode); } public SNode getCopiedOutputNodeForInputNode(SNode inputNode) { if (inputNode == null) return null; if (!myGenerator.areMappingsAvailable()) { myGenerator.getLogger().error(getTemplateNodeRef(), "'get copied node for input' cannot be used here"); } return myGenerator.findCopiedOutputNodeForInputNode(inputNode); } public SNode getPreviousInputNodeByMappingLabel(String label) { return myContext == null ? null : myContext.getNamedInput(label); } public SNode getOriginalCopiedInputNode(SNode node) { if (node == null) return null; SRepository repo = myGenerator.getGeneratorSessionContext().getRepository(); SNode result = TracingUtil.getInputNode(node, repo); return result != null ? result : node; } public String createUniqueName(String baseName, SNode contextNode) { return myGenerator.getGeneratorSessionContext().createUniqueName(baseName, contextNode, getInputNode()); } // user objects public Object putTransientObject(Object key, Object o) { myGenerator.getGeneratorSessionContext().putTransientObject(key, o); return o; } public Object getTransientObject(Object key) { return myGenerator.getGeneratorSessionContext().getTransientObject(key); } public Object getVariable(String name) { return myContext == null ? null : myContext.getVariable(name); } public Object getPatternVariable(String name) { return myContext == null ? null : myContext.getPatternVariable(name); } public Object getGenerationParameter(String name) { return myGenerator.getGeneratorSessionContext().getGenerationParameter(name); } public Object putStepObject(Object key, Object o) { myGenerator.getGeneratorSessionContext().putStepObject(key, o); return o; } public Object getStepObject(Object key) { return myGenerator.getGeneratorSessionContext().getStepObject(key); } public Object putSessionObject(Object key, Object o) { myGenerator.getGeneratorSessionContext().putSessionObject(key, o); return o; } public Object getSessionObject(Object key) { return myGenerator.getGeneratorSessionContext().getSessionObject(key); } public SNode getOutputNodeProxy(SNode inputNode, String exportLabelName) { if (inputNode == null) { showErrorMessage(null, String.format("Attempt to find proxy for non-existent node. Label %s, model %s", exportLabelName, myGenerator.getInputModel().toString())); return null; } final ExportsSessionContext exports = myGenerator.getGeneratorSessionContext().getExports(); final Collection<SNode> exportProxies = exports.find(exportLabelName, getInputModel(), inputNode); if (exportProxies.isEmpty()) { return null; } if (exportProxies.size() > 1) { showErrorMessage(inputNode, String.format("There are %d known exports with label %s for input node %s", exportProxies.size(), exportLabelName, inputNode)); } return exportProxies.iterator().next(); } public void showInformationMessage(SNode node, String message) { myGenerator.getLogger().info(node == null ? getTemplateNodeRef() : node.getReference(), message); } public void showWarningMessage(SNode node, String message) { myGenerator.getLogger().warning(node == null ? getTemplateNodeRef() : node.getReference(), message); } public void showErrorMessage(SNode node, String message) { SNode inputNode = (node != null) ? node : getInputNode(); SNodeReference tn = getTemplateNodeRef(); SNodeReference rnr = getRuleNode(); myGenerator.getLogger().error(rnr == null ? tn : rnr, message, GeneratorUtil.describeIfExists(inputNode, "input node"), GeneratorUtil.describeIfExists(tn, "template node")); } /** * Node in template model most close to the query being evaluated. For macro nodes, however * shall point to macro's parent node (genContext.templateNode op contract) * @deprecated doesn't make sense for generated templates. Switch to {@link #getTemplateReference()} * */ @Deprecated @ToRemove(version = 3.4) public SNode getTemplateNode() { SNodeReference tnr = getTemplateNodeRef(); SRepository repo = myGenerator.getGeneratorSessionContext().getRepository(); return tnr == null ? null : tnr.resolve(repo); } /** * @return context template node where the query is evaluated, if known. */ @Nullable public final SNodeReference getTemplateReference() { return getTemplateNodeRef(); } /** * @return context template node where the query is evaluated */ @Nullable protected SNodeReference getTemplateNodeRef() { return myTemplateNode; } /** * @return context rule, where query is being evaluated, if available */ @Nullable protected SNodeReference getRuleNode() { return null; } }