org.tigris.subversion.subclipse.ui.actions.CommitAction.java Source code

Java tutorial

Introduction

Here is the source code for org.tigris.subversion.subclipse.ui.actions.CommitAction.java

Source

/*******************************************************************************
 * Copyright (c) 2003, 2006 Subclipse project and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Subclipse project committers - initial API and implementation
 ******************************************************************************/
package org.tigris.subversion.subclipse.ui.actions;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.core.subscribers.ActiveChangeSet;
import org.eclipse.team.internal.core.subscribers.ChangeSet;
import org.tigris.subversion.subclipse.core.ISVNCoreConstants;
import org.tigris.subversion.subclipse.core.ISVNLocalResource;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.core.SVNProviderPlugin;
import org.tigris.subversion.subclipse.core.commands.GetStatusCommand;
import org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
import org.tigris.subversion.subclipse.core.util.File2Resource;
import org.tigris.subversion.subclipse.core.util.Util;
import org.tigris.subversion.subclipse.ui.ISVNUIConstants;
import org.tigris.subversion.subclipse.ui.Policy;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.dialogs.CommitToTagsWarningDialog;
import org.tigris.subversion.subclipse.ui.operations.CommitOperation;
import org.tigris.subversion.subclipse.ui.settings.ProjectProperties;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizard;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardCommitPage;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardDialog;
import org.tigris.subversion.svnclientadapter.ISVNStatus;
import org.tigris.subversion.svnclientadapter.utils.SVNStatusUtils;

/**
 * Action for checking in files to a subversion provider
 * Prompts the user for a release comment, and shows a selection
 * list of added and modified resources, including unversioned resources.
 * If selected, unversioned resources will be added to version control,
 * and committed.
 */
public class CommitAction extends WorkbenchWindowAction {
    protected String commitComment;
    protected IResource[] resourcesToCommit;
    protected String url;
    protected boolean hasUnaddedResources;
    protected boolean commit;
    protected boolean keepLocks;
    protected IResource[] selectedResources;
    private String proposedComment;
    private boolean canRunAsJob = true;
    //    private boolean sharing;

    private HashMap statusMap;

    public CommitAction() {

    }

    public CommitAction(String proposedComment) {
        this.proposedComment = proposedComment;
    }

    /*
      * get non added resources and prompts for resources to be added
      * prompts for comments
      * add non added files
      * commit selected files
     * @see SVNAction#execute(IAction)
     */
    public void execute(IAction action) throws InvocationTargetException, InterruptedException {
        statusMap = new HashMap();
        final IResource[] resources = getSelectedResources();
        final List resourcesToBeAdded = new ArrayList();
        final List resourcesToBeDeleted = new ArrayList();
        if (action != null && !action.isEnabled()) {
            action.setEnabled(true);
        } else {
            run(new IRunnableWithProgress() {
                public void run(IProgressMonitor monitor) throws InvocationTargetException {
                    try {
                        // search for modified or added, non-ignored resources in the selection.
                        IResource[] modified = getChangeSetResources(getModifiedResources(resources, monitor));

                        // if no changes since last commit, do not show commit dialog.
                        if (modified.length == 0) {
                            MessageDialog.openInformation(getShell(), Policy.bind("CommitDialog.title"), //$NON-NLS-1$
                                    Policy.bind("CommitDialog.noChanges")); //$NON-NLS-1$
                            commit = false;
                        } else {
                            ProjectProperties projectProperties = ProjectProperties
                                    .getProjectProperties(modified[0]);
                            commit = confirmCommit(modified, projectProperties);
                        }

                        // if commit was not canceled, create a list of any
                        // unversioned resources that were selected and a list of any missing
                        // resources that were selected.
                        if (commit) {
                            for (int i = 0; i < resourcesToCommit.length; i++) {
                                IResource resource = resourcesToCommit[i];
                                ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
                                if (svnResource.exists() && !svnResource.isManaged())
                                    resourcesToBeAdded.add(resource);
                                if (svnResource.getStatus().isMissing())
                                    resourcesToBeDeleted.add(resource);
                            }
                        }
                    } catch (TeamException e) {
                        throw new InvocationTargetException(e);
                    }
                }
            }, true /* cancelable */, PROGRESS_BUSYCURSOR); //$NON-NLS-1$

            if (!commit) {
                return; // user canceled
            }

            CommitOperation commitOperation = new CommitOperation(getTargetPart(), resources,
                    (IResource[]) resourcesToBeAdded.toArray(new IResource[resourcesToBeAdded.size()]),
                    (IResource[]) resourcesToBeDeleted.toArray(new IResource[resourcesToBeDeleted.size()]),
                    resourcesToCommit, commitComment, keepLocks);
            commitOperation.setCanRunAsJob(canRunAsJob);
            commitOperation.run();
        }
    }

