org.gitective.core.TreeUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.gitective.core.TreeUtils.java

Source

/*
 * Copyright (c) 2011 Kevin Sawicki <kevinsawicki@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
package org.gitective.core;

import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
import static org.eclipse.jgit.treewalk.filter.TreeFilter.ANY_DIFF;

import java.io.IOException;

import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;

/**
 * Utilities for dealing with Git trees.
 */
public abstract class TreeUtils {

    /**
     * Interface for visiting elements in a tree
     */
    public static interface ITreeVisitor {

        /**
         * Visit the given element
         *
         * @param mode
         *            mode of current entry
         * @param path
         *            parent path of entry, null for root entries
         * @param name
         *            name of current entry
         * @param id
         *            id of current entry
         * @return true to continue, false to abort
         */
        boolean accept(FileMode mode, String path, String name, AnyObjectId id);
    }

    /**
     * Get the tree associated with the given commit.
     *
     * @param walk
     * @param commit
     * @return tree
     * @throws IOException
     */
    protected static RevTree getTree(final RevWalk walk, final RevCommit commit) throws IOException {
        final RevTree tree = commit.getTree();
        if (tree != null)
            return tree;
        walk.parseHeaders(commit);
        return commit.getTree();
    }

    /**
     * Create a tree walk with the commit's parents.
     *
     * @param reader
     * @param rWalk
     * @param commit
     * @return tree walk
     * @throws IOException
     */
    protected static TreeWalk withParents(final ObjectReader reader, final RevWalk rWalk, final RevCommit commit)
            throws IOException {
        final TreeWalk walk = new TreeWalk(reader);
        final int parentCount = commit.getParentCount();
        switch (parentCount) {
        case 0:
            walk.addTree(new EmptyTreeIterator());
            break;
        case 1:
            walk.addTree(getTree(rWalk, commit.getParent(0)));
            break;
        default:
            final RevCommit[] parents = commit.getParents();
            for (int i = 0; i < parentCount; i++)
                walk.addTree(getTree(rWalk, parents[i]));
        }
        walk.addTree(getTree(rWalk, commit));
        return walk;
    }

    /**
     * Create a tree walk with all the trees from the given commit's parents.
     *
     * @param repository
     * @param commitId
     * @return tree walk
     */
    public static TreeWalk withParents(final Repository repository, final AnyObjectId commitId) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (commitId == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Commit id"));

        final ObjectReader reader = repository.newObjectReader();
        final RevWalk walk = new RevWalk(reader);
        try {
            return withParents(reader, walk, walk.parseCommit(commitId));
        } catch (IOException e) {
            walk.release();
            throw new GitException(e, repository);
        }
    }

    /**
     * Create a tree walk with all the trees from the given revision's commit
     * parents.
     *
     * @param repository
     * @param revision
     * @return tree walk
     */
    public static TreeWalk withParents(final Repository repository, final String revision) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (revision == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Revision"));
        if (revision.length() == 0)
            throw new IllegalArgumentException(Assert.formatNotEmpty("Revision"));

        final ObjectId commit = CommitUtils.strictResolve(repository, revision);
        final ObjectReader reader = repository.newObjectReader();
        final RevWalk walk = new RevWalk(reader);
        try {
            return withParents(reader, walk, walk.parseCommit(commit));
        } catch (IOException e) {
            walk.release();
            throw new GitException(e, repository);
        }
    }

