From 9b6e311e4829d8e9f68268e43e952cfc43d79d57 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 10 Jul 2012 13:15:56 +0200 Subject: [PATCH] faster path macro replacement index building (IDEA-84390) --- .../components/impl/PathMacroManagerTest.java | 544 +++++++++--------- .../options/ReplacePathToMacroMap.java | 432 +++++++------- .../components/ExpandMacroToPathMap.java | 2 +- .../openapi/components/PathMacroMap.java | 4 - .../components/impl/BasePathMacroManager.java | 508 ++++++++-------- 5 files changed, 733 insertions(+), 757 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/PathMacroManagerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/PathMacroManagerTest.java index fecb730cbd1f..268dfb3f354c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/PathMacroManagerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/PathMacroManagerTest.java @@ -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 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 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(); + } +} diff --git a/platform/projectModel-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java b/platform/projectModel-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java index 25758670ed14..7fbabea7c64c 100644 --- a/platform/projectModel-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java +++ b/platform/projectModel-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java @@ -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 myPathsIndex = null; - - private static final Comparator> PATHS_COMPARATOR = new Comparator>() { - public int compare(final Map.Entry o1, final Map.Entry 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 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 getPathIndex() { - if (myPathsIndex == null || myPathsIndex.size() != size()) { - - final Set> entrySet = entries(); - Map.Entry[] entries = entrySet.toArray(new Map.Entry[entrySet.size()]); - ContainerUtil.sort(entries, PATHS_COMPARATOR); - myPathsIndex = new ArrayList(entries.length); - - for (Map.Entry 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 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 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 getPathIndex() { + if (myPathsIndex == null || myPathsIndex.size() != size()) { + List> entries = new ArrayList>(myMacroMap.entrySet()); + + final TObjectIntHashMap> weights = new TObjectIntHashMap>(); + for (Map.Entry entry : entries) { + weights.put(entry, getIndex(entry) * 512 + stripPrefix(entry.getKey())); + } + + ContainerUtil.sort(entries, new Comparator>() { + public int compare(final Map.Entry o1, final Map.Entry o2) { + return weights.get(o2) - weights.get(o1); + } + }); + myPathsIndex = ContainerUtil.map2List(entries, new Function, String>() { + @Override + public String fun(Map.Entry 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(); + } +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java b/platform/projectModel-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java index c177cac64e19..292d1e9b0b5c 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java @@ -35,7 +35,7 @@ public class ExpandMacroToPathMap extends PathMacroMap { public String substitute(String text, boolean caseSensitive) { if (text == null) return null; - for (Map.Entry entry : entries()) { + for (Map.Entry 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); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroMap.java b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroMap.java index 12ab9fe2ec44..03fdbf31b982 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroMap.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroMap.java @@ -105,10 +105,6 @@ public abstract class PathMacroMap { return myMacroMap.size(); } - protected Set> entries() { - return myMacroMap.entrySet(); - } - protected Set keySet() { return myMacroMap.keySet(); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java index e4c6e77b92af..fff3871889bb 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java @@ -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 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 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> myMacroToComponentNames = new FactoryMap>() { - @Override - protected Set create(String key) { - return new HashSet(); - } - }; - - private final Map> myComponentNameToMacros = new FactoryMap>() { - @Override - protected Set create(String key) { - return new HashSet(); - } - }; - - 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 macros) { - for (final String macro : macros) { - final Set components = myMacroToComponentNames.get(macro); - for (final String component : components) { - myComponentNameToMacros.remove(component); - } - - myMacroToComponentNames.remove(macro); - } - } - - public Collection getComponents(final Collection macros) { - final Set result = new HashSet(); - for (String macro : myMacroToComponentNames.keySet()) { - if (macros.contains(macro)) { - result.addAll(myMacroToComponentNames.get(macro)); - } - } - - return result; - } - - public Collection getUnknownMacros(final String componentName) { - final Set result = new HashSet(); - result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName)); - return Collections.unmodifiableCollection(result); - } - - public void addUnknownMacros(final String componentName, final Collection unknownMacros) { - if (unknownMacros.isEmpty()) return; - - for (String unknownMacro : unknownMacros) { - final Set 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 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 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> myMacroToComponentNames = new FactoryMap>() { + @Override + protected Set create(String key) { + return new HashSet(); + } + }; + + private final Map> myComponentNameToMacros = new FactoryMap>() { + @Override + protected Set create(String key) { + return new HashSet(); + } + }; + + 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 macros) { + for (final String macro : macros) { + final Set components = myMacroToComponentNames.get(macro); + for (final String component : components) { + myComponentNameToMacros.remove(component); + } + + myMacroToComponentNames.remove(macro); + } + } + + public Collection getComponents(final Collection macros) { + final Set result = new HashSet(); + for (String macro : myMacroToComponentNames.keySet()) { + if (macros.contains(macro)) { + result.addAll(myMacroToComponentNames.get(macro)); + } + } + + return result; + } + + public Collection getUnknownMacros(final String componentName) { + final Set result = new HashSet(); + result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName)); + return Collections.unmodifiableCollection(result); + } + + public void addUnknownMacros(final String componentName, final Collection unknownMacros) { + if (unknownMacros.isEmpty()) return; + + for (String unknownMacro : unknownMacros) { + final Set 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)); + } +}