Existe-t-il une bibliothèque Diff pour Java qui prend en charge l'annotate / blâme?

StackOverflow https://stackoverflow.com/questions/4443568

  •  10-10-2019
  •  | 
  •  

Question

Je fouille les résultats de Google (Open Source) Java Diff Libaries, et il semble que beaucoup d'entre eux travaillent même avec des objets génériques et pas seulement avec des chaînes).

Avant de creuser des tonnes de résultats de recherche et de ne pas trouver ce que je recherche, je vais demander ici en premier:

Est-ce que l'une de ces bibliothèques Diff prend en charge une fonctionnalité comme CVS Annotate ou SVN Blame. Je veux

  • passer le courant String[] à une fonction
  • Continuez à passer des versions plus anciennes du String[] À une fonction, jusqu'à ce que je les ai tous utilisés, soit la bibliothèque me dit qu'aucune ligne d'origine n'a été laissée à l'un-ci-nange (la dernière chose n'est pas vraiment un must mais très utile depuis la récupération des versions plus anciennes de la String[] est cher donc je voudrais m'arrêter le plus tôt possible)
  • Appelez une fonction qui me donne un ìnt[] Cela me dit pour chaque ligne de la version actuelle, dans quelle version elle a été modifiée pour la dernière fois ou si elle n'a pas été modifiée du tout (c'est-à-dire la dernière fois changé dans la toute première version).

Avoir une prise en charge des objets qui ne sont pas StringS c'est bien, mais pas de moi. Et si l'API n'est pas exactement comme cette façon, je suppose que je pourrais vivre avec.

S'il n'y en a pas, quelqu'un peut-il suggérer une bibliothèque Diff extensible où cette fonctionnalité peut être ajoutée facilement, de préférence une qui souhaite recevoir cette fonctionnalité comme contribution (et ne nécessite pas de paperasse Projet GNU)? Je me porterais volontaire pour (au moins essayer de) l'ajouter là-bas, alors.

Était-ce utile?

La solution

J'ai décidé de le mettre en œuvre moi-même pour Dmitry Naumenko java-diff-utils bibliothèque:

/*
   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();
    }
}

Je l'ai également envoyé à Dmitry Naumenko (avec quelques cas de test) au cas où il voudrait l'inclure.

Autres conseils

Je me trompe peut-être, mais je pense que Annotate / Blame a besoin d'un système de contrôle de version pour fonctionner, car il doit accéder à l'historique du fichier. Une bibliothèque diff générique ne peut pas faire cela. Donc, s'il s'agit de votre cible, consultez les bibliothèques qui fonctionnent avec ces VC, comme svnkit. Sinon, une telle bibliothèque peut être un bon point de départ sur la façon dont l'annotate / blâme est effectuée, cela implique très souvent de diffuser la chaîne de toutes les versions d'un fichier.

Vous pouvez utiliserXwiki-Commons-Blame-API. Il utilise réellement Le code de la réponse acceptée de ce fil (Merci à Michael Schierl pour avoir partagé ce code sur Stackoverflow)

Vous pouvez voir comment l'utiliser en java en Ce sont des tests unitaires.

Ou à Scala comme:

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:_*)
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top