mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-17 11:19:15 +00:00
309 lines
12 KiB
Plaintext
Executable File
309 lines
12 KiB
Plaintext
Executable File
/*
|
|
Copyright 2009 Dmitry Naumenko (dm.naumenko@gmail.com)
|
|
|
|
This file is part of Java Diff Utils Library.
|
|
|
|
Java Diff Utils Library is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Java Diff Utils Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Java Diff Utils Library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package difflib;
|
|
|
|
import java.util.*;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import difflib.myers.*;
|
|
|
|
/**
|
|
* Implements the difference and patching engine
|
|
*
|
|
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
|
* @version 0.4.1
|
|
*/
|
|
public class DiffUtils {
|
|
private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff();
|
|
private static Pattern unifiedDiffChunkRe =
|
|
Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
|
|
|
|
/**
|
|
* Compute the difference between the original and revised texts with default diff algorithm
|
|
*
|
|
* @param original the original text
|
|
* @param revised the revised text
|
|
* @return the patch describing the difference between the original and revised texts
|
|
*/
|
|
public static Patch diff(List<?> original, List<?> revised) {
|
|
return DiffUtils.diff(original, revised, defaultDiffAlgorithm);
|
|
}
|
|
|
|
/**
|
|
* Compute the difference between the original and revised texts with given diff algorithm
|
|
*
|
|
* @param original the original text
|
|
* @param revised the revised text
|
|
* @param algorithm the given algorithm
|
|
* @return the patch describing the difference between the original and revised texts
|
|
*/
|
|
public static Patch diff(List<?> original, List<?> revised, DiffAlgorithm algorithm) {
|
|
return algorithm.diff(original, revised);
|
|
}
|
|
|
|
/**
|
|
* Patch the original text with given patch
|
|
*
|
|
* @param original the original text
|
|
* @param patch the given patch
|
|
* @return the revised text
|
|
* @throws PatchFailedException if can't apply patch
|
|
*/
|
|
public static List<?> patch(List<?> original, Patch patch) throws PatchFailedException {
|
|
return patch.applyTo(original);
|
|
}
|
|
|
|
/**
|
|
* Unpatch the revised text for a given patch
|
|
*
|
|
* @param revised the revised text
|
|
* @param patch the given patch
|
|
* @return the original text
|
|
*/
|
|
public static List<?> unpatch(List<?> revised, Patch patch) {
|
|
return patch.restore(revised); // bla-bla-bla
|
|
}
|
|
|
|
/**
|
|
* Parse the given text in unified format and creates the list of deltas for it.
|
|
*
|
|
* @param diff the text in unified format
|
|
* @return the patch with deltas.
|
|
*/
|
|
public static Patch parseUnifiedDiff(List<String> diff) {
|
|
boolean inPrelude = true;
|
|
List<Object[]> rawChunk = new ArrayList<Object[]>();
|
|
Patch patch = new Patch();
|
|
|
|
int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0;
|
|
String tag = "", rest = "";
|
|
for (String line: diff) {
|
|
// Skip leading lines until after we've seen one starting with '+++'
|
|
if (inPrelude) {
|
|
if (line.startsWith("+++")) {
|
|
inPrelude = false;
|
|
}
|
|
continue;
|
|
}
|
|
Matcher m = unifiedDiffChunkRe.matcher(line);
|
|
if (m.find()) {
|
|
// Process the lines in the previous chunk
|
|
if (rawChunk.size() != 0) {
|
|
List<String> oldChunkLines = new ArrayList<String>();
|
|
List<String> newChunkLines = new ArrayList<String>();
|
|
|
|
for (Object[] raw_line: rawChunk) {
|
|
tag = (String)raw_line[0];
|
|
rest = (String)raw_line[1];
|
|
if (tag.equals(" ") || tag.equals("-")) {
|
|
oldChunkLines.add(rest);
|
|
}
|
|
if (tag.equals(" ") || tag.equals("+")) {
|
|
newChunkLines.add(rest);
|
|
}
|
|
}
|
|
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines),
|
|
new Chunk(new_ln - 1, new_n, newChunkLines)));
|
|
rawChunk.clear();
|
|
}
|
|
// Parse the @@ header
|
|
old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1));
|
|
old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2));
|
|
new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
|
|
new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4));
|
|
old_ln = Integer.parseInt(m.group(1));
|
|
|
|
if (old_ln == 0) {
|
|
old_ln += 1;
|
|
}
|
|
if (new_ln == 0) {
|
|
new_ln += 1;
|
|
}
|
|
} else {
|
|
if (line.length() > 0) {
|
|
tag = line.substring(0, 1);
|
|
rest = line.substring(1);
|
|
if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) {
|
|
rawChunk.add(new Object[] {tag, rest});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process the lines in the last chunk
|
|
if (rawChunk.size() != 0) {
|
|
List<String> oldChunkLines = new ArrayList<String>();
|
|
List<String> newChunkLines = new ArrayList<String>();
|
|
|
|
for (Object[] raw_line: rawChunk)
|
|
{
|
|
tag = (String)raw_line[0];
|
|
rest = (String)raw_line[1];
|
|
if (tag.equals(" ") || tag.equals("-"))
|
|
{
|
|
oldChunkLines.add(rest);
|
|
}
|
|
if (tag.equals(" ") || tag.equals("+"))
|
|
{
|
|
newChunkLines.add(rest);
|
|
}
|
|
}
|
|
|
|
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines),
|
|
new Chunk(new_ln - 1, new_n, newChunkLines)));
|
|
rawChunk.clear();
|
|
}
|
|
|
|
return patch;
|
|
}
|
|
|
|
/**
|
|
* generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format text representing the Patch.
|
|
* @author Bill James (tankerbay@gmail.com)
|
|
*
|
|
* @param fname1 - Filename of the original (unrevised file)
|
|
* @param fname2 - Filename of the revised file
|
|
* @param originalLines - Lines of the original file
|
|
* @param patch - Patch created by the diff() function
|
|
* @param contextSize - number of lines of context output around each difference in the file.
|
|
* @return List of strings representing the Unified Diff representation of the Patch argument.
|
|
*/
|
|
public static List<String> generateUnifiedDiff(String fname1, String fname2, List<String> originalLines, Patch patch, int contextSize ) {
|
|
List<String> ret = new ArrayList<String>();
|
|
ret.add( "--- " + fname1 );
|
|
ret.add( "+++ " + fname2 );
|
|
|
|
List<Delta> cur = new ArrayList<Delta>(); // current list of Delta's to process
|
|
int deltact = patch.getDeltas().size();
|
|
// if there's more than 1 Delta, we may need to output them together
|
|
if ( deltact > 1 ) {
|
|
Delta curDelta = patch.getDelta(0);
|
|
cur.add( curDelta ); // add the first Delta to the current set
|
|
for ( int i = 1; i < deltact; i++ ) {
|
|
int curpos = curDelta.getOriginal().getPosition(); // store the current position of the first Delta
|
|
Delta nextDelta = patch.getDelta(i); // Check if the next Delta is too close to the current position
|
|
if ( (curpos + curDelta.getOriginal().getSize() + contextSize) >= ( nextDelta.getOriginal().getPosition()-contextSize ) ) {
|
|
cur.add( nextDelta ); // if it is, add it to the current set
|
|
} else {
|
|
List<String> curBlock = processDeltas( originalLines, cur, contextSize );
|
|
ret.addAll( curBlock ); // if it isn't, output the current set, then create a new
|
|
cur.clear(); // set and add the current Delta to it.
|
|
cur.add( nextDelta );
|
|
}
|
|
curDelta = nextDelta;
|
|
}
|
|
List<String> curBlock = processDeltas( originalLines, cur, contextSize ); // don't forget to process the last set of Deltas
|
|
ret.addAll( curBlock );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* processDeltas takes a list of Deltas and outputs them together in a single block of Unified-Diff-format text.
|
|
* @author Bill James (tankerbay@gmail.com)
|
|
*
|
|
* @param origLines - the lines of the original file
|
|
* @param deltas - the Deltas to be output as a single block
|
|
* @param contextSize - the number of lines of context to place around block
|
|
* @return
|
|
*/
|
|
private static List<String> processDeltas( List<String> origLines, List<Delta> deltas, int contextSize ) {
|
|
List<String> buffer = new ArrayList<String>();
|
|
int origTotal = 0; // counter for total lines output from Original
|
|
int revTotal = 0; // counter for total lines output from Original
|
|
int line;
|
|
|
|
Delta curDelta = deltas.get(0); // start with the first Delta
|
|
int origStart = curDelta.getOriginal().getPosition()+1 - contextSize; // note the +1 to overcome the 0-offset Position
|
|
if ( origStart < 1 ) origStart = 1; // clamp to the start of the file
|
|
int revStart = curDelta.getRevised().getPosition()+1 - contextSize; // note the +1 to overcome the 0-offset Position
|
|
if ( revStart < 1 ) revStart = 1; // clamp to the start of the file
|
|
int contextStart = curDelta.getOriginal().getPosition() - contextSize; // find the start of the wrapper context code
|
|
if ( contextStart < 0 ) contextStart = 0; // clamp to the start of the file
|
|
for ( line = contextStart; line < curDelta.getOriginal().getPosition(); line++ ) { // output the context before the first Delta
|
|
buffer.add( " " + origLines.get( line ) );
|
|
origTotal++;
|
|
revTotal++;
|
|
}
|
|
buffer.addAll( getDeltaText( curDelta ) ); // output the first Delta
|
|
origTotal += curDelta.getOriginal().getLines().size();
|
|
revTotal += curDelta.getRevised().getLines().size();
|
|
|
|
int deltaIndex = 1;
|
|
while ( deltaIndex < deltas.size() ) { // for each of the other Deltas
|
|
Delta nextDelta = deltas.get( deltaIndex );
|
|
int intermediateStart = curDelta.getOriginal().getPosition() + curDelta.getOriginal().getLines().size();
|
|
for ( line = intermediateStart; line < nextDelta.getOriginal().getPosition(); line++ ) {
|
|
buffer.add( " " + origLines.get( line ) ); // output the code between the last Delta and this one
|
|
origTotal++;
|
|
revTotal++;
|
|
}
|
|
buffer.addAll( getDeltaText( nextDelta ) ); // output the Delta
|
|
origTotal += nextDelta.getOriginal().getLines().size();
|
|
revTotal += nextDelta.getRevised().getLines().size();
|
|
curDelta = nextDelta;
|
|
deltaIndex++; // increment the iterator
|
|
}
|
|
|
|
// Now output the post-Delta context code, clamping the end of the file
|
|
contextStart = curDelta.getOriginal().getPosition() + curDelta.getOriginal().getLines().size();
|
|
for ( line = contextStart; ( line < (contextStart + contextSize )) && ( line < origLines.size() ); line++ ) {
|
|
buffer.add( " " + origLines.get( line ) );
|
|
origTotal++;
|
|
revTotal++;
|
|
}
|
|
|
|
// Create and insert the block header, conforming to the Unified Diff standard
|
|
StringBuffer header = new StringBuffer();
|
|
header.append( "@@ -" );
|
|
header.append( origStart );
|
|
header.append( "," );
|
|
header.append( origTotal );
|
|
header.append( " +" );
|
|
header.append( revStart );
|
|
header.append( "," );
|
|
header.append( revTotal );
|
|
header.append( " @@" );
|
|
buffer.add( 0, header.toString() );
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
|
|
* @author Bill James (tankerbay@gmail.com)
|
|
*
|
|
* @param delta - the Delta to output
|
|
* @return list of String lines of code.
|
|
*/
|
|
private static List<String> getDeltaText( Delta delta ) {
|
|
List<String> buffer = new ArrayList<String>();
|
|
for ( Object line: delta.getOriginal().getLines() ) {
|
|
buffer.add( "-" + line );
|
|
}
|
|
for ( Object line: delta.getRevised().getLines() ) {
|
|
buffer.add( "+" + line );
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
}
|