DiffService.java
/*
* Copyright (C) 2003-2010 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.wiki.service.diff;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.ecs.Filter;
import org.apache.ecs.filter.CharacterFilter;
import org.suigeneris.jrcs.diff.Diff;
import org.suigeneris.jrcs.diff.DifferentiationFailedException;
import org.suigeneris.jrcs.diff.Revision;
import org.suigeneris.jrcs.diff.delta.ChangeDelta;
import org.suigeneris.jrcs.diff.delta.Chunk;
import org.suigeneris.jrcs.diff.delta.Delta;
import org.suigeneris.jrcs.util.ToString;
/**
* Provides different methods which are used for comparing and making a list of differences between content.
*
* @LevelAPI Experimental
*/
public class DiffService {
/**
* Gets a list of Delta objects representing line differences between text1 and text2.
* @param text1 The original content.
* @param text2 The revised content.
* @return The list of Delta objects.
* @throws DifferentiationFailedException
*/
public List getDifferencesAsList(String text1, String text2) throws DifferentiationFailedException {
if (text1 == null)
text1 = "";
if (text2 == null)
text2 = "";
return getDeltas(Diff.diff(ToString.stringToArray(text1), ToString.stringToArray(text2)));
}
/**
* Gets a list of Delta objects representing word differences between text1 and text2.
* @param text1 The original content.
* @param text2 The revised content.
* @return The list of Delta objects.
* @throws DifferentiationFailedException
*/
public List getWordDifferencesAsList(String text1, String text2) throws DifferentiationFailedException {
text1 = text1.replaceAll(" ", "\n");
text2 = text2.replaceAll(" ", "\n");
return getDeltas(Diff.diff(ToString.stringToArray(text1), ToString.stringToArray(text2)));
}
/**
* Gets a Delta object containing word differences between text1 and text2.
* @param text1 The original content.
* @param text2 The revised content.
* @return The Delta object.
* @throws DifferentiationFailedException
*/
public DiffResult getWordDifferencesAsHTML(String text1, String text2) throws DifferentiationFailedException {
int changes = 0;
text1 = "~~PLACEHOLDER~~" + text1 + "~~PLACEHOLDER~~";
text2 = "~~PLACEHOLDER~~" + text2 + "~~PLACEHOLDER~~";
StringBuffer html = new StringBuffer("<div class=\"diffmodifiedline\">");
List list = getWordDifferencesAsList(text1, text2);
String[] words = StringUtils.splitPreserveAllTokens(text1, ' ');
int cursor = 0;
boolean addSpace = false;
for (int i = 0; i < list.size(); i++) {
if (addSpace) {
html.append(" ");
addSpace = false;
}
Delta delta = (Delta) list.get(i);
boolean isChangeDelta = (delta instanceof ChangeDelta);
int position = delta.getOriginal().anchor();
// First we fill in all text that has not been changed
while (cursor < position) {
html.append(escape(words[cursor]));
html.append(" ");
cursor++;
}
// Then we fill in what has been removed
Chunk orig = delta.getOriginal();
if (orig.size() > 0) {
html.append("<span class=\"diffremoveword\">");
List chunks = orig.chunk();
for (int j = 0; j < chunks.size(); j++) {
if (j > 0)
html.append(" ");
html.append(escape((String) chunks.get(j)));
cursor++;
}
changes++;
html.append("</span>");
addSpace = true;
}
// Then we fill in what has been added
Chunk rev = delta.getRevised();
if (rev.size() > 0) {
html.append("<span class=\"diffaddword\">");
List chunks = rev.chunk();
for (int j = 0; j < chunks.size(); j++) {
if (j > 0)
html.append(" ");
html.append(escape((String) chunks.get(j)));
}
// If is changeDelta, only add change 1 times
if (!isChangeDelta) changes++;
html.append("</span>");
addSpace = true;
}
}
// First we fill in all text that has not been changed
while (cursor < words.length) {
if (addSpace)
html.append(" ");
html.append(escape(words[cursor]));
addSpace = true;
cursor++;
}
html.append("</div>");
return new DiffResult(html.toString().replaceAll("~~PLACEHOLDER~~", ""), changes);
}
/**
* Gets a Delta object containing line differences in the HTML format between text1 and text2.
* @param text1 The original content.
* @param text2 The revised content.
* @param allDoc Shows the whole content.
* @return The Delta object.
* @throws DifferentiationFailedException
*/
public DiffResult getDifferencesAsHTML(String text1, String text2, boolean allDoc) throws DifferentiationFailedException {
StringBuffer html = new StringBuffer("<div class=\"diff\">");
int changes = 0;
if (text1 == null)
text1 = "";
if (text2 == null)
text2 = "";
List list = getDifferencesAsList(text1, text2);
String[] lines = ToString.stringToArray(text1);
int cursor = 0;
boolean addBR = false;
for (int i = 0; i < list.size(); i++) {
if (addBR) {
addBR = false;
}
Delta delta = (Delta) list.get(i);
int position = delta.getOriginal().anchor();
// First we fill in all text that has not been changed
while (cursor < position) {
if (allDoc) {
html.append("<div class=\"diffunmodifiedline\">");
String text = escape(lines[cursor]);
if (text.equals(""))
text = " ";
html.append(text);
html.append("</div>");
}
cursor++;
}
// Then we fill in what has been removed
Chunk orig = delta.getOriginal();
Chunk rev = delta.getRevised();
if (orig.size() > 0) {
List chunks = orig.chunk();
int j2 = 0;
for (int j = 0; j < chunks.size(); j++) {
String origline = (String) chunks.get(j);
// if (j>0)
// html.append("<br/>");
List revchunks = rev.chunk();
String revline = "";
while ("".equals(revline)) {
revline = (j2 >= revchunks.size()) ? null : (String) revchunks.get(j2);
j2++;
}
if (revline != null) {
DiffResult diffLine = getWordDifferencesAsHTML(origline, revline);
html.append(diffLine.getDiffHTML());
rev.chunk().remove(revline);
changes += diffLine.getChanges();
} else {
html.append("<div class=\"diffmodifiedline\">");
if (origline.equals("")) {
html.append("<pre class=\"diffremoveword\">");
html.append(" ");
html.append("</pre>");
} else {
html.append("<span class=\"diffremoveword\">");
html.append(escape(origline));
html.append("</span>");
}
html.append("</div>");
changes++;
}
addBR = true;
cursor++;
}
}
// Then we fill in what has been added
if (rev.size() > 0 ) {
List chunks = rev.chunk();
for (int j = 0; j < chunks.size(); j++) {
String revline = (String) chunks.get(j);
html.append("<div class=\"diffmodifiedline\">");
if (revline.equals("")) {
html.append("<pre class=\"diffaddword\">");
html.append("</pre>");
} else {
html.append("<span class=\"diffaddword\">");
html.append(escape(revline));
html.append("</span>");
}
html.append("</div>");
changes++;
}
addBR = true;
}
}
// First we fill in all text that has not been changed
if (allDoc) {
while (cursor < lines.length) {
html.append("<div class=\"diffunmodifiedline\">");
String text = escape(lines[cursor]);
if (text.equals(""))
text = " ";
html.append(text);
html.append("</div>");
cursor++;
}
}
html.append("</div>");
return new DiffResult(html.toString(), changes);
}
/**
* Gets a list of Delta objects by a wiki page revision.
* @param rev The wiki page revision.
* @return The list of Delta objects.
*/
protected List getDeltas(Revision rev) {
ArrayList list = new ArrayList();
for (int i = 0; i < rev.size(); i++) {
list.add(rev.getDelta(i));
}
return list;
}
/**
* Escapes a string by a common filter.
* @param text The text to escape.
* @return The string.
*/
protected String escape(String text) {
Filter filter = new CharacterFilter();
String scontent = filter.process(text);
return scontent;
}
}