diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java index 6bd1f03d3b7d..15fc27deca14 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java @@ -1,165 +1,166 @@ -/* - * 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.module.impl; - -import com.intellij.ProjectTopics; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.openapi.components.StoragePathMacros; -import com.intellij.openapi.components.StorageScheme; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.module.UnknownModuleType; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectBundle; -import com.intellij.openapi.project.impl.ProjectLifecycleListener; -import com.intellij.util.messages.MessageBus; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.util.messages.MessageHandler; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.List; - -/** - * @author yole - */ -@State( - name = ModuleManagerImpl.COMPONENT_NAME, - storages = { - @Storage( file = StoragePathMacros.PROJECT_FILE) - ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/modules.xml", scheme = StorageScheme.DIRECTORY_BASED) - } -) -public class ModuleManagerComponent extends ModuleManagerImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleManagerComponent"); - private final ProgressManager myProgressManager; - private final MessageBusConnection myConnection; - - public ModuleManagerComponent(Project project, ProgressManager progressManager, MessageBus bus) { - super(project, bus); - myConnection = bus.connect(project); - myProgressManager = progressManager; - myConnection.setDefaultHandler(new MessageHandler() { - @Override - public void handle(Method event, Object... params) { - cleanCachedStuff(); - } - }); - - myConnection.subscribe(ProjectTopics.PROJECT_ROOTS); - myConnection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() { - @Override - public void projectComponentsInitialized(final Project project) { - long start = System.currentTimeMillis(); - loadModules(myModuleModel); - LOG.info(myModuleModel.getModules().length + " modules loaded in " + (System.currentTimeMillis() - start) + " ms"); - } - }); - - } - - @Override - protected void showUnknownModuleTypeNotification(List modulesWithUnknownTypes) { - if (!ApplicationManager.getApplication().isHeadlessEnvironment() && !modulesWithUnknownTypes.isEmpty()) { - String message; - if (modulesWithUnknownTypes.size() == 1) { - message = ProjectBundle.message("module.unknown.type.single.error", modulesWithUnknownTypes.get(0).getName(), - ModuleType.get(modulesWithUnknownTypes.get(0)).getId()); - } - else { - StringBuilder modulesBuilder = new StringBuilder(); - for (final Module module : modulesWithUnknownTypes) { - modulesBuilder.append("
\""); - modulesBuilder.append(module.getName()).append("\" (type '").append(ModuleType.get(module).getId()).append("')"); - } - modulesBuilder.append("
"); - message = ProjectBundle.message("module.unknown.type.multiple.error", modulesBuilder.toString()); - } - // it is not modal warning at all - //Messages.showWarningDialog(myProject, message, ProjectBundle.message("module.unknown.type.title")); - Notifications.Bus.notify(new Notification( - "Module Manager", - ProjectBundle.message("module.unknown.type.title"), - message, - NotificationType.WARNING - ), myProject); - } - } - - @Override - protected ModuleEx createModule(String filePath) { - return new ModuleImpl(filePath, myProject); - } - - @Override - protected ModuleEx createAndLoadModule(String filePath) throws IOException { - ModuleImpl module = new ModuleImpl(filePath, myProject); - module.getStateStore().load(); - return module; - } - - @Override - protected boolean isUnknownModuleType(Module module) { - return ModuleType.get(module) instanceof UnknownModuleType; - } - - @Override - protected void fireModulesAdded() { - Runnable runnableWithProgress = new Runnable() { - @Override - public void run() { - for (final Module module : myModuleModel.myPathToModule.values()) { - final Application app = ApplicationManager.getApplication(); - final Runnable swingRunnable = new Runnable() { - @Override - public void run() { - fireModuleAddedInWriteAction(module); - } - }; - if (app.isDispatchThread() || app.isHeadlessEnvironment()) { - swingRunnable.run(); - } - else { - ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator(); - app.invokeAndWait(swingRunnable, pi.getModalityState()); - } - } - } - }; - - ProgressIndicator progressIndicator = myProgressManager.getProgressIndicator(); - if (progressIndicator == null) { - myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Loading modules", false, myProject); - } - else { - runnableWithProgress.run(); - } - } - - @Override - protected void deliverPendingEvents() { - myConnection.deliverImmediately(); - } -} +/* + * 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.module.impl; + +import com.intellij.ProjectTopics; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationType; +import com.intellij.notification.Notifications; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.components.StorageScheme; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.UnknownModuleType; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectBundle; +import com.intellij.openapi.project.impl.ProjectLifecycleListener; +import com.intellij.util.messages.MessageBus; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.messages.MessageHandler; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.List; + +/** + * @author yole + */ +@State( + name = ModuleManagerImpl.COMPONENT_NAME, + storages = { + @Storage( file = StoragePathMacros.PROJECT_FILE) + ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/modules.xml", scheme = StorageScheme.DIRECTORY_BASED) + } +) +public class ModuleManagerComponent extends ModuleManagerImpl { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleManagerComponent"); + private final ProgressManager myProgressManager; + private final MessageBusConnection myConnection; + + public ModuleManagerComponent(Project project, ProgressManager progressManager, MessageBus bus) { + super(project, bus); + myConnection = bus.connect(project); + myProgressManager = progressManager; + myConnection.setDefaultHandler(new MessageHandler() { + @Override + public void handle(Method event, Object... params) { + cleanCachedStuff(); + } + }); + + myConnection.subscribe(ProjectTopics.PROJECT_ROOTS); + myConnection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() { + @Override + public void projectComponentsInitialized(final Project project) { + long t = System.currentTimeMillis(); + loadModules(myModuleModel); + t = System.currentTimeMillis() - t; + LOG.info(myModuleModel.getModules().length + " module(s) loaded in " + t + " ms"); + } + }); + + } + + @Override + protected void showUnknownModuleTypeNotification(List modulesWithUnknownTypes) { + if (!ApplicationManager.getApplication().isHeadlessEnvironment() && !modulesWithUnknownTypes.isEmpty()) { + String message; + if (modulesWithUnknownTypes.size() == 1) { + message = ProjectBundle.message("module.unknown.type.single.error", modulesWithUnknownTypes.get(0).getName(), + ModuleType.get(modulesWithUnknownTypes.get(0)).getId()); + } + else { + StringBuilder modulesBuilder = new StringBuilder(); + for (final Module module : modulesWithUnknownTypes) { + modulesBuilder.append("
\""); + modulesBuilder.append(module.getName()).append("\" (type '").append(ModuleType.get(module).getId()).append("')"); + } + modulesBuilder.append("
"); + message = ProjectBundle.message("module.unknown.type.multiple.error", modulesBuilder.toString()); + } + // it is not modal warning at all + //Messages.showWarningDialog(myProject, message, ProjectBundle.message("module.unknown.type.title")); + Notifications.Bus.notify(new Notification( + "Module Manager", + ProjectBundle.message("module.unknown.type.title"), + message, + NotificationType.WARNING + ), myProject); + } + } + + @Override + protected ModuleEx createModule(String filePath) { + return new ModuleImpl(filePath, myProject); + } + + @Override + protected ModuleEx createAndLoadModule(String filePath) throws IOException { + ModuleImpl module = new ModuleImpl(filePath, myProject); + module.getStateStore().load(); + return module; + } + + @Override + protected boolean isUnknownModuleType(Module module) { + return ModuleType.get(module) instanceof UnknownModuleType; + } + + @Override + protected void fireModulesAdded() { + Runnable runnableWithProgress = new Runnable() { + @Override + public void run() { + for (final Module module : myModuleModel.myPathToModule.values()) { + final Application app = ApplicationManager.getApplication(); + final Runnable swingRunnable = new Runnable() { + @Override + public void run() { + fireModuleAddedInWriteAction(module); + } + }; + if (app.isDispatchThread() || app.isHeadlessEnvironment()) { + swingRunnable.run(); + } + else { + ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator(); + app.invokeAndWait(swingRunnable, pi.getModalityState()); + } + } + } + }; + + ProgressIndicator progressIndicator = myProgressManager.getProgressIndicator(); + if (progressIndicator == null) { + myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Loading modules", false, myProject); + } + else { + runnableWithProgress.run(); + } + } + + @Override + protected void deliverPendingEvents() { + myConnection.deliverImmediately(); + } +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index 53f6a0d65851..45acc4f93c4a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -1,986 +1,978 @@ -/* - * 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.module.impl; - -import com.intellij.ProjectTopics; -import com.intellij.openapi.application.ApplicationManager; -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.containers.HashMap; -import com.intellij.util.containers.HashSet; -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.messages.MessageBus; -import gnu.trove.THashMap; -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, ModificationTracker { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleManagerImpl"); - public static final Key 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 myModulePaths; - private final List myFailedModulePaths = new ArrayList(); - @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"; - private long myModificationCount; - - 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 long getModificationCount() { - return myModificationCount; - } - - @Override - public Element getState() { - final Element e = new Element("state"); - writeExternal(e); - return e; - } - - @Override - public void loadState(Element state) { - List prevPaths = myModulePaths; - readExternal(state); - if (prevPaths != null) { - ModifiableModuleModel model = getModifiableModel(); - - Module[] existingModules = model.getModules(); - - - 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 ? groupStr.split(MODULE_GROUP_SEPARATOR) : null; - if (!Arrays.equals(group, model.getModuleGroupPath(existingModule))) { - model.setModuleGroupPath(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; - } - } - - public static ModulePath[] getPathsToModuleFiles(Element element) { - final List paths = new ArrayList(); - 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(Arrays.asList(getPathsToModuleFiles(element))); - } - - protected void loadModules(final ModuleModelImpl moduleModel) { - if (myModulePaths != null && !myModulePaths.isEmpty()) { - final ProgressIndicator progressIndicator = myProject.isDefault() ? null : ProgressIndicatorProvider.getGlobalProgressIndicator(); - if (progressIndicator != null) { - progressIndicator.setText("Loading modules..."); - progressIndicator.setText2(""); - } - myFailedModulePaths.clear(); - myFailedModulePaths.addAll(myModulePaths); - final List modulesWithUnknownTypes = new ArrayList(); - List errors = new ArrayList(); - - for (final ModulePath modulePath : myModulePaths) { - try { - final Module module = moduleModel.loadModuleInternal(modulePath.getPath(), progressIndicator); - if (isUnknownModuleType(module)) { - modulesWithUnknownTypes.add(module); - } - final String groupPathString = modulePath.getModuleGroup(); - if (groupPathString != null) { - final String[] groupPath = groupPathString.split(MODULE_GROUP_SEPARATOR); - moduleModel.setModuleGroupPath(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)); - } - } - - fireErrors(errors); - - showUnknownModuleTypeNotification(modulesWithUnknownTypes); - } - } - - protected boolean isUnknownModuleType(Module module) { - return false; - } - - protected void showUnknownModuleTypeNotification(List 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 modules) { - if (!modules.isEmpty()) { - myMessageBus.syncPublisher(ProjectTopics.MODULES).modulesRenamed(myProject, modules); - } - } - - private void fireErrors(final List 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); - } - }); - } - } - - 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(StandardFileSystems.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 sorted = new ArrayList(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() { - @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) { - myModificationCount++; - 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 { - myModificationCount++; - 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 myCachedModuleComparator = null; - - @Override - @NotNull - public Comparator moduleDependencyComparator() { - ApplicationManager.getApplication().assertReadAccessAllowed(); - deliverPendingEvents(); - if (myCachedModuleComparator == null) { - myCachedModuleComparator = myModuleModel.moduleDependencyComparator(); - } - return myCachedModuleComparator; - } - - protected void deliverPendingEvents() { - } - - @Override - @NotNull - public Graph moduleGraph() { - return moduleGraph(true); - } - - @NotNull - @Override - public Graph moduleGraph(boolean includeTests) { - ApplicationManager.getApplication().assertReadAccessAllowed(); - return myModuleModel.moduleGraph(includeTests); - } - - @Override - @NotNull public List 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 myPathToModule = new LinkedHashMap(); - private Module[] myModulesCache; - - private final List myModulesToDispose = new ArrayList(); - private final Map myModuleToNewName = new HashMap(); - private final Map myNewNameToModule = new HashMap(); - private boolean myIsWritable; - private Map myModuleGroupPath; - - ModuleModelImpl() { - myIsWritable = false; - } - - ModuleModelImpl(ModuleModelImpl that) { - myPathToModule.putAll(that.myPathToModule); - final Map groupPath = that.myModuleGroupPath; - if (groupPath != null){ - myModuleGroupPath = new THashMap(); - 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 modules = myPathToModule.values(); - myModulesCache = modules.toArray(new Module[modules.size()]); - } - return myModulesCache; - } - - private Module[] getSortedModules() { - Module[] allModules = getModules(); - 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 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 option : options.entrySet()) { - module.setOption(option.getKey(),option.getValue()); - } - } - module.loadModuleComponents(); - initModule(module); - } - return module; - } - - private String resolveShortWindowsName(String filePath) { - try { - filePath = FileUtil.resolveShortWindowsName(filePath); - } - catch (IOException ignored) { - } - return filePath; - } - - @Nullable - private ModuleEx getModuleByFilePath(String filePath) { - final Collection modules = myPathToModule.values(); - for (Module module : modules) { - if (filePath.equals(module.getModuleFilePath())) { - return (ModuleEx)module; - } - } - return null; - } - - @Override - @NotNull - public Module loadModule(@NotNull String filePath) throws InvalidDataException, - IOException, - ModuleWithNameAlreadyExists { - assertWritable(); - try { - return loadModuleInternal(filePath, null); - } - catch (StateStorageException e) { - throw new IOException(ProjectBundle.message("module.corrupted.file.error", FileUtil.toSystemDependentName(filePath), e.getMessage())); - } - } - - private Module loadModuleInternal(String filePath, @Nullable ProgressIndicator progressIndicator) throws ModuleWithNameAlreadyExists, - IOException, StateStorageException { - - final VirtualFile moduleFile = StandardFileSystems.local().findFileByPath(filePath); - if (moduleFile == null) { - throw new IOException(ProjectBundle.message("module.file.does.not.exist.error", filePath)); - } - - filePath = resolveShortWindowsName(filePath); - final String name = moduleFile.getName(); - if (progressIndicator != null) { - progressIndicator.setText2(FileUtil.getNameWithoutExtension(name)); - } - - 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); - } - } - } - if (!moduleFile.exists()) { - throw new IOException(ProjectBundle.message("module.file.does.not.exist.error", moduleFile.getPath())); - } - ModuleEx module = getModuleByFilePath(filePath); - if (module == null) { - final VirtualFile moduleVFile = StandardFileSystems.local().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(filePath)); - if (moduleVFile != null) { - moduleVFile.refresh(false, false); - } - module = createAndLoadModule(filePath); - 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 moduleDependencyComparator() { - DFSTBuilder builder = new DFSTBuilder(moduleGraph(true)); - return builder.comparator(); - } - - private Graph moduleGraph(final boolean includeTests) { - return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph() { - @Override - public Collection getNodes() { - return myPathToModule.values(); - } - - @Override - public Iterator getIn(Module m) { - Module[] dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests); - return Arrays.asList(dependentModules).iterator(); - } - })); - } - - @NotNull private List getModuleDependentModules(Module module) { - List result = new ArrayList(); - 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 list = myModuleModel.myPathToModule.values(); - final Collection 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 thisModules = new HashSet(myPathToModule.values()); - Set thatModules = new HashSet(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 collection = myPathToModule.values(); - for (final Module aCollection : collection) { - ModuleEx module = (ModuleEx)aCollection; - module.projectOpened(); - } - } - - public void projectClosed() { - final Collection 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(Module module, String[] groupPath) { - if (myModuleGroupPath == null) { - myModuleGroupPath = new THashMap(); - } - if (groupPath == null) { - myModuleGroupPath.remove(module); - } - else { - myModuleGroupPath.put(module, groupPath); - } - } - - @Override - public void setModuleFilePath(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; - myModificationCount++; - ApplicationManager.getApplication().assertWriteAccessAllowed(); - final Collection oldModules = myModuleModel.myPathToModule.values(); - final Collection newModules = moduleModel.myPathToModule.values(); - final List removedModules = new ArrayList(oldModules); - removedModules.removeAll(newModules); - final List addedModules = new ArrayList(newModules); - addedModules.removeAll(oldModules); - - ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() { - @Override - public void run() { - for (Module removedModule : removedModules) { - fireBeforeModuleRemoved(removedModule); - cleanCachedStuff(); - } - - List neverAddedModules = new ArrayList(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 modulesToNewNamesMap = moduleModel.myModuleToNewName; - final Set modulesToBeRenamed = modulesToNewNamesMap.keySet(); - modulesToBeRenamed.removeAll(moduleModel.myModulesToDispose); - final List modules = new ArrayList(); - for (final Module moduleToBeRenamed : modulesToBeRenamed) { - ModuleEx module = (ModuleEx)moduleToBeRenamed; - moduleModel.myPathToModule.remove(moduleToBeRenamed.getModuleFilePath()); - modules.add(moduleToBeRenamed); - module.rename(modulesToNewNamesMap.get(moduleToBeRenamed)); - moduleModel.myPathToModule.put(moduleToBeRenamed.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); - cleanCachedStuff(); - } - }, false, true); - } - - void fireModuleRenamedByVfsEvent(@NotNull final Module module) { - ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() { - @Override - public void run() { - fireModulesRenamed(Collections.singletonList(module)); - } - }, false, true); - } - - @Override - public String[] getModuleGroupPath(@NotNull Module module) { - return myModuleModel.getModuleGroupPath(module); - } - - public void setModuleGroupPath(Module module, String[] groupPath) { - myModuleModel.setModuleGroupPath(module, groupPath); - } -} - +/* + * 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.module.impl; + +import com.intellij.ProjectTopics; +import com.intellij.openapi.application.ApplicationManager; +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.containers.HashMap; +import com.intellij.util.containers.HashSet; +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.messages.MessageBus; +import gnu.trove.THashMap; +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, ModificationTracker { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleManagerImpl"); + public static final Key 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 myModulePaths; + private final List myFailedModulePaths = new ArrayList(); + @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"; + private long myModificationCount; + + 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 long getModificationCount() { + return myModificationCount; + } + + @Override + public Element getState() { + final Element e = new Element("state"); + writeExternal(e); + return e; + } + + @Override + public void loadState(Element state) { + List prevPaths = myModulePaths; + readExternal(state); + if (prevPaths != null) { + ModifiableModuleModel model = getModifiableModel(); + + Module[] existingModules = model.getModules(); + + + 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 ? groupStr.split(MODULE_GROUP_SEPARATOR) : null; + if (!Arrays.equals(group, model.getModuleGroupPath(existingModule))) { + model.setModuleGroupPath(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; + } + } + + public static ModulePath[] getPathsToModuleFiles(Element element) { + final List paths = new ArrayList(); + 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(Arrays.asList(getPathsToModuleFiles(element))); + } + + protected void loadModules(final ModuleModelImpl moduleModel) { + if (myModulePaths != null && !myModulePaths.isEmpty()) { + final ProgressIndicator progressIndicator = myProject.isDefault() ? null : ProgressIndicatorProvider.getGlobalProgressIndicator(); + if (progressIndicator != null) { + progressIndicator.setText("Loading modules..."); + progressIndicator.setText2(""); + } + myFailedModulePaths.clear(); + myFailedModulePaths.addAll(myModulePaths); + final List modulesWithUnknownTypes = new ArrayList(); + List errors = new ArrayList(); + + for (final ModulePath modulePath : myModulePaths) { + try { + final Module module = moduleModel.loadModuleInternal(modulePath.getPath(), progressIndicator); + if (isUnknownModuleType(module)) { + modulesWithUnknownTypes.add(module); + } + final String groupPathString = modulePath.getModuleGroup(); + if (groupPathString != null) { + final String[] groupPath = groupPathString.split(MODULE_GROUP_SEPARATOR); + moduleModel.setModuleGroupPath(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)); + } + } + + fireErrors(errors); + + showUnknownModuleTypeNotification(modulesWithUnknownTypes); + } + } + + protected boolean isUnknownModuleType(Module module) { + return false; + } + + protected void showUnknownModuleTypeNotification(List 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 modules) { + if (!modules.isEmpty()) { + myMessageBus.syncPublisher(ProjectTopics.MODULES).modulesRenamed(myProject, modules); + } + } + + private void fireErrors(final List 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); + } + }); + } + } + + 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(StandardFileSystems.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 sorted = new ArrayList(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() { + @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) { + myModificationCount++; + 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 { + myModificationCount++; + 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 myCachedModuleComparator = null; + + @Override + @NotNull + public Comparator moduleDependencyComparator() { + ApplicationManager.getApplication().assertReadAccessAllowed(); + deliverPendingEvents(); + if (myCachedModuleComparator == null) { + myCachedModuleComparator = myModuleModel.moduleDependencyComparator(); + } + return myCachedModuleComparator; + } + + protected void deliverPendingEvents() { + } + + @Override + @NotNull + public Graph moduleGraph() { + return moduleGraph(true); + } + + @NotNull + @Override + public Graph moduleGraph(boolean includeTests) { + ApplicationManager.getApplication().assertReadAccessAllowed(); + return myModuleModel.moduleGraph(includeTests); + } + + @Override + @NotNull public List 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 myPathToModule = new LinkedHashMap(); + private Module[] myModulesCache; + + private final List myModulesToDispose = new ArrayList(); + private final Map myModuleToNewName = new HashMap(); + private final Map myNewNameToModule = new HashMap(); + private boolean myIsWritable; + private Map myModuleGroupPath; + + ModuleModelImpl() { + myIsWritable = false; + } + + ModuleModelImpl(ModuleModelImpl that) { + myPathToModule.putAll(that.myPathToModule); + final Map groupPath = that.myModuleGroupPath; + if (groupPath != null){ + myModuleGroupPath = new THashMap(); + 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 modules = myPathToModule.values(); + myModulesCache = modules.toArray(new Module[modules.size()]); + } + return myModulesCache; + } + + private Module[] getSortedModules() { + Module[] allModules = getModules(); + 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 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 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) { + final Collection modules = myPathToModule.values(); + for (Module module : modules) { + if (FileUtil.pathsEqual(filePath, module.getModuleFilePath())) { + return (ModuleEx)module; + } + } + return null; + } + + @Override + @NotNull + public Module loadModule(@NotNull String filePath) throws InvalidDataException, IOException, ModuleWithNameAlreadyExists { + assertWritable(); + try { + return loadModuleInternal(filePath, null); + } + catch (StateStorageException e) { + throw new IOException(ProjectBundle.message("module.corrupted.file.error", FileUtil.toSystemDependentName(filePath), e.getMessage())); + } + } + + private Module loadModuleInternal(String filePath, @Nullable ProgressIndicator progressIndicator) + throws ModuleWithNameAlreadyExists, IOException, StateStorageException { + + final VirtualFile moduleFile = StandardFileSystems.local().findFileByPath(resolveShortWindowsName(filePath)); + if (moduleFile == null || !moduleFile.exists()) { + throw new IOException(ProjectBundle.message("module.file.does.not.exist.error", filePath)); + } + + final String name = moduleFile.getName(); + if (progressIndicator != null) { + progressIndicator.setText2(FileUtil.getNameWithoutExtension(name)); + } + + 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) { + moduleFile.refresh(false, false); + 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 moduleDependencyComparator() { + DFSTBuilder builder = new DFSTBuilder(moduleGraph(true)); + return builder.comparator(); + } + + private Graph moduleGraph(final boolean includeTests) { + return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph() { + @Override + public Collection getNodes() { + return myPathToModule.values(); + } + + @Override + public Iterator getIn(Module m) { + Module[] dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests); + return Arrays.asList(dependentModules).iterator(); + } + })); + } + + @NotNull private List getModuleDependentModules(Module module) { + List result = new ArrayList(); + 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 list = myModuleModel.myPathToModule.values(); + final Collection 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 thisModules = new HashSet(myPathToModule.values()); + Set thatModules = new HashSet(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 collection = myPathToModule.values(); + for (final Module aCollection : collection) { + ModuleEx module = (ModuleEx)aCollection; + module.projectOpened(); + } + } + + public void projectClosed() { + final Collection 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(Module module, String[] groupPath) { + if (myModuleGroupPath == null) { + myModuleGroupPath = new THashMap(); + } + if (groupPath == null) { + myModuleGroupPath.remove(module); + } + else { + myModuleGroupPath.put(module, groupPath); + } + } + + @Override + public void setModuleFilePath(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; + myModificationCount++; + ApplicationManager.getApplication().assertWriteAccessAllowed(); + final Collection oldModules = myModuleModel.myPathToModule.values(); + final Collection newModules = moduleModel.myPathToModule.values(); + final List removedModules = new ArrayList(oldModules); + removedModules.removeAll(newModules); + final List addedModules = new ArrayList(newModules); + addedModules.removeAll(oldModules); + + ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() { + @Override + public void run() { + for (Module removedModule : removedModules) { + fireBeforeModuleRemoved(removedModule); + cleanCachedStuff(); + } + + List neverAddedModules = new ArrayList(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 modulesToNewNamesMap = moduleModel.myModuleToNewName; + final Set modulesToBeRenamed = modulesToNewNamesMap.keySet(); + modulesToBeRenamed.removeAll(moduleModel.myModulesToDispose); + final List modules = new ArrayList(); + for (final Module moduleToBeRenamed : modulesToBeRenamed) { + ModuleEx module = (ModuleEx)moduleToBeRenamed; + moduleModel.myPathToModule.remove(moduleToBeRenamed.getModuleFilePath()); + modules.add(moduleToBeRenamed); + module.rename(modulesToNewNamesMap.get(moduleToBeRenamed)); + moduleModel.myPathToModule.put(moduleToBeRenamed.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); + cleanCachedStuff(); + } + }, false, true); + } + + void fireModuleRenamedByVfsEvent(@NotNull final Module module) { + ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(new Runnable() { + @Override + public void run() { + fireModulesRenamed(Collections.singletonList(module)); + } + }, false, true); + } + + @Override + public String[] getModuleGroupPath(@NotNull Module module) { + return myModuleModel.getModuleGroupPath(module); + } + + public void setModuleGroupPath(Module module, String[] groupPath) { + myModuleModel.setModuleGroupPath(module, groupPath); + } +} +