com.mindquarry.desktop.client.action.workspace.SynchronizeWorkspacesAction.java Source code

Java tutorial

Introduction

Here is the source code for com.mindquarry.desktop.client.action.workspace.SynchronizeWorkspacesAction.java

Source

/*
 * Copyright (C) 2006-2007 Mindquarry GmbH, All Rights Reserved
 * 
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 */
package com.mindquarry.desktop.client.action.workspace;

import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.tigris.subversion.javahl.Notify2;
import org.tigris.subversion.javahl.NotifyInformation;
import org.tigris.subversion.javahl.Status;
import org.tigris.subversion.javahl.StatusKind;

import com.mindquarry.desktop.client.I18N;
import com.mindquarry.desktop.client.MindClient;
import com.mindquarry.desktop.client.action.ActionBase;
import com.mindquarry.desktop.client.dialog.workspace.CommitDialog;
import com.mindquarry.desktop.client.widget.workspace.ChangeSet;
import com.mindquarry.desktop.client.widget.workspace.ChangeSets;
import com.mindquarry.desktop.client.widget.workspace.WorkspaceBrowserWidget;
import com.mindquarry.desktop.model.team.SVNRepo;
import com.mindquarry.desktop.model.team.Team;
import com.mindquarry.desktop.preferences.profile.Profile;
import com.mindquarry.desktop.workspace.SVNSynchronizer;

/**
 * Trigger workspace synchronization, i.e. SVN update + commit.
 * 
 * @author <a href="mailto:alexander(dot)saar(at)mindquarry(dot)com">Alexander
 *         Saar</a>
 */
public class SynchronizeWorkspacesAction extends ActionBase {
    public static final String ID = SynchronizeWorkspacesAction.class.getSimpleName();

    private WorkspaceBrowserWidget workspaceWidget;

    private static final Image IMAGE = new Image(Display.getCurrent(), SynchronizeWorkspacesAction.class
            .getResourceAsStream("/com/mindquarry/icons/" + ICON_SIZE + "/actions/synchronize-vertical.png")); //$NON-NLS-1$

    protected static final String SYNC_WORKSPACE_MESSAGE = I18N.getString("Synchronizing workspaces"); //$NON-NLS-1$
    protected static final String SYNC_WORKSPACE_NOTE = I18N
            .getString("Please do not modify, copy, or move files " + //$NON-NLS-1$
                    "in your workspace during the synchronization."); //$NON-NLS-1$

    protected static final String SYNC_WORKSPACE_NOTE2 = I18N.getString("Currently working on: "); //$NON-NLS-1$

    private static final String REFRESHING_MESSAGE = I18N.getString("Refreshing workspaces changes");

    private Thread updateThread;

    public SynchronizeWorkspacesAction(MindClient client) {
        super(client);

        setId(ID);
        setActionDefinitionId(ID);

        setText(I18N.getString("Synchronize")); //$NON-NLS-1$
        setToolTipText(I18N.getString("Synchronize workspaces with your desktop.")); //$NON-NLS-1$
        setAccelerator(SWT.CTRL + +SWT.SHIFT + 'S');
        setImageDescriptor(ImageDescriptor.createFromImage(IMAGE));
    }

    public void run() {
        updateThread = new Thread(new SyncThread(), "workspace-synchronise");
        updateThread.setDaemon(true);
        updateThread.start();
    }

    public String getGroup() {
        return ActionBase.WORKSPACE_ACTION_GROUP;
    }

    public boolean isToolbarAction() {
        return true;
    }

    public void setWorkspaceWidget(WorkspaceBrowserWidget workspaceWidget) {
        this.workspaceWidget = workspaceWidget;
    }

    public void stop() {
        if (updateThread != null && updateThread.isAlive()) {
            log.debug("Killing synchronize thread");
            // TODO: use non-deprecated way to stop threads: interrupt(); 
            updateThread.stop();
            workspaceWidget.showErrorMessage(I18N.getString("Synchronisation stopped."));
            client.stopAction(REFRESHING_MESSAGE);
            client.stopAction(SYNC_WORKSPACE_MESSAGE);
            client.enableActions(true, ActionBase.WORKSPACE_ACTION_GROUP);
            client.enableActions(false, ActionBase.STOP_ACTION_GROUP);
        }
    }

    class NotifyListener implements Notify2 {

        public void onNotify(NotifyInformation info) {
            workspaceWidget.setUpdateMessage(SYNC_WORKSPACE_NOTE2 + "\n" + info.getPath()); //$NON-NLS-1$
        }

    }

    class SyncThread implements Runnable {

