diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java new file mode 100644 index 000000000000..52582de8366b --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2017 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.externalSystem.service.project; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.ExternalProjectInfo; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * Aggregates all {@link ProjectDataService#EP_NAME registered data services} + * and provides entry points for project data management. + * + * @author Vladislav Soroka + * @since 4/16/13 11:38 AM + */ +public interface ProjectDataManager { + static ProjectDataManager getInstance() { + return ServiceManager.getService(ProjectDataManager.class); + } + + @SuppressWarnings("unchecked") + void importData(@NotNull Collection> nodes, + @NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + boolean synchronous); + + void importData(@NotNull Collection> nodes, @NotNull Project project, boolean synchronous); + + void importData(@NotNull DataNode node, + @NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + boolean synchronous); + + void importData(@NotNull DataNode node, + @NotNull Project project, + boolean synchronous); + + void ensureTheDataIsReadyToUse(@Nullable DataNode dataNode); + + @Nullable + ExternalProjectInfo getExternalProjectData(@NotNull Project project, + @NotNull ProjectSystemId projectSystemId, + @NotNull String externalProjectPath); + + @NotNull + Collection getExternalProjectsData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId); +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java index bdb4445ef521..37fc4f8f27aa 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -15,394 +15,102 @@ */ package com.intellij.openapi.externalSystem.service.project.manage; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.externalSystem.model.*; -import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.ExternalProjectInfo; +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; -import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl; -import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange; -import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; -import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; -import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.impl.ProjectImpl; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.NotNullLazyValue; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ExceptionUtil; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.ContainerUtilRt; -import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; - -import static com.intellij.util.containers.ContainerUtil.map2Array; +import java.util.Collection; /** - * Aggregates all {@link ProjectDataService#EP_NAME registered data services} and provides entry points for project data management. - * - * @author Denis Zhdanov - * @since 4/16/13 11:38 AM + * @deprecated use {@link com.intellij.openapi.externalSystem.service.project.ProjectDataManager} instead */ -public class ProjectDataManager { - - private static final Logger LOG = Logger.getInstance("#" + ProjectDataManager.class.getName()); - private static final com.intellij.openapi.util.Key DATA_READY = - com.intellij.openapi.util.Key.create("externalSystem.data.ready"); - - @NotNull private final NotNullLazyValue, List>>> myServices; +public class ProjectDataManager extends ProjectDataManagerImpl { public static ProjectDataManager getInstance() { - return ServiceManager.getService(ProjectDataManager.class); + return new ProjectDataManager(ProjectDataManagerImpl.getInstance()); } - public ProjectDataManager() { - myServices = new NotNullLazyValue, List>>>() { - @NotNull - @Override - protected Map, List>> compute() { - Map, List>> result = ContainerUtilRt.newHashMap(); - for (ProjectDataService service : ProjectDataService.EP_NAME.getExtensions()) { - List> services = result.get(service.getTargetDataKey()); - if (services == null) { - result.put(service.getTargetDataKey(), services = ContainerUtilRt.newArrayList()); - } - services.add(service); - } + private final ProjectDataManagerImpl delegate; - for (List> services : result.values()) { - ExternalSystemApiUtil.orderAwareSort(services); - } - return result; - } - }; - } + public ProjectDataManager(ProjectDataManagerImpl delegate) {this.delegate = delegate;} - @SuppressWarnings("unchecked") + @Override public void importData(@NotNull Collection> nodes, @NotNull Project project, @NotNull IdeModifiableModelsProvider modelsProvider, boolean synchronous) { - if (project.isDisposed()) return; - - MultiMap, DataNode> grouped = ExternalSystemApiUtil.recursiveGroup(nodes); - for (Key key : myServices.getValue().keySet()) { - if (!grouped.containsKey(key)) { - grouped.put(key, Collections.>emptyList()); - } - } - - final Collection> projects = grouped.get(ProjectKeys.PROJECT); - // only one project(can be multi-module project) expected for per single import - assert projects.size() == 1 || projects.isEmpty(); - - final DataNode projectNode = (DataNode)ContainerUtil.getFirstItem(projects); - final ProjectData projectData; - ProjectSystemId projectSystemId; - if (projectNode != null) { - projectData = projectNode.getData(); - projectSystemId = projectNode.getData().getOwner(); - ExternalProjectsDataStorage.getInstance(project).saveInclusionSettings(projectNode); - } - else { - projectData = null; - DataNode aModuleNode = (DataNode)ContainerUtil.getFirstItem(grouped.get(ProjectKeys.MODULE)); - projectSystemId = aModuleNode != null ? aModuleNode.getData().getOwner() : null; - } - - if (projectSystemId != null) { - ExternalSystemUtil.scheduleExternalViewStructureUpdate(project, projectSystemId); - } - - List onSuccessImportTasks = ContainerUtil.newSmartList(); - try { - final Set, Collection>>> entries = grouped.entrySet(); - final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator != null) { - indicator.setIndeterminate(false); - } - final int size = entries.size(); - int count = 0; - List postImportTasks = ContainerUtil.newSmartList(); - for (Map.Entry, Collection>> entry : entries) { - if (indicator != null) { - String message = ExternalSystemBundle.message( - "progress.update.text", projectSystemId != null ? projectSystemId.getReadableName() : "", - "Refresh " + getReadableText(entry.getKey())); - indicator.setText(message); - indicator.setFraction((double)count++ / size); - } - doImportData(entry.getKey(), entry.getValue(), projectData, project, modelsProvider, postImportTasks, onSuccessImportTasks); - } - - for (Runnable postImportTask : postImportTasks) { - postImportTask.run(); - } - - commit(modelsProvider, project, synchronous, "Imported data"); - if (indicator != null) { - indicator.setIndeterminate(true); - } - } - catch (Throwable t) { - dispose(modelsProvider, project, synchronous); - ExceptionUtil.rethrowAllAsUnchecked(t); - } - - for (Runnable onSuccessImportTask : ContainerUtil.reverse(onSuccessImportTasks)) { - onSuccessImportTask.run(); - } + delegate.importData(nodes, project, modelsProvider, synchronous); } - @NotNull - private static String getReadableText(@NotNull Key key) { - StringBuilder buffer = new StringBuilder(); - String s = key.toString(); - for (int i = 0; i < s.length(); i++) { - char currChar = s.charAt(i); - if (Character.isUpperCase(currChar)) { - if (i != 0) { - buffer.append(' '); - } - buffer.append(StringUtil.toLowerCase(currChar)); - } - else { - buffer.append(currChar); - } - } - return buffer.toString(); - } - - public void importData(@NotNull Collection> nodes, @NotNull Project project, boolean synchronous) { - Collection> dummy = ContainerUtil.newSmartList(); - for (DataNode node : nodes) { - dummy.add(node); - } - importData(dummy, project, new IdeModifiableModelsProviderImpl(project), synchronous); + @Override + public void importData(@NotNull Collection> nodes, + @NotNull Project project, boolean synchronous) { + delegate.importData(nodes, project, synchronous); } + @Override public void importData(@NotNull DataNode node, @NotNull Project project, @NotNull IdeModifiableModelsProvider modelsProvider, boolean synchronous) { - Collection> dummy = ContainerUtil.newSmartList(); - dummy.add(node); - importData(dummy, project, modelsProvider, synchronous); + delegate.importData(node, project, modelsProvider, synchronous); } + @Override public void importData(@NotNull DataNode node, - @NotNull Project project, - boolean synchronous) { - importData(node, project, new IdeModifiableModelsProviderImpl(project), synchronous); - } - - @SuppressWarnings("unchecked") - private void doImportData(@NotNull Key key, - @NotNull Collection> nodes, - @Nullable final ProjectData projectData, - @NotNull final Project project, - @NotNull final IdeModifiableModelsProvider modelsProvider, - @NotNull final List postImportTasks, - @NotNull final List onSuccessImportTasks) { - if (project.isDisposed()) return; - if (project instanceof ProjectImpl) { - assert ((ProjectImpl)project).isComponentsCreated(); - } - - final List> toImport = ContainerUtil.newSmartList(); - final List> toIgnore = ContainerUtil.newSmartList(); - - for (DataNode node : nodes) { - if (!key.equals(node.getKey())) continue; - - if (node.isIgnored()) { - toIgnore.add(node); - } - else { - toImport.add(node); - } - } - - ensureTheDataIsReadyToUse((Collection)toImport); - - final List> services = myServices.getValue().get(key); - if (services == null) { - LOG.warn(String.format( - "Can't import data nodes '%s'. Reason: no service is registered for key %s. Available services for %s", - toImport, key, myServices.getValue().keySet() - )); - } - else { - for (ProjectDataService service : services) { - final long importStartTime = System.currentTimeMillis(); - ((ProjectDataService)service).importData(toImport, projectData, project, modelsProvider); - if(LOG.isDebugEnabled()) { - final long importTimeInMs = (System.currentTimeMillis() - importStartTime); - LOG.debug(String.format("Service %s imported data in %d ms", service.getClass().getSimpleName(), importTimeInMs)); - } - - if(projectData != null) { - ensureTheDataIsReadyToUse((Collection)toIgnore); - final long removeStartTime = System.currentTimeMillis(); - final Computable> orphanIdeDataComputable = - ((ProjectDataService)service).computeOrphanData(toImport, projectData, project, modelsProvider); - ((ProjectDataService)service).removeData(orphanIdeDataComputable, toIgnore, projectData, project, modelsProvider); - if(LOG.isDebugEnabled()) { - final long removeTimeInMs = (System.currentTimeMillis() - removeStartTime); - LOG.debug(String.format("Service %s computed and removed data in %d ms", service.getClass().getSimpleName(), removeTimeInMs)); - } - } - } - } - - if (services != null && projectData != null) { - postImportTasks.add(() -> { - for (ProjectDataService service : services) { - if (service instanceof AbstractProjectDataService) { - final long taskStartTime = System.currentTimeMillis(); - ((AbstractProjectDataService)service).postProcess(toImport, projectData, project, modelsProvider); - if(LOG.isDebugEnabled()) { - final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime); - LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs)); - } - } - } - }); - onSuccessImportTasks.add(() -> { - for (ProjectDataService service : services) { - if (service instanceof AbstractProjectDataService) { - final long taskStartTime = System.currentTimeMillis(); - ((AbstractProjectDataService)service).onSuccessImport(project); - if(LOG.isDebugEnabled()) { - final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime); - LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs)); - } - } - } - }); - } + @NotNull Project project, boolean synchronous) { + delegate.importData(node, project, synchronous); } + @Override public void ensureTheDataIsReadyToUse(@Nullable DataNode dataNode) { - if (dataNode == null) return; - if (Boolean.TRUE.equals(dataNode.getUserData(DATA_READY))) return; - - ExternalSystemApiUtil.visit(dataNode, dataNode1 -> { - prepareDataToUse(dataNode1); - dataNode1.putUserData(DATA_READY, Boolean.TRUE); - }); + delegate.ensureTheDataIsReadyToUse(dataNode); } - @SuppressWarnings("unchecked") + @Override public void removeData(@NotNull Key key, @NotNull Collection toRemove, - @NotNull final Collection> toIgnore, - @NotNull final ProjectData projectData, + @NotNull Collection> toIgnore, + @NotNull ProjectData projectData, @NotNull Project project, - @NotNull final IdeModifiableModelsProvider modelsProvider, + @NotNull IdeModifiableModelsProvider modelsProvider, boolean synchronous) { - try { - List> services = myServices.getValue().get(key); - for (ProjectDataService service : services) { - final long removeStartTime = System.currentTimeMillis(); - service.removeData(new Computable.PredefinedValueComputable(toRemove), toIgnore, projectData, project, modelsProvider); - if(LOG.isDebugEnabled()) { - final long removeTimeInMs = System.currentTimeMillis() - removeStartTime; - LOG.debug(String.format("Service %s removed data in %d ms", service.getClass().getSimpleName(), removeTimeInMs)); - } - } - - commit(modelsProvider, project, synchronous, "Removed data"); - } - catch (Throwable t) { - dispose(modelsProvider, project, synchronous); - ExceptionUtil.rethrowAllAsUnchecked(t); - } + delegate.removeData(key, toRemove, toIgnore, projectData, project, modelsProvider, synchronous); } + @Override public void removeData(@NotNull Key key, @NotNull Collection toRemove, - @NotNull final Collection> toIgnore, - @NotNull final ProjectData projectData, - @NotNull Project project, - boolean synchronous) { - removeData(key, toRemove, toIgnore, projectData, project, new IdeModifiableModelsProviderImpl(project), synchronous); + @NotNull Collection> toIgnore, + @NotNull ProjectData projectData, + @NotNull Project project, boolean synchronous) { + delegate.removeData(key, toRemove, toIgnore, projectData, project, synchronous); } - public void updateExternalProjectData(@NotNull Project project, @NotNull ExternalProjectInfo externalProjectInfo) { - if (!project.isDisposed()) { - ExternalProjectsManager.getInstance(project).updateExternalProjectData(externalProjectInfo); - } + @Override + public void updateExternalProjectData(@NotNull Project project, + @NotNull ExternalProjectInfo externalProjectInfo) { + delegate.updateExternalProjectData(project, externalProjectInfo); } @Nullable + @Override public ExternalProjectInfo getExternalProjectData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) { - return !project.isDisposed() ? ExternalProjectsDataStorage.getInstance(project).get(projectSystemId, externalProjectPath) : null; + return delegate.getExternalProjectData(project, projectSystemId, externalProjectPath); } @NotNull - public Collection getExternalProjectsData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId) { - if (!project.isDisposed()) { - return ExternalProjectsDataStorage.getInstance(project).list(projectSystemId); - } - else { - return ContainerUtil.emptyList(); - } - } - - private void ensureTheDataIsReadyToUse(@NotNull Collection> nodes) { - for (DataNode node : nodes) { - ensureTheDataIsReadyToUse(node); - } - } - - private void prepareDataToUse(@NotNull DataNode dataNode) { - final Map, List>> servicesByKey = myServices.getValue(); - List> services = servicesByKey.get(dataNode.getKey()); - if (services != null) { - try { - dataNode.prepareData(map2Array(services, ClassLoader.class, service -> service.getClass().getClassLoader())); - } - catch (Exception e) { - LOG.debug(e); - dataNode.clear(true); - } - } - } - - private static void commit(@NotNull final IdeModifiableModelsProvider modelsProvider, - @NotNull Project project, - boolean synchronous, - @NotNull final String commitDesc) { - ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) { - @Override - public void execute() { - final long startTime = System.currentTimeMillis(); - modelsProvider.commit(); - final long timeInMs = System.currentTimeMillis() - startTime; - LOG.debug(String.format("%s committed in %d ms", commitDesc, timeInMs)); - } - }); - } - - private static void dispose(@NotNull final IdeModifiableModelsProvider modelsProvider, - @NotNull Project project, - boolean synchronous) { - ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) { - @Override - public void execute() { - modelsProvider.dispose(); - } - }); + @Override + public Collection getExternalProjectsData(@NotNull Project project, + @NotNull ProjectSystemId projectSystemId) { + return delegate.getExternalProjectsData(project, projectSystemId); } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java new file mode 100644 index 000000000000..dcdaad0854b1 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java @@ -0,0 +1,416 @@ +/* + * Copyright 2000-2013 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.externalSystem.service.project.manage; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.*; +import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl; +import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; +import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; +import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.impl.ProjectImpl; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ExceptionUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.ContainerUtilRt; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +import static com.intellij.util.containers.ContainerUtil.map2Array; + +/** + * Aggregates all {@link ProjectDataService#EP_NAME registered data services} and provides entry points for project data management. + * + * @author Denis Zhdanov + * @since 4/16/13 11:38 AM + */ +public class ProjectDataManagerImpl implements ProjectDataManager { + + private static final Logger LOG = Logger.getInstance("#" + ProjectDataManagerImpl.class.getName()); + private static final com.intellij.openapi.util.Key DATA_READY = + com.intellij.openapi.util.Key.create("externalSystem.data.ready"); + + @NotNull private final NotNullLazyValue, List>>> myServices; + + public static ProjectDataManagerImpl getInstance() { + ProjectDataManager service = ServiceManager.getService(ProjectDataManager.class); + return (ProjectDataManagerImpl)service; + } + + public ProjectDataManagerImpl() { + myServices = new NotNullLazyValue, List>>>() { + @NotNull + @Override + protected Map, List>> compute() { + Map, List>> result = ContainerUtilRt.newHashMap(); + for (ProjectDataService service : ProjectDataService.EP_NAME.getExtensions()) { + List> services = result.get(service.getTargetDataKey()); + if (services == null) { + result.put(service.getTargetDataKey(), services = ContainerUtilRt.newArrayList()); + } + services.add(service); + } + + for (List> services : result.values()) { + ExternalSystemApiUtil.orderAwareSort(services); + } + return result; + } + }; + } + + @SuppressWarnings("unchecked") + @Override + public void importData(@NotNull Collection> nodes, + @NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + boolean synchronous) { + if (project.isDisposed()) return; + + MultiMap, DataNode> grouped = ExternalSystemApiUtil.recursiveGroup(nodes); + for (Key key : myServices.getValue().keySet()) { + if (!grouped.containsKey(key)) { + grouped.put(key, Collections.emptyList()); + } + } + + final Collection> projects = grouped.get(ProjectKeys.PROJECT); + // only one project(can be multi-module project) expected for per single import + assert projects.size() == 1 || projects.isEmpty(); + + final DataNode projectNode = (DataNode)ContainerUtil.getFirstItem(projects); + final ProjectData projectData; + ProjectSystemId projectSystemId; + if (projectNode != null) { + projectData = projectNode.getData(); + projectSystemId = projectNode.getData().getOwner(); + ExternalProjectsDataStorage.getInstance(project).saveInclusionSettings(projectNode); + } + else { + projectData = null; + DataNode aModuleNode = (DataNode)ContainerUtil.getFirstItem(grouped.get(ProjectKeys.MODULE)); + projectSystemId = aModuleNode != null ? aModuleNode.getData().getOwner() : null; + } + + if (projectSystemId != null) { + ExternalSystemUtil.scheduleExternalViewStructureUpdate(project, projectSystemId); + } + + List onSuccessImportTasks = ContainerUtil.newSmartList(); + try { + final Set, Collection>>> entries = grouped.entrySet(); + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + if (indicator != null) { + indicator.setIndeterminate(false); + } + final int size = entries.size(); + int count = 0; + List postImportTasks = ContainerUtil.newSmartList(); + for (Map.Entry, Collection>> entry : entries) { + if (indicator != null) { + String message = ExternalSystemBundle.message( + "progress.update.text", projectSystemId != null ? projectSystemId.getReadableName() : "", + "Refresh " + getReadableText(entry.getKey())); + indicator.setText(message); + indicator.setFraction((double)count++ / size); + } + doImportData(entry.getKey(), entry.getValue(), projectData, project, modelsProvider, postImportTasks, onSuccessImportTasks); + } + + for (Runnable postImportTask : postImportTasks) { + postImportTask.run(); + } + + commit(modelsProvider, project, synchronous, "Imported data"); + if (indicator != null) { + indicator.setIndeterminate(true); + } + + project.getMessageBus().syncPublisher(ProjectDataImportListener.TOPIC) + .onImportFinished(projectData != null ? projectData.getLinkedExternalProjectPath() : null); + } + catch (Throwable t) { + dispose(modelsProvider, project, synchronous); + ExceptionUtil.rethrowAllAsUnchecked(t); + } + + for (Runnable onSuccessImportTask : ContainerUtil.reverse(onSuccessImportTasks)) { + onSuccessImportTask.run(); + } + } + + @NotNull + private static String getReadableText(@NotNull Key key) { + StringBuilder buffer = new StringBuilder(); + String s = key.toString(); + for (int i = 0; i < s.length(); i++) { + char currChar = s.charAt(i); + if (Character.isUpperCase(currChar)) { + if (i != 0) { + buffer.append(' '); + } + buffer.append(StringUtil.toLowerCase(currChar)); + } + else { + buffer.append(currChar); + } + } + return buffer.toString(); + } + + @Override + public void importData(@NotNull Collection> nodes, @NotNull Project project, boolean synchronous) { + Collection> dummy = ContainerUtil.newSmartList(); + dummy.addAll(nodes); + importData(dummy, project, new IdeModifiableModelsProviderImpl(project), synchronous); + } + + @Override + public void importData(@NotNull DataNode node, + @NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + boolean synchronous) { + Collection> dummy = ContainerUtil.newSmartList(); + dummy.add(node); + importData(dummy, project, modelsProvider, synchronous); + } + + @Override + public void importData(@NotNull DataNode node, + @NotNull Project project, + boolean synchronous) { + importData(node, project, new IdeModifiableModelsProviderImpl(project), synchronous); + } + + @SuppressWarnings("unchecked") + private void doImportData(@NotNull Key key, + @NotNull Collection> nodes, + @Nullable final ProjectData projectData, + @NotNull final Project project, + @NotNull final IdeModifiableModelsProvider modelsProvider, + @NotNull final List postImportTasks, + @NotNull final List onSuccessImportTasks) { + if (project.isDisposed()) return; + if (project instanceof ProjectImpl) { + assert ((ProjectImpl)project).isComponentsCreated(); + } + + final List> toImport = ContainerUtil.newSmartList(); + final List> toIgnore = ContainerUtil.newSmartList(); + + for (DataNode node : nodes) { + if (!key.equals(node.getKey())) continue; + + if (node.isIgnored()) { + toIgnore.add(node); + } + else { + toImport.add(node); + } + } + + ensureTheDataIsReadyToUse((Collection)toImport); + + final List> services = myServices.getValue().get(key); + if (services == null) { + LOG.warn(String.format( + "Can't import data nodes '%s'. Reason: no service is registered for key %s. Available services for %s", + toImport, key, myServices.getValue().keySet() + )); + } + else { + for (ProjectDataService service : services) { + final long importStartTime = System.currentTimeMillis(); + ((ProjectDataService)service).importData(toImport, projectData, project, modelsProvider); + if (LOG.isDebugEnabled()) { + final long importTimeInMs = (System.currentTimeMillis() - importStartTime); + LOG.debug(String.format("Service %s imported data in %d ms", service.getClass().getSimpleName(), importTimeInMs)); + } + + if (projectData != null) { + ensureTheDataIsReadyToUse((Collection)toIgnore); + final long removeStartTime = System.currentTimeMillis(); + final Computable> orphanIdeDataComputable = + ((ProjectDataService)service).computeOrphanData(toImport, projectData, project, modelsProvider); + ((ProjectDataService)service).removeData(orphanIdeDataComputable, toIgnore, projectData, project, modelsProvider); + if (LOG.isDebugEnabled()) { + final long removeTimeInMs = (System.currentTimeMillis() - removeStartTime); + LOG.debug(String.format("Service %s computed and removed data in %d ms", service.getClass().getSimpleName(), removeTimeInMs)); + } + } + } + } + + if (services != null && projectData != null) { + postImportTasks.add(() -> { + for (ProjectDataService service : services) { + if (service instanceof AbstractProjectDataService) { + final long taskStartTime = System.currentTimeMillis(); + ((AbstractProjectDataService)service).postProcess(toImport, projectData, project, modelsProvider); + if (LOG.isDebugEnabled()) { + final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime); + LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs)); + } + } + } + }); + onSuccessImportTasks.add(() -> { + for (ProjectDataService service : services) { + if (service instanceof AbstractProjectDataService) { + final long taskStartTime = System.currentTimeMillis(); + ((AbstractProjectDataService)service).onSuccessImport(project); + if (LOG.isDebugEnabled()) { + final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime); + LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs)); + } + } + } + }); + } + } + + @Override + public void ensureTheDataIsReadyToUse(@Nullable DataNode dataNode) { + if (dataNode == null) return; + if (Boolean.TRUE.equals(dataNode.getUserData(DATA_READY))) return; + + ExternalSystemApiUtil.visit(dataNode, dataNode1 -> { + prepareDataToUse(dataNode1); + dataNode1.putUserData(DATA_READY, Boolean.TRUE); + }); + } + + @SuppressWarnings("unchecked") + public void removeData(@NotNull Key key, + @NotNull Collection toRemove, + @NotNull final Collection> toIgnore, + @NotNull final ProjectData projectData, + @NotNull Project project, + @NotNull final IdeModifiableModelsProvider modelsProvider, + boolean synchronous) { + try { + List> services = myServices.getValue().get(key); + for (ProjectDataService service : services) { + final long removeStartTime = System.currentTimeMillis(); + service.removeData(new Computable.PredefinedValueComputable(toRemove), toIgnore, projectData, project, modelsProvider); + if (LOG.isDebugEnabled()) { + final long removeTimeInMs = System.currentTimeMillis() - removeStartTime; + LOG.debug(String.format("Service %s removed data in %d ms", service.getClass().getSimpleName(), removeTimeInMs)); + } + } + + commit(modelsProvider, project, synchronous, "Removed data"); + } + catch (Throwable t) { + dispose(modelsProvider, project, synchronous); + ExceptionUtil.rethrowAllAsUnchecked(t); + } + } + + public void removeData(@NotNull Key key, + @NotNull Collection toRemove, + @NotNull final Collection> toIgnore, + @NotNull final ProjectData projectData, + @NotNull Project project, + boolean synchronous) { + removeData(key, toRemove, toIgnore, projectData, project, new IdeModifiableModelsProviderImpl(project), synchronous); + } + + public void updateExternalProjectData(@NotNull Project project, @NotNull ExternalProjectInfo externalProjectInfo) { + if (!project.isDisposed()) { + ExternalProjectsManagerImpl.getInstance(project).updateExternalProjectData(externalProjectInfo); + } + } + + @Nullable + @Override + public ExternalProjectInfo getExternalProjectData(@NotNull Project project, + @NotNull ProjectSystemId projectSystemId, + @NotNull String externalProjectPath) { + return !project.isDisposed() ? ExternalProjectsDataStorage.getInstance(project).get(projectSystemId, externalProjectPath) : null; + } + + @NotNull + @Override + public Collection getExternalProjectsData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId) { + if (!project.isDisposed()) { + return ExternalProjectsDataStorage.getInstance(project).list(projectSystemId); + } + else { + return ContainerUtil.emptyList(); + } + } + + private void ensureTheDataIsReadyToUse(@NotNull Collection> nodes) { + for (DataNode node : nodes) { + ensureTheDataIsReadyToUse(node); + } + } + + private void prepareDataToUse(@NotNull DataNode dataNode) { + final Map, List>> servicesByKey = myServices.getValue(); + List> services = servicesByKey.get(dataNode.getKey()); + if (services != null) { + try { + dataNode.prepareData(map2Array(services, ClassLoader.class, service -> service.getClass().getClassLoader())); + } + catch (Exception e) { + LOG.debug(e); + dataNode.clear(true); + } + } + } + + private static void commit(@NotNull final IdeModifiableModelsProvider modelsProvider, + @NotNull Project project, + boolean synchronous, + @NotNull final String commitDesc) { + ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) { + @Override + public void execute() { + final long startTime = System.currentTimeMillis(); + modelsProvider.commit(); + final long timeInMs = System.currentTimeMillis() - startTime; + LOG.debug(String.format("%s committed in %d ms", commitDesc, timeInMs)); + } + }); + } + + private static void dispose(@NotNull final IdeModifiableModelsProvider modelsProvider, + @NotNull Project project, + boolean synchronous) { + ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) { + @Override + public void execute() { + modelsProvider.dispose(); + } + }); + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java index 96461c020e32..cd7f5336ce84 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java @@ -31,7 +31,8 @@ import com.intellij.openapi.externalSystem.model.project.Identifiable; import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData; import com.intellij.openapi.externalSystem.model.project.ProjectData; -import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil; @@ -123,7 +124,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper { } private void init(@NotNull ExternalProjectInfo projectInfo) { - ProjectDataManager.getInstance().ensureTheDataIsReadyToUse(projectInfo.getExternalProjectStructure()); + ProjectDataManagerImpl.getInstance().ensureTheDataIsReadyToUse(projectInfo.getExternalProjectStructure()); myProjectInfo = projectInfo; myExternalSystemUiAware = ExternalSystemUiUtil.getUiAware(myProjectInfo.getProjectSystemId()); myTree = createTree(); diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java index 465ea6900ea6..0aab905163b5 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java @@ -15,8 +15,6 @@ */ package com.intellij.openapi.externalSystem.test; -import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.ex.CompilerPathsEx; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; @@ -26,7 +24,8 @@ import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback; -import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl; import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings; import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; @@ -45,14 +44,12 @@ import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.packaging.artifacts.Artifact; import com.intellij.packaging.artifacts.ArtifactManager; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.util.BooleanFunction; -import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; @@ -162,7 +159,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest List actual = new ArrayList<>(); for (ContentEntry contentRoot : contentRoots) { for (SourceFolder f : contentRoot.getSourceFolders(rootType)) { - rootUrl = rootUrl == null ? VirtualFileManager.extractPath(contentRoot.getUrl()) : VirtualFileManager.extractPath(rootUrl); + rootUrl = VirtualFileManager.extractPath(rootUrl == null ? contentRoot.getUrl() : rootUrl); String folderUrl = VirtualFileManager.extractPath(f.getUrl()); if (folderUrl.startsWith(rootUrl)) { int length = rootUrl.length() + 1; @@ -215,7 +212,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest } private static String getAbsolutePath(String path) { - path = VfsUtil.urlToPath(path); + path = VfsUtilCore.urlToPath(path); path = PathUtil.getCanonicalPath(path); return FileUtil.toSystemIndependentName(path); } @@ -364,28 +361,12 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest protected void assertArtifacts(String... expectedNames) { final List actualNames = ContainerUtil.map( - ArtifactManager.getInstance(myProject).getAllArtifactsIncludingInvalid(), new Function() { - @Override - public String fun(Artifact artifact) { - return artifact.getName(); - } - }); + ArtifactManager.getInstance(myProject).getAllArtifactsIncludingInvalid(), + (Function)artifact -> artifact.getName()); assertUnorderedElementsAreEqual(actualNames, expectedNames); } - protected Module getModule(final String name) { - AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); - try { - Module m = ModuleManager.getInstance(myProject).findModuleByName(name); - assertNotNull("Module " + name + " not found", m); - return m; - } - finally { - accessToken.finish(); - } - } - private ContentEntry getContentRoot(String moduleName) { ContentEntry[] ee = getContentRoots(moduleName); List roots = new ArrayList<>(); @@ -415,7 +396,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest } protected void ignoreData(BooleanFunction> booleanFunction, final boolean ignored) { - final ExternalProjectInfo externalProjectInfo = ProjectDataManager.getInstance().getExternalProjectData( + final ExternalProjectInfo externalProjectInfo = ProjectDataManagerImpl.getInstance().getExternalProjectData( myProject, getExternalSystemId(), getCurrentExternalProjectSettings().getExternalProjectPath()); assertNotNull(externalProjectInfo);