blob: 81f04d24fdc13ca56111c75735fe9fe9eceafe31 [file] [log] [blame]
/*
* 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.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
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.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import gnu.trove.THashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleComponent {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.ModuleRootManagerImpl");
private final Module myModule;
private final ProjectRootManagerImpl myProjectRootManager;
private final VirtualFilePointerManager myFilePointerManager;
private RootModelImpl myRootModel;
private boolean myIsDisposed = false;
private boolean myLoaded = false;
private boolean isModuleAdded = false;
private final OrderRootsCache myOrderRootsCache;
private final Map<RootModelImpl, Throwable> myModelCreations = new THashMap<RootModelImpl, Throwable>();
public ModuleRootManagerImpl(Module module,
ProjectRootManagerImpl projectRootManager,
VirtualFilePointerManager filePointerManager) {
myModule = module;
myProjectRootManager = projectRootManager;
myFilePointerManager = filePointerManager;
myRootModel = new RootModelImpl(this, myProjectRootManager, myFilePointerManager);
myOrderRootsCache = new OrderRootsCache(module);
}
@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
public ModifiableRootModel getModifiableModel() {
return getModifiableModel(new RootConfigurationAccessor());
}
@NotNull
public ModifiableRootModel getModifiableModel(final RootConfigurationAccessor accessor) {
ApplicationManager.getApplication().assertReadAccessAllowed();
final RootModelImpl model = new RootModelImpl(myRootModel, this, true, accessor, myFilePointerManager, myProjectRootManager) {
@Override
public void dispose() {
super.dispose();
if (Disposer.isDebugMode()) {
myModelCreations.remove(this);
}
for (OrderEntry entry : ModuleRootManagerImpl.this.getOrderEntries()) {
assert !((RootModelComponentBase)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;
}
@NotNull
@Override
public ContentEntry[] getContentEntries() {
return myRootModel.getContentEntries();
}
@Override
@NotNull
public OrderEntry[] getOrderEntries() {
return myRootModel.getOrderEntries();
}
@Override
public Sdk getSdk() {
return myRootModel.getSdk();
}
@Override
public boolean isSdkInherited() {
return myRootModel.isSdkInherited();
}
void commitModel(RootModelImpl rootModel) {
ApplicationManager.getApplication().assertWriteAccessAllowed();
LOG.assertTrue(rootModel.myModuleRootManager == this);
final Project project = myModule.getProject();
final ModifiableModuleModel moduleModel = ModuleManager.getInstance(project).getModifiableModel();
ModifiableModelCommitter.multiCommit(new ModifiableRootModel[]{rootModel}, moduleModel);
}
static void doCommit(RootModelImpl rootModel) {
rootModel.docommit();
rootModel.dispose();
}
@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();
}
@Override
public <T> T getModuleExtension(final Class<T> klass) {
return myRootModel.getModuleExtension(klass);
}
@Override
public <R> R processOrder(RootPolicy<R> policy, R initialValue) {
LOG.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 == OrderRootType.CLASSES) {
return base.exportedOnly().withoutModuleSourceEntries().recursively().classes();
}
if (type == OrderRootType.SOURCES) {
return base.exportedOnly().recursively().sources();
}
return base.roots(type);
}
@Override
@NotNull
public VirtualFile[] getContentRoots() {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getContentRoots();
}
@Override
@NotNull
public String[] getContentRootUrls() {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getContentRootUrls();
}
@Override
@NotNull
public String[] getExcludeRootUrls() {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getExcludeRootUrls();
}
@Override
@NotNull
public VirtualFile[] getExcludeRoots() {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getExcludeRoots();
}
@Override
@NotNull
public String[] getSourceRootUrls() {
return getSourceRootUrls(true);
}
@NotNull
@Override
public String[] getSourceRootUrls(boolean includingTests) {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getSourceRootUrls(includingTests);
}
@Override
@NotNull
public VirtualFile[] getSourceRoots() {
return getSourceRoots(true);
}
@Override
@NotNull
public VirtualFile[] getSourceRoots(final boolean includingTests) {
LOG.assertTrue(!myIsDisposed);
return myRootModel.getSourceRoots(includingTests);
}
@NotNull
@Override
public List<VirtualFile> getSourceRoots(@NotNull JpsModuleSourceRootType<?> rootType) {
return myRootModel.getSourceRoots(rootType);
}
@NotNull
@Override
public List<VirtualFile> getSourceRoots(@NotNull Set<? extends JpsModuleSourceRootType<?>> rootTypes) {
return myRootModel.getSourceRoots(rootTypes);
}
@Override
public void projectOpened() {
}
@Override
public void projectClosed() {
}
@Override
public void moduleAdded() {
isModuleAdded = true;
}
public void dropCaches() {
myOrderRootsCache.clearCache();
}
public ModuleRootManagerState getState() {
return new ModuleRootManagerState(myRootModel);
}
public void loadState(ModuleRootManagerState object) {
loadState(object, myLoaded || isModuleAdded);
myLoaded = true;
}
protected void loadState(ModuleRootManagerState object, boolean throwEvent) {
try {
final RootModelImpl newModel = new RootModelImpl(object.getRootModelElement(), this, myProjectRootManager, myFilePointerManager, throwEvent);
if (throwEvent) {
makeRootsChange(new Runnable() {
@Override
public void run() {
doCommit(newModel);
}
});
}
else {
myRootModel.dispose();
myRootModel = newModel;
}
assert !myRootModel.isOrderEntryDisposed();
}
catch (InvalidDataException e) {
LOG.error(e);
}
}
public static class ModuleRootManagerState implements JDOMExternalizable {
private RootModelImpl myRootModel;
private Element myRootModelElement = null;
public ModuleRootManagerState() {
}
public ModuleRootManagerState(final RootModelImpl rootModel) {
myRootModel = rootModel;
}
@Override
public void readExternal(Element element) throws InvalidDataException {
myRootModelElement = element;
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
myRootModel.writeExternal(element);
}
public Element getRootModelElement() {
return myRootModelElement;
}
}
}