James Moger
2011-07-01 d9f68798555e5ba8b4f7ddecf4a8cabbe9bb161a
Documentation. Added JavaDoc comments.
20 files modified
691 ■■■■ changed files
src/com/gitblit/models/AnnotatedLine.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/GitNote.java 12 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/Metric.java 9 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/PathModel.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/RefModel.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/RepositoryModel.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/TicketModel.java 51 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/UserModel.java 8 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/ByteFormat.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/DiffUtils.java 161 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/FileUtils.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/utils/GitBlitDiffFormatter.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/GitWebDiffFormatter.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/MarkdownUtils.java 21 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/MetricUtils.java 74 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/PatchFormatter.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/StringUtils.java 137 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/SyndicationUtils.java 18 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/TicgitUtils.java 62 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/TimeUtils.java 76 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/AnnotatedLine.java
@@ -20,6 +20,13 @@
import org.eclipse.jgit.revwalk.RevCommit;
/**
 * AnnotatedLine is a serializable model class that represents a the most recent
 * author, date, and commit id of a line in a source file.
 *
 * @author James Moger
 *
 */
public class AnnotatedLine implements Serializable {
    private static final long serialVersionUID = 1L;
src/com/gitblit/models/GitNote.java
@@ -17,12 +17,20 @@
import java.io.Serializable;
/**
 * GitNote is a serializable model class that represents a git note. This class
 * retains an instance of the RefModel which contains the commit in which this
 * git note was created.
 *
 * @author James Moger
 *
 */
public class GitNote implements Serializable {
    private static final long serialVersionUID = 1L;
    public String content;
    public RefModel notesRef;
    public final String content;
    public final RefModel notesRef;
    public GitNote(RefModel notesRef, String text) {
        this.notesRef = notesRef;
src/com/gitblit/models/Metric.java
@@ -17,11 +17,18 @@
import java.io.Serializable;
/**
 * Metric is a serializable model class that encapsulates metrics for some given
 * type.
 *
 * @author James Moger
 *
 */
public class Metric implements Serializable {
    private static final long serialVersionUID = 1L;
    public String name;
    public final String name;
    public double count;
    public double tag;
    public int duration;
src/com/gitblit/models/PathModel.java
@@ -20,6 +20,13 @@
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.FileMode;
/**
 * PathModel is a serializable model class that represents a file or a folder,
 * including all its metadata and associated commit id.
 *
 * @author James Moger
 *
 */
public class PathModel implements Serializable, Comparable<PathModel> {
    private static final long serialVersionUID = 1L;
@@ -71,6 +78,13 @@
        return 1;
    }
    /**
     * PathChangeModel is a serializable class that represents a file changed in
     * a commit.
     *
     * @author James Moger
     *
     */
    public static class PathChangeModel extends PathModel {
        private static final long serialVersionUID = 1L;
src/com/gitblit/models/RefModel.java
@@ -25,6 +25,13 @@
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
/**
 * RefModel is a serializable model class that represents a tag or branch and
 * includes the referenced object.
 *
 * @author James Moger
 *
 */
public class RefModel implements Serializable, Comparable<RefModel> {
    private static final long serialVersionUID = 1L;
src/com/gitblit/models/RepositoryModel.java
@@ -20,6 +20,13 @@
import com.gitblit.Constants.AccessRestrictionType;
/**
 * RepositoryModel is a serializable model class that represents a Gitblit
 * repository including its configuration settings and access restriction.
 *
 * @author James Moger
 *
 */
public class RepositoryModel implements Serializable {
    private static final long serialVersionUID = 1L;
src/com/gitblit/models/TicketModel.java
@@ -21,6 +21,12 @@
import java.util.Date;
import java.util.List;
/**
 * TicketModel is a serializable model class that represents a Ticgit ticket.
 *
 * @author James Moger
 *
 */
public class TicketModel implements Serializable, Comparable<TicketModel> {
    private static final long serialVersionUID = 1L;
@@ -50,6 +56,32 @@
        }
    }
    @Override
    public int hashCode() {
        return id.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof TicketModel) {
            TicketModel other = (TicketModel) o;
            return id.equals(other.id);
        }
        return super.equals(o);
    }
    @Override
    public int compareTo(TicketModel o) {
        return date.compareTo(o.date);
    }
    /**
     * Comment is a serializable model class that represents a Ticgit ticket
     * comment.
     *
     * @author James Moger
     *
     */
    public static class Comment implements Serializable, Comparable<Comment> {
        private static final long serialVersionUID = 1L;
@@ -83,24 +115,5 @@
        public int compareTo(Comment o) {
            return date.compareTo(o.date);
        }
    }
    @Override
    public int hashCode() {
        return id.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof TicketModel) {
            TicketModel other = (TicketModel) o;
            return id.equals(other.id);
        }
        return super.equals(o);
    }
    @Override
    public int compareTo(TicketModel o) {
        return date.compareTo(o.date);
    }
}
src/com/gitblit/models/UserModel.java
@@ -20,6 +20,14 @@
import java.util.ArrayList;
import java.util.List;
/**
 * UserModel is a serializable model class that represents a user and the user's
 * restricted repository memberships. Instances of UserModels are also used as
 * servlet user principals.
 *
 * @author James Moger
 *
 */
public class UserModel implements Principal, Serializable {
    private static final long serialVersionUID = 1L;
src/com/gitblit/utils/ByteFormat.java
@@ -20,6 +20,13 @@
import java.text.Format;
import java.text.ParsePosition;
/**
 * ByteFormat is a formatter which takes numbers and returns filesizes in bytes,
 * kilobytes, megabytes, or gigabytes.
 *
 * @author James Moger
 *
 */
public class ByteFormat extends Format {
    private static final long serialVersionUID = 1L;
src/com/gitblit/utils/DiffUtils.java
@@ -30,13 +30,20 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.models.AnnotatedLine;
/**
 * DiffUtils is a class of utility methods related to diff, patch, and blame.
 *
 * The diff methods support pluggable diff output types like Gitblit, Gitweb,
 * and Plain.
 *
 * @author James Moger
 *
 */
public class DiffUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(DiffUtils.class);
@@ -54,43 +61,67 @@
        }
    }
    public static String getCommitDiff(Repository r, RevCommit commit, DiffOutputType outputType) {
        return getDiff(r, null, commit, null, outputType);
    /**
     * Returns the complete diff of the specified commit compared to its primary
     * parent.
     *
     * @param repository
     * @param commit
     * @param outputType
     * @return the diff as a string
     */
    public static String getCommitDiff(Repository repository, RevCommit commit,
            DiffOutputType outputType) {
        return getDiff(repository, null, commit, null, outputType);
    }
    public static String getDiff(Repository r, RevCommit commit, String path,
    /**
     * Returns the diff for the specified file or folder from the specified
     * commit compared to its primary parent.
     *
     * @param repository
     * @param commit
     * @param path
     * @param outputType
     * @return the diff as a string
     */
    public static String getDiff(Repository repository, RevCommit commit, String path,
            DiffOutputType outputType) {
        return getDiff(r, null, commit, path, outputType);
        return getDiff(repository, null, commit, path, outputType);
    }
    public static String getDiff(Repository r, RevCommit baseCommit, RevCommit commit,
    /**
     * Returns the complete diff between the two specified commits.
     *
     * @param repository
     * @param baseCommit
     * @param commit
     * @param outputType
     * @return the diff as a string
     */
    public static String getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
            DiffOutputType outputType) {
        return getDiff(r, baseCommit, commit, null, outputType);
        return getDiff(repository, baseCommit, commit, null, outputType);
    }
    public static String getDiff(Repository r, RevCommit baseCommit, RevCommit commit, String path,
            DiffOutputType outputType) {
    /**
     * Returns the diff between two commits for the specified file.
     *
     * @param repository
     * @param baseCommit
     *            if base commit is null the diff is to the primary parent of
     *            the commit.
     * @param commit
     * @param path
     *            if the path is specified, the diff is restricted to that file
     *            or folder. if unspecified, the diff is for the entire commit.
     * @param outputType
     * @return the diff as a string
     */
    public static String getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
            String path, DiffOutputType outputType) {
        String diff = null;
        try {
            RevTree baseTree;
            if (baseCommit == null) {
                final RevWalk rw = new RevWalk(r);
                RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
                rw.dispose();
                baseTree = parent.getTree();
            } else {
                baseTree = baseCommit.getTree();
            }
            RevTree commitTree = commit.getTree();
            final TreeWalk walk = new TreeWalk(r);
            walk.reset();
            walk.setRecursive(true);
            walk.addTree(baseTree);
            walk.addTree(commitTree);
            walk.setFilter(TreeFilter.ANY_DIFF);
            final ByteArrayOutputStream os = new ByteArrayOutputStream();
            RawTextComparator cmp = RawTextComparator.DEFAULT;
            DiffFormatter df;
@@ -106,9 +137,21 @@
                df = new DiffFormatter(os);
                break;
            }
            df.setRepository(r);
            df.setRepository(repository);
            df.setDiffComparator(cmp);
            df.setDetectRenames(true);
            RevTree commitTree = commit.getTree();
            RevTree baseTree;
            if (baseCommit == null) {
                final RevWalk rw = new RevWalk(repository);
                RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
                rw.dispose();
                baseTree = parent.getTree();
            } else {
                baseTree = baseCommit.getTree();
            }
            List<DiffEntry> diffEntries = df.scan(baseTree, commitTree);
            if (path != null && path.length() > 0) {
                for (DiffEntry diffEntry : diffEntries) {
@@ -133,33 +176,42 @@
        return diff;
    }
    public static String getCommitPatch(Repository r, RevCommit baseCommit, RevCommit commit,
            String path) {
    /**
     * Returns the diff between the two commits for the specified file or folder
     * formatted as a patch.
     *
     * @param repository
     * @param baseCommit
     *            if base commit is unspecified, the patch is generated against
     *            the primary parent of the specified commit.
     * @param commit
     * @param path
     *            if path is specified, the patch is generated only for the
     *            specified file or folder. if unspecified, the patch is
     *            generated for the entire diff between the two commits.
     * @return patch as a string
     */
    public static String getCommitPatch(Repository repository, RevCommit baseCommit,
            RevCommit commit, String path) {
        String diff = null;
        try {
            final ByteArrayOutputStream os = new ByteArrayOutputStream();
            RawTextComparator cmp = RawTextComparator.DEFAULT;
            PatchFormatter df = new PatchFormatter(os);
            df.setRepository(repository);
            df.setDiffComparator(cmp);
            df.setDetectRenames(true);
            RevTree commitTree = commit.getTree();
            RevTree baseTree;
            if (baseCommit == null) {
                final RevWalk rw = new RevWalk(r);
                final RevWalk rw = new RevWalk(repository);
                RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
                baseTree = parent.getTree();
            } else {
                baseTree = baseCommit.getTree();
            }
            RevTree commitTree = commit.getTree();
            final TreeWalk walk = new TreeWalk(r);
            walk.reset();
            walk.setRecursive(true);
            walk.addTree(baseTree);
            walk.addTree(commitTree);
            walk.setFilter(TreeFilter.ANY_DIFF);
            final ByteArrayOutputStream os = new ByteArrayOutputStream();
            RawTextComparator cmp = RawTextComparator.DEFAULT;
            PatchFormatter df = new PatchFormatter(os);
            df.setRepository(r);
            df.setDiffComparator(cmp);
            df.setDetectRenames(true);
            List<DiffEntry> diffEntries = df.scan(baseTree, commitTree);
            if (path != null && path.length() > 0) {
                for (DiffEntry diffEntry : diffEntries) {
@@ -179,15 +231,24 @@
        return diff;
    }
    public static List<AnnotatedLine> blame(Repository r, String blobPath, String objectId) {
    /**
     * Returns the list of lines in the specified source file annotated with the
     * source commit metadata.
     *
     * @param repository
     * @param blobPath
     * @param objectId
     * @return list of annotated lines
     */
    public static List<AnnotatedLine> blame(Repository repository, String blobPath, String objectId) {
        List<AnnotatedLine> lines = new ArrayList<AnnotatedLine>();
        try {
            if (StringUtils.isEmpty(objectId)) {
                objectId = Constants.HEAD;
            }
            BlameCommand blameCommand = new BlameCommand(r);
            BlameCommand blameCommand = new BlameCommand(repository);
            blameCommand.setFilePath(blobPath);
            blameCommand.setStartCommit(r.resolve(objectId));
            blameCommand.setStartCommit(repository.resolve(objectId));
            BlameResult blameResult = blameCommand.call();
            RawText rawText = blameResult.getResultContents();
            int length = rawText.size();
src/com/gitblit/utils/FileUtils.java
@@ -34,7 +34,7 @@
     * 
     * @param file
     * @param lineEnding
     * @return
     * @return the string content of the file
     */
    public static String readContent(File file, String lineEnding) {
        StringBuilder sb = new StringBuilder();
src/com/gitblit/utils/GitBlitDiffFormatter.java
@@ -21,6 +21,12 @@
import org.eclipse.jgit.diff.RawText;
/**
 * Generates an html snippet of a diff in Gitblit's style.
 *
 * @author James Moger
 *
 */
public class GitBlitDiffFormatter extends GitWebDiffFormatter {
    private final OutputStream os;
src/com/gitblit/utils/GitWebDiffFormatter.java
@@ -24,6 +24,12 @@
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawText;
/**
 * Returns an html snippet of the diff in the standard Gitweb style.
 *
 * @author James Moger
 *
 */
public class GitWebDiffFormatter extends DiffFormatter {
    private final OutputStream os;
src/com/gitblit/utils/MarkdownUtils.java
@@ -23,8 +23,21 @@
import org.tautua.markdownpapers.Markdown;
import org.tautua.markdownpapers.parser.ParseException;
/**
 * Utility methods for transforming raw markdown text to html.
 *
 * @author James Moger
 *
 */
public class MarkdownUtils {
    /**
     * Returns the html version of the markdown source text.
     *
     * @param markdown
     * @return html version of markdown text
     * @throws java.text.ParseException
     */
    public static String transformMarkdown(String markdown) throws java.text.ParseException {
        try {
            return transformMarkdown(new StringReader(markdown));
@@ -33,6 +46,14 @@
        }
    }
    /**
     * Returns the html version of the markdown source reader. The reader is
     * closed regardless of success or failure.
     *
     * @param markdownReader
     * @return html version of the markdown text
     * @throws java.text.ParseException
     */
    public static String transformMarkdown(Reader markdownReader) throws java.text.ParseException {
        // Read raw markdown content and transform it to html
        StringWriter writer = new StringWriter();
src/com/gitblit/utils/MetricUtils.java
@@ -35,33 +35,58 @@
import com.gitblit.models.Metric;
import com.gitblit.models.RefModel;
/**
 * Utility class for collecting metrics on a branch, tag, or other ref within
 * the repository.
 *
 * @author James Moger
 *
 */
public class MetricUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(MetricUtils.class);
    public static List<Metric> getDateMetrics(Repository r, String objectId, boolean includeTotal,
            String format) {
    /**
     * Returns the list of metrics for the specified commit reference, branch,
     * or tag within the repository. If includeTotal is true, the total of all
     * the metrics will be included as the first element in the returned list.
     *
     * If the dateformat is unspecified an attempt is made to determine an
     * appropriate date format by determining the time difference between the
     * first commit on the branch and the most recent commit. This assumes that
     * the commits are linear.
     *
     * @param repository
     * @param objectId
     *            if null or empty, HEAD is assumed.
     * @param includeTotal
     * @param dateFormat
     * @return list of metrics
     */
    public static List<Metric> getDateMetrics(Repository repository, String objectId,
            boolean includeTotal, String dateFormat) {
        Metric total = new Metric("TOTAL");
        final Map<String, Metric> metricMap = new HashMap<String, Metric>();
        if (StringUtils.isEmpty(objectId)) {
            objectId = Constants.HEAD;
        }
        if (JGitUtils.hasCommits(r)) {
            final List<RefModel> tags = JGitUtils.getTags(r, true, -1);
        if (JGitUtils.hasCommits(repository)) {
            final List<RefModel> tags = JGitUtils.getTags(repository, true, -1);
            final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
            for (RefModel tag : tags) {
                tagMap.put(tag.getReferencedObjectId(), tag);
            }
            RevWalk revWalk = null;
            try {
                RevWalk walk = new RevWalk(r);
                ObjectId object = r.resolve(objectId);
                RevCommit lastCommit = walk.parseCommit(object);
                walk.markStart(lastCommit);
                revWalk = new RevWalk(repository);
                ObjectId object = repository.resolve(objectId);
                RevCommit lastCommit = revWalk.parseCommit(object);
                revWalk.markStart(lastCommit);
                DateFormat df;
                if (StringUtils.isEmpty(format)) {
                if (StringUtils.isEmpty(dateFormat)) {
                    // dynamically determine date format
                    RevCommit firstCommit = JGitUtils.getFirstCommit(r, Constants.HEAD);
                    RevCommit firstCommit = JGitUtils.getFirstCommit(repository, Constants.HEAD);
                    int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime())
                            / (60 * 60 * 24);
                    total.duration = diffDays;
@@ -74,10 +99,10 @@
                    }
                } else {
                    // use specified date format
                    df = new SimpleDateFormat(format);
                    df = new SimpleDateFormat(dateFormat);
                }
                Iterable<RevCommit> revlog = walk;
                Iterable<RevCommit> revlog = revWalk;
                for (RevCommit rev : revlog) {
                    Date d = JGitUtils.getCommitDate(rev);
                    String p = df.format(d);
@@ -94,6 +119,10 @@
                }
            } catch (Throwable t) {
                LOGGER.error("Failed to mine log history for date metrics", t);
            } finally {
                if (revWalk != null) {
                    revWalk.dispose();
                }
            }
        }
        List<String> keys = new ArrayList<String>(metricMap.keySet());
@@ -108,22 +137,33 @@
        return metrics;
    }
    public static List<Metric> getAuthorMetrics(Repository r, String objectId, boolean byEmail) {
    /**
     * Returns a list of author metrics for the specified repository.
     *
     * @param repository
     * @param objectId
     *            if null or empty, HEAD is assumed.
     * @param byEmailAddress
     *            group metrics by author email address otherwise by author name
     * @return list of metrics
     */
    public static List<Metric> getAuthorMetrics(Repository repository, String objectId,
            boolean byEmailAddress) {
        final Map<String, Metric> metricMap = new HashMap<String, Metric>();
        if (StringUtils.isEmpty(objectId)) {
            objectId = Constants.HEAD;
        }
        if (JGitUtils.hasCommits(r)) {
        if (JGitUtils.hasCommits(repository)) {
            try {
                RevWalk walk = new RevWalk(r);
                ObjectId object = r.resolve(objectId);
                RevWalk walk = new RevWalk(repository);
                ObjectId object = repository.resolve(objectId);
                RevCommit lastCommit = walk.parseCommit(object);
                walk.markStart(lastCommit);
                Iterable<RevCommit> revlog = walk;
                for (RevCommit rev : revlog) {
                    String p;
                    if (byEmail) {
                    if (byEmailAddress) {
                        p = rev.getAuthorIdent().getEmailAddress().toLowerCase();
                        if (StringUtils.isEmpty(p)) {
                            p = rev.getAuthorIdent().getName().toLowerCase();
src/com/gitblit/utils/PatchFormatter.java
@@ -30,6 +30,12 @@
import com.gitblit.Constants;
/**
 * A diff formatter that outputs standard patch content.
 *
 * @author James Moger
 *
 */
public class PatchFormatter extends DiffFormatter {
    private final OutputStream os;
src/com/gitblit/utils/StringUtils.java
@@ -22,18 +22,44 @@
import java.util.List;
import java.util.regex.PatternSyntaxException;
/**
 * Utility class of string functions.
 *
 * @author James Moger
 *
 */
public class StringUtils {
    public static final String MD5_TYPE = "MD5:";
    /**
     * Returns true if the string is null or empty.
     *
     * @param value
     * @return true if string is null or empty
     */
    public static boolean isEmpty(String value) {
        return value == null || value.trim().length() == 0;
    }
    /**
     * Replaces carriage returns and line feeds with html line breaks.
     *
     * @param string
     * @return plain text with html line breaks
     */
    public static String breakLinesForHtml(String string) {
        return string.replace("\r\n", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
    }
    /**
     * Prepare text for html presentation. Replace sensitive characters with
     * html entities.
     *
     * @param inStr
     * @param changeSpace
     * @return plain text escaped for html
     */
    public static String escapeForHtml(String inStr, boolean changeSpace) {
        StringBuffer retStr = new StringBuffer();
        int i = 0;
@@ -58,11 +84,23 @@
        return retStr.toString();
    }
    /**
     * Decode html entities back into plain text characters.
     *
     * @param inStr
     * @return returns plain text from html
     */
    public static String decodeFromHtml(String inStr) {
        return inStr.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">")
                .replace("&quot;", "\"").replace("&nbsp;", " ");
    }
    /**
     * Encodes a url parameter by escaping troublesome characters.
     *
     * @param inStr
     * @return properly escaped url
     */
    public static String encodeURL(String inStr) {
        StringBuffer retStr = new StringBuffer();
        int i = 0;
@@ -79,10 +117,24 @@
        return retStr.toString();
    }
    /**
     * Flatten the list of strings into a single string with a space separator.
     *
     * @param values
     * @return flattened list
     */
    public static String flattenStrings(List<String> values) {
        return flattenStrings(values, " ");
    }
    /**
     * Flatten the list of strings into a single string with the specified
     * separator.
     *
     * @param values
     * @param separator
     * @return flattened list
     */
    public static String flattenStrings(List<String> values, String separator) {
        StringBuilder sb = new StringBuilder();
        for (String value : values) {
@@ -91,6 +143,15 @@
        return sb.toString().trim();
    }
    /**
     * Returns a string trimmed to a maximum length with trailing ellipses. If
     * the string length is shorter than the max, the original string is
     * returned.
     *
     * @param value
     * @param max
     * @return trimmed string
     */
    public static String trimString(String value, int max) {
        if (value.length() <= max) {
            return value;
@@ -98,10 +159,25 @@
        return value.substring(0, max - 3) + "...";
    }
    /**
     * Returns a trimmed shortlog message.
     *
     * @param string
     * @return trimmed shortlog message
     */
    public static String trimShortLog(String string) {
        return trimString(string, 60);
    }
    /**
     * Left pad a string with the specified character, if the string length is
     * less than the specified length.
     *
     * @param input
     * @param length
     * @param pad
     * @return left-padded string
     */
    public static String leftPad(String input, int length, char pad) {
        if (input.length() < length) {
            StringBuilder sb = new StringBuilder();
@@ -114,6 +190,15 @@
        return input;
    }
    /**
     * Right pad a string with the specified character, if the string length is
     * less then the specified length.
     *
     * @param input
     * @param length
     * @param pad
     * @return right-padded string
     */
    public static String rightPad(String input, int length, char pad) {
        if (input.length() < length) {
            StringBuilder sb = new StringBuilder();
@@ -126,6 +211,12 @@
        return input;
    }
    /**
     * Calculates the SHA1 of the string.
     *
     * @param text
     * @return sha1 of the string
     */
    public static String getSHA1(String text) {
        try {
            byte[] bytes = text.getBytes("iso-8859-1");
@@ -135,6 +226,12 @@
        }
    }
    /**
     * Calculates the SHA1 of the byte array.
     *
     * @param bytes
     * @return sha1 of the byte array
     */
    public static String getSHA1(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
@@ -146,6 +243,12 @@
        }
    }
    /**
     * Calculates the MD5 of the string.
     *
     * @param string
     * @return md5 of the string
     */
    public static String getMD5(String string) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
@@ -160,6 +263,12 @@
        }
    }
    /**
     * Returns the hex representation of the byte array.
     *
     * @param bytes
     * @return byte array as hex string
     */
    private static String toHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
@@ -171,6 +280,13 @@
        return sb.toString();
    }
    /**
     * Returns the root path of the specified path. Returns a blank string if
     * there is no root path.
     *
     * @param path
     * @return root path or blank
     */
    public static String getRootPath(String path) {
        if (path.indexOf('/') > -1) {
            return path.substring(0, path.lastIndexOf('/'));
@@ -178,6 +294,14 @@
        return "";
    }
    /**
     * Returns the path remainder after subtracting the basePath from the
     * fullPath.
     *
     * @param basePath
     * @param fullPath
     * @return the relative path
     */
    public static String getRelativePath(String basePath, String fullPath) {
        String relativePath = fullPath.substring(basePath.length()).replace('\\', '/');
        if (relativePath.charAt(0) == '/') {
@@ -186,10 +310,23 @@
        return relativePath;
    }
    /**
     * Splits the space-separated string into a list of strings.
     *
     * @param value
     * @return list of strings
     */
    public static List<String> getStringsFromValue(String value) {
        return getStringsFromValue(value, " ");
    }
    /**
     * Splits the string into a list of string by the specified separator.
     *
     * @param value
     * @param separator
     * @return list of strings
     */
    public static List<String> getStringsFromValue(String value, String separator) {
        List<String> strings = new ArrayList<String>();
        try {
src/com/gitblit/utils/SyndicationUtils.java
@@ -35,8 +35,26 @@
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedOutput;
/**
 * Utility class for RSS feeds.
 *
 * @author James Moger
 *
 */
public class SyndicationUtils {
    /**
     * Outputs an RSS feed of the list of commits to the outputstream.
     *
     * @param hostUrl
     * @param title
     * @param description
     * @param repository
     * @param commits
     * @param os
     * @throws IOException
     * @throws FeedException
     */
    public static void toRSS(String hostUrl, String title, String description, String repository,
            List<RevCommit> commits, OutputStream os) throws IOException, FeedException {
src/com/gitblit/utils/TicgitUtils.java
@@ -30,15 +30,28 @@
import com.gitblit.models.TicketModel;
import com.gitblit.models.TicketModel.Comment;
/**
 * Utility class for reading Ticgit issues.
 *
 * @author James Moger
 *
 */
public class TicgitUtils {
    static final Logger LOGGER = LoggerFactory.getLogger(TicgitUtils.class);
    public static RefModel getTicketsBranch(Repository r) {
    /**
     * Returns a RefModel for the Ticgit branch in the repository. If the branch
     * can not be found, null is returned.
     *
     * @param repository
     * @return a refmodel for the ticgit branch or null
     */
    public static RefModel getTicketsBranch(Repository repository) {
        RefModel ticgitBranch = null;
        try {
            // search for ticgit branch in local heads
            for (RefModel ref : JGitUtils.getLocalBranches(r, false, -1)) {
            for (RefModel ref : JGitUtils.getLocalBranches(repository, false, -1)) {
                if (ref.displayName.endsWith("ticgit")) {
                    ticgitBranch = ref;
                    break;
@@ -47,7 +60,7 @@
            // search for ticgit branch in remote heads
            if (ticgitBranch == null) {
                for (RefModel ref : JGitUtils.getRemoteBranches(r, false, -1)) {
                for (RefModel ref : JGitUtils.getRemoteBranches(repository, false, -1)) {
                    if (ref.displayName.endsWith("ticgit")) {
                        ticgitBranch = ref;
                        break;
@@ -60,19 +73,25 @@
        return ticgitBranch;
    }
    public static List<TicketModel> getTickets(Repository r) {
        RefModel ticgitBranch = getTicketsBranch(r);
    /**
     * Returns a list of all tickets in the ticgit branch of the repository.
     *
     * @param repository
     * @return list of tickets
     */
    public static List<TicketModel> getTickets(Repository repository) {
        RefModel ticgitBranch = getTicketsBranch(repository);
        if (ticgitBranch == null) {
            return null;
        }
        RevCommit commit = (RevCommit) ticgitBranch.referencedObject;
        List<PathModel> paths = JGitUtils.getFilesInPath(r, null, commit);
        List<PathModel> paths = JGitUtils.getFilesInPath(repository, null, commit);
        List<TicketModel> tickets = new ArrayList<TicketModel>();
        for (PathModel ticketFolder : paths) {
            if (ticketFolder.isTree()) {
                try {
                    TicketModel t = new TicketModel(ticketFolder.name);
                    readTicketContents(r, ticgitBranch, t);
                    loadTicketContents(repository, ticgitBranch, t);
                    tickets.add(t);
                } catch (Throwable t) {
                    LOGGER.error("Failed to get a ticket!", t);
@@ -84,12 +103,20 @@
        return tickets;
    }
    public static TicketModel getTicket(Repository r, String ticketFolder) {
        RefModel ticketsBranch = getTicketsBranch(r);
    /**
     * Returns a TicketModel for the specified ticgit ticket. Returns null if
     * the ticket does not exist or some other error occurs.
     *
     * @param repository
     * @param ticketFolder
     * @return a ticket
     */
    public static TicketModel getTicket(Repository repository, String ticketFolder) {
        RefModel ticketsBranch = getTicketsBranch(repository);
        if (ticketsBranch != null) {
            try {
                TicketModel ticket = new TicketModel(ticketFolder);
                readTicketContents(r, ticketsBranch, ticket);
                loadTicketContents(repository, ticketsBranch, ticket);
                return ticket;
            } catch (Throwable t) {
                LOGGER.error("Failed to get ticket " + ticketFolder, t);
@@ -98,11 +125,20 @@
        return null;
    }
    private static void readTicketContents(Repository r, RefModel ticketsBranch, TicketModel ticket) {
    /**
     * Loads the contents of the ticket.
     *
     * @param repository
     * @param ticketsBranch
     * @param ticket
     */
    private static void loadTicketContents(Repository repository, RefModel ticketsBranch,
            TicketModel ticket) {
        RevCommit commit = (RevCommit) ticketsBranch.referencedObject;
        List<PathModel> ticketFiles = JGitUtils.getFilesInPath(r, ticket.name, commit);
        List<PathModel> ticketFiles = JGitUtils.getFilesInPath(repository, ticket.name, commit);
        for (PathModel file : ticketFiles) {
            String content = JGitUtils.getStringContent(r, commit.getTree(), file.path).trim();
            String content = JGitUtils.getStringContent(repository, commit.getTree(), file.path)
                    .trim();
            if (file.name.equals("TICKET_ID")) {
                ticket.id = content;
            } else if (file.name.equals("TITLE")) {
src/com/gitblit/utils/TimeUtils.java
@@ -18,6 +18,12 @@
import java.util.Calendar;
import java.util.Date;
/**
 * Utility class of time functions.
 *
 * @author James Moger
 *
 */
public class TimeUtils {
    public static final long MIN = 1000 * 60L;
@@ -29,10 +35,22 @@
    public static final long ONEYEAR = ONEDAY * 365L;
    /**
     * Returns true if date is today.
     *
     * @param date
     * @return true if date is today
     */
    public static boolean isToday(Date date) {
        return (System.currentTimeMillis() - date.getTime()) < ONEDAY;
    }
    /**
     * Returns true if date is yesterday.
     *
     * @param date
     * @return true if date is yesterday
     */
    public static boolean isYesterday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
@@ -40,6 +58,13 @@
        return (System.currentTimeMillis() - cal.getTimeInMillis()) < ONEDAY;
    }
    /**
     * Returns the string representation of the duration as days, months and/or
     * years.
     *
     * @param days
     * @return duration as string in days, months, and/or years
     */
    public static String duration(int days) {
        if (days <= 60) {
            return days + (days > 1 ? " days" : " day");
@@ -69,6 +94,15 @@
        }
    }
    /**
     * Returns the number of minutes ago between the start time and the end
     * time.
     *
     * @param date
     * @param endTime
     * @param roundup
     * @return difference in minutes
     */
    public static int minutesAgo(Date date, long endTime, boolean roundup) {
        long diff = endTime - date.getTime();
        int mins = (int) (diff / MIN);
@@ -78,10 +112,24 @@
        return mins;
    }
    /**
     * Return the difference in minutes between now and the date.
     *
     * @param date
     * @param roundup
     * @return minutes ago
     */
    public static int minutesAgo(Date date, boolean roundup) {
        return minutesAgo(date, System.currentTimeMillis(), roundup);
    }
    /**
     * Return the difference in hours between now and the date.
     *
     * @param date
     * @param roundup
     * @return hours ago
     */
    public static int hoursAgo(Date date, boolean roundup) {
        long diff = System.currentTimeMillis() - date.getTime();
        int hours = (int) (diff / ONEHOUR);
@@ -91,6 +139,13 @@
        return hours;
    }
    /**
     * Return the difference in days between now and the date.
     *
     * @param date
     * @param roundup
     * @return days ago
     */
    public static int daysAgo(Date date, boolean roundup) {
        long diff = System.currentTimeMillis() - date.getTime();
        int days = (int) (diff / ONEDAY);
@@ -100,14 +155,35 @@
        return days;
    }
    /**
     * Returns the string representation of the duration between now and the
     * date.
     *
     * @param date
     * @return duration as a string
     */
    public static String timeAgo(Date date) {
        return timeAgo(date, false);
    }
    /**
     * Returns the CSS class for the date based on its age from Now.
     *
     * @param date
     * @return the css class
     */
    public static String timeAgoCss(Date date) {
        return timeAgo(date, true);
    }
    /**
     * Returns the string representation of the duration OR the css class for
     * the duration.
     *
     * @param date
     * @param css
     * @return the string representation of the duration OR the css class
     */
    private static String timeAgo(Date date, boolean css) {
        String ago = null;
        if (isToday(date) || isYesterday(date)) {