blob: c19ba91e3346cc67f467b4ea09be55617afc0279 [file] [log] [blame]
/*
* 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.module.impl;
import com.intellij.ProjectTopics;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.components.StateStorageException;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.*;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.roots.impl.ModifiableModelCommitter;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.StringInterner;
import com.intellij.util.containers.hash.EqualityPolicy;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.util.io.URLUtil;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.FilePathHashingStrategy;
import gnu.trove.THashMap;
import gnu.trove.TObjectHashingStrategy;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author max
*/
public abstract class ModuleManagerImpl extends ModuleManager implements ProjectComponent, PersistentStateComponent<Element> {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleManagerImpl");
public static final Key<String> DISPOSED_MODULE_NAME = Key.create("DisposedNeverAddedModuleName");
private static final String IML_EXTENSION = ".iml";
protected final Project myProject;
protected final MessageBus myMessageBus;
protected volatile ModuleModelImpl myModuleModel = new ModuleModelImpl();
@NonNls public static final String COMPONENT_NAME = "ProjectModuleManager";
private static final String MODULE_GROUP_SEPARATOR = "/";
private List<ModulePath> myModulePaths;
private final List<ModulePath> myFailedModulePaths = new ArrayList<ModulePath>();
@NonNls public static final String ELEMENT_MODULES = "modules";
@NonNls public static final String ELEMENT_MODULE = "module";
@NonNls private static final String ATTRIBUTE_FILEURL = "fileurl";
@NonNls public static final String ATTRIBUTE_FILEPATH = "filepath";
@NonNls private static final String ATTRIBUTE_GROUP = "group";
public static ModuleManagerImpl getInstanceImpl(Project project) {
return (ModuleManagerImpl)getInstance(project);
}
protected void cleanCachedStuff() {
myCachedModuleComparator = null;
myCachedSortedModules = null;
}
public ModuleManagerImpl(Project project, MessageBus messageBus) {
myProject = project;
myMessageBus = messageBus;
}
@Override
@NotNull
public String getComponentName() {
return COMPONENT_NAME;
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
myModuleModel.disposeModel();
}
@Override
public Element getState() {
final Element e = new Element("state");
writeExternal(e);
return e;
}
private static class ModuleGroupInterner {
private final StringInterner groups = new StringInterner();
private final Map<String[], String[]> paths = new THashMap<String[], String[]>(new TObjectHashingStrategy<String[]>() {
@Override
public int computeHashCode(String[] object) {
return Arrays.hashCode(object);
}
@Override
public boolean equals(String[] o1, String[] o2) {
return Arrays.equals(o1, o2);
}
});
private void setModuleGroupPath(@NotNull ModifiableModuleModel model, Module module, @Nullable String[] group) {
String[] cached = group == null ? null : paths.get(group);
if (cached == null && group != null) {
cached = new String[group.length];
for (int i = 0; i < group.length; i++) {
String g = group[i];
cached[i] = groups.intern(g);
}
paths.put(cached, cached);
}
model.setModuleGroupPath(module, cached);
}
}
@Override
public void loadState(Element state) {
List<ModulePath> prevPaths = myModulePaths;
readExternal(state);
if (prevPaths != null) {
ModifiableModuleModel model = getModifiableModel();
Module[] existingModules = model.getModules();
ModuleGroupInterner groupInterner = new ModuleGroupInterner();
for (Module existingModule : existingModules) {
ModulePath correspondingPath = findCorrespondingPath(existingModule);
if (correspondingPath == null) {
model.disposeModule(existingModule);
}
else {
myModulePaths.remove(correspondingPath);
String groupStr = correspondingPath.getModuleGroup();
String[] group = groupStr == null ? null : groupStr.split(MODULE_GROUP_SEPARATOR);
if (!Arrays.equals(group, model.getModuleGroupPath(existingModule))) {
groupInterner.setModuleGroupPath(model, existingModule, group);
}
}
}
loadModules((ModuleModelImpl)model);
model.commit();
}
}
private ModulePath findCorrespondingPath(final Module existingModule) {
for (ModulePath modulePath : myModulePaths) {
if (modulePath.getPath().equals(existingModule.getModuleFilePath())) return modulePath;
}
return null;
}
public static final class ModulePath {
private final String myPath;
private final String myModuleGroup;
public ModulePath(String path, String moduleGroup) {
myPath = path;
myModuleGroup = moduleGroup;
}
public String getPath() {
return myPath;
}
public String getModuleGroup() {
return myModuleGroup;
}
}
@NotNull
public static ModulePath[] getPathsToModuleFiles(Element element) {
final List<ModulePath> paths = new ArrayList<ModulePath>();
final Element modules = element.getChild(ELEMENT_MODULES);
if (modules != null) {
for (final Object value : modules.getChildren(ELEMENT_MODULE)) {
Element moduleElement = (Element)value;
final String fileUrlValue = moduleElement.getAttributeValue(ATTRIBUTE_FILEURL);
final String filepath;
if (fileUrlValue != null) {
filepath = VirtualFileManager.extractPath(fileUrlValue).replace('/', File.separatorChar);
}
else {
// [dsl] support for older formats
filepath = moduleElement.getAttributeValue(ATTRIBUTE_FILEPATH).replace('/', File.separatorChar);
}
final String group = moduleElement.getAttributeValue(ATTRIBUTE_GROUP);
paths.add(new ModulePath(filepath, group));
}
}
return paths.toArray(new ModulePath[paths.size()]);
}
public void readExternal(final Element element) {
myModulePaths = new ArrayList<ModulePath>(Arrays.asList(getPathsToModuleFiles(element)));
}
protected void loadModules(final ModuleModelImpl moduleModel) {
if (myModulePaths == null || myModulePaths.isEmpty()) {
return;
}
ModuleGroupInterner groupInterner = new ModuleGroupInterner();
final ProgressIndicator progressIndicator = myProject.isDefault() ? null : ProgressIndicatorProvider.getGlobalProgressIndicator();
if (progressIndicator != null) {
progressIndicator.setText("Loading modules...");
progressIndicator.setText2("");
}
myFailedModulePaths.clear();
myFailedModulePaths.addAll(myModulePaths);
final List<Module> modulesWithUnknownTypes = new ArrayList<Module>();
List<ModuleLoadingErrorDescription> errors = new ArrayList<ModuleLoadingErrorDescription>();
for (int i = 0; i < myModulePaths.size(); i++) {
ModulePath modulePath = myModulePaths.get(i);
if (progressIndicator != null) {
progressIndicator.setFraction((double) i / myModulePaths.size());
}
try {
final Module module = moduleModel.loadModuleInternal(modulePath.getPath());
if (isUnknownModuleType(module)) {
modulesWithUnknownTypes.add(module);
}
final String groupPathString = modulePath.getModuleGroup();
if (groupPathString != null) {
final String[] groupPath = groupPathString.split(MODULE_GROUP_SEPARATOR);
groupInterner.setModuleGroupPath(moduleModel, module, groupPath); //model should be updated too
}
myFailedModulePaths.remove(modulePath);
}
catch (final IOException e) {
errors.add(ModuleLoadingErrorDescription.create(ProjectBundle.message("module.cannot.load.error", modulePath.getPath(), e.getMessage()),
modulePath, this));
}
catch (final ModuleWithNameAlreadyExists moduleWithNameAlreadyExists) {
errors.add(ModuleLoadingErrorDescription.create(moduleWithNameAlreadyExists.getMessage(), modulePath, this));
}
catch (StateStorageException e) {
errors.add(ModuleLoadingErrorDescription.create(ProjectBundle.message("module.cannot.load.error", modulePath.getPath(), e.getMessage()),
modulePath, this));
}
}
onModuleLoadErrors(errors);
showUnknownModuleTypeNotification(modulesWithUnknownTypes);
if (progressIndicator != null) {
progressIndicator.setIndeterminate(true);
}
}
protected boolean isUnknownModuleType(Module module) {
return false;
}
protected void showUnknownModuleTypeNotification(List<Module> types) {
}
protected void fireModuleAdded(Module module) {
myMessageBus.syncPublisher(ProjectTopics.MODULES).moduleAdded(myProject, module);
}
protected void fireModuleRemoved(Module module) {
myMessageBus.syncPublisher(ProjectTopics.MODULES).moduleRemoved(myProject, module);
}
protected void fireBeforeModuleRemoved(Module module) {
myMessageBus.syncPublisher(ProjectTopics.MODULES).beforeModuleRemoved(myProject, module);
}
protected void fireModulesRenamed(List<Module> modules, final Map<Module, String> oldNames) {
if (!modules.isEmpty()) {
myMessageBus.syncPublisher(ProjectTopics.MODULES).modulesRenamed(myProject, modules, new Function<Module, String>() {
@Override
public String fun(Module module) {
return oldNames.get(module);
}
});
}
}
protected void onModuleLoadErrors(final List<ModuleLoadingErrorDescription> errors) {
if (errors.isEmpty()) return;
myModuleModel.myModulesCache = null;
for (ModuleLoadingErrorDescription error : errors) {
final Module module = myModuleModel.myPathToModule.remove(FileUtil.toSystemIndependentName(error.getModulePath().getPath()));
if (module != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Disposer.dispose(module);
}
});
}
}
fireModuleLoadErrors(errors);
}
protected void fireModuleLoadErrors(List<ModuleLoadingErrorDescription> errors) {
if (ApplicationManager.getApplication().isHeadlessEnvironment()) {
throw new RuntimeException(errors.get(0).getDescription());
}
ProjectLoadingErrorsNotifier.getInstance(myProject).registerErrors(errors);
}
public void removeFailedModulePath(@NotNull ModulePath modulePath) {
myFailedModulePaths.remove(modulePath);
}
@Override
@NotNull
public ModifiableModuleModel getModifiableModel() {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModuleModelImpl(myModuleModel);
}
private abstract static class SaveItem {
protected abstract String getModuleName();
protected abstract String getGroupPathString();
protected abstract String getModuleFilePath();
public final void writeExternal(Element parentElement) {
Element moduleElement = new Element(ELEMENT_MODULE);
final String moduleFilePath = getModuleFilePath();
final String url = VirtualFileManager.constructUrl(URLUtil.FILE_PROTOCOL, moduleFilePath);
moduleElement.setAttribute(ATTRIBUTE_FILEURL, url);
// [dsl] support for older builds
moduleElement.setAttribute(ATTRIBUTE_FILEPATH, moduleFilePath);
final String groupPath = getGroupPathString();
if (groupPath != null) {
moduleElement.setAttribute(ATTRIBUTE_GROUP, groupPath);
}
parentElement.addContent(moduleElement);
}
}
private class ModuleSaveItem extends SaveItem{
private final Module myModule;
public ModuleSaveItem(Module module) {
myModule = module;
}
@Override
protected String getModuleName() {
return myModule.getName();
}
@Override
protected String getGroupPathString() {
String[] groupPath = getModuleGroupPath(myModule);
return groupPath != null ? StringUtil.join(groupPath, MODULE_GROUP_SEPARATOR) : null;
}
@Override
protected String getModuleFilePath() {
return myModule.getModuleFilePath().replace(File.separatorChar, '/');
}
}
private static class ModulePathSaveItem extends SaveItem{
private final ModulePath myModulePath;
private final String myFilePath;
private final String myName;
public ModulePathSaveItem(ModulePath modulePath) {
myModulePath = modulePath;
myFilePath = modulePath.getPath().replace(File.separatorChar, '/');
final int slashIndex = myFilePath.lastIndexOf('/');
final int startIndex = slashIndex >= 0 && slashIndex + 1 < myFilePath.length() ? slashIndex + 1 : 0;
final int endIndex = myFilePath.endsWith(IML_EXTENSION)
? myFilePath.length() - IML_EXTENSION.length()
: myFilePath.length();
myName = myFilePath.substring(startIndex, endIndex);
}
@Override
protected String getModuleName() {
return myName;
}
@Override
protected String getGroupPathString() {
return myModulePath.getModuleGroup();
}
@Override
protected String getModuleFilePath() {
return myFilePath;
}
}
public void writeExternal(Element element) {
final Element modules = new Element(ELEMENT_MODULES);
final Module[] collection = getModules();
ArrayList<SaveItem> sorted = new ArrayList<SaveItem>(collection.length + myFailedModulePaths.size());
for (Module module : collection) {
sorted.add(new ModuleSaveItem(module));
}
for (ModulePath modulePath : myFailedModulePaths) {
sorted.add(new ModulePathSaveItem(modulePath));
}
Collections.sort(sorted, new Comparator<SaveItem>() {
@Override
public int compare(SaveItem item1, SaveItem item2) {
return item1.getModuleName().compareTo(item2.getModuleName());
}
});
for (SaveItem saveItem : sorted) {
saveItem.writeExternal(modules);
}
element.addContent(modules);
}
@Override
@NotNull
public Module newModule(@NotNull String filePath, final String moduleTypeId) {
incModificationCount();
final ModifiableModuleModel modifiableModel = getModifiableModel();
final Module module = modifiableModel.newModule(filePath, moduleTypeId);
modifiableModel.commit();
return module;
}
@Override
@NotNull
public Module loadModule(@NotNull String filePath) throws InvalidDataException, IOException, JDOMException, ModuleWithNameAlreadyExists {
incModificationCount();
final ModifiableModuleModel modifiableModel = getModifiableModel();
final Module module = modifiableModel.loadModule(filePath);
modifiableModel.commit();
return module;
}
@Override
public void disposeModule(@NotNull final Module module) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final ModifiableModuleModel modifiableModel = getModifiableModel();
modifiableModel.disposeModule(module);
modifiableModel.commit();
}
});
}
@Override
@NotNull
public Module[] getModules() {
if (myModuleModel.myIsWritable) {
ApplicationManager.getApplication().assertReadAccessAllowed();
}
return myModuleModel.getModules();
}
private Module[] myCachedSortedModules = null;
@Override
@NotNull
public Module[] getSortedModules() {
ApplicationManager.getApplication().assertReadAccessAllowed();
deliverPendingEvents();
if (myCachedSortedModules == null) {
myCachedSortedModules = myModuleModel.getSortedModules();
}
return myCachedSortedModules;
}
@Override
public Module findModuleByName(@NotNull String name) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return myModuleModel.findModuleByName(name);
}
private Comparator<Module> myCachedModuleComparator = null;
@Override
@NotNull
public Comparator<Module> moduleDependencyComparator() {
ApplicationManager.getApplication().assertReadAccessAllowed();
deliverPendingEvents();
if (myCachedModuleComparator == null) {
myCachedModuleComparator = myModuleModel.moduleDependencyComparator();
}
return myCachedModuleComparator;
}
protected void deliverPendingEvents() {
}
@Override
@NotNull
public Graph<Module> moduleGraph() {
return moduleGraph(true);
}
@NotNull
@Override
public Graph<Module> moduleGraph(boolean includeTests) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return myModuleModel.moduleGraph(includeTests);
}
@Override
@NotNull public List<Module> getModuleDependentModules(@NotNull Module module) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return myModuleModel.getModuleDependentModules(module);
}
@Override
public boolean isModuleDependent(@NotNull Module module, @NotNull Module onModule) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return myModuleModel.isModuleDependent(module, onModule);
}
@Override
public void projectOpened() {
fireModulesAdded();
myModuleModel.projectOpened();
}
protected void fireModulesAdded() {
for (final Module module : myModuleModel.myPathToModule.values()) {
fireModuleAddedInWriteAction(module);
}
}
protected void fireModuleAddedInWriteAction(final Module module) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
((ModuleEx)module).moduleAdded();
fireModuleAdded(module);
}
});
}
@Override
public void projectClosed() {
myModuleModel.projectClosed();
}
public static void commitModelWithRunnable(ModifiableModuleModel model, Runnable runnable) {
((ModuleModelImpl)model).commitWithRunnable(runnable);
}
protected abstract ModuleEx createModule(String filePath);
protected abstract ModuleEx createAndLoadModule(String filePath) throws IOException;
class ModuleModelImpl implements ModifiableModuleModel {
final Map<String, Module> myPathToModule = new LinkedHashMap<String, Module>(new EqualityPolicy.ByHashingStrategy<String>(FilePathHashingStrategy.create()));
private Module[] myModulesCache;
private final List<Module> myModulesToDispose = new ArrayList<Module>();
private final Map<Module, String> myModuleToNewName = new HashMap<Module, String>();
private final Map<String, Module> myNewNameToModule = new HashMap<String, Module>();
private boolean myIsWritable;
private Map<Module, String[]> myModuleGroupPath;
ModuleModelImpl() {
myIsWritable = false;
}
ModuleModelImpl(ModuleModelImpl that) {
myPathToModule.putAll(that.myPathToModule);
final Map<Module, String[]> groupPath = that.myModuleGroupPath;
if (groupPath != null){
myModuleGroupPath = new THashMap<Module, String[]>();
myModuleGroupPath.putAll(that.myModuleGroupPath);
}
myIsWritable = true;
}
private void assertWritable() {
LOG.assertTrue(myIsWritable, "Attempt to modify committed ModifiableModuleModel");
}
@Override
@NotNull
public Module[] getModules() {
if (myModulesCache == null) {
Collection<Module> modules = myPathToModule.values();
myModulesCache = modules.toArray(new Module[modules.size()]);
}
return myModulesCache;
}
private Module[] getSortedModules() {
Module[] allModules = getModules().clone();
Arrays.sort(allModules, moduleDependencyComparator());
return allModules;
}
@Override
public void renameModule(@NotNull Module module, @NotNull String newName) throws ModuleWithNameAlreadyExists {
final Module oldModule = getModuleByNewName(newName);
myNewNameToModule.remove(myModuleToNewName.get(module));
if(module.getName().equals(newName)){ // if renaming to itself, forget it altogether
myModuleToNewName.remove(module);
myNewNameToModule.remove(newName);
} else {
myModuleToNewName.put(module, newName);
myNewNameToModule.put(newName, module);
}
if (oldModule != null) {
throw new ModuleWithNameAlreadyExists(ProjectBundle.message("module.already.exists.error", newName), newName);
}
}
@Override
public Module getModuleToBeRenamed(@NotNull String newName) {
return myNewNameToModule.get(newName);
}
public Module getModuleByNewName(String newName) {
final Module moduleToBeRenamed = getModuleToBeRenamed(newName);
if (moduleToBeRenamed != null) {
return moduleToBeRenamed;
}
final Module moduleWithOldName = findModuleByName(newName);
if (myModuleToNewName.get(moduleWithOldName) == null) {
return moduleWithOldName;
}
else {
return null;
}
}
@Override
public String getNewName(@NotNull Module module) {
return myModuleToNewName.get(module);
}
@Override
@NotNull
public Module newModule(@NotNull String filePath, final String moduleTypeId) {
return newModule(filePath, moduleTypeId, null);
}
@Override
@NotNull
public Module newModule(@NotNull String filePath,
final String moduleTypeId,
@Nullable Map<String, String> options) {
assertWritable();
filePath = resolveShortWindowsName(filePath);
ModuleEx module = getModuleByFilePath(filePath);
if (module == null) {
module = createModule(filePath);
module.setOption(Module.ELEMENT_TYPE, moduleTypeId);
if (options != null) {
for ( Map.Entry<String,String> option : options.entrySet()) {
module.setOption(option.getKey(),option.getValue());
}
}
module.loadModuleComponents();
initModule(module);
}
return module;
}
private String resolveShortWindowsName(String filePath) {
try {
return FileUtil.resolveShortWindowsName(filePath);
}
catch (IOException ignored) {
return filePath;
}
}
@Nullable
private ModuleEx getModuleByFilePath(String filePath) {
return (ModuleEx)myPathToModule.get(filePath);
}
@Override
@NotNull
public Module loadModule(@NotNull String filePath) throws InvalidDataException, IOException, ModuleWithNameAlreadyExists {
assertWritable();
try {
return loadModuleInternal(filePath);
}
catch (StateStorageException e) {
throw new IOException(ProjectBundle.message("module.corrupted.file.error", FileUtil.toSystemDependentName(filePath), e.getMessage()));
}
}
private Module loadModuleInternal(String filePath) throws ModuleWithNameAlreadyExists, IOException, StateStorageException {
filePath = resolveShortWindowsName(filePath);
final VirtualFile moduleFile = StandardFileSystems.local().findFileByPath(filePath);
if (moduleFile == null || !moduleFile.exists()) {
throw new IOException(ProjectBundle.message("module.file.does.not.exist.error", filePath));
}
final String name = moduleFile.getName();
if (name.endsWith(IML_EXTENSION)) {
final String moduleName = name.substring(0, name.length() - 4);
for (Module module : myPathToModule.values()) {
if (module.getName().equals(moduleName)) {
throw new ModuleWithNameAlreadyExists(ProjectBundle.message("module.already.exists.error", moduleName), moduleName);
}
}
}
ModuleEx module = getModuleByFilePath(moduleFile.getPath());
if (module == null) {
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
moduleFile.refresh(false, false);
}
}, ModalityState.defaultModalityState());
module = createAndLoadModule(moduleFile.getPath());
module.loadModuleComponents();
initModule(module);
}
return module;
}
private void initModule(ModuleEx module) {
String path = module.getModuleFilePath();
myModulesCache = null;
myPathToModule.put(path, module);
module.init();
}
@Override
public void disposeModule(@NotNull Module module) {
assertWritable();
myModulesCache = null;
if (myPathToModule.values().contains(module)) {
myPathToModule.remove(module.getModuleFilePath());
myModulesToDispose.add(module);
}
if (myModuleGroupPath != null){
myModuleGroupPath.remove(module);
}
}
@Override
public Module findModuleByName(@NotNull String name) {
for (Module module : myPathToModule.values()) {
if (!module.isDisposed() && module.getName().equals(name)) {
return module;
}
}
return null;
}
private Comparator<Module> moduleDependencyComparator() {
DFSTBuilder<Module> builder = new DFSTBuilder<Module>(moduleGraph(true));
return builder.comparator();
}
private Graph<Module> moduleGraph(final boolean includeTests) {
return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Module>() {
@Override
public Collection<Module> getNodes() {
return myPathToModule.values();
}
@Override
public Iterator<Module> getIn(Module m) {
Module[] dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests);
return Arrays.asList(dependentModules).iterator();
}
}));
}
@NotNull private List<Module> getModuleDependentModules(Module module) {
List<Module> result = new ArrayList<Module>();
for (Module aModule : myPathToModule.values()) {
if (isModuleDependent(aModule, module)) {
result.add(aModule);
}
}
return result;
}
private boolean isModuleDependent(Module module, Module onModule) {
return ModuleRootManager.getInstance(module).isDependsOn(onModule);
}
@Override
public void commit() {
ModifiableRootModel[] rootModels = new ModifiableRootModel[0];
ModifiableModelCommitter.multiCommit(rootModels, this);
}
public void commitWithRunnable(Runnable runnable) {
commitModel(this, runnable);
clearRenamingStuff();
}
private void clearRenamingStuff() {
myModuleToNewName.clear();
myNewNameToModule.clear();
}
@Override
public void dispose() {
assertWritable();
ApplicationManager.getApplication().assertWriteAccessAllowed();
final Collection<Module> list = myModuleModel.myPathToModule.values();
final Collection<Module> thisModules = myPathToModule.values();
for (Module thisModule : thisModules) {
if (!list.contains(thisModule)) {
Disposer.dispose(thisModule);
}
}
for (Module moduleToDispose : myModulesToDispose) {
if (!list.contains(moduleToDispose)) {
Disposer.dispose(moduleToDispose);
}
}
clearRenamingStuff();
}
@Override
public boolean isChanged() {
if (!myIsWritable) {
return false;
}
Set<Module> thisModules = new HashSet<Module>(myPathToModule.values());
Set<Module> thatModules = new HashSet<Module>(myModuleModel.myPathToModule.values());
return !thisModules.equals(thatModules) || !Comparing.equal(myModuleModel.myModuleGroupPath, myModuleGroupPath);
}
private void disposeModel() {
myModulesCache = null;
for (final Module module : myPathToModule.values()) {
Disposer.dispose(module);
}
myPathToModule.clear();
myModuleGroupPath = null;
}
public void projectOpened() {
final Collection<Module> collection = myPathToModule.values();
for (final Module aCollection : collection) {
ModuleEx module = (ModuleEx)aCollection;
module.projectOpened();
}
}
public void projectClosed() {
final Collection<Module> collection = myPathToModule.values();
for (final Module aCollection : collection) {
ModuleEx module = (ModuleEx)aCollection;
module.projectClosed();
}
}
@Override
public String[] getModuleGroupPath(Module module) {
return myModuleGroupPath == null ? null : myModuleGroupPath.get(module);
}
@Override
public boolean hasModuleGroups() {
return myModuleGroupPath != null && !myModuleGroupPath.isEmpty();
}
@Override
public void setModuleGroupPath(@NotNull Module module, @Nullable("null means remove") String[] groupPath) {
if (myModuleGroupPath == null) {
myModuleGroupPath = new THashMap<Module, String[]>();
}
if (groupPath == null) {
myModuleGroupPath.remove(module);
}
else {
myModuleGroupPath.put(module, groupPath);
}
}
@Override
public void setModuleFilePath(@NotNull Module module, String oldPath, String newFilePath) {
myPathToModule.remove(oldPath);
myPathToModule.put(newFilePath, module);
}
}
private void commitModel(final ModuleModelImpl moduleModel, final Runnable runnable) {
myModuleModel.myModulesCache = null;
incModificationCount();
ApplicationManager.getApplication().assertWriteAccessAllowed();
final Collection<Module> oldModules = myModuleModel.myPathToModule.values();
final Collection<Module> newModules = moduleModel.myPathToModule.values();
final List<Module> removedModules = new ArrayList<Module>(oldModules);
removedModules.removeAll(newModules);
final List<Module> addedModules = new ArrayList<Module>(newModules);
addedModules.removeAll(oldModules);
ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() {
@Override
public void run() {
for (Module removedModule : removedModules) {
fireBeforeModuleRemoved(removedModule);
cleanCachedStuff();
}
List<Module> neverAddedModules = new ArrayList<Module>(moduleModel.myModulesToDispose);
neverAddedModules.removeAll(myModuleModel.myPathToModule.values());
for (final Module neverAddedModule : neverAddedModules) {
neverAddedModule.putUserData(DISPOSED_MODULE_NAME, neverAddedModule.getName());
Disposer.dispose(neverAddedModule);
}
if (runnable != null) {
runnable.run();
}
final Map<Module, String> modulesToNewNamesMap = moduleModel.myModuleToNewName;
final Set<Module> modulesToBeRenamed = modulesToNewNamesMap.keySet();
modulesToBeRenamed.removeAll(moduleModel.myModulesToDispose);
List<Module> modules = new ArrayList<Module>();
Map<Module, String> oldNames = ContainerUtil.newHashMap();
for (final Module module : modulesToBeRenamed) {
oldNames.put(module, module.getName());
moduleModel.myPathToModule.remove(module.getModuleFilePath());
modules.add(module);
((ModuleEx)module).rename(modulesToNewNamesMap.get(module));
moduleModel.myPathToModule.put(module.getModuleFilePath(), module);
}
moduleModel.myIsWritable = false;
myModuleModel = moduleModel;
for (Module module : removedModules) {
fireModuleRemoved(module);
cleanCachedStuff();
Disposer.dispose(module);
cleanCachedStuff();
}
for (Module addedModule : addedModules) {
((ModuleEx)addedModule).moduleAdded();
cleanCachedStuff();
fireModuleAdded(addedModule);
cleanCachedStuff();
}
cleanCachedStuff();
fireModulesRenamed(modules, oldNames);
cleanCachedStuff();
}
}, false, true);
}
void fireModuleRenamedByVfsEvent(@NotNull final Module module, @NotNull final String oldName) {
ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() {
@Override
public void run() {
fireModulesRenamed(Collections.singletonList(module), Collections.singletonMap(module, oldName));
}
}, false, true);
}
@Override
public String[] getModuleGroupPath(@NotNull Module module) {
return myModuleModel.getModuleGroupPath(module);
}
public void setModuleGroupPath(Module module, String[] groupPath) {
myModuleModel.setModuleGroupPath(module, groupPath);
}
}