23fada946e81d099a4dc640616328cdfcfed9fba
[idea/community.git] / platform / projectModel-impl / src / com / intellij / application / options / PathMacrosImpl.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.application.options;
17
18 import com.intellij.openapi.application.PathMacros;
19 import com.intellij.openapi.components.*;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.text.StringUtil;
22 import com.intellij.util.containers.ContainerUtil;
23 import com.intellij.util.containers.hash.LinkedHashMap;
24 import gnu.trove.THashMap;
25 import gnu.trove.THashSet;
26 import org.jdom.Element;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30 import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
31 import org.jetbrains.jps.model.serialization.PathMacroUtil;
32
33 import java.util.Collection;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.concurrent.locks.ReentrantReadWriteLock;
38
39 @State(
40   name = "PathMacrosImpl",
41   storages = @Storage(value = "path.macros.xml", roamingType = RoamingType.PER_OS)
42 )
43 public class PathMacrosImpl extends PathMacros implements PersistentStateComponent<Element> {
44   private static final Logger LOG = Logger.getInstance(PathMacrosImpl.class);
45
46   private final Map<String, String> myLegacyMacros = new THashMap<>();
47   private final Map<String, String> myMacros = new LinkedHashMap<>();
48   private int myModificationStamp = 0;
49   private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock();
50   private final List<String> myIgnoredMacros = ContainerUtil.createLockFreeCopyOnWriteList();
51
52   private static final String MACRO_ELEMENT = JpsGlobalLoader.PathVariablesSerializer.MACRO_TAG;
53   private static final String NAME_ATTR = JpsGlobalLoader.PathVariablesSerializer.NAME_ATTRIBUTE;
54   private static final String VALUE_ATTR = JpsGlobalLoader.PathVariablesSerializer.VALUE_ATTRIBUTE;
55
56   @NonNls
57   public static final String IGNORED_MACRO_ELEMENT = "ignoredMacro";
58
59   // predefined macros
60   @NonNls
61   public static final String APPLICATION_HOME_MACRO_NAME = PathMacroUtil.APPLICATION_HOME_DIR;
62   @NonNls
63   public static final String PROJECT_DIR_MACRO_NAME = PathMacroUtil.PROJECT_DIR_MACRO_NAME;
64   @NonNls
65   public static final String MODULE_DIR_MACRO_NAME = PathMacroUtil.MODULE_DIR_MACRO_NAME;
66   @NonNls
67   public static final String USER_HOME_MACRO_NAME = PathMacroUtil.USER_HOME_NAME;
68
69   private static final Set<String> SYSTEM_MACROS = new THashSet<>();
70   @NonNls public static final String EXT_FILE_NAME = "path.macros";
71
72   static {
73     SYSTEM_MACROS.add(APPLICATION_HOME_MACRO_NAME);
74     SYSTEM_MACROS.add(PathMacroUtil.APPLICATION_PLUGINS_DIR);
75     SYSTEM_MACROS.add(PROJECT_DIR_MACRO_NAME);
76     SYSTEM_MACROS.add(MODULE_DIR_MACRO_NAME);
77     SYSTEM_MACROS.add(USER_HOME_MACRO_NAME);
78   }
79
80   @SuppressWarnings("SpellCheckingInspection")
81   private static final Set<String> ourToolsMacros = ContainerUtil.immutableSet(
82     "ClasspathEntry",
83     "Classpath",
84     "ColumnNumber",
85     "ContentRoot",
86     "FileClass",
87     "FileDir",
88     "FileParentDir",
89     "FileDirName",
90     "FileDirPathFromParent",
91     "FileDirRelativeToProjectRoot",
92     "/FileDirRelativeToProjectRoot",
93     "FileDirRelativeToSourcepath",
94     "/FileDirRelativeToSourcepath",
95     "FileExt",
96     "FileFQPackage",
97     "FileName",
98     "FileNameWithoutExtension",
99     "FileNameWithoutAllExtensions",
100     "FilePackage",
101     "FilePath",
102     "UnixSeparators",
103     "FilePathRelativeToProjectRoot",
104     "/FilePathRelativeToProjectRoot",
105     "FilePathRelativeToSourcepath",
106     "/FilePathRelativeToSourcepath",
107     "FilePrompt",
108     "FileRelativeDir",
109     "/FileRelativeDir",
110     "FileRelativePath",
111     "/FileRelativePath",
112     "FileEncoding",
113     "JavaDocPath",
114     "JDKPath",
115     "LineNumber",
116     "ModuleFileDir",
117     "ModuleFilePath",
118     "ModuleName",
119     "ModuleSourcePath",
120     "ModuleSdkPath",
121     "OutputPath",
122     "PhpExecutable",
123     "ProjectFileDir",
124     "ProjectFilePath",
125     "ProjectName",
126     "Projectpath",
127     "Prompt",
128     "SourcepathEntry",
129     "Sourcepath",
130     "SHOW_CHANGES",
131     "ClipboardContent",
132     "SelectedText",
133     "SelectionStartLine",
134     "SelectionEndLine",
135     "SelectionStartColumn",
136     "SelectionEndColumn",
137     "PyInterpreterDirectory",
138     "ExecutableByFileExt"
139   );
140
141   public PathMacrosImpl() {
142     //setMacro(USER_HOME_MACRO_NAME, FileUtil.toSystemIndependentName(SystemProperties.getUserHome()));
143   }
144
145   public static PathMacrosImpl getInstanceEx() {
146     return (PathMacrosImpl)getInstance();
147   }
148
149   @Override
150   public Set<String> getUserMacroNames() {
151     myLock.readLock().lock();
152     try {
153       return new THashSet<>(myMacros.keySet()); // keyset should not escape the lock
154     }
155     finally {
156       myLock.readLock().unlock();
157     }
158   }
159
160   public static Set<String> getToolMacroNames() {
161     return ourToolsMacros;
162   }
163
164   @Override
165   public Set<String> getSystemMacroNames() {
166     return SYSTEM_MACROS;
167   }
168
169   @Override
170   public Collection<String> getIgnoredMacroNames() {
171     return myIgnoredMacros;
172   }
173
174   @Override
175   public void setIgnoredMacroNames(@NotNull final Collection<String> names) {
176     myIgnoredMacros.clear();
177     myIgnoredMacros.addAll(names);
178   }
179
180   @Override
181   public void addIgnoredMacro(@NotNull String name) {
182     if (!myIgnoredMacros.contains(name)) myIgnoredMacros.add(name);
183   }
184
185   public int getModificationStamp() {
186     myLock.readLock().lock();
187     try {
188       return myModificationStamp;
189     }
190     finally {
191       myLock.readLock().unlock();
192     }
193   }
194
195   @Override
196   public boolean isIgnoredMacroName(@NotNull String macro) {
197     return myIgnoredMacros.contains(macro);
198   }
199
200   @Override
201   public Set<String> getAllMacroNames() {
202     final Set<String> userMacroNames = getUserMacroNames();
203     final Set<String> systemMacroNames = getSystemMacroNames();
204     final Set<String> allNames = new THashSet<>(userMacroNames.size() + systemMacroNames.size());
205     allNames.addAll(systemMacroNames);
206     allNames.addAll(userMacroNames);
207     return allNames;
208   }
209
210   @Override
211   public String getValue(String name) {
212     try {
213       myLock.readLock().lock();
214       return myMacros.get(name);
215     }
216     finally {
217       myLock.readLock().unlock();
218     }
219   }
220
221   @Override
222   public void removeAllMacros() {
223     try {
224       myLock.writeLock().lock();
225       myMacros.clear();
226     }
227     finally {
228       myModificationStamp++;
229       myLock.writeLock().unlock();
230     }
231   }
232
233   @Override
234   public Collection<String> getLegacyMacroNames() {
235     try {
236       myLock.readLock().lock();
237       // keyset should not escape the lock
238       return new THashSet<>(myLegacyMacros.keySet());
239     }
240     finally {
241       myLock.readLock().unlock();
242     }
243   }
244
245   @Override
246   public void setMacro(@NotNull String name, @NotNull String value) {
247     if (StringUtil.isEmptyOrSpaces(value)) {
248       return;
249     }
250
251     try {
252       myLock.writeLock().lock();
253       myMacros.put(name, value);
254     }
255     finally {
256       myModificationStamp++;
257       myLock.writeLock().unlock();
258     }
259   }
260
261   @Override
262   public void addLegacyMacro(@NotNull String name, @NotNull String value) {
263     try {
264       myLock.writeLock().lock();
265       myLegacyMacros.put(name, value);
266       myMacros.remove(name);
267     }
268     finally {
269       myModificationStamp++;
270       myLock.writeLock().unlock();
271     }
272   }
273
274   @Override
275   public void removeMacro(String name) {
276     try {
277       myLock.writeLock().lock();
278       final String value = myMacros.remove(name);
279       LOG.assertTrue(value != null);
280     }
281     finally {
282       myModificationStamp++;
283       myLock.writeLock().unlock();
284     }
285   }
286
287   @Nullable
288   @Override
289   public Element getState() {
290     try {
291       Element element = new Element("state");
292       myLock.writeLock().lock();
293
294       for (Map.Entry<String, String> entry : myMacros.entrySet()) {
295         String value = entry.getValue();
296         if (!StringUtil.isEmptyOrSpaces(value)) {
297           final Element macro = new Element(MACRO_ELEMENT);
298           macro.setAttribute(NAME_ATTR, entry.getKey());
299           macro.setAttribute(VALUE_ATTR, value);
300           element.addContent(macro);
301         }
302       }
303
304       for (final String macro : myIgnoredMacros) {
305         final Element macroElement = new Element(IGNORED_MACRO_ELEMENT);
306         macroElement.setAttribute(NAME_ATTR, macro);
307         element.addContent(macroElement);
308       }
309       return element;
310     }
311     finally {
312       myLock.writeLock().unlock();
313     }
314   }
315
316   @Override
317   public void loadState(Element element) {
318     try {
319       myLock.writeLock().lock();
320
321       for (Element macro : element.getChildren(MACRO_ELEMENT)) {
322         final String name = macro.getAttributeValue(NAME_ATTR);
323         String value = macro.getAttributeValue(VALUE_ATTR);
324         if (name == null || value == null) {
325           continue;
326         }
327
328         if (SYSTEM_MACROS.contains(name)) {
329           continue;
330         }
331
332         if (value.length() > 1 && value.charAt(value.length() - 1) == '/') {
333           value = value.substring(0, value.length() - 1);
334         }
335
336         myMacros.put(name, value);
337       }
338
339       for (Element macroElement : element.getChildren(IGNORED_MACRO_ELEMENT)) {
340         String ignoredName = macroElement.getAttributeValue(NAME_ATTR);
341         if (!StringUtil.isEmpty(ignoredName) && !myIgnoredMacros.contains(ignoredName)) {
342           myIgnoredMacros.add(ignoredName);
343         }
344       }
345     }
346     finally {
347       myModificationStamp++;
348       myLock.writeLock().unlock();
349     }
350   }
351
352   public void addMacroReplacements(ReplacePathToMacroMap result) {
353     for (String name : getUserMacroNames()) {
354       String value = getValue(name);
355       if (!StringUtil.isEmptyOrSpaces(value)) {
356         result.addMacroReplacement(value, name);
357       }
358     }
359   }
360
361   public void addMacroExpands(ExpandMacroToPathMap result) {
362     for (String name : getUserMacroNames()) {
363       String value = getValue(name);
364       if (!StringUtil.isEmptyOrSpaces(value)) {
365         result.addMacroExpand(name, value);
366       }
367     }
368
369     myLock.readLock().lock();
370     try {
371       for (Map.Entry<String, String> entry : myLegacyMacros.entrySet()) {
372         result.addMacroExpand(entry.getKey(), entry.getValue());
373       }
374     }
375     finally {
376       myLock.readLock().unlock();
377     }
378   }
379 }