org.eclipse.egit.ui.internal.CompareUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.ui.internal.CompareUtils.java

Source

/*******************************************************************************
 * Copyright (c) 2010, SAP AG
 *
 * 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:
 *    Dariusz Luksza - add getFileCachedRevisionTypedElement(String, Repository)
 *    Stefan Lay (SAP AG) - initial implementation
 *******************************************************************************/
package org.eclipse.egit.ui.internal;

import java.io.IOException;

import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.CompareUI;
import org.eclipse.compare.ITypedElement;
import org.eclipse.core.resources.IEncodedStorage;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.internal.storage.GitFileRevision;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIText;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;

/**
 * A collection of helper methods useful for comparing content
 */
public class CompareUtils {
    /**
     * A copy of the non-accessible preference constant
     * IPreferenceIds.REUSE_OPEN_COMPARE_EDITOR from the team ui plug in
     */
    private static final String REUSE_COMPARE_EDITOR_PREFID = "org.eclipse.team.ui.reuse_open_compare_editors"; //$NON-NLS-1$

    /** The team ui plugin ID which is not accessible */
    private static final String TEAM_UI_PLUGIN = "org.eclipse.team.ui"; //$NON-NLS-1$

    /**
     *
     * @param gitPath
     *            path within the commit's tree of the file.
     * @param commit
     *            the commit the blob was identified to be within.
     * @param db
     *            the repository this commit was loaded out of.
     * @return an instance of {@link ITypedElement} which can be used in
     *         {@link CompareEditorInput}
     */
    public static ITypedElement getFileRevisionTypedElement(final String gitPath, final RevCommit commit,
            final Repository db) {
        return getFileRevisionTypedElement(gitPath, commit, db, null);
    }

    /**
     * @param gitPath
     *            path within the commit's tree of the file.
     * @param commit
     *            the commit the blob was identified to be within.
     * @param db
     *            the repository this commit was loaded out of, and that this
     *            file's blob should also be reachable through.
     * @param blobId
     *            unique name of the content.
     * @return an instance of {@link ITypedElement} which can be used in
     *         {@link CompareEditorInput}
     */
    public static ITypedElement getFileRevisionTypedElement(final String gitPath, final RevCommit commit,
            final Repository db, ObjectId blobId) {
        ITypedElement right = new GitCompareFileRevisionEditorInput.EmptyTypedElement(
                NLS.bind(UIText.GitHistoryPage_FileNotInCommit, getName(gitPath), commit));

        try {
            IFileRevision nextFile = getFileRevision(gitPath, commit, db, blobId);
            if (nextFile != null)
                right = new FileRevisionTypedElement(nextFile);
        } catch (IOException e) {
            Activator.error(NLS.bind(UIText.GitHistoryPage_errorLookingUpPath, gitPath, commit.getId()), e);
        }
        return right;
    }

    private static String getName(String gitPath) {
        final int last = gitPath.lastIndexOf('/');
        return last >= 0 ? gitPath.substring(last + 1) : gitPath;
    }

    /**
     *
     * @param gitPath
     *            path within the commit's tree of the file.
     * @param commit
     *            the commit the blob was identified to be within.
     * @param db
     *            the repository this commit was loaded out of, and that this
     *            file's blob should also be reachable through.
     * @param blobId
     *            unique name of the content.
     * @return an instance of {@link IFileRevision} or null if the file is not
     *         contained in {@code commit}
     * @throws IOException
     */
    public static IFileRevision getFileRevision(final String gitPath, final RevCommit commit, final Repository db,
            ObjectId blobId) throws IOException {

        TreeWalk w = TreeWalk.forPath(db, gitPath, commit.getTree());
        // check if file is contained in commit
        if (w != null) {
            final IFileRevision fileRevision = GitFileRevision.inCommit(db, commit, gitPath, blobId);
            return fileRevision;
        }
        return null;
    }

    /**
     * @param element
     * @param adapterType
     * @return the adapted element, or null
     */
    public static Object getAdapter(Object element, Class adapterType) {
        return getAdapter(element, adapterType, false);
    }

    /**
     * @param ci
     * @return a truncated revision identifier if it is long
     */
    public static String truncatedRevision(String ci) {
        if (ci.length() > 10)
            return ci.substring(0, 7) + "..."; //$NON-NLS-1$
        else
            return ci;
    }

    /**
     * Determine the encoding used by Eclipse for the resource which belongs to
     * repoPath in the eclipse workspace or null if no resource is found
     *
     * @param db
     *            the repository
     * @param repoPath
     *            the path in the git repository
     * @return the encoding used in eclipse for the resource or null if
     *
     */
    public static String getResourceEncoding(Repository db, String repoPath) {
        if (db.isBare())
            return null;
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IWorkspaceRoot root = workspace.getRoot();
        IPath absolutePath = new Path(db.getWorkTree().getAbsolutePath()).append(repoPath);
        IResource resource = root.getFileForLocation(absolutePath);
        if (resource == null)
            return null;

        return getResourceEncoding(resource);
    }

    /**
     * Determine the encoding used by eclipse for the resource.
     *
     * @param resource
     *            must be an instance of IEncodedStorage
     * @return the encoding used in Eclipse for the resource if found or null
     */
    public static String getResourceEncoding(IResource resource) {
        // Get the encoding for the current version. As a matter of
        // principle one might want to use the eclipse settings for the
        // version we are retrieving as that may be defined by the
        // project settings, but there is no historic API for this.
        String charset;
        IEncodedStorage encodedStorage = ((IEncodedStorage) resource);
        try {
            charset = encodedStorage.getCharset();
            if (charset == null)
                charset = resource.getParent().getDefaultCharset();
        } catch (CoreException e) {
            charset = Constants.CHARACTER_ENCODING;
        }
        return charset;
    }

    /**
     * @param element
     * @param adapterType
     * @param load
     * @return the adapted element, or null
     */
    private static Object getAdapter(Object element, Class adapterType, boolean load) {
        if (adapterType.isInstance(element))
            return element;
        if (element instanceof IAdaptable) {
            Object adapted = ((IAdaptable) element).getAdapter(adapterType);
            if (adapterType.isInstance(adapted))
                return adapted;
        }
        if (load) {
            Object adapted = Platform.getAdapterManager().loadAdapter(element, adapterType.getName());
            if (adapterType.isInstance(adapted))
                return adapted;
        } else {
            Object adapted = Platform.getAdapterManager().getAdapter(element, adapterType);
            if (adapterType.isInstance(adapted))
                return adapted;
        }
        return null;
    }

    /**
     * @param workBenchPage
     * @param input
     */
    public static void openInCompare(IWorkbenchPage workBenchPage, CompareEditorInput input) {
        IEditorPart editor = findReusableCompareEditor(input, workBenchPage);
        if (editor != null) {
            IEditorInput otherInput = editor.getEditorInput();
            if (otherInput.equals(input)) {
                // simply provide focus to editor
                if (OpenStrategy.activateOnOpen())
                    workBenchPage.activate(editor);
                else
                    workBenchPage.bringToTop(editor);
            } else {
                // if editor is currently not open on that input either re-use
                // existing
                CompareUI.reuseCompareEditor(input, (IReusableEditor) editor);
                if (OpenStrategy.activateOnOpen())
                    workBenchPage.activate(editor);
                else
                    workBenchPage.bringToTop(editor);
            }
        } else {
            CompareUI.openCompareEditor(input);
        }
    }

    private static IEditorPart findReusableCompareEditor(CompareEditorInput input, IWorkbenchPage page) {
        IEditorReference[] editorRefs = page.getEditorReferences();
        // first loop looking for an editor with the same input
        for (int i = 0; i < editorRefs.length; i++) {
            IEditorPart part = editorRefs[i].getEditor(false);
            if (part != null && (part.getEditorInput() instanceof GitCompareFileRevisionEditorInput)
                    && part instanceof IReusableEditor && part.getEditorInput().equals(input)) {
                return part;
            }
        }
        // if none found and "Reuse open compare editors" preference is on use
        // a non-dirty editor
        if (isReuseOpenEditor()) {
            for (int i = 0; i < editorRefs.length; i++) {
                IEditorPart part = editorRefs[i].getEditor(false);
                if (part != null && (part.getEditorInput() instanceof SaveableCompareEditorInput)
                        && part instanceof IReusableEditor && !part.isDirty()) {
                    return part;
                }
            }
        }
        // no re-usable editor found
        return null;
    }

    /**
     * Action to toggle the team 'reuse compare editor' preference
     */
    public static class ReuseCompareEditorAction extends Action
            implements IPreferenceChangeListener, IWorkbenchAction {
        IEclipsePreferences node = new InstanceScope().getNode(TEAM_UI_PLUGIN);

        /**
         * Default constructor
         */
        public ReuseCompareEditorAction() {
            node.addPreferenceChangeListener(this);
            setText(UIText.GitHistoryPage_ReuseCompareEditorMenuLabel);
            setChecked(CompareUtils.isReuseOpenEditor());
        }

        public void run() {
            CompareUtils.setReuseOpenEditor(isChecked());
        }

        public void dispose() {
            // stop listening
            node.removePreferenceChangeListener(this);
        }

        public void preferenceChange(PreferenceChangeEvent event) {
            setChecked(isReuseOpenEditor());

        }
    }

    private static boolean isReuseOpenEditor() {
        boolean defaultReuse = new DefaultScope().getNode(TEAM_UI_PLUGIN).getBoolean(REUSE_COMPARE_EDITOR_PREFID,
                false);
        return new InstanceScope().getNode(TEAM_UI_PLUGIN).getBoolean(REUSE_COMPARE_EDITOR_PREFID, defaultReuse);
    }

    private static void setReuseOpenEditor(boolean value) {
        new InstanceScope().getNode(TEAM_UI_PLUGIN).putBoolean(REUSE_COMPARE_EDITOR_PREFID, value);
    }

    /**
     * Creates {@link ITypedElement} of file that was cached
     *
     * @param gitPath
     * @param db
     * @return {@link ITypedElement} instance for given cached file or
     *         {@code null} if file isn't cached
     */
    public static ITypedElement getFileCachedRevisionTypedElement(String gitPath, Repository db) {
        try {
            DirCache dc = db.lockDirCache();
            DirCacheEntry entry = dc.getEntry(gitPath);
            dc.unlock();

            // check if file is staged
            if (entry != null) {
                return new FileRevisionTypedElement(GitFileRevision.inIndex(db, gitPath));
            }
        } catch (IOException e) {
            Activator.error(NLS.bind(UIText.GitHistoryPage_errorLookingUpPath, gitPath), e);
        }

        return new GitCompareFileRevisionEditorInput.EmptyTypedElement(NLS.bind(
                UIText.CompareWithIndexAction_FileNotInIndex, gitPath.substring(gitPath.lastIndexOf("/")) + 1)); //$NON-NLS-1$
    }

}