diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java index 395200e10000..a90138146a4d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java @@ -151,7 +151,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon if (myGeneratedSources.contains(FileBasedIndex.getFileId(file))) { return true; } - if (isUnderRoots(myRootToModuleMap.keySet(), file)) { + if (VfsUtilCore.isUnder(file, myRootToModuleMap.keySet())) { return true; } final Module module = getModuleByFile(file); @@ -449,7 +449,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon if (myProjectFileIndex.isInTestSourceContent(fileOrDir)) { return true; } - if (isUnderRoots(myGeneratedTestRoots, fileOrDir)) { + if (VfsUtilCore.isUnder(fileOrDir, myGeneratedTestRoots)) { return true; } return false; @@ -459,25 +459,12 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon if (myProjectFileIndex.isInSourceContent(fileOrDir)) { return true; } - if (isUnderRoots(myRootToModuleMap.keySet(), fileOrDir)) { + if (VfsUtilCore.isUnder(fileOrDir, myRootToModuleMap.keySet())) { return true; } return false; } - public static boolean isUnderRoots(@NotNull Set roots, @NotNull VirtualFile file) { - VirtualFile parent = file; - while (true) { - if (parent == null) { - return false; - } - if (roots.contains(parent)) { - return true; - } - parent = parent.getParent(); - } - } - public UUID getSessionId() { return mySessionId; } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExcludedRootElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExcludedRootElement.java new file mode 100644 index 000000000000..b1fad5c3f3f5 --- /dev/null +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExcludedRootElement.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2013 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.roots.ui.configuration.libraryEditor; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFileManager; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class ExcludedRootElement extends LibraryTableTreeContentElement { + @NotNull private final String myUrl; + + public ExcludedRootElement(@NotNull NodeDescriptor parentDescriptor, String rootUrl, @NotNull String excludedUrl) { + super(parentDescriptor); + myUrl = excludedUrl; + if (excludedUrl.startsWith(rootUrl)) { + String relativePath = StringUtil.trimStart(excludedUrl.substring(rootUrl.length()), "/"); + myName = relativePath.isEmpty() ? "" : relativePath; + } + else { + myName = ItemElement.getPresentablePath(excludedUrl); + } + myColor = getForegroundColor(VirtualFileManager.getInstance().findFileByUrl(excludedUrl) != null); + setIcon(AllIcons.Modules.ExcludeRoot); + } + + @NotNull + public String getUrl() { + return myUrl; + } +} diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java index 996a546ae818..7e4b7721619e 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java @@ -29,17 +29,17 @@ import java.util.Arrays; import java.util.Collection; public class ExistingLibraryEditor extends LibraryEditorBase implements Disposable { - private final Library myLibrary; + private final LibraryEx myLibrary; private final LibraryEditorListener myListener; private String myLibraryName = null; private LibraryProperties myLibraryProperties; private LibraryProperties myDetectedLibraryProperties; - private Library.ModifiableModel myModel = null; + private LibraryEx.ModifiableModelEx myModel = null; private LibraryType myDetectedType; private boolean myDetectedTypeComputed; public ExistingLibraryEditor(@NotNull Library library, @Nullable LibraryEditorListener listener) { - myLibrary = library; + myLibrary = (LibraryEx)library; myListener = listener; } @@ -66,7 +66,7 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab @Override public void setType(@NotNull LibraryType type) { - ((LibraryEx.ModifiableModelEx)getModel()).setKind(type.getKind()); + getModel().setKind(type.getKind()); } private LibraryType detectType() { @@ -104,7 +104,7 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab } private LibraryProperties getOriginalProperties() { - return ((LibraryEx)myLibrary).getProperties(); + return myLibrary.getProperties(); } @Override @@ -131,6 +131,14 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab return myLibrary.getFiles(rootType); } + @Override + public String[] getExcludedRootUrls() { + if (myModel != null) { + return myModel.getExcludedRootUrls(); + } + return myLibrary.getExcludedRootUrls(); + } + @Override public void setName(String name) { String oldName = getModel().getName(); @@ -151,6 +159,11 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab getModel().addRoot(url, rootType); } + @Override + public void addExcludedRoot(@NotNull String url) { + getModel().addExcludedRoot(url); + } + @Override public void addJarDirectory(VirtualFile file, boolean recursive, OrderRootType rootType) { getModel().addJarDirectory(file, recursive, rootType); @@ -163,13 +176,22 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab @Override public void removeRoot(String url, OrderRootType rootType) { - while (getModel().removeRoot(url, rootType)) ; + boolean removed; + do { + removed = getModel().removeRoot(url, rootType); + } + while (removed); + } + + @Override + public void removeExcludedRoot(@NotNull String url) { + getModel().removeExcludedRoot(url); } public void commit() { if (myModel != null) { if (myLibraryProperties != null) { - ((LibraryEx.ModifiableModelEx)myModel).setProperties(myLibraryProperties); + myModel.setProperties(myLibraryProperties); } myModel.commit(); myModel = null; @@ -178,7 +200,7 @@ public class ExistingLibraryEditor extends LibraryEditorBase implements Disposab } } - public Library.ModifiableModel getModel() { + public LibraryEx.ModifiableModelEx getModel() { if (myModel == null) { myModel = myLibrary.getModifiableModel(); } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java index 86cb7a9fe99e..e99efe8cca79 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java @@ -23,69 +23,25 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.http.HttpFileSystem; -import com.intellij.ui.JBColor; import com.intellij.util.PlatformIcons; -import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.io.File; class ItemElement extends LibraryTableTreeContentElement { - private final String myUrl; + protected final String myUrl; private final OrderRootType myRootType; - public ItemElement(OrderRootTypeElement parent, String url, OrderRootType rootType, final boolean isJarDirectory, + public ItemElement(@NotNull OrderRootTypeElement parent, @NotNull String url, @NotNull OrderRootType rootType, final boolean isJarDirectory, boolean isValid) { super(parent); myUrl = url; - myRootType = rootType; myName = getPresentablePath(url).replace('/', File.separatorChar); - myColor = isValid ? UIUtil.getListForeground() : JBColor.RED; + myColor = getForegroundColor(isValid); setIcon(getIconForUrl(url, isValid, isJarDirectory)); - } - - public OrderRootTypeElement getParent() { - return (OrderRootTypeElement)getParentDescriptor(); - } - - @Override - public boolean update() { - return false; - } - - @Override - public ItemElement getElement() { - return this; - } - - public String getUrl() { - return myUrl; - } - - public OrderRootType getRootType() { - return myRootType; - } - - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ItemElement)) return false; - - final ItemElement itemElement = (ItemElement)o; - - if (!getParent().equals(itemElement.getParent())) return false; - if (!myRootType.equals(itemElement.myRootType)) return false; - if (!myUrl.equals(itemElement.myUrl)) return false; - - return true; - } - - public int hashCode() { - int result; - result = getParent().hashCode(); - result = 29 * result + myUrl.hashCode(); - result = 29 * result + myRootType.hashCode(); - return result; + myRootType = rootType; } private static Icon getIconForUrl(final String url, final boolean isValid, final boolean isJarDirectory) { @@ -126,7 +82,7 @@ class ItemElement extends LibraryTableTreeContentElement { return icon; } - private static String getPresentablePath(final String url) { + public static String getPresentablePath(final String url) { String presentablePath = VirtualFileManager.extractPath(url); if (isJarFileRoot(url)) { presentablePath = presentablePath.substring(0, presentablePath.length() - JarFileSystem.JAR_SEPARATOR.length()); @@ -137,4 +93,39 @@ class ItemElement extends LibraryTableTreeContentElement { private static boolean isJarFileRoot(final String url) { return VirtualFileManager.extractPath(url).endsWith(JarFileSystem.JAR_SEPARATOR); } + + public OrderRootTypeElement getParent() { + return (OrderRootTypeElement)getParentDescriptor(); + } + + @NotNull + public OrderRootType getRootType() { + return myRootType; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ItemElement)) return false; + + final ItemElement itemElement = (ItemElement)o; + + if (!getParent().equals(itemElement.getParent())) return false; + if (!myRootType.equals(itemElement.myRootType)) return false; + if (!myUrl.equals(itemElement.myUrl)) return false; + + return true; + } + + @NotNull + public String getUrl() { + return myUrl; + } + + public int hashCode() { + int result; + result = getParent().hashCode(); + result = 29 * result + myUrl.hashCode(); + result = 29 * result + myRootType.hashCode(); + return result; + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java index f4c6db4c3f1f..b69163ff5779 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.roots.ui.configuration.libraryEditor; +import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.ide.util.treeView.AbstractTreeStructure; import com.intellij.ide.util.treeView.NodeDescriptor; @@ -26,11 +27,13 @@ import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.PersistentOrderRootType; import com.intellij.openapi.roots.libraries.LibraryKind; import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.LibraryType; @@ -42,14 +45,17 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.ui.AnActionButton; import com.intellij.ui.AnActionButtonRunnable; -import com.intellij.ui.AnActionButtonUpdater; import com.intellij.ui.ToolbarDecorator; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.*; +import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -75,6 +81,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent private LibraryPropertiesEditor myPropertiesEditor; private Tree myTree; private LibraryTableTreeBuilder myTreeBuilder; + private VirtualFile myLastChosen; private final Collection myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @Nullable private final Project myProject; @@ -82,6 +89,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent private final Computable myLibraryEditorComputable; private LibraryRootsComponentDescriptor myDescriptor; private Module myContextModule; + private LibraryRootsComponent.AddExcludedRootActionButton myAddExcludedRootActionButton; public LibraryRootsComponent(@Nullable Project project, @NotNull LibraryEditor libraryEditor) { this(project, new Computable.PredefinedValueComputable(libraryEditor)); @@ -105,6 +113,11 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent } init(new LibraryTreeStructure(this, myDescriptor)); updatePropertiesLabel(); + onRootsChanged(); + } + + private void onRootsChanged() { + myAddExcludedRootActionButton.setEnabled(!getNotExcludedRoots().isEmpty()); } @NotNull @@ -166,10 +179,13 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent getLibraryEditor().removeRoot(url, rootType); } } + else if (selectedElement instanceof ExcludedRootElement) { + getLibraryEditor().removeExcludedRoot(((ExcludedRootElement)selectedElement).getUrl()); + } } } }); - librariesChanged(true); + libraryChanged(true); } }); @@ -187,6 +203,9 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent popupItems.add(descriptor); } } + myAddExcludedRootActionButton = new AddExcludedRootActionButton(); + toolbarDecorator.addExtraAction(myAddExcludedRootActionButton); + actionsOrder.add(myAddExcludedRootActionButton.getTemplatePresentation().getText()); actionsOrder.add("Remove"); toolbarDecorator.setAddAction(new AnActionButtonRunnable() { @@ -212,22 +231,6 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent toolbarDecorator.setButtonComparator(ArrayUtil.toStringArray(actionsOrder)); myTreePanel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER); - ToolbarDecorator.findRemoveButton(myTreePanel).addCustomUpdater(new AnActionButtonUpdater() { - @Override - public boolean isEnabled(AnActionEvent e) { - final Object[] selectedElements = getSelectedElements(); - for (Object element : selectedElements) { - if (element instanceof ItemElement) { - return true; - } - if (element instanceof OrderRootTypeElement && getLibraryEditor().getUrls(((OrderRootTypeElement)element).getOrderRootType()).length > 0) { - return true; - } - } - return false; - } - }); - Disposer.register(this, myTreeBuilder); } @@ -330,7 +333,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent public void renameLibrary(String newName) { final LibraryEditor libraryEditor = getLibraryEditor(); libraryEditor.setName(newName); - librariesChanged(false); + libraryChanged(false); } @Override @@ -360,6 +363,19 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent } } + @Nullable + private VirtualFile getFileToSelect() { + if (myLastChosen != null) { + return myLastChosen; + } + + final VirtualFile directory = getExistingRootDirectory(); + if (directory != null) { + return directory; + } + return getBaseDirectory(); + } + private class AttachFilesAction extends AttachItemActionBase { public AttachFilesAction(String title) { super(title); @@ -380,25 +396,10 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent } public abstract class AttachItemActionBase extends DumbAwareAction { - private VirtualFile myLastChosen = null; - protected AttachItemActionBase(String text) { super(text); } - @Nullable - protected VirtualFile getFileToSelect() { - if (myLastChosen != null) { - return myLastChosen; - } - - final VirtualFile directory = getExistingRootDirectory(); - if (directory != null) { - return directory; - } - return getBaseDirectory(); - } - @Override public void actionPerformed(@Nullable AnActionEvent e) { VirtualFile toSelect = getFileToSelect(); @@ -410,7 +411,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent if (first != null) { myLastChosen = first.getFile(); } - fireLibrariesChanged(); + fireLibraryChanged(); myTree.requestFocus(); } @@ -449,6 +450,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent } }); updatePropertiesLabel(); + onRootsChanged(); myTreeBuilder.queueUpdate(); } return rootsToAttach; @@ -465,16 +467,17 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent return result; } - private void librariesChanged(boolean putFocusIntoTree) { + private void libraryChanged(boolean putFocusIntoTree) { + onRootsChanged(); updatePropertiesLabel(); myTreeBuilder.queueUpdate(); if (putFocusIntoTree) { myTree.requestFocus(); } - fireLibrariesChanged(); + fireLibraryChanged(); } - private void fireLibrariesChanged() { + private void fireLibraryChanged() { for (Runnable listener : myListeners) { listener.run(); } @@ -487,4 +490,61 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent public void removeListener(Runnable listener) { myListeners.remove(listener); } + + private Set getNotExcludedRoots() { + Set roots = new LinkedHashSet(); + String[] excludedRootUrls = getLibraryEditor().getExcludedRootUrls(); + Set excludedRoots = new HashSet(); + for (String url : excludedRootUrls) { + ContainerUtil.addIfNotNull(excludedRoots, VirtualFileManager.getInstance().findFileByUrl(url)); + } + for (PersistentOrderRootType type : OrderRootType.getAllPersistentTypes()) { + VirtualFile[] files = getLibraryEditor().getFiles(type); + for (VirtualFile file : files) { + if (!VfsUtilCore.isUnder(file, excludedRoots)) { + roots.add(PathUtil.getLocalFile(file)); + } + } + } + return roots; + } + + private class AddExcludedRootActionButton extends AnActionButton { + public AddExcludedRootActionButton() { + super("Add Excluded", null, AllIcons.Modules.ExcludeRoot); + } + + @Override + public void actionPerformed(AnActionEvent e) { + FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createMultipleJavaPathDescriptor(); + descriptor.setTitle("Add Excluded Roots"); + descriptor.setDescription("Select directories which should be excluded from the library content. Content of excluded directories won't be processed by IDE."); + Set roots = getNotExcludedRoots(); + descriptor.setRoots(roots.toArray(new VirtualFile[roots.size()])); + if (roots.size() < 2) { + descriptor.setIsTreeRootVisible(true); + } + VirtualFile toSelect = null; + for (Object o : getSelectedElements()) { + Object itemElement = o instanceof ExcludedRootElement ? ((ExcludedRootElement)o).getParentDescriptor() : o; + if (itemElement instanceof ItemElement) { + toSelect = VirtualFileManager.getInstance().findFileByUrl(((ItemElement)itemElement).getUrl()); + break; + } + } + final VirtualFile[] files = FileChooser.chooseFiles(descriptor, myPanel, myProject, toSelect); + if (files.length > 0) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + for (VirtualFile file : files) { + getLibraryEditor().addExcludedRoot(file.getUrl()); + } + } + }); + myLastChosen = files[0]; + libraryChanged(true); + } + } + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java index 63f224df70c9..7afc7277d93d 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java @@ -41,7 +41,7 @@ class LibraryTableTreeBuilder extends AbstractTreeBuilder { protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { final Object element = nodeDescriptor.getElement(); final Object rootElement = getTreeStructure().getRootElement(); - return rootElement.equals(element) || element instanceof OrderRootTypeElement; + return rootElement.equals(element) || element instanceof OrderRootTypeElement || element instanceof ItemElement; } @Override diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java index 3f6e0c250801..e8ca89bd2b53 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java @@ -16,10 +16,28 @@ package com.intellij.openapi.roots.ui.configuration.libraryEditor; import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.ui.JBColor; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; +import java.awt.*; + public abstract class LibraryTableTreeContentElement extends NodeDescriptor { protected LibraryTableTreeContentElement(@Nullable NodeDescriptor parentDescriptor) { super(null, parentDescriptor); } + + protected static Color getForegroundColor(boolean isValid) { + return isValid ? UIUtil.getListForeground() : JBColor.RED; + } + + @Override + public boolean update() { + return false; + } + + @Override + public E getElement() { + return (E)this; + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java index 9f684155c985..11fab29014c9 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java @@ -21,11 +21,14 @@ import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor; import com.intellij.openapi.roots.libraries.ui.OrderRootTypePresentation; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class LibraryTreeStructure extends AbstractTreeStructure { private final NodeDescriptor myRootElementDescriptor; @@ -55,11 +58,11 @@ public class LibraryTreeStructure extends AbstractTreeStructure { @Override public Object[] getChildElements(Object element) { + final LibraryEditor libraryEditor = myParentEditor.getLibraryEditor(); if (element == myRootElementDescriptor) { ArrayList elements = new ArrayList(3); - final LibraryEditor parentEditor = myParentEditor.getLibraryEditor(); for (OrderRootType type : myComponentDescriptor.getRootTypes()) { - final String[] urls = parentEditor.getUrls(type); + final String[] urls = libraryEditor.getUrls(type); if (urls.length > 0) { OrderRootTypePresentation presentation = myComponentDescriptor.getRootTypePresentation(type); if (presentation == null) { @@ -75,7 +78,6 @@ public class LibraryTreeStructure extends AbstractTreeStructure { OrderRootTypeElement rootTypeElement = (OrderRootTypeElement)element; OrderRootType orderRootType = rootTypeElement.getOrderRootType(); ArrayList items = new ArrayList(); - final LibraryEditor libraryEditor = myParentEditor.getLibraryEditor(); final String[] urls = libraryEditor.getUrls(orderRootType).clone(); Arrays.sort(urls, LibraryRootsComponent.ourUrlComparator); for (String url : urls) { @@ -83,6 +85,22 @@ public class LibraryTreeStructure extends AbstractTreeStructure { } return items.toArray(); } + + if (element instanceof ItemElement) { + ItemElement itemElement = (ItemElement)element; + List excludedUrls = new ArrayList(); + for (String excludedUrl : libraryEditor.getExcludedRootUrls()) { + if (VfsUtilCore.isEqualOrAncestor(itemElement.getUrl(), excludedUrl)) { + excludedUrls.add(excludedUrl); + } + } + ExcludedRootElement[] items = new ExcludedRootElement[excludedUrls.size()]; + Collections.sort(excludedUrls, LibraryRootsComponent.ourUrlComparator); + for (int i = 0; i < excludedUrls.size(); i++) { + items[i] = new ExcludedRootElement(itemElement, itemElement.getUrl(), excludedUrls.get(i)); + } + return items; + } return ArrayUtil.EMPTY_OBJECT_ARRAY; } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java index afc72363c146..e94b4f5ca876 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java @@ -23,6 +23,7 @@ import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.LibraryType; import com.intellij.openapi.roots.ui.LightFilePointer; import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.MultiMap; @@ -31,6 +32,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; /** @@ -39,6 +41,7 @@ import java.util.List; public class NewLibraryEditor extends LibraryEditorBase { private String myLibraryName; private final MultiMap myRoots; + private final List myExcludedRoots; private final JarDirectories myJarDirectories = new JarDirectories(); private LibraryType myType; private LibraryProperties myProperties; @@ -51,6 +54,7 @@ public class NewLibraryEditor extends LibraryEditorBase { myType = type; myProperties = properties; myRoots = new MultiMap(); + myExcludedRoots = new ArrayList(); } @Override @@ -86,7 +90,10 @@ public class NewLibraryEditor extends LibraryEditorBase { @Override public String[] getUrls(OrderRootType rootType) { - final Collection pointers = myRoots.get(rootType); + return pointersToUrls(myRoots.get(rootType)); + } + + private static String[] pointersToUrls(Collection pointers) { List urls = new ArrayList(); for (LightFilePointer pointer : pointers) { urls.add(pointer.getUrl()); @@ -115,6 +122,11 @@ public class NewLibraryEditor extends LibraryEditorBase { return VfsUtil.toVirtualFileArray(result); } + @Override + public String[] getExcludedRootUrls() { + return pointersToUrls(myExcludedRoots); + } + @Override public void setName(String name) { myLibraryName = name; @@ -135,6 +147,15 @@ public class NewLibraryEditor extends LibraryEditorBase { addJarDirectory(file.getUrl(), recursive, rootType); } + @Override + public void addExcludedRoot(@NotNull String url) { + myExcludedRoots.add(new LightFilePointer(url)); + } + + public void removeExcludedRoot(@NotNull String url) { + myExcludedRoots.remove(new LightFilePointer(url)); + } + @Override public void addJarDirectory(final String url, boolean recursive, OrderRootType rootType) { addRoot(url, rootType); @@ -144,9 +165,25 @@ public class NewLibraryEditor extends LibraryEditorBase { @Override public void removeRoot(String url, OrderRootType rootType) { myRoots.remove(rootType, new LightFilePointer(url)); + Iterator iterator = myExcludedRoots.iterator(); + while (iterator.hasNext()) { + LightFilePointer pointer = iterator.next(); + if (!isUnderRoots(pointer.getUrl())) { + iterator.remove(); + } + } myJarDirectories.remove(rootType, url); } + private boolean isUnderRoots(@NotNull String url) { + for (LightFilePointer pointer : myRoots.values()) { + if (VfsUtilCore.isEqualOrAncestor(pointer.getUrl(), url)) { + return true; + } + } + return false; + } + @Override public boolean hasChanges() { return true; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java index 71c5455b8554..27fb0aa4f1f3 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java @@ -48,14 +48,4 @@ public class OrderRootTypeElement extends LibraryTableTreeContentElement roots) { + if (roots == null || roots.isEmpty()) return false; + + VirtualFile parent = file; + while (parent != null) { + if (roots.contains(parent)) { + return true; + } + parent = parent.getParent(); + } + return false; + } + + public static boolean isEqualOrAncestor(@NotNull String ancestorUrl, @NotNull String fileUrl) { + return ancestorUrl.equals(fileUrl) || StringUtil.startsWithConcatenation(fileUrl, ancestorUrl, "/"); + } + public static boolean isAncestor(@NotNull File ancestor, @NotNull File file, boolean strict) { File parent = strict ? file.getParentFile() : file; while (parent != null) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java index da28416b232c..6941065ae985 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java @@ -32,6 +32,8 @@ import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.impl.libraries.LibraryEx; +import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; @@ -245,12 +247,13 @@ public class DirectoryIndexImpl extends DirectoryIndex { if (parentInfo.hasLibraryClassRoot()) { String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); - state.fillMapWithLibraryClasses(file, newDirPackageName, (NewVirtualFile)parentInfo.getLibraryClassRoot(), null, interned); + state.fillMapWithLibraryClasses(file, newDirPackageName, (NewVirtualFile)parentInfo.getLibraryClassRoot(), null, interned, + null); } if (parentInfo.isInLibrarySource()) { String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); - state.fillMapWithLibrarySources(file, newDirPackageName, (NewVirtualFile)parentInfo.getSourceRoot(), null, interned); + state.fillMapWithLibrarySources(file, newDirPackageName, (NewVirtualFile)parentInfo.getSourceRoot(), null, interned, null); } OrderEntry[] entries = parentInfo.getOrderEntries(); @@ -972,8 +975,10 @@ public class DirectoryIndexImpl extends DirectoryIndex { return myDirToPackageName.get(id) == ArrayUtil.EMPTY_INT_ARRAY; } - private void initLibrarySources(@NotNull Module module, @NotNull ProgressIndicator progress, - @Nullable TObjectIntHashMap interned) { + private void initLibrarySources(@NotNull Module module, + @NotNull ProgressIndicator progress, + @Nullable TObjectIntHashMap interned, + Map libraryExcludedRoots) { assertWritable(); progress.checkCanceled(); progress.setText2(ProjectBundle.message("project.index.processing.library.sources.progress", module.getName())); @@ -981,9 +986,10 @@ public class DirectoryIndexImpl extends DirectoryIndex { for (OrderEntry orderEntry : getOrderEntries(module)) { if (orderEntry instanceof LibraryOrSdkOrderEntry) { VirtualFile[] sourceRoots = ((LibraryOrSdkOrderEntry)orderEntry).getRootFiles(OrderRootType.SOURCES); + TIntHashSet excludedRoots = getExcludedRootsOfLibrary((LibraryOrSdkOrderEntry)orderEntry, libraryExcludedRoots); for (final VirtualFile sourceRoot : sourceRoots) { if (sourceRoot instanceof NewVirtualFile) { - fillMapWithLibrarySources((NewVirtualFile)sourceRoot, "", (NewVirtualFile)sourceRoot, progress, interned); + fillMapWithLibrarySources((NewVirtualFile)sourceRoot, "", (NewVirtualFile)sourceRoot, progress, interned, excludedRoots); } } } @@ -994,7 +1000,8 @@ public class DirectoryIndexImpl extends DirectoryIndex { @Nullable final String packageName, @NotNull final NewVirtualFile sourceRoot, @Nullable final ProgressIndicator progress, - @Nullable final TObjectIntHashMap interned) { + @Nullable final TObjectIntHashMap interned, + @Nullable final TIntHashSet excludedRoots) { assertWritable(); if (!isValid(dir)) return; VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() { @@ -1005,6 +1012,7 @@ public class DirectoryIndexImpl extends DirectoryIndex { if (progress != null) progress.checkCanceled(); int dirId = ((NewVirtualFile)file).getId(); if (!file.isDirectory() && dirId != dir.getId() || isIgnored(file)) return false; + if (excludedRoots != null && excludedRoots.contains(dirId)) return false; DirectoryInfo info = getOrCreateDirInfo(dirId); if (info.isInLibrarySource()) { // library sources overlap @@ -1024,8 +1032,10 @@ public class DirectoryIndexImpl extends DirectoryIndex { }); } - private void initLibraryClasses(@NotNull Module module, @NotNull ProgressIndicator progress, - @Nullable TObjectIntHashMap interned) { + private void initLibraryClasses(@NotNull Module module, + @NotNull ProgressIndicator progress, + @Nullable TObjectIntHashMap interned, + @Nullable Map libraryExcludedRoots) { assertWritable(); progress.checkCanceled(); progress.setText2(ProjectBundle.message("project.index.processing.library.classes.progress", module.getName())); @@ -1033,9 +1043,10 @@ public class DirectoryIndexImpl extends DirectoryIndex { for (OrderEntry orderEntry : getOrderEntries(module)) { if (orderEntry instanceof LibraryOrSdkOrderEntry) { VirtualFile[] classRoots = ((LibraryOrSdkOrderEntry)orderEntry).getRootFiles(OrderRootType.CLASSES); + TIntHashSet excludedRoots = getExcludedRootsOfLibrary((LibraryOrSdkOrderEntry)orderEntry, libraryExcludedRoots); for (final VirtualFile classRoot : classRoots) { if (classRoot instanceof NewVirtualFile) { - fillMapWithLibraryClasses((NewVirtualFile)classRoot, "", (NewVirtualFile)classRoot, progress, interned); + fillMapWithLibraryClasses((NewVirtualFile)classRoot, "", (NewVirtualFile)classRoot, progress, interned, excludedRoots); } } } @@ -1043,11 +1054,11 @@ public class DirectoryIndexImpl extends DirectoryIndex { } private void fillMapWithLibraryClasses(@NotNull final NewVirtualFile dir, - @NotNull final String packageName, - @NotNull final NewVirtualFile classRoot, - @Nullable final ProgressIndicator progress, - @Nullable final TObjectIntHashMap interned - ) { + @NotNull final String packageName, + @NotNull final NewVirtualFile classRoot, + @Nullable final ProgressIndicator progress, + @Nullable final TObjectIntHashMap interned, + final TIntHashSet excludedRoots) { assertWritable(); if (!isValid(dir)) return; VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() { @@ -1059,6 +1070,7 @@ public class DirectoryIndexImpl extends DirectoryIndex { if (!file.isDirectory() && !Comparing.equal(file, dir) || isIgnored(file)) return false; int dirId = ((NewVirtualFile)file).getId(); + if (excludedRoots != null && excludedRoots.contains(dirId)) return false; DirectoryInfo info = getOrCreateDirInfo(dirId); if (info.hasLibraryClassRoot()) { // library classes overlap @@ -1079,6 +1091,30 @@ public class DirectoryIndexImpl extends DirectoryIndex { }); } + @Nullable + private TIntHashSet getExcludedRootsOfLibrary(LibraryOrSdkOrderEntry orderEntry, Map libraryExcludedRoots) { + if (orderEntry instanceof LibraryOrderEntry) { + Library library = ((LibraryOrderEntry)orderEntry).getLibrary(); + if (library != null) { + TIntHashSet cached = libraryExcludedRoots.get(library); + if (cached != null) return cached; + + VirtualFile[] files = ((LibraryEx)library).getExcludedRoots(); + if (files.length > 0) { + TIntHashSet set = new TIntHashSet(); + for (VirtualFile file : files) { + if (file instanceof NewVirtualFile) { + set.add(((NewVirtualFile)file).getId()); + } + } + libraryExcludedRoots.put(library, set); + return set; + } + } + } + return null; + } + private void initOrderEntries(@NotNull Module module, @NotNull MultiMap depEntries, @NotNull MultiMap libClassRootEntries, @@ -1252,10 +1288,11 @@ public class DirectoryIndexImpl extends DirectoryIndex { // Important! Because module's contents may overlap, // first modules should be marked and only after that sources markup // should be added. (src markup depends on module markup) + IdentityHashMap libraryExcludedRoots = new IdentityHashMap(); for (Module module : modules) { initModuleSources(module, reverseAllSets, progress, interned); - initLibrarySources(module, progress, interned); - initLibraryClasses(module, progress , interned); + initLibrarySources(module, progress, interned, libraryExcludedRoots); + initLibraryClasses(module, progress , interned, libraryExcludedRoots); } progress.checkCanceled(); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditor.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditor.java index ea684851154b..a99a36aab654 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditor.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditor.java @@ -20,6 +20,7 @@ import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.LibraryType; import com.intellij.openapi.roots.libraries.ui.OrderRoot; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -34,6 +35,8 @@ public interface LibraryEditor { VirtualFile[] getFiles(OrderRootType rootType); + String[] getExcludedRootUrls(); + void setName(String name); void addRoot(VirtualFile file, OrderRootType rootType); @@ -44,8 +47,12 @@ public interface LibraryEditor { void addJarDirectory(String url, boolean recursive, OrderRootType rootType); + void addExcludedRoot(@NotNull String url); + void removeRoot(String url, OrderRootType rootType); + void removeExcludedRoot(@NotNull String url); + void removeAllRoots(); boolean hasChanges(); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java index 980123ed0a1a..ac33bed01964 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java @@ -19,6 +19,7 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.Library; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; @@ -40,11 +41,19 @@ public class ModuleRootModificationUtil { final List classesRoots, final List sourceRoots, final DependencyScope scope) { + addModuleLibrary(module, libName, classesRoots, sourceRoots, Collections.emptyList(), scope); + } + + public static void addModuleLibrary(final Module module, final String libName, + final List classesRoots, + final List sourceRoots, + final List excludedRoots, + final DependencyScope scope) { updateModel(module, new Consumer() { @Override public void consume(final ModifiableRootModel model) { - final Library library = model.getModuleLibraryTable().createLibrary(libName); - final Library.ModifiableModel libraryModel = library.getModifiableModel(); + final LibraryEx library = (LibraryEx)model.getModuleLibraryTable().createLibrary(libName); + final LibraryEx.ModifiableModelEx libraryModel = library.getModifiableModel(); for (String root : classesRoots) { libraryModel.addRoot(root, OrderRootType.CLASSES); @@ -52,6 +61,9 @@ public class ModuleRootModificationUtil { for (String root : sourceRoots) { libraryModel.addRoot(root, OrderRootType.SOURCES); } + for (String excluded : excludedRoots) { + libraryModel.addExcludedRoot(excluded); + } LibraryOrderEntry entry = model.findLibraryOrderEntry(library); assert entry != null : library; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java index 93ebe7031d1f..01e1b984ef74 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Comparing; @@ -592,6 +593,14 @@ public class RootModelImpl extends RootModelBase implements ModifiableRootModel boolean equal = Comparing.equal(libraryOrderEntry1.getLibraryName(), libraryOrderEntry2.getLibraryName()) && Comparing.equal(libraryOrderEntry1.getLibraryLevel(), libraryOrderEntry2.getLibraryLevel()); if (!equal) return false; + + Library library1 = libraryOrderEntry1.getLibrary(); + Library library2 = libraryOrderEntry2.getLibrary(); + if (library1 != null && library2 != null) { + if (!Arrays.equals(((LibraryEx)library1).getExcludedRootUrls(), ((LibraryEx)library2).getExcludedRootUrls())) { + return false; + } + } } final OrderRootType[] allTypes = OrderRootType.getAllTypes(); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryEx.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryEx.java index 40909bd757ef..eba0bb645764 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryEx.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryEx.java @@ -21,6 +21,8 @@ import com.intellij.openapi.roots.impl.RootModelImpl; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.PersistentLibraryKind; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -35,11 +37,21 @@ public interface LibraryEx extends Library { boolean isDisposed(); + @NotNull + @Override + ModifiableModelEx getModifiableModel(); + @Nullable PersistentLibraryKind getKind(); LibraryProperties getProperties(); + @NotNull + String[] getExcludedRootUrls(); + + @NotNull + VirtualFile[] getExcludedRoots(); + interface ModifiableModelEx extends ModifiableModel { void setProperties(LibraryProperties properties); @@ -48,5 +60,12 @@ public interface LibraryEx extends Library { void setKind(PersistentLibraryKind type); PersistentLibraryKind getKind(); + + void addExcludedRoot(@NotNull String url); + + boolean removeExcludedRoot(@NotNull String url); + + @NotNull + String[] getExcludedRootUrls(); } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java index 019c008592d0..ac7ae606a148 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java @@ -62,9 +62,11 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi @NonNls public static final String ELEMENT = "library"; @NonNls public static final String PROPERTIES_ELEMENT = "properties"; private static final SkipDefaultValuesSerializationFilters SERIALIZATION_FILTERS = new SkipDefaultValuesSerializationFilters(); + private static final String EXCLUDED_ROOTS_TAG = "excluded"; private String myName; private final LibraryTable myLibraryTable; private final Map myRoots; + private VirtualFilePointerContainer myExcludedRoots; private final JarDirectories myJarDirectories = new JarDirectories(); private final LibraryImpl mySource; private PersistentLibraryKind myKind; @@ -105,6 +107,9 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi final VirtualFilePointerContainer thatContainer = from.myRoots.get(rootType); thisContainer.addAll(thatContainer); } + if (from.myExcludedRoots != null) { + myExcludedRoots = from.myExcludedRoots.clone(myPointersDisposable); + } myJarDirectories.copyFrom(from.myJarDirectories); } @@ -197,7 +202,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi /* you have to commit modifiable model or dispose it by yourself! */ @Override @NotNull - public ModifiableModel getModifiableModel() { + public ModifiableModelEx getModifiableModel() { assert !isDisposed(); return new LibraryImpl(this, this, myRootModel); } @@ -287,6 +292,17 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi VirtualFilePointerContainer roots = myRoots.get(rootType); roots.readExternal(rootChild, ROOT_PATH_ELEMENT); } + Element excludedRoot = element.getChild(EXCLUDED_ROOTS_TAG); + if (excludedRoot != null) { + getOrCreateExcludedRoots().readExternal(excludedRoot, ROOT_PATH_ELEMENT); + } + } + + private VirtualFilePointerContainer getOrCreateExcludedRoots() { + if (myExcludedRoots == null) { + myExcludedRoots = VirtualFilePointerManager.getInstance().createContainer(myPointersDisposable); + } + return myExcludedRoots; } //TODO Remove the next two methods as a temporary solution. Sort in OrderRootType. @@ -342,6 +358,11 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi roots.writeExternal(rootTypeElement, ROOT_PATH_ELEMENT); element.addContent(rootTypeElement); } + if (myExcludedRoots != null && myExcludedRoots.size() > 0) { + Element excluded = new Element(EXCLUDED_ROOTS_TAG); + myExcludedRoots.writeExternal(excluded, ROOT_PATH_ELEMENT); + element.addContent(excluded); + } myJarDirectories.writeExternal(element); rootElement.addContent(element); } @@ -356,6 +377,35 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi return myKind; } + @Override + public void addExcludedRoot(@NotNull String url) { + getOrCreateExcludedRoots().add(url); + } + + @Override + public boolean removeExcludedRoot(@NotNull String url) { + if (myExcludedRoots != null) { + VirtualFilePointer pointer = myExcludedRoots.findByUrl(url); + if (pointer != null) { + myExcludedRoots.remove(pointer); + return true; + } + } + return false; + } + + @NotNull + @Override + public String[] getExcludedRootUrls() { + return myExcludedRoots != null ? myExcludedRoots.getUrls() : ArrayUtil.EMPTY_STRING_ARRAY; + } + + @NotNull + @Override + public VirtualFile[] getExcludedRoots() { + return myExcludedRoots != null ? myExcludedRoots.getFiles() : VirtualFile.EMPTY_ARRAY; + } + @Override public LibraryProperties getProperties() { return myProperties; @@ -439,12 +489,31 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi final VirtualFilePointer byUrl = container.findByUrl(url); if (byUrl != null) { container.remove(byUrl); + for (String excludedRoot : myExcludedRoots.getUrls()) { + if (!isUnderRoots(excludedRoot)) { + VirtualFilePointer pointer = myExcludedRoots.findByUrl(url); + if (pointer != null) { + myExcludedRoots.remove(pointer); + } + } + } myJarDirectories.remove(rootType, url); return true; } return false; } + private boolean isUnderRoots(@NotNull String url) { + for (VirtualFilePointerContainer container : myRoots.values()) { + for (String rootUrl : container.getUrls()) { + if (VfsUtilCore.isEqualOrAncestor(rootUrl, url)) { + return true; + } + } + } + return false; + } + @Override public void moveRootUp(@NotNull String url, @NotNull OrderRootType rootType) { assert !isDisposed(); @@ -531,12 +600,17 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi VirtualFilePointerContainer clone = container.clone(myPointersDisposable); myRoots.put(rootType, clone); } + VirtualFilePointerContainer excludedRoots = fromModel.myExcludedRoots; + myExcludedRoots = excludedRoots != null ? excludedRoots.clone(myPointersDisposable) : null; } private void disposeMyPointers() { for (VirtualFilePointerContainer container : new THashSet(myRoots.values())) { container.killAll(); } + if (myExcludedRoots != null) { + myExcludedRoots.killAll(); + } Disposer.dispose(myPointersDisposable); Disposer.register(this, myPointersDisposable); } @@ -575,6 +649,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi if (myRoots != null ? !myRoots.equals(library.myRoots) : library.myRoots != null) return false; if (myKind != null ? !myKind.equals(library.myKind) : library.myKind != null) return false; if (myProperties != null ? !myProperties.equals(library.myProperties) : library.myProperties != null) return false; + if (!Comparing.equal(myExcludedRoots, library.myExcludedRoots)) return false; return true; } @@ -582,7 +657,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi public int hashCode() { int result = myName != null ? myName.hashCode() : 0; result = 31 * result + (myRoots != null ? myRoots.hashCode() : 0); - result = 31 * result + (myJarDirectories != null ? myJarDirectories.hashCode() : 0); + result = 31 * result + myJarDirectories.hashCode(); return result; } diff --git a/platform/projectModel-impl/src/com/intellij/project/model/impl/library/JpsLibraryDelegate.java b/platform/projectModel-impl/src/com/intellij/project/model/impl/library/JpsLibraryDelegate.java index bb154d927fbc..b32dda5121a8 100644 --- a/platform/projectModel-impl/src/com/intellij/project/model/impl/library/JpsLibraryDelegate.java +++ b/platform/projectModel-impl/src/com/intellij/project/model/impl/library/JpsLibraryDelegate.java @@ -145,10 +145,22 @@ public class JpsLibraryDelegate implements LibraryEx { @NotNull @Override - public ModifiableModel getModifiableModel() { + public ModifiableModelEx getModifiableModel() { throw new UnsupportedOperationException("'getModifiableModel' not implemented in " + getClass().getName()); } + @NotNull + @Override + public String[] getExcludedRootUrls() { + return ArrayUtil.EMPTY_STRING_ARRAY; + } + + @NotNull + @Override + public VirtualFile[] getExcludedRoots() { + return VirtualFile.EMPTY_ARRAY; + } + @Override public void readExternal(Element element) throws InvalidDataException { throw new UnsupportedOperationException(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenFoldersImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenFoldersImporter.java index be9ebda42a54..837c19e0b8f8 100755 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenFoldersImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenFoldersImporter.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.impl.ModifiableModelCommitter; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.util.ArrayUtil; import com.intellij.util.PairConsumer; import com.intellij.util.containers.LinkedMultiMap; @@ -153,8 +154,8 @@ public class MavenFoldersImporter { private void addSourceFolderIfNotOverlap(String path, JpsModuleSourceRootType type, List addedPaths) { String canonicalPath = myModel.toPath(path).getPath(); for (String existing : addedPaths) { - if (MavenRootModelAdapter.isEqualOrAncestor(existing, canonicalPath) - || MavenRootModelAdapter.isEqualOrAncestor(canonicalPath, existing)) { + if (VfsUtilCore.isEqualOrAncestor(existing, canonicalPath) + || VfsUtilCore.isEqualOrAncestor(canonicalPath, existing)) { return; } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenRootModelAdapter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenRootModelAdapter.java index 87d5f5e64abe..e3a42a5b036d 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenRootModelAdapter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenRootModelAdapter.java @@ -25,8 +25,8 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.pom.java.LanguageLevel; import com.intellij.util.Processor; @@ -81,7 +81,7 @@ public class MavenRootModelAdapter { private ContentEntry getContentRootFor(Url url) { for (ContentEntry e : myRootModel.getContentEntries()) { - if (isEqualOrAncestor(e.getUrl(), url.getUrl())) return e; + if (VfsUtilCore.isEqualOrAncestor(e.getUrl(), url.getUrl())) return e; } return null; } @@ -155,7 +155,7 @@ public class MavenRootModelAdapter { String url = toUrl(f.getPath()).getUrl(); for (ContentEntry eachEntry : myRootModel.getContentEntries()) { for (SourceFolder eachFolder : eachEntry.getSourceFolders()) { - if (isEqualOrAncestor(url, eachFolder.getUrl())) return true; + if (VfsUtilCore.isEqualOrAncestor(url, eachFolder.getUrl())) return true; } } return false; @@ -165,16 +165,12 @@ public class MavenRootModelAdapter { String url = toUrl(f.getPath()).getUrl(); for (ContentEntry eachEntry : myRootModel.getContentEntries()) { for (ExcludeFolder eachFolder : eachEntry.getExcludeFolders()) { - if (isEqualOrAncestor(eachFolder.getUrl(), url)) return true; + if (VfsUtilCore.isEqualOrAncestor(eachFolder.getUrl(), url)) return true; } } return false; } - public static boolean isEqualOrAncestor(String ancestor, String child) { - return ancestor.equals(child) || StringUtil.startsWithConcatenation(child, ancestor, "/"); - } - private boolean exists(String path) { return new File(toPath(path).getPath()).exists(); } @@ -196,7 +192,7 @@ public class MavenRootModelAdapter { for (SourceFolder eachFolder : eachEntry.getSourceFolders()) { String ancestor = under ? url.getUrl() : eachFolder.getUrl(); String child = under ? eachFolder.getUrl() : url.getUrl(); - if (isEqualOrAncestor(ancestor, child)) { + if (VfsUtilCore.isEqualOrAncestor(ancestor, child)) { eachEntry.removeSourceFolder(eachFolder); } } @@ -206,7 +202,7 @@ public class MavenRootModelAdapter { String ancestor = under ? url.getUrl() : eachFolder.getUrl(); String child = under ? eachFolder.getUrl() : url.getUrl(); - if (isEqualOrAncestor(ancestor, child)) { + if (VfsUtilCore.isEqualOrAncestor(ancestor, child)) { if (eachFolder.isSynthetic()) { getCompilerExtension().setExcludeOutput(false); } @@ -225,7 +221,7 @@ public class MavenRootModelAdapter { for (SourceFolder eachFolder : eachEntry.getSourceFolders()) { String ancestor = url.getUrl(); String child = eachFolder.getUrl(); - if (isEqualOrAncestor(ancestor, child) || isEqualOrAncestor(child, ancestor)) { + if (VfsUtilCore.isEqualOrAncestor(ancestor, child) || VfsUtilCore.isEqualOrAncestor(child, ancestor)) { return true; } } @@ -234,7 +230,7 @@ public class MavenRootModelAdapter { String ancestor = url.getUrl(); String child = eachFolder.getUrl(); - if (isEqualOrAncestor(ancestor, child) || isEqualOrAncestor(child, ancestor)) { + if (VfsUtilCore.isEqualOrAncestor(ancestor, child) || VfsUtilCore.isEqualOrAncestor(child, ancestor)) { return true; } }