| /* |
| * Copyright 2000-2009 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 org.jetbrains.idea.svn.update; |
| |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.vcs.VcsBundle; |
| import com.intellij.openapi.vcs.update.FileGroup; |
| import com.intellij.openapi.vcs.update.UpdatedFiles; |
| import com.intellij.openapi.wm.StatusBar; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.Stack; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.svn.SvnBundle; |
| import org.jetbrains.idea.svn.SvnFileUrlMapping; |
| import org.jetbrains.idea.svn.SvnRevisionNumber; |
| import org.jetbrains.idea.svn.SvnVcs; |
| import org.jetbrains.idea.svn.api.EventAction; |
| import org.jetbrains.idea.svn.api.ProgressEvent; |
| import org.jetbrains.idea.svn.api.ProgressTracker; |
| import org.jetbrains.idea.svn.status.StatusType; |
| import org.tmatesoft.svn.core.SVNCancelException; |
| import org.tmatesoft.svn.core.SVNURL; |
| import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; |
| import org.tmatesoft.svn.core.wc.SVNRevision; |
| import org.tmatesoft.svn.util.SVNLogType; |
| |
| import java.io.File; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author lesya |
| */ |
| public class UpdateEventHandler implements ProgressTracker { |
| private ProgressIndicator myProgressIndicator; |
| private UpdatedFiles myUpdatedFiles; |
| private int myExternalsCount; |
| private final SvnVcs myVCS; |
| @Nullable private final SvnUpdateContext mySequentialUpdatesContext; |
| private final Map<File, SVNURL> myUrlToCheckForSwitch; |
| // pair.first - group id, pair.second - file path |
| // Stack is used to correctly handle cases when updates of externals occur during ordinary update, because these inner updates could have |
| // its own revisions. |
| private final Stack<List<Pair<String, String>>> myFilesWaitingForRevision; |
| |
| protected String myText; |
| protected String myText2; |
| |
| public UpdateEventHandler(SvnVcs vcs, ProgressIndicator progressIndicator, |
| @Nullable final SvnUpdateContext sequentialUpdatesContext) { |
| myProgressIndicator = progressIndicator; |
| myVCS = vcs; |
| mySequentialUpdatesContext = sequentialUpdatesContext; |
| myExternalsCount = 1; |
| myUrlToCheckForSwitch = new HashMap<File, SVNURL>(); |
| myFilesWaitingForRevision = ContainerUtil.newStack(); |
| // It is more suitable to make this push while handling UPDATE_NONE event - for command line like "svn update <folder>" this event will |
| // be fired when update of <folder> is started. But it's not clear if this event won't be fired in other cases by SVNKit. So currently |
| // first push is made here. If further we want to support commands like "svn update <folder1> <folder2>" this logic should be revised. |
| myFilesWaitingForRevision.push(ContainerUtil.<Pair<String, String>>newArrayList()); |
| } |
| |
| public void addToSwitch(final File file, final SVNURL url) { |
| myUrlToCheckForSwitch.put(file, url); |
| } |
| |
| public void setUpdatedFiles(final UpdatedFiles updatedFiles) { |
| myUpdatedFiles = updatedFiles; |
| } |
| |
| public void consume(final ProgressEvent event) { |
| if (event == null || event.getFile() == null) { |
| return; |
| } |
| |
| String path = event.getFile().getAbsolutePath(); |
| String displayPath = event.getFile().getName(); |
| myText2 = null; |
| myText = null; |
| |
| if (handleInDescendants(event)) { |
| updateProgressIndicator(); |
| return; |
| } |
| |
| if (event.getAction() == EventAction.TREE_CONFLICT) { |
| myText2 = SvnBundle.message("progress.text2.treeconflicted", displayPath); |
| updateProgressIndicator(); |
| myUpdatedFiles.registerGroup(createFileGroup(VcsBundle.message("update.group.name.merged.with.tree.conflicts"), |
| FileGroup.MERGED_WITH_TREE_CONFLICT)); |
| addFileToGroup(FileGroup.MERGED_WITH_TREE_CONFLICT, event); |
| } |
| |
| if (event.getAction() == EventAction.UPDATE_ADD || |
| event.getAction() == EventAction.ADD) { |
| myText2 = SvnBundle.message("progress.text2.added", displayPath); |
| if (event.getContentsStatus() == StatusType.CONFLICTED || event.getPropertiesStatus() == StatusType.CONFLICTED) { |
| addFileToGroup(FileGroup.MERGED_WITH_CONFLICT_ID, event); |
| myText2 = SvnBundle.message("progress.text2.conflicted", displayPath); |
| } else if (myUpdatedFiles.getGroupById(FileGroup.REMOVED_FROM_REPOSITORY_ID).getFiles().contains(path)) { |
| myUpdatedFiles.getGroupById(FileGroup.REMOVED_FROM_REPOSITORY_ID).getFiles().remove(path); |
| if (myUpdatedFiles.getGroupById(AbstractSvnUpdateIntegrateEnvironment.REPLACED_ID) == null) { |
| myUpdatedFiles.registerGroup(createFileGroup(SvnBundle.message("status.group.name.replaced"), |
| AbstractSvnUpdateIntegrateEnvironment.REPLACED_ID)); |
| } |
| addFileToGroup(AbstractSvnUpdateIntegrateEnvironment.REPLACED_ID, event); |
| } else { |
| addFileToGroup(FileGroup.CREATED_ID, event); |
| } |
| } |
| else if (event.getAction() == EventAction.UPDATE_NONE) { |
| // skip it |
| return; |
| } |
| else if (event.getAction() == EventAction.UPDATE_DELETE) { |
| myText2 = SvnBundle.message("progress.text2.deleted", displayPath); |
| addFileToGroup(FileGroup.REMOVED_FROM_REPOSITORY_ID, event); |
| } |
| else if (event.getAction() == EventAction.UPDATE_UPDATE) { |
| possiblySwitched(event); |
| if (event.getContentsStatus() == StatusType.CONFLICTED || event.getPropertiesStatus() == StatusType.CONFLICTED) { |
| if (event.getContentsStatus() == StatusType.CONFLICTED) { |
| addFileToGroup(FileGroup.MERGED_WITH_CONFLICT_ID, event); |
| } |
| if (event.getPropertiesStatus() == StatusType.CONFLICTED) { |
| addFileToGroup(FileGroup.MERGED_WITH_PROPERTY_CONFLICT_ID, event); |
| } |
| myText2 = SvnBundle.message("progress.text2.conflicted", displayPath); |
| } |
| else if (event.getContentsStatus() == StatusType.MERGED || event.getPropertiesStatus() == StatusType.MERGED) { |
| myText2 = SvnBundle.message("progres.text2.merged", displayPath); |
| addFileToGroup(FileGroup.MERGED_ID, event); |
| } |
| else if (event.getContentsStatus() == StatusType.CHANGED || event.getPropertiesStatus() == StatusType.CHANGED) { |
| myText2 = SvnBundle.message("progres.text2.updated", displayPath); |
| addFileToGroup(FileGroup.UPDATED_ID, event); |
| } |
| else if (event.getContentsStatus() == StatusType.UNCHANGED && |
| (event.getPropertiesStatus() == StatusType.UNCHANGED || event.getPropertiesStatus() == StatusType.UNKNOWN)) { |
| myText2 = SvnBundle.message("progres.text2.updated", displayPath); |
| } else if (StatusType.INAPPLICABLE.equals(event.getContentsStatus()) && |
| (event.getPropertiesStatus() == StatusType.UNCHANGED || event.getPropertiesStatus() == StatusType.UNKNOWN)) { |
| myText2 = SvnBundle.message("progres.text2.updated", displayPath); |
| } |
| else { |
| myText2 = ""; |
| addFileToGroup(FileGroup.UNKNOWN_ID, event); |
| } |
| } |
| else if (event.getAction() == EventAction.UPDATE_EXTERNAL) { |
| if (mySequentialUpdatesContext != null) { |
| mySequentialUpdatesContext.registerExternalRootBeingUpdated(event.getFile()); |
| } |
| myFilesWaitingForRevision.push(ContainerUtil.<Pair<String, String>>newArrayList()); |
| myExternalsCount++; |
| myText = SvnBundle.message("progress.text.updating.external.location", event.getFile().getAbsolutePath()); |
| } |
| else if (event.getAction() == EventAction.RESTORE) { |
| myText2 = SvnBundle.message("progress.text2.restored.file", displayPath); |
| addFileToGroup(FileGroup.RESTORED_ID, event); |
| } |
| else if (event.getAction() == EventAction.UPDATE_COMPLETED && event.getRevision() >= 0) { |
| possiblySwitched(event); |
| setRevisionForWaitingFiles(event.getRevision()); |
| myExternalsCount--; |
| myText2 = SvnBundle.message("progres.text2.updated.to.revision", event.getRevision()); |
| if (myExternalsCount == 0) { |
| myExternalsCount = 1; |
| StatusBar.Info.set(SvnBundle.message("status.text.updated.to.revision", event.getRevision()), myVCS.getProject()); |
| } |
| } |
| else if (event.getAction() == EventAction.SKIP) { |
| myText2 = SvnBundle.message("progress.text2.skipped.file", displayPath); |
| addFileToGroup(FileGroup.SKIPPED_ID, event); |
| } |
| |
| updateProgressIndicator(); |
| } |
| |
| private void possiblySwitched(ProgressEvent event) { |
| final File file = event.getFile(); |
| if (file == null) return; |
| final SVNURL wasUrl = myUrlToCheckForSwitch.get(file); |
| if (wasUrl != null && ! wasUrl.equals(event.getURL())) { |
| myUrlToCheckForSwitch.remove(file); |
| addFileToGroup(FileGroup.SWITCHED_ID, event); |
| } |
| } |
| |
| private boolean itemSwitched(final ProgressEvent event) { |
| final File file = event.getFile(); |
| final SvnFileUrlMapping urlMapping = myVCS.getSvnFileUrlMapping(); |
| final SVNURL currentUrl = urlMapping.getUrlForFile(file); |
| return (currentUrl != null) && (! currentUrl.equals(event.getURL())); |
| } |
| |
| private void updateProgressIndicator() { |
| if (myProgressIndicator != null) { |
| if (myText != null) { |
| myProgressIndicator.setText(myText); |
| } |
| if (myText2 != null) { |
| myProgressIndicator.setText2(myText2); |
| } |
| } |
| } |
| |
| protected boolean handleInDescendants(final ProgressEvent event) { |
| return false; |
| } |
| |
| protected void addFileToGroup(final String id, final ProgressEvent event) { |
| final FileGroup fileGroup = myUpdatedFiles.getGroupById(id); |
| final String path = event.getFile().getAbsolutePath(); |
| myFilesWaitingForRevision.peek().add(Pair.create(id, path)); |
| if (event.getErrorMessage() != null) { |
| fileGroup.addError(path, event.getErrorMessage().getMessage()); |
| } |
| } |
| |
| private void setRevisionForWaitingFiles(long revisionNumber) { |
| SvnRevisionNumber revision = new SvnRevisionNumber(SVNRevision.create(revisionNumber)); |
| |
| for (Pair<String, String> pair : myFilesWaitingForRevision.pop()) { |
| FileGroup fileGroup = myUpdatedFiles.getGroupById(pair.getFirst()); |
| |
| fileGroup.add(pair.getSecond(), SvnVcs.getKey(), revision); |
| } |
| } |
| |
| public void checkCancelled() throws SVNCancelException { |
| if (myProgressIndicator != null) { |
| myProgressIndicator.checkCanceled(); |
| if (myProgressIndicator.isCanceled()) { |
| SVNErrorManager.cancel(SvnBundle.message("exception.text.update.operation.cancelled"), SVNLogType.DEFAULT); |
| } |
| } |
| } |
| |
| private static FileGroup createFileGroup(String name, String id) { |
| return new FileGroup(name, name, false, id, true); |
| } |
| |
| public void setProgressIndicator(ProgressIndicator progressIndicator) { |
| myProgressIndicator = progressIndicator; |
| } |
| } |