From 158d3552c373f77af44c0196be69d0ce412b3621 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 15 Sep 2011 11:54:31 +0200 Subject: [PATCH] start implementing mercurial blame viewer --- .../sonia/scm/repository/HgBlameViewer.java | 232 ++++++++++++++++++ .../scm/repository/HgRepositoryHandler.java | 19 +- 2 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgBlameViewer.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgBlameViewer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgBlameViewer.java new file mode 100644 index 0000000000..dc1724d128 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgBlameViewer.java @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.AssertUtil; +import sonia.scm.util.IOUtil; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author Sebastian Sdorra + */ +public class HgBlameViewer implements BlameViewer +{ + + /** the logger for HgBlameViewer */ + private static final Logger logger = + LoggerFactory.getLogger(HgBlameViewer.class); + + // sample: "Sebastian Sdorra 34 Wed Sep 08 10:22:46 2010 +0200:1: " + + /** Field description */ + public static final Pattern REGEX_FIRSTPART = + Pattern.compile( + "(?i)^(.*)\\s+([0-9]+)\\s+([a-z]{3}\\s[a-z]{3}\\s[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\s[0-9]{4}\\s[+-][0-9]{4}):([0-9]+):\\s(.*)$"); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param handler + * @param repositoryDirectory + */ + public HgBlameViewer(HgRepositoryHandler handler, File repositoryDirectory) + { + this.handler = handler; + this.repositoryDirectory = repositoryDirectory; + } + + /** + * Constructs ... + * + * + * + * @param handler + * @param repository + */ + public HgBlameViewer(HgRepositoryHandler handler, Repository repository) + { + this(handler, handler.getDirectory(repository)); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param revision + * @param path + * + * @return + * + * @throws IOException + */ + @Override + public BlamePagingResult getBlame(String revision, String path) + throws IOException + { + AssertUtil.assertIsNotEmpty(path); + + if (Util.isEmpty(revision)) + { + revision = "tip"; + } + + ProcessBuilder builder = + new ProcessBuilder(handler.getConfig().getHgBinary(), "annotate", "-r", + revision, "-dnulv", Util.nonNull(path)); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + + for (String param : builder.command()) + { + msg.append(param).append(" "); + } + + logger.debug(msg.toString()); + } + + Process p = builder.directory(repositoryDirectory).start(); + BufferedReader reader = null; + BlamePagingResult result = null; + + try + { + reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + result = parseBlameInput(reader); + } + finally + { + IOUtil.close(reader); + } + + return result; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param reader + * + * @return + * + * @throws IOException + */ + private BlamePagingResult parseBlameInput(BufferedReader reader) + throws IOException + { + List blameLines = new ArrayList(); + String line = reader.readLine(); + + while (line != null) + { + BlameLine blameLine = parseBlameLine(line); + + if (blameLine != null) + { + blameLines.add(blameLine); + } + + line = reader.readLine(); + } + + return new BlamePagingResult(blameLines.size(), blameLines); + } + + /** + * Method description + * + * + * @param line + * + * @return + */ + private BlameLine parseBlameLine(String line) + { + BlameLine blameLine = null; + Matcher m = REGEX_FIRSTPART.matcher(line); + + if (m.matches()) + { + String author = m.group(1); + + // todo extract mail + Person authorPerson = new Person(author); + String whenString = m.group(3); + + // todo parse date + Long when = null; + + blameLine = new BlameLine(authorPerson, when, m.group(2), m.group(5), + Integer.parseInt(m.group(4))); + } + + return blameLine; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private HgRepositoryHandler handler; + + /** Field description */ + private File repositoryDirectory; +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index 93dd058c7d..df53ada293 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -198,7 +198,24 @@ public class HgRepositoryHandler @Override public BlameViewer getBlameViewer(Repository repository) { - return null; + BlameViewer blameViewer = null; + + AssertUtil.assertIsNotNull(repository); + + String type = repository.getType(); + + AssertUtil.assertIsNotEmpty(type); + + if (TYPE_NAME.equals(type)) + { + blameViewer = new HgBlameViewer(this, repository); + } + else + { + throw new IllegalArgumentException("mercurial repository is required"); + } + + return blameViewer; } /**