        public void run() {
            boolean cancelled = false;
            client.enableActions(false, ActionBase.WORKSPACE_ACTION_GROUP);
            client.enableActions(true, ActionBase.STOP_ACTION_GROUP);

            workspaceWidget.showRefreshMessage(I18N.getString("Refreshing workspaces changes") + " ..."); //$NON-NLS-1$ //$NON-NLS-2

            client.startAction(REFRESHING_MESSAGE); //$NON-NLS-1$
            boolean refreshNeeded = workspaceWidget.refreshNeeded(true);
            client.stopAction(REFRESHING_MESSAGE); //$NON-NLS-1$

            if (refreshNeeded) {
                Display.getDefault().syncExec(new Runnable() {
                    public void run() {
                        MessageBox messageBox = new MessageBox(client.getShell(), SWT.ICON_INFORMATION | SWT.OK);
                        messageBox.setMessage(
                                I18N.getString("The list of changes is not up to date. It will be updated now. " //$NON-NLS-1$
                                        + "\n" //$NON-NLS-1$
                                        + "Please check the list of changes and press synchronize again.")); //$NON-NLS-1$
                        messageBox.open();
                    }
                });
                workspaceWidget.showEmptyMessage(workspaceWidget.isRefreshListEmpty());
            } else {
                workspaceWidget.showRefreshMessage(SYNC_WORKSPACE_MESSAGE + " ...\n" + SYNC_WORKSPACE_NOTE); //$NON-NLS-1$
                client.startAction(SYNC_WORKSPACE_MESSAGE);

                // retrieve selected profile
                PreferenceStore store = client.getPreferenceStore();
                Profile selected = Profile.getSelectedProfile(store);
                if (selected == null) {
                    log.debug("No profile selected."); //$NON-NLS-1$
                    return;
                }

                // retrieve selected teams
                final List<Team> teams = new ArrayList<Team>();
                Display.getDefault().syncExec(new Runnable() {
                    public void run() {
                        teams.addAll(client.getSelectedTeams());
                    }
                });
                try {
                    // ask the user for the message(s) and remember it, this
                    // way he can let the sync run in the background:
                    final Map<Team, String> commitMessages = new HashMap<Team, String>();
                    ChangeSets changeSets = workspaceWidget.getChangeSets();
                    for (final ChangeSet changeSet : changeSets.getList()) {
                        // TODO: clean up, this is only needed because the list
                        // returned by getLocalChanges() does actually also contain
                        // remote changes:
                        if (!hasLocalChanges(changeSet)) {
                            continue;
                        }
                        Display.getDefault().syncExec(new Runnable() {
                            public void run() {
                                CommitDialog dlg = new CommitDialog(client.getShell(),
                                        changeSet.getTeam().getName(), workspaceWidget.getChangeTree(),
                                        workspaceWidget.getWorkspaceRoot());
                                int result = dlg.open();
                                if (result == IDialogConstants.OK_ID) {
                                    commitMessages.put(changeSet.getTeam(), dlg.getCommitMessage());
                                } else {
                                    throw new SynchronizeCancelException();
                                }
                            }
                        });
                    }
                    // now actually update and commit for each team:
                    for (Team team : teams) {
                        long startTime = System.currentTimeMillis();
                        SVNSynchronizer sc = team.createSynchronizer(selected,
                                new InteractiveConflictHandler(client.getShell()));
                        sc.setNotifyListener(new NotifyListener());
                        sc.setCommitMessageHandler(new CommitMessageHandler(commitMessages.get(team)));
                        sc.synchronizeOrCheckout();
                        log.debug("synchronizeOrCheckout for team '" + team + "' took "
                                + (System.currentTimeMillis() - startTime) + "ms (incl. user interaction if any)");
                    }
                } catch (SynchronizeCancelException e) {
                    // TODO: rename SynchronizeCancelException to UserCancelledException and inherit from
                    // CancelException to make a difference between user cancelled and synch aborted due
                    // to some other error
                    log.info("synchronization cancelled (1)"); //$NON-NLS-1$
                    cancelled = true;
                } catch (final Exception e) {
                    if (e.getCause() != null && e.getCause().getCause() != null
                            && e.getCause().getCause().getClass() == SynchronizeCancelException.class) {
                        // cancel clicked in commit message dialog , don't show error:
                        log.info("synchronization cancelled (2)"); //$NON-NLS-1$
                        cancelled = true;
                    } else if (e.getCause() != null
                            && e.getCause().getClass() == SynchronizeCancelException.class) {
                        // cancel clicked in content conflict dialog, don't show error:
                        log.info("synchronization cancelled (3)"); //$NON-NLS-1$
                        cancelled = true;
                    } else {
                        log.error(e.toString(), e);
                        cancelled = true;
                        Display.getDefault().syncExec(new Runnable() {
                            public void run() {
                                MessageBox messageBox = new MessageBox(client.getShell(), SWT.ICON_ERROR | SWT.OK);
                                messageBox.setMessage(I18N.getString("An error occured during synchronization: ") + //$NON-NLS-1$
                                e.getMessage());
                                messageBox.open();
                            }
                        });
                    }
                }
                client.stopAction(SYNC_WORKSPACE_MESSAGE);

                if (cancelled) {
                    // show list of file changes
                    workspaceWidget.showEmptyMessage(false);
                } else {
                    // show "sucessfully synchronized"
                    workspaceWidget.showEmptyMessage(I18N.get("Synchronized successfully at {0}.",
                            DateFormat.getTimeInstance().format(new Date()))); //$NON-NLS-1$
                }
            }
            client.enableActions(true, ActionBase.WORKSPACE_ACTION_GROUP);
            client.enableActions(false, ActionBase.STOP_ACTION_GROUP);
        }

        private boolean hasLocalChanges(ChangeSet changeSet) {
            for (File file : changeSet.getChanges().keySet()) {
                Status status = changeSet.getChanges().get(file).getStatus();
                // TODO: better check for local changes
                // Note: At this point, when dirs/files are added, they are
                // 'unversioned', but they will be added later (which is a local
                // change).
                if (status.getTextStatus() != StatusKind.none && status.getTextStatus() != StatusKind.normal) {
                    return true;
                }
            }
            return false;
        }
    }
}