faster path macro replacement index building (IDEA-84390)

This commit is contained in:
peter
2012-07-10 13:15:56 +02:00
parent c4d856126e
commit 9b6e311e48
5 changed files with 733 additions and 757 deletions
@@ -1,273 +1,271 @@
/*
* 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.mock.MockFileSystem;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.PathMacros;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SystemProperties;
import com.intellij.util.io.fs.FileSystem;
import com.intellij.util.io.fs.IFileSystem;
import org.hamcrest.Description;
import org.jetbrains.annotations.Nullable;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* @author mike
*/
@RunWith(JMock.class)
public class PathMacroManagerTest {
private static final String APP_HOME = FileUtil.toSystemIndependentName(PathManager.getHomePath());
private static final String USER_HOME = FileUtil.toSystemIndependentName(StringUtil.trimEnd(SystemProperties.getUserHome(), "/"));
private Module myModule;
private ProjectEx myProject;
private PathMacrosImpl myPathMacros;
private Mockery context;
protected ApplicationEx myApplication;
private IFileSystem myOldFileSystem;
protected MockFileSystem myFileSystem;
@Before
public final void setupApplication() throws Exception {
context = new JUnit4Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
myApplication = context.mock(ApplicationEx.class, "application");
context.checking(new Expectations() {
{
allowing(myApplication).isUnitTestMode(); will(returnValue(false));
allowing(myApplication).getName(); will(returnValue("IDEA"));
//some tests leave invokeLaters after them...
allowing(myApplication).invokeLater(with(any(Runnable.class)), with(any(ModalityState.class)));
allowing(myApplication).runReadAction(with(any(Runnable.class)));
will(new Action() {
@Override
public void describeTo(final Description description) {
description.appendText("runs runnable");
}
@Override
@Nullable
public Object invoke(final Invocation invocation) throws Throwable {
((Runnable)invocation.getParameter(0)).run();
return null;
}
});
}
});
}
@Before
public final void setupFileSystem() throws Exception {
myOldFileSystem = FileSystem.FILE_SYSTEM;
myFileSystem = new MockFileSystem();
FileSystem.FILE_SYSTEM = myFileSystem;
BasePathMacroManager.DEBUG = true;
}
@After
public final void restoreFilesystem() throws Exception {
BasePathMacroManager.DEBUG = false;
FileSystem.FILE_SYSTEM = myOldFileSystem;
}
private void setUpMocks(final String projectPath) {
myModule = context.mock(Module.class);
myPathMacros = context.mock(PathMacrosImpl.class);
myProject = context.mock(ProjectEx.class);
final VirtualFile projectFile = context.mock(VirtualFile.class, "projectFile");
context.checking(new Expectations() {{
allowing(myModule).isDisposed(); will(returnValue(false));
allowing(myProject).isDisposed(); will(returnValue(false));
allowing(projectFile).getPath(); will(returnValue(projectPath));
final String moduleFilePath = projectPath + "/module/module.iml";
allowing(myApplication).getComponent(with(equal(PathMacros.class))); will(returnValue(myPathMacros));
allowing(myPathMacros).addMacroReplacements(with(any(ReplacePathToMacroMap.class)));
allowing(myProject).getBaseDir(); will(returnValue(projectFile));
allowing(myModule).getModuleFilePath(); will(returnValue(moduleFilePath));
allowing(myModule).getProject(); will(returnValue(myProject));
}});
}
@Test
public void testRightMacrosOrder_RelativeValues_NoVariables() throws Exception {
setUpMocks("/tmp/foo");
final ReplacePathToMacroMap replacePathMap = new ModulePathMacroManager(myPathMacros, myModule).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:/tmp/foo/module -> file:$MODULE_DIR$\n" +
"file://tmp/foo/module -> file:/$MODULE_DIR$\n" +
"file:///tmp/foo/module -> file://$MODULE_DIR$\n" +
"jar:/tmp/foo/module -> jar:$MODULE_DIR$\n" +
"jar://tmp/foo/module -> jar:/$MODULE_DIR$\n" +
"jar:///tmp/foo/module -> jar://$MODULE_DIR$\n" +
"/tmp/foo/module -> $MODULE_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$\n" +
"file:/tmp/foo -> file:$MODULE_DIR$/..\n" +
"file://tmp/foo -> file:/$MODULE_DIR$/..\n" +
"file:///tmp/foo -> file://$MODULE_DIR$/..\n" +
"jar:/tmp/foo -> jar:$MODULE_DIR$/..\n" +
"jar://tmp/foo -> jar:/$MODULE_DIR$/..\n" +
"jar:///tmp/foo -> jar://$MODULE_DIR$/..\n" +
"/tmp/foo -> $MODULE_DIR$/..\n" +
"file:/tmp -> file:$MODULE_DIR$/../..\n" +
"file://tmp -> file:/$MODULE_DIR$/../..\n" +
"file:///tmp -> file://$MODULE_DIR$/../..\n" +
"jar:/tmp -> jar:$MODULE_DIR$/../..\n" +
"jar://tmp -> jar:/$MODULE_DIR$/../..\n" +
"jar:///tmp -> jar://$MODULE_DIR$/../..\n" +
"/tmp -> $MODULE_DIR$/../..", s);
}
@Test
public void testPathsOutsideProject() throws Exception {
setUpMocks("/tmp/foo");
final ReplacePathToMacroMap replacePathMap = new ProjectPathMacroManager(myPathMacros, myProject).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:/tmp/foo -> file:$PROJECT_DIR$\n" +
"file://tmp/foo -> file:/$PROJECT_DIR$\n" +
"file:///tmp/foo -> file://$PROJECT_DIR$\n" +
"jar:/tmp/foo -> jar:$PROJECT_DIR$\n" +
"jar://tmp/foo -> jar:/$PROJECT_DIR$\n" +
"jar:///tmp/foo -> jar://$PROJECT_DIR$\n" +
"/tmp/foo -> $PROJECT_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$\n" +
"file:/tmp -> file:$PROJECT_DIR$/..\n" +
"file://tmp -> file:/$PROJECT_DIR$/..\n" +
"file:///tmp -> file://$PROJECT_DIR$/..\n" +
"jar:/tmp -> jar:$PROJECT_DIR$/..\n" +
"jar://tmp -> jar:/$PROJECT_DIR$/..\n" +
"jar:///tmp -> jar://$PROJECT_DIR$/..\n" +
"/tmp -> $PROJECT_DIR$/..", s);
}
@Test
public void testProjectUnderUserHome() throws Exception {
setUpMocks(USER_HOME + "/IdeaProjects/foo");
final ReplacePathToMacroMap replacePathMap = new ModulePathMacroManager(myPathMacros, myModule).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:" + USER_HOME + "/IdeaProjects/foo/module -> file:$MODULE_DIR$\n" +
"file:/" + USER_HOME + "/IdeaProjects/foo/module -> file:/$MODULE_DIR$\n" +
"file://" + USER_HOME + "/IdeaProjects/foo/module -> file://$MODULE_DIR$\n" +
"jar:" + USER_HOME + "/IdeaProjects/foo/module -> jar:$MODULE_DIR$\n" +
"jar:/" + USER_HOME + "/IdeaProjects/foo/module -> jar:/$MODULE_DIR$\n" +
"jar://" + USER_HOME + "/IdeaProjects/foo/module -> jar://$MODULE_DIR$\n" +
USER_HOME + "/IdeaProjects/foo/module -> $MODULE_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"file:" + USER_HOME + "/IdeaProjects/foo -> file:$MODULE_DIR$/..\n" +
"file:/" + USER_HOME + "/IdeaProjects/foo -> file:/$MODULE_DIR$/..\n" +
"file://" + USER_HOME + "/IdeaProjects/foo -> file://$MODULE_DIR$/..\n" +
"jar:" + USER_HOME + "/IdeaProjects/foo -> jar:$MODULE_DIR$/..\n" +
"jar:/" + USER_HOME + "/IdeaProjects/foo -> jar:/$MODULE_DIR$/..\n" +
"jar://" + USER_HOME + "/IdeaProjects/foo -> jar://$MODULE_DIR$/..\n" +
USER_HOME + "/IdeaProjects/foo -> $MODULE_DIR$/..\n" +
"file:" + USER_HOME + "/IdeaProjects -> file:$MODULE_DIR$/../..\n" +
"file:/" + USER_HOME + "/IdeaProjects -> file:/$MODULE_DIR$/../..\n" +
"file://" + USER_HOME + "/IdeaProjects -> file://$MODULE_DIR$/../..\n" +
"jar:" + USER_HOME + "/IdeaProjects -> jar:$MODULE_DIR$/../..\n" +
"jar:/" + USER_HOME + "/IdeaProjects -> jar:/$MODULE_DIR$/../..\n" +
"jar://" + USER_HOME + "/IdeaProjects -> jar://$MODULE_DIR$/../..\n" +
USER_HOME + "/IdeaProjects -> $MODULE_DIR$/../..\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$", s);
}
private static String mapToString(final ReplacePathToMacroMap replacePathMap) {
final StringBuilder buf = new StringBuilder();
final List<String> pathIndex = replacePathMap.getPathIndex();
for (String s : pathIndex) {
if (buf.length() > 0) buf.append("\n");
buf.append(s);
buf.append(" -> ");
buf.append(replacePathMap.get(s));
}
return buf.toString();
}
}
/*
* 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.mock.MockFileSystem;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.PathMacros;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SystemProperties;
import com.intellij.util.io.fs.FileSystem;
import com.intellij.util.io.fs.IFileSystem;
import org.hamcrest.Description;
import org.jetbrains.annotations.Nullable;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* @author mike
*/
@RunWith(JMock.class)
public class PathMacroManagerTest {
private static final String APP_HOME = FileUtil.toSystemIndependentName(PathManager.getHomePath());
private static final String USER_HOME = FileUtil.toSystemIndependentName(StringUtil.trimEnd(SystemProperties.getUserHome(), "/"));
private Module myModule;
private ProjectEx myProject;
private PathMacrosImpl myPathMacros;
private Mockery context;
protected ApplicationEx myApplication;
private IFileSystem myOldFileSystem;
protected MockFileSystem myFileSystem;
@Before
public final void setupApplication() throws Exception {
context = new JUnit4Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
myApplication = context.mock(ApplicationEx.class, "application");
context.checking(new Expectations() {
{
allowing(myApplication).isUnitTestMode(); will(returnValue(false));
allowing(myApplication).getName(); will(returnValue("IDEA"));
//some tests leave invokeLaters after them...
allowing(myApplication).invokeLater(with(any(Runnable.class)), with(any(ModalityState.class)));
allowing(myApplication).runReadAction(with(any(Runnable.class)));
will(new Action() {
@Override
public void describeTo(final Description description) {
description.appendText("runs runnable");
}
@Override
@Nullable
public Object invoke(final Invocation invocation) throws Throwable {
((Runnable)invocation.getParameter(0)).run();
return null;
}
});
}
});
}
@Before
public final void setupFileSystem() throws Exception {
myOldFileSystem = FileSystem.FILE_SYSTEM;
myFileSystem = new MockFileSystem();
FileSystem.FILE_SYSTEM = myFileSystem;
}
@After
public final void restoreFilesystem() throws Exception {
FileSystem.FILE_SYSTEM = myOldFileSystem;
}
private void setUpMocks(final String projectPath) {
myModule = context.mock(Module.class);
myPathMacros = context.mock(PathMacrosImpl.class);
myProject = context.mock(ProjectEx.class);
final VirtualFile projectFile = context.mock(VirtualFile.class, "projectFile");
context.checking(new Expectations() {{
allowing(myModule).isDisposed(); will(returnValue(false));
allowing(myProject).isDisposed(); will(returnValue(false));
allowing(projectFile).getPath(); will(returnValue(projectPath));
final String moduleFilePath = projectPath + "/module/module.iml";
allowing(myApplication).getComponent(with(equal(PathMacros.class))); will(returnValue(myPathMacros));
allowing(myPathMacros).addMacroReplacements(with(any(ReplacePathToMacroMap.class)));
allowing(myProject).getBaseDir(); will(returnValue(projectFile));
allowing(myModule).getModuleFilePath(); will(returnValue(moduleFilePath));
allowing(myModule).getProject(); will(returnValue(myProject));
}});
}
@Test
public void testRightMacrosOrder_RelativeValues_NoVariables() throws Exception {
setUpMocks("/tmp/foo");
final ReplacePathToMacroMap replacePathMap = new ModulePathMacroManager(myPathMacros, myModule).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:/tmp/foo/module -> file:$MODULE_DIR$\n" +
"file://tmp/foo/module -> file:/$MODULE_DIR$\n" +
"file:///tmp/foo/module -> file://$MODULE_DIR$\n" +
"jar:/tmp/foo/module -> jar:$MODULE_DIR$\n" +
"jar://tmp/foo/module -> jar:/$MODULE_DIR$\n" +
"jar:///tmp/foo/module -> jar://$MODULE_DIR$\n" +
"/tmp/foo/module -> $MODULE_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$\n" +
"file:/tmp/foo -> file:$MODULE_DIR$/..\n" +
"file://tmp/foo -> file:/$MODULE_DIR$/..\n" +
"file:///tmp/foo -> file://$MODULE_DIR$/..\n" +
"jar:/tmp/foo -> jar:$MODULE_DIR$/..\n" +
"jar://tmp/foo -> jar:/$MODULE_DIR$/..\n" +
"jar:///tmp/foo -> jar://$MODULE_DIR$/..\n" +
"/tmp/foo -> $MODULE_DIR$/..\n" +
"file:/tmp -> file:$MODULE_DIR$/../..\n" +
"file://tmp -> file:/$MODULE_DIR$/../..\n" +
"file:///tmp -> file://$MODULE_DIR$/../..\n" +
"jar:/tmp -> jar:$MODULE_DIR$/../..\n" +
"jar://tmp -> jar:/$MODULE_DIR$/../..\n" +
"jar:///tmp -> jar://$MODULE_DIR$/../..\n" +
"/tmp -> $MODULE_DIR$/../..", s);
}
@Test
public void testPathsOutsideProject() throws Exception {
setUpMocks("/tmp/foo");
final ReplacePathToMacroMap replacePathMap = new ProjectPathMacroManager(myPathMacros, myProject).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:/tmp/foo -> file:$PROJECT_DIR$\n" +
"file://tmp/foo -> file:/$PROJECT_DIR$\n" +
"file:///tmp/foo -> file://$PROJECT_DIR$\n" +
"jar:/tmp/foo -> jar:$PROJECT_DIR$\n" +
"jar://tmp/foo -> jar:/$PROJECT_DIR$\n" +
"jar:///tmp/foo -> jar://$PROJECT_DIR$\n" +
"/tmp/foo -> $PROJECT_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$\n" +
"file:/tmp -> file:$PROJECT_DIR$/..\n" +
"file://tmp -> file:/$PROJECT_DIR$/..\n" +
"file:///tmp -> file://$PROJECT_DIR$/..\n" +
"jar:/tmp -> jar:$PROJECT_DIR$/..\n" +
"jar://tmp -> jar:/$PROJECT_DIR$/..\n" +
"jar:///tmp -> jar://$PROJECT_DIR$/..\n" +
"/tmp -> $PROJECT_DIR$/..", s);
}
@Test
public void testProjectUnderUserHome() throws Exception {
setUpMocks(USER_HOME + "/IdeaProjects/foo");
final ReplacePathToMacroMap replacePathMap = new ModulePathMacroManager(myPathMacros, myModule).getReplacePathMap();
final String s = mapToString(replacePathMap);
assertEquals("file:" + USER_HOME + "/IdeaProjects/foo/module -> file:$MODULE_DIR$\n" +
"file:/" + USER_HOME + "/IdeaProjects/foo/module -> file:/$MODULE_DIR$\n" +
"file://" + USER_HOME + "/IdeaProjects/foo/module -> file://$MODULE_DIR$\n" +
"jar:" + USER_HOME + "/IdeaProjects/foo/module -> jar:$MODULE_DIR$\n" +
"jar:/" + USER_HOME + "/IdeaProjects/foo/module -> jar:/$MODULE_DIR$\n" +
"jar://" + USER_HOME + "/IdeaProjects/foo/module -> jar://$MODULE_DIR$\n" +
USER_HOME + "/IdeaProjects/foo/module -> $MODULE_DIR$\n" +
APP_HOME + " -> $APPLICATION_HOME_DIR$\n" +
"file:" + APP_HOME + " -> file:$APPLICATION_HOME_DIR$\n" +
"file:/" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"file://" + APP_HOME + " -> file://$APPLICATION_HOME_DIR$\n" +
"jar:" + APP_HOME + " -> jar:$APPLICATION_HOME_DIR$\n" +
"jar:/" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"jar://" + APP_HOME + " -> jar://$APPLICATION_HOME_DIR$\n" +
"file:" + USER_HOME + "/IdeaProjects/foo -> file:$MODULE_DIR$/..\n" +
"file:/" + USER_HOME + "/IdeaProjects/foo -> file:/$MODULE_DIR$/..\n" +
"file://" + USER_HOME + "/IdeaProjects/foo -> file://$MODULE_DIR$/..\n" +
"jar:" + USER_HOME + "/IdeaProjects/foo -> jar:$MODULE_DIR$/..\n" +
"jar:/" + USER_HOME + "/IdeaProjects/foo -> jar:/$MODULE_DIR$/..\n" +
"jar://" + USER_HOME + "/IdeaProjects/foo -> jar://$MODULE_DIR$/..\n" +
USER_HOME + "/IdeaProjects/foo -> $MODULE_DIR$/..\n" +
"file:" + USER_HOME + "/IdeaProjects -> file:$MODULE_DIR$/../..\n" +
"file:/" + USER_HOME + "/IdeaProjects -> file:/$MODULE_DIR$/../..\n" +
"file://" + USER_HOME + "/IdeaProjects -> file://$MODULE_DIR$/../..\n" +
"jar:" + USER_HOME + "/IdeaProjects -> jar:$MODULE_DIR$/../..\n" +
"jar:/" + USER_HOME + "/IdeaProjects -> jar:/$MODULE_DIR$/../..\n" +
"jar://" + USER_HOME + "/IdeaProjects -> jar://$MODULE_DIR$/../..\n" +
USER_HOME + "/IdeaProjects -> $MODULE_DIR$/../..\n" +
USER_HOME + " -> $USER_HOME$\n" +
"file:" + USER_HOME + " -> file:$USER_HOME$\n" +
"file:/" + USER_HOME + " -> file://$USER_HOME$\n" +
"file://" + USER_HOME + " -> file://$USER_HOME$\n" +
"jar:" + USER_HOME + " -> jar:$USER_HOME$\n" +
"jar:/" + USER_HOME + " -> jar://$USER_HOME$\n" +
"jar://" + USER_HOME + " -> jar://$USER_HOME$", s);
}
private static String mapToString(final ReplacePathToMacroMap replacePathMap) {
final StringBuilder buf = new StringBuilder();
final List<String> pathIndex = replacePathMap.getPathIndex();
for (String s : pathIndex) {
if (buf.length() > 0) buf.append("\n");
buf.append(s);
buf.append(" -> ");
buf.append(replacePathMap.get(s));
}
return buf.toString();
}
}
@@ -1,220 +1,212 @@
/*
* 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.components.impl.BasePathMacroManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: Dec 6, 2004
*/
public class ReplacePathToMacroMap extends PathMacroMap {
private List<String> myPathsIndex = null;
private static final Comparator<Map.Entry<String, String>> PATHS_COMPARATOR = new Comparator<Map.Entry<String, String>>() {
public int compare(final Map.Entry<String, String> o1, final Map.Entry<String, String> o2) {
int idx1 = getIndex(o1);
int idx2 = getIndex(o2);
if (idx1 != idx2) return idx1 - idx2;
return stripPrefix(o2.getKey()).length() - stripPrefix(o1.getKey()).length();
}
private int getIndex(final Map.Entry<String, String> s) {
final String replacement = s.getValue();
if (replacement.indexOf("..") >= 0) return 3;
if (replacement.indexOf("$" + PathMacrosImpl.USER_HOME_MACRO_NAME + "$") >= 0) return 3;
if (replacement.indexOf("$" + PathMacrosImpl.MODULE_DIR_MACRO_NAME + "$") >= 0) return 1;
if (replacement.indexOf("$" + PathMacrosImpl.PROJECT_DIR_MACRO_NAME + "$") >= 0) return 1;
return 2;
}
private String stripPrefix(String key) {
key = StringUtil.trimStart(key, "jar:");
key = StringUtil.trimStart(key, "file:");
while (key.startsWith("/")) {
key = key.substring(1);
}
return key;
}
};
@NonNls private static final String[] PROTOCOLS = new String[]{"file", "jar"};
public void addMacroReplacement(String path, String macroName) {
final String p = quotePath(path);
final String m = "$" + macroName + "$";
if (BasePathMacroManager.DEBUG) {
System.out.println("BasePathMacroManager.addFileHierarchyReplacements");
System.out.println("path = " + p);
System.out.println("macro = " + macroName);
}
put(p, m);
for (String protocol : PROTOCOLS) {
put(protocol + ":" + p, protocol + ":" + m);
put(protocol + ":/" + p, protocol + "://" + m);
put(protocol + "://" + p, protocol + "://" + m);
}
}
public String substitute(String text, boolean caseSensitive) {
if (text == null) return null;
for (final String path : getPathIndex()) {
final String macro = get(path);
text = replacePathMacro(text, path, macro, caseSensitive);
}
return text;
}
private static String replacePathMacro(String text,
String path,
final String macro,
boolean caseSensitive) {
if (text.length() < path.length() || path.length() == 0) {
return text;
}
boolean startsWith;
if (caseSensitive) {
startsWith = text.startsWith(path);
}
else {
startsWith = StringUtil.startsWithIgnoreCase(text, path);
}
if (!startsWith) return text;
final StringBuilder newText = StringBuilderSpinAllocator.alloc();
try {
//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/cdef"
int endOfOccurence = path.length();
final boolean isWindowsRoot = path.endsWith(":/");
if (!isWindowsRoot &&
endOfOccurence < text.length() &&
text.charAt(endOfOccurence) != '/' &&
!text.substring(endOfOccurence).startsWith("!/")) {
return text;
}
newText.append(macro);
newText.append(text.substring(endOfOccurence));
return newText.toString();
}
finally {
StringBuilderSpinAllocator.dispose(newText);
}
}
@Override
public String substituteRecursively(String text, final boolean caseSensitive) {
for (final String path : getPathIndex()) {
final String macro = get(path);
text = replacePathMacroRecursively(text, path, macro, caseSensitive);
}
return text;
}
private static String replacePathMacroRecursively(String text,
String path,
final String macro,
boolean caseSensitive) {
if (text.length() < path.length()) {
return text;
}
if (path.length() == 0) return text;
final StringBuilder newText = StringBuilderSpinAllocator.alloc();
try {
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 endOfOccurence = occurrenceOfPath + path.length();
if (!isWindowsRoot &&
endOfOccurence < text.length() &&
text.charAt(endOfOccurence) != '/' &&
text.charAt(endOfOccurence) != '\"' &&
text.charAt(endOfOccurence) != ' ' &&
!text.substring(endOfOccurence).startsWith("!/")) {
newText.append(text.substring(i, endOfOccurence));
i = endOfOccurence;
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(macro);
i = occurrenceOfPath + path.length();
}
}
return newText.toString();
}
finally {
StringBuilderSpinAllocator.dispose(newText);
}
}
public List<String> getPathIndex() {
if (myPathsIndex == null || myPathsIndex.size() != size()) {
final Set<Map.Entry<String, String>> entrySet = entries();
Map.Entry<String, String>[] entries = entrySet.toArray(new Map.Entry[entrySet.size()]);
ContainerUtil.sort(entries, PATHS_COMPARATOR);
myPathsIndex = new ArrayList<String>(entries.length);
for (Map.Entry<String, String> entry : entries) {
myPathsIndex.add(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();
}
}
/*
* 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.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: Dec 6, 2004
*/
public class ReplacePathToMacroMap extends PathMacroMap {
private List<String> myPathsIndex = null;
@NonNls private static final String[] PROTOCOLS = new String[]{"file", "jar"};
public void addMacroReplacement(String path, String macroName) {
final String p = quotePath(path);
final String m = "$" + macroName + "$";
put(p, m);
for (String protocol : PROTOCOLS) {
put(protocol + ":" + p, protocol + ":" + m);
put(protocol + ":/" + p, protocol + "://" + m);
put(protocol + "://" + p, protocol + "://" + m);
}
}
@Nullable
public String substitute(String text, boolean caseSensitive) {
if (text == null) return null;
for (final String path : getPathIndex()) {
final String macro = get(path);
text = replacePathMacro(text, path, macro, caseSensitive);
}
return text;
}
private static String replacePathMacro(String text,
String path,
final String macro,
boolean caseSensitive) {
if (text.length() < path.length() || path.length() == 0) {
return text;
}
boolean startsWith;
if (caseSensitive) {
startsWith = text.startsWith(path);
}
else {
startsWith = StringUtil.startsWithIgnoreCase(text, path);
}
if (!startsWith) return text;
final StringBuilder newText = StringBuilderSpinAllocator.alloc();
try {
//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/cdef"
int endOfOccurence = path.length();
final boolean isWindowsRoot = path.endsWith(":/");
if (!isWindowsRoot &&
endOfOccurence < text.length() &&
text.charAt(endOfOccurence) != '/' &&
!text.substring(endOfOccurence).startsWith("!/")) {
return text;
}
newText.append(macro);
newText.append(text.substring(endOfOccurence));
return newText.toString();
}
finally {
StringBuilderSpinAllocator.dispose(newText);
}
}
@Override
public String substituteRecursively(String text, final boolean caseSensitive) {
for (final String path : getPathIndex()) {
final String macro = get(path);
text = replacePathMacroRecursively(text, path, macro, caseSensitive);
}
return text;
}
private static String replacePathMacroRecursively(String text,
String path,
final String macro,
boolean caseSensitive) {
if (text.length() < path.length()) {
return text;
}
if (path.length() == 0) return text;
final StringBuilder newText = StringBuilderSpinAllocator.alloc();
try {
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 endOfOccurence = occurrenceOfPath + path.length();
if (!isWindowsRoot &&
endOfOccurence < text.length() &&
text.charAt(endOfOccurence) != '/' &&
text.charAt(endOfOccurence) != '\"' &&
text.charAt(endOfOccurence) != ' ' &&
!text.substring(endOfOccurence).startsWith("!/")) {
newText.append(text.substring(i, endOfOccurence));
i = endOfOccurence;
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(macro);
i = occurrenceOfPath + path.length();
}
}
return newText.toString();
}
finally {
StringBuilderSpinAllocator.dispose(newText);
}
}
private static int getIndex(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(String key) {
key = StringUtil.trimStart(key, "jar:");
key = StringUtil.trimStart(key, "file:");
while (key.startsWith("/")) {
key = key.substring(1);
}
return key.length();
}
public List<String> getPathIndex() {
if (myPathsIndex == null || myPathsIndex.size() != 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>>() {
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();
}
}
@@ -35,7 +35,7 @@ public class ExpandMacroToPathMap extends PathMacroMap {
public String substitute(String text, boolean caseSensitive) {
if (text == null) return null;
for (Map.Entry<String, String> entry : entries()) {
for (Map.Entry<String, String> entry : myMacroMap.entrySet()) {
// when replacing macros with actual paths the replace utility may be used as always 'case-sensitive'
// for case-insensitive file systems there will be no unnecesary toLowerCase() transforms.
text = StringUtil.replace(text, entry.getKey(), entry.getValue(), false);
@@ -105,10 +105,6 @@ public abstract class PathMacroMap {
return myMacroMap.size();
}
protected Set<Map.Entry<String, String>> entries() {
return myMacroMap.entrySet();
}
protected Set<String> keySet() {
return myMacroMap.keySet();
}
@@ -1,259 +1,249 @@
/*
* 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.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.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class BasePathMacroManager extends PathMacroManager {
public static boolean DEBUG = false;
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 + "/..");
final String path = FileUtil.toSystemIndependentName(f.getCanonicalPath());
if (StringUtil.endsWithChar(path, '/')) {
result.put(macro + "/", path);
result.put(macro, path.substring(0, path.length()-1));
}
else {
result.put(macro, path);
}
}
protected static void addFileHierarchyReplacements(ReplacePathToMacroMap result, String macroName, @Nullable String path, @Nullable String stopAt) {
if (path == null) return;
String macro = "$" + macroName + "$";
if (DEBUG) {
System.out.println("BasePathMacroManager.addFileHierarchyReplacements");
System.out.println("macroName = " + macroName);
}
path = StringUtil.trimEnd(FileUtil.toSystemIndependentName(path), "/");
boolean check = false;
while (StringUtil.isNotEmpty(path) && path.contains("/")) {
if (DEBUG) {
System.out.println("path = " + path);
System.out.println("macro = " + macro);
}
putIfAbsent(result, "file:" + path, "file:" + macro, check);
putIfAbsent(result, "file:/" + path, "file:/" + macro, check);
putIfAbsent(result, "file://" + path, "file://" + macro, check);
putIfAbsent(result, "jar:" + path, "jar:" + macro, check);
putIfAbsent(result, "jar:/" + path, "jar:/" + macro, check);
putIfAbsent(result, "jar://" + path, "jar://" + macro, check);
if (!path.equalsIgnoreCase("e:/") && !path.equalsIgnoreCase("r:/") && !path.equalsIgnoreCase("p:/")) {
putIfAbsent(result, path, macro, check);
}
if (path.equals(stopAt)) {
break;
}
macro += "/..";
check = true;
path = StringUtil.getPackageName(path, '/');
}
}
private static VirtualFileSystem getLocalFileSystem() {
// Use VFM directly because of mocks in tests.
return VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL);
}
private static void putIfAbsent(final ReplacePathToMacroMap result,
@NonNls final String pathWithPrefix,
@NonNls final String substitutionWithPrefix,
final boolean check) {
if (check && result.get(pathWithPrefix) != null)
return;
if (StringUtil.endsWithChar(pathWithPrefix, '/')) {
result.put(pathWithPrefix, substitutionWithPrefix + "/");
result.put(pathWithPrefix.substring(0, pathWithPrefix.length()-1), substitutionWithPrefix);
}
else {
result.put(pathWithPrefix, substitutionWithPrefix);
}
}
protected ExpandMacroToPathMap getExpandMacroMap() {
ExpandMacroToPathMap result = new ExpandMacroToPathMap();
for (Map.Entry<String, String> entry : PathMacrosImpl.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 : PathMacrosImpl.getGlobalSystemMacros().entrySet()) {
result.addMacroReplacement(entry.getValue(), entry.getKey());
}
getPathMacros().addMacroReplacements(result);
return result;
}
public TrackingPathMacroSubstitutor createTrackingSubstitutor() {
return new MyTrackingPathMacroSubstitutor();
}
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public void collapsePathsRecursively(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, true);
}
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
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() {
}
public void reset() {
myMacroToComponentNames.clear();
myComponentNameToMacros.clear();
}
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public void collapsePaths(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public int hashCode() {
return getExpandMacroMap().hashCode();
}
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);
}
}
public Collection<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;
}
public Collection<String> getUnknownMacros(final String componentName) {
final Set<String> result = new HashSet<String>();
result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName));
return Collections.unmodifiableCollection(result);
}
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));
}
}
/*
* 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.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.NonNls;
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 + "/..");
final String path = FileUtil.toSystemIndependentName(f.getCanonicalPath());
if (StringUtil.endsWithChar(path, '/')) {
result.put(macro + "/", path);
result.put(macro, path.substring(0, path.length()-1));
}
else {
result.put(macro, path);
}
}
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 check = false;
while (StringUtil.isNotEmpty(path) && path.contains("/")) {
putIfAbsent(result, "file:" + path, "file:" + macro, check);
putIfAbsent(result, "file:/" + path, "file:/" + macro, check);
putIfAbsent(result, "file://" + path, "file://" + macro, check);
putIfAbsent(result, "jar:" + path, "jar:" + macro, check);
putIfAbsent(result, "jar:/" + path, "jar:/" + macro, check);
putIfAbsent(result, "jar://" + path, "jar://" + macro, check);
if (!path.equalsIgnoreCase("e:/") && !path.equalsIgnoreCase("r:/") && !path.equalsIgnoreCase("p:/")) {
putIfAbsent(result, path, macro, check);
}
if (path.equals(stopAt)) {
break;
}
macro += "/..";
check = true;
path = StringUtil.getPackageName(path, '/');
}
}
private static VirtualFileSystem getLocalFileSystem() {
// Use VFM directly because of mocks in tests.
return VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL);
}
private static void putIfAbsent(final ReplacePathToMacroMap result,
@NonNls final String pathWithPrefix,
@NonNls final String substitutionWithPrefix,
final boolean check) {
if (check && result.get(pathWithPrefix) != null)
return;
if (StringUtil.endsWithChar(pathWithPrefix, '/')) {
result.put(pathWithPrefix, substitutionWithPrefix + "/");
result.put(pathWithPrefix.substring(0, pathWithPrefix.length()-1), substitutionWithPrefix);
}
else {
result.put(pathWithPrefix, substitutionWithPrefix);
}
}
protected ExpandMacroToPathMap getExpandMacroMap() {
ExpandMacroToPathMap result = new ExpandMacroToPathMap();
for (Map.Entry<String, String> entry : PathMacrosImpl.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 : PathMacrosImpl.getGlobalSystemMacros().entrySet()) {
result.addMacroReplacement(entry.getValue(), entry.getKey());
}
getPathMacros().addMacroReplacements(result);
return result;
}
public TrackingPathMacroSubstitutor createTrackingSubstitutor() {
return new MyTrackingPathMacroSubstitutor();
}
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public void collapsePathsRecursively(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, true);
}
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
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() {
}
public void reset() {
myMacroToComponentNames.clear();
myComponentNameToMacros.clear();
}
public String expandPath(final String path) {
return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public String collapsePath(final String path) {
return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive);
}
public void expandPaths(final Element element) {
getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public void collapsePaths(final Element element) {
getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive);
}
public int hashCode() {
return getExpandMacroMap().hashCode();
}
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);
}
}
public Collection<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;
}
public Collection<String> getUnknownMacros(final String componentName) {
final Set<String> result = new HashSet<String>();
result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName));
return Collections.unmodifiableCollection(result);
}
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));
}
}