/*==========================================================================*\
| $Id: GitRepository.java,v 1.3 2012/06/22 16:23:18 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2011-2012 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT 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 Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.core.git;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import org.apache.log4j.Logger;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TagCommand;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.PathSuffixFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.jfree.util.Log;
import org.webcat.archives.IWritableContainer;
import org.webcat.core.EOBase;
import org.webcat.core.NSMutableDataOutputStream;
import org.webcat.core.git.GitUtilities.RefFilter;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
//-------------------------------------------------------------------------
/**
* TODO real description
*
* @author Tony Allevato
* @author Last changed by $Author: aallowat $
* @version $Revision: 1.3 $, $Date: 2012/06/22 16:23:18 $
*/
public class GitRepository
implements NSKeyValueCodingAdditions
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
public GitRepository(Repository repository)
{
this.repository = repository;
}
//~ Methods ...............................................................
// ----------------------------------------------------------
public static GitRepository repositoryForObject(EOBase eo)
{
Repository repo = GitUtilities.repositoryForObject(eo);
GitRepository gr = new GitRepository(repo);
gr.provider = eo;
return gr;
}
// ----------------------------------------------------------
public EOBase provider()
{
return provider;
}
// ----------------------------------------------------------
public Repository repository()
{
return repository;
}
// ----------------------------------------------------------
private NSArray<GitRef> filteredRefs(RefFilter filter)
{
NSMutableArray<GitRef> result = new NSMutableArray<GitRef>();
Map<String, Ref> refs = repository.getAllRefs();
for (Ref ref : RefComparator.sort(refs.values()))
{
if (filter == null || filter.accepts(ref))
{
result.addObject(new GitRef(this, ref));
}
}
return result;
}
// ----------------------------------------------------------
public NSArray<GitRef> headRefs()
{
return filteredRefs(new RefFilter() {
public boolean accepts(Ref ref)
{
return ref.getName().startsWith(Constants.R_HEADS);
}
});
}
// ----------------------------------------------------------
public NSArray<GitRef> tagRefs()
{
return filteredRefs(new RefFilter() {
public boolean accepts(Ref ref)
{
return ref.getName().startsWith(Constants.R_TAGS);
}
});
}
// ----------------------------------------------------------
public NSArray<GitRef> allRefs()
{
return filteredRefs(null);
}
// ----------------------------------------------------------
public GitRef refWithName(String name)
{
Ref ref = null;
try
{
ref = repository.getRef(name);
}
catch (IOException e)
{
// Do nothing.
}
return ref != null ? new GitRef(this, ref) : null;
}
// ----------------------------------------------------------
public ObjectId resolve(String revstr)
{
try
{
return repository.resolve(revstr);
}
catch (Exception e)
{
Log.error("An error occurred when resolving the string " + revstr
+ " in the repository at " + repository.getDirectory(), e);
return null;
}
}
// ----------------------------------------------------------
public NSArray<GitCommit> commitsWithId(ObjectId commitId, String path)
{
if (commitId != null)
{
return commitsWithIds(new NSArray<ObjectId>(commitId), path);
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Parses an array of Git commits and returns an array of commits that
* affected the specified object.
*
* @param repository the Git repository
* @param commitIds the Git commit IDs to parse
* @param path the object affected
* @return an array of {@link GitCommit} objects representing the commits
*/
public NSArray<GitCommit> commitsWithIds(NSArray<ObjectId> commitIds,
String path)
{
try
{
RevWalk rw = new RevWalk(repository);
try
{
rw.sort(RevSort.COMMIT_TIME_DESC);
if (path != null)
{
rw.setTreeFilter(AndTreeFilter.create(
PathSuffixFilter.create(path),
TreeFilter.ANY_DIFF));
}
else
{
rw.setTreeFilter(TreeFilter.ALL);
}
for (ObjectId commitId : commitIds)
{
rw.markStart(rw.parseCommit(commitId));
}
NSMutableArray<GitCommit> commits =
new NSMutableArray<GitCommit>();
for (RevCommit commit : rw)
{
commits.add(new GitCommit(commit));
}
return commits;
}
finally
{
rw.release();
}
}
catch (Exception e)
{
log.error("An exception occurred while parsing the commit: ", e);
return null;
}
}
// ----------------------------------------------------------
public NSDictionary<GitRef, NSArray<GitCommit>> commitsForRefs(
NSArray<GitRef> refs)
{
NSMutableDictionary<GitRef, NSArray<GitCommit>> refsToCommits =
new NSMutableDictionary<GitRef, NSArray<GitCommit>>();
for (GitRef ref : refs)
{
refsToCommits.setObjectForKey(ref.commits(), ref);
}
return refsToCommits;
}
// ----------------------------------------------------------
/**
* Gets the type of the object with the specified ID (either a tree, tag,
* blob, or commit).
*
* @param id the object ID
* @return the object type, one of the {@code OBJ_*} constants in the
* {@link Constants} class.
*/
public int typeOfObject(ObjectId id)
{
ObjectReader reader = repository.newObjectReader();
try
{
ObjectLoader loader = reader.open(id);
return loader.getType();
}
catch (Exception e)
{
return Constants.OBJ_BAD;
}
finally
{
reader.release();
}
}
// ----------------------------------------------------------
/**
* Gets the contents of the specified blob as raw data.
*
* @param objectId the id of the blob
* @return an {@code NSData} object containing the raw data from the blob
*/
public NSData contentForBlob(ObjectId objectId)
{
ObjectReader reader = repository.newObjectReader();
try
{
ObjectLoader loader = reader.open(objectId);
NSMutableDataOutputStream output = new NSMutableDataOutputStream();
loader.copyTo(output);
output.close();
return output.data();
}
catch (IOException e)
{
log.error("An exception occurred while getting the blob "
+ "contents: ", e);
return null;
}
finally
{
reader.release();
}
}
// ----------------------------------------------------------
/**
* Gets the contents of the specified blob as a string.
*
* @param objectId the id of the blob
* @return a string containing the data from the blob
*/
public String stringContentForBlob(ObjectId objectId)
{
try
{
return new String(contentForBlob(objectId).bytes(), "UTF-8");
}
catch (IOException e)
{
log.error("An exception occurred while getting the blob "
+ "contents: ", e);
return null;
}
}
// ----------------------------------------------------------
/**
* Writes the contents of the specified blob to an output stream.
*
* @param objectId the id of the blob
* @param stream the stream to write the blob to
*/
public void writeBlobToStream(ObjectId objectId, OutputStream stream)
{
ObjectReader reader = repository.newObjectReader();
try
{
ObjectLoader loader = reader.open(objectId);
loader.copyTo(stream);
}
catch (IOException e)
{
log.error("An exception occurred while getting the blob "
+ "contents: ", e);
}
finally
{
reader.release();
}
}
// ----------------------------------------------------------
/**
* Copies an item from the repository into a container. If the item is a
* blob, it will be copied into the destination with the specified name; if
* the item is a tree or commit, its children will be recursively copied.
*
* @param objectId the id of the object to copy
* @param name the destination name to use (only when the item is a blob)
* @param container the container where the items should be copied
* @throws IOException if an I/O error occurred
*/
public void copyItemToContainer(ObjectId objectId, String name,
IWritableContainer container) throws IOException
{
int type = typeOfObject(objectId);
if (type == Constants.OBJ_BLOB)
{
OutputStream fileStream = container.createFile(name, -1);
writeBlobToStream(objectId, fileStream);
fileStream.close();
}
else if (type == Constants.OBJ_TREE || type == Constants.OBJ_COMMIT)
{
GitTreeIterator iterator = new GitTreeIterator(this, objectId);
try
{
for (GitTreeEntry entry : iterator)
{
if (entry.isTree())
{
IWritableContainer childContainer =
container.createContainer(entry.name());
copyItemToContainer(entry.objectId(), entry.name(),
childContainer);
childContainer.finish();
}
else
{
copyItemToContainer(entry.objectId(), entry.name(),
container);
}
}
}
finally
{
iterator.release();
}
}
}
// ----------------------------------------------------------
public GitRef createTagForObject(String name, ObjectId objectId)
{
TagCommand tag = new Git(repository).tag();
tag.setName(name);
if (objectId == null)
{
tag.setObjectId(null);
}
else
{
RevWalk revWalk = new RevWalk(repository);
try
{
RevCommit commit = revWalk.parseCommit(objectId);
tag.setObjectId(commit);
}
catch (Exception e)
{
return null;
}
finally
{
revWalk.release();
}
}
try
{
tag.call();
return refWithName(Constants.R_TAGS + name);
}
catch (Exception e)
{
return null;
}
}
// ----------------------------------------------------------
@Override
public int hashCode()
{
return repository.hashCode() ^ 0xEA70BEEF;
}
// ----------------------------------------------------------
@Override
public boolean equals(Object other)
{
if (other instanceof GitRepository)
{
GitRepository otherRepository = (GitRepository) other;
return repository == otherRepository.repository;
}
else
{
return false;
}
}
// ----------------------------------------------------------
public void takeValueForKeyPath(Object value, String keyPath)
{
NSKeyValueCodingAdditions.DefaultImplementation.takeValueForKeyPath(
this, value, keyPath);
}
// ----------------------------------------------------------
public Object valueForKeyPath(String keyPath)
{
return NSKeyValueCodingAdditions.DefaultImplementation.valueForKeyPath(
this, keyPath);
}
// ----------------------------------------------------------
public void takeValueForKey(Object value, String key)
{
NSKeyValueCoding.DefaultImplementation.takeValueForKey(
this, value, key);
}
// ----------------------------------------------------------
public Object valueForKey(String key)
{
return NSKeyValueCoding.DefaultImplementation.valueForKey(this, key);
}
//~ Static/instance variables .............................................
private Repository repository;
private EOBase provider;
private static final Logger log = Logger.getLogger(GitRepository.class);
}