package jetbrains.mps.build.util;
/*Generated by MPS */
import java.util.Map;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.Set;
import jetbrains.mps.generator.template.TemplateQueryContext;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import jetbrains.mps.extapi.model.TransientSModel;
public class DependenciesHelper {
private final Map<SNode, String> locationMap;
private final Map<SNode, String> contentLocationMap;
private final Map<Object, SNode> idToArtifactMap;
private final Set<SNode> requiresFetch;
protected final MacroHelper macros;
private final TemplateQueryContext myGenContext;
private final SNode myProject;
private final String myLocationKey;
private final String myContentLocationKey;
private String myLayoutRelativeKey;
public DependenciesHelper(@NotNull TemplateQueryContext genContext, SNode project) {
this.locationMap = GenerationUtil.<SNode,String>getSessionMap(project, genContext, "location");
this.contentLocationMap = GenerationUtil.<SNode,String>getSessionMap(project, genContext, "contentLocation");
this.idToArtifactMap = GenerationUtil.<Object,SNode>getSessionMap(project, genContext, "IDToArtifact");
this.macros = new MacroHelper.MacroContext(project, genContext).getMacros(project);
this.requiresFetch = GenerationUtil.getSessionSet(project, genContext, "requiresFetch");
myGenContext = genContext;
myProject = project;
myLocationKey = "location:" + SModelOperations.getModelName(SNodeOperations.getModel(project)) + '/' + SPropertyOperations.getString(project, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name"));
myContentLocationKey = "contentLocation:" + SModelOperations.getModelName(SNodeOperations.getModel(project)) + '/' + SPropertyOperations.getString(project, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name"));
myLayoutRelativeKey = "layout-relative:" + SModelOperations.getModelName(SNodeOperations.getModel(project)) + '/' + SPropertyOperations.getString(project, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name"));
}
public TemplateQueryContext getGenContext() {
return myGenContext;
}
/**
*
* @deprecated use appropriate accessors instead
*/
@Deprecated
public Map<SNode, String> locations() {
return locationMap;
}
public void putLocation(SNode layoutNode, String location) {
locationMap.put(layoutNode, location);
if (isFromTransformedModel(layoutNode)) {
layoutNode.putUserObject(myLocationKey, location);
}
}
public String getLocation(SNode layoutNode) {
String rv = locationMap.get(layoutNode);
if (rv == null) {
// See aliases MC, where BuildLayout_File, recorded in locations, is wrapped with BuildLayout_Copy
// MAP-SRC in BuildLayout_File's rule, default case.
rv = (String) layoutNode.getUserObject(myLocationKey);
}
return rv;
}
public void putContentLocation(SNode node, String location) {
contentLocationMap.put(node, location);
if (isFromTransformedModel(node)) {
node.putUserObject(myContentLocationKey, location);
}
}
public String getContentLocation(SNode n) {
String rv = contentLocationMap.get(n);
if (rv == null) {
rv = (String) n.getUserObject(myContentLocationKey);
}
return rv;
}
public void preserveLocations(SNode from, SNode to) {
// this method is invoked from generation for specific usecases (wrap of a File wuth Copy),
// hence we expect nodes to be free-floating/transient, never from a regular model
assert SNodeOperations.getModel(to) == null || SNodeOperations.getModel(to) instanceof TransientSModel;
to.putUserObject(myLocationKey, from.getUserObject(myLocationKey));
to.putUserObject(myContentLocationKey, from.getUserObject(myContentLocationKey));
}
/**
* {@link jetbrains.mps.build.util.DependenciesHelper#getArtifact(SNode) }
*
* @param layoutNode artifact, likely from getArtifact()
* @param key node that has a path relative to layoutNode, likely from the same model as layoutNode
* @param location path for the key node
*/
public void putLayoutRelativePath(SNode layoutNode, SNode key, String location) {
// FIXME shall respect layoutNode as there are chances to have same 'key' (e.g. BuildMps_AbstractModule) exposed through
// different layout nodes, just left simplest possible variant to test and get further
key.putUserObject(myLayoutRelativeKey, location);
}
/**
*
* @param layoutNode artifact, likely the one from getArtifact() call, from a model the moment DH was initialized
* @param key node with path relative to layoutNode, may come from a model other than that of layoutNode (i.e. later transient), and might be different from the key in putLayoutRelativePath()
* @return location path for the key node, if any
*/
public String getLayoutRelativePath(SNode layoutNode, SNode key) {
// FIXME see putLayoutRelativePath for details
return (String) key.getUserObject(myLayoutRelativeKey);
}
/*package*/ Map<Object, SNode> artifacts() {
return idToArtifactMap;
}
public SNode getArtifact(String id) {
return SNodeOperations.as(idToArtifactMap.get(id), MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x668c6cfbafac4c85L, "jetbrains.mps.build.structure.BuildLayout_Node"));
}
public SNode getArtifact(SNode id) {
return SNodeOperations.as(idToArtifactMap.get(getOriginalNode(id)), MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x668c6cfbafac4c85L, "jetbrains.mps.build.structure.BuildLayout_Node"));
}
public SNode getArtifact(LocalSourcePathArtifact id) {
return SNodeOperations.as(idToArtifactMap.get(id), MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x668c6cfbafac4c85L, "jetbrains.mps.build.structure.BuildLayout_Node"));
}
public void putArtifact(String id, SNode artifact) {
putArtifact0(id, artifact);
}
public void putArtifact(SNode id, SNode artifact) {
putArtifact0(getOriginalNode(id), artifact);
}
public void putArtifact(LocalSourcePathArtifact id, SNode artifact) {
putArtifact0(id, artifact);
}
private void putArtifact0(Object id, SNode artifact) {
idToArtifactMap.put(id, artifact);
}
public boolean requiresFetch(SNode node) {
return requiresFetch.contains(node);
}
/*package*/ void doFetch(SNode node) {
requiresFetch.add(node);
}
public MacroHelper getMacroHelper() {
return macros;
}
/**
* Check if layout node comes from build project being transformed, or the one being transformed along with it, i.e. if we CAN and NEED to associate
* location values with it. Layout nodes are not necessarily belong to the generated project, an import from external
* ayout brings foreign nodes, which can't get changed if they come from another model, non-transient, and the value associated would affect
* any dependant project until the model is unloaded.
* If an external node comes from another project from the same model (few projects may get transformed simultaneously), we need to record location
* that is specific to each project (given projects A, B and C in a single model, where B and C re-use artifacts declared in A, layout nodes of A shall
* keep distinct locations for project B and C).
*/
private boolean isFromTransformedModel(SNode n) {
SNode ancestorProject = SNodeOperations.getNodeAncestor(n, MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4df58c6f18f84a13L, "jetbrains.mps.build.structure.BuildProject"), false, false);
// ancestorProject could be null for a layout node from external layout root
return ancestorProject == myProject || (SNodeOperations.getModel(n) == SNodeOperations.getModel(myProject) && SNodeOperations.getModel(n) instanceof TransientSModel);
}
public SNode getOriginalNode(SNode node) {
return getOriginalNode(node, myGenContext);
}
public static SNode getOriginalNode(SNode node, TemplateQueryContext genContext) {
// node.model could be legitimately == null for a node from transient model which is already disposed.
// however, we need to answer its original node anyway, or the whole build process would fail:
// RequiredPlugins records transient nodes and getArtifact(node<>) needs to find out original node of that node.
// If generation doesn't keep transient models (or uses in-place transformation), check for node.model==null here
// would effectively prevent from using getArtifacts(recordedTransientNode).
if (SNodeOperations.getModel(node) != null && !((SNodeOperations.getModel(node) instanceof TransientSModel))) {
return node;
}
if (genContext == null) {
throw new IllegalStateException("transient model is not expected");
}
SNode originalNode = genContext.getOriginalCopiedInputNode(node);
if ((originalNode == null)) {
genContext.showErrorMessage(node, "cannot resolve dependency on transient model, no original node is available");
return null;
}
return originalNode;
}
}