утренние изменения

удаление эталона и роль студента
This commit is contained in:
2025-03-26 16:12:00 +03:00
parent 1ae4d1980a
commit 19afec4d25
62 changed files with 576 additions and 1098 deletions

View File

@@ -10,8 +10,8 @@ public class EnvironmentJson {
name = src.name;
value = src.value;
}
public EnvironmentJson(String name_in,String value_in){
name=name_in;
value=value_in;
public EnvironmentJson(String name_in, String value_in) {
name = name_in;
value = value_in;
}
}

View File

@@ -1,8 +1,6 @@
package _VisualDVM.GlobalData.CompilerOption.Json;
import _VisualDVM.GlobalData.CompilerOption.CompilerOption;
import com.google.gson.annotations.Expose;
import java.io.Serializable;
public class OptionJson {
@Expose
public String name; //в том числе и с разделителем если есть. поиск по startswith
@@ -11,8 +9,8 @@ public class OptionJson {
public OptionJson(CompilerOption src) {
this(src.name + src.parameterSeparator, src.parameterValue);
}
public OptionJson(String name_in, String value_in){
name=name_in;
value= value_in;
public OptionJson(String name_in, String value_in) {
name = name_in;
value = value_in;
}
}

View File

@@ -2,7 +2,6 @@ package _VisualDVM.GlobalData.CompilerOption.Json;
import Common.Utils.Utils_;
import com.google.gson.annotations.Expose;
import java.io.Serializable;
import java.util.List;
import java.util.Vector;
public class OptionsJson {

View File

@@ -1,10 +1,9 @@
package _VisualDVM.GlobalData.CompilerOption.Json;
import com.google.gson.annotations.Expose;
import java.io.Serializable;
import java.util.List;
import java.util.Vector;
public class OptionsSetJson {
public class OptionsSetJson {
@Expose
public List<OptionsJson> values = new Vector<>();
public OptionsSetJson() {

View File

@@ -148,7 +148,7 @@ public class MainModule extends MainModule_<GlobalDatabase, MainUI> {
for (PassCode code : accountRoleDependentPasses)
getPass(code).setControlsVisible(false);
}
public void SetStudentPassesAccess(){
public void SetStudentPassesAccess() {
SetUserPassesAccess();
}
public void SetDeveloperPassesAccess() {

View File

@@ -1,12 +1,9 @@
package _VisualDVM.Passes.All;
import Common.CommonConstants;
import Common.Passes.Pass;
import Common.Utils.Utils_;
import Common.Visual.UI;
import _VisualDVM.Global;
import _VisualDVM.TestingSystem.SAPFOR.SapforPackage.SapforPackage;
import java.util.Vector;
public class CompareSapforPackageToEthalon extends CompareSapforPackages {
@Override
protected boolean canStart(Object... args) throws Exception {
@@ -28,8 +25,8 @@ public class CompareSapforPackageToEthalon extends CompareSapforPackages {
master = ethalon;
slave = sapforPackage;
return true;
}else
return UI.Question("Отмечено более одного пакета. Желаете сравнить их")&&super.canStart(args);
} else
return UI.Question("Отмечено более одного пакета. Желаете сравнить их") && super.canStart(args);
}
}

View File

@@ -1,7 +1,6 @@
package _VisualDVM.Passes.All;
import Common.CommonConstants;
import _VisualDVM.Global;
public class DropSapforConfigurationEthalon extends EditSapforConfiguration{
public class DropSapforConfigurationEthalon extends EditSapforConfiguration {
@Override
public String getIconPath() {
return "/Common/icons/Clean.png";

View File

@@ -45,7 +45,6 @@ public class TestPass extends Pass {
@Override
protected void body() throws Exception {
List<Integer> numbers = new Vector_<>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Use parallelStream() to create a parallel stream
ForkJoinPool commonPool = ForkJoinPool.commonPool();
commonPool.submit(() ->
@@ -57,7 +56,6 @@ public class TestPass extends Pass {
).join();
System.out.println("DONE");
//----
DiffRowGenerator generator = DiffRowGenerator.create()
.showInlineDiffs(true)
.inlineDiffByWord(true)
@@ -67,7 +65,6 @@ public class TestPass extends Pass {
List<DiffRow> rows = generator.generateDiffRows(
Arrays.asList("This is a test senctence.", "This is the second line.", "And here is the finish."),
Arrays.asList("This is a test for diffutils.", "This is the second line."));
System.out.println("|original|new|");
System.out.println("|--------|---|");
for (DiffRow row : rows) {

View File

@@ -361,8 +361,7 @@ public enum PassCode implements PassCode_ {
ComponentsServerBackUp,
TestingServerBackUp,
CompareDVMRunTaskToEthalon,
DropSapforConfigurationEthalon
;
DropSapforConfigurationEthalon;
//--
@Override
public String getDescription() {

View File

@@ -5,7 +5,6 @@ import _VisualDVM.TestingSystem.DVM.DVMCompilationOptionsSet.DVMCompilationOptio
import _VisualDVM.TestingSystem.DVM.DVMEnvironment.DVMEnvironment;
import _VisualDVM.TestingSystem.DVM.DVMEnvironmentsSet.DVMEnvironmentsSet;
import _VisualDVM.TestingSystem.DVM.DVMSettings.DVMSettings;
import javafx.util.Pair;
import java.util.Vector;
public class DVMSettingsCache extends VisualCache {
@@ -16,10 +15,10 @@ public class DVMSettingsCache extends VisualCache {
//-->>
Vector<String> optionsSummary_ = new Vector<>();
Vector<DVMCompilationOptionsSet> optionsSets = Global.testingServer.db.getVectorByFK(dvmSettings, DVMCompilationOptionsSet.class);
for (DVMCompilationOptionsSet optionsSet: optionsSets){
for (DVMCompilationOptionsSet optionsSet : optionsSets) {
Vector<String> optionsValues = new Vector<>();
Vector<DVMCompilationOption> options = Global.testingServer.db.getVectorByFK(optionsSet, DVMCompilationOption.class);
for (DVMCompilationOption option: options){
for (DVMCompilationOption option : options) {
optionsValues.add(option.print());
}
optionsSummary_.add(String.join(" ", optionsValues));
@@ -27,11 +26,11 @@ public class DVMSettingsCache extends VisualCache {
optionsSummary = String.join(";\n", optionsSummary_);
//-->>
Vector<String> environmentsSummary_ = new Vector<>();
Vector<DVMEnvironmentsSet> environmentsSets = Global.testingServer.db.getVectorByFK(dvmSettings,DVMEnvironmentsSet.class);
for (DVMEnvironmentsSet environmentsSet: environmentsSets){
Vector<DVMEnvironmentsSet> environmentsSets = Global.testingServer.db.getVectorByFK(dvmSettings, DVMEnvironmentsSet.class);
for (DVMEnvironmentsSet environmentsSet : environmentsSets) {
Vector<String> environmentsValues = new Vector<>();
Vector<DVMEnvironment> environments = Global.testingServer.db.getVectorByFK(environmentsSet, DVMEnvironment.class);
for (DVMEnvironment environment: environments){
for (DVMEnvironment environment : environments) {
environmentsValues.add(environment.print());
}
environmentsSummary_.add(String.join(" ", environmentsValues));

View File

@@ -105,6 +105,18 @@ public class TestsDatabase extends SQLiteDatabase {
public TestsDatabase() {
super(Paths.get(System.getProperty("user.dir"), "Data", Constants.tests_db_name + ".sqlite").toFile());
}
public static String printOptionsLine(Vector<DVMCompilationOption> options) {
Vector<String> res = new Vector<>();
for (DVMCompilationOption option : options)
res.add(option.print());
return String.join(" ", res);
}
public static String printEnvironmentsLine(Vector<DVMEnvironment> environments) {
Vector<String> res = new Vector<>();
for (DVMEnvironment environment : environments)
res.add(environment.print());
return String.join(" ", res);
}
@Override
protected void initAllTables() throws Exception {
addTable(groups = new GroupsDBTable());
@@ -702,18 +714,6 @@ public class TestsDatabase extends SQLiteDatabase {
Commit();
*/
}
public static String printOptionsLine(Vector<DVMCompilationOption> options) {
Vector<String> res = new Vector<>();
for (DVMCompilationOption option : options)
res.add(option.print());
return String.join(" ", res);
}
public static String printEnvironmentsLine(Vector<DVMEnvironment> environments) {
Vector<String> res = new Vector<>();
for (DVMEnvironment environment : environments)
res.add(environment.print());
return String.join(" ", res);
}
public Vector<Pair<String, String>> getTasksParameters(DVMSettings dvmSettings_in) {
//уравниваем количество наборов опций и окружений и сопоставляем 1 к 1
Vector<Pair<String, String>> res = new Vector<>();

View File

@@ -5,22 +5,23 @@ import Common.Database.Objects.iDBObject;
import Common.Utils.Utils_;
public class DVMCompilationOption extends iDBObject {
public String name = "";
public String value= "";
public String value = "";
public int dvmcompilationoptionsset_id = CommonConstants.Nan;
public DVMCompilationOption(String name_in, String value_in) {
name = name_in;
value = value_in;
}
public DVMCompilationOption() {
}
@Override
public void SynchronizeFields(DBObject src) {
super.SynchronizeFields(src);
DVMCompilationOption src_ = (DVMCompilationOption) src;
name = src_.name;
value=src_.value;
value = src_.value;
dvmcompilationoptionsset_id = src_.dvmcompilationoptionsset_id;
}
public DVMCompilationOption(){}
public String print(){
return name + (value.contains(" ") ? Utils_.DQuotes(value) : value);
public String print() {
return name + (value.contains(" ") ? Utils_.DQuotes(value) : value);
}
}

View File

@@ -1,13 +1,5 @@
package _VisualDVM.TestingSystem.DVM.DVMCompilationOption;
import Common.Database.Objects.DBObject;
import Common.Database.Tables.FKBehaviour;
import Common.Database.Tables.FKCurrentObjectBehaviuor;
import Common.Database.Tables.FKDataBehaviour;
import Common.Database.Tables.iDBTable;
import _VisualDVM.TestingSystem.DVM.DVMCompilationOptionsSet.DVMCompilationOptionsSet;
import _VisualDVM.TestingSystem.DVM.DVMConfigurationSettings.DVMConfigurationSettings;
import java.util.LinkedHashMap;
public class DVMCompilationOptionsDBTable extends iDBTable<DVMCompilationOption> {
public DVMCompilationOptionsDBTable() {
super(DVMCompilationOption.class);

View File

@@ -11,15 +11,15 @@ public class DVMCompilationOptionsSet extends iDBObject {
public int dvmsettings_id = CommonConstants.Nan;
@Description("IGNORE")
public Vector<DVMCompilationOption> options = null;
public DVMCompilationOptionsSet() {
}
public DVMCompilationOptionsSet(DVMSettings settings_in) {
dvmsettings_id = settings_in.id;
}
@Override
public void SynchronizeFields(DBObject src) {
super.SynchronizeFields(src);
DVMCompilationOptionsSet src_ = (DVMCompilationOptionsSet) src;
dvmsettings_id = src_.dvmsettings_id;
}
public DVMCompilationOptionsSet() {
}
public DVMCompilationOptionsSet(DVMSettings settings_in) {
dvmsettings_id = settings_in.id;
}
}

View File

@@ -4,10 +4,8 @@ import Common.Database.Tables.FKBehaviour;
import Common.Database.Tables.FKCurrentObjectBehaviuor;
import Common.Database.Tables.FKDataBehaviour;
import Common.Database.Tables.iDBTable;
import Common.Visual.DataSetControlForm;
import _VisualDVM.TestingSystem.DVM.DVMCompilationOption.DVMCompilationOption;
import javax.swing.*;
import java.util.LinkedHashMap;
public class DVMCompilationOptionsSetsDBTable extends iDBTable<DVMCompilationOptionsSet> {
public DVMCompilationOptionsSetsDBTable() {

View File

@@ -11,6 +11,8 @@ public class DVMEnvironment extends iDBObject {
name = name_in;
value = value_in;
}
public DVMEnvironment() {
}
@Override
public void SynchronizeFields(DBObject src) {
super.SynchronizeFields(src);
@@ -19,8 +21,6 @@ public class DVMEnvironment extends iDBObject {
value = src_.value;
dvmenvironmentsset_id = src_.dvmenvironmentsset_id;
}
public DVMEnvironment() {
}
public String print() {
return name + "=" + Utils_.DQuotes(value);
}

View File

@@ -1,12 +1,5 @@
package _VisualDVM.TestingSystem.DVM.DVMEnvironment;
import Common.Database.Objects.DBObject;
import Common.Database.Tables.FKBehaviour;
import Common.Database.Tables.FKCurrentObjectBehaviuor;
import Common.Database.Tables.FKDataBehaviour;
import Common.Database.Tables.iDBTable;
import _VisualDVM.TestingSystem.DVM.DVMCompilationOption.DVMCompilationOption;
import java.util.LinkedHashMap;
public class DVMEnvironmentsDBTable extends iDBTable<DVMEnvironment> {
public DVMEnvironmentsDBTable() {
super(DVMEnvironment.class);

View File

@@ -8,19 +8,18 @@ import com.sun.org.glassfish.gmbal.Description;
import java.util.Vector;
public class DVMEnvironmentsSet extends iDBObject {
public int dvmsettings_id=CommonConstants.Nan;
public int dvmsettings_id = CommonConstants.Nan;
@Description("IGNORE")
public Vector<DVMEnvironment> environments = null;
public DVMEnvironmentsSet(DVMSettings dvmSettings){
public DVMEnvironmentsSet(DVMSettings dvmSettings) {
dvmsettings_id = dvmSettings.id;
}
public DVMEnvironmentsSet(){
public DVMEnvironmentsSet() {
}
@Override
public void SynchronizeFields(DBObject src) {
super.SynchronizeFields(src);
DVMEnvironmentsSet src_= (DVMEnvironmentsSet) src;
DVMEnvironmentsSet src_ = (DVMEnvironmentsSet) src;
dvmsettings_id = src_.dvmsettings_id;
}
}

View File

@@ -11,8 +11,6 @@ import _VisualDVM.GlobalData.Machine.Machine;
import _VisualDVM.GlobalData.Machine.MachineType;
import _VisualDVM.GlobalData.Tasks.TaskState;
import _VisualDVM.GlobalData.User.User;
import _VisualDVM.ServerObjectsCache.DVMSettingsCache;
import _VisualDVM.ServerObjectsCache.VisualCaches;
import _VisualDVM.TestingSystem.Common.Configuration.Configuration;
import _VisualDVM.TestingSystem.Common.Group.Group;
import _VisualDVM.TestingSystem.Common.TasksPackageState;

View File

@@ -18,6 +18,6 @@ public class DVMPackageConfiguration extends iDBObject {
super.SynchronizeFields(src);
DVMPackageConfiguration src_ = (DVMPackageConfiguration) src;
dvmconfiguration_id = src_.dvmconfiguration_id;
dvmpackage_id =src_.dvmpackage_id;
dvmpackage_id = src_.dvmpackage_id;
}
}

View File

@@ -1,7 +1,5 @@
package _VisualDVM.TestingSystem.DVM.DVMSettings;
import Common.Database.Objects.DBObject;
import _VisualDVM.GlobalData.CompilerEnvironment.Json.EnvironmentsSetJson;
import _VisualDVM.GlobalData.CompilerOption.Json.OptionsSetJson;
import _VisualDVM.GlobalData.RunConfiguration.RunConfiguration;
import _VisualDVM.TestingSystem.Common.Settings.Settings;
import com.sun.org.glassfish.gmbal.Description;

View File

@@ -1,13 +1,11 @@
package _VisualDVM.TestingSystem.DVM.DVMSettings.UI;
import Common.MainModule_;
import Common.Utils.Utils_;
import Common.Visual.TextField.StyledTextField;
import Common.Visual.Windows.Dialog.DialogFields;
import _VisualDVM.GlobalData.CompilerEnvironment.EnvironmentsLinesSet;
import _VisualDVM.GlobalData.CompilerEnvironment.Json.EnvironmentsSetJson;
import _VisualDVM.GlobalData.CompilerOption.Json.OptionsSetJson;
import _VisualDVM.GlobalData.CompilerOption.OptionsLinesSet;
import _VisualDVM.TestingSystem.DVM.DVMCompilationOptionsSet.DVMCompilationOptionsSet;
import javax.swing.*;
import java.awt.*;

View File

@@ -9,8 +9,8 @@ public class SapforConfigurationFields implements DialogFields {
public JTextField tfName;
public JSpinner sTransformationMaxtime;
public JSpinner sKernels;
private JPanel content;
public JCheckBox cbGroupsOnly;
private JPanel content;
public SapforConfigurationFields() {
sKernels.setModel(new SpinnerNumberModel(1, 1,
Constants.testingMaxKernels,

View File

@@ -16,7 +16,7 @@ public class SapforConfigurationSettings extends iDBObject {
@Override
public void SynchronizeFields(DBObject src) {
super.SynchronizeFields(src);
SapforConfigurationSettings src_= (SapforConfigurationSettings) src;
SapforConfigurationSettings src_ = (SapforConfigurationSettings) src;
sapforconfiguration_id = src_.sapforconfiguration_id;
sapforsettings_id = src_.sapforsettings_id;
}

View File

@@ -130,7 +130,7 @@ public class SapforPackagesForm extends RDataSetControlForm<SapforPackage> {
addSeparator();
addPasses(PassCode.AbortSapforPackage);
addSeparator();
addPasses(PassCode.CompareSapforPackageToEthalon,PassCode.JoinSapforTestingVersionsToGroup);
addPasses(PassCode.CompareSapforPackageToEthalon, PassCode.JoinSapforTestingVersionsToGroup);
addSeparator();
addPasses(PassCode.DeleteSapforPackage);
}

View File

@@ -2,7 +2,6 @@ package _VisualDVM.TestingSystem.SAPFOR.SapforPackageConfiguration;
import Common.CommonConstants;
import Common.Database.Objects.DBObject;
import Common.Database.Objects.iDBObject;
import _VisualDVM.TestingSystem.DVM.DVMPackageConfiguration.DVMPackageConfiguration;
import _VisualDVM.TestingSystem.SAPFOR.SapforConfiguration.SapforConfiguration;
public class SapforPackageConfiguration extends iDBObject {
public int sapforpackage_id = CommonConstants.Nan;
@@ -19,6 +18,6 @@ public class SapforPackageConfiguration extends iDBObject {
super.SynchronizeFields(src);
SapforPackageConfiguration src_ = (SapforPackageConfiguration) src;
sapforconfiguration_id = src_.sapforconfiguration_id;
sapforpackage_id =src_.sapforpackage_id;
sapforpackage_id = src_.sapforpackage_id;
}
}

View File

@@ -129,7 +129,7 @@ public class MainUI extends UIModule_ {
Global.normalProperties.AutoCheckTesting = false;
Global.normalProperties.Update();
}
void showStudentRights(){
void showStudentRights() {
Global.mainModule.SetUserPassesAccess();
getMainWindow().ShowStudentTabs();
getTestingMenuBar().showServerAdminLabel(false);

View File

@@ -5,10 +5,10 @@ public class VersionsComparisonMenu extends PropertiesSubmenu {
public VersionsComparisonMenu() {
super("Сравнение версий", null,
Global.normalProperties,
// "RegisterOn",
// "SpacesOn",
// "EmptyLinesOn",
// "FortranWrapsOn",
// "RegisterOn",
// "SpacesOn",
// "EmptyLinesOn",
// "FortranWrapsOn",
"ExtensionsOn",
"ComparsionDiffMergeOn"
);

View File

@@ -15,13 +15,14 @@ import javafx.util.Pair;
import org.fife.ui.rtextarea.RTextScrollPane;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Highlighter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Vector;
public abstract class ComparisonForm<T> {
final String separator = "\u200B";
final char cseparator = '\u200B';
public Class<T> t; //класс объектов.
//-->>
public Vector<String> lines = new Vector<>(); //строки с учетом/неучетом пробелов. для сравнения
@@ -34,6 +35,10 @@ public abstract class ComparisonForm<T> {
protected T object = null;
//-->>
protected BaseEditor Body;
//невидимый пробел https://translated.turbopages.org/proxy_u/en-ru.ru.898e1daf-67e318c0-3fccff8a-74722d776562/https/stackoverflow.com/questions/17978720/invisible-characters-ascii
//--->>
// protected Object ownScrollModel = null;
protected Vector<Pair<Integer, Integer>> diffs = new Vector<>();
//-->>
ComparisonForm<T> this_ = null; //?
ComparisonForm<T> slave = null;
@@ -48,22 +53,6 @@ public abstract class ComparisonForm<T> {
//-----
private boolean events_on = false;//относится только к мастеру, отвечает за скроллы.
private int current_diff_num = -1;
final String separator = "\u200B";
final char cseparator = '\u200B';
//невидимый пробел https://translated.turbopages.org/proxy_u/en-ru.ru.898e1daf-67e318c0-3fccff8a-74722d776562/https/stackoverflow.com/questions/17978720/invisible-characters-ascii
//--->>
// protected Object ownScrollModel = null;
protected Vector<Pair<Integer, Integer>> diffs = new Vector<>();
//---<<
private void ShowCurrentDiff() {
try {
int diff_line = Body.getLineOfOffset(diffs.get(current_diff_num).getKey());
Body.gotoLine_(diff_line);
}
catch (Exception ex){
ex.printStackTrace();
}
}
public ComparisonForm(Class<T> t_in, ComparisonForm<T> slave_in) {
//-
Body = new BaseEditor();
@@ -130,6 +119,15 @@ public abstract class ComparisonForm<T> {
}
});
}
//---<<
private void ShowCurrentDiff() {
try {
int diff_line = Body.getLineOfOffset(diffs.get(current_diff_num).getKey());
Body.gotoLine_(diff_line);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public JPanel getContent() {
return content;
}
@@ -176,7 +174,7 @@ public abstract class ComparisonForm<T> {
protected void Compare() throws Exception {
events_on = false;
current_diff_num = CommonConstants.Nan;
slave.current_diff_num =CommonConstants.Nan;
slave.current_diff_num = CommonConstants.Nan;
//-----------------------------------------------------------------------------------------------
Body.setText("");
slave.Body.setText("");
@@ -217,31 +215,30 @@ public abstract class ComparisonForm<T> {
diffs.clear();
boolean flag = false;
char[] chars = Body.getText().toCharArray();
int dif_start=CommonConstants.Nan;
int dif_end=CommonConstants.Nan;
int dif_start = CommonConstants.Nan;
int dif_end = CommonConstants.Nan;
for (int i = 0; i < chars.length; ++i) {
char c = chars[i];
//--
if (flag) {
//различие
switch (c){
switch (c) {
case cseparator:
//кончилось различие
dif_end =i;
flag=false;
diffs.add(new Pair<>(dif_start,dif_end));
dif_start=CommonConstants.Nan;
dif_end=CommonConstants.Nan;
dif_end = i;
flag = false;
diffs.add(new Pair<>(dif_start, dif_end));
dif_start = CommonConstants.Nan;
dif_end = CommonConstants.Nan;
break;
}
} else {
//поиск
switch (c) {
case cseparator:
//началось различие
dif_start =i;
flag=true;
dif_start = i;
flag = true;
break;
default:
break;
@@ -249,18 +246,18 @@ public abstract class ComparisonForm<T> {
}
}
if (!diffs.isEmpty())
current_diff_num=0;
current_diff_num = 0;
}
public void colorDiffs() throws Exception{
Highlighter.HighlightPainter painter = isMaster()? SPFEditor.RedTextPainter: SPFEditor.GreenTextPainter;
for (Pair<Integer, Integer> diff: diffs){
Body.getHighlighter().addHighlight(diff.getKey(),diff.getValue(), painter);
public void colorDiffs() throws Exception {
Highlighter.HighlightPainter painter = isMaster() ? SPFEditor.RedTextPainter : SPFEditor.GreenTextPainter;
for (Pair<Integer, Integer> diff : diffs) {
Body.getHighlighter().addHighlight(diff.getKey(), diff.getValue(), painter);
}
}
public void Show() throws Exception {
events_on = false;
current_diff_num = CommonConstants.Nan;
slave.current_diff_num =CommonConstants.Nan;
slave.current_diff_num = CommonConstants.Nan;
//----------------------------------------------------------------------------------------------
Body.setText("");
slave.Body.setText("");

View File

@@ -14,7 +14,6 @@
* limitations under the License.
*/
package com.github.difflib;
import com.github.difflib.algorithm.DiffAlgorithmFactory;
import com.github.difflib.algorithm.DiffAlgorithmI;
import com.github.difflib.algorithm.DiffAlgorithmListener;
@@ -22,23 +21,19 @@ import com.github.difflib.algorithm.myers.MyersDiff;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.Patch;
import com.github.difflib.patch.PatchFailedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.*;
import java.util.function.BiPredicate;
/**
* Utility class to implement the difference and patching engine.
*/
public final class DiffUtils {
/**
* This factory generates the DEFAULT_DIFF algorithm for all these routines.
*/
static DiffAlgorithmFactory DEFAULT_DIFF = MyersDiff.factory();
private DiffUtils() {
}
/**
* Sets the default diff algorithm factory to be used by all diff routines.
*
@@ -47,51 +42,47 @@ public final class DiffUtils {
public static void withDefaultDiffAlgorithmFactory(DiffAlgorithmFactory factory) {
DEFAULT_DIFF = factory;
}
/**
* Computes the difference between two sequences of elements using the default diff algorithm.
*
* @param <T> a generic representing the type of the elements to be compared.
* @param <T> a generic representing the type of the elements to be compared.
* @param original a {@link List} representing the original sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @param progress a {@link DiffAlgorithmListener} representing the progress listener. Can be {@code null}.
* @return The patch describing the difference between the original and revised sequences. Never {@code null}.
*/
public static <T> Patch<T> diff(List<T> original, List<T> revised, DiffAlgorithmListener progress) {
return DiffUtils.diff(original, revised, DEFAULT_DIFF.create(), progress);
}
/**
* Computes the difference between two sequences of elements using the default diff algorithm.
*
* @param <T> a generic representing the type of the elements to be compared.
* @param <T> a generic representing the type of the elements to be compared.
* @param original a {@link List} representing the original sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @return The patch describing the difference between the original and revised sequences. Never {@code null}.
*/
public static <T> Patch<T> diff(List<T> original, List<T> revised) {
return DiffUtils.diff(original, revised, DEFAULT_DIFF.create(), null);
}
/**
* Computes the difference between two sequences of elements using the default diff algorithm.
*
* @param <T> a generic representing the type of the elements to be compared.
* @param original a {@link List} representing the original sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @param <T> a generic representing the type of the elements to be compared.
* @param original a {@link List} representing the original sequence of elements. Must not be {@code null}.
* @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}.
* @param includeEqualParts a {@link boolean} representing whether to include equal parts in the resulting patch.
* @return The patch describing the difference between the original and revised sequences. Never {@code null}.
*/
public static <T> Patch<T> diff(List<T> original, List<T> revised, boolean includeEqualParts) {
return DiffUtils.diff(original, revised, DEFAULT_DIFF.create(), null, includeEqualParts);
}
/**
* Computes the difference between two strings using the default diff algorithm.
*
* @param sourceText a {@link String} representing the original string. Must not be {@code null}.
* @param targetText a {@link String} representing the revised string. Must not be {@code null}.
* @param progress a {@link DiffAlgorithmListener} representing the progress listener. Can be {@code null}.
* @param progress a {@link DiffAlgorithmListener} representing the progress listener. Can be {@code null}.
* @return The patch describing the difference between the original and revised strings. Never {@code null}.
*/
public static Patch<String> diff(String sourceText, String targetText,
@@ -100,16 +91,15 @@ public final class DiffUtils {
Arrays.asList(sourceText.split("\n")),
Arrays.asList(targetText.split("\n")), progress);
}
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param source a {@link List} representing the original text. Must not be {@code null}.
* @param target a {@link List} representing the revised text. Must not be {@code null}.
* @param source a {@link List} representing the original text. Must not be {@code null}.
* @param target a {@link List} representing the revised text. Must not be {@code null}.
* @param equalizer a {@link BiPredicate} representing the equalizer object to replace the default compare
* algorithm (Object.equals). If {@code null} the default equalizer of the
* default algorithm is used.
* 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}.
*/
@@ -121,20 +111,18 @@ public final class DiffUtils {
}
return DiffUtils.diff(source, target, new MyersDiff<>());
}
public static <T> Patch<T> diff(List<T> original, List<T> revised,
DiffAlgorithmI<T> algorithm, DiffAlgorithmListener progress) {
return diff(original, revised, algorithm, progress, false);
}
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param original a {@link List} representing the original text. Must not be {@code null}.
* @param revised a {@link List} representing the revised text. Must not be {@code null}.
* @param algorithm a {@link DiffAlgorithmI} representing the diff algorithm. Must not be {@code null}.
* @param progress a {@link DiffAlgorithmListener} representing the diff algorithm listener.
* @param original a {@link List} representing the original text. Must not be {@code null}.
* @param revised a {@link List} representing the revised text. Must not be {@code null}.
* @param algorithm a {@link DiffAlgorithmI} representing the diff algorithm. Must not be {@code null}.
* @param progress a {@link DiffAlgorithmListener} representing the diff algorithm listener.
* @param includeEqualParts Include equal data parts into the patch.
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
@@ -145,17 +133,14 @@ public final class DiffUtils {
Objects.requireNonNull(original, "original must not be null");
Objects.requireNonNull(revised, "revised must not be null");
Objects.requireNonNull(algorithm, "algorithm must not be null");
return Patch.generate(original, revised, algorithm.computeDiff(original, revised, progress), includeEqualParts);
}
/**
* Computes the difference between the original and revised list of elements
* with default diff algorithm
*
* @param original a {@link List} representing the original text. Must not be {@code null}.
* @param revised a {@link List} representing the revised text. Must not be {@code null}.
* @param original a {@link List} representing the original text. Must not be {@code null}.
* @param revised a {@link List} representing the revised text. Must not be {@code null}.
* @param algorithm a {@link DiffAlgorithmI} representing the diff algorithm. Must not be {@code null}.
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
@@ -163,14 +148,13 @@ public final class DiffUtils {
public static <T> Patch<T> diff(List<T> original, List<T> revised, DiffAlgorithmI<T> algorithm) {
return diff(original, revised, algorithm, null);
}
/**
* Computes the difference between the given texts inline. This one uses the
* "trick" to make out of texts lists of characters, like DiffRowGenerator
* does and merges those changes at the end together again.
*
* @param original a {@link String} representing the original text. Must not be {@code null}.
* @param revised a {@link String} representing the revised text. Must not be {@code null}.
* @param revised a {@link String} representing the revised text. Must not be {@code null}.
* @return The patch describing the difference between the original and
* revised sequences. Never {@code null}.
*/
@@ -190,12 +174,11 @@ public final class DiffUtils {
}
return patch;
}
/**
* Applies the given patch to the original list and returns the revised list.
*
* @param original a {@link List} representing the original list.
* @param patch a {@link List} representing the patch to apply.
* @param patch a {@link List} representing the patch to apply.
* @return the revised list.
* @throws PatchFailedException if the patch cannot be applied.
*/
@@ -203,26 +186,21 @@ public final class DiffUtils {
throws PatchFailedException {
return patch.applyTo(original);
}
/**
* Applies the given patch to the revised list and returns the original list.
*
* @param revised a {@link List} representing the revised list.
* @param patch a {@link Patch} representing the patch to apply.
* @param patch a {@link Patch} representing the patch to apply.
* @return the original list.
* @throws PatchFailedException if the patch cannot be applied.
*/
public static <T> List<T> unpatch(List<T> revised, Patch<T> patch) {
return patch.restore(revised);
}
private static List<String> compressLines(List<String> lines, String delimiter) {
if (lines.isEmpty()) {
return Collections.emptyList();
}
return Collections.singletonList(String.join(delimiter, lines));
}
private DiffUtils() {
}
}

View File

@@ -14,31 +14,24 @@
* limitations under the License.
*/
package com.github.difflib;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.ChangeDelta;
import com.github.difflib.patch.Chunk;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.Patch;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
*
* @author toben
*/
public final class UnifiedDiffUtils {
private static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern
.compile("^@@\\s+-(\\d+)(?:,(\\d+))?\\s+\\+(\\d+)(?:,(\\d+))?\\s+@@.*$");
private static final String NULL_FILE_INDICATOR = "/dev/null";
private UnifiedDiffUtils() {
}
/**
* Parse the given text in unified format and creates the list of deltas for it.
*
@@ -49,7 +42,6 @@ public final class UnifiedDiffUtils {
boolean inPrelude = true;
List<String[]> rawChunk = new ArrayList<>();
Patch<String> patch = new Patch<>();
int old_ln = 0;
int new_ln = 0;
String tag;
@@ -69,7 +61,6 @@ public final class UnifiedDiffUtils {
// Parse the @@ header
old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1));
new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
if (old_ln == 0) {
old_ln = 1;
}
@@ -88,20 +79,16 @@ public final class UnifiedDiffUtils {
}
}
}
// Process the lines in the last chunk
processLinesInPrevChunk(rawChunk, patch, old_ln, new_ln);
return patch;
}
private static void processLinesInPrevChunk(List<String[]> rawChunk, Patch<String> patch, int old_ln, int new_ln) {
String tag;
String rest;
if (!rawChunk.isEmpty()) {
List<String> oldChunkLines = new ArrayList<>();
List<String> newChunkLines = new ArrayList<>();
List<Integer> removePosition = new ArrayList<>();
List<Integer> addPosition = new ArrayList<>();
int removeNum = 0;
@@ -130,29 +117,26 @@ public final class UnifiedDiffUtils {
rawChunk.clear();
}
}
/**
* generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format
* text representing the Patch. Author: Bill James (tankerbay@gmail.com).
*
* @param originalFileName - Filename of the original (unrevised file)
* @param revisedFileName - 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.
* @param revisedFileName - 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 originalFileName,
String revisedFileName, List<String> originalLines, Patch<String> patch,
int contextSize) {
String revisedFileName, List<String> originalLines, Patch<String> patch,
int contextSize) {
if (!patch.getDeltas().isEmpty()) {
List<String> ret = new ArrayList<>();
ret.add("--- " + Optional.ofNullable(originalFileName).orElse(NULL_FILE_INDICATOR));
ret.add("+++ " + Optional.ofNullable(revisedFileName).orElse(NULL_FILE_INDICATOR));
List<AbstractDelta<String>> patchDeltas = new ArrayList<>(
patch.getDeltas());
// code outside the if block also works for single-delta issues.
List<AbstractDelta<String>> deltas = new ArrayList<>(); // current
// list
@@ -170,7 +154,6 @@ public final class UnifiedDiffUtils {
// 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
@@ -190,7 +173,6 @@ public final class UnifiedDiffUtils {
}
delta = nextDelta;
}
}
// don't forget to process the last set of Deltas
List<String> curBlock = processDeltas(originalLines, deltas,
@@ -200,23 +182,21 @@ public final class UnifiedDiffUtils {
}
return new ArrayList<>();
}
/**
* 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 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<AbstractDelta<String>> deltas, int contextSize, boolean newFile) {
List<AbstractDelta<String>> deltas, int contextSize, boolean newFile) {
List<String> 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;
AbstractDelta<String> curDelta = deltas.get(0);
int origStart;
if (newFile) {
@@ -228,30 +208,25 @@ public final class UnifiedDiffUtils {
origStart = 1;
}
}
int revStart = curDelta.getTarget().getPosition() + 1 - contextSize;
if (revStart < 1) {
revStart = 1;
}
// find the start of the wrapper context code
int contextStart = curDelta.getSource().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.getSource().getPosition(); line++) { //
buffer.add(" " + origLines.get(line));
origTotal++;
revTotal++;
}
// output the first Delta
buffer.addAll(getDeltaText(curDelta));
origTotal += curDelta.getSource().getLines().size();
revTotal += curDelta.getTarget().getLines().size();
int deltaIndex = 1;
while (deltaIndex < deltas.size()) { // for each of the other Deltas
AbstractDelta<String> nextDelta = deltas.get(deltaIndex);
@@ -270,7 +245,6 @@ public final class UnifiedDiffUtils {
curDelta = nextDelta;
deltaIndex++;
}
// Now output the post-Delta context code, clamping the end of the file
contextStart = curDelta.getSource().getPosition()
+ curDelta.getSource().getLines().size();
@@ -280,7 +254,6 @@ public final class UnifiedDiffUtils {
origTotal++;
revTotal++;
}
// Create and insert the block header, conforming to the Unified Diff
// standard
StringBuilder header = new StringBuilder();
@@ -294,10 +267,8 @@ public final class UnifiedDiffUtils {
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).
*
@@ -314,13 +285,9 @@ public final class UnifiedDiffUtils {
}
return buffer;
}
private UnifiedDiffUtils() {
}
/**
* Compare the differences between two files and return to the original file and diff format
*
* <p>
* (This method compares the original file with the comparison file to obtain a diff, and inserts the diff into the corresponding position of the original file.
* You can see all the differences and unmodified places from the original file.
* Also, this will be very easy and useful for making side-by-side comparison display applications,
@@ -329,16 +296,13 @@ public final class UnifiedDiffUtils {
*
* @param original Original file content
* @param revised revised file content
*
*/
public static List<String> generateOriginalAndDiff(List<String> original, List<String> revised) {
return generateOriginalAndDiff(original, revised, null, null);
}
/**
* Compare the differences between two files and return to the original file and diff format
*
* <p>
* (This method compares the original file with the comparison file to obtain a diff, and inserts the diff into the corresponding position of the original file.
* You can see all the differences and unmodified places from the original file.
* Also, this will be very easy and useful for making side-by-side comparison display applications,
@@ -372,7 +336,6 @@ public final class UnifiedDiffUtils {
List<String> originalWithPrefix = original.stream().map(v -> " " + v).collect(Collectors.toList());
return insertOrig(originalWithPrefix, unifiedDiff);
}
//Insert the diff format to the original file
private static List<String> insertOrig(List<String> original, List<String> unifiedDiff) {
List<String> result = new ArrayList<>();
@@ -401,7 +364,6 @@ public final class UnifiedDiffUtils {
insertOrig(diffList, result, original);
return result;
}
//Insert the diff format to the original file
private static void insertOrig(List<List<String>> diffList, List<String> result, List<String> original) {
for (int i = 0; i < diffList.size(); i++) {
@@ -429,14 +391,12 @@ public final class UnifiedDiffUtils {
}
}
}
//Insert the unchanged content in the source file into result
private static void insert(List<String> result, List<String> noChangeContent) {
for (String ins : noChangeContent) {
result.add(ins);
}
}
//Parse the line containing @@ to get the modified line number to delete or add a few lines
private static Map<String, Integer> getRowMap(String str) {
Map<String, Integer> map = new HashMap<>();
@@ -452,7 +412,6 @@ public final class UnifiedDiffUtils {
}
return map;
}
//Get the specified part of the line from the original file
private static List<String> getOrigList(List<String> originalWithPrefix, int start, int end) {
List<String> list = new ArrayList<>();

View File

@@ -14,21 +14,16 @@
* limitations under the License.
*/
package com.github.difflib.algorithm;
import com.github.difflib.patch.DeltaType;
/**
*
* @author <a href="t.warneke@gmx.net">Tobias Warneke</a>
*/
public class Change {
public final DeltaType deltaType;
public final int startOriginal;
public final int endOriginal;
public final int startRevised;
public final int endRevised;
public Change(DeltaType deltaType, int startOriginal, int endOriginal, int startRevised, int endRevised) {
this.deltaType = deltaType;
this.startOriginal = startOriginal;
@@ -36,11 +31,9 @@ public class Change {
this.startRevised = startRevised;
this.endRevised = endRevised;
}
public Change withEndOriginal(int endOriginal) {
return new Change(deltaType, startOriginal, endOriginal, startRevised, endRevised);
}
public Change withEndRevised(int endRevised) {
return new Change(deltaType, startOriginal, endOriginal, startRevised, endRevised);
}

View File

@@ -14,16 +14,14 @@
* limitations under the License.
*/
package com.github.difflib.algorithm;
import java.util.function.BiPredicate;
/**
* Tool to create new instances of a diff algorithm. This one is only needed at the moment to
* Tool to create new instances of a diff algorithm. This one is only needed at the moment to
* set DiffUtils default diff algorithm.
*
* @author tw
*/
public interface DiffAlgorithmFactory {
<T> DiffAlgorithmI<T> create();
<T> DiffAlgorithmI<T> create(BiPredicate<T, T> equalizer);
}

View File

@@ -14,28 +14,24 @@
* limitations under the License.
*/
package com.github.difflib.algorithm;
import java.util.Arrays;
import java.util.List;
/**
* Interface of a diff algorithm.
*
* @author Tobias Warneke (t.warneke@gmx.net)
* @param <T> type of data that is diffed.
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public interface DiffAlgorithmI<T> {
/**
* Computes the changeset to patch the source list to the target list.
*
* @param source source data
* @param target target data
* @param source source data
* @param target target data
* @param progress progress listener
* @return
*/
List<Change> computeDiff(List<T> source, List<T> target, DiffAlgorithmListener progress);
/**
* Simple extension to compute a changeset using arrays.
*

View File

@@ -14,20 +14,18 @@
* limitations under the License.
*/
package com.github.difflib.algorithm;
/**
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public interface DiffAlgorithmListener {
void diffStart();
/**
* This is a step within the diff algorithm. Due to different implementations the value
* is not strict incrementing to the max and is not garantee to reach the max. It could
* stop before.
*
* @param value
* @param max
* @param max
*/
void diffStep(int value, int max);
void diffEnd();

View File

@@ -14,44 +14,55 @@
* limitations under the License.
*/
package com.github.difflib.algorithm.myers;
import com.github.difflib.algorithm.Change;
import com.github.difflib.algorithm.DiffAlgorithmFactory;
import com.github.difflib.algorithm.DiffAlgorithmI;
import com.github.difflib.algorithm.DiffAlgorithmListener;
import com.github.difflib.patch.DeltaType;
import com.github.difflib.patch.Patch;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
/**
* A clean-room implementation of Eugene Myers greedy differencing algorithm.
*/
public final class MyersDiff<T> implements DiffAlgorithmI<T> {
private final BiPredicate<T, T> equalizer;
public MyersDiff() {
equalizer = Object::equals;
}
public MyersDiff(final BiPredicate<T, T> equalizer) {
Objects.requireNonNull(equalizer, "equalizer must not be null");
this.equalizer = equalizer;
}
/**
* Factory to create instances of this specific diff algorithm.
*/
public static DiffAlgorithmFactory factory() {
return new DiffAlgorithmFactory() {
@Override
public <T> DiffAlgorithmI<T>
create() {
return new MyersDiff<>();
}
@Override
public <T> DiffAlgorithmI<T>
create(BiPredicate<T, T> equalizer) {
return new MyersDiff<>(equalizer);
}
};
}
/**
* {@inheritDoc}
*
* <p>
* Return empty diff if get the error while procession the difference.
*/
@Override
public List<Change> computeDiff(final List<T> source, final List<T> target, DiffAlgorithmListener progress) {
Objects.requireNonNull(source, "source list must not be null");
Objects.requireNonNull(target, "target list must not be null");
if (progress != null) {
progress.diffStart();
}
@@ -62,30 +73,26 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
}
return result;
}
/**
* Computes the minimum diffpath that expresses de differences between the
* original and revised sequences, according to Gene Myers differencing
* algorithm.
*
* @param orig The original sequence.
* @param rev The revised sequence.
* @param rev The revised sequence.
* @return A minimum {@link PathNode Path} accross the differences graph.
* @throws DifferentiationFailedException if a diff path could not be found.
*/
private PathNode buildPath(final List<T> orig, final List<T> rev, DiffAlgorithmListener progress) {
Objects.requireNonNull(orig, "original sequence is null");
Objects.requireNonNull(rev, "revised sequence is null");
// these are local constants
final int N = orig.size();
final int M = rev.size();
final int MAX = N + M + 1;
final int size = 1 + 2 * MAX;
final int middle = size / 2;
final PathNode diagonal[] = new PathNode[size];
diagonal[middle + 1] = new PathNode(0, -1, true, true, null);
for (int d = 0; d < MAX; d++) {
if (progress != null) {
@@ -97,7 +104,6 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
final int kminus = kmiddle - 1;
PathNode prev;
int i;
if ((k == -d) || (k != d && diagonal[kminus].i < diagonal[kplus].i)) {
i = diagonal[kplus].i;
prev = diagonal[kplus];
@@ -105,24 +111,17 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
i = diagonal[kminus].i + 1;
prev = diagonal[kminus];
}
diagonal[kminus] = null; // no longer used
int j = i - k;
PathNode node = new PathNode(i, j, false, false, prev);
while (i < N && j < M && equalizer.test(orig.get(i), rev.get(j))) {
i++;
j++;
}
if (i != node.i) {
node = new PathNode(i, j, true, false, node);
}
diagonal[kmiddle] = node;
if (i >= N && j >= M) {
return diagonal[kmiddle];
}
@@ -132,22 +131,20 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
// According to Myers, this cannot happen
throw new IllegalStateException("could not find a diff path");
}
/**
* Constructs a {@link Patch} from a difference path.
*
* @param actualPath The path.
* @param orig The original sequence.
* @param rev The revised sequence.
* @param orig The original sequence.
* @param rev The revised sequence.
* @return A {@link Patch} script corresponding to the path.
* @throws DifferentiationFailedException if a {@link Patch} could not be
* built from the given path.
* built from the given path.
*/
private List<Change> buildRevision(PathNode actualPath, List<T> orig, List<T> rev) {
Objects.requireNonNull(actualPath, "path is null");
Objects.requireNonNull(orig, "original sequence is null");
Objects.requireNonNull(rev, "revised sequence is null");
PathNode path = actualPath;
List<Change> changes = new ArrayList<>();
if (path.isSnake()) {
@@ -159,11 +156,9 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
}
int i = path.i;
int j = path.j;
path = path.prev;
int ianchor = path.i;
int janchor = path.j;
if (ianchor == i && janchor != j) {
changes.add(new Change(DeltaType.INSERT, ianchor, i, janchor, j));
} else if (ianchor != i && janchor == j) {
@@ -171,30 +166,10 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
} else {
changes.add(new Change(DeltaType.CHANGE, ianchor, i, janchor, j));
}
if (path.isSnake()) {
path = path.prev;
}
}
return changes;
}
/**
* Factory to create instances of this specific diff algorithm.
*/
public static DiffAlgorithmFactory factory() {
return new DiffAlgorithmFactory() {
@Override
public <T> DiffAlgorithmI<T>
create() {
return new MyersDiff<>();
}
@Override
public <T> DiffAlgorithmI<T>
create(BiPredicate < T, T > equalizer) {
return new MyersDiff<>(equalizer);
}
};
}
}

View File

@@ -14,60 +14,65 @@
* limitations under the License.
*/
package com.github.difflib.algorithm.myers;
import com.github.difflib.algorithm.Change;
import com.github.difflib.algorithm.DiffAlgorithmFactory;
import com.github.difflib.algorithm.DiffAlgorithmI;
import com.github.difflib.algorithm.DiffAlgorithmListener;
import com.github.difflib.patch.DeltaType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
/**
*
* @author tw
*/
public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
private final BiPredicate<T, T> equalizer;
public MyersDiffWithLinearSpace() {
equalizer = Object::equals;
}
public MyersDiffWithLinearSpace(final BiPredicate<T, T> equalizer) {
Objects.requireNonNull(equalizer, "equalizer must not be null");
this.equalizer = equalizer;
}
/**
* Factory to create instances of this specific diff algorithm.
*/
public static DiffAlgorithmFactory factory() {
return new DiffAlgorithmFactory() {
@Override
public <T> DiffAlgorithmI<T>
create() {
return new MyersDiffWithLinearSpace<>();
}
@Override
public <T> DiffAlgorithmI<T>
create(BiPredicate<T, T> equalizer) {
return new MyersDiffWithLinearSpace<>(equalizer);
}
};
}
@Override
public List<Change> computeDiff(List<T> source, List<T> target, DiffAlgorithmListener progress) {
Objects.requireNonNull(source, "source list must not be null");
Objects.requireNonNull(target, "target list must not be null");
if (progress != null) {
progress.diffStart();
}
DiffData data = new DiffData(source, target);
int maxIdx = source.size() + target.size();
buildScript(data, 0, source.size(), 0, target.size(), idx -> {
if (progress != null) {
progress.diffStep(idx, maxIdx);
}
});
if (progress != null) {
progress.diffEnd();
}
return data.script;
}
private void buildScript(DiffData data, int start1, int end1, int start2, int end2, Consumer<Integer> progress) {
if (progress != null) {
progress.accept((end1 - start1) / 2 + (end2 - start2) / 2);
@@ -112,35 +117,29 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
buildScript(data, middle.end, end1, middle.end - middle.diag, end2, progress);
}
}
private Snake getMiddleSnake(DiffData data, int start1, int end1, int start2, int end2) {
final int m = end1 - start1;
final int n = end2 - start2;
if (m == 0 || n == 0) {
return null;
}
final int delta = m - n;
final int sum = n + m;
final int offset = (sum % 2 == 0 ? sum : sum + 1) / 2;
data.vDown[1 + offset] = start1;
data.vUp[1 + offset] = end1 + 1;
for (int d = 0; d <= offset; ++d) {
// Down
for (int k = -d; k <= d; k += 2) {
// First step
final int i = k + offset;
if (k == -d || k != d && data.vDown[i - 1] < data.vDown[i + 1]) {
data.vDown[i] = data.vDown[i + 1];
} else {
data.vDown[i] = data.vDown[i - 1] + 1;
}
int x = data.vDown[i];
int y = x - start1 + start2 - k;
while (x < end1 && y < end2 && equalizer.test(data.source.get(x), data.target.get(y))) {
data.vDown[i] = ++x;
++y;
@@ -152,7 +151,6 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
}
}
}
// Up
for (int k = delta - d; k <= delta + d; k += 2) {
// First step
@@ -163,7 +161,6 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
} else {
data.vUp[i] = data.vUp[i - 1];
}
int x = data.vUp[i] - 1;
int y = x - start1 + start2 - k;
while (x >= start1 && y >= start2 && equalizer.test(data.source.get(x), data.target.get(y))) {
@@ -178,11 +175,9 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
}
}
}
// According to Myers, this cannot happen
throw new IllegalStateException("could not find a diff path");
}
private Snake buildSnake(DiffData data, final int start, final int diag, final int end1, final int end2) {
int end = start;
while (end - diag < end2 && end < end1 && equalizer.test(data.source.get(end), data.target.get(end - diag))) {
@@ -190,16 +185,13 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
}
return new Snake(start, end, diag);
}
private class DiffData {
final int size;
final int[] vDown;
final int[] vUp;
final List<Change> script;
final List<T> source;
final List<T> target;
public DiffData(List<T> source, List<T> target) {
this.source = source;
this.target = target;
@@ -209,36 +201,14 @@ public class MyersDiffWithLinearSpace<T> implements DiffAlgorithmI<T> {
script = new ArrayList<>();
}
}
private class Snake {
final int start;
final int end;
final int diag;
public Snake(final int start, final int end, final int diag) {
this.start = start;
this.end = end;
this.diag = diag;
}
}
/**
* Factory to create instances of this specific diff algorithm.
*/
public static DiffAlgorithmFactory factory() {
return new DiffAlgorithmFactory() {
@Override
public <T> DiffAlgorithmI<T>
create() {
return new MyersDiffWithLinearSpace<>();
}
@Override
public <T> DiffAlgorithmI<T>
create(BiPredicate < T, T > equalizer) {
return new MyersDiffWithLinearSpace<>(equalizer);
}
};
}
}

View File

@@ -14,14 +14,12 @@
* limitations under the License.
*/
package com.github.difflib.algorithm.myers;
/**
* A node in a diffpath.
*
* @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
*/
public final class PathNode {
/**
* Position in the original sequence.
*/
@@ -34,16 +32,13 @@ public final class PathNode {
* The previous node in the path.
*/
public final PathNode prev;
public final boolean snake;
public final boolean bootstrap;
/**
* Concatenates a new path node with an existing diffpath.
*
* @param i The position in the original sequence for the new node.
* @param j The position in the revised sequence for the new node.
* @param i The position in the original sequence for the new node.
* @param j The position in the revised sequence for the new node.
* @param prev The previous node in the path.
*/
public PathNode(int i, int j, boolean snake, boolean bootstrap, PathNode prev) {
@@ -57,11 +52,9 @@ public final class PathNode {
}
this.snake = snake;
}
public boolean isSnake() {
return snake;
}
/**
* Is this a bootstrap node?
* <p>
@@ -72,7 +65,6 @@ public final class PathNode {
public boolean isBootstrap() {
return bootstrap;
}
/**
* Skips sequences of {@link PathNode PathNodes} until a snake or bootstrap node is found, or the end of the
* path is reached.
@@ -88,7 +80,6 @@ public final class PathNode {
}
return this;
}
/**
* {@inheritDoc}
*/

View File

@@ -14,20 +14,18 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* Abstract delta between a source and a target.
* Abstract delta between a source and a target.
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public abstract class AbstractDelta<T> implements Serializable {
private final Chunk<T> source;
private final Chunk<T> target;
private final DeltaType type;
public AbstractDelta(DeltaType type, Chunk<T> source, Chunk<T> target) {
Objects.requireNonNull(source);
Objects.requireNonNull(target);
@@ -36,28 +34,24 @@ public abstract class AbstractDelta<T> implements Serializable {
this.source = source;
this.target = target;
}
public Chunk<T> getSource() {
return source;
}
public Chunk<T> getTarget() {
return target;
}
public DeltaType getType() {
return type;
}
/**
* Verify the chunk of this delta, to fit the target.
*
* @param target
* @throws PatchFailedException
* @throws PatchFailedException
*/
protected VerifyChunk verifyChunkToFitTarget(List<T> target) throws PatchFailedException {
return getSource().verifyChunk(target);
}
protected VerifyChunk verifyAndApplyTo(List<T> target) throws PatchFailedException {
final VerifyChunk verify = verifyChunkToFitTarget(target);
if (verify == VerifyChunk.OK) {
@@ -65,16 +59,13 @@ public abstract class AbstractDelta<T> implements Serializable {
}
return verify;
}
protected abstract void applyTo(List<T> target) throws PatchFailedException;
protected abstract void restore(List<T> target);
/**
* Apply patch fuzzy.
*
* @param target the list this patch will be applied to
* @param fuzz the number of elements to ignore before/after the patched elements
* @param target the list this patch will be applied to
* @param fuzz the number of elements to ignore before/after the patched elements
* @param position the position this patch will be applied to. ignores {@code source.getPosition()}
* @see <a href="https://www.gnu.org/software/diffutils/manual/html_node/Inexact.html">Description of Fuzzy Patch</a> for more information.
*/
@@ -82,17 +73,14 @@ public abstract class AbstractDelta<T> implements Serializable {
protected void applyFuzzyToAt(List<T> target, int fuzz, int position) throws PatchFailedException {
throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports applying patch fuzzy");
}
/**
* Create a new delta of the actual instance with customized chunk data.
*/
public abstract AbstractDelta<T> withChunks(Chunk<T> original, Chunk<T> revised);
@Override
public int hashCode() {
return Objects.hash(this.source, this.target, this.type);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {

View File

@@ -14,18 +14,15 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.util.List;
import java.util.Objects;
/**
* Describes the change-delta between original and revised texts.
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
* @param <T> The type of the compared elements in the data 'lines'.
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
*/
public final class ChangeDelta<T> extends AbstractDelta<T> {
/**
* Creates a change delta with the two given chunks.
*
@@ -37,7 +34,6 @@ public final class ChangeDelta<T> extends AbstractDelta<T> {
Objects.requireNonNull(source, "source must not be null");
Objects.requireNonNull(target, "target must not be null");
}
@Override
protected void applyTo(List<T> target) throws PatchFailedException {
int position = getSource().getPosition();
@@ -51,7 +47,6 @@ public final class ChangeDelta<T> extends AbstractDelta<T> {
i++;
}
}
@Override
protected void restore(List<T> target) {
int position = getTarget().getPosition();
@@ -65,26 +60,22 @@ public final class ChangeDelta<T> extends AbstractDelta<T> {
i++;
}
}
protected void applyFuzzyToAt(List<T> target, int fuzz, int position) throws PatchFailedException {
int size = getSource().size();
for (int i = fuzz; i < size - fuzz; i++) {
target.remove(position + fuzz);
}
int i = fuzz;
for (T line : getTarget().getLines().subList(fuzz, getTarget().size() - fuzz)) {
target.add(position + i, line);
i++;
}
}
@Override
public String toString() {
return "[ChangeDelta, position: " + getSource().getPosition() + ", lines: "
+ getSource().getLines() + " to " + getTarget().getLines() + "]";
}
@Override
public AbstractDelta<T> withChunks(Chunk<T> original, Chunk<T> revised) {
return new ChangeDelta<T>(original, revised);

View File

@@ -14,13 +14,11 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Holds the information about the part of text involved in the diff process
*
@@ -32,20 +30,18 @@ import java.util.Objects;
* differencing using this library.
* </p>
*
* @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
* @param <T> The type of the compared elements in the 'lines'.
* @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
*/
public final class Chunk<T> implements Serializable {
private final int position;
private List<T> lines;
private final List<Integer> changePosition;
private List<T> lines;
/**
* Creates a chunk and saves a copy of affected lines
*
* @param position the start position
* @param lines the affected lines
* @param position the start position
* @param lines the affected lines
* @param changePosition the positions of changed lines
*/
public Chunk(int position, List<T> lines, List<Integer> changePosition) {
@@ -53,22 +49,20 @@ public final class Chunk<T> implements Serializable {
this.lines = new ArrayList<>(lines);
this.changePosition = changePosition != null ? new ArrayList<>(changePosition) : null;
}
/**
* Creates a chunk and saves a copy of affected lines
*
* @param position the start position
* @param lines the affected lines
* @param lines the affected lines
*/
public Chunk(int position, List<T> lines) {
this(position, lines, null);
}
/**
* Creates a chunk and saves a copy of affected lines
*
* @param position the start position
* @param lines the affected lines
* @param position the start position
* @param lines the affected lines
* @param changePosition the positions of changed lines
*/
public Chunk(int position, T[] lines, List<Integer> changePosition) {
@@ -76,17 +70,15 @@ public final class Chunk<T> implements Serializable {
this.lines = Arrays.asList(lines);
this.changePosition = changePosition != null ? new ArrayList<>(changePosition) : null;
}
/**
* Creates a chunk and saves a copy of affected lines
*
* @param position the start position
* @param lines the affected lines
* @param lines the affected lines
*/
public Chunk(int position, T[] lines) {
this(position, lines, null);
}
/**
* Verifies that this chunk's saved text matches the corresponding text in
* the given sequence.
@@ -97,13 +89,12 @@ public final class Chunk<T> implements Serializable {
public VerifyChunk verifyChunk(List<T> target) throws PatchFailedException {
return verifyChunk(target, 0, getPosition());
}
/**
* Verifies that this chunk's saved text matches the corresponding text in
* the given sequence.
*
* @param target the sequence to verify against.
* @param fuzz the count of ignored prefix/suffix
* @param target the sequence to verify against.
* @param fuzz the count of ignored prefix/suffix
* @param position the position of target
* @throws PatchFailedException
*/
@@ -112,7 +103,6 @@ public final class Chunk<T> implements Serializable {
int startIndex = fuzz;
int lastIndex = size() - fuzz;
int last = position + size() - 1;
if (position + fuzz > target.size() || last - fuzz > target.size()) {
return VerifyChunk.POSITION_OUT_OF_TARGET;
}
@@ -123,48 +113,40 @@ public final class Chunk<T> implements Serializable {
}
return VerifyChunk.OK;
}
/**
* @return the start position of chunk in the text
*/
public int getPosition() {
return position;
}
public void setLines(List<T> lines) {
this.lines = lines;
}
/**
* @return the affected lines
*/
public List<T> getLines() {
return lines;
}
public void setLines(List<T> lines) {
this.lines = lines;
}
/**
* @return the positions of changed lines of chunk in the text
*/
public List<Integer> getChangePosition() {
return changePosition;
}
public int size() {
return lines.size();
}
/**
* Returns the index of the last line of the chunk.
*/
public int last() {
return getPosition() + size() - 1;
}
@Override
public int hashCode() {
return Objects.hash(lines, position, size());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -186,10 +168,8 @@ public final class Chunk<T> implements Serializable {
}
return position == other.position;
}
@Override
public String toString() {
return "[position: " + position + ", size: " + size() + ", lines: " + lines + "]";
}
}

View File

@@ -18,16 +18,12 @@ limitations under the License.
* #L%
*/
package com.github.difflib.patch;
import java.io.Serializable;
import java.util.List;
/**
*
* @author tw
*/
@FunctionalInterface
public interface ConflictOutput<T> extends Serializable {
public void processConflict(VerifyChunk verifyChunk, AbstractDelta<T> delta, List<T> result) throws PatchFailedException;
}

View File

@@ -14,27 +14,23 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.util.List;
/**
* Describes the delete-delta between original and revised texts.
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
* @param <T> The type of the compared elements in the 'lines'.
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
*/
public final class DeleteDelta<T> extends AbstractDelta<T> {
/**
* Creates a change delta with the two given chunks.
*
* @param original The original chunk. Must not be {@code null}.
* @param revised The original chunk. Must not be {@code null}.
* @param revised The original chunk. Must not be {@code null}.
*/
public DeleteDelta(Chunk<T> original, Chunk<T> revised) {
super(DeltaType.DELETE, original, revised);
}
@Override
protected void applyTo(List<T> target) throws PatchFailedException {
int position = getSource().getPosition();
@@ -43,7 +39,6 @@ public final class DeleteDelta<T> extends AbstractDelta<T> {
target.remove(position);
}
}
@Override
protected void restore(List<T> target) {
int position = this.getTarget().getPosition();
@@ -52,13 +47,11 @@ public final class DeleteDelta<T> extends AbstractDelta<T> {
target.add(position + i, lines.get(i));
}
}
@Override
public String toString() {
return "[DeleteDelta, position: " + getSource().getPosition() + ", lines: "
+ getSource().getLines() + "]";
}
@Override
public AbstractDelta<T> withChunks(Chunk<T> original, Chunk<T> revised) {
return new DeleteDelta<T>(original, revised);

View File

@@ -14,21 +14,19 @@
* limitations under the License.
*/
package com.github.difflib.patch;
/**
* Specifies the type of the delta. There are three types of modifications from
* the original to get the revised text.
*
* Specifies the type of the delta. There are three types of modifications from
* the original to get the revised text.
* <p>
* CHANGE: a block of data of the original is replaced by another block of data.
* DELETE: a block of data of the original is removed
* INSERT: at a position of the original a block of data is inserted
*
* to be complete there is also
*
* <p>
* to be complete there is also
* <p>
* EQUAL: a block of data of original and the revised text is equal
*
* <p>
* which is no change at all.
*
*/
public enum DeltaType {
/**

View File

@@ -14,19 +14,15 @@
* limitations under the License.
*/
package com.github.difflib.patch;
/**
* Base class for all exceptions emanating from this package.
*
* @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
*/
public class DiffException extends Exception {
private static final long serialVersionUID = 1L;
public DiffException() {
}
public DiffException(String msg) {
super(msg);
}

View File

@@ -14,27 +14,22 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.util.List;
/**
* This delta contains equal lines of data. Therefore nothing is to do in applyTo and restore.
*
* @author tobens
*/
public class EqualDelta<T> extends AbstractDelta<T> {
public EqualDelta(Chunk<T> source, Chunk<T> target) {
super(DeltaType.EQUAL, source, target);
}
@Override
protected void applyTo(List<T> target) throws PatchFailedException {
}
@Override
protected void restore(List<T> target) {
}
/**
* {@inheritDoc}
*/
@@ -42,13 +37,11 @@ public class EqualDelta<T> extends AbstractDelta<T> {
protected void applyFuzzyToAt(List<T> target, int fuzz, int delta) {
// equals so no operations
}
@Override
public String toString() {
return "[EqualDelta, position: " + getSource().getPosition() + ", lines: "
+ getSource().getLines() + "]";
}
@Override
public AbstractDelta<T> withChunks(Chunk<T> original, Chunk<T> revised) {
return new EqualDelta<T>(original, revised);

View File

@@ -14,27 +14,23 @@
* limitations under the License.
*/
package com.github.difflib.patch;
import java.util.List;
/**
* Describes the add-delta between original and revised texts.
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
* @param <T> The type of the compared elements in the 'lines'.
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
*/
public final class InsertDelta<T> extends AbstractDelta<T> {
/**
* Creates an insert delta with the two given chunks.
*
* @param original The original chunk. Must not be {@code null}.
* @param revised The original chunk. Must not be {@code null}.
* @param revised The original chunk. Must not be {@code null}.
*/
public InsertDelta(Chunk<T> original, Chunk<T> revised) {
super(DeltaType.INSERT, original, revised);
}
@Override
protected void applyTo(List<T> target) throws PatchFailedException {
int position = this.getSource().getPosition();
@@ -43,7 +39,6 @@ public final class InsertDelta<T> extends AbstractDelta<T> {
target.add(position + i, lines.get(i));
}
}
@Override
protected void restore(List<T> target) {
int position = getTarget().getPosition();
@@ -52,13 +47,11 @@ public final class InsertDelta<T> extends AbstractDelta<T> {
target.remove(position);
}
}
@Override
public String toString() {
return "[InsertDelta, position: " + getSource().getPosition()
+ ", lines: " + getTarget().getLines() + "]";
}
@Override
public AbstractDelta<T> withChunks(Chunk<T> original, Chunk<T> revised) {
return new InsertDelta<T>(original, revised);

View File

@@ -18,8 +18,6 @@ limitations under the License.
* #L%
*/
package com.github.difflib.patch;
import static java.util.Comparator.comparing;
import com.github.difflib.algorithm.Change;
import java.io.Serializable;
@@ -28,25 +26,93 @@ import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import static java.util.Comparator.comparing;
/**
* Describes the patch holding all deltas between the original and revised
* texts.
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
* @param <T> The type of the compared elements in the 'lines'.
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
*/
public final class Patch<T> implements Serializable {
/**
* Git like merge conflict output.
*/
public static final ConflictOutput<String> CONFLICT_PRODUCES_MERGE_CONFLICT = (VerifyChunk verifyChunk, AbstractDelta<String> delta, List<String> result) -> {
if (result.size() > delta.getSource().getPosition()) {
List<String> orgData = new ArrayList<>();
for (int i = 0; i < delta.getSource().size(); i++) {
orgData.add(result.get(delta.getSource().getPosition()));
result.remove(delta.getSource().getPosition());
}
orgData.add(0, "<<<<<< HEAD");
orgData.add("======");
orgData.addAll(delta.getSource().getLines());
orgData.add(">>>>>>> PATCH");
result.addAll(delta.getSource().getPosition(), orgData);
} else {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
/**
* Standard Patch behaviour to throw an exception for pathching conflicts.
*/
public final ConflictOutput<T> CONFLICT_PRODUCES_EXCEPTION = (VerifyChunk verifyChunk, AbstractDelta<T> delta, List<T> result) -> {
throw new PatchFailedException("could not apply patch due to " + verifyChunk.toString());
};
private final List<AbstractDelta<T>> deltas;
private ConflictOutput<T> conflictOutput = CONFLICT_PRODUCES_EXCEPTION;
public Patch() {
this(10);
}
public Patch(int estimatedPatchSize) {
deltas = new ArrayList<>(estimatedPatchSize);
}
public static <T> Patch<T> generate(List<T> original, List<T> revised, List<Change> changes) {
return generate(original, revised, changes, false);
}
private static <T> Chunk<T> buildChunk(int start, int end, List<T> data) {
return new Chunk<>(start, new ArrayList<>(data.subList(start, end)));
}
public static <T> Patch<T> generate(List<T> original, List<T> revised, List<Change> _changes, boolean includeEquals) {
Patch<T> patch = new Patch<>(_changes.size());
int startOriginal = 0;
int startRevised = 0;
List<Change> changes = _changes;
if (includeEquals) {
changes = new ArrayList<Change>(_changes);
Collections.sort(changes, comparing(d -> d.startOriginal));
}
for (Change change : changes) {
if (includeEquals && startOriginal < change.startOriginal) {
patch.addDelta(new EqualDelta<T>(
buildChunk(startOriginal, change.startOriginal, original),
buildChunk(startRevised, change.startRevised, revised)));
}
Chunk<T> orgChunk = buildChunk(change.startOriginal, change.endOriginal, original);
Chunk<T> revChunk = buildChunk(change.startRevised, change.endRevised, revised);
switch (change.deltaType) {
case DELETE:
patch.addDelta(new DeleteDelta<>(orgChunk, revChunk));
break;
case INSERT:
patch.addDelta(new InsertDelta<>(orgChunk, revChunk));
break;
case CHANGE:
patch.addDelta(new ChangeDelta<>(orgChunk, revChunk));
break;
default:
}
startOriginal = change.endOriginal;
startRevised = change.endRevised;
}
if (includeEquals && startOriginal < original.size()) {
patch.addDelta(new EqualDelta<T>(
buildChunk(startOriginal, original.size(), original),
buildChunk(startRevised, revised.size(), revised)));
}
return patch;
}
/**
* Creates a new list, the patch is being applied to.
*
@@ -59,14 +125,13 @@ public final class Patch<T> implements Serializable {
applyToExisting(result);
return result;
}
/**
* Applies the patch to the supplied list.
*
* @param target The list to apply the changes to. This list has to be modifiable,
* otherwise exceptions may be thrown, depending on the used type of list.
* @throws PatchFailedException if the patch cannot be applied
* @throws RuntimeException (or similar) if the list is not modifiable.
* @throws RuntimeException (or similar) if the list is not modifiable.
*/
public void applyToExisting(List<T> target) throws PatchFailedException {
ListIterator<AbstractDelta<T>> it = getDeltas().listIterator(deltas.size());
@@ -78,33 +143,10 @@ public final class Patch<T> implements Serializable {
}
}
}
private static class PatchApplyingContext<T> {
public final List<T> result;
public final int maxFuzz;
// the position last patch applied to.
public int lastPatchEnd = -1;
///// passing values from find to apply
public int currentFuzz = 0;
public int defaultPosition;
public boolean beforeOutRange = false;
public boolean afterOutRange = false;
private PatchApplyingContext(List<T> result, int maxFuzz) {
this.result = result;
this.maxFuzz = maxFuzz;
}
}
public List<T> applyFuzzy(List<T> target, int maxFuzz) throws PatchFailedException {
PatchApplyingContext<T> ctx = new PatchApplyingContext<>(new ArrayList<>(target), maxFuzz);
// the difference between patch's position and actually applied position
int lastPatchDelta = 0;
for (AbstractDelta<T> delta : getDeltas()) {
ctx.defaultPosition = delta.getSource().getPosition() + lastPatchDelta;
int patchPosition = findPositionFuzzy(ctx, delta);
@@ -116,10 +158,8 @@ public final class Patch<T> implements Serializable {
conflictOutput.processConflict(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, delta, ctx.result);
}
}
return ctx.result;
}
// negative for not found
private int findPositionFuzzy(PatchApplyingContext<T> ctx, AbstractDelta<T> delta) throws PatchFailedException {
for (int fuzz = 0; fuzz <= ctx.maxFuzz; fuzz++) {
@@ -131,16 +171,13 @@ public final class Patch<T> implements Serializable {
}
return -1;
}
// negative for not found
private int findPositionWithFuzz(PatchApplyingContext<T> ctx, AbstractDelta<T> delta, int fuzz) throws PatchFailedException {
if (delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition) == VerifyChunk.OK) {
return ctx.defaultPosition;
}
ctx.beforeOutRange = false;
ctx.afterOutRange = false;
// moreDelta >= 0: just for overflow guard, not a normal condition
//noinspection OverflowingLoopIndex
for (int moreDelta = 0; moreDelta >= 0; moreDelta++) {
@@ -152,10 +189,8 @@ public final class Patch<T> implements Serializable {
break;
}
}
return -1;
}
// negative for not found
private int findPositionWithFuzzAndMoreDelta(PatchApplyingContext<T> ctx, AbstractDelta<T> delta, int fuzz, int moreDelta) throws PatchFailedException {
// range check: can't apply before end of last patch
@@ -174,7 +209,6 @@ public final class Patch<T> implements Serializable {
ctx.afterOutRange = true;
}
}
if (!ctx.beforeOutRange) {
VerifyChunk before = delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition - moreDelta);
if (before == VerifyChunk.OK) {
@@ -189,40 +223,6 @@ public final class Patch<T> implements Serializable {
}
return -1;
}
/**
* Standard Patch behaviour to throw an exception for pathching conflicts.
*/
public final ConflictOutput<T> CONFLICT_PRODUCES_EXCEPTION = (VerifyChunk verifyChunk, AbstractDelta<T> delta, List<T> result) -> {
throw new PatchFailedException("could not apply patch due to " + verifyChunk.toString());
};
/**
* Git like merge conflict output.
*/
public static final ConflictOutput<String> CONFLICT_PRODUCES_MERGE_CONFLICT = (VerifyChunk verifyChunk, AbstractDelta<String> delta, List<String> result) -> {
if (result.size() > delta.getSource().getPosition()) {
List<String> orgData = new ArrayList<>();
for (int i = 0; i < delta.getSource().size(); i++) {
orgData.add(result.get(delta.getSource().getPosition()));
result.remove(delta.getSource().getPosition());
}
orgData.add(0, "<<<<<< HEAD");
orgData.add("======");
orgData.addAll(delta.getSource().getLines());
orgData.add(">>>>>>> PATCH");
result.addAll(delta.getSource().getPosition(), orgData);
} else {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
private ConflictOutput<T> conflictOutput = CONFLICT_PRODUCES_EXCEPTION;
/**
* Alter normal conflict output behaviour to e.g. inclide some conflict
* statements in the result, like git does it.
@@ -231,7 +231,6 @@ public final class Patch<T> implements Serializable {
this.conflictOutput = conflictOutput;
return this;
}
/**
* Creates a new list, containing the restored state of the given list.
* Opposite to {@link #applyTo(List)} method.
@@ -244,8 +243,6 @@ public final class Patch<T> implements Serializable {
restoreToExisting(result);
return result;
}
/**
* Restores all changes within the given list.
* Opposite to {@link #applyToExisting(List)} method.
@@ -261,7 +258,6 @@ public final class Patch<T> implements Serializable {
delta.restore(target);
}
}
/**
* Add the given delta to this patch
*
@@ -270,7 +266,6 @@ public final class Patch<T> implements Serializable {
public void addDelta(AbstractDelta<T> delta) {
deltas.add(delta);
}
/**
* Get the list of computed deltas
*
@@ -280,65 +275,23 @@ public final class Patch<T> implements Serializable {
deltas.sort(comparing(d -> d.getSource().getPosition()));
return deltas;
}
@Override
public String toString() {
return "Patch{" + "deltas=" + deltas + '}';
}
public static <T> Patch<T> generate(List<T> original, List<T> revised, List<Change> changes) {
return generate(original, revised, changes, false);
}
private static <T> Chunk<T> buildChunk(int start, int end, List<T> data) {
return new Chunk<>(start, new ArrayList<>(data.subList(start, end)));
}
public static <T> Patch<T> generate(List<T> original, List<T> revised, List<Change> _changes, boolean includeEquals) {
Patch<T> patch = new Patch<>(_changes.size());
int startOriginal = 0;
int startRevised = 0;
List<Change> changes = _changes;
if (includeEquals) {
changes = new ArrayList<Change>(_changes);
Collections.sort(changes, comparing(d -> d.startOriginal));
private static class PatchApplyingContext<T> {
public final List<T> result;
public final int maxFuzz;
// the position last patch applied to.
public int lastPatchEnd = -1;
///// passing values from find to apply
public int currentFuzz = 0;
public int defaultPosition;
public boolean beforeOutRange = false;
public boolean afterOutRange = false;
private PatchApplyingContext(List<T> result, int maxFuzz) {
this.result = result;
this.maxFuzz = maxFuzz;
}
for (Change change : changes) {
if (includeEquals && startOriginal < change.startOriginal) {
patch.addDelta(new EqualDelta<T>(
buildChunk(startOriginal, change.startOriginal, original),
buildChunk(startRevised, change.startRevised, revised)));
}
Chunk<T> orgChunk = buildChunk(change.startOriginal, change.endOriginal, original);
Chunk<T> revChunk = buildChunk(change.startRevised, change.endRevised, revised);
switch (change.deltaType) {
case DELETE:
patch.addDelta(new DeleteDelta<>(orgChunk, revChunk));
break;
case INSERT:
patch.addDelta(new InsertDelta<>(orgChunk, revChunk));
break;
case CHANGE:
patch.addDelta(new ChangeDelta<>(orgChunk, revChunk));
break;
default:
}
startOriginal = change.endOriginal;
startRevised = change.endRevised;
}
if (includeEquals && startOriginal < original.size()) {
patch.addDelta(new EqualDelta<T>(
buildChunk(startOriginal, original.size(), original),
buildChunk(startRevised, revised.size(), revised)));
}
return patch;
}
}

View File

@@ -14,19 +14,15 @@
* limitations under the License.
*/
package com.github.difflib.patch;
/**
* Thrown whenever a delta cannot be applied as a patch to a given text.
*
* @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
*/
public class PatchFailedException extends DiffException {
private static final long serialVersionUID = 1L;
public PatchFailedException() {
}
public PatchFailedException(String msg) {
super(msg);
}

View File

@@ -14,9 +14,7 @@
* limitations under the License.
*/
package com.github.difflib.patch;
/**
*
* @author tw
*/
public enum VerifyChunk {

View File

@@ -14,64 +14,50 @@
* limitations under the License.
*/
package com.github.difflib.text;
import java.io.Serializable;
import java.util.Objects;
/**
* Describes the diff row in form [tag, oldLine, newLine) for showing the difference between two texts
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
*/
public final class DiffRow implements Serializable {
private Tag tag;
private final String oldLine;
private final String newLine;
private Tag tag;
public DiffRow(Tag tag, String oldLine, String newLine) {
this.tag = tag;
this.oldLine = oldLine;
this.newLine = newLine;
}
public enum Tag {
INSERT, DELETE, CHANGE, EQUAL
}
/**
* @return the tag
*/
public Tag getTag() {
return tag;
}
/**
* @param tag the tag to set
*/
public void setTag(Tag tag) {
this.tag = tag;
}
/**
* @return the oldLine
*/
public String getOldLine() {
return oldLine;
}
/**
* @return the newLine
*/
public String getNewLine() {
return newLine;
}
@Override
public int hashCode() {
return Objects.hash(newLine, oldLine, tag);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -107,9 +93,11 @@ public final class DiffRow implements Serializable {
}
return true;
}
@Override
public String toString() {
return "[" + this.tag + "," + this.oldLine + "," + this.newLine + "]";
}
public enum Tag {
INSERT, DELETE, CHANGE, EQUAL
}
}

View File

@@ -14,50 +14,38 @@
* limitations under the License.
*/
package com.github.difflib.text;
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.ChangeDelta;
import com.github.difflib.patch.Chunk;
import com.github.difflib.patch.DeleteDelta;
import com.github.difflib.patch.DeltaType;
import com.github.difflib.patch.InsertDelta;
import com.github.difflib.patch.Patch;
import com.github.difflib.patch.*;
import com.github.difflib.text.DiffRow.Tag;
import com.github.difflib.text.deltamerge.DeltaMergeUtils;
import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toList;
/**
* This class for generating DiffRows for side-by-sidy view. You can customize
* the way of generating. For example, show inline diffs on not, ignoring white
* spaces or/and blank lines and so on. All parameters for generating are
* optional. If you do not specify them, the class will use the default values.
*
* <p>
* These values are: showInlineDiffs = false; ignoreWhiteSpaces = true;
* ignoreBlankLines = true; ...
*
* <p>
* For instantiating the DiffRowGenerator you should use the its builder. Like
* in example <code>
* DiffRowGenerator generator = new DiffRowGenerator.Builder().showInlineDiffs(true).
* ignoreWhiteSpaces(true).columnWidth(100).build();
* DiffRowGenerator generator = new DiffRowGenerator.Builder().showInlineDiffs(true).
* ignoreWhiteSpaces(true).columnWidth(100).build();
* </code>
*/
public final class DiffRowGenerator {
public static final BiPredicate<String, String> DEFAULT_EQUALIZER = Object::equals;
public static final BiPredicate<String, String> IGNORE_WHITESPACE_EQUALIZER = (original, revised)
-> adjustWhitespace(original).equals(adjustWhitespace(revised));
public static final Function<String, String> LINE_NORMALIZER_FOR_HTML = StringUtils::normalize;
/**
* Splitting lines by character to achieve char by char diff checking.
*/
@@ -68,31 +56,63 @@ public final class DiffRowGenerator {
}
return list;
};
public static final Pattern SPLIT_BY_WORD_PATTERN = Pattern.compile("\\s+|[,.\\[\\](){}/\\\\*+\\-#<>;:&\\']+");
/**
* Splitting lines by word to achieve word by word diff checking.
*/
public static final Function<String, List<String>> SPLITTER_BY_WORD = line -> splitStringPreserveDelimiter(line, SPLIT_BY_WORD_PATTERN);
public static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
public static final BiPredicate<String, String> IGNORE_WHITESPACE_EQUALIZER = (original, revised)
-> adjustWhitespace(original).equals(adjustWhitespace(revised));
public static final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> DEFAULT_INLINE_DELTA_MERGER = InlineDeltaMergeInfo::getDeltas;
/**
* Merge diffs which are separated by equalities consisting of whitespace only.
*/
public static final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> WHITESPACE_EQUALITIES_MERGER = deltaMergeInfo -> DeltaMergeUtils
.mergeInlineDeltas(deltaMergeInfo, equalities -> equalities.stream().allMatch(s -> s==null || s.replaceAll("\\s+", "").equals("")));
.mergeInlineDeltas(deltaMergeInfo, equalities -> equalities.stream().allMatch(s -> s == null || s.replaceAll("\\s+", "").equals("")));
private final int columnWidth;
private final BiPredicate<String, String> equalizer;
private final boolean ignoreWhiteSpaces;
private final Function<String, List<String>> inlineDiffSplitter;
private final boolean mergeOriginalRevised;
private final BiFunction<Tag, Boolean, String> newTag;
private final BiFunction<Tag, Boolean, String> oldTag;
private final boolean reportLinesUnchanged;
private final Function<String, String> lineNormalizer;
private final Function<String, String> processDiffs;
private final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger;
private final boolean showInlineDiffs;
private final boolean replaceOriginalLinefeedInChangesWithSpaces;
private final boolean decompressDeltas;
private DiffRowGenerator(Builder builder) {
showInlineDiffs = builder.showInlineDiffs;
ignoreWhiteSpaces = builder.ignoreWhiteSpaces;
oldTag = builder.oldTag;
newTag = builder.newTag;
columnWidth = builder.columnWidth;
mergeOriginalRevised = builder.mergeOriginalRevised;
inlineDiffSplitter = builder.inlineDiffSplitter;
decompressDeltas = builder.decompressDeltas;
if (builder.equalizer != null) {
equalizer = builder.equalizer;
} else {
equalizer = ignoreWhiteSpaces ? IGNORE_WHITESPACE_EQUALIZER : DEFAULT_EQUALIZER;
}
reportLinesUnchanged = builder.reportLinesUnchanged;
lineNormalizer = builder.lineNormalizer;
processDiffs = builder.processDiffs;
inlineDeltaMerger = builder.inlineDeltaMerger;
replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
Objects.requireNonNull(inlineDiffSplitter);
Objects.requireNonNull(lineNormalizer);
Objects.requireNonNull(inlineDeltaMerger);
}
public static Builder create() {
return new Builder();
}
private static String adjustWhitespace(String raw) {
return WHITESPACE_PATTERN.matcher(raw.trim()).replaceAll(" ");
}
protected final static List<String> splitStringPreserveDelimiter(String str, Pattern SPLIT_PATTERN) {
List<String> list = new ArrayList<>();
if (str != null) {
@@ -111,22 +131,19 @@ public final class DiffRowGenerator {
}
return list;
}
/**
* Wrap the elements in the sequence with the given tag
*
* @param startPosition the position from which tag should start. The
* counting start from a zero.
* @param endPosition the position before which tag should should be closed.
* @param tagGenerator the tag generator
* counting start from a zero.
* @param endPosition the position before which tag should should be closed.
* @param tagGenerator the tag generator
*/
static void wrapInTag(List<String> sequence, int startPosition,
int endPosition, Tag tag, BiFunction<Tag, Boolean, String> tagGenerator,
Function<String, String> processDiffs, boolean replaceLinefeedWithSpace) {
int endPosition, Tag tag, BiFunction<Tag, Boolean, String> tagGenerator,
Function<String, String> processDiffs, boolean replaceLinefeedWithSpace) {
int endPos = endPosition;
while (endPos >= startPosition) {
//search position for end tag
while (endPos > startPosition) {
if (!"\n".equals(sequence.get(endPos - 1))) {
@@ -137,18 +154,15 @@ public final class DiffRowGenerator {
}
endPos--;
}
if (endPos == startPosition) {
break;
}
sequence.add(endPos, tagGenerator.apply(tag, false));
if (processDiffs != null) {
sequence.set(endPos - 1,
processDiffs.apply(sequence.get(endPos - 1)));
}
endPos--;
//search position for end tag
while (endPos > startPosition) {
if ("\n".equals(sequence.get(endPos - 1))) {
@@ -164,82 +178,34 @@ public final class DiffRowGenerator {
}
endPos--;
}
sequence.add(endPos, tagGenerator.apply(tag, true));
endPos--;
}
}
private final int columnWidth;
private final BiPredicate<String, String> equalizer;
private final boolean ignoreWhiteSpaces;
private final Function<String, List<String>> inlineDiffSplitter;
private final boolean mergeOriginalRevised;
private final BiFunction<Tag, Boolean, String> newTag;
private final BiFunction<Tag, Boolean, String> oldTag;
private final boolean reportLinesUnchanged;
private final Function<String, String> lineNormalizer;
private final Function<String, String> processDiffs;
private final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger;
private final boolean showInlineDiffs;
private final boolean replaceOriginalLinefeedInChangesWithSpaces;
private final boolean decompressDeltas;
private DiffRowGenerator(Builder builder) {
showInlineDiffs = builder.showInlineDiffs;
ignoreWhiteSpaces = builder.ignoreWhiteSpaces;
oldTag = builder.oldTag;
newTag = builder.newTag;
columnWidth = builder.columnWidth;
mergeOriginalRevised = builder.mergeOriginalRevised;
inlineDiffSplitter = builder.inlineDiffSplitter;
decompressDeltas = builder.decompressDeltas;
if (builder.equalizer != null) {
equalizer = builder.equalizer;
} else {
equalizer = ignoreWhiteSpaces ? IGNORE_WHITESPACE_EQUALIZER : DEFAULT_EQUALIZER;
}
reportLinesUnchanged = builder.reportLinesUnchanged;
lineNormalizer = builder.lineNormalizer;
processDiffs = builder.processDiffs;
inlineDeltaMerger = builder.inlineDeltaMerger;
replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
Objects.requireNonNull(inlineDiffSplitter);
Objects.requireNonNull(lineNormalizer);
Objects.requireNonNull(inlineDeltaMerger);
}
/**
* Get the DiffRows describing the difference between original and revised
* texts using the given patch. Useful for displaying side-by-side diff.
*
* @param original the original text
* @param revised the revised text
* @param revised the revised text
* @return the DiffRows between original and revised texts
*/
public List<DiffRow> generateDiffRows(List<String> original, List<String> revised) {
return generateDiffRows(original, DiffUtils.diff(original, revised, equalizer));
}
/**
* Generates the DiffRows describing the difference between original and
* revised texts using the given patch. Useful for displaying side-by-side
* diff.
*
* @param original the original text
* @param patch the given patch
* @param patch the given patch
* @return the DiffRows between original and revised texts
*/
public List<DiffRow> generateDiffRows(final List<String> original, Patch<String> patch) {
List<DiffRow> diffRows = new ArrayList<>();
int endPos = 0;
final List<AbstractDelta<String>> deltaList = patch.getDeltas();
if (decompressDeltas) {
for (AbstractDelta<String> originalDelta : deltaList) {
for (AbstractDelta<String> delta : decompressDeltas(originalDelta)) {
@@ -251,25 +217,21 @@ public final class DiffRowGenerator {
endPos = transformDeltaIntoDiffRow(original, endPos, diffRows, delta);
}
}
// Copy the final matching chunk if any.
for (String line : original.subList(endPos, original.size())) {
diffRows.add(buildDiffRow(Tag.EQUAL, line, line));
}
return diffRows;
}
/**
* Transforms one patch delta into a DiffRow object.
*/
private int transformDeltaIntoDiffRow(final List<String> original, int endPos, List<DiffRow> diffRows, AbstractDelta<String> delta) {
Chunk<String> orig = delta.getSource();
Chunk<String> rev = delta.getTarget();
for (String line : original.subList(endPos, orig.getPosition())) {
diffRows.add(buildDiffRow(Tag.EQUAL, line, line));
}
switch (delta.getType()) {
case INSERT:
for (String line : rev.getLines()) {
@@ -292,10 +254,8 @@ public final class DiffRowGenerator {
}
}
}
return orig.last() + 1;
}
/**
* Decompresses ChangeDeltas with different source and target size to a
* ChangeDelta with same size and a following InsertDelta or DeleteDelta.
@@ -307,15 +267,12 @@ public final class DiffRowGenerator {
if (delta.getType() == DeltaType.CHANGE && delta.getSource().size() != delta.getTarget().size()) {
List<AbstractDelta<String>> deltas = new ArrayList<>();
//System.out.println("decompress this " + delta);
int minSize = Math.min(delta.getSource().size(), delta.getTarget().size());
Chunk<String> orig = delta.getSource();
Chunk<String> rev = delta.getTarget();
deltas.add(new ChangeDelta<String>(
new Chunk<>(orig.getPosition(), orig.getLines().subList(0, minSize)),
new Chunk<>(rev.getPosition(), rev.getLines().subList(0, minSize))));
if (orig.getLines().size() < rev.getLines().size()) {
deltas.add(new InsertDelta<String>(
new Chunk<>(orig.getPosition() + minSize, Collections.emptyList()),
@@ -327,10 +284,8 @@ public final class DiffRowGenerator {
}
return deltas;
}
return Collections.singletonList(delta);
}
private DiffRow buildDiffRow(Tag type, String orgline, String newline) {
if (reportLinesUnchanged) {
return new DiffRow(type, orgline, newline);
@@ -352,21 +307,18 @@ public final class DiffRowGenerator {
return new DiffRow(type, wrapOrg, wrapNew);
}
}
private DiffRow buildDiffRowWithoutNormalizing(Tag type, String orgline, String newline) {
return new DiffRow(type,
StringUtils.wrapText(orgline, columnWidth),
StringUtils.wrapText(newline, columnWidth));
}
List<String> normalizeLines(List<String> list) {
return reportLinesUnchanged
? list
: list.stream()
.map(lineNormalizer::apply)
.collect(toList());
.map(lineNormalizer::apply)
.collect(toList());
}
/**
* Add the inline diffs for given delta
*
@@ -379,15 +331,12 @@ public final class DiffRowGenerator {
List<String> revList;
String joinedOrig = String.join("\n", orig);
String joinedRev = String.join("\n", rev);
origList = inlineDiffSplitter.apply(joinedOrig);
revList = inlineDiffSplitter.apply(joinedRev);
List<AbstractDelta<String>> originalInlineDeltas = DiffUtils.diff(origList, revList, equalizer)
.getDeltas();
List<AbstractDelta<String>> inlineDeltas = inlineDeltaMerger
.apply(new InlineDeltaMergeInfo(originalInlineDeltas, origList, revList));
Collections.reverse(inlineDeltas);
for (AbstractDelta<String> inlineDelta : inlineDeltas) {
Chunk<String> inlineOrig = inlineDelta.getSource();
@@ -435,7 +384,6 @@ public final class DiffRowGenerator {
for (String character : revList) {
revResult.append(character);
}
List<String> original = Arrays.asList(origResult.toString().split("\n"));
List<String> revised = Arrays.asList(revResult.toString().split("\n"));
List<DiffRow> diffRows = new ArrayList<>();
@@ -447,7 +395,6 @@ public final class DiffRowGenerator {
}
return diffRows;
}
private String preprocessLine(String line) {
if (columnWidth == 0) {
return lineNormalizer.apply(line);
@@ -455,24 +402,19 @@ public final class DiffRowGenerator {
return StringUtils.wrapText(lineNormalizer.apply(line), columnWidth);
}
}
/**
* This class used for building the DiffRowGenerator.
*
* @author dmitry
*
*/
public static class Builder {
private boolean showInlineDiffs = false;
private boolean ignoreWhiteSpaces = false;
private boolean decompressDeltas = true;
private BiFunction<Tag, Boolean, String> oldTag
= (tag, f) -> f ? "<span class=\"editOldInline\">" : "</span>";
private BiFunction<Tag, Boolean, String> newTag
= (tag, f) -> f ? "<span class=\"editNewInline\">" : "</span>";
private int columnWidth = 0;
private boolean mergeOriginalRevised = false;
private boolean reportLinesUnchanged = false;
@@ -482,10 +424,8 @@ public final class DiffRowGenerator {
private BiPredicate<String, String> equalizer = null;
private boolean replaceOriginalLinefeedInChangesWithSpaces = false;
private Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger = DEFAULT_INLINE_DELTA_MERGER;
private Builder() {
}
/**
* Show inline diffs in generating diff rows or not.
*
@@ -496,7 +436,6 @@ public final class DiffRowGenerator {
showInlineDiffs = val;
return this;
}
/**
* Ignore white spaces in generating diff rows or not.
*
@@ -507,7 +446,6 @@ public final class DiffRowGenerator {
ignoreWhiteSpaces = val;
return this;
}
/**
* Report all lines without markup on the old or new text.
*
@@ -518,7 +456,6 @@ public final class DiffRowGenerator {
reportLinesUnchanged = val;
return this;
}
/**
* Generator for Old-Text-Tags.
*
@@ -529,7 +466,6 @@ public final class DiffRowGenerator {
this.oldTag = generator;
return this;
}
/**
* Generator for Old-Text-Tags.
*
@@ -540,7 +476,6 @@ public final class DiffRowGenerator {
this.oldTag = (tag, f) -> generator.apply(f);
return this;
}
/**
* Generator for New-Text-Tags.
*
@@ -551,7 +486,6 @@ public final class DiffRowGenerator {
this.newTag = generator;
return this;
}
/**
* Generator for New-Text-Tags.
*
@@ -562,7 +496,6 @@ public final class DiffRowGenerator {
this.newTag = (tag, f) -> generator.apply(f);
return this;
}
/**
* Processor for diffed text parts. Here e.g. whitecharacters could be
* replaced by something visible.
@@ -574,13 +507,12 @@ public final class DiffRowGenerator {
this.processDiffs = processDiffs;
return this;
}
/**
* Set the column width of generated lines of original and revised
* texts.
*
* @param width the width to set. Making it &lt; 0 doesn't make any
* sense. Default 80.
* sense. Default 80.
* @return builder with config of column width
*/
public Builder columnWidth(int width) {
@@ -589,7 +521,6 @@ public final class DiffRowGenerator {
}
return this;
}
/**
* Build the DiffRowGenerator. If some parameters is not set, the
* default values are used.
@@ -599,7 +530,6 @@ public final class DiffRowGenerator {
public DiffRowGenerator build() {
return new DiffRowGenerator(this);
}
/**
* Merge the complete result within the original text. This makes sense
* for one line display.
@@ -611,7 +541,6 @@ public final class DiffRowGenerator {
this.mergeOriginalRevised = mergeOriginalRevised;
return this;
}
/**
* Deltas could be in a state, that would produce some unreasonable
* results within an inline diff. So the deltas are decompressed into
@@ -624,7 +553,6 @@ public final class DiffRowGenerator {
this.decompressDeltas = decompressDeltas;
return this;
}
/**
* Per default each character is separatly processed. This variant
* introduces processing by word, which does not deliver in word
@@ -639,7 +567,6 @@ public final class DiffRowGenerator {
inlineDiffSplitter = inlineDiffByWord ? SPLITTER_BY_WORD : SPLITTER_BY_CHARACTER;
return this;
}
/**
* To provide some customized splitting a splitter can be provided. Here
* someone could think about sentence splitter, comma splitter or stuff
@@ -652,7 +579,6 @@ public final class DiffRowGenerator {
this.inlineDiffSplitter = inlineDiffSplitter;
return this;
}
/**
* By default DiffRowGenerator preprocesses lines for HTML output. Tabs
* and special HTML characters like "&lt;" are replaced with its encoded
@@ -666,7 +592,6 @@ public final class DiffRowGenerator {
this.lineNormalizer = lineNormalizer;
return this;
}
/**
* Provide an equalizer for diff processing.
*
@@ -677,7 +602,6 @@ public final class DiffRowGenerator {
this.equalizer = equalizer;
return this;
}
/**
* Sometimes it happens that a change contains multiple lines. If there
* is no correspondence in old and new. To keep the merged line more
@@ -690,7 +614,6 @@ public final class DiffRowGenerator {
this.replaceOriginalLinefeedInChangesWithSpaces = replace;
return this;
}
/**
* Provide an inline delta merger for use case specific delta optimizations.
*

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.github.difflib.text;
import java.util.List;
import static java.util.stream.Collectors.toList;
final class StringUtils {
private StringUtils() {
}
/**
* Replaces all opening and closing tags with <code>&lt;</code> or <code>&gt;</code>.
*
@@ -29,21 +29,18 @@ final class StringUtils {
public static String htmlEntites(String str) {
return str.replace("<", "&lt;").replace(">", "&gt;");
}
public static String normalize(String str) {
return htmlEntites(str).replace("\t", " ");
}
public static List<String> wrapText(List<String> list, int columnWidth) {
return list.stream()
.map(line -> wrapText(line, columnWidth))
.collect(toList());
}
/**
* Wrap the text with the given column width
*
* @param line the text
* @param line the text
* @param columnWidth the given column
* @return the wrapped text
*/
@@ -57,27 +54,21 @@ final class StringUtils {
int length = line.length();
int delimiter = "<br/>".length();
int widthIndex = columnWidth;
StringBuilder b = new StringBuilder(line);
for (int count = 0; length > widthIndex; count++) {
int breakPoint = widthIndex + delimiter * count;
if (Character.isHighSurrogate(b.charAt(breakPoint - 1)) &&
Character.isLowSurrogate(b.charAt(breakPoint))) {
// Shift a breakpoint that would split a supplemental code-point.
breakPoint += 1;
if (breakPoint == b.length()) {
// Break before instead of after if this is the last code-point.
breakPoint -= 2;
}
Character.isLowSurrogate(b.charAt(breakPoint))) {
// Shift a breakpoint that would split a supplemental code-point.
breakPoint += 1;
if (breakPoint == b.length()) {
// Break before instead of after if this is the last code-point.
breakPoint -= 2;
}
}
b.insert(breakPoint, "<br/>");
widthIndex += columnWidth;
}
return b.toString();
}
private StringUtils() {
}
}

View File

@@ -14,39 +14,35 @@
* limitations under the License.
*/
package com.github.difflib.text.deltamerge;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.ChangeDelta;
import com.github.difflib.patch.Chunk;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Provides utility features for merge inline deltas
*
* @author <a href="christian.meier@epictec.ch">Christian Meier</a>
*/
final public class DeltaMergeUtils {
private DeltaMergeUtils() {
}
public static List<AbstractDelta<String>> mergeInlineDeltas(InlineDeltaMergeInfo deltaMergeInfo,
Predicate<List<String>> replaceEquality) {
Predicate<List<String>> replaceEquality) {
final List<AbstractDelta<String>> originalDeltas = deltaMergeInfo.getDeltas();
if (originalDeltas.size() < 2) {
return originalDeltas;
}
final List<AbstractDelta<String>> newDeltas = new ArrayList<>();
newDeltas.add(originalDeltas.get(0));
for (int i = 1; i < originalDeltas.size(); i++) {
final AbstractDelta<String> previousDelta = newDeltas.get(newDeltas.size()-1);
final AbstractDelta<String> previousDelta = newDeltas.get(newDeltas.size() - 1);
final AbstractDelta<String> currentDelta = originalDeltas.get(i);
final List<String> equalities = deltaMergeInfo.getOrigList().subList(
previousDelta.getSource().getPosition() + previousDelta.getSource().size(),
currentDelta.getSource().getPosition());
if (replaceEquality.test(equalities)) {
// Merge the previous delta, the equality and the current delta into one
// ChangeDelta and replace the previous delta by this new ChangeDelta.
@@ -54,26 +50,19 @@ final public class DeltaMergeUtils {
allSourceLines.addAll(previousDelta.getSource().getLines());
allSourceLines.addAll(equalities);
allSourceLines.addAll(currentDelta.getSource().getLines());
final List<String> allTargetLines = new ArrayList<>();
allTargetLines.addAll(previousDelta.getTarget().getLines());
allTargetLines.addAll(equalities);
allTargetLines.addAll(currentDelta.getTarget().getLines());
final ChangeDelta<String> replacement = new ChangeDelta<>(
new Chunk<>(previousDelta.getSource().getPosition(), allSourceLines),
new Chunk<>(previousDelta.getTarget().getPosition(), allTargetLines));
newDeltas.remove(newDeltas.size()-1);
newDeltas.remove(newDeltas.size() - 1);
newDeltas.add(replacement);
} else {
newDeltas.add(currentDelta);
}
}
return newDeltas;
}
private DeltaMergeUtils() {
}
}

View File

@@ -14,11 +14,9 @@
* limitations under the License.
*/
package com.github.difflib.text.deltamerge;
import java.util.List;
import com.github.difflib.patch.AbstractDelta;
import java.util.List;
/**
* Holds the information required to merge deltas originating from an inline
* diff
@@ -26,25 +24,20 @@ import com.github.difflib.patch.AbstractDelta;
* @author <a href="christian.meier@epictec.ch">Christian Meier</a>
*/
public final class InlineDeltaMergeInfo {
private final List<AbstractDelta<String>> deltas;
private final List<String> origList;
private final List<String> revList;
public InlineDeltaMergeInfo(List<AbstractDelta<String>> deltas, List<String> origList, List<String> revList) {
this.deltas = deltas;
this.origList = origList;
this.revList = revList;
}
public List<AbstractDelta<String>> getDeltas() {
return deltas;
}
public List<String> getOrigList() {
return origList;
}
public List<String> getRevList() {
return revList;
}

View File

@@ -14,47 +14,46 @@
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
import com.github.difflib.patch.PatchFailedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
/**
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public final class UnifiedDiff {
private final List<UnifiedDiffFile> files = new ArrayList<>();
private String header;
private String tail;
private final List<UnifiedDiffFile> files = new ArrayList<>();
public static UnifiedDiff from(String header, String tail, UnifiedDiffFile... files) {
UnifiedDiff diff = new UnifiedDiff();
diff.setHeader(header);
diff.setTailTxt(tail);
for (UnifiedDiffFile file : files) {
diff.addFile(file);
}
return diff;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
void addFile(UnifiedDiffFile file) {
files.add(file);
}
public List<UnifiedDiffFile> getFiles() {
return Collections.unmodifiableList(files);
}
void setTailTxt(String tailTxt) {
this.tail = tailTxt;
}
public String getTail() {
return tail;
}
public List<String> applyPatchTo(Predicate<String> findFile, List<String> originalLines) throws PatchFailedException {
UnifiedDiffFile file = files.stream()
.filter(diff -> findFile.test(diff.getFromFile()))
@@ -65,14 +64,4 @@ public final class UnifiedDiff {
return originalLines;
}
}
public static UnifiedDiff from(String header, String tail, UnifiedDiffFile... files) {
UnifiedDiff diff = new UnifiedDiff();
diff.setHeader(header);
diff.setTailTxt(tail);
for (UnifiedDiffFile file : files) {
diff.addFile(file);
}
return diff;
}
}

View File

@@ -14,16 +14,13 @@
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
import com.github.difflib.patch.Patch;
/**
* Data structure for one patched file from a unified diff file.
*
* Data structure for one patched file from a unified diff file.
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public final class UnifiedDiffFile {
private String diffCommand;
private String fromFile;
private String fromTimestamp;
@@ -44,99 +41,6 @@ public final class UnifiedDiffFile {
private Patch<String> patch = new Patch<>();
private boolean noNewLineAtTheEndOfTheFile = false;
private Integer similarityIndex;
public String getDiffCommand() {
return diffCommand;
}
public void setDiffCommand(String diffCommand) {
this.diffCommand = diffCommand;
}
public String getFromFile() {
return fromFile;
}
public void setFromFile(String fromFile) {
this.fromFile = fromFile;
}
public String getToFile() {
return toFile;
}
public void setToFile(String toFile) {
this.toFile = toFile;
}
public void setIndex(String index) {
this.index = index;
}
public String getIndex() {
return index;
}
public Patch<String> getPatch() {
return patch;
}
public String getFromTimestamp() {
return fromTimestamp;
}
public void setFromTimestamp(String fromTimestamp) {
this.fromTimestamp = fromTimestamp;
}
public String getToTimestamp() {
return toTimestamp;
}
public void setToTimestamp(String toTimestamp) {
this.toTimestamp = toTimestamp;
}
public Integer getSimilarityIndex() {
return similarityIndex;
}
public void setSimilarityIndex(Integer similarityIndex) {
this.similarityIndex = similarityIndex;
}
public String getRenameFrom() {
return renameFrom;
}
public void setRenameFrom(String renameFrom) {
this.renameFrom = renameFrom;
}
public String getRenameTo() {
return renameTo;
}
public void setRenameTo(String renameTo) {
this.renameTo = renameTo;
}
public String getCopyFrom() {
return copyFrom;
}
public void setCopyFrom(String copyFrom) {
this.copyFrom = copyFrom;
}
public String getCopyTo() {
return copyTo;
}
public void setCopyTo(String copyTo) {
this.copyTo = copyTo;
}
public static UnifiedDiffFile from(String fromFile, String toFile, Patch<String> patch) {
UnifiedDiffFile file = new UnifiedDiffFile();
file.setFromFile(fromFile);
@@ -144,67 +48,120 @@ public final class UnifiedDiffFile {
file.patch = patch;
return file;
}
public void setNewFileMode(String newFileMode) {
this.newFileMode = newFileMode;
public String getDiffCommand() {
return diffCommand;
}
public void setDiffCommand(String diffCommand) {
this.diffCommand = diffCommand;
}
public String getFromFile() {
return fromFile;
}
public void setFromFile(String fromFile) {
this.fromFile = fromFile;
}
public String getToFile() {
return toFile;
}
public void setToFile(String toFile) {
this.toFile = toFile;
}
public String getIndex() {
return index;
}
public void setIndex(String index) {
this.index = index;
}
public Patch<String> getPatch() {
return patch;
}
public String getFromTimestamp() {
return fromTimestamp;
}
public void setFromTimestamp(String fromTimestamp) {
this.fromTimestamp = fromTimestamp;
}
public String getToTimestamp() {
return toTimestamp;
}
public void setToTimestamp(String toTimestamp) {
this.toTimestamp = toTimestamp;
}
public Integer getSimilarityIndex() {
return similarityIndex;
}
public void setSimilarityIndex(Integer similarityIndex) {
this.similarityIndex = similarityIndex;
}
public String getRenameFrom() {
return renameFrom;
}
public void setRenameFrom(String renameFrom) {
this.renameFrom = renameFrom;
}
public String getRenameTo() {
return renameTo;
}
public void setRenameTo(String renameTo) {
this.renameTo = renameTo;
}
public String getCopyFrom() {
return copyFrom;
}
public void setCopyFrom(String copyFrom) {
this.copyFrom = copyFrom;
}
public String getCopyTo() {
return copyTo;
}
public void setCopyTo(String copyTo) {
this.copyTo = copyTo;
}
public String getNewFileMode() {
return newFileMode;
}
public void setNewFileMode(String newFileMode) {
this.newFileMode = newFileMode;
}
public String getDeletedFileMode() {
return deletedFileMode;
}
public void setDeletedFileMode(String deletedFileMode) {
this.deletedFileMode = deletedFileMode;
}
public String getOldMode() {
return oldMode;
}
public void setOldMode(String oldMode) {
this.oldMode = oldMode;
}
public String getNewMode() {
return newMode;
}
public void setNewMode(String newMode) {
this.newMode = newMode;
}
public String getBinaryAdded() {
return binaryAdded;
}
public void setBinaryAdded(String binaryAdded) {
this.binaryAdded = binaryAdded;
}
public String getBinaryDeleted() {
return binaryDeleted;
}
public void setBinaryDeleted(String binaryDeleted) {
this.binaryDeleted = binaryDeleted;
}
public String getBinaryEdited() {
return binaryEdited;
}
public void setBinaryEdited(String binaryEdited) {
this.binaryEdited = binaryEdited;
}
public boolean isNoNewLineAtTheEndOfTheFile() {
return noNewLineAtTheEndOfTheFile;
}
public void setNoNewLineAtTheEndOfTheFile(boolean noNewLineAtTheEndOfTheFile) {
this.noNewLineAtTheEndOfTheFile = noNewLineAtTheEndOfTheFile;
}

View File

@@ -14,30 +14,22 @@
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
/**
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public class UnifiedDiffParserException extends RuntimeException {
public UnifiedDiffParserException() {
}
public UnifiedDiffParserException(String message) {
super(message);
}
public UnifiedDiffParserException(String message, Throwable cause) {
super(message, cause);
}
public UnifiedDiffParserException(Throwable cause) {
super(cause);
}
public UnifiedDiffParserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -14,14 +14,10 @@
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
import com.github.difflib.patch.ChangeDelta;
import com.github.difflib.patch.Chunk;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -31,19 +27,16 @@ import java.util.logging.Logger;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author Tobias Warneke (t.warneke@gmx.net)
*/
public final class UnifiedDiffReader {
static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern.compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
static final Pattern TIMESTAMP_REGEXP = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}\\.\\d{3,})(?: [+-]\\d+)?");
private static final Logger LOG = Logger.getLogger(UnifiedDiffReader.class.getName());
private final InternalUnifiedDiffReader READER;
private final UnifiedDiff data = new UnifiedDiff();
private UnifiedDiffFile actualFile;
private final UnifiedDiffLine DIFF_COMMAND = new UnifiedDiffLine(true, "^diff\\s", this::processDiff);
private final UnifiedDiffLine SIMILARITY_INDEX = new UnifiedDiffLine(true, "^similarity index (\\d+)%$", this::processSimilarityIndex);
private final UnifiedDiffLine INDEX = new UnifiedDiffLine(true, "^index\\s[\\da-zA-Z]+\\.\\.[\\da-zA-Z]+(\\s(\\d+))?$", this::processIndex);
@@ -51,32 +44,57 @@ public final class UnifiedDiffReader {
private final UnifiedDiffLine TO_FILE = new UnifiedDiffLine(true, "^\\+\\+\\+\\s", this::processToFile);
private final UnifiedDiffLine RENAME_FROM = new UnifiedDiffLine(true, "^rename\\sfrom\\s(.+)$", this::processRenameFrom);
private final UnifiedDiffLine RENAME_TO = new UnifiedDiffLine(true, "^rename\\sto\\s(.+)$", this::processRenameTo);
private final UnifiedDiffLine COPY_FROM = new UnifiedDiffLine(true, "^copy\\sfrom\\s(.+)$", this::processCopyFrom);
private final UnifiedDiffLine COPY_TO = new UnifiedDiffLine(true, "^copy\\sto\\s(.+)$", this::processCopyTo);
private final UnifiedDiffLine NEW_FILE_MODE = new UnifiedDiffLine(true, "^new\\sfile\\smode\\s(\\d+)", this::processNewFileMode);
private final UnifiedDiffLine DELETED_FILE_MODE = new UnifiedDiffLine(true, "^deleted\\sfile\\smode\\s(\\d+)", this::processDeletedFileMode);
private final UnifiedDiffLine OLD_MODE = new UnifiedDiffLine(true, "^old\\smode\\s(\\d+)", this::processOldMode);
private final UnifiedDiffLine NEW_MODE = new UnifiedDiffLine(true, "^new\\smode\\s(\\d+)", this::processNewMode);
private final UnifiedDiffLine BINARY_ADDED = new UnifiedDiffLine(true, "^Binary\\sfiles\\s/dev/null\\sand\\sb/(.+)\\sdiffer", this::processBinaryAdded);
private final UnifiedDiffLine BINARY_DELETED = new UnifiedDiffLine(true, "^Binary\\sfiles\\sa/(.+)\\sand\\s/dev/null\\sdiffer", this::processBinaryDeleted);
private final UnifiedDiffLine BINARY_EDITED = new UnifiedDiffLine(true, "^Binary\\sfiles\\sa/(.+)\\sand\\sb/(.+)\\sdiffer", this::processBinaryEdited);
private List<String> originalTxt = new ArrayList<>();
private List<String> revisedTxt = new ArrayList<>();
private List<Integer> addLineIdxList = new ArrayList<>();
private List<Integer> delLineIdxList = new ArrayList<>();
private int old_ln;
private int old_size;
private int new_ln;
private int new_size;
private final UnifiedDiffLine CHUNK = new UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP, this::processChunk);
private final UnifiedDiffLine LINE_NORMAL = new UnifiedDiffLine("^\\s", this::processNormalLine);
private int delLineIdx = 0;
private final UnifiedDiffLine LINE_DEL = new UnifiedDiffLine("^-", this::processDelLine);
private int addLineIdx = 0;
private final UnifiedDiffLine LINE_NORMAL = new UnifiedDiffLine("^\\s", this::processNormalLine);
private final UnifiedDiffLine LINE_ADD = new UnifiedDiffLine("^\\+", this::processAddLine);
private UnifiedDiffFile actualFile;
UnifiedDiffReader(Reader reader) {
this.READER = new InternalUnifiedDiffReader(reader);
}
// schema = [[/^\s+/, normal], [/^diff\s/, start], [/^new file mode \d+$/, new_file],
// [/^deleted file mode \d+$/, deleted_file], [/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/, index],
// [/^---\s/, from_file], [/^\+\+\+\s/, to_file], [/^@@\s+\-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/, chunk],
static String[] parseFileNames(String line) {
String[] split = line.split(" ");
return new String[]{
split[2].replaceAll("^a/", ""),
split[3].replaceAll("^b/", "")
};
}
/**
* To parse a diff file use this method.
*
* @param stream This is the diff file data.
* @return In a UnifiedDiff structure this diff file data is returned.
* @throws IOException
* @throws UnifiedDiffParserException
*/
public static UnifiedDiff parseUnifiedDiff(InputStream stream) throws IOException, UnifiedDiffParserException {
UnifiedDiffReader parser = new UnifiedDiffReader(new BufferedReader(new InputStreamReader(stream)));
return parser.parse();
}
private static Integer toInteger(MatchResult match, int group, int defValue) throws NumberFormatException {
return Integer.valueOf(Objects.toString(match.group(group), "" + defValue));
}
// schema = [[/^\s+/, normal], [/^diff\s/, start], [/^new file mode \d+$/, new_file],
// [/^deleted file mode \d+$/, deleted_file], [/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/, index],
// [/^---\s/, from_file], [/^\+\+\+\s/, to_file], [/^@@\s+\-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/, chunk],
// [/^-/, del], [/^\+/, add], [/^\\ No newline at end of file$/, eof]];
private UnifiedDiff parse() throws IOException, UnifiedDiffParserException {
// String headerTxt = "";
@@ -96,7 +114,6 @@ public final class UnifiedDiffReader {
// if (!"".equals(headerTxt)) {
// data.setHeader(headerTxt);
// }
String line = READER.readLine();
while (line != null) {
String headerTxt = "";
@@ -104,13 +121,13 @@ public final class UnifiedDiffReader {
while (line != null) {
LOG.log(Level.FINE, "parsing line {0}", line);
if (validLine(line, DIFF_COMMAND, SIMILARITY_INDEX, INDEX,
FROM_FILE, TO_FILE,
RENAME_FROM, RENAME_TO,
COPY_FROM, COPY_TO,
NEW_FILE_MODE, DELETED_FILE_MODE,
OLD_MODE, NEW_MODE,
BINARY_ADDED, BINARY_DELETED,
BINARY_EDITED, CHUNK)) {
FROM_FILE, TO_FILE,
RENAME_FROM, RENAME_TO,
COPY_FROM, COPY_TO,
NEW_FILE_MODE, DELETED_FILE_MODE,
OLD_MODE, NEW_MODE,
BINARY_ADDED, BINARY_DELETED,
BINARY_EDITED, CHUNK)) {
break;
} else {
headerTxt += line + "\n";
@@ -129,7 +146,7 @@ public final class UnifiedDiffReader {
COPY_FROM, COPY_TO,
NEW_FILE_MODE, DELETED_FILE_MODE,
OLD_MODE, NEW_MODE,
BINARY_ADDED , BINARY_DELETED,
BINARY_ADDED, BINARY_DELETED,
BINARY_EDITED)) {
throw new UnifiedDiffParserException("expected file start line not found");
}
@@ -140,7 +157,6 @@ public final class UnifiedDiffReader {
processLine(line, CHUNK);
while ((line = READER.readLine()) != null) {
line = checkForNoNewLineAtTheEndOfTheFile(line);
if (!processLine(line, LINE_NORMAL, LINE_ADD, LINE_DEL)) {
throw new UnifiedDiffParserException("expected data line not found");
}
@@ -152,14 +168,12 @@ public final class UnifiedDiffReader {
}
}
line = READER.readLine();
line = checkForNoNewLineAtTheEndOfTheFile(line);
}
if (line == null || (line.startsWith("--") && !line.startsWith("---"))) {
break;
}
}
if (READER.ready()) {
String tailTxt = "";
while (READER.ready()) {
@@ -170,10 +184,8 @@ public final class UnifiedDiffReader {
}
data.setTailTxt(tailTxt);
}
return data;
}
private String checkForNoNewLineAtTheEndOfTheFile(String line) throws IOException {
if ("\\ No newline at end of file".equals(line)) {
actualFile.setNoNewLineAtTheEndOfTheFile(true);
@@ -181,30 +193,6 @@ public final class UnifiedDiffReader {
}
return line;
}
static String[] parseFileNames(String line) {
String[] split = line.split(" ");
return new String[]{
split[2].replaceAll("^a/", ""),
split[3].replaceAll("^b/", "")
};
}
private static final Logger LOG = Logger.getLogger(UnifiedDiffReader.class.getName());
/**
* To parse a diff file use this method.
*
* @param stream This is the diff file data.
* @return In a UnifiedDiff structure this diff file data is returned.
* @throws IOException
* @throws UnifiedDiffParserException
*/
public static UnifiedDiff parseUnifiedDiff(InputStream stream) throws IOException, UnifiedDiffParserException {
UnifiedDiffReader parser = new UnifiedDiffReader(new BufferedReader(new InputStreamReader(stream)));
return parser.parse();
}
private boolean processLine(String line, UnifiedDiffLine... rules) throws UnifiedDiffParserException {
if (line == null) {
return false;
@@ -219,8 +207,7 @@ public final class UnifiedDiffReader {
return false;
//throw new UnifiedDiffParserException("parsing error at line " + line);
}
private boolean validLine(String line, UnifiedDiffLine ... rules) {
private boolean validLine(String line, UnifiedDiffLine... rules) {
if (line == null) {
return false;
}
@@ -232,7 +219,6 @@ public final class UnifiedDiffReader {
}
return false;
}
private void initFileIfNecessary() {
if (!originalTxt.isEmpty() || !revisedTxt.isEmpty()) {
throw new IllegalStateException();
@@ -243,7 +229,6 @@ public final class UnifiedDiffReader {
data.addFile(actualFile);
}
}
private void processDiff(MatchResult match, String line) {
//initFileIfNecessary();
LOG.log(Level.FINE, "start {0}", line);
@@ -252,22 +237,9 @@ public final class UnifiedDiffReader {
actualFile.setToFile(fromTo[1]);
actualFile.setDiffCommand(line);
}
private void processSimilarityIndex(MatchResult match, String line) {
actualFile.setSimilarityIndex(Integer.valueOf(match.group(1)));
}
private List<String> originalTxt = new ArrayList<>();
private List<String> revisedTxt = new ArrayList<>();
private List<Integer> addLineIdxList = new ArrayList<>();
private List<Integer> delLineIdxList = new ArrayList<>();
private int old_ln;
private int old_size;
private int new_ln;
private int new_size;
private int delLineIdx = 0;
private int addLineIdx = 0;
private void finalizeChunk() {
if (!originalTxt.isEmpty() || !revisedTxt.isEmpty()) {
actualFile.getPatch().addDelta(new ChangeDelta<>(new Chunk<>(
@@ -283,7 +255,6 @@ public final class UnifiedDiffReader {
addLineIdx = 0;
}
}
private void processNormalLine(MatchResult match, String line) {
String cline = line.substring(1);
originalTxt.add(cline);
@@ -291,21 +262,18 @@ public final class UnifiedDiffReader {
delLineIdx++;
addLineIdx++;
}
private void processAddLine(MatchResult match, String line) {
String cline = line.substring(1);
revisedTxt.add(cline);
addLineIdx++;
addLineIdxList.add(new_ln - 1 + addLineIdx);
}
private void processDelLine(MatchResult match, String line) {
String cline = line.substring(1);
originalTxt.add(cline);
delLineIdx++;
delLineIdxList.add(old_ln - 1 + delLineIdx);
}
private void processChunk(MatchResult match, String chunkStart) {
// finalizeChunk();
old_ln = toInteger(match, 1, 1);
@@ -319,75 +287,56 @@ public final class UnifiedDiffReader {
new_ln = 1;
}
}
private static Integer toInteger(MatchResult match, int group, int defValue) throws NumberFormatException {
return Integer.valueOf(Objects.toString(match.group(group), "" + defValue));
}
private void processIndex(MatchResult match, String line) {
//initFileIfNecessary();
LOG.log(Level.FINE, "index {0}", line);
actualFile.setIndex(line.substring(6));
}
private void processFromFile(MatchResult match, String line) {
//initFileIfNecessary();
actualFile.setFromFile(extractFileName(line));
actualFile.setFromTimestamp(extractTimestamp(line));
}
private void processToFile(MatchResult match, String line) {
//initFileIfNecessary();
actualFile.setToFile(extractFileName(line));
actualFile.setToTimestamp(extractTimestamp(line));
}
private void processRenameFrom(MatchResult match, String line) {
actualFile.setRenameFrom(match.group(1));
}
private void processRenameTo(MatchResult match, String line) {
actualFile.setRenameTo(match.group(1));
}
private void processCopyFrom(MatchResult match, String line) {
actualFile.setCopyFrom(match.group(1));
}
private void processCopyTo(MatchResult match, String line) {
actualFile.setCopyTo(match.group(1));
}
private void processNewFileMode(MatchResult match, String line) {
//initFileIfNecessary();
actualFile.setNewFileMode(match.group(1));
}
private void processDeletedFileMode(MatchResult match, String line) {
//initFileIfNecessary();
actualFile.setDeletedFileMode(match.group(1));
}
private void processOldMode(MatchResult match, String line) {
actualFile.setOldMode(match.group(1));
}
private void processNewMode(MatchResult match, String line) {
actualFile.setNewMode(match.group(1));
}
private void processBinaryAdded(MatchResult match, String line) {
actualFile.setBinaryAdded(match.group(1));
}
private void processBinaryDeleted(MatchResult match, String line) {
actualFile.setBinaryDeleted(match.group(1));
}
private void processBinaryEdited(MatchResult match, String line) {
actualFile.setBinaryEdited(match.group(1));
}
private String extractFileName(String _line) {
Matcher matcher = TIMESTAMP_REGEXP.matcher(_line);
String line = _line;
@@ -398,7 +347,6 @@ public final class UnifiedDiffReader {
return line.substring(4).replaceFirst("^(a|b|old|new)/", "")
.trim();
}
private String extractTimestamp(String line) {
Matcher matcher = TIMESTAMP_REGEXP.matcher(line);
if (matcher.find()) {
@@ -406,34 +354,27 @@ public final class UnifiedDiffReader {
}
return null;
}
final class UnifiedDiffLine {
private final Pattern pattern;
private final BiConsumer<MatchResult, String> command;
private final boolean stopsHeaderParsing;
public UnifiedDiffLine(String pattern, BiConsumer<MatchResult, String> command) {
this(false, pattern, command);
}
public UnifiedDiffLine(boolean stopsHeaderParsing, String pattern, BiConsumer<MatchResult, String> command) {
this.pattern = Pattern.compile(pattern);
this.command = command;
this.stopsHeaderParsing = stopsHeaderParsing;
}
public UnifiedDiffLine(boolean stopsHeaderParsing, Pattern pattern, BiConsumer<MatchResult, String> command) {
this.pattern = pattern;
this.command = command;
this.stopsHeaderParsing = stopsHeaderParsing;
}
public boolean validLine(String line) {
Matcher m = pattern.matcher(line);
return m.find();
}
public boolean processLine(String line) throws UnifiedDiffParserException {
Matcher m = pattern.matcher(line);
if (m.find()) {
@@ -443,32 +384,25 @@ public final class UnifiedDiffReader {
return false;
}
}
public boolean isStopsHeaderParsing() {
return stopsHeaderParsing;
}
@Override
public String toString() {
return "UnifiedDiffLine{" + "pattern=" + pattern + ", stopsHeaderParsing=" + stopsHeaderParsing + '}';
}
}
}
class InternalUnifiedDiffReader extends BufferedReader {
private String lastLine;
public InternalUnifiedDiffReader(Reader reader) {
super(reader);
}
@Override
public String readLine() throws IOException {
lastLine = super.readLine();
return lastLine();
}
String lastLine() {
return lastLine;
}

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
package com.github.difflib.unifieddiff;
import com.github.difflib.patch.AbstractDelta;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
@@ -25,15 +25,12 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @todo use an instance to store contextSize and originalLinesProvider.
* @author Tobias Warneke (t.warneke@gmx.net)
* @todo use an instance to store contextSize and originalLinesProvider.
*/
public class UnifiedDiffWriter {
private static final Logger LOG = Logger.getLogger(UnifiedDiffWriter.class.getName());
public static void write(UnifiedDiff diff, Function<String, List<String>> originalLinesProvider, Writer writer, int contextSize) throws IOException {
Objects.requireNonNull(originalLinesProvider, "original lines provider needs to be specified");
write(diff, originalLinesProvider, line -> {
@@ -44,12 +41,10 @@ public class UnifiedDiffWriter {
}
}, contextSize);
}
public static void write(UnifiedDiff diff, Function<String, List<String>> originalLinesProvider, Consumer<String> writer, int contextSize) throws IOException {
if (diff.getHeader() != null) {
writer.accept(diff.getHeader());
}
for (UnifiedDiffFile file : diff.getFiles()) {
List<AbstractDelta<String>> patchDeltas = new ArrayList<>(
file.getPatch().getDeltas());
@@ -58,24 +53,18 @@ public class UnifiedDiffWriter {
if (file.getIndex() != null) {
writer.accept("index " + file.getIndex());
}
writer.accept("--- " + (file.getFromFile() == null ? "/dev/null" : file.getFromFile()));
if (file.getToFile() != null) {
writer.accept("+++ " + file.getToFile());
}
List<String> originalLines = originalLinesProvider.apply(file.getFromFile());
List<AbstractDelta<String>> deltas = new ArrayList<>();
AbstractDelta<String> 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.getSource().getPosition();
// Check if the next Delta is too close to the current
// position.
// And if it is, add it to the current set
@@ -93,30 +82,25 @@ public class UnifiedDiffWriter {
}
delta = nextDelta;
}
}
// don't forget to process the last set of Deltas
processDeltas(writer, originalLines, deltas, contextSize,
patchDeltas.size() == 1 && file.getFromFile() == null);
}
}
if (diff.getTail() != null) {
writer.accept("--");
writer.accept(diff.getTail());
}
}
private static void processDeltas(Consumer<String> writer,
List<String> origLines, List<AbstractDelta<String>> deltas,
int contextSize, boolean newFile) {
List<String> origLines, List<AbstractDelta<String>> deltas,
int contextSize, boolean newFile) {
List<String> 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;
AbstractDelta<String> curDelta = deltas.get(0);
int origStart;
if (newFile) {
origStart = 0;
@@ -127,18 +111,15 @@ public class UnifiedDiffWriter {
origStart = 1;
}
}
int revStart = curDelta.getTarget().getPosition() + 1 - contextSize;
if (revStart < 1) {
revStart = 1;
}
// find the start of the wrapper context code
int contextStart = curDelta.getSource().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.getSource().getPosition()
&& line < origLines.size(); line++) { //
@@ -150,7 +131,6 @@ public class UnifiedDiffWriter {
getDeltaText(txt -> buffer.add(txt), curDelta);
origTotal += curDelta.getSource().getLines().size();
revTotal += curDelta.getTarget().getLines().size();
int deltaIndex = 1;
while (deltaIndex < deltas.size()) { // for each of the other Deltas
AbstractDelta<String> nextDelta = deltas.get(deltaIndex);
@@ -169,7 +149,6 @@ public class UnifiedDiffWriter {
curDelta = nextDelta;
deltaIndex++;
}
// Now output the post-Delta context code, clamping the end of the file
contextStart = curDelta.getSource().getPosition()
+ curDelta.getSource().getLines().size();
@@ -179,7 +158,6 @@ public class UnifiedDiffWriter {
origTotal++;
revTotal++;
}
// Create and insert the block header, conforming to the Unified Diff
// standard
writer.accept("@@ -" + origStart + "," + origTotal + " +" + revStart + "," + revTotal + " @@");
@@ -187,12 +165,11 @@ public class UnifiedDiffWriter {
writer.accept(txt);
});
}
/**
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter.
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter.
*
* @param writer consumer for the list of String lines of code
* @param delta the Delta to output
* @param delta the Delta to output
*/
private static void getDeltaText(Consumer<String> writer, AbstractDelta<String> delta) {
for (String line : delta.getSource().getLines()) {
@@ -202,7 +179,6 @@ public class UnifiedDiffWriter {
writer.accept("+" + line);
}
}
private static void writeOrNothing(Consumer<String> writer, String str) throws IOException {
if (str != null) {
writer.accept(str);