/** * Copyright (c) 2009--2014 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.common.filediff; import org.apache.commons.lang.StringEscapeUtils; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Iterator; /** * Converts a list of hunks from a file diff into an html string that represents * the view of a diff. * @version $Rev$ */ public class RhnHtmlDiffWriter implements DiffWriter, DiffVisitor { private static final int CHARS_PER_LINE = 40; private final StringBuffer oldfile; private final StringBuffer newfile; private final NumberFormat formatter; private boolean onlyChanged; /** * @param lines The number of lines in the longest file. * Used to find out how many digits a line number should be. * Ex: if lines is 12, line one should be shown as 01, but * if lines is 100, line one should be shown as 001. */ public RhnHtmlDiffWriter(int lines) { onlyChanged = false; oldfile = new StringBuffer(); newfile = new StringBuffer(); formatter = new DecimalFormat(); formatter.setMaximumFractionDigits(0); formatter.setMinimumIntegerDigits(Integer.toString(lines).length()); } /** * {@inheritDoc} */ public void writeHunk(Hunk hunk) { hunk.visit(this); } /** * {@inheritDoc} */ public void accept(ChangeHunk hunk) { printStartDiv("changed"); int numOld = printLines(oldfile, hunk.getOldLines()); int numNew = printLines(newfile, hunk.getNewLines()); //Line up the changes. if (numOld > numNew) { printBlankLines(newfile, numOld - numNew); } else { printBlankLines(oldfile, numNew - numOld); } printEndDiv(); } /** * {@inheritDoc} */ public void accept(DeleteHunk hunk) { printStartDiv("deleted"); int numlines = printLines(oldfile, hunk.getOldLines()); //to line up the two files in the html, print blank lines for //each deleted line. printBlankLines(newfile, numlines); printEndDiv(); } /** * {@inheritDoc} */ public void accept(MatchHunk hunk) { if (!onlyChanged) { printLines(oldfile, hunk.getOldLines()); printLines(newfile, hunk.getNewLines()); } else { //So the changes don't butt up against eachother. oldfile.append("<br />"); newfile.append("<br />"); } } /** * {@inheritDoc} */ public void accept(InsertHunk hunk) { printStartDiv("inserted"); int numlines = printLines(newfile, hunk.getNewLines()); //to line up the two files in the html, print blank lines for //each inserted line. printBlankLines(oldfile, numlines); printEndDiv(); } private int printLines(StringBuffer buffy, FileLines block) { Iterator<String> i = block.getLines().iterator(); int numWritten = 0; int linenum = block.getFromLine(); while (i.hasNext()) { String line = i.next(); buffy.append(formatter.format(linenum)); buffy.append(" "); while (line.length() > CHARS_PER_LINE) { //We want to escape all of the html inside the file //This utility function doesn't escape spaces, so I'll do that //myself, ... the easy way. buffy.append(StringEscapeUtils .escapeHtml(line.substring(0, CHARS_PER_LINE)) .replaceAll(" ", " ")); buffy.append("<br />"); for (int p = 0; p < formatter.getMinimumIntegerDigits() + 1; p++) { buffy.append(" "); } line = line.substring(CHARS_PER_LINE); numWritten++; } buffy.append(StringEscapeUtils.escapeHtml(line) .replaceAll(" ", " ")); buffy.append("<br />"); numWritten++; linenum++; } return numWritten; } private void printBlankLines(StringBuffer buffy, int number) { for (int i = 0; i < number; i++) { buffy.append(" <br />"); } } private void printStartDiv(String cssClass) { oldfile.append("<div class=\"" + cssClass + "\">"); newfile.append("<div class=\"" + cssClass + "\">"); } private void printEndDiv() { oldfile.append("</div>"); newfile.append("</div>"); } /** * @return The resulting html String. Valid only after running report. */ public String getResult() { StringBuilder result = new StringBuilder(); result.append("<div class=\"oldfile\">"); result.append(oldfile); result.append("</div>"); result.append("<div class=\"newfile\">"); result.append(newfile); result.append("</div>"); return result.toString(); } /** * @param onlyChangedIn The onlyChanged to set. */ public void setOnlyChanged(boolean onlyChangedIn) { onlyChanged = onlyChangedIn; } }