/* * @(#)RevisionRenderer.java * * Copyright 2009 Instituto Superior Tecnico * Founding Authors: Paulo Abrantes * * https://fenix-ashes.ist.utl.pt/ * * This file is part of the Meta-Workflow Module. * * The Meta-Workflow Module is free software: you can * redistribute it and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation, either version * 3 of the License, or (at your option) any later version. * * The Meta-Workflow Module 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Meta-Workflow Module. If not, see . * */ package module.metaWorkflow.presentationTier.renderers; import java.util.ArrayList; import java.util.List; import module.metaWorkflow.util.versioning.DiffUtil.Revision; import org.apache.commons.jrcs.diff.Chunk; import org.apache.commons.jrcs.diff.Delta; import org.apache.commons.jrcs.diff.Diff; import org.apache.commons.jrcs.util.ToString; import org.apache.commons.lang.StringUtils; import pt.ist.fenixWebFramework.renderers.OutputRenderer; import pt.ist.fenixWebFramework.renderers.components.HtmlBlockContainer; import pt.ist.fenixWebFramework.renderers.components.HtmlComponent; import pt.ist.fenixWebFramework.renderers.components.HtmlContainer; import pt.ist.fenixWebFramework.renderers.components.HtmlInlineContainer; import pt.ist.fenixWebFramework.renderers.components.HtmlText; import pt.ist.fenixWebFramework.renderers.layouts.Layout; /** * * The code of this Rendered is basically the adaption of * com.xpn.xwiki.plugin.diff.DiffPlugin from XWiki which has a kick ass diff * HTML display algorithm! * * Base code can be found in: https://svn.xwiki.org/svnroot/xwiki/platform * /core/trunk/xwiki-core/src * /main/java/com/xpn/xwiki/plugin/diff/DiffPlugin.java * * @author Paulo Abrantes * */ public class RevisionRenderer extends OutputRenderer { private String modifiedLineClass; private String unmodifiedline; private String removeWord; private String addWord; private boolean allDoc; public boolean isAllDoc() { return allDoc; } public void setAllDoc(boolean allDoc) { this.allDoc = allDoc; } public String getModifiedLineClass() { return modifiedLineClass; } public void setModifiedLineClass(String modifiedLineClass) { this.modifiedLineClass = modifiedLineClass; } public String getUnmodifiedline() { return unmodifiedline; } public void setUnmodifiedline(String unmodifiedline) { this.unmodifiedline = unmodifiedline; } public String getRemoveWord() { return removeWord; } public void setRemoveWord(String removeword) { this.removeWord = removeword; } public String getAddWord() { return addWord; } public void setAddWord(String addWord) { this.addWord = addWord; } @Override protected Layout getLayout(Object arg0, Class arg1) { return new Layout() { @Override public HtmlComponent createComponent(Object arg0, Class arg1) { Revision revision = (Revision) arg0; HtmlBlockContainer container = new HtmlBlockContainer(); List deltaList = getDeltas(revision.getRevision()); String[] lines = ToString.stringToArray(revision.getText1()); int cursor = 0; boolean addBR = false; for (int i = 0; i < deltaList.size(); i++) { if (addBR) { addBR = false; } Delta delta = deltaList.get(i); int position = delta.getOriginal().anchor(); // First we fill in all text that has not been changed while (cursor < position) { if (isAllDoc()) { HtmlBlockContainer unmodifiedContainer = new HtmlBlockContainer(); container.addChild(unmodifiedContainer); unmodifiedContainer.setClasses(getUnmodifiedline()); String text = escape(lines[cursor]); if (text.equals("")) { text = " "; } unmodifiedContainer.addChild(new HtmlText(text)); } cursor++; } // Then we fill in what has been removed Chunk orig = delta.getOriginal(); Chunk rev = delta.getRevised(); int j1 = 0; if (orig.size() > 0) { List chunks = orig.chunk(); int j2 = 0; for (int j = 0; j < chunks.size(); j++) { String origline = chunks.get(j); if (origline.equals("")) { cursor++; continue; } // if (j>0) // html.append("
"); List revchunks = rev.chunk(); String revline = ""; while ("".equals(revline)) { revline = (j2 >= revchunks.size()) ? null : revchunks.get(j2); j2++; j1++; } if (revline != null) { container.addChild(getWordDifference(origline, revline)); } else { HtmlBlockContainer modifiedLine = new HtmlBlockContainer(); container.addChild(modifiedLine); modifiedLine.setClasses(getModifiedLineClass()); HtmlInlineContainer modifiedWord = new HtmlInlineContainer(); modifiedLine.addChild(modifiedWord); modifiedLine.setClasses(getRemoveWord()); modifiedLine.addChild(new HtmlText(origline)); } addBR = true; cursor++; } } // Then we fill in what has been added if (rev.size() > 0) { List chunks = rev.chunk(); for (int j = j1; j < chunks.size(); j++) { HtmlBlockContainer modifiedLine = new HtmlBlockContainer(); container.addChild(modifiedLine); modifiedLine.setClasses(getModifiedLineClass()); HtmlInlineContainer modifiedWord = new HtmlInlineContainer(); modifiedLine.addChild(modifiedWord); modifiedLine.setClasses(getAddWord()); modifiedLine.addChild(new HtmlText(chunks.get(j))); } addBR = true; } } // First we fill in all text that has not been changed if (isAllDoc()) { while (cursor < lines.length) { HtmlBlockContainer unmodifiedLine = new HtmlBlockContainer(); container.addChild(unmodifiedLine); unmodifiedLine.setClasses(getUnmodifiedline()); String text = escape(lines[cursor]); if (text.equals("")) { text = " "; } unmodifiedLine.addChild(new HtmlText(text)); cursor++; } } return container; } private HtmlContainer getWordDifference(String text1, String text2) { text1 = "~~PLACEHOLDER~~" + text1 + "~~PLACEHOLDER~~"; text2 = "~~PLACEHOLDER~~" + text2 + "~~PLACEHOLDER~~"; HtmlBlockContainer container = new HtmlBlockContainer(); container.setClasses(getModifiedLineClass()); 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) { container.addChild(new HtmlText(" ")); addSpace = false; } Delta delta = list.get(i); int position = delta.getOriginal().anchor(); // First we fill in all text that has not been changed while (cursor < position) { container.addChild(new HtmlText(escape(words[cursor]))); container.addChild(new HtmlText(" ")); cursor++; } // Then we fill in what has been removed Chunk orig = delta.getOriginal(); if (orig.size() > 0) { HtmlInlineContainer removeWord = new HtmlInlineContainer(); container.addChild(removeWord); removeWord.setClasses(getRemoveWord()); List chunks = orig.chunk(); for (int j = 0; j < chunks.size(); j++) { if (j > 0) { removeWord.addChild(new HtmlText(" ")); } removeWord.addChild(new HtmlText(escape(chunks.get(j)))); cursor++; } addSpace = true; } // Then we fill in what has been added Chunk rev = delta.getRevised(); if (rev.size() > 0) { HtmlInlineContainer addWord = new HtmlInlineContainer(); container.addChild(addWord); addWord.setClasses(getAddWord()); List chunks = rev.chunk(); for (int j = 0; j < chunks.size(); j++) { if (j > 0) { addWord.addChild(new HtmlText(" ")); } addWord.addChild(new HtmlText(escape(chunks.get(j)))); } addSpace = true; } } // First we fill in all text that has not been changed while (cursor < words.length) { if (addSpace) { container.addChild(new HtmlText(" ")); } container.addChild(new HtmlText(escape(words[cursor]))); addSpace = true; cursor++; } return container; // return html.toString().replaceAll("~~PLACEHOLDER~~", ""); } private List getWordDifferencesAsList(String text1, String text2) { try { text1 = text1.replaceAll(" ", "\n"); text2 = text2.replaceAll(" ", "\n"); return getDeltas(Diff.diff(ToString.stringToArray(text1), ToString.stringToArray(text2))); } catch (Exception e) { e.printStackTrace(); return null; } } private List getDeltas(org.apache.commons.jrcs.diff.Revision revision) { ArrayList deltas = new ArrayList(); for (int i = 0; i < revision.size(); i++) { deltas.add(revision.getDelta(i)); } return deltas; } private String escape(String string) { return string.replaceAll("~~PLACEHOLDER~~", ""); } }; } }