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; } }