blob: b3d1edb42b4ed9331a8fd05b696d610fab08b4c7 [file] [log] [blame]
/*
* 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.PathMacrosCollector;
import com.intellij.application.options.PathMacrosImpl;
import com.intellij.application.options.ReplacePathToMacroMap;
import com.intellij.openapi.application.PathMacros;
import com.intellij.openapi.components.CompositePathMacroFilter;
import com.intellij.openapi.components.ExpandMacroToPathMap;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
import com.intellij.openapi.extensions.Extensions;
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.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.SmartHashSet;
import gnu.trove.THashSet;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.serialization.PathMacroUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class BasePathMacroManager extends PathMacroManager {
private static final CompositePathMacroFilter FILTER = new CompositePathMacroFilter(Extensions.getExtensions(PathMacrosCollector.MACRO_FILTER_EXTENSION_POINT_NAME));
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) {
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(@Nullable String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePathsRecursively(@NotNull final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, true);
}
@Override
public String collapsePathsRecursively(@NotNull final String text) {
return getReplacePathMap().substituteRecursively(text, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void expandPaths(@NotNull final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePaths(@NotNull 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 String myLock = new String("MyTrackingPathMacroSubstitutor.lock");
private final MultiMap<String, String> myMacroToComponentNames = MultiMap.createSet();
private final MultiMap<String, String> myComponentNameToMacros = MultiMap.createSet();
public MyTrackingPathMacroSubstitutor() {
}
@Override
public void reset() {
synchronized (myLock) {
myMacroToComponentNames.clear();
myComponentNameToMacros.clear();
}
}
@Override
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public String collapsePath(@Nullable String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void expandPaths(@NotNull final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
@Override
public void collapsePaths(@NotNull final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, false, FILTER);
}
public int hashCode() {
return getExpandMacroMap().hashCode();
}
@Override
public void invalidateUnknownMacros(@NotNull Set<String> macros) {
synchronized (myLock) {
for (String macro : macros) {
Collection<String> componentNames = myMacroToComponentNames.remove(macro);
if (!ContainerUtil.isEmpty(componentNames)) {
for (String component : componentNames) {
myComponentNameToMacros.remove(component);
}
}
}
}
}
@NotNull
@Override
public Collection<String> getComponents(@NotNull Collection<String> macros) {
synchronized (myLock) {
Set<String> result = new SmartHashSet<String>();
for (String macro : macros) {
result.addAll(myMacroToComponentNames.get(macro));
}
return result;
}
}
@NotNull
@Override
public Collection<String> getUnknownMacros(@Nullable String componentName) {
synchronized (myLock) {
Collection<String> list = componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName);
return ContainerUtil.isEmpty(list) ? Collections.<String>emptyList() : new THashSet<String>(list);
}
}
@Override
public void addUnknownMacros(@NotNull String componentName, @NotNull Collection<String> unknownMacros) {
if (unknownMacros.isEmpty()) {
return;
}
synchronized (myLock) {
for (String unknownMacro : unknownMacros) {
myMacroToComponentNames.putValue(unknownMacro, componentName);
}
myComponentNameToMacros.putValues(componentName, unknownMacros);
}
}
}
protected static boolean pathsEqual(@Nullable String path1, @Nullable String path2) {
return path1 != null && path2 != null &&
FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path1), FileUtil.toSystemIndependentName(path2));
}
}