diff options
Diffstat (limited to 'platform/projectModel-impl/src/com/intellij')
14 files changed, 866 insertions, 323 deletions
diff --git a/platform/projectModel-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java b/platform/projectModel-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java index c0c4c4956211..7acb0fb12243 100644 --- a/platform/projectModel-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java +++ b/platform/projectModel-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java @@ -109,4 +109,16 @@ public class ProjectRootsUtil { } return null; } + + @Nullable + public static ExcludeFolder findExcludeFolder(@NotNull Module module, @NotNull VirtualFile root) { + for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { + for (ExcludeFolder folder : entry.getExcludeFolders()) { + if (root.equals(folder.getFile())) { + return folder; + } + } + } + return null; + } }
\ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/lang/LanguagePerFileMappings.java b/platform/projectModel-impl/src/com/intellij/lang/LanguagePerFileMappings.java new file mode 100644 index 000000000000..c1a186b614c7 --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/lang/LanguagePerFileMappings.java @@ -0,0 +1,281 @@ +/* + * Copyright 2000-2009 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.lang; + +import com.intellij.injected.editor.VirtualFileWindow; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.impl.FilePropertyPusher; +import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashMap; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.util.*; + +/** + * @author peter + */ +public abstract class LanguagePerFileMappings<T> implements PersistentStateComponent<Element>, PerFileMappings<T> { + + private static final Logger LOG = Logger.getInstance("com.intellij.lang.LanguagePerFileMappings"); + + private final Map<VirtualFile, T> myMappings = new HashMap<VirtualFile, T>(); + private final Project myProject; + + public LanguagePerFileMappings(final Project project) { + myProject = project; + } + + @Nullable + protected FilePropertyPusher<T> getFilePropertyPusher() { + return null; + } + + @Override + public Map<VirtualFile, T> getMappings() { + synchronized (myMappings) { + cleanup(); + return Collections.unmodifiableMap(myMappings); + } + } + + private void cleanup() { + for (final VirtualFile file : new ArrayList<VirtualFile>(myMappings.keySet())) { + if (file != null //PROJECT, top-level + && !file.isValid()) { + myMappings.remove(file); + } + } + } + + @Override + @Nullable + public T getMapping(@Nullable VirtualFile file) { + FilePropertyPusher<T> pusher = getFilePropertyPusher(); + T t = getMappingInner(file, myMappings, pusher == null? null : pusher.getFileDataKey()); + return t == null? getDefaultMapping(file) : t; + } + + @Nullable + protected static <T> T getMappingInner(@Nullable VirtualFile file, @Nullable Map<VirtualFile, T> mappings, @Nullable Key<T> pusherKey) { + if (file instanceof VirtualFileWindow) { + final VirtualFileWindow window = (VirtualFileWindow)file; + file = window.getDelegate(); + } + VirtualFile originalFile = file instanceof LightVirtualFile ? ((LightVirtualFile)file).getOriginalFile() : null; + if (Comparing.equal(originalFile, file)) originalFile = null; + + if (file != null) { + final T pushedValue = pusherKey == null? null : file.getUserData(pusherKey); + if (pushedValue != null) return pushedValue; + } + if (originalFile != null) { + final T pushedValue = pusherKey == null? null : originalFile.getUserData(pusherKey); + if (pushedValue != null) return pushedValue; + } + if (mappings == null) return null; + synchronized (mappings) { + for (VirtualFile cur = file; ; cur = cur.getParent()) { + T t = mappings.get(cur); + if (t != null) return t; + if (originalFile != null) { + t = mappings.get(originalFile); + if (t != null) return t; + originalFile = originalFile.getParent(); + } + if (cur == null) break; + } + } + return null; + } + + @Override + public T chosenToStored(VirtualFile file, T value) { + return value; + } + + @Override + public boolean isSelectable(T value) { + return true; + } + + @Override + @Nullable + public T getDefaultMapping(@Nullable final VirtualFile file) { + return null; + } + + @Nullable + public T getImmediateMapping(@Nullable final VirtualFile file) { + synchronized (myMappings) { + return myMappings.get(file); + } + } + + @Override + public void setMappings(final Map<VirtualFile, T> mappings) { + final Collection<VirtualFile> oldFiles; + synchronized (myMappings) { + oldFiles = new ArrayList<VirtualFile>(myMappings.keySet()); + myMappings.clear(); + myMappings.putAll(mappings); + cleanup(); + } + handleMappingChange(mappings.keySet(), oldFiles, !getProject().isDefault()); + } + + public void setMapping(@Nullable final VirtualFile file, @Nullable T dialect) { + synchronized (myMappings) { + if (dialect == null) { + myMappings.remove(file); + } + else { + myMappings.put(file, dialect); + } + } + final List<VirtualFile> files = ContainerUtil.createMaybeSingletonList(file); + handleMappingChange(files, files, false); + } + + private void handleMappingChange(final Collection<VirtualFile> files, Collection<VirtualFile> oldFiles, final boolean includeOpenFiles) { + final FilePropertyPusher<T> pusher = getFilePropertyPusher(); + if (pusher != null) { + for (VirtualFile oldFile : oldFiles) { + if (oldFile == null) continue; // project + oldFile.putUserData(pusher.getFileDataKey(), null); + } + PushedFilePropertiesUpdater updater = PushedFilePropertiesUpdater.getInstance(myProject); + if (updater == null) { + if (!myProject.isDefault()) { + LOG.error("updater = null. project=" + myProject.getName()+", this="+getClass().getSimpleName()); + } + } + else { + updater.pushAll(pusher); + } + } + if (shouldReparseFiles()) { + PsiDocumentManager.getInstance(myProject).reparseFiles(files, includeOpenFiles); + } + } + + @Override + public Collection<T> getAvailableValues(VirtualFile file) { + return getAvailableValues(); + } + + protected abstract List<T> getAvailableValues(); + + @Nullable + protected abstract String serialize(T t); + + @Override + public Element getState() { + synchronized (myMappings) { + cleanup(); + final Element element = new Element("x"); + final List<VirtualFile> files = new ArrayList<VirtualFile>(myMappings.keySet()); + Collections.sort(files, new Comparator<VirtualFile>() { + @Override + public int compare(final VirtualFile o1, final VirtualFile o2) { + if (o1 == null || o2 == null) return o1 == null ? o2 == null ? 0 : 1 : -1; + return o1.getPath().compareTo(o2.getPath()); + } + }); + for (VirtualFile file : files) { + final T dialect = myMappings.get(file); + String value = serialize(dialect); + if (value != null) { + final Element child = new Element("file"); + element.addContent(child); + child.setAttribute("url", file == null ? "PROJECT" : file.getUrl()); + child.setAttribute(getValueAttribute(), value); + } + } + return element; + } + } + + @Nullable + protected T handleUnknownMapping(VirtualFile file, String value) { + return null; + } + + @NotNull + protected String getValueAttribute() { + return "dialect"; + } + + @Override + public void loadState(final Element state) { + synchronized (myMappings) { + final THashMap<String, T> dialectMap = new THashMap<String, T>(); + for (T dialect : getAvailableValues()) { + String key = serialize(dialect); + if (key != null) { + dialectMap.put(key, dialect); + } + } + final List<Element> files = state.getChildren("file"); + for (Element fileElement : files) { + final String url = fileElement.getAttributeValue("url"); + final String dialectID = fileElement.getAttributeValue(getValueAttribute()); + final VirtualFile file = url.equals("PROJECT") ? null : VirtualFileManager.getInstance().findFileByUrl(url); + T dialect = dialectMap.get(dialectID); + if (dialect == null) { + dialect = handleUnknownMapping(file, dialectID); + if (dialect == null) continue; + } + if (file != null || url.equals("PROJECT")) { + myMappings.put(file, dialect); + } + } + } + } + + @TestOnly + public void cleanupForNextTest() { + synchronized (myMappings) { + myMappings.clear(); + } + } + + protected Project getProject() { + return myProject; + } + + protected boolean shouldReparseFiles() { + return true; + } + + public boolean hasMappings() { + synchronized (myMappings) { + return !myMappings.isEmpty(); + } + } + +} 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 3bf16fcca67c..0b4f135837c9 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java @@ -22,10 +22,12 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -167,6 +169,28 @@ public class ModuleRootModificationUtil { } } + public static void updateExcludedFolders(final Module module, + @NotNull final VirtualFile contentRoot, + final Collection<String> urlsToUnExclude, + final Collection<String> urlsToExclude) { + updateModel(module, new Consumer<ModifiableRootModel>() { + @Override + public void consume(ModifiableRootModel modifiableModel) { + for (final ContentEntry contentEntry : modifiableModel.getContentEntries()) { + if (contentRoot.equals(contentEntry.getFile())) { + for (String url : urlsToUnExclude) { + contentEntry.removeExcludeFolder(url); + } + for (String url : urlsToExclude) { + contentEntry.addExcludeFolder(url); + } + break; + } + } + } + }); + } + private static void doWriteAction(final Runnable action) { final Application application = ApplicationManager.getApplication(); application.invokeAndWait(new Runnable() { diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryIndex.java index 635a6f1afc1f..9fdd8e09a57d 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryIndex.java @@ -34,14 +34,20 @@ public abstract class DirectoryIndex { @TestOnly public abstract void checkConsistency(); + /** + * The same as {@link #getInfoForFile} but works only for directories or file roots and returns {@code null} for directories + * which aren't included in project content or libraries + * @deprecated use {@link #getInfoForFile(com.intellij.openapi.vfs.VirtualFile)} instead + */ + @Deprecated public abstract DirectoryInfo getInfoForDirectory(@NotNull VirtualFile dir); + @NotNull + public abstract DirectoryInfo getInfoForFile(@NotNull VirtualFile file); + @Nullable public abstract JpsModuleSourceRootType<?> getSourceRootType(@NotNull DirectoryInfo info); - public abstract boolean isProjectExcludeRoot(@NotNull VirtualFile dir); - public abstract boolean isModuleExcludeRoot(@NotNull VirtualFile dir); - @NotNull public abstract Query<VirtualFile> getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfo.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfo.java index dfff4973a5a6..87448a584a23 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfo.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfo.java @@ -18,229 +18,59 @@ package com.intellij.openapi.roots.impl; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.OrderEntry; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.RootPolicy; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.BitUtil; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; import java.util.List; public abstract class DirectoryInfo { - public static final int MAX_ROOT_TYPE_ID = (1 << (Byte.SIZE - 2)) - 1; - private final Module module; // module to which content it belongs or null - private final VirtualFile libraryClassRoot; // class root in library - private final VirtualFile contentRoot; - private final VirtualFile sourceRoot; - - private static final byte MODULE_SOURCE_FLAG = 1; // set if files in this directory belongs to sources of the module (if field 'module' is not null) - private static final byte LIBRARY_SOURCE_FLAG = 2; // set if it's a directory with sources of some library - private final byte sourceRootTypeData;//two least significant bits are used for MODULE_SOURCE_FLAG and LIBRARY_SOURCE_FLAG, the remaining bits store module root type id (source/tests/resources/...) - - DirectoryInfo(Module module, - VirtualFile contentRoot, - VirtualFile sourceRoot, - VirtualFile libraryClassRoot, - byte sourceRootTypeData) { - this.module = module; - this.libraryClassRoot = libraryClassRoot; - this.contentRoot = contentRoot; - this.sourceRoot = sourceRoot; - this.sourceRootTypeData = sourceRootTypeData; - } + /** + * @return {@code true} if located under project content or library roots and not excluded or ignored + */ + public abstract boolean isInProject(); - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + /** + * @return {@code true} if located under ignored directory + */ + public abstract boolean isIgnored(); - DirectoryInfo info = (DirectoryInfo)o; + /** + * @return {@code true} if located project content, output or library root but excluded from the project + */ + public abstract boolean isExcluded(); - return sourceRootTypeData == info.sourceRootTypeData && - Comparing.equal(contentRoot, info.contentRoot) && - Comparing.equal(libraryClassRoot, info.libraryClassRoot) && - Comparing.equal(module, info.module) && - Arrays.equals(getOrderEntries(), info.getOrderEntries()) && - Comparing.equal(sourceRoot, info.sourceRoot); - } + public abstract boolean isInModuleSource(); - @Override - public int hashCode() { - int result = module != null ? module.hashCode() : 0; - result = 31 * result + (libraryClassRoot != null ? libraryClassRoot.hashCode() : 0); - result = 31 * result + (contentRoot != null ? contentRoot.hashCode() : 0); - result = 31 * result + (sourceRoot != null ? sourceRoot.hashCode() : 0); - result = 31 * result + (int)sourceRootTypeData; - return result; - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - public String toString() { - return "DirectoryInfo{" + - "module=" + getModule() + - ", isInModuleSource=" + isInModuleSource() + - ", rootTypeId=" + getSourceRootTypeId() + - ", isInLibrarySource=" + isInLibrarySource() + - ", libraryClassRoot=" + getLibraryClassRoot() + - ", contentRoot=" + getContentRoot() + - ", sourceRoot=" + getSourceRoot() + - ", orderEntries=" + Arrays.toString(getOrderEntries()) + - "}"; - } - - @NotNull - public abstract OrderEntry[] getOrderEntries(); + public abstract boolean isInLibrarySource(); @Nullable - OrderEntry findOrderEntryWithOwnerModule(@NotNull Module ownerModule) { - OrderEntry[] entries = getOrderEntries(); - if (entries.length < 10) { - for (OrderEntry entry : entries) { - if (entry.getOwnerModule() == ownerModule) return entry; - } - return null; - } - int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), BY_OWNER_MODULE); - return index < 0 ? null : entries[index]; - } + public abstract VirtualFile getSourceRoot(); - @NotNull - List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull Module ownerModule) { - OrderEntry[] entries = getOrderEntries(); - if (entries.length == 1) { - OrderEntry entry = entries[0]; - return entry.getOwnerModule() == ownerModule ? Arrays.asList(entries) : Collections.<OrderEntry>emptyList(); - } - int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), BY_OWNER_MODULE); - if (index < 0) { - return Collections.emptyList(); - } - int firstIndex = index; - while (firstIndex-1 >= 0 && entries[firstIndex-1].getOwnerModule() == ownerModule) { - firstIndex--; - } - int lastIndex = index+1; - while (lastIndex < entries.length && entries[lastIndex].getOwnerModule() == ownerModule) { - lastIndex++; - } - - OrderEntry[] subArray = new OrderEntry[lastIndex - firstIndex]; - System.arraycopy(entries, firstIndex, subArray, 0, lastIndex - firstIndex); - - return Arrays.asList(subArray); - } + public abstract int getSourceRootTypeId(); - @NotNull - private static OrderEntry createFakeOrderEntry(@NotNull final Module ownerModule) { - return new OrderEntry() { - @NotNull - @Override - public VirtualFile[] getFiles(OrderRootType type) { - throw new IncorrectOperationException(); - } - - @NotNull - @Override - public String[] getUrls(OrderRootType rootType) { - throw new IncorrectOperationException(); - } - - @NotNull - @Override - public String getPresentableName() { - throw new IncorrectOperationException(); - } - - @Override - public boolean isValid() { - throw new IncorrectOperationException(); - } - - @NotNull - @Override - public Module getOwnerModule() { - return ownerModule; - } - - @Override - public <R> R accept(RootPolicy<R> policy, @Nullable R initialValue) { - throw new IncorrectOperationException(); - } - - @Override - public int compareTo(@NotNull OrderEntry o) { - throw new IncorrectOperationException(); - } - - @Override - public boolean isSynthetic() { - throw new IncorrectOperationException(); - } - }; + public boolean hasLibraryClassRoot() { + return getLibraryClassRoot() != null; } - public static final Comparator<OrderEntry> BY_OWNER_MODULE = new Comparator<OrderEntry>() { - @Override - public int compare(OrderEntry o1, OrderEntry o2) { - String name1 = o1.getOwnerModule().getName(); - String name2 = o2.getOwnerModule().getName(); - return name1.compareTo(name2); - } - }; + public abstract VirtualFile getLibraryClassRoot(); @Nullable - public VirtualFile getSourceRoot() { - return sourceRoot; - } - - public VirtualFile getLibraryClassRoot() { - return libraryClassRoot; - } - - public boolean hasLibraryClassRoot() { - return getLibraryClassRoot() != null; - } + public abstract VirtualFile getContentRoot(); @Nullable - public VirtualFile getContentRoot() { - return contentRoot; - } + public abstract Module getModule(); - public boolean isInModuleSource() { - return BitUtil.isSet(sourceRootTypeData, MODULE_SOURCE_FLAG); - } + @NotNull + public abstract OrderEntry[] getOrderEntries(); - public boolean isInLibrarySource() { - return BitUtil.isSet(sourceRootTypeData, LIBRARY_SOURCE_FLAG); - } + @Nullable + abstract OrderEntry findOrderEntryWithOwnerModule(@NotNull Module ownerModule); - public Module getModule() { - return module; - } + @NotNull + abstract List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull Module ownerModule); @TestOnly - void assertConsistency() { - OrderEntry[] entries = getOrderEntries(); - for (int i=1; i<entries.length; i++) { - assert BY_OWNER_MODULE.compare(entries[i-1], entries[i]) <= 0; - } - } - - public int getSourceRootTypeId() { - return sourceRootTypeData >> 2; - } - - public static int createSourceRootTypeData(boolean isInModuleSources, boolean isInLibrarySource, int moduleSourceRootTypeId) { - if (moduleSourceRootTypeId > MAX_ROOT_TYPE_ID) { - throw new IllegalArgumentException("Module source root type id " + moduleSourceRootTypeId + " exceeds the maximum allowable value (" + MAX_ROOT_TYPE_ID + ")"); - } - return (isInModuleSources ? MODULE_SOURCE_FLAG : 0) | (isInLibrarySource ? LIBRARY_SOURCE_FLAG : 0) | moduleSourceRootTypeId << 2; - } + abstract void assertConsistency(); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfoImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfoImpl.java new file mode 100644 index 000000000000..0be339bf3c5f --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/DirectoryInfoImpl.java @@ -0,0 +1,255 @@ +/* + * Copyright 2000-2014 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.impl; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.RootPolicy; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author nik + */ +public abstract class DirectoryInfoImpl extends DirectoryInfo { + public static final Comparator<OrderEntry> BY_OWNER_MODULE = new Comparator<OrderEntry>() { + @Override + public int compare(OrderEntry o1, OrderEntry o2) { + String name1 = o1.getOwnerModule().getName(); + String name2 = o2.getOwnerModule().getName(); + return name1.compareTo(name2); + } + }; + public static final int MAX_ROOT_TYPE_ID = Byte.MAX_VALUE; + private final Module module; // module to which content it belongs or null + private final VirtualFile libraryClassRoot; // class root in library + private final VirtualFile contentRoot; + private final VirtualFile sourceRoot; + private final boolean myInModuleSource; + private final boolean myInLibrarySource; + private final boolean myExcluded; + private final byte mySourceRootTypeId; + + DirectoryInfoImpl(Module module, VirtualFile contentRoot, VirtualFile sourceRoot, VirtualFile libraryClassRoot, + boolean inModuleSource, boolean inLibrarySource, boolean isExcluded, int sourceRootTypeId) { + this.module = module; + this.libraryClassRoot = libraryClassRoot; + this.contentRoot = contentRoot; + this.sourceRoot = sourceRoot; + myInModuleSource = inModuleSource; + myInLibrarySource = inLibrarySource; + myExcluded = isExcluded; + if (sourceRootTypeId > MAX_ROOT_TYPE_ID) { + throw new IllegalArgumentException( + "Module source root type id " + sourceRootTypeId + " exceeds the maximum allowable value (" + MAX_ROOT_TYPE_ID + ")"); + } + mySourceRootTypeId = (byte)sourceRootTypeId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DirectoryInfoImpl info = (DirectoryInfoImpl)o; + + return mySourceRootTypeId == info.mySourceRootTypeId && + myInModuleSource == info.myInModuleSource && + myInLibrarySource == info.myInLibrarySource && + myExcluded == info.myExcluded && + Comparing.equal(contentRoot, info.contentRoot) && + Comparing.equal(libraryClassRoot, info.libraryClassRoot) && + Comparing.equal(module, info.module) && + Arrays.equals(getOrderEntries(), info.getOrderEntries()) && + Comparing.equal(sourceRoot, info.sourceRoot); + } + + @Override + public int hashCode() { + int result = module != null ? module.hashCode() : 0; + result = 31 * result + (libraryClassRoot != null ? libraryClassRoot.hashCode() : 0); + result = 31 * result + (contentRoot != null ? contentRoot.hashCode() : 0); + result = 31 * result + (sourceRoot != null ? sourceRoot.hashCode() : 0); + result = 31 * result + (myInModuleSource ? 1 : 0); + result = 31 * result + (myInLibrarySource ? 1 : 0); + result = 31 * result + (myExcluded ? 1 : 0); + result = 31 * result + (int)mySourceRootTypeId; + return result; + } + + @SuppressWarnings({"HardCodedStringLiteral"}) + public String toString() { + return "DirectoryInfo{" + + "module=" + getModule() + + ", isInModuleSource=" + isInModuleSource() + + ", rootTypeId=" + getSourceRootTypeId() + + ", isInLibrarySource=" + isInLibrarySource() + + ", isExcludedFromModule=" + isExcluded() + + ", libraryClassRoot=" + getLibraryClassRoot() + + ", contentRoot=" + getContentRoot() + + ", sourceRoot=" + getSourceRoot() + + ", orderEntries=" + Arrays.toString(getOrderEntries()) + + "}"; + } + + @NotNull + private static OrderEntry createFakeOrderEntry(@NotNull final Module ownerModule) { + return new OrderEntry() { + @NotNull + @Override + public VirtualFile[] getFiles(OrderRootType type) { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public String[] getUrls(OrderRootType rootType) { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public String getPresentableName() { + throw new IncorrectOperationException(); + } + + @Override + public boolean isValid() { + throw new IncorrectOperationException(); + } + + @NotNull + @Override + public Module getOwnerModule() { + return ownerModule; + } + + @Override + public <R> R accept(RootPolicy<R> policy, @Nullable R initialValue) { + throw new IncorrectOperationException(); + } + + @Override + public int compareTo(@NotNull OrderEntry o) { + throw new IncorrectOperationException(); + } + + @Override + public boolean isSynthetic() { + throw new IncorrectOperationException(); + } + }; + } + + @Nullable + OrderEntry findOrderEntryWithOwnerModule(@NotNull Module ownerModule) { + OrderEntry[] entries = getOrderEntries(); + if (entries.length < 10) { + for (OrderEntry entry : entries) { + if (entry.getOwnerModule() == ownerModule) return entry; + } + return null; + } + int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), BY_OWNER_MODULE); + return index < 0 ? null : entries[index]; + } + + @NotNull + List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull Module ownerModule) { + OrderEntry[] entries = getOrderEntries(); + if (entries.length == 1) { + OrderEntry entry = entries[0]; + return entry.getOwnerModule() == ownerModule ? Arrays.asList(entries) : Collections.<OrderEntry>emptyList(); + } + int index = Arrays.binarySearch(entries, createFakeOrderEntry(ownerModule), BY_OWNER_MODULE); + if (index < 0) { + return Collections.emptyList(); + } + int firstIndex = index; + while (firstIndex-1 >= 0 && entries[firstIndex-1].getOwnerModule() == ownerModule) { + firstIndex--; + } + int lastIndex = index+1; + while (lastIndex < entries.length && entries[lastIndex].getOwnerModule() == ownerModule) { + lastIndex++; + } + + OrderEntry[] subArray = new OrderEntry[lastIndex - firstIndex]; + System.arraycopy(entries, firstIndex, subArray, 0, lastIndex - firstIndex); + + return Arrays.asList(subArray); + } + + public boolean isInProject() { + return !isExcluded(); + } + + public boolean isIgnored() { + return false; + } + + @Nullable + public VirtualFile getSourceRoot() { + return sourceRoot; + } + + public VirtualFile getLibraryClassRoot() { + return libraryClassRoot; + } + + @Nullable + public VirtualFile getContentRoot() { + return contentRoot; + } + + public boolean isInModuleSource() { + return myInModuleSource; + } + + public boolean isInLibrarySource() { + return myInLibrarySource; + } + + public boolean isExcluded() { + return myExcluded; + } + + public Module getModule() { + return module; + } + + @TestOnly + void assertConsistency() { + OrderEntry[] entries = getOrderEntries(); + for (int i=1; i<entries.length; i++) { + assert BY_OWNER_MODULE.compare(entries[i-1], entries[i]) <= 0; + } + } + + public int getSourceRootTypeId() { + return mySourceRootTypeId; + } +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java index 565bb432bad7..746e227fdf82 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java @@ -5,7 +5,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndex; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author nik @@ -19,21 +18,9 @@ public abstract class FileIndexBase implements FileIndex { myFileTypeRegistry = fileTypeManager; } - @Nullable + @NotNull protected DirectoryInfo getInfoForFileOrDirectory(@NotNull VirtualFile file) { - if (!file.isDirectory() && file.getParent() == null) return null; // e.g. LightVirtualFile in test - DirectoryInfo info = myDirectoryIndex.getInfoForDirectory(file); - if (info != null) { - return info; - } - - if (!file.isDirectory() && !myDirectoryIndex.isModuleExcludeRoot(file)) { - VirtualFile dir = file.getParent(); - if (dir != null) { - return myDirectoryIndex.getInfoForDirectory(dir); - } - } - return null; + return myDirectoryIndex.getInfoForFile(file); } @Override diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FilePropertyPusher.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FilePropertyPusher.java index 7a22aefbf833..b8af3172bef9 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FilePropertyPusher.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FilePropertyPusher.java @@ -50,7 +50,7 @@ public interface FilePropertyPusher<T> { boolean acceptsFile(@NotNull VirtualFile file); boolean acceptsDirectory(@NotNull VirtualFile file, @NotNull Project project); - void persistAttribute(@NotNull VirtualFile fileOrDir, @NotNull T value) throws IOException; + void persistAttribute(@NotNull Project project, @NotNull VirtualFile fileOrDir, @NotNull T value) throws IOException; interface Engine { void pushAll(); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java index 1e009d14f0cd..3b5ea4fa1610 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java @@ -29,7 +29,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -49,8 +48,8 @@ public class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileInde for (VirtualFile contentRoot : contentRoots) { VirtualFile parent = contentRoot.getParent(); if (parent != null) { - DirectoryInfo parentInfo = myDirectoryIndex.getInfoForDirectory(parent); - if (parentInfo != null && myModule.equals(parentInfo.getModule())) continue; // inner content - skip it + DirectoryInfo parentInfo = myDirectoryIndex.getInfoForFile(parent); + if (parentInfo.isInProject() && myModule.equals(parentInfo.getModule())) continue; // inner content - skip it } boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); @@ -68,50 +67,45 @@ public class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileInde @Override public boolean isInContent(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && myModule.equals(info.getModule()); + return info.isInProject() && myModule.equals(info.getModule()); } @Override public boolean isInSourceContent(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource() && myModule.equals(info.getModule()); + return info.isInModuleSource() && myModule.equals(info.getModule()); } @Override @NotNull public List<OrderEntry> getOrderEntriesForFile(@NotNull VirtualFile fileOrDir) { - DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - if (info == null) return Collections.emptyList(); - return info.findAllOrderEntriesWithOwnerModule(myModule); + return getInfoForFileOrDirectory(fileOrDir).findAllOrderEntriesWithOwnerModule(myModule); } @Override public OrderEntry getOrderEntryForFile(@NotNull VirtualFile fileOrDir) { - DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - if (info == null) return null; - return info.findOrderEntryWithOwnerModule(myModule); + return getInfoForFileOrDirectory(fileOrDir).findOrderEntryWithOwnerModule(myModule); } @Override public boolean isInTestSourceContent(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource() && myModule.equals(info.getModule()) + return info.isInModuleSource() && myModule.equals(info.getModule()) && JavaModuleSourceRootTypes.isTestSourceOrResource(myDirectoryIndex.getSourceRootType(info)); } @Override public boolean isUnderSourceRootOfType(@NotNull VirtualFile fileOrDir, @NotNull Set<? extends JpsModuleSourceRootType<?>> rootTypes) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource() && myModule.equals(info.getModule()) - && rootTypes.contains(myDirectoryIndex.getSourceRootType(info)); + return info.isInModuleSource() && myModule.equals(info.getModule()) && rootTypes.contains(myDirectoryIndex.getSourceRootType(info)); } private class ContentFilter implements VirtualFileFilter { @Override public boolean accept(@NotNull VirtualFile file) { if (file.isDirectory()) { - DirectoryInfo info = myDirectoryIndex.getInfoForDirectory(file); - return info != null && myModule.equals(info.getModule()); + DirectoryInfo info = myDirectoryIndex.getInfoForFile(file); + return info.isInProject() && myModule.equals(info.getModule()); } else { return !myFileTypeRegistry.isFileIgnored(file); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/NonProjectDirectoryInfo.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/NonProjectDirectoryInfo.java new file mode 100644 index 000000000000..7937f08fc7b4 --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/NonProjectDirectoryInfo.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2014 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.impl; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +class NonProjectDirectoryInfo extends DirectoryInfo { + public static final NonProjectDirectoryInfo IGNORED = new NonProjectDirectoryInfo("ignored") { + @Override + public boolean isIgnored() { + return true; + } + }; + public static final NonProjectDirectoryInfo EXCLUDED = new NonProjectDirectoryInfo("excluded from project") { + @Override + public boolean isExcluded() { + return true; + } + }; + public static final NonProjectDirectoryInfo NOT_UNDER_PROJECT_ROOTS = new NonProjectDirectoryInfo("not under project roots"); + public static final NonProjectDirectoryInfo INVALID = new NonProjectDirectoryInfo("invalid"); + public static final NonProjectDirectoryInfo NOT_SUPPORTED_VIRTUAL_FILE_IMPLEMENTATION = new NonProjectDirectoryInfo("not supported VirtualFile implementation"); + private final String myDebugName; + + private NonProjectDirectoryInfo(String debugName) { + myDebugName = debugName; + } + + public boolean isInProject() { + return false; + } + + @NotNull + @Override + public OrderEntry[] getOrderEntries() { + return OrderEntry.EMPTY_ARRAY; + } + + @Nullable + OrderEntry findOrderEntryWithOwnerModule(@NotNull Module ownerModule) { + return null; + } + + @NotNull + List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull Module ownerModule) { + return Collections.emptyList(); + } + + @Override + public String toString() { + return "DirectoryInfo: " + myDebugName; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + public boolean isIgnored() { + return false; + } + + @Nullable + public VirtualFile getSourceRoot() { + return null; + } + + public VirtualFile getLibraryClassRoot() { + return null; + } + + @Nullable + public VirtualFile getContentRoot() { + return null; + } + + public boolean isInModuleSource() { + return false; + } + + public boolean isInLibrarySource() { + return false; + } + + public boolean isExcluded() { + return false; + } + + public Module getModule() { + return null; + } + + void assertConsistency() { + } + + public int getSourceRootTypeId() { + return 0; + } +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java index 057f6b076070..0bb65c600083 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java @@ -66,7 +66,7 @@ public class ProjectFileIndexFacade extends FileIndexFacade { @Override public boolean isExcludedFile(@NotNull final VirtualFile file) { - return myFileIndex.isIgnored(file); + return myFileIndex.isExcluded(file); } @Nullable @@ -83,7 +83,7 @@ public class ProjectFileIndexFacade extends FileIndexFacade { while (true) { if (childDir == null) return false; if (childDir.equals(baseDir)) return true; - if (myDirectoryIndex.getInfoForDirectory(childDir) == null) return false; + if (!myDirectoryIndex.getInfoForFile(childDir).isInProject()) return false; childDir = childDir.getParent(); } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java index d3fd78289771..acb6598f55c8 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java @@ -34,7 +34,6 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -57,13 +56,13 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots(); for (VirtualFile contentRoot : contentRoots) { DirectoryInfo info = getInfoForFileOrDirectory(contentRoot); - if (info == null) continue; // is excluded or ignored + if (!info.isInProject()) continue; // is excluded or ignored if (!module.equals(info.getModule())) continue; // maybe 2 modules have the same content root? VirtualFile parent = contentRoot.getParent(); if (parent != null) { DirectoryInfo parentInfo = getInfoForFileOrDirectory(parent); - if (parentInfo != null && parentInfo.getModule() != null) continue; // inner content - skip it + if (parentInfo.isInProject() && parentInfo.getModule() != null) continue; // inner content - skip it } boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); @@ -80,61 +79,43 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn } @Override - public boolean isIgnored(@NotNull VirtualFile file) { - if (myFileTypeRegistry.isFileIgnored(file)) return true; - VirtualFile dir = file.isDirectory() ? file : file.getParent(); - if (dir == null) return false; - - DirectoryInfo info = getInfoForFileOrDirectory(dir); - if (info != null) return false; - if (myDirectoryIndex.isProjectExcludeRoot(dir)) return true; - - VirtualFile parent = dir.getParent(); - while (true) { - if (parent == null) return false; - DirectoryInfo parentInfo = getInfoForFileOrDirectory(parent); - if (parentInfo != null) return true; - if (myDirectoryIndex.isProjectExcludeRoot(parent)) return true; - parent = parent.getParent(); - } + public boolean isExcluded(@NotNull VirtualFile file) { + DirectoryInfo info = getInfoForFileOrDirectory(file); + return info.isIgnored() || info.isExcluded(); } @Override public Module getModuleForFile(@NotNull VirtualFile file) { if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate(); - VirtualFile dir = file.isDirectory() ? file : file.getParent(); - if (dir == null) return null; - DirectoryInfo info = getInfoForFileOrDirectory(dir); - if (info == null) return null; + DirectoryInfo info = getInfoForFileOrDirectory(file); + if (!info.isInProject()) return null; return info.getModule(); } @Override @NotNull public List<OrderEntry> getOrderEntriesForFile(@NotNull VirtualFile file) { - DirectoryInfo info = getInfoForFileOrDirectory(file); - if (info == null) return Collections.emptyList(); - return Arrays.asList(info.getOrderEntries()); + return Arrays.asList(getInfoForFileOrDirectory(file).getOrderEntries()); } @Override public VirtualFile getClassRootForFile(@NotNull VirtualFile file) { final DirectoryInfo info = getInfoForFileOrDirectory(file); - if (info == null) return null; + if (!info.isInProject()) return null; return info.getLibraryClassRoot(); } @Override public VirtualFile getSourceRootForFile(@NotNull VirtualFile file) { final DirectoryInfo info = getInfoForFileOrDirectory(file); - if (info == null) return null; + if (!info.isInProject()) return null; return info.getSourceRoot(); } @Override public VirtualFile getContentRootForFile(@NotNull VirtualFile file) { final DirectoryInfo info = getInfoForFileOrDirectory(file); - if (info == null) return null; + if (!info.isInProject()) return null; return info.getContentRoot(); } @@ -147,58 +128,60 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn @Override public boolean isLibraryClassFile(@NotNull VirtualFile file) { if (file.isDirectory()) return false; - if (myFileTypeRegistry.isFileIgnored(file)) return false; DirectoryInfo parentInfo = getInfoForFileOrDirectory(file); - return parentInfo != null && parentInfo.hasLibraryClassRoot(); + return parentInfo.isInProject() && parentInfo.hasLibraryClassRoot(); } @Override public boolean isInSource(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && (info.isInModuleSource() || info.isInLibrarySource()); + return info.isInModuleSource() || info.isInLibrarySource(); } @Override public boolean isInLibraryClasses(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.hasLibraryClassRoot(); + return info.isInProject() && info.hasLibraryClassRoot(); } @Override public boolean isInLibrarySource(@NotNull VirtualFile fileOrDir) { - DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInLibrarySource(); + return getInfoForFileOrDirectory(fileOrDir).isInLibrarySource(); + } + + @Override + public boolean isIgnored(@NotNull VirtualFile file) { + return isExcluded(file); } @Override public boolean isInContent(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.getModule() != null; + return info.isInProject() && info.getModule() != null; } @Override public boolean isInSourceContent(@NotNull VirtualFile fileOrDir) { - DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource(); + return getInfoForFileOrDirectory(fileOrDir).isInModuleSource(); } @Override public boolean isInTestSourceContent(@NotNull VirtualFile fileOrDir) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource() && JavaModuleSourceRootTypes.isTestSourceOrResource(myDirectoryIndex.getSourceRootType(info)); + return info.isInModuleSource() && JavaModuleSourceRootTypes.isTestSourceOrResource(myDirectoryIndex.getSourceRootType(info)); } @Override public boolean isUnderSourceRootOfType(@NotNull VirtualFile fileOrDir, @NotNull Set<? extends JpsModuleSourceRootType<?>> rootTypes) { DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir); - return info != null && info.isInModuleSource() && rootTypes.contains(myDirectoryIndex.getSourceRootType(info)); + return info.isInModuleSource() && rootTypes.contains(myDirectoryIndex.getSourceRootType(info)); } private class ContentFilter implements VirtualFileFilter { @Override public boolean accept(@NotNull VirtualFile file) { DirectoryInfo info = getInfoForFileOrDirectory(file); - if (info == null || info.getModule() == null) return false; + if (!info.isInProject() || info.getModule() == null) return false; if (file.isDirectory()) { return true; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java new file mode 100644 index 000000000000..63fcc111df5d --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 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.impl; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +public abstract class PushedFilePropertiesUpdater { + @NotNull + public static PushedFilePropertiesUpdater getInstance(Project project) { + return project.getComponent(PushedFilePropertiesUpdater.class); + } + + public abstract void initializeProperties(); + public abstract void pushAll(final FilePropertyPusher... pushers); + public abstract void filePropertiesChanged(@NotNull final VirtualFile file); + public abstract void processPendingEvents(); + public abstract void pushAllPropertiesNow(); + public abstract <T> void findAndUpdateValue(final VirtualFile fileOrDir, final FilePropertyPusher<T> pusher, final T moduleValue); +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index ad3b851cf2f6..ca68cee3d061 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -43,16 +43,8 @@ import java.util.*; public class RootIndex extends DirectoryIndex { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.RootIndex"); - private static final DirectoryInfo NULL_INFO = new DirectoryInfo(null, null, null, null, (byte)0) { - @NotNull - @Override - public OrderEntry[] getOrderEntries() { - throw new UnsupportedOperationException("Not implemented"); - } - }; private final Set<VirtualFile> myProjectExcludedRoots = ContainerUtil.newHashSet(); - private final Set<VirtualFile> myModuleExcludedRoots; private final MultiMap<String, VirtualFile> myPackagePrefixRoots = new MultiMap<String, VirtualFile>() { @NotNull @Override @@ -77,15 +69,15 @@ public class RootIndex extends DirectoryIndex { Set<VirtualFile> allRoots = info.getAllRoots(); for (VirtualFile root : allRoots) { List<VirtualFile> hierarchy = getHierarchy(root, allRoots, info); - Pair<DirectoryInfo, String> pair = hierarchy == null ? new Pair<DirectoryInfo, String>(null, null) : calcDirectoryInfo(root, hierarchy, info); + Pair<DirectoryInfo, String> pair = hierarchy != null + ? calcDirectoryInfo(root, hierarchy, info) + : new Pair<DirectoryInfo, String>(NonProjectDirectoryInfo.IGNORED, null); cacheInfos(root, root, pair.first); myPackagePrefixRoots.putValue(pair.second, root); if (info.shouldMarkAsProjectExcluded(root, hierarchy)) { myProjectExcludedRoots.add(root); } } - - myModuleExcludedRoots = info.excludedFromModule.keySet(); } @NotNull @@ -232,7 +224,7 @@ public class RootIndex extends DirectoryIndex { } OrderEntry[] array = orderEntries.toArray(new OrderEntry[orderEntries.size()]); - Arrays.sort(array, DirectoryInfo.BY_OWNER_MODULE); + Arrays.sort(array, DirectoryInfoImpl.BY_OWNER_MODULE); return array; } @@ -254,7 +246,7 @@ public class RootIndex extends DirectoryIndex { } int id = myRootTypes.size(); - if (id > DirectoryInfo.MAX_ROOT_TYPE_ID) { + if (id > DirectoryInfoImpl.MAX_ROOT_TYPE_ID) { LOG.error("Too many different types of module source roots (" + id + ") registered: " + myRootTypes); } myRootTypes.add(rootType); @@ -263,14 +255,30 @@ public class RootIndex extends DirectoryIndex { } @Override - @Nullable - public DirectoryInfo getInfoForDirectory(@NotNull final VirtualFile dir) { - if (!dir.isValid()) { - return null; + public DirectoryInfo getInfoForDirectory(@NotNull VirtualFile dir) { + DirectoryInfo info = getInfoForFile(dir); + return info.isInProject() ? info : null; + } + + @NotNull + @Override + public DirectoryInfo getInfoForFile(@NotNull VirtualFile file) { + if (!file.isValid()) { + return NonProjectDirectoryInfo.INVALID; + } + VirtualFile dir; + if (!file.isDirectory()) { + DirectoryInfo info = myInfoCache.getCachedInfo(file); + if (info != null) { + return info; + } + if (isIgnored(file)) { + return NonProjectDirectoryInfo.IGNORED; + } + dir = file.getParent(); } - if (!dir.isDirectory()) { - DirectoryInfo info = myInfoCache.getCachedInfo(dir); - return info == NULL_INFO ? null : info; + else { + dir = file; } int count = 0; @@ -283,21 +291,21 @@ public class RootIndex extends DirectoryIndex { if (!dir.equals(root)) { cacheInfos(dir, root, info); } - return info == NULL_INFO ? null : info; + return info; } if (isIgnored(root)) { - return cacheInfos(dir, root, null); + return cacheInfos(dir, root, NonProjectDirectoryInfo.IGNORED); } } - return cacheInfos(dir, null, null); + return cacheInfos(dir, null, NonProjectDirectoryInfo.NOT_UNDER_PROJECT_ROOTS); } - @Nullable - private DirectoryInfo cacheInfos(VirtualFile dir, @Nullable VirtualFile stopAt, @Nullable DirectoryInfo info) { + @NotNull + private DirectoryInfo cacheInfos(VirtualFile dir, @Nullable VirtualFile stopAt, @NotNull DirectoryInfo info) { while (dir != null) { - myInfoCache.cacheInfo(dir, info == null ? NULL_INFO : info); + myInfoCache.cacheInfo(dir, info); if (dir.equals(stopAt)) { break; } @@ -307,16 +315,6 @@ public class RootIndex extends DirectoryIndex { } @Override - public boolean isProjectExcludeRoot(@NotNull final VirtualFile dir) { - return myProjectExcludedRoots.contains(dir); - } - - @Override - public boolean isModuleExcludeRoot(@NotNull final VirtualFile dir) { - return myModuleExcludedRoots.contains(dir); - } - - @Override @NotNull public Query<VirtualFile> getDirectoriesByPackageName(@NotNull final String packageName, final boolean includeLibrarySources) { List<VirtualFile> result = myDirectoriesByPackageNameCache.get(packageName); @@ -327,7 +325,8 @@ public class RootIndex extends DirectoryIndex { String shortName = StringUtil.getShortName(packageName); for (VirtualFile parentDir : getDirectoriesByPackageName(StringUtil.getPackageName(packageName), true)) { VirtualFile child = parentDir.findChild(shortName); - if (child != null && child.isDirectory() && getInfoForDirectory(child) != null && packageName.equals(getPackageName(child))) { + if (child != null && child.isDirectory() && getInfoForFile(child).isInProject() + && packageName.equals(getPackageName(child))) { result.add(child); } } @@ -344,8 +343,8 @@ public class RootIndex extends DirectoryIndex { result = ContainerUtil.filter(result, new Condition<VirtualFile>() { @Override public boolean value(VirtualFile file) { - DirectoryInfo info = getInfoForDirectory(file); - return info != null && (!info.isInLibrarySource() || info.isInModuleSource() || info.hasLibraryClassRoot()); + DirectoryInfo info = getInfoForFile(file); + return info.isInProject() && (!info.isInLibrarySource() || info.isInModuleSource() || info.hasLibraryClassRoot()); } }); } @@ -397,7 +396,7 @@ public class RootIndex extends DirectoryIndex { return false; } - @Nullable + @Nullable("returns null only if dir is under ignored folder") private static List<VirtualFile> getHierarchy(VirtualFile dir, @NotNull Set<VirtualFile> allRoots, @NotNull RootInfo info) { List<VirtualFile> hierarchy = ContainerUtil.newArrayList(); boolean hasContentRoots = false; @@ -472,6 +471,15 @@ public class RootIndex extends DirectoryIndex { } @Nullable + private Module findParentModuleForExcluded(@NotNull List<VirtualFile> hierarchy) { + for (VirtualFile root : hierarchy) { + Module module = contentRootOf.get(root); + if (module != null) return module; + } + return null; + } + + @Nullable private VirtualFile findLibraryRootInfo(@NotNull List<VirtualFile> hierarchy, boolean source) { Set<Library> librariesToIgnore = ContainerUtil.newHashSet(); for (VirtualFile root : hierarchy) { @@ -566,8 +574,12 @@ public class RootIndex extends DirectoryIndex { VirtualFile moduleContentRoot = info.findModuleRootInfo(hierarchy); VirtualFile libraryClassRoot = info.findLibraryRootInfo(hierarchy, false); VirtualFile librarySourceRoot = info.findLibraryRootInfo(hierarchy, true); + Module parentModuleForExcluded = null; if (moduleContentRoot == null && libraryClassRoot == null && librarySourceRoot == null) { - return new Pair<DirectoryInfo, String>(null, null); + parentModuleForExcluded = info.findParentModuleForExcluded(hierarchy); + if (parentModuleForExcluded == null) { + return new Pair<DirectoryInfo, String>(NonProjectDirectoryInfo.EXCLUDED, null); + } } VirtualFile sourceRoot = info.findPackageRootInfo(hierarchy, moduleContentRoot, null, librarySourceRoot); @@ -577,11 +589,9 @@ public class RootIndex extends DirectoryIndex { boolean inLibrarySource = librarySourceRoot != null; int typeId = moduleSourceRoot != null ? info.rootTypeId.get(moduleSourceRoot) : 0; - DirectoryInfo directoryInfo = new DirectoryInfo(info.contentRootOf.get(moduleContentRoot), - moduleContentRoot, - sourceRoot, - libraryClassRoot, - (byte)DirectoryInfo.createSourceRootTypeData(inModuleSources, inLibrarySource, typeId)) { + Module module = parentModuleForExcluded != null ? parentModuleForExcluded : info.contentRootOf.get(moduleContentRoot); + DirectoryInfo directoryInfo = new DirectoryInfoImpl(module, moduleContentRoot, sourceRoot, libraryClassRoot, inModuleSources, inLibrarySource, + parentModuleForExcluded != null, typeId) { @NotNull @Override public OrderEntry[] getOrderEntries() { |