/*
* 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.openapi.components.impl;
import com.intellij.application.options.PathMacrosImpl;
import com.intellij.application.options.ReplacePathToMacroMap;
import com.intellij.openapi.application.PathMacros;
import com.intellij.openapi.components.ExpandMacroToPathMap;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.components.PathMacroUtil;
import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.util.containers.FactoryMap;
import org.jdom.Element;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class BasePathMacroManager extends PathMacroManager {
private PathMacrosImpl myPathMacros;
public BasePathMacroManager(@Nullable PathMacros pathMacros) {
myPathMacros = (PathMacrosImpl)pathMacros;
}
protected static void addFileHierarchyReplacements(ExpandMacroToPathMap result, String macroName, @Nullable String path) {
if (path == null) return;
addFileHierarchyReplacements(result, getLocalFileSystem().findFileByPath(path), "$" + macroName + "$");
}
private static void addFileHierarchyReplacements(ExpandMacroToPathMap result, @Nullable VirtualFile f, String macro) {
if (f == null) return;
addFileHierarchyReplacements(result, f.getParent(), macro + "/..");
result.put(macro, StringUtil.trimEnd(f.getPath(), "/"));
}
protected static void addFileHierarchyReplacements(ReplacePathToMacroMap result, String macroName, @Nullable String path, @Nullable String stopAt) {
if (path == null) return;
String macro = "$" + macroName + "$";
path = StringUtil.trimEnd(FileUtil.toSystemIndependentName(path), "/");
boolean overwrite = true;
while (StringUtil.isNotEmpty(path) && path.contains("/")) {
result.addReplacement(path, macro, overwrite);
if (path.equals(stopAt)) {
break;
}
macro += "/..";
overwrite = false;
path = StringUtil.getPackageName(path, '/');
}
}
private static VirtualFileSystem getLocalFileSystem() {
// Use VFM directly because of mocks in tests.
return VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL);
}
public ExpandMacroToPathMap getExpandMacroMap() {
ExpandMacroToPathMap result = new ExpandMacroToPathMap();
for (Map.Entry<String, String> entry : PathMacroUtil.getGlobalSystemMacros().entrySet()) {
result.addMacroExpand(entry.getKey(), entry.getValue());
}
getPathMacros().addMacroExpands(result);
return result;
}
protected ReplacePathToMacroMap getReplacePathMap() {
ReplacePathToMacroMap result = new ReplacePathToMacroMap();
for (Map.Entry<String, String> entry : PathMacroUtil.getGlobalSystemMacros().entrySet()) {
result.addMacroReplacement(entry.getValue(), entry.getKey());
}
getPathMacros().addMacroReplacements(result);
return result;
}
@Override
public TrackingPathMacroSubstitutor createTrackingSubstitutor() {
return new MyTrackingPathMacroSubstitutor();
}
@Override
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePathsRecursively(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, true);
}
@Override
public String collapsePathsRecursively(final String text) {
return getReplacePathMap().substituteRecursively(text, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePaths(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public PathMacrosImpl getPathMacros() {
if (myPathMacros == null) {
myPathMacros = PathMacrosImpl.getInstanceEx();
}
return myPathMacros;
}
private class MyTrackingPathMacroSubstitutor implements TrackingPathMacroSubstitutor {
private final Map<String, Set<String>> myMacroToComponentNames = new FactoryMap<String, Set<String>>() {
@Override
protected Set<String> create(String key) {
return new HashSet<String>();
}
};
private final Map<String, Set<String>> myComponentNameToMacros = new FactoryMap<String, Set<String>>() {
@Override
protected Set<String> create(String key) {
return new HashSet<String>();
}
};
public MyTrackingPathMacroSubstitutor() {
}
@Override
public void reset() {
myMacroToComponentNames.clear();
myComponentNameToMacros.clear();
}
@Override
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePaths(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public int hashCode() {
return getExpandMacroMap().hashCode();
}
@Override
public void invalidateUnknownMacros(final Set<String> macros) {
for (final String macro : macros) {
final Set<String> components = myMacroToComponentNames.get(macro);
for (final String component : components) {
myComponentNameToMacros.remove(component);
}
myMacroToComponentNames.remove(macro);
}
}
@Override
public Set<String> getComponents(final Collection<String> macros) {
final Set<String> result = new HashSet<String>();
for (String macro : myMacroToComponentNames.keySet()) {
if (macros.contains(macro)) {
result.addAll(myMacroToComponentNames.get(macro));
}
}
return result;
}
@Override
public Set<String> getUnknownMacros(final String componentName) {
final Set<String> result = new HashSet<String>();
result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName));
return Collections.unmodifiableSet(result);
}
@Override
public void addUnknownMacros(final String componentName, final Collection<String> unknownMacros) {
if (unknownMacros.isEmpty()) return;
for (String unknownMacro : unknownMacros) {
final Set<String> stringList = myMacroToComponentNames.get(unknownMacro);
stringList.add(componentName);
}
myComponentNameToMacros.get(componentName).addAll(unknownMacros);
}
}
protected static boolean pathsEqual(@Nullable String path1, @Nullable String path2) {
return path1 != null && path2 != null &&
FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path1), FileUtil.toSystemIndependentName(path2));
}
}