org.jetbrains.idea.svn.update.AbstractSvnUpdateIntegrateEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.idea.svn.update.AbstractSvnUpdateIntegrateEnvironment.java

Source

/*
 * 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 org.jetbrains.idea.svn.update;

import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.update.*;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.util.WaitForProgressToShow;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.actions.SvnMergeProvider;

import java.io.File;
import java.util.*;

public abstract class AbstractSvnUpdateIntegrateEnvironment implements UpdateEnvironment {
    protected final SvnVcs myVcs;
    private final ProjectLevelVcsManager myVcsManager;
    @NonNls
    public static final String REPLACED_ID = "replaced";
    @NonNls
    public static final String EXTERNAL_ID = "external";

    protected AbstractSvnUpdateIntegrateEnvironment(final SvnVcs vcs) {
        myVcs = vcs;
        myVcsManager = ProjectLevelVcsManager.getInstance(vcs.getProject());
    }

    public void fillGroups(UpdatedFiles updatedFiles) {
        updatedFiles
                .registerGroup(new FileGroup(VcsBundle.message("update.group.name.merged.with.property.conflicts"),
                        VcsBundle.message("status.group.name.will.be.merged.with.property.conflicts"), false,
                        FileGroup.MERGED_WITH_PROPERTY_CONFLICT_ID, false));
        updatedFiles.registerGroup(new FileGroup(VcsBundle.message("update.group.name.merged.with.tree.conflicts"),
                VcsBundle.message("status.group.name.will.be.merged.with.tree.conflicts"), false,
                FileGroup.MERGED_WITH_TREE_CONFLICT, false));
    }

    @NotNull
    public UpdateSession updateDirectories(@NotNull final FilePath[] contentRoots, final UpdatedFiles updatedFiles,
            final ProgressIndicator progressIndicator, @NotNull final Ref<SequentialUpdatesContext> context)
            throws ProcessCanceledException {

        if (context.isNull()) {
            context.set(new SvnUpdateContext(myVcs, contentRoots));
        }

        final ArrayList<VcsException> exceptions = new ArrayList<VcsException>();
        UpdateEventHandler eventHandler = new UpdateEventHandler(myVcs, progressIndicator,
                (SvnUpdateContext) context.get());
        eventHandler.setUpdatedFiles(updatedFiles);

        boolean totalUpdate = true;
        AbstractUpdateIntegrateCrawler crawler = createCrawler(eventHandler, totalUpdate, exceptions, updatedFiles);

        Collection<VirtualFile> updatedRoots = new HashSet<VirtualFile>();
        Arrays.sort(contentRoots, new Comparator<FilePath>() {
            public int compare(FilePath o1, FilePath o2) {
                return SystemInfo.isFileSystemCaseSensitive
                        ? o1.getPath().replace("/", "\\").compareTo(o2.getPath().replace("/", "\\"))
                        : o1.getPath().replace("/", "\\").compareToIgnoreCase(o2.getPath().replace("/", "\\"));
            }
        });
        for (FilePath contentRoot : contentRoots) {
            if (progressIndicator != null) {
                progressIndicator.checkCanceled();
            }
            final File ioRoot = contentRoot.getIOFile();
            if (!((SvnUpdateContext) context.get()).shouldRunFor(ioRoot))
                continue;

            Collection<VirtualFile> roots = SvnUtil.crawlWCRoots(myVcs.getProject(), ioRoot, crawler,
                    progressIndicator);
            updatedRoots.addAll(roots);
        }
        if (updatedRoots.isEmpty()) {
            WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() {
                public void run() {
                    Messages.showErrorDialog(myVcs.getProject(),
                            SvnBundle.message("message.text.update.no.directories.found"),
                            SvnBundle.message("messate.text.update.error"));
                }
            }, null, myVcs.getProject());
            return new UpdateSessionAdapter(Collections.<VcsException>emptyList(), true);
        }

        return new MyUpdateSessionAdapter(contentRoots, updatedFiles, exceptions);
    }

    private class MyUpdateSessionAdapter extends UpdateSessionAdapter {
        private final FilePath[] myContentRoots;
        private final UpdatedFiles myUpdatedFiles;
        private final VcsDirtyScopeManager myDirtyScopeManager;
        private final List<Runnable> myGroupWorkers;

        private MyUpdateSessionAdapter(@NotNull final FilePath[] contentRoots, final UpdatedFiles updatedFiles,
                final List<VcsException> exceptions) {
            super(exceptions, false);
            myContentRoots = contentRoots;
            myUpdatedFiles = updatedFiles;
            myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myVcs.getProject());

            if (!isDryRun()) {
                myGroupWorkers = Arrays.asList(new MyTextConflictWorker(),
                        new MyConflictWorker(FileGroup.MERGED_WITH_PROPERTY_CONFLICT_ID) {
                            protected List<VirtualFile> merge() {
                                return null;
                            }
                        }, new MyTreeConflictWorker(), new MyReplacedWorker());
            } else {
                myGroupWorkers = Collections.emptyList();
            }
        }

        // update switched/ignored status of directories
        private void dirtyRoots() {
            final Collection<VirtualFile> vfColl = new ArrayList<VirtualFile>(myContentRoots.length);
            for (FilePath contentRoot : myContentRoots) {
                final VirtualFile vf = contentRoot.getVirtualFile();
                if (vf != null) {
                    vfColl.add(vf);
                }
            }
            myDirtyScopeManager.filesDirty(vfColl, null);
        }

        public void onRefreshFilesCompleted() {
            dirtyRoots();

            for (Runnable groupWorker : myGroupWorkers) {
                groupWorker.run();
            }
        }

        // not a conflict worker; to correctly show replaced items
        private class MyReplacedWorker implements Runnable {
            public void run() {
                final FileGroup replacedGroup = myUpdatedFiles.getGroupById(REPLACED_ID);
                final FileGroup deletedGroup = myUpdatedFiles.getGroupById(FileGroup.REMOVED_FROM_REPOSITORY_ID);
                if ((deletedGroup != null) && (replacedGroup != null) && (!deletedGroup.isEmpty())
                        && (!replacedGroup.isEmpty())) {
                    final Set<String> replacedFiles = new HashSet<String>(replacedGroup.getFiles());
                    final Collection<String> deletedFiles = new HashSet<String>(deletedGroup.getFiles());

                    for (String deletedFile : deletedFiles) {
                        if (replacedFiles.contains(deletedFile)) {
                            deletedGroup.remove(deletedFile);
                        }
                    }
                }
            }
        }

        // at the moment no resolve, only refresh files & statuses
        private class MyTreeConflictWorker implements Runnable {
            public void run() {
                final LocalFileSystem lfs = LocalFileSystem.getInstance();
                final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(FileGroup.MERGED_WITH_TREE_CONFLICT);
                final Collection<String> conflictedFiles = conflictedGroup.getFiles();
                final Collection<VirtualFile> parents = new ArrayList<VirtualFile>();

                if ((conflictedFiles != null) && (!conflictedFiles.isEmpty())) {
                    for (final String conflictedFile : conflictedFiles) {
                        final File file = new File(conflictedFile);
                        final VirtualFile vfFile = lfs.refreshAndFindFileByIoFile(file);
                        if (vfFile != null) {
                            parents.add(vfFile);
                            continue;
                        }
                        final File parent = file.getParentFile();

                        VirtualFile vf = lfs.findFileByIoFile(parent);
                        if (vf == null) {
                            vf = lfs.refreshAndFindFileByIoFile(parent);
                        }
                        if (vf != null) {
                            parents.add(vf);
                        }
                    }
                }

                if (!parents.isEmpty()) {
                    RefreshQueue.getInstance().refresh(true, true, new Runnable() {
                        public void run() {
                            myDirtyScopeManager.filesDirty(null, parents);
                        }
                    }, parents);
                }
            }
        }

        private class MyTextConflictWorker extends MyConflictWorker {
            private MyTextConflictWorker() {
                super(FileGroup.MERGED_WITH_CONFLICT_ID);
            }

            protected List<VirtualFile> merge() {
                final List<VirtualFile> writable = prepareWritable(myFiles);
                final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(myVcs.getProject());
                return vcsHelper.showMergeDialog(writable, new SvnMergeProvider(myVcs.getProject()));
            }
        }

        private abstract class MyConflictWorker implements Runnable {
            private final String groupId;
            protected final List<VirtualFile> myFiles;
            private final LocalFileSystem myLfs;
            private final ProjectLevelVcsManager myPlVcsManager;

            protected MyConflictWorker(final String groupId) {
                this.groupId = groupId;
                myFiles = new ArrayList<VirtualFile>();
                myLfs = LocalFileSystem.getInstance();
                myPlVcsManager = ProjectLevelVcsManager.getInstance(myVcs.getProject());
            }

            // for reuse
            protected List<VirtualFile> prepareWritable(final Collection<VirtualFile> files) {
                final List<VirtualFile> writable = new ArrayList<VirtualFile>();
                for (VirtualFile vf : files) {
                    if (myVcs.equals(myPlVcsManager.getVcsFor(vf))) {
                        writable.add(vf);
                    }
                }
                final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler
                        .getInstance(myVcs.getProject()).ensureFilesWritable(writable);
                writable.removeAll(Arrays.asList(operationStatus.getReadonlyFiles()));

                return writable;
            }

            @Nullable
            protected abstract List<VirtualFile> merge();

            public void run() {
                fillAndRefreshFiles();
                if (!myFiles.isEmpty()) {
                    final List<VirtualFile> merged = merge();
                    if (merged != null && (!merged.isEmpty())) {
                        moveToMergedGroup(merged);

                        // do we need this
                        myDirtyScopeManager.filesDirty(merged, null);
                    }
                }
            }

            protected void moveToMergedGroup(final List<VirtualFile> merged) {
                final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(groupId);
                FileGroup mergedGroup = myUpdatedFiles.getGroupById(FileGroup.MERGED_ID);
                for (VirtualFile mergedFile : merged) {
                    final String path = FileUtil.toSystemDependentName(mergedFile.getPresentableUrl());
                    final VcsRevisionNumber revision = conflictedGroup.getRevision(myVcsManager, path);
                    conflictedGroup.remove(path);
                    mergedGroup.add(path, myVcs.getKeyInstanceMethod(), revision);
                }
            }

            protected void fillAndRefreshFiles() {
                final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(groupId);
                final Collection<String> conflictedFiles = conflictedGroup.getFiles();
                final Collection<VirtualFile> parents = new ArrayList<VirtualFile>();

                if ((conflictedFiles != null) && (!conflictedFiles.isEmpty())) {
                    for (final String conflictedFile : conflictedFiles) {
                        final File file = new File(conflictedFile);
                        VirtualFile vf = myLfs.findFileByIoFile(file);
                        if (vf == null) {
                            vf = myLfs.refreshAndFindFileByIoFile(file);
                        }
                        if (vf != null) {
                            myFiles.add(vf);
                            final VirtualFile parent = vf.getParent();
                            if (parent != null) {
                                parents.add(parent);
                            }
                        }
                    }
                }

                if (!myFiles.isEmpty()) {
                    RefreshQueue.getInstance().refresh(true, true, null, parents);
                    myDirtyScopeManager.filesDirty(myFiles, null);
                }
            }
        }
    }

    protected boolean isDryRun() {
        return false;
    }

    protected abstract AbstractUpdateIntegrateCrawler createCrawler(UpdateEventHandler eventHandler,
            boolean totalUpdate, ArrayList<VcsException> exceptions, UpdatedFiles updatedFiles);

    @Nullable
    public abstract Configurable createConfigurable(Collection<FilePath> collection);
}