/* * Copyright 2000-2016 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.vcs.log.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.VcsRoot; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsLogRefresher; import com.intellij.vcs.log.data.VcsLogData; import com.intellij.vcs.log.data.VcsLogStorage; import com.intellij.vcs.log.ui.*; import com.intellij.vcs.log.visible.VcsLogFilterer; import com.intellij.vcs.log.visible.VisiblePackRefresherImpl; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class VcsLogManager implements Disposable { private static final Logger LOG = Logger.getInstance(VcsLogManager.class); @NotNull private final Project myProject; @NotNull private final VcsLogTabsProperties myUiProperties; @Nullable private final Consumer<Throwable> myRecreateMainLogHandler; @NotNull private final VcsLogData myLogData; @NotNull private final VcsLogColorManagerImpl myColorManager; @NotNull private final VcsLogTabsWatcher myTabsLogRefresher; @NotNull private final PostponableLogRefresher myPostponableRefresher; private boolean myInitialized = false; public VcsLogManager(@NotNull Project project, @NotNull VcsLogTabsProperties uiProperties, @NotNull Collection<VcsRoot> roots) { this(project, uiProperties, roots, true, null); } public VcsLogManager(@NotNull Project project, @NotNull VcsLogTabsProperties uiProperties, @NotNull Collection<VcsRoot> roots, boolean scheduleRefreshImmediately, @Nullable Consumer<Throwable> recreateHandler) { myProject = project; myUiProperties = uiProperties; myRecreateMainLogHandler = recreateHandler; Map<VirtualFile, VcsLogProvider> logProviders = findLogProviders(roots, myProject); myLogData = new VcsLogData(myProject, logProviders, new MyFatalErrorsHandler()); myPostponableRefresher = new PostponableLogRefresher(myLogData); myTabsLogRefresher = new VcsLogTabsWatcher(myProject, myPostponableRefresher, myLogData); refreshLogOnVcsEvents(logProviders, myPostponableRefresher, myLogData); myColorManager = new VcsLogColorManagerImpl(logProviders.keySet()); if (scheduleRefreshImmediately) { scheduleInitialization(); } Disposer.register(project, this); } @CalledInAwt public void scheduleInitialization() { if (!myInitialized) { myInitialized = true; myLogData.initialize(); } } @CalledInAwt public boolean isLogVisible() { return myPostponableRefresher.isLogVisible(); } @NotNull public VcsLogData getDataManager() { return myLogData; } @NotNull public JComponent createLogPanel(@NotNull String logId, @Nullable String contentTabName) { AbstractVcsLogUi ui = createLogUi(logId, contentTabName); return new VcsLogPanel(this, ui); } @NotNull public VcsLogUiImpl createLogUi(@NotNull String logId, @Nullable String contentTabName) { return createLogUi(contentTabName, getMainLogUiFactory(logId)); } @NotNull public VcsLogUiFactory<? extends VcsLogUiImpl> getMainLogUiFactory(@NotNull String logId) { return new MainVcsLogUiFactory(logId); } @NotNull public <U extends AbstractVcsLogUi> U createLogUi(@Nullable String contentTabName, @NotNull VcsLogUiFactory<U> factory) { U ui = factory.createLogUi(myProject, myLogData, myColorManager); Disposable disposable; if (contentTabName != null) { disposable = myTabsLogRefresher.addTabToWatch(contentTabName, ui.getRefresher()); } else { disposable = myPostponableRefresher.addLogWindow(ui.getRefresher()); } Disposer.register(ui, disposable); ui.requestFocus(); return ui; } private static void refreshLogOnVcsEvents(@NotNull Map<VirtualFile, VcsLogProvider> logProviders, @NotNull VcsLogRefresher refresher, @NotNull Disposable disposableParent) { MultiMap<VcsLogProvider, VirtualFile> providers2roots = MultiMap.create(); logProviders.forEach((key, value) -> providers2roots.putValue(value, key)); for (Map.Entry<VcsLogProvider, Collection<VirtualFile>> entry : providers2roots.entrySet()) { Disposable disposable = entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), refresher); Disposer.register(disposableParent, disposable); } } @NotNull public static Map<VirtualFile, VcsLogProvider> findLogProviders(@NotNull Collection<VcsRoot> roots, @NotNull Project project) { Map<VirtualFile, VcsLogProvider> logProviders = ContainerUtil.newHashMap(); VcsLogProvider[] allLogProviders = Extensions.getExtensions(VcsLogProvider.LOG_PROVIDER_EP, project); for (VcsRoot root : roots) { AbstractVcs vcs = root.getVcs(); VirtualFile path = root.getPath(); if (vcs == null || path == null) { LOG.error("Skipping invalid VCS root: " + root); continue; } for (VcsLogProvider provider : allLogProviders) { if (provider.getSupportedVcs().equals(vcs.getKeyInstanceMethod())) { logProviders.put(path, provider); break; } } } return logProviders; } public void disposeLog() { Disposer.dispose(myLogData); } /* * Use VcsLogProjectManager to get main log. * Left here for upsource plugin. * */ @Nullable @Deprecated public static VcsLogManager getInstance(@NotNull Project project) { return ServiceManager.getService(project, VcsProjectLog.class).getLogManager(); } /* * Use VcsLogProjectManager.getMainLogUi to get main log ui. * Left here for upsource plugin. * */ @Nullable @Deprecated public VcsLogUiImpl getMainLogUi() { return ServiceManager.getService(myProject, VcsProjectLog.class).getMainLogUi(); } @Override public void dispose() { disposeLog(); } @NotNull public Set<String> getTabNames() { return myTabsLogRefresher.getTabNames(); } private class MyFatalErrorsHandler implements FatalErrorHandler { private final AtomicBoolean myIsBroken = new AtomicBoolean(false); @Override public void consume(@Nullable Object source, @NotNull Exception e) { if (myIsBroken.compareAndSet(false, true)) { processError(source, e); } else { LOG.debug("Vcs Log storage is broken and is being recreated", e); } } protected void processError(@Nullable Object source, @NotNull Exception e) { if (myRecreateMainLogHandler != null) { ApplicationManager.getApplication().invokeLater(() -> myRecreateMainLogHandler.consume(e)); } else { LOG.error(e); } if (source instanceof VcsLogStorage) { myLogData.getIndex().markCorrupted(); } } @Override public void displayFatalErrorMessage(@NotNull String message) { VcsBalloonProblemNotifier.showOverChangesView(myProject, message, MessageType.ERROR); } } @FunctionalInterface public interface VcsLogUiFactory<T extends AbstractVcsLogUi> { T createLogUi(@NotNull Project project, @NotNull VcsLogData logData, @NotNull VcsLogColorManager colorManager); } private class MainVcsLogUiFactory implements VcsLogUiFactory<VcsLogUiImpl> { private final String myLogId; public MainVcsLogUiFactory(@NotNull String logId) { myLogId = logId; } @Override public VcsLogUiImpl createLogUi(@NotNull Project project, @NotNull VcsLogData logData, @NotNull VcsLogColorManager manager) { MainVcsLogUiProperties properties = myUiProperties.createProperties(myLogId); VisiblePackRefresherImpl refresher = new VisiblePackRefresherImpl(project, logData, properties.get(MainVcsLogUiProperties.BEK_SORT_TYPE), new VcsLogFilterer(logData.getLogProviders(), logData.getStorage(), logData.getTopCommitsCache(), logData.getCommitDetailsGetter(), logData.getIndex())); return new VcsLogUiImpl(logData, project, manager, properties, refresher); } } }