/* * Copyright 2003-2015 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.reference; import jetbrains.mps.generator.impl.GeneratorUtil; import jetbrains.mps.generator.impl.TemplateGenerator; import jetbrains.mps.generator.runtime.TemplateContext; import jetbrains.mps.smodel.DynamicReference.DynamicReferenceOrigin; import jetbrains.mps.util.SNodeOperations; 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.model.SReference; /** * Restore a reference originally between template nodes * Evgeny Gryaznov, 11/19/10 */ public class ReferenceInfo_Template extends ReferenceInfo { private final SNodeReference myTemplateSourceNode; private final String myTemplateTargetNode; private final TemplateContext myContext; private final String myResolveInfo; public ReferenceInfo_Template(SNodeReference sourceNode, String targetNodeId, String resolveInfo, TemplateContext context) { myContext = context; myTemplateSourceNode = sourceNode; myTemplateTargetNode = targetNodeId; myResolveInfo = resolveInfo; } @Nullable @Override public SReference create(@NotNull PostponedReference ref) { SNode outputTargetNode = doResolve_Straightforward(ref); if (outputTargetNode != null) { return createStaticReference(ref, outputTargetNode); } if (myResolveInfo != null) { final SReference dr = createDynamicReference(ref, myResolveInfo, new DynamicReferenceOrigin(myTemplateSourceNode, myContext.getInput().getReference())); ref.getGenerator().registerDynamicReference(dr); return dr; } return createInvalidReference(ref, null); } private SNode doResolve_Straightforward(PostponedReference ref) { final TemplateGenerator generator = ref.getGenerator(); // try to find for the same inputNode SNode outputTargetNode = generator.findOutputNodeByInputAndTemplateNode(myContext.getInput(), myTemplateTargetNode); if (outputTargetNode != null) { checkCrossRootTemplateReference(outputTargetNode, ref); return outputTargetNode; } // if template has been applied exactly once, then we have unique output node for each template node outputTargetNode = generator.findOutputNodeByTemplateNodeUnique(myTemplateTargetNode); if (outputTargetNode != null) { checkCrossRootTemplateReference(outputTargetNode, ref); return outputTargetNode; } // try to find for indirect input nodes // XXX likely, myContext.getInput() we've checked already above, would come here again. for (SNode historyInputNode : myContext.getInputHistory()) { outputTargetNode = generator.findOutputNodeByInputAndTemplateNode(historyInputNode, myTemplateTargetNode); if (outputTargetNode != null) { checkCrossRootTemplateReference(outputTargetNode, ref); return outputTargetNode; } } return null; } private void checkCrossRootTemplateReference(@NotNull SNode outputTarget, PostponedReference ref) { final TemplateGenerator generator = ref.getGenerator(); if (!generator.isStrict() || !generator.isIncremental()) { return; } // Additional check - reference target should be generated from the same root (required for incremental generation) // I believe origin of this error is the chance next incremental generation may proceed target as unchanged, and there would be no // output mapping for the given templateNodeId, and to prevent this case the error is reported. // There are, however, legitimate references like this when target is known to be regenerated regardless of incremental // mode (e.g. QueriesGenerated in generator) - target being a "common" dependency, the one that is affected by any model change. SNode outputTargetRoot = outputTarget.getContainingRoot(); SNode outputSourceRoot = ref.getSourceNode().getContainingRoot(); SModel model = outputTargetRoot.getModel(); if (model == null || outputTargetRoot.getParent() != null) { SNode inputSourceRoot = generator.getOriginalRootByGenerated(outputSourceRoot); SNode inputTargetRoot = generator.getOriginalRootByGenerated(outputTargetRoot); if (inputTargetRoot != inputSourceRoot) { generator.getLogger().warning(myTemplateSourceNode, "references across templates for different roots are not allowed: use mapping labels or turn off incremental mode, " + "source root: " + (inputSourceRoot == null ? "<conditional root>" : SNodeOperations.getDebugText(inputSourceRoot)) + ", target root: " + (inputTargetRoot == null ? "<conditional root>" : SNodeOperations.getDebugText(inputTargetRoot)), GeneratorUtil.describeIfExists(ref.getSourceNode(), "source"), GeneratorUtil.describeIfExists(outputTarget, "target")); } } } }