Существует ли библиотека Diff для Java, которая поддерживает Annotate / Blame? - PullRequest
8 голосов
/ 14 декабря 2010

Я копаю результаты Google для бесплатных (с открытым исходным кодом) Java-библиотек различий, и, похоже, их довольно много (некоторые из них даже работают со стандартными объектами, а не только со строками).

Прежде чем копаться в тоннах результатов поиска и не найти то, что я ищу, я сначала задам вопрос:

Поддерживает ли какая-либо из этих библиотек сравнения такую ​​функцию, как cvs annotate или svn blame.Я хочу

  • передать текущий String[] функции
  • и продолжить передачу старых версий String[] в функцию, пока я не израсходую все из них,или библиотека сообщает мне, что ни одна оригинальная строка не была оставлена ​​без аннотации (последнее, что на самом деле не является обязательным, но очень полезным, так как получение более старых версий String[] стоит дорого, поэтому я хотел бы остановиться как можно раньше)
  • вызывает функцию, которая дает мне ìnt[], которая сообщает мне для каждой строки текущей версии, в какой версии она была изменена последней или не была ли она вообще изменена (т.е. последняя изменена в самой первой версии).

Поддерживать объекты, которые не String s, приятно, но не обязательно.И если API не совсем такой, думаю, я бы с этим смирился.

Если его нет, кто-нибудь может предложить расширяемую библиотеку diff, в которую можно легко добавить эту функцию, предпочтительно такую, которая бы хотелаполучить эту функцию в качестве вклада (и не требует заполнения тонны документов до того, как они примут вклады, как проект GNU)?Тогда я бы добровольно (хотя бы попытался) добавить его туда.

Ответы [ 3 ]

2 голосов
/ 27 декабря 2010

Я решил реализовать это сам для библиотеки Дмитрия Науменко java-diff-utils :

/*
   Copyright 2010 Michael Schierl (schierlm@gmx.de)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package difflib.annotate;

import java.util.*;

import difflib.*;

/**
 * Generates an annotated version of a revision based on a list of older
 * revisions, like <tt>cvs annotate</tt> or <tt>svn blame</tt>.
 * 
 * @author <a href="schierlm@gmx.de">Michael Schierl</a>
 * 
 * @param <R>
 *            Type of the revision metadata
 */
public class Annotate<R> {

    private final List<R> revisions;
    private final int[] lineNumbers;
    private R currentRevision;
    private final List<Object> currentLines;
    private final List<Integer> currentLineMap;

    /**
     * Creates a new annotation generator.
     * 
     * @param revision
     *            Revision metadata for the revision to be annotated
     * @param targetLines
     *            Lines of the revision to be annotated
     */
    public Annotate(R revision, List<?> targetLines) {
        revisions = new ArrayList<R>();
        lineNumbers = new int[targetLines.size()];
        currentRevision = revision;
        currentLines = new ArrayList<Object>(targetLines);
        currentLineMap = new ArrayList<Integer>();
        for (int i = 0; i < lineNumbers.length; i++) {
            lineNumbers[i] = -1;
            revisions.add(null);
            currentLineMap.add(i);
        }
    }

    /**
     * Check whether there are still lines that are unannotated. In that case,
     * more older revisions should be retrieved and passed to the function. Note
     * that as soon as you pass an empty revision, all lines will be annotated
     * (with a later revision), therefore if you do not have any more revisions,
     * pass an empty revision to annotate the rest of the lines.
     */
    public boolean areLinesUnannotated() {
        for (int i = 0; i < lineNumbers.length; i++) {
            if (lineNumbers[i] == -1 || revisions.get(i) == null)
                return true;
        }
        return false;
    }

    /**
     * Add the previous revision and update annotation info.
     * 
     * @param revision
     *            Revision metadata for this revision
     * @param lines
     *            Lines of this revision
     */
    public void addRevision(R revision, List<?> lines) {
        Patch patch = DiffUtils.diff(currentLines, lines);
        int lineOffset = 0; // remember number of already deleted/added lines
        for (Delta d : patch.getDeltas()) {
            Chunk original = d.getOriginal();
            Chunk revised = d.getRevised();
            int pos = original.getPosition() + lineOffset;
            // delete lines
            for (int i = 0; i < original.getSize(); i++) {
                int origLine = currentLineMap.remove(pos);
                currentLines.remove(pos);
                if (origLine != -1) {
                    lineNumbers[origLine] = original.getPosition() + i;
                    revisions.set(origLine, currentRevision);
                }
            }
            for (int i = 0; i < revised.getSize(); i++) {
                currentLines.add(pos + i, revised.getLines().get(i));
                currentLineMap.add(pos + i, -1);
            }
            lineOffset += revised.getSize() - original.getSize();
        }

        currentRevision = revision;
        if (!currentLines.equals(lines))
            throw new RuntimeException("Patch application failed");
    }

    /**
     * Return the result of the annotation. It will be a List of the same length
     * as the target revision, for which every entry states the revision where
     * the line appeared last.
     */
    public List<R> getAnnotatedRevisions() {
        return Collections.unmodifiableList(revisions);
    }

    /**
     * Return the result of the annotation. It will be a List of the same length
     * as the target revision, for which every entry states the line number in
     * the revision where the line appeared last.
     */
    public int[] getAnnotatedLineNumbers() {
        return (int[]) lineNumbers.clone();
    }
}

Я также отправил ее Дмитрию Науменко (с несколькими тестовыми примерами) на случайон хочет включить это.

1 голос
/ 21 февраля 2015

Вы можете использовать XWiki-Обще-пенять-апи . На самом деле он использует код из принятого ответа этой ветки ( Спасибо Михаэлю Ширлу за предоставление этого кода в StackOverflow )

Вы можете увидеть, как использовать его в Java в это модульные тесты.

Или в Scala, например:

import java.util
import org.xwiki.blame.AnnotatedContent
import org.xwiki.blame.internal.DefaultBlameManager

case class Revision(id: Int,
                    parentId: Option[Int] = None,
                    content: Option[String] = None)

def createAnnotation(revisions: Seq[Revision]): Option[AnnotatedContent[Revision, String]] = {
    val blameManager = new DefaultBlameManager()

    val annotatedContent = revisions.foldLeft(null.asInstanceOf[AnnotatedContent[Revision, String]]){
      (annotation, revision) =>
        blameManager.blame(annotation, revision, splitByWords(revision.content))
    }
    Option(annotatedContent)
}

def splitByWords(content: Option[String]): util.List[String] = {
    val array = content.fold(Array[String]())(_.split("[^\\pL_\\pN]+"))
    util.Arrays.asList(array:_*)
}
1 голос
/ 23 декабря 2010

Возможно, я ошибаюсь, но я думаю, что annotate / blame нуждается в системе контроля версий, чтобы получить доступ к истории файла. Универсальная библиотека diff не может этого сделать. Так что, если это ваша цель, проверьте библиотеки, которые работают с этими VCS, например svnkit . Если нет, такая библиотека может быть хорошей отправной точкой для того, как делается аннотирование / обвинение, очень часто это включает в себя определение цепочки всех версий файла.

...