2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.application.options;
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;
33 import java.util.Collection;
34 import java.util.List;
37 import java.util.concurrent.locks.ReentrantReadWriteLock;
40 name = "PathMacrosImpl",
41 storages = @Storage(value = "path.macros.xml", roamingType = RoamingType.PER_OS)
43 public class PathMacrosImpl extends PathMacros implements PersistentStateComponent<Element> {
44 private static final Logger LOG = Logger.getInstance(PathMacrosImpl.class);
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();
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;
57 public static final String IGNORED_MACRO_ELEMENT = "ignoredMacro";
61 public static final String APPLICATION_HOME_MACRO_NAME = PathMacroUtil.APPLICATION_HOME_DIR;
63 public static final String PROJECT_DIR_MACRO_NAME = PathMacroUtil.PROJECT_DIR_MACRO_NAME;
65 public static final String MODULE_DIR_MACRO_NAME = PathMacroUtil.MODULE_DIR_MACRO_NAME;
67 public static final String USER_HOME_MACRO_NAME = PathMacroUtil.USER_HOME_NAME;
69 private static final Set<String> SYSTEM_MACROS = new THashSet<>();
70 @NonNls public static final String EXT_FILE_NAME = "path.macros";
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);
80 @SuppressWarnings("SpellCheckingInspection")
81 private static final Set<String> ourToolsMacros = ContainerUtil.immutableSet(
90 "FileDirPathFromParent",
91 "FileDirRelativeToProjectRoot",
92 "/FileDirRelativeToProjectRoot",
93 "FileDirRelativeToSourcepath",
94 "/FileDirRelativeToSourcepath",
98 "FileNameWithoutExtension",
99 "FileNameWithoutAllExtensions",
103 "FilePathRelativeToProjectRoot",
104 "/FilePathRelativeToProjectRoot",
105 "FilePathRelativeToSourcepath",
106 "/FilePathRelativeToSourcepath",
133 "SelectionStartLine",
135 "SelectionStartColumn",
136 "SelectionEndColumn",
137 "PyInterpreterDirectory",
138 "ExecutableByFileExt"
141 public PathMacrosImpl() {
144 public static PathMacrosImpl getInstanceEx() {
145 return (PathMacrosImpl)getInstance();
149 public Set<String> getUserMacroNames() {
150 myLock.readLock().lock();
152 return new THashSet<>(myMacros.keySet()); // keyset should not escape the lock
155 myLock.readLock().unlock();
159 public static Set<String> getToolMacroNames() {
160 return ourToolsMacros;
164 public Set<String> getSystemMacroNames() {
165 return SYSTEM_MACROS;
169 public Collection<String> getIgnoredMacroNames() {
170 return myIgnoredMacros;
174 public void setIgnoredMacroNames(@NotNull final Collection<String> names) {
175 myIgnoredMacros.clear();
176 myIgnoredMacros.addAll(names);
180 public void addIgnoredMacro(@NotNull String name) {
181 if (!myIgnoredMacros.contains(name)) myIgnoredMacros.add(name);
184 public int getModificationStamp() {
185 myLock.readLock().lock();
187 return myModificationStamp;
190 myLock.readLock().unlock();
195 public boolean isIgnoredMacroName(@NotNull String macro) {
196 return myIgnoredMacros.contains(macro);
200 public Set<String> getAllMacroNames() {
201 return ContainerUtil.union(getUserMacroNames(), getSystemMacroNames());
205 public String getValue(String name) {
207 myLock.readLock().lock();
208 return myMacros.get(name);
211 myLock.readLock().unlock();
216 public void removeAllMacros() {
218 myLock.writeLock().lock();
222 myModificationStamp++;
223 myLock.writeLock().unlock();
228 public Collection<String> getLegacyMacroNames() {
230 myLock.readLock().lock();
231 // keyset should not escape the lock
232 return new THashSet<>(myLegacyMacros.keySet());
235 myLock.readLock().unlock();
240 public void setMacro(@NotNull String name, @NotNull String value) {
241 if (StringUtil.isEmptyOrSpaces(value)) {
246 myLock.writeLock().lock();
247 myMacros.put(name, value);
250 myModificationStamp++;
251 myLock.writeLock().unlock();
256 public void addLegacyMacro(@NotNull String name, @NotNull String value) {
258 myLock.writeLock().lock();
259 myLegacyMacros.put(name, value);
260 myMacros.remove(name);
263 myModificationStamp++;
264 myLock.writeLock().unlock();
269 public void removeMacro(String name) {
271 myLock.writeLock().lock();
272 final String value = myMacros.remove(name);
273 LOG.assertTrue(value != null);
276 myModificationStamp++;
277 myLock.writeLock().unlock();
283 public Element getState() {
285 Element element = new Element("state");
286 myLock.writeLock().lock();
288 for (Map.Entry<String, String> entry : myMacros.entrySet()) {
289 String value = entry.getValue();
290 if (!StringUtil.isEmptyOrSpaces(value)) {
291 final Element macro = new Element(MACRO_ELEMENT);
292 macro.setAttribute(NAME_ATTR, entry.getKey());
293 macro.setAttribute(VALUE_ATTR, value);
294 element.addContent(macro);
298 for (final String macro : myIgnoredMacros) {
299 final Element macroElement = new Element(IGNORED_MACRO_ELEMENT);
300 macroElement.setAttribute(NAME_ATTR, macro);
301 element.addContent(macroElement);
306 myLock.writeLock().unlock();
311 public void loadState(Element element) {
313 myLock.writeLock().lock();
315 for (Element macro : element.getChildren(MACRO_ELEMENT)) {
316 final String name = macro.getAttributeValue(NAME_ATTR);
317 String value = macro.getAttributeValue(VALUE_ATTR);
318 if (name == null || value == null) {
322 if (SYSTEM_MACROS.contains(name)) {
326 if (value.length() > 1 && value.charAt(value.length() - 1) == '/') {
327 value = value.substring(0, value.length() - 1);
330 myMacros.put(name, value);
333 for (Element macroElement : element.getChildren(IGNORED_MACRO_ELEMENT)) {
334 String ignoredName = macroElement.getAttributeValue(NAME_ATTR);
335 if (!StringUtil.isEmpty(ignoredName) && !myIgnoredMacros.contains(ignoredName)) {
336 myIgnoredMacros.add(ignoredName);
341 myModificationStamp++;
342 myLock.writeLock().unlock();
346 public void addMacroReplacements(ReplacePathToMacroMap result) {
347 for (String name : getUserMacroNames()) {
348 String value = getValue(name);
349 if (!StringUtil.isEmptyOrSpaces(value)) {
350 result.addMacroReplacement(value, name);
355 public void addMacroExpands(ExpandMacroToPathMap result) {
356 for (String name : getUserMacroNames()) {
357 String value = getValue(name);
358 if (!StringUtil.isEmptyOrSpaces(value)) {
359 result.addMacroExpand(name, value);
363 myLock.readLock().lock();
365 for (Map.Entry<String, String> entry : myLegacyMacros.entrySet()) {
366 result.addMacroExpand(entry.getKey(), entry.getValue());
370 myLock.readLock().unlock();