2010-12-14 16 views
8

Estoy explorando los resultados de Google de libre (código abierto) Java diff libaries, y parecen bastantes de ellos (algunos de ellos incluso trabajan con Objetos genéricos y no solo con Strings).¿Hay una biblioteca Diff para Java que admita Anotar/Culpar?

Antes de que yo estoy cavando a través de toneladas de resultados de búsqueda y no encontrar lo que estoy buscando, voy a pedir aquí primero:

¿Algo de esas bibliotecas diff apoyar una función como anotar CVS o SVN culpa. Quiero

  • pasar la corriente String[] a una función
  • siguen pasando las versiones anteriores del String[] a una función, hasta que he agotado todos ellos, o en la biblioteca mí que hay una línea original le dice dejado unánimemente (lo último no es realmente obligatorio, pero muy útil ya que recuperar versiones anteriores de String[] es costoso, por lo que me gustaría detenerme lo antes posible)
  • llamar a una función que me da un ìnt[] que me indica cada línea de la versión actual, en qué versión fue modificada por última vez o si no se modificó (es decir, las t cambiado en la primera versión).

Tener soporte para objetos que no son String s es bueno, pero no es obligatorio. Y si la API no es exactamente así, creo que podría vivir con eso.

Si no hay nadie, ¿alguien puede sugerir una biblioteca extensible diff donde esa característica se puede agregar fácilmente, preferiblemente una que desea recibir esa característica como contribución (y no requiere toneladas de papeleo antes de aceptar contribuciones, como el proyecto GNU)? Sería voluntario (al menos intentaré) agregarlo allí, entonces.

Respuesta

2

decidí a ponerlo en práctica a mí mismo por java-diff-utils biblioteca de Dmitry Naumenko:

/* 
    Copyright 2010 Michael Schierl ([email protected]) 

    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="[email protected]">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(); 
    } 
} 

también lo envié a Dmitry Naumenko (con unos pocos casos de prueba) en caso de que quiera incluirlo.

1

Podría estar equivocado, pero creo que anotar/echarle la culpa necesita un sistema de control de versiones para funcionar, ya que necesita acceder al historial del archivo. Una biblioteca de diferencias genérica no puede hacer eso. Por lo tanto, si este es su objetivo, consulte las bibliotecas que funcionan con esos VCS, como svnkit. Si no, tal biblioteca puede ser un buen punto de partida sobre cómo se realiza la anotación/culpa, muy a menudo esto implica diferir la cadena de todas las versiones de un archivo.

+0

tengo todas las versiones anteriores del archivo, es decir no el problema (como está escrito en la pregunta). Simplemente no en un VCS que admite Blame. Y comprometerlos en un SVN temporal solo para culpar a un archivo no es lo que estoy buscando ... Y el algoritmo no es la parte difícil aquí (sí implica diferir todas las versiones y rastrear dónde van los números de línea y cuándo "desaparecerán"), es solo que necesita implementarlo :) – mihi

+1

Gotcha. Otra idea sería mirar el código fuente de wikis basados ​​en Java. Dado que tienen que implementar generalmente, el código podría estar allí. http://c2.com/cgi/wiki?JavaWikiEngines enumera algunos. – vasquez

1

Puede usar xwiki-commons-blame-api. En realidad usa code from this thread's accepted answer (Thanks to Michael Schierl for sharing this code on StackOverflow)

Se puede ver cómo usarlo en Java en it's unit tests.

O en Scala como:

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:_*) 
} 
Cuestiones relacionadas