    /**
     * get the modified and unadded resources in resources parameter
     */
    protected IResource[] getModifiedResources(IResource[] resources, IProgressMonitor iProgressMonitor)
            throws SVNException {
        boolean ignoreHiddenChanges = SVNProviderPlugin.getPlugin().getPluginPreferences()
                .getBoolean(ISVNCoreConstants.PREF_IGNORE_HIDDEN_CHANGES);
        IResource[] allResources = getSelectedResources(true);
        List allSelections = new ArrayList();
        for (int i = 0; i < allResources.length; i++)
            allSelections.add(allResources[i]);
        List conflictFiles = new ArrayList();
        final List modified = new ArrayList();
        List unversionedFolders = new ArrayList();
        hasUnaddedResources = false;
        for (int i = 0; i < resources.length; i++) {
            IResource resource = resources[i];
            ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);

            // This check is for when the action is called with unmanaged resources
            if (svnResource.getRepository() == null) {
                continue;
            }

            // if only one resource selected, get url.  Commit dialog displays this.
            if (resources.length == 1) {
                url = svnResource.getStatus().getUrlString();
                if ((url == null) || (resource.getType() == IResource.FILE))
                    url = Util.getParentUrl(svnResource);
            }

            boolean descend = true;
            if (resource instanceof IContainer) {
                outer: for (int j = 0; j < allResources.length; j++) {
                    if (allResources[j] == resource)
                        continue;

                    IContainer parent = allResources[j].getParent();
                    while (parent != null) {
                        if (parent.equals(resource)) {
                            descend = false;
                            break outer;
                        }
                        parent = parent.getParent();
                    }
                }
            }
            // get adds, deletes, updates and property updates.
            GetStatusCommand command = new GetStatusCommand(svnResource, descend, false);
            command.run(iProgressMonitor);
            ISVNStatus[] statuses = command.getStatuses();
            boolean switched = false;
            for (int j = 0; j < statuses.length; j++) {
                if (SVNStatusUtils.isReadyForCommit(statuses[j]) || SVNStatusUtils.isMissing(statuses[j])) {
                    IResource currentResource = SVNWorkspaceRoot.getResourceFor(resource, statuses[j]);
                    if (currentResource != null && (descend == true || allSelections.contains(currentResource))) {

                        SVNProviderPlugin.getPlugin().getStatusCacheManager().updateCache(currentResource,
                                statuses[j]);

                        ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(currentResource);
                        if (!localResource.isIgnored()) {
                            if (!SVNStatusUtils.isManaged(statuses[j])) {
                                if (!Util.isHidden(currentResource)) {
                                    hasUnaddedResources = true;
                                    if ((currentResource.getType() != IResource.FILE)
                                            && !isSymLink(currentResource))
                                        unversionedFolders.add(currentResource);
                                    else {
                                        if (!modified.contains(currentResource)) {
                                            modified.add(currentResource);
                                            if (currentResource instanceof IContainer)
                                                statusMap.put(currentResource, statuses[j].getPropStatus());
                                            else
                                                statusMap.put(currentResource, statuses[j].getTextStatus());
                                        }
                                    }
                                }
                            } else if (!ignoreHiddenChanges || !Util.isHidden(currentResource)) {
                                if (!modified.contains(currentResource)) {

                                    if (statuses[j].isSwitched()) {
                                        switched = true;
                                        url = statuses[j].getUrlString();
                                    }

                                    modified.add(currentResource);
                                    if (currentResource instanceof IContainer)
                                        statusMap.put(currentResource, statuses[j].getPropStatus());
                                    else {
                                        statusMap.put(currentResource, statuses[j].getTextStatus());
                                        if (SVNStatusUtils.isTextConflicted(statuses[j])) {
                                            IFile conflictNewFile = (IFile) File2Resource
                                                    .getResource(statuses[j].getConflictNew());
                                            if (conflictNewFile != null)
                                                conflictFiles.add(conflictNewFile);
                                            IFile conflictOldFile = (IFile) File2Resource
                                                    .getResource(statuses[j].getConflictOld());
                                            if (conflictOldFile != null)
                                                conflictFiles.add(conflictOldFile);
                                            IFile conflictWorkingFile = (IFile) File2Resource
                                                    .getResource(statuses[j].getConflictWorking());
                                            if (conflictWorkingFile != null)
                                                conflictFiles.add(conflictWorkingFile);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (switched && modified.size() > 1) {
                url = null;
            }
        }

        IResource[] unaddedResources = getUnaddedResources(unversionedFolders, iProgressMonitor);
        for (int i = 0; i < unaddedResources.length; i++)
            if (!modified.contains(unaddedResources[i]))
                modified.add(unaddedResources[i]);
        Iterator iter = conflictFiles.iterator();
        while (iter.hasNext()) {
            IFile conflictFile = (IFile) iter.next();
            modified.remove(conflictFile);
            statusMap.remove(conflictFile);
        }
        return (IResource[]) modified.toArray(new IResource[modified.size()]);
    }

    public int getHighestProblemSeverity(IResource[] resources) {
        int mostSeriousSeverity = -1;

        for (int i = 0; i < resources.length; i++) {
            IResource resource = resources[i];
            try {
                IMarker[] problems = resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO);
                for (int j = 0; j < problems.length; j++) {
                    IMarker problem = problems[j];
                    int severity = problem.getAttribute(IMarker.SEVERITY, 0);
                    if (severity > mostSeriousSeverity) {
                        mostSeriousSeverity = severity;
                    }
                }
            } catch (CoreException e) {
            }
        }

        return mostSeriousSeverity;
    }

    /**
     * prompt commit of selected resources.
     * @throws SVNException
     */
    protected boolean confirmCommit(IResource[] modifiedResources, ProjectProperties projectProperties)
            throws SVNException {
        IPreferenceStore preferenceStore = SVNUIPlugin.getPlugin().getPreferenceStore();
        boolean commitToTagsPathWithoutWarning = preferenceStore
                .getBoolean(ISVNUIConstants.PREF_COMMIT_TO_TAGS_PATH_WITHOUT_WARNING);
        if (!commitToTagsPathWithoutWarning && onTagPath(modifiedResources)) {
            // Warning - working copy appears to be on a tag path.        
            CommitToTagsWarningDialog dialog = new CommitToTagsWarningDialog(getShell());
            if (dialog.open() != CommitToTagsWarningDialog.OK) {
                return false;
            }
        }

        int highestProblemSeverity = getHighestProblemSeverity(modifiedResources);
        switch (highestProblemSeverity) {
        case IMarker.SEVERITY_WARNING:
            String allowCommitsWithWarnings = preferenceStore
                    .getString(ISVNUIConstants.PREF_ALLOW_COMMIT_WITH_WARNINGS);
            if (MessageDialogWithToggle.PROMPT.equals(allowCommitsWithWarnings)
                    || MessageDialogWithToggle.NEVER.equals(allowCommitsWithWarnings)) {
                MessageDialogWithToggle warningDialog = MessageDialogWithToggle.openYesNoQuestion(shell,
                        Policy.bind("CommitWizard.commitResources"), Policy.bind("CommitWizard.warningMarkers"), //$NON-NLS-1$//$NON-NLS-2$
                        Policy.bind("CommitWizard.warningQuestion"), false, preferenceStore, //$NON-NLS-1$
                        ISVNUIConstants.PREF_ALLOW_COMMIT_WITH_WARNINGS);
                if (IDialogConstants.YES_ID != warningDialog.getReturnCode()) {
                    return false;
                }
            }
            break;
        case IMarker.SEVERITY_ERROR:
            String allowCommitsWithErrors = preferenceStore
                    .getString(ISVNUIConstants.PREF_ALLOW_COMMIT_WITH_ERRORS);
            if (MessageDialogWithToggle.PROMPT.equals(allowCommitsWithErrors)
                    || MessageDialogWithToggle.NEVER.equals(allowCommitsWithErrors)) {
                MessageDialogWithToggle errorDialog = MessageDialogWithToggle.openYesNoQuestion(shell,
                        Policy.bind("CommitWizard.commitResources"), Policy.bind("CommitWizard.errorMarkers"), //$NON-NLS-1$//$NON-NLS-2$
                        Policy.bind("CommitWizard.errorQuestion"), false, preferenceStore, //$NON-NLS-1$
                        ISVNUIConstants.PREF_ALLOW_COMMIT_WITH_ERRORS);
                if (IDialogConstants.YES_ID != errorDialog.getReturnCode()) {
                    return false;
                }
            }
            break;
        }

        SvnWizardCommitPage commitPage = new SvnWizardCommitPage(modifiedResources, url, projectProperties,
                statusMap, null, false);
        //      commitPage.setSharing(sharing);

        SvnWizard wizard = new SvnWizard(commitPage);
        SvnWizardDialog dialog = new SvnWizardDialog(getShell(), wizard);
        if (proposedComment == null || proposedComment.length() == 0) {
            commitPage.setComment(getProposedComment(modifiedResources));
        } else {
            commitPage.setComment(proposedComment);
        }
        wizard.setParentDialog(dialog);
        boolean commitOK = (dialog.open() == SvnWizardDialog.OK);
        url = null;
        commitComment = commitPage.getComment();
        resourcesToCommit = commitPage.getSelectedResources();
        keepLocks = commitPage.isKeepLocks();
        return commitOK;
    }

    private boolean onTagPath(IResource[] modifiedResources) throws SVNException {
        // Multiple resources selected.
        if (url == null) {
            IResource resource = modifiedResources[0];
            ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
            String firstUrl = svnResource.getStatus().getUrlString();
            if ((firstUrl == null) || (resource.getType() == IResource.FILE))
                firstUrl = Util.getParentUrl(svnResource);
            if (firstUrl.indexOf("/tags/") != -1) //$NON-NLS-1$
                return true;
        }
        // One resource selected.
        else if (url.indexOf("/tags/") != -1) //$NON-NLS-1$
            return true;
        return false;
    }

    /**
    * @see org.tigris.subversion.subclipse.ui.actions.SVNAction#getErrorTitle()
    */
    protected String getErrorTitle() {
        return Policy.bind("CommitAction.commitFailed"); //$NON-NLS-1$
    }

    /**
     * @see org.tigris.subversion.subclipse.ui.actions.WorkspaceAction#isEnabledForUnmanagedResources()
     */
    protected boolean isEnabledForUnmanagedResources() {
        return true;
    }

    protected boolean isEnabled() throws TeamException {

        // invoke the inherited method so that overlaps are maintained
        IResource[] resources = super.getSelectedResources();

        // disable if no resources are selected
        if (resources.length == 0)
            return false;

        // validate enabled for each resource in the selection
        for (int i = 0; i < resources.length; i++) {
            IResource resource = resources[i];

            // no SVN actions are enabled if the selection contains a linked resource
            if (SVNWorkspaceRoot.isLinkedResource(resource))
                return false;

            // only enable for resources in a project shared with SVN
            if (RepositoryProvider.getProvider(resource.getProject(), SVNProviderPlugin.getTypeId()) == null) {
                return false;
            }

            // ensure that resource management state matches what the action requires
            ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
            if (!isEnabledForSVNResource(svnResource)) {
                return false;
            }
        }
        return true;
    }

    /**
     * get the unadded resources in resources parameter
     */
    private IResource[] getUnaddedResources(List resources, IProgressMonitor iProgressMonitor) throws SVNException {
        final List unadded = new ArrayList();
        final SVNException[] exception = new SVNException[] { null };
        for (Iterator iter = resources.iterator(); iter.hasNext();) {
            IResource resource = (IResource) iter.next();
            if (resource.exists()) {
                // visit each resource deeply
                try {
                    resource.accept(new IResourceVisitor() {
                        public boolean visit(IResource aResource) {
                            ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(aResource);
                            // skip ignored resources and their children
                            try {
                                if (svnResource.isIgnored())
                                    return false;
                                // visit the children of shared resources
                                if (svnResource.isManaged())
                                    return true;
                                if ((aResource.getType() == IResource.FOLDER) && isSymLink(aResource)) // don't traverse into symlink folders
                                    return false;
                            } catch (SVNException e) {
                                exception[0] = e;
                            }
                            // file/folder is unshared so record it
                            unadded.add(aResource);
                            return aResource.getType() == IResource.FOLDER;
                        }
                    }, IResource.DEPTH_INFINITE, false /* include phantoms */);
                } catch (CoreException e) {
                    throw SVNException.wrapException(e);
                }
                if (exception[0] != null)
                    throw exception[0];
            }
        }
        if (unadded.size() > 0)
            hasUnaddedResources = true;
        return (IResource[]) unadded.toArray(new IResource[unadded.size()]);
    }

    protected boolean isSymLink(IResource resource) {
        File file = resource.getLocation().toFile();
        try {
            if (!file.exists())
                return true;
            else {
                String cnnpath = file.getCanonicalPath();
                String abspath = file.getAbsolutePath();
                return !abspath.equals(cnnpath);
            }
        } catch (IOException ex) {
            return true;
        }
    }

    protected IResource[] getSelectedResources() {
        if (selectedResources == null)
            return super.getSelectedResources();
        else
            return selectedResources;
    }

    public void setSelectedResources(IResource[] selectedResources) {
        this.selectedResources = selectedResources;
    }

    /*
     * Get a proposed comment by looking at the active change sets
     */
    private String getProposedComment(IResource[] resourcesToCommit) {
        StringBuffer comment = new StringBuffer();
        ChangeSet[] sets = SVNProviderPlugin.getPlugin().getChangeSetManager().getSets();
        int numMatchedSets = 0;
        for (int i = 0; i < sets.length; i++) {
            ChangeSet set = sets[i];
            if (isUserSet(set) && containsOne(set, resourcesToCommit)) {
                if (numMatchedSets > 0)
                    comment.append(System.getProperty("line.separator")); //$NON-NLS-1$
                comment.append(set.getComment());
                numMatchedSets++;
            }
        }
        return comment.toString();
    }

    private IResource[] getChangeSetResources(IResource[] allResources) {
        ActiveChangeSet changeSet = SVNProviderPlugin.getPlugin().getChangeSetManager().getDefaultSet();
        if (changeSet != null && !("<No Active Task>".equals(changeSet.getName()))) {
            List<IResource> changeSetResourceList = new ArrayList<IResource>();
            for (IResource resource : allResources) {
                if (changeSet.contains(resource)) {
                    changeSetResourceList.add(resource);
                }
            }
            IResource[] changeSetResources = new IResource[changeSetResourceList.size()];
            changeSetResourceList.toArray(changeSetResources);
            return changeSetResources;
        }
        return allResources;
    }

    private boolean isUserSet(ChangeSet set) {
        if (set instanceof ActiveChangeSet) {
            ActiveChangeSet acs = (ActiveChangeSet) set;
            return acs.isUserCreated();
        }
        return false;
    }

    private boolean containsOne(ChangeSet set, IResource[] resourcesToCommit) {
        for (int j = 0; j < resourcesToCommit.length; j++) {
            IResource resource = resourcesToCommit[j];
            if (set.contains(resource)) {
                return true;
            }
            if (set instanceof ActiveChangeSet) {
                ActiveChangeSet acs = (ActiveChangeSet) set;
                if (acs.getDiffTree().members(resource).length > 0)
                    return true;
            }
        }
        return false;
    }

    public boolean hasOutgoingChanges() {
        try {
            return getModifiedResources(selectedResources, new NullProgressMonitor()).length > 0;
        } catch (SVNException e) {
        }
        return false;
    }

    //   public void setSharing(boolean sharing) {
    //      this.sharing = sharing;
    //   }    

    protected String getImageId() {
        return ISVNUIConstants.IMG_MENU_COMMIT;
    }

    public void setCanRunAsJob(boolean canRunAsJob) {
        this.canRunAsJob = canRunAsJob;
    }

}