/*
* 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.IGeneratorLogger.ProblemDescription;
import jetbrains.mps.generator.TransientModelsModule;
import jetbrains.mps.generator.impl.GeneratorUtil;
import jetbrains.mps.generator.impl.RoleValidation.Status;
import jetbrains.mps.generator.impl.TemplateGenerator;
import jetbrains.mps.smodel.DynamicReference.DynamicReferenceOrigin;
import jetbrains.mps.smodel.SModelStereotype;
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.SReference;
/**
* Restore a reference that points to a node from input model, either copied or transformed.
* Created by: Sergey Dmitriev
* Date: Jan 25, 2007
*/
public class ReferenceInfo_CopiedInputNode extends ReferenceInfo {
private final SNode myInputNode;
private final SNode myInputTargetNode;
/**
* @param inputTargetNode reference target in input model
*/
public ReferenceInfo_CopiedInputNode(SNode nodeBeingCopied, SNode inputTargetNode) {
myInputNode = nodeBeingCopied;
myInputTargetNode = inputTargetNode;
}
@Nullable
@Override
public SReference create(@NotNull PostponedReference ref) {
if (myInputTargetNode != null) {
// output target node might has been copied (reduced) from the input target node
// here accept only one-to-one copying
SNode ultimateTarget = ref.getGenerator().findCopiedOutputNodeForInputNode_unique(myInputTargetNode);
if (ultimateTarget != null) {
return createStaticReference(ref, ultimateTarget);
}
String resolveInfo = jetbrains.mps.util.SNodeOperations.getResolveInfo(myInputTargetNode);
if (resolveInfo != null) {
final SReference dr = createDynamicReference(ref, resolveInfo, new DynamicReferenceOrigin(null, myInputNode.getReference()));
ref.getGenerator().registerDynamicReference(dr);
return dr;
}
// if input was copied - return one of its copies
// this can easy produce incorrect references
SNode ambiguousTarget = ref.getGenerator().findCopiedOutputNodeForInputNode(myInputTargetNode);
if (ambiguousTarget != null) {
// RI_CIN is the only case doResolve_Tricky was implemented and hence checkResolveTarget check moved here.
if (checkResolvedTarget(ref, ambiguousTarget)) {
return createStaticReference(ref, ambiguousTarget);
} else {
return jetbrains.mps.smodel.SReference.create(ref.getLink(), ref.getSourceNode(), ref.getGenerator().getOutputModel().getReference(), null);
}
}
}
return createInvalidReference(ref, null);
}
private ProblemDescription[] getErrorDescriptions() {
return new ProblemDescription[]{
GeneratorUtil.describe(myInputNode, "input node")
};
}
private boolean checkResolvedTarget(PostponedReference ref, SNode outputTargetNode) {
final SNode outputSourceNode = ref.getSourceNode();
final TemplateGenerator generator = ref.getGenerator();
Status status = generator.getReferentRoleValidator(outputSourceNode, ref.getLink()).validate(outputTargetNode);
if (status != null) {
generator.getLogger().error(outputSourceNode.getReference(), status.getMessage(getClass().getSimpleName()), getErrorDescriptions());
return false;
}
SModel referentNodeModel = outputTargetNode.getModel();
if (referentNodeModel != null && referentNodeModel != outputSourceNode.getModel()) {
if (SModelStereotype.isGeneratorModel(referentNodeModel)) {
// references to template nodes are not acceptable
String msg = "bad reference, cannot refer to a generator model: %s for role '%s' in %s";
generator.getLogger().error(outputSourceNode.getReference(), String.format(msg,
SNodeOperations.getDebugText(outputTargetNode), ref.getLink(), SNodeOperations.getDebugText(outputSourceNode)),
getErrorDescriptions());
return false;
}
if (referentNodeModel .getModule() instanceof TransientModelsModule) {
// references to transient nodes in a model outside one being generated are not acceptable
String msg = "bad reference, cannot refer to an external transient model: %s for role '%s' in %s";
generator.getLogger().error(outputSourceNode.getReference(), String.format(msg,
SNodeOperations.getDebugText(outputTargetNode), ref.getLink(), SNodeOperations.getDebugText(outputSourceNode)),
getErrorDescriptions());
return false;
}
}
return true;
}
}