    /**
     * Create a tree walk with all the trees from the given commit's parents.
     *
     * @param walk
     * @param commit
     * @return tree walk
     */
    public static TreeWalk withParents(final RevWalk walk, final RevCommit commit) {
        if (walk == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Walk"));
        if (commit == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Commit"));

        try {
            return withParents(walk.getObjectReader(), walk, commit);
        } catch (IOException e) {
            throw new GitException(e, null);
        }
    }

    /**
     * Create a tree walk configured to diff the given commit against all the
     * parent commits.
     *
     * @param repository
     * @param commitId
     * @return tree walk
     */
    public static TreeWalk diffWithParents(final Repository repository, final AnyObjectId commitId) {
        final TreeWalk walk = withParents(repository, commitId);
        walk.setFilter(ANY_DIFF);
        return walk;
    }

    /**
     * Create a tree walk configured to diff the given commit against all the
     * parent commits.
     *
     * @param walk
     * @param commit
     * @return tree walk
     */
    public static TreeWalk diffWithParents(final RevWalk walk, final RevCommit commit) {
        final TreeWalk treeWalk = withParents(walk, commit);
        treeWalk.setFilter(ANY_DIFF);
        return treeWalk;
    }

    /**
     * Create a tree walk configured to diff the given revision against all the
     * parent commits.
     *
     * @param repository
     * @param revision
     * @return tree walk
     */
    public static TreeWalk diffWithParents(final Repository repository, final String revision) {
        final TreeWalk walk = withParents(repository, revision);
        walk.setFilter(ANY_DIFF);
        return walk;
    }

    /**
     * Create a tree walk configured with the given commit revisions
     *
     * @param repository
     * @param revisions
     * @return tree walk
     */
    public static TreeWalk withCommits(final Repository repository, final String... revisions) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (revisions == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Revisions"));
        if (revisions.length == 0)
            throw new IllegalArgumentException(Assert.formatNotEmpty("Revisions"));

        final TreeWalk walk = new TreeWalk(repository);
        try {
            for (String revision : revisions)
                walk.addTree(CommitUtils.getCommit(repository, revision).getTree());
        } catch (IOException e) {
            throw new GitException(e, repository);
        }
        return walk;
    }

    /**
     * Create a tree walk configured with the given commits
     *
     * @param repository
     * @param commits
     * @return tree walk
     */
    public static TreeWalk withCommits(final Repository repository, final ObjectId... commits) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (commits == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Commits"));
        if (commits.length == 0)
            throw new IllegalArgumentException(Assert.formatNotEmpty("Commits"));

        final TreeWalk walk = new TreeWalk(repository);
        try {
            for (ObjectId commit : commits)
                walk.addTree(CommitUtils.getCommit(repository, commit).getTree());
        } catch (IOException e) {
            throw new GitException(e, repository);
        }
        return walk;
    }

    /**
     * Create a tree walk configured to diff the given commits
     *
     * @param repository
     * @param commits
     * @return tree walk
     */
    public static TreeWalk diffWithCommits(final Repository repository, final ObjectId... commits) {
        final TreeWalk walk = withCommits(repository, commits);
        walk.setFilter(ANY_DIFF);
        return walk;
    }

    /**
     * Create a tree walk configured to diff the given commit revisions
     *
     * @param repository
     * @param revisions
     * @return tree walk
     */
    public static TreeWalk diffWithCommits(final Repository repository, final String... revisions) {
        final TreeWalk walk = withCommits(repository, revisions);
        walk.setFilter(ANY_DIFF);
        return walk;
    }

    /**
     * Get the id of the tree at the path in the given commit.
     *
     * @param repository
     * @param commit
     * @param path
     * @return tree id, null if not present
     */
    protected static ObjectId lookupId(final Repository repository, final RevCommit commit, final String path) {
        final TreeWalk walk;
        try {
            walk = TreeWalk.forPath(repository, path, commit.getTree());
        } catch (IOException e) {
            throw new GitException(e, repository);
        }
        if (walk == null)
            return null;
        if ((walk.getRawMode(0) & TYPE_MASK) != TYPE_TREE)
            return null;
        return walk.getObjectId(0);
    }

    /**
     * Get the id of the tree at the path in the given commit
     *
     * @param repository
     * @param commitId
     * @param path
     * @return tree id or null if no tree id at path
     */
    public static ObjectId getId(final Repository repository, final ObjectId commitId, final String path) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (commitId == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Commit Id"));
        if (path == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Path"));
        if (path.length() == 0)
            throw new IllegalArgumentException(Assert.formatNotNull("Path"));

        final RevCommit commit = CommitUtils.parse(repository, commitId);
        return lookupId(repository, commit, path);
    }

    /**
     * Get the id of the tree at the path in the given revision
     *
     * @param repository
     * @param revision
     * @param path
     * @return tree id or null if no tree id at path
     */
    public static ObjectId getId(final Repository repository, final String revision, final String path) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (revision == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Revision"));
        if (revision.length() == 0)
            throw new IllegalArgumentException(Assert.formatNotEmpty("Revision"));
        if (path == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Path"));
        if (path.length() == 0)
            throw new IllegalArgumentException(Assert.formatNotNull("Path"));

        final RevCommit commit = CommitUtils.parse(repository, CommitUtils.strictResolve(repository, revision));
        return lookupId(repository, commit, path);
    }

    /**
     * Visit entries in the given tree
     *
     * @param repository
     * @param treeId
     * @param visitor
     * @return true if fully completed, false if terminated early
     */
    public static boolean visit(final Repository repository, final ObjectId treeId, final ITreeVisitor visitor) {
        if (repository == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Repository"));
        if (treeId == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Tree Id"));
        if (visitor == null)
            throw new IllegalArgumentException(Assert.formatNotNull("Visitor"));

        final TreeWalk walk = new TreeWalk(repository);
        walk.setPostOrderTraversal(true);
        final MutableObjectId id = new MutableObjectId();
        try {
            walk.addTree(treeId);
            if (!visit(repository, walk, id, null, visitor))
                return false;
        } catch (IOException e) {
            throw new GitException(e, repository);
        } finally {
            walk.release();
        }
        return true;
    }

    private static boolean visit(final Repository repository, final TreeWalk walk, final MutableObjectId id,
            final String path, final ITreeVisitor visitor) throws IOException {
        while (walk.next()) {
            if (walk.isPostChildren())
                break;

            if (walk.isSubtree()) {
                final String subTreePath = walk.getPathString();
                walk.enterSubtree();
                if (!visit(repository, walk, id, subTreePath, visitor))
                    return false;
            }

            walk.getObjectId(id, 0);
            if (!visitor.accept(walk.getFileMode(0), path, walk.getNameString(), id))
                return false;
        }
        return true;
    }
}