Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
framework.Diff Maven / Gradle / Ivy
package framework;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import difflib.Chunk;
import difflib.Delta;
import difflib.myers.Equalizer;
import difflib.myers.MyersDiff;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Diff
*
* @param Diff element type
*/
public class Diff {
@SuppressWarnings("javadoc")
public enum Type {
EQUAL,
INSERT,
DELETE,
CHANGE,
SKIP;
@Override
public String toString() {
return name().toLowerCase(Locale.ENGLISH);
}
}
/**
* Diff type
*/
public Type type;
/**
* before index
*/
public Optional beforeIndex;
/**
* before
*/
public Optional before;
/**
* after index
*/
public Optional afterIndex;
/**
* after
*/
public Optional after;
/**
* @return before index as text
*/
public String getBeforeIndexText() {
return type == Type.SKIP ? getBeforeText() : beforeIndex.map(String::valueOf).orElse("");
}
/**
* @return before as text
*/
public String getBeforeText() {
return before.map(String::valueOf).orElse("");
}
/**
* @return after index as text
*/
public String getAfterIndexText() {
return type == Type.SKIP ? getAfterText() : afterIndex.map(String::valueOf).orElse("");
}
/**
* @return after as text
*/
public String getAfterText() {
return after.map(String::valueOf).orElse("");
}
/**
* default equalizer
*
* @param element type
* @return equalizer
*/
public static Equalizer DEFAULT() {
return Object::equals;
}
/**
* ignore space equalizer
*/
public static final Equalizer IGNORE_SPACE = (i, j) -> i.replaceAll("\\s{2,}", " ").equals(j.replaceAll("\\s{2,}", " "));
/**
* HTML escape editor
*/
public static final Consumer> ESCAPE = d -> {
d.before = d.before.map(Tool::htmlEscape);
d.after = d.after.map(Tool::htmlEscape);
};
/**
* @param start start index
* @param end end index
* @param tag tag name
* @return insert tag function
*/
static Function insertTag(int start, int end, String tag) {
return s -> s.substring(0, start) + "<" + tag + ">" + s.substring(start, end) + "" + tag + ">" + s.substring(end);
}
/**
* @param tag letter decoration tag(ex: b)
* @param limit mark limit
* @return editor
*/
public static Consumer> INLINE(String tag, int limit) {
return d -> {
d.before = d.before.map(Tool::htmlEscape);
d.after = d.after.map(Tool::htmlEscape);
if (d.type != Type.CHANGE) {
return;
}
List> deltas = new MyersDiff(DEFAULT())
.diff(d.before.map(Tool::toCharacterArray).orElse(new Character[] {}), d.after.map(Tool::toCharacterArray).orElse(new Character[] {}))
.getDeltas();
if (deltas.isEmpty()) {
return;
}
Function, Integer> getEnd = i -> i.getPosition() + i.getLines().size();
if (deltas.size() > limit) {
Delta top = deltas.get(0);
Delta last = deltas.get(deltas.size() - 1);
{
int start = top.getOriginal().getPosition();
int end = Tool.val(last.getOriginal(), getEnd);
if (start < end) {
d.before = d.before.map(insertTag(start, end, tag));
}
}
{
int start = top.getRevised().getPosition();
int end = Tool.val(last.getRevised(), getEnd);
if (start < end) {
d.after = d.after.map(insertTag(start, end, tag));
}
}
} else {
Collections.reverse(deltas);
for (Delta delta : deltas) {
{
int start = delta.getOriginal().getPosition();
int end = Tool.val(delta.getOriginal(), getEnd);
if (start < end) {
d.before = d.before.map(insertTag(start, end, tag));
}
}
{
int start = delta.getRevised().getPosition();
int end = Tool.val(delta.getRevised(), getEnd);
if (start < end) {
d.after = d.after.map(insertTag(start, end, tag));
}
}
}
}
};
}
/**
* @param size tab size
* @return editor
*/
public static Consumer> TAB(int size) {
return d -> {
String tab = Collections.nCopies(size, " ").stream().collect(Collectors.joining());
Function f = s -> s.replace("\t", tab).replaceAll("[ ]{2}", " ");
d.before = d.before.map(f);
d.after = d.after.map(f);
};
}
/**
* @param type Diff element type
* @param beforeIndex before line number
* @param before before line text
* @param afterIndex after line number
* @param after after line text
*/
@SuppressFBWarnings({ "URF_UNREAD_FIELD", "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" })
public Diff(Type type, Optional beforeIndex, Optional before, Optional afterIndex, Optional after) {
this.type = type;
this.beforeIndex = beforeIndex;
this.before = before;
this.afterIndex = afterIndex;
this.after = after;
}
/**
* @param Diff type
* @param before before
* @param after after
* @param equalizer equalizer(default Diff.DEFAULT())
* @param editor editor(default null)
* @return diff
*/
public static List> diff(T[] before, T[] after, Equalizer equalizer, Consumer> editor) {
List> list = new ArrayList<>();
int currentB = 0, currentA = 0;
for (Delta delta : new MyersDiff(equalizer).diff(before, after).getDeltas()) {
Chunk b = delta.getOriginal();
Chunk a = delta.getRevised();
List bLines = b.getLines();
List aLines = a.getLines();
for (int i = 0, startB = b.getPosition(), startA = a.getPosition(), maxB = b.size(), maxA = a.size(), endB = startB + maxB, endA = startA
+ maxA; startB < endB || startA < endA; i++, startB++, startA++) {
for (; currentB < before.length && currentA < after.length && currentB < startB && currentA < startA; currentB++, currentA++) {
list.add(Tool.peek(new Diff<>(Type.EQUAL, Optional.of(currentB + 1), Optional.of(before[currentB]), Optional.of(currentA + 1),
Optional.of(after[currentA])), editor));
}
Type type = Type.valueOf(delta.getType().name());
if (type == Type.CHANGE) {
if (i >= maxB) {
type = Type.INSERT;
} else if (i >= maxA) {
type = Type.DELETE;
}
}
boolean hasB = i < maxB;
boolean hasA = i < maxA;
list.add(Tool.peek(new Diff<>(type, Optional.of(currentB + 1).filter(n -> hasB), Tool.of(hasB ? bLines.get(i) : null),
Optional.of(currentA + 1).filter(n -> hasA), Tool.of(hasA ? aLines.get(i) : null)), editor));
if (currentB < endB) {
currentB++;
}
if (currentA < endA) {
currentA++;
}
}
}
for (int endB = before.length, endA = after.length, end = Math.max(endB, endA); currentB < end; currentB++, currentA++) {
boolean hasB = currentB < endB;
boolean hasA = currentA < endA;
Type type = hasB && hasA ? Type.EQUAL : hasB ? Type.DELETE : Type.INSERT;
list.add(Tool.peek(new Diff<>(type, Optional.of(currentB + 1).filter(n -> hasB), Tool.of(hasB ? before[currentB] : null),
Optional.of(currentA + 1).filter(n -> hasA), Tool.of(hasA ? after[currentA] : null)), editor));
}
return list;
}
/**
* @param args not use
*/
public static void main(String[] args) {
System.out.print(
Tool.json(Diff.diff(Tool.htmlEscape(" ").chars().mapToObj(i -> (char) i)
.toArray(Character[]::new), Tool.htmlEscape(" -->").chars().mapToObj(i -> (char) i).toArray(Character[]::new), DEFAULT(), null)));
}
/**
* @param element type
* @param diffs target
* @param lines rest lines(no compact if less then 0)
* @param skipContent skip line content
* @return compacted diffs
*/
public static List> compact(List> diffs, int lines, T skipContent) {
if (lines <= 0) {
return diffs;
}
SortedSet picks = new TreeSet<>();
int max = diffs.size();
int i = 0;
for (Diff diff : diffs) {
if (diff.type != Type.EQUAL) {
for (int j = Math.max(0, i - lines), j2 = Math.min(i + lines + 1, max); j < j2; j++) {
picks.add(j);
}
}
i++;
}
List> result = new ArrayList<>();
int pick0 = -1;
for (int pick : picks) {
if (pick0 + 1 != pick) {
result.add(new Diff<>(Type.SKIP, Optional.empty(), Tool.of(skipContent), Optional.empty(), Tool.of(skipContent)));
}
pick0 = pick;
result.add(diffs.get(pick));
}
return result;
}
}