com.genuitec.org.eclipse.egit.ui.internal.branch.BranchProjectTracker.java Source code

Java tutorial

Introduction

Here is the source code for com.genuitec.org.eclipse.egit.ui.internal.branch.BranchProjectTracker.java

Source

/**
 *  Copyright (c) 2015 Genuitec LLC.
 *  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:
 *  Piotr Tomiak <piotr@genuitec.com> - initial API and implementation
 */
package com.genuitec.org.eclipse.egit.ui.internal.branch;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.egit.core.GitProvider;
import org.eclipse.egit.core.internal.util.ProjectUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.clone.ProjectRecord;
import org.eclipse.egit.ui.internal.clone.ProjectUtils;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;

/**
 * Class to track which projects are imported for each branch.
 * <p>
 * A unique preference is set for each repository/branch combination that is
 * persisted that includes information to be able to restore projects when the
 * branch is later checked out.
 *
 * <p>
 * The workflow is as follows:
 * <p>
 * 1. Call {@link #snapshot()} to get the current projects for the currently
 * checked out branch
 * <p>
 * 2. Call {@link #save(IMemento)} after the new branch has been successfully
 * checked out with the memento returned from step 1
 * <p>
 * 3. Call {@link #restore(IProgressMonitor)} to restore the projects for the
 * newly checked out branch
 *
 */
class BranchProjectTracker {

    private static final String PREFIX = "BranchProjectTracker_"; //$NON-NLS-1$

    private static final String KEY_PROJECTS = "projects"; //$NON-NLS-1$

    private static final String KEY_PROJECT = "project"; //$NON-NLS-1$

    private static final String KEY_BRANCH = "branch"; //$NON-NLS-1$

    private static final String REPO_ROOT = "/"; //$NON-NLS-1$

    private final Repository repository;

    /**
     * Create tracker for repository
     *
     * @param repository
     */
    public BranchProjectTracker(final Repository repository) {
        this.repository = repository;
    }

    private String getBranch() {
        try {
            return repository.getBranch();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Get preference key for branch. This will be unique to the repository and
     * branch.
     *
     * @param branch
     * @return key
     */
    public String getPreference(final String branch) {
        if (branch == null)
            throw new IllegalArgumentException("Branch cannot be null"); //$NON-NLS-1$
        if (branch.length() == 0)
            throw new IllegalArgumentException("Branch cannot be empty"); //$NON-NLS-1$

        return PREFIX + '_' + repository.getDirectory().getAbsolutePath() + '_' + branch;
    }

    /**
     * Snapshot the projects currently associated with the repository
     * <p>
     * The memento returned can be later passed to {@link #save(IMemento)} to
     * persist it
     *
     * @see #save(IMemento)
     * @return memento, will be null on failures
     */
    public IMemento snapshot() {
        String branch = getBranch();
        if (branch == null)
            return null;

        IProject[] projects;
        try {
            projects = ProjectUtil.getValidOpenProjects(repository);
        } catch (CoreException e) {
            return null;
        }
        XMLMemento memento = XMLMemento.createWriteRoot(KEY_PROJECTS);
        memento.putString(KEY_BRANCH, branch);
        final String workDir = repository.getWorkTree().getAbsolutePath();
        for (IProject project : projects) {
            IPath path = project.getLocation();
            if (path == null)
                continue;
            // Only remember mapped projects
            if (!(RepositoryProvider.getProvider(project) instanceof GitProvider))
                continue;
            String fullPath = path.toOSString();
            if (fullPath.startsWith(workDir)) {
                String relative = fullPath.substring(workDir.length());
                if (relative.length() == 0)
                    relative = REPO_ROOT;
                IMemento child = memento.createChild(KEY_PROJECT);
                child.putTextData(relative);
            }
        }
        return memento;
    }

    /**
     * Associate projects with branch. The specified memento must the one
     * previously returned from a call to {@link #snapshot()}.
     *
     * @see #snapshot()
     * @param memento
     * @return this tracker
     */
    public BranchProjectTracker save(final IMemento memento) {
        if (!(memento instanceof XMLMemento))
            throw new IllegalArgumentException("Invalid memento"); //$NON-NLS-1$

        String branch = memento.getString(KEY_BRANCH);
        IPreferenceStore store = Activator.getDefault().getPreferenceStore();
        String pref = getPreference(branch);
        StringWriter writer = new StringWriter();
        try {
            ((XMLMemento) memento).save(writer);
            store.setValue(pref, writer.toString());
        } catch (IOException e) {
            Activator.logError("Error writing branch-project associations", e); //$NON-NLS-1$
        }
        return this;
    }

    /**
     * Load the project paths associated with the currently checked out branch.
     * These paths will be relative to the repository root.
     *
     * @return non-null but possibly empty array of projects
     */
    public String[] getProjectPaths() {
        String branch = getBranch();
        if (branch == null)
            return new String[0];
        return getProjectPaths(branch);
    }

    /**
     * Load the project paths associated with the given branch. These paths will
     * be relative to the repository root.
     *
     * @param branch
     * @return non-null but possibly empty array of projects
     */
    public String[] getProjectPaths(final String branch) {
        String pref = getPreference(branch);
        String value = Activator.getDefault().getPreferenceStore().getString(pref);
        if (value.length() == 0)
            return new String[0];
        XMLMemento memento;
        try {
            memento = XMLMemento.createReadRoot(new StringReader(value));
        } catch (WorkbenchException e) {
            Activator.logError("Error reading branch-project associations", e); //$NON-NLS-1$
            return new String[0];
        }
        IMemento[] children = memento.getChildren(KEY_PROJECT);
        if (children.length == 0)
            return new String[0];
        List<String> projects = new ArrayList<String>(children.length);
        for (int i = 0; i < children.length; i++) {
            String path = children[i].getTextData();
            if (path != null && path.length() > 0)
                projects.add(path);
        }
        return projects.toArray(new String[projects.size()]);
    }

    /**
     * Restore projects associated with the currently checked out branch to the
     * workspace
     *
     * @param monitor
     */
    public void restore(final IProgressMonitor monitor) {
        String branch = getBranch();
        if (branch != null)
            restore(branch, monitor);
    }

    /**
     * Restore projects associated with the given branch to the workspace
     *
     * @param branch
     * @param monitor
     */
    public void restore(final String branch, final IProgressMonitor monitor) {
        String[] paths = getProjectPaths(branch);
        if (paths.length == 0)
            return;

        Set<ProjectRecord> records = new LinkedHashSet<ProjectRecord>();
        File parent = repository.getWorkTree();
        for (String path : paths) {
            File root;
            if (!REPO_ROOT.equals(path))
                root = new File(parent, path);
            else
                root = parent;

            if (!root.isDirectory())
                continue;
            File projectDescription = new File(root, IProjectDescription.DESCRIPTION_FILE_NAME);
            if (!projectDescription.isFile())
                continue;
            records.add(new ProjectRecord(projectDescription));
        }
        if (records.isEmpty())
            return;
        IProgressMonitor importMonitor;
        if (monitor != null)
            importMonitor = monitor;
        else
            importMonitor = new NullProgressMonitor();
        try {
            ProjectUtils.createProjects(records, true, null, importMonitor);
        } catch (InvocationTargetException e) {
            Activator.logError("Error restoring branch-project associations", e); //$NON-NLS-1$
        } catch (InterruptedException ignored) {
            // Ignored
        }
    }
}