
pw.prok.kdiff.diff.DiffUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kDiff Show documentation
Show all versions of kDiff Show documentation
Library for generating and applying diff/patch files
The newest version!
/*
Copyright 2010 Dmitry Naumenko ([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 pw.prok.kdiff.diff;
import pw.prok.kdiff.*;
import pw.prok.kdiff.delta.Delta;
import pw.prok.kdiff.diff.parsers.UnifedDiffParser;
import pw.prok.kdiff.diff.visitors.MappedVisitor;
import pw.prok.kdiff.diff.visitors.SimpleVisitor;
import pw.prok.kdiff.myers.MyersDiff;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Implements the difference and patching engine
*/
public class DiffUtils {
private static Pattern unifiedDiffChunkRe = Pattern
.compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@$");
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param element type
* @param patch result type
* @param original The original text. Must not be {@code null}.
* @param revised The revised text. Must not be {@code null}.
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
*/
public static > Patch diff(List original, List revised) {
return diff(original, revised, new MyersDiff());
}
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param element type
* @param patch result type
* @param original The original text. Must not be {@code null}.
* @param revised The revised text. Must not be {@code null}.
* @param equalizer the equalizer object to replace the default compare algorithm
* (Object.equals). If {@code null} the default equalizer of the
* default algorithm is used..
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
*/
public static > Patch diff(List original, List revised,
Equalizer equalizer) {
if (equalizer != null) {
return DiffUtils.diff(original, revised,
new MyersDiff(equalizer));
}
return DiffUtils.diff(original, revised, new MyersDiff());
}
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param element type
* @param patch result type
* @param original The original text. Must not be {@code null}.
* @param revised The revised text. Must not be {@code null}.
* @param algorithm The diff algorithm. Must not be {@code null}.
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
*/
public static > Patch diff(List original, List revised,
DiffAlgorithm algorithm) {
if (original == null) {
throw new IllegalArgumentException("original must not be null");
}
if (revised == null) {
throw new IllegalArgumentException("revised must not be null");
}
if (algorithm == null) {
throw new IllegalArgumentException("algorithm must not be null");
}
return algorithm.diff(original, revised);
}
/**
* Patch the original text with given patch
*
* @param element type
* @param patch result type
* @param original the original text
* @param patch the given patch
* @return the revised text
* @throws PatchFailedException if can't apply patch
*/
public static > R patch(List original, Patch patch)
throws PatchFailedException {
return patch.patch(original);
}
/**
* RestoreВ the revised text for a given patch
*
* @param element type
* @param patch result type
* @param revised the revised text
* @param patch the given patch
* @return the original text
*/
public static > R restore(List revised, Patch patch) {
return patch.restore(revised);
}
/**
* Parse the given text in unified format and creates the patch for it. Supports only one diff in file.
* For multi-filed patches see {@link #parseUnifedDiff(List)}
*
* @param diff the text in unified format
* @return the patch with deltas.
* @throws DiffException if patch cannot be patched
* @see #parseUnifedDiff(List)
*/
public static Patch> parseDiff(List diff) throws DiffException {
final SimpleVisitor visitor = new SimpleVisitor<>();
UnifedDiffParser.INSTANCE.parse(diff, visitor);
return visitor.getPatch();
}
/**
* Parse the given text in unified format and creates the patches for it.
*
* @param diff the text in unified format
* @return map of patches
* @throws DiffException if patch cannot be patched
* @see #parseUnifedDiff(List, Map)
*/
public static Map, Patch>> parseUnifedDiff(List diff) throws DiffException {
final MappedVisitor visitor = new MappedVisitor<>();
UnifedDiffParser.INSTANCE.parse(diff, visitor);
return visitor.getMap();
}
/**
* Parse the given text in unified format and creates the patches for it.
*
* @param diff the text in unified format
* @param map starting map of patches
* @return map of patches
* @throws DiffException if patch cannot be patched
* @see #parseUnifedDiff(List)
*/
public static Map, Patch>> parseUnifedDiff(List diff, Map, Patch>> map) throws DiffException {
final MappedVisitor visitor = new MappedVisitor<>(map);
UnifedDiffParser.INSTANCE.parse(diff, visitor);
return visitor.getMap();
}
private static > void processLine(List rawChunk, Patch patch, int old_ln, int new_ln) {
if (rawChunk.size() != 0) {
List oldChunkLines = new ArrayList<>();
List newChunkLines = new ArrayList<>();
for (String[] raw_line : rawChunk) {
String tag = raw_line[0];
String rest = raw_line[1];
if (tag.equals(" ") || tag.equals("-")) {
oldChunkLines.add(rest);
}
if (tag.equals(" ") || tag.equals("+")) {
newChunkLines.add(rest);
}
}
patch.addDelta(new Delta<>(new Chunk<>(
old_ln - 1, oldChunkLines), new Chunk<>(
new_ln - 1, newChunkLines)));
rawChunk.clear();
}
}
/**
* makeDiff takes a Patch and some other arguments, returning the
* Unified Diff format text representing the Patch.
*
* @param original - Filename of the original (unrevised file)
* @param revised - 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.
* @author Bill James ([email protected])
*/
public static List makeDiff(String original, String revised, List originalLines, Patch patch, int contextSize) {
if (!patch.getDeltas().isEmpty()) {
List ret = new ArrayList<>();
ret.add("--- " + original);
ret.add("+++ " + revised);
List> patchDeltas = new ArrayList<>(
patch.getDeltas());
// code outside the if block also works for single-delta issues.
List> deltas = new ArrayList<>(); // current
// list of Delta's to process
Delta delta = patchDeltas.get(0);
deltas.add(delta); // add the first Delta to the current set
// if there's more than 1 Delta, we may need to output them together
if (patchDeltas.size() > 1) {
for (int i = 1; i < patchDeltas.size(); i++) {
int position = delta.getOriginal().getPosition(); // store the current position of the first Delta
// Check if the next Delta is too close to the current position.
// And if it is, add it to the current set
Delta nextDelta = patchDeltas.get(i);
if ((position + delta.getOriginal().size() + contextSize) >= (nextDelta
.getOriginal().getPosition() - contextSize)) {
deltas.add(nextDelta);
} else {
// if it isn't, output the current set, then create
// a new set and add the current Delta to it.
List curBlock = processDeltas(originalLines,
deltas, contextSize);
ret.addAll(curBlock);
deltas.clear();
deltas.add(nextDelta);
}
delta = nextDelta;
}
}
// don't forget to process the last set of Deltas
List curBlock = processDeltas(originalLines, deltas, contextSize);
ret.addAll(curBlock);
return ret;
}
return new ArrayList<>();
}
/**
* processDeltas takes a list of Deltas and outputs them together in a
* single block of Unified-Diff-format text.
*
* @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
* @author Bill James ([email protected])
*/
private static List processDeltas(List origLines,
List> deltas, int contextSize) {
List buffer = new ArrayList<>();
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);
// NOTE: +1 to overcome the 0-offset Position
int origStart = curDelta.getOriginal().getPosition() + 1 - contextSize;
if (origStart < 1) {
origStart = 1;
}
int revStart = curDelta.getRevised().getPosition() + 1 - contextSize;
if (revStart < 1) {
revStart = 1;
}
// find the start of the wrapper context code
int contextStart = curDelta.getOriginal().getPosition() - contextSize;
if (contextStart < 0) {
contextStart = 0; // clamp to the start of the file
}
// output the context before the first Delta
for (line = contextStart; line < curDelta.getOriginal().getPosition(); line++) { //
buffer.add(" " + origLines.get(line));
origTotal++;
revTotal++;
}
// output the first Delta
buffer.addAll(getDeltaText(curDelta));
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++) {
// output the code between the last Delta and this one
buffer.add(" " + origLines.get(line));
origTotal++;
revTotal++;
}
buffer.addAll(getDeltaText(nextDelta)); // output the Delta
origTotal += nextDelta.getOriginal().getLines().size();
revTotal += nextDelta.getRevised().getLines().size();
curDelta = nextDelta;
deltaIndex++;
}
// 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
*
* @param delta - the Delta to output
* @return list of String lines of code.
* @author Bill James ([email protected])
*/
private static List getDeltaText(Delta delta) {
List buffer = new ArrayList<>();
for (String line : delta.getOriginal().getLines()) {
buffer.add("-" + line);
}
for (String line : delta.getRevised().getLines()) {
buffer.add("+" + line);
}
return buffer;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy