fix "IDEA-221944 Deadlock on opening second project" and support preloading for proje...
[idea/community.git] / platform / projectModel-impl / src / com / intellij / openapi / components / PathMacroManager.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.components;
3
4 import com.intellij.application.options.PathMacrosCollector;
5 import com.intellij.application.options.PathMacrosImpl;
6 import com.intellij.application.options.ReplacePathToMacroMap;
7 import com.intellij.openapi.application.PathMacroFilter;
8 import com.intellij.openapi.application.PathMacros;
9 import com.intellij.openapi.util.SystemInfo;
10 import com.intellij.openapi.util.io.FileUtil;
11 import com.intellij.openapi.util.text.StringUtil;
12 import com.intellij.util.PathUtilRt;
13 import org.jdom.Element;
14 import org.jetbrains.annotations.Contract;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
17 import org.jetbrains.annotations.SystemIndependent;
18 import org.jetbrains.jps.model.serialization.PathMacroUtil;
19
20 import java.util.Map;
21 import java.util.Objects;
22
23 public class PathMacroManager implements PathMacroSubstitutor {
24   @NotNull
25   public static PathMacroManager getInstance(@NotNull ComponentManager componentManager) {
26     return Objects.requireNonNull(componentManager.getService(PathMacroManager.class));
27   }
28
29   private static class Holder {
30     private static final CompositePathMacroFilter FILTER = new CompositePathMacroFilter(PathMacrosCollector.MACRO_FILTER_EXTENSION_POINT_NAME.getExtensionList());
31   }
32
33   private PathMacrosImpl myPathMacros;
34
35   private ReplacePathToMacroMap myReplacePathToMacroMap;
36   private long myPathMacrosModificationCount;
37
38   public PathMacroManager(@Nullable PathMacros pathMacros) {
39     myPathMacros = (PathMacrosImpl)pathMacros;
40   }
41
42   @NotNull
43   public PathMacroFilter getMacroFilter() {
44     return Holder.FILTER;
45   }
46
47   protected static void addFileHierarchyReplacements(@NotNull ExpandMacroToPathMap result, @NotNull String macroName, @SystemIndependent @Nullable String path) {
48     if (path != null) {
49       doAddFileHierarchyReplacements(result, StringUtil.trimEnd(path, "/"), '$' + macroName + '$');
50     }
51   }
52
53   private static void doAddFileHierarchyReplacements(@NotNull ExpandMacroToPathMap result, @NotNull String path, @NotNull String macro) {
54     String parentPath = PathUtilRt.getParentPath(path);
55     if (!parentPath.isEmpty()) {
56       doAddFileHierarchyReplacements(result, parentPath, macro + "/..");
57     }
58     result.put(macro, path);
59   }
60
61   protected static void addFileHierarchyReplacements(ReplacePathToMacroMap result, String macroName, @Nullable String path, @Nullable String stopAt) {
62     if (path == null) {
63       return;
64     }
65
66     String macro = '$' + macroName + '$';
67     path = StringUtil.trimEnd(FileUtil.toSystemIndependentName(path), "/");
68     boolean overwrite = true;
69     while (StringUtil.isNotEmpty(path) && path.contains("/")) {
70       result.addReplacement(path, macro, overwrite);
71
72       if (path.equals(stopAt)) {
73         break;
74       }
75
76       macro += "/..";
77       overwrite = false;
78       path = StringUtil.getPackageName(path, '/');
79     }
80   }
81
82   @NotNull
83   public ExpandMacroToPathMap getExpandMacroMap() {
84     ExpandMacroToPathMap result = new ExpandMacroToPathMap();
85     getPathMacros().addMacroExpands(result);
86     for (Map.Entry<String, String> entry : PathMacroUtil.getGlobalSystemMacros().entrySet()) {
87       result.addMacroExpand(entry.getKey(), entry.getValue());
88     }
89     return result;
90   }
91
92   @NotNull
93   public final synchronized ReplacePathToMacroMap getReplacePathMap() {
94     long pathMacrosModificationCount = getPathMacros().getModificationCount();
95     if (myReplacePathToMacroMap != null && pathMacrosModificationCount == myPathMacrosModificationCount) {
96       return myReplacePathToMacroMap;
97     }
98
99     myReplacePathToMacroMap = computeReplacePathMap();
100     myPathMacrosModificationCount = pathMacrosModificationCount;
101     return myReplacePathToMacroMap;
102   }
103
104   @NotNull
105   protected ReplacePathToMacroMap computeReplacePathMap() {
106     ReplacePathToMacroMap result = new ReplacePathToMacroMap();
107     getPathMacros().addMacroReplacements(result);
108     for (Map.Entry<String, String> entry : PathMacroUtil.getGlobalSystemMacros().entrySet()) {
109       result.addMacroReplacement(entry.getValue(), entry.getKey());
110     }
111     return result;
112   }
113
114   @Override
115   @Contract("null -> null; !null -> !null")
116   public String expandPath(@Nullable String text) {
117     if (StringUtil.isEmpty(text)) {
118       return text;
119     }
120     return getExpandMacroMap().substitute(text, SystemInfo.isFileSystemCaseSensitive);
121   }
122
123   @Contract("null, _ -> null; !null, _ -> !null")
124   @Override
125   public String collapsePath(@Nullable String text, boolean recursively) {
126     if (StringUtil.isEmpty(text)) {
127       return text;
128     }
129     return getReplacePathMap().substitute(text, SystemInfo.isFileSystemCaseSensitive, recursively);
130   }
131
132   @Override
133   public void expandPaths(@NotNull Element element) {
134     getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
135   }
136
137   @Override
138   public void collapsePaths(@NotNull Element element, boolean recursively) {
139     collapsePaths(element, recursively, getReplacePathMap());
140   }
141
142   public static void collapsePaths(@NotNull Element element, boolean recursively, @NotNull ReplacePathToMacroMap map) {
143     map.substitute(element, SystemInfo.isFileSystemCaseSensitive, recursively, Holder.FILTER);
144   }
145
146   @NotNull
147   private PathMacrosImpl getPathMacros() {
148     if (myPathMacros == null) {
149       myPathMacros = PathMacrosImpl.getInstanceEx();
150     }
151     return myPathMacros;
152   }
153
154   protected static boolean pathsEqual(@Nullable String path1, @Nullable String path2) {
155     return path1 != null && path2 != null &&
156            FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path1), FileUtil.toSystemIndependentName(path2));
157   }
158 }