com.nextep.designer.vcs.ui.controllers.MergeController.java Source code

Java tutorial

Introduction

Here is the source code for com.nextep.designer.vcs.ui.controllers.MergeController.java

Source

/*******************************************************************************
 * Copyright (c) 2011 neXtep Software and contributors.
 * All rights reserved.
 *
 * This file is part of neXtep designer.
 *
 * NeXtep designer is free software: you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or any later version.
 *
 * NeXtep designer is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     neXtep Softwares - initial API and implementation
 *******************************************************************************/
/**
 *
 */
package com.nextep.designer.vcs.ui.controllers;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.hibernate.classic.Session;
import com.nextep.datadesigner.Designer;
import com.nextep.datadesigner.exception.CancelException;
import com.nextep.datadesigner.exception.ErrorException;
import com.nextep.datadesigner.gui.impl.CommandProgress;
import com.nextep.datadesigner.gui.impl.GUIWrapper;
import com.nextep.datadesigner.gui.model.InvokableController;
import com.nextep.datadesigner.hibernate.HibernateUtil;
import com.nextep.datadesigner.impl.Observable;
import com.nextep.datadesigner.model.ChangeEvent;
import com.nextep.datadesigner.model.ICommand;
import com.nextep.datadesigner.model.IEventListener;
import com.nextep.datadesigner.model.IObservable;
import com.nextep.datadesigner.vcs.gui.dialog.MergeInitGUI;
import com.nextep.datadesigner.vcs.gui.dialog.MergePreviewWizard;
import com.nextep.datadesigner.vcs.gui.dialog.MergeResultGUI;
import com.nextep.datadesigner.vcs.gui.dialog.MergeWizard;
import com.nextep.datadesigner.vcs.impl.MergeStrategy;
import com.nextep.datadesigner.vcs.impl.MergerFactory;
import com.nextep.datadesigner.vcs.services.VersionHelper;
import com.nextep.designer.core.CorePlugin;
import com.nextep.designer.core.dao.IIdentifiableDAO;
import com.nextep.designer.core.model.IConnection;
import com.nextep.designer.core.services.IConnectionService;
import com.nextep.designer.core.services.IRepositoryService;
import com.nextep.designer.vcs.VCSPlugin;
import com.nextep.designer.vcs.model.IActivity;
import com.nextep.designer.vcs.model.IComparisonItem;
import com.nextep.designer.vcs.model.IMerger;
import com.nextep.designer.vcs.model.IVersionContainer;
import com.nextep.designer.vcs.model.IVersionStatus;
import com.nextep.designer.vcs.model.IVersionable;
import com.nextep.designer.vcs.model.IWorkspace;
import com.nextep.designer.vcs.model.MergeStatus;
import com.nextep.designer.vcs.model.impl.Workspace;
import com.nextep.designer.vcs.services.IVersioningService;
import com.nextep.designer.vcs.ui.VCSUIMessages;
import com.nextep.designer.vcs.ui.VCSUIPlugin;
import com.nextep.designer.vcs.ui.services.VersionUIHelper;

/**
 * @author Christophe Fondacci
 */
public class MergeController extends InvokableController {

    private static final Log LOGGER = LogFactory.getLog(MergeController.class);

    private static MergeController instance;

    public MergeController() {
    }

    public static MergeController getInstance() {
        if (instance == null) {
            instance = new MergeController();
        }
        return instance;
    }

    /**
     * @see com.nextep.datadesigner.gui.model.InvokableController#invoke(java.lang.Object)
     */
    @Override
    public Object invoke(Object... model) {
        if (model.length == 0) {
            throw new ErrorException("The merge controller need at least 1 non-null argument to proceed.");
        } else {
            final IVersionable<?> versionable = (IVersionable<?>) model[0];
            final MergeWizard wiz = new MergeWizard(new MergeInitGUI(versionable),
                    new MergePreviewWizard(versionable));
            wiz.setToVersionable(versionable);
            WizardDialog w = new WizardDialog(
                    VCSUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), wiz);
            w.setBlockOnOpen(true);
            w.open();
            if (w.getReturnCode() != Window.CANCEL) {
                // FIXME QUICKFIX calling this static method => TO remove !!!
                MergeStrategy.setIsGenerating(false);
                // try {
                final IMerger m = MergerFactory.getMerger(wiz.getToRelease());
                // Building compare command
                ICommand compareCommand = new ICommand() {

                    @Override
                    public Object execute(Object... parameters) {
                        return m.compare(wiz.getReference(), wiz.getFromRelease(), wiz.getToRelease(), true);
                    }

                    @Override
                    public String getName() {
                        return "Computing differences between " + wiz.getToRelease().getLabel() + " and "
                                + wiz.getFromRelease().getLabel();
                    }
                };
                final IComparisonItem comp = (IComparisonItem) CommandProgress.runWithProgress(compareCommand)
                        .iterator().next();

                ProgressMonitorDialog pd = new ProgressMonitorDialog(
                        VCSUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell());
                try {
                    pd.run(true, false, new MergeProgress(comp, m));
                } catch (Exception e) {
                    throw new ErrorException(e);
                }
                // } finally {
                MergeStrategy.setIsGenerating(true);
                // }
                MergeResultGUI mergeGUI = new MergeResultGUI(true, comp);
                mergeGUI.setIsRepositoryMerge(true);
                mergeGUI.setRootText("Source release " + wiz.getFromRelease().getLabel() + " [" //$NON-NLS-2$
                        + wiz.getFromRelease().getBranch().getName() + "]", //$NON-NLS-1$
                        "Current target release " + wiz.getToRelease().getLabel() + " [" //$NON-NLS-2$
                                + wiz.getToRelease().getBranch().getName() + "]", //$NON-NLS-1$
                        "Merge result");
                boolean doItAgain = true;
                while (doItAgain) {
                    invokeGUI(new GUIWrapper(mergeGUI, "Merge results", 800, 600));
                    // Checking that everything is resolved
                    if (comp.getMergeInfo().getStatus() != MergeStatus.MERGE_RESOLVED) {
                        // IF not we ask if the user would like to edit again
                        doItAgain = MessageDialog.openQuestion(
                                VCSUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(),
                                VCSUIMessages.getString("mergeUnresolvedTitle"), //$NON-NLS-1$
                                VCSUIMessages.getString("mergeUnresolved")); //$NON-NLS-1$
                        if (!doItAgain) {
                            throw new CancelException("Merge operation aborted due to unresolved conflicts.");
                        }
                    } else {
                        doItAgain = false;
                    }
                }
                // We fall here if no cancel exception
                // HibernateUtil.getInstance().getSession().clear();
                // IdentifiableDAO.getInstance().loadAll(VersionReference.class);
                HibernateUtil.getInstance().clearAllSessions();
                pd = new ProgressMonitorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
                try {
                    pd.run(false, false, new IRunnableWithProgress() {

                        @Override
                        public void run(IProgressMonitor monitor)
                                throws InvocationTargetException, InterruptedException {
                            monitor.beginTask("Merge in progress...", 5);
                            // Global listeners switch off
                            Observable.deactivateListeners();
                            // Refreshing progress
                            monitor.worked(1);
                            IVersionable<?> mergedObject = null;
                            try {
                                // Building the merge result
                                final String activityText = "Merged " + wiz.getFromRelease().getLabel() + " -> " //$NON-NLS-2$
                                        + wiz.getFromRelease().getBranch().getName();
                                final IVersioningService versioningService = VCSPlugin
                                        .getService(IVersioningService.class);
                                final IActivity mergeActivity = versioningService.createActivity(activityText);
                                mergedObject = (IVersionable<?>) m.buildMergedObject(comp, mergeActivity);
                                // Checking if the merge did anything
                                if (mergedObject instanceof IVersionable<?>) {
                                    if (((IVersionable<?>) mergedObject).getVersion()
                                            .getStatus() == IVersionStatus.CHECKED_IN) {
                                        final boolean forceMerge = MessageDialog.openQuestion(
                                                Display.getCurrent().getActiveShell(),
                                                VCSUIMessages.getString("mergeDidNothingTitle"), //$NON-NLS-1$
                                                VCSUIMessages.getString("mergeDidNothing")); //$NON-NLS-1$
                                        if (forceMerge) {
                                            comp.getMergeInfo().setMergeProposal(null);
                                            mergedObject = (IVersionable<?>) m.buildMergedObject(comp,
                                                    mergeActivity);
                                        } else {
                                            return;
                                        }
                                    }
                                }
                            } finally {
                                Observable.activateListeners();
                            }
                            LOGGER.info("Merged successfully!");
                            // Refreshing progress
                            monitor.worked(1);
                            monitor.setTaskName("Updating view contents...");

                            // Temporary bugfix for DES-710
                            // TODO refactor EVERYTHING as a service
                            final Session session = HibernateUtil.getInstance().getSandBoxSession();

                            // FIXME draft of view removal / addition of the merged object
                            // Switching versionables (for database only)
                            IVersionContainer parent = versionable.getContainer();
                            // First removing current versionable in current session
                            parent.getContents().remove(versionable);
                            versionable.setContainer(null);

                            // Forcing save of module without parent
                            final IIdentifiableDAO identifiableDao = CorePlugin.getIdentifiableDao();
                            identifiableDao.save(parent);
                            // Refreshing progress
                            monitor.worked(1);
                            // Reloading parent container in sandbox (because merged object is in
                            // sandbox)

                            if (parent instanceof IWorkspace) {
                                parent = (IVersionContainer) identifiableDao.load(Workspace.class, parent.getUID(),
                                        session, false);
                            } else {
                                parent = (IVersionContainer) identifiableDao.load(IVersionable.class,
                                        parent.getUID(), session, false);
                            }
                            // Refreshing progress
                            monitor.worked(1);
                            monitor.setTaskName("Committing to repository...");
                            // Adding sandbox merged object
                            mergedObject.setContainer(parent);
                            parent.getContents().add(mergedObject);
                            // Saving to sandbox before reloading view

                            identifiableDao.save(mergedObject);
                            session.flush();
                            // **************
                            // DES-710 urgent workaround
                            // Hibernate does not properly send the INSERT statements to the db
                            // But logs it properly in the logs !
                            final IRepositoryService repositoryService = CorePlugin.getRepositoryService();
                            final IConnectionService connectionService = CorePlugin.getConnectionService();
                            final IConnection repositoryConnection = repositoryService.getRepositoryConnection();
                            Connection conn = null;
                            PreparedStatement stmt = null;
                            try {
                                conn = connectionService.connect(repositoryConnection);
                                String insertSql = null;
                                if (parent instanceof IWorkspace) {
                                    insertSql = "INSERT INTO rep_view_contents ( " //$NON-NLS-1$
                                            + "  view_id, version_id " //$NON-NLS-1$
                                            + ") VALUES ( " //$NON-NLS-1$
                                            + "  ?, ? " //$NON-NLS-1$
                                            + ") "; //$NON-NLS-1$
                                } else {
                                    insertSql = "INSERT INTO rep_module_contents ( " //$NON-NLS-1$
                                            + "  module_id, version_id " //$NON-NLS-1$
                                            + ") VALUES ( " //$NON-NLS-1$
                                            + "  ?, ? " //$NON-NLS-1$
                                            + ") "; //$NON-NLS-1$
                                }
                                stmt = conn.prepareStatement(insertSql);
                                stmt.setLong(1, parent.getUID().rawId());
                                stmt.setLong(2, mergedObject.getUID().rawId());
                                stmt.execute();
                                if (!conn.getAutoCommit()) {
                                    conn.commit();
                                }
                            } catch (SQLException e) {
                                LOGGER.error(e);
                            } finally {
                                if (stmt != null) {
                                    try {
                                        stmt.close();
                                    } catch (SQLException e) {
                                        LOGGER.error(e);
                                    }
                                }
                                if (conn != null) {
                                    try {
                                        conn.close();
                                    } catch (SQLException e) {
                                        LOGGER.error(e);
                                    }
                                }
                            }
                            // End of DES-710 workaround
                            // **************

                            // HibernateUtil.getInstance().reconnectAll();
                            // Merger.save(mergedObject);
                            // Merger.save(parent);
                            monitor.worked(1);
                            LOGGER.info("Please wait while view is reloading...");
                            monitor.setTaskName("Finished: Reloading view...");
                            monitor.done();
                            // END OF the fixme part

                        }

                    });
                } catch (Exception e) {
                    throw new ErrorException(e);
                }

                // Restoring a new view
                LOGGER.info("Refreshing current view");
                VersionUIHelper.changeView(VersionHelper.getCurrentView().getUID());

                // Designer.getInstance().invokeSelection("com.neXtep.designer.vcs.SelectionInvoker",
                // "version.compare", comp);
            }
        }
        return null;
    }

    private class MergeProgress implements IRunnableWithProgress, IEventListener {

        private IComparisonItem comp;
        private IMerger merger;
        private IProgressMonitor monitor;
        int count = 0;

        public MergeProgress(IComparisonItem comp, IMerger merger) {
            this.comp = comp;
            this.merger = merger;
        }

        @Override
        public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
            this.monitor = monitor;
            Designer.setProgressMonitor(monitor);
            // Counting items
            countItems(comp);
            try {
                MergerFactory.registerMergeListener(this);
                monitor.beginTask("Merge in progress", count + 1);
                merger.merge(comp, null, null);
                monitor.done();
            } finally {
                MergerFactory.unregisterMergeListener(this);
                Designer.setProgressMonitor(null);
            }
        }

        private void countItems(IComparisonItem i) {
            for (IComparisonItem subItem : i.getSubItems()) {
                countItems(subItem);
            }
            count += i.getSubItems().size();
        }

        @Override
        public void handleEvent(ChangeEvent event, IObservable source, Object data) {
            monitor.setTaskName((String) data);
            monitor.worked(1);

        }

    }

    /**
     * Checks whether everything in this comparison tree is resolved for a merge operation.
     * 
     * @param item a {@link IComparisonItem} to check
     * @return <code>true</code> if resolved, else <code>false</code>
     */
    private boolean isMergeResolved(IComparisonItem item) {
        if (item.getMergeInfo().getStatus() != MergeStatus.MERGE_RESOLVED) {
            return false;
        } else {
            for (IComparisonItem subItem : item.getSubItems()) {
                if (!isMergeResolved(subItem)) {
                    return false;
                }
            }
            return true;
        }
    }

}