/* * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.roots.impl; import com.google.common.base.Predicate; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.*; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; import consulo.annotations.RequiredReadAction; import consulo.annotations.RequiredWriteAction; import consulo.module.extension.ModuleExtension; import consulo.roots.ContentFolderTypeProvider; import consulo.roots.ModuleRootLayer; import consulo.roots.types.BinariesOrderRootType; import consulo.roots.types.SourcesOrderRootType; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Map; import java.util.Set; public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleComponent { private static final Logger LOGGER = Logger.getInstance(ModuleRootManagerImpl.class); private final Module myModule; private RootModelImpl myRootModel; private boolean myIsDisposed; private boolean isModuleAdded; private final OrderRootsCache myOrderRootsCache; private final Map<RootModelImpl, Throwable> myModelCreations = new THashMap<>(); public ModuleRootManagerImpl(Module module) { myModule = module; myRootModel = new RootModelImpl(this); myOrderRootsCache = new OrderRootsCache(module); } @NotNull @Override public Project getProject() { return myModule.getProject(); } @Override @NotNull public Module getModule() { return myModule; } @Override @NotNull public ModuleFileIndex getFileIndex() { return ModuleServiceManager.getService(myModule, ModuleFileIndex.class); } @Override @NotNull public String getComponentName() { return "NewModuleRootManager"; } @Override public void initComponent() { } @Override public void disposeComponent() { myRootModel.dispose(); myIsDisposed = true; if (Disposer.isDebugMode()) { final Set<Map.Entry<RootModelImpl, Throwable>> entries = myModelCreations.entrySet(); for (final Map.Entry<RootModelImpl, Throwable> entry : new ArrayList<Map.Entry<RootModelImpl, Throwable>>(entries)) { System.err.println("***********************************************************************************************"); System.err.println("*** R O O T M O D E L N O T D I S P O S E D ***"); System.err.println("***********************************************************************************************"); System.err.println("Created at:"); entry.getValue().printStackTrace(System.err); entry.getKey().dispose(); } } } @Override @NotNull @RequiredReadAction public ModifiableRootModel getModifiableModel() { return getModifiableModel(new RootConfigurationAccessor()); } @NotNull @RequiredReadAction public ModifiableRootModel getModifiableModel(final RootConfigurationAccessor accessor) { ApplicationManager.getApplication().assertReadAccessAllowed(); final RootModelImpl model = new RootModelImpl(myRootModel, this, accessor) { @Override public void dispose() { super.dispose(); if (Disposer.isDebugMode()) { myModelCreations.remove(this); } for (OrderEntry entry : ModuleRootManagerImpl.this.getOrderEntries()) { assert !((BaseModuleRootLayerChild)entry).isDisposed(); } } }; if (Disposer.isDebugMode()) { myModelCreations.put(model, new Throwable()); } return model; } void makeRootsChange(@NotNull Runnable runnable) { ProjectRootManagerEx projectRootManagerEx = (ProjectRootManagerEx)ProjectRootManager.getInstance(myModule.getProject()); // IMPORTANT: should be the first listener! projectRootManagerEx.makeRootsChange(runnable, false, isModuleAdded); } public RootModelImpl getRootModel() { return myRootModel; } @Override public ContentEntry[] getContentEntries() { return myRootModel.getContentEntries(); } @Override public boolean iterateContentEntries(@NotNull Processor<ContentEntry> processor) { return myRootModel.iterateContentEntries(processor); } @Override @NotNull public OrderEntry[] getOrderEntries() { return myRootModel.getOrderEntries(); } @RequiredWriteAction void commitModel(RootModelImpl rootModel) { ApplicationManager.getApplication().assertWriteAccessAllowed(); LOGGER.assertTrue(rootModel.myModuleRootManager == this); final Project project = myModule.getProject(); final ModifiableModuleModel moduleModel = ModuleManager.getInstance(project).getModifiableModel(); ModifiableModelCommitter.multiCommit(new ModifiableRootModel[]{rootModel}, moduleModel); } @Override @NotNull public Module[] getDependencies() { return myRootModel.getModuleDependencies(); } @NotNull @Override public Module[] getDependencies(boolean includeTests) { return myRootModel.getModuleDependencies(includeTests); } @NotNull @Override public Module[] getModuleDependencies() { return myRootModel.getModuleDependencies(); } @NotNull @Override public Module[] getModuleDependencies(boolean includeTests) { return myRootModel.getModuleDependencies(includeTests); } @Override public boolean isDependsOn(Module module) { return myRootModel.isDependsOn(module); } @Override @NotNull public String[] getDependencyModuleNames() { return myRootModel.getDependencyModuleNames(); } @Nullable @Override public <T extends ModuleExtension> T getExtension(Class<T> clazz) { return myRootModel.getExtension(clazz); } @Override public <T extends ModuleExtension> T getExtension(@NotNull String key) { return myRootModel.getExtension(key); } @Nullable @Override public <T extends ModuleExtension> T getExtensionWithoutCheck(Class<T> clazz) { return myRootModel.getExtensionWithoutCheck(clazz); } @Nullable @Override public <T extends ModuleExtension> T getExtensionWithoutCheck(@NotNull String key) { return myRootModel.getExtensionWithoutCheck(key); } @NotNull @Override public ModuleExtension[] getExtensions() { return myRootModel.getExtensions(); } @Override public <R> R processOrder(RootPolicy<R> policy, R initialValue) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.processOrder(policy, initialValue); } @NotNull @Override public OrderEnumerator orderEntries() { return new ModuleOrderEnumerator(myRootModel, myOrderRootsCache); } public static OrderRootsEnumerator getCachingEnumeratorForType(OrderRootType type, Module module) { return getEnumeratorForType(type, module).usingCache(); } @NotNull private static OrderRootsEnumerator getEnumeratorForType(OrderRootType type, Module module) { OrderEnumerator base = OrderEnumerator.orderEntries(module); if (type == BinariesOrderRootType.getInstance()) { return base.exportedOnly().withoutModuleSourceEntries().recursively().classes(); } if (type == SourcesOrderRootType.getInstance()) { return base.exportedOnly().recursively().sources(); } return base.roots(type); } @Override @NotNull public VirtualFile[] getContentRoots() { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getContentRoots(); } @Override @NotNull public String[] getContentRootUrls() { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getContentRootUrls(); } @NotNull @Override public String[] getContentFolderUrls(@NotNull Predicate<ContentFolderTypeProvider> predicate) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getContentFolderUrls(predicate); } @NotNull @Override public VirtualFile[] getContentFolderFiles(@NotNull Predicate<ContentFolderTypeProvider> predicate) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getContentFolderFiles(predicate); } @NotNull @Override public ContentFolder[] getContentFolders(@NotNull Predicate<ContentFolderTypeProvider> predicate) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getContentFolders(predicate); } @Override @NotNull public String[] getExcludeRootUrls() { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getExcludeRootUrls(); } @Override @NotNull public VirtualFile[] getExcludeRoots() { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getExcludeRoots(); } @Override @NotNull public String[] getSourceRootUrls() { return getSourceRootUrls(true); } @NotNull @Override public String[] getSourceRootUrls(boolean includingTests) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getSourceRootUrls(includingTests); } @Override @NotNull public VirtualFile[] getSourceRoots() { return getSourceRoots(true); } @Override @NotNull public VirtualFile[] getSourceRoots(final boolean includingTests) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getSourceRoots(includingTests); } @NotNull @Override public ModuleRootLayer getCurrentLayer() { return myRootModel.getCurrentLayer(); } @NotNull @Override public String getCurrentLayerName() { return myRootModel.getCurrentLayerName(); } @Nullable @Override public ModuleRootLayer findLayerByName(@NotNull String name) { LOGGER.assertTrue(!myIsDisposed); return myRootModel.findLayerByName(name); } @NotNull @Override public Map<String, ModuleRootLayer> getLayers() { LOGGER.assertTrue(!myIsDisposed); return myRootModel.getLayers(); } @Override public void projectOpened() { } @Override public void projectClosed() { } @Override public void moduleAdded() { isModuleAdded = true; } public void dropCaches() { myOrderRootsCache.clearCache(); } public void saveState(Element parent) { myRootModel.putState(parent); } @RequiredReadAction public void loadState(Element parent, @Nullable ProgressIndicator indicator) { loadState(parent, indicator, myRootModel != null); } @RequiredReadAction protected void loadState(Element element, @Nullable ProgressIndicator indicator, boolean throwEvent) { try { final RootModelImpl newModel = new RootModelImpl(element, indicator, this, throwEvent); if (throwEvent) { makeRootsChange(() -> newModel.doCommitAndDispose(false)); } else { myRootModel = newModel; } } catch (InvalidDataException e) { LOGGER.error(e); } } }