| /* |
| * Copyright 2000-2012 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.application.options; |
| |
| import com.intellij.openapi.components.PathMacroMap; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.Function; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.ContainerUtilRt; |
| import gnu.trove.TObjectIntHashMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author Eugene Zhuravlev |
| * Date: Dec 6, 2004 |
| * |
| * @see PathMacrosImpl#addMacroReplacements(ReplacePathToMacroMap) |
| * @see com.intellij.openapi.components.PathMacroManager |
| */ |
| public class ReplacePathToMacroMap extends PathMacroMap { |
| private List<String> myPathsIndex = null; |
| private final Map<String, String> myMacroMap = ContainerUtilRt.newLinkedHashMap(); |
| |
| @NonNls public static final String[] PROTOCOLS; |
| static { |
| List<String> protocols = new ArrayList<String>(); |
| protocols.add("file"); |
| protocols.add("jar"); |
| if (Extensions.getRootArea().hasExtensionPoint(PathMacroExpandableProtocolBean.EP_NAME.getName())) { |
| for (PathMacroExpandableProtocolBean bean : PathMacroExpandableProtocolBean.EP_NAME.getExtensions()) { |
| protocols.add(bean.protocol); |
| } |
| } |
| PROTOCOLS = ArrayUtil.toStringArray(protocols); |
| } |
| |
| public void addMacroReplacement(String path, String macroName) { |
| addReplacement(quotePath(path), "$" + macroName + "$", true); |
| } |
| |
| public void addReplacement(String path, String macroExpr, boolean overwrite) { |
| path = StringUtil.trimEnd(path, "/"); |
| putIfAbsent(path, macroExpr, overwrite); |
| for (String protocol : PROTOCOLS) { |
| putIfAbsent(protocol + ":" + path, protocol + ":" + macroExpr, overwrite); |
| putIfAbsent(protocol + ":/" + path, protocol + ":/" + macroExpr, overwrite); |
| putIfAbsent(protocol + "://" + path, protocol + "://" + macroExpr, overwrite); |
| } |
| } |
| |
| private void putIfAbsent(final String path, final String substitution, final boolean overwrite) { |
| if (overwrite || !myMacroMap.containsKey(path)) { |
| myMacroMap.put(path, substitution); |
| } |
| } |
| |
| @Override |
| public String substitute(@Nullable String text, boolean caseSensitive) { |
| if (text == null) { |
| //noinspection ConstantConditions |
| return null; |
| } |
| |
| for (final String path : getPathIndex()) { |
| text = replacePathMacro(text, path, caseSensitive); |
| } |
| return text; |
| } |
| |
| private String replacePathMacro(@NotNull String text, @NotNull final String path, boolean caseSensitive) { |
| if (text.length() < path.length() || path.isEmpty()) { |
| return text; |
| } |
| |
| boolean startsWith = caseSensitive ? text.startsWith(path) : StringUtil.startsWithIgnoreCase(text, path); |
| |
| if (!startsWith) return text; |
| |
| //check that this is complete path (ends with "/" or "!/") |
| // do not collapse partial paths, i.e. do not substitute "/a/b/cd" in paths like "/a/b/cdeFgh" |
| int endOfOccurrence = path.length(); |
| final boolean isWindowsRoot = path.endsWith(":/"); |
| if (!isWindowsRoot && |
| endOfOccurrence < text.length() && |
| text.charAt(endOfOccurrence) != '/' && |
| !text.substring(endOfOccurrence).startsWith("!/")) { |
| return text; |
| } |
| |
| return myMacroMap.get(path) + text.substring(endOfOccurrence); |
| } |
| |
| @Override |
| public String substituteRecursively(@NotNull String text, final boolean caseSensitive) { |
| for (final String path : getPathIndex()) { |
| text = replacePathMacroRecursively(text, path, caseSensitive); |
| } |
| return text; |
| } |
| |
| private String replacePathMacroRecursively(@NotNull final String text, @NotNull final String path, boolean caseSensitive) { |
| if (text.length() < path.length()) { |
| return text; |
| } |
| |
| if (path.isEmpty()) return text; |
| |
| final StringBuilder newText = new StringBuilder(); |
| final boolean isWindowsRoot = path.endsWith(":/"); |
| int i = 0; |
| while (i < text.length()) { |
| int occurrenceOfPath = caseSensitive ? text.indexOf(path, i) : StringUtil.indexOfIgnoreCase(text, path, i); |
| if (occurrenceOfPath >= 0) { |
| int endOfOccurrence = occurrenceOfPath + path.length(); |
| if (!isWindowsRoot && |
| endOfOccurrence < text.length() && |
| text.charAt(endOfOccurrence) != '/' && |
| text.charAt(endOfOccurrence) != '\"' && |
| text.charAt(endOfOccurrence) != ' ' && |
| !text.substring(endOfOccurrence).startsWith("!/")) { |
| newText.append(text.substring(i, endOfOccurrence)); |
| i = endOfOccurrence; |
| continue; |
| } |
| if (occurrenceOfPath > 0) { |
| char prev = text.charAt(occurrenceOfPath - 1); |
| if (Character.isLetterOrDigit(prev) || prev == '_') { |
| newText.append(text.substring(i, endOfOccurrence)); |
| i = endOfOccurrence; |
| continue; |
| } |
| } |
| } |
| if (occurrenceOfPath < 0) { |
| if (newText.length() == 0) { |
| return text; |
| } |
| newText.append(text.substring(i)); |
| break; |
| } |
| else { |
| newText.append(text.substring(i, occurrenceOfPath)); |
| newText.append(myMacroMap.get(path)); |
| i = occurrenceOfPath + path.length(); |
| } |
| } |
| return newText.toString(); |
| } |
| |
| private static int getIndex(@NotNull final Map.Entry<String, String> s) { |
| final String replacement = s.getValue(); |
| if (replacement.contains("..")) return 1; |
| if (replacement.contains("$" + PathMacrosImpl.USER_HOME_MACRO_NAME + "$")) return 1; |
| if (replacement.contains("$" + PathMacrosImpl.MODULE_DIR_MACRO_NAME + "$")) return 3; |
| if (replacement.contains("$" + PathMacrosImpl.PROJECT_DIR_MACRO_NAME + "$")) return 3; |
| return 2; |
| } |
| |
| private static int stripPrefix(@NotNull String key) { |
| key = StringUtil.trimStart(key, "jar:"); |
| key = StringUtil.trimStart(key, "file:"); |
| while (key.startsWith("/")) { |
| key = key.substring(1); |
| } |
| return key.length(); |
| } |
| |
| @NotNull |
| public List<String> getPathIndex() { |
| if (myPathsIndex == null || myPathsIndex.size() != myMacroMap.size()) { |
| List<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>(myMacroMap.entrySet()); |
| |
| final TObjectIntHashMap<Map.Entry<String, String>> weights = new TObjectIntHashMap<Map.Entry<String, String>>(); |
| for (Map.Entry<String, String> entry : entries) { |
| weights.put(entry, getIndex(entry) * 512 + stripPrefix(entry.getKey())); |
| } |
| |
| ContainerUtil.sort(entries, new Comparator<Map.Entry<String, String>>() { |
| @Override |
| public int compare(final Map.Entry<String, String> o1, final Map.Entry<String, String> o2) { |
| return weights.get(o2) - weights.get(o1); |
| } |
| }); |
| myPathsIndex = ContainerUtil.map2List(entries, new Function<Map.Entry<String, String>, String>() { |
| @Override |
| public String fun(Map.Entry<String, String> entry) { |
| return entry.getKey(); |
| } |
| }); |
| } |
| return myPathsIndex; |
| } |
| |
| public boolean equals(Object obj) { |
| if (obj == this) return true; |
| if (!(obj instanceof ReplacePathToMacroMap)) return false; |
| |
| return myMacroMap.equals(((ReplacePathToMacroMap)obj).myMacroMap); |
| } |
| |
| public int hashCode() { |
| return myMacroMap.hashCode(); |
| } |
| |
| public void put(String path, String replacement) { |
| myMacroMap.put(path, replacement); |
| } |
| |
| } |