/** * Writes a line spanning the full width of the code view, including the gutter. * * @param text to put on that line; will be HTML-escaped. */ private void writeFullWidthLine(String text) { try { os.write("<tr><td class='diff-cell' colspan='4'>".getBytes()); os.write(StringUtils.escapeForHtml(text, false).getBytes()); os.write("</td></tr>\n".getBytes()); } catch (IOException ex) { // Cannot happen with a ByteArrayOutputStream } }
protected void writeRange(final char prefix, final int begin, final int cnt) throws IOException { os.write(' '); os.write(prefix); switch (cnt) { case 0: // If the range is empty, its beginning number must be the // line just before the range, or 0 if the range is at the // start of the file stream. Here, begin is always 1 based, // so an empty file would produce "0,0". // os.write(encodeASCII(begin - 1)); os.write(','); os.write('0'); break; case 1: // If the range is exactly one line, produce only the number. // os.write(encodeASCII(begin)); break; default: os.write(encodeASCII(begin)); os.write(','); os.write(encodeASCII(cnt)); break; } }
@Override public void flush() throws IOException { if (truncated) { os.resetTo(truncateTo); } super.flush(); }
/** Rewind and issue a message that the diff is too large. */ private void reset() { if (!isOff) { os.resetTo(startCurrent); writeFullWidthLine(getMsg("gb.diffFileDiffTooLarge", "Diff too large")); totalNofLinesCurrent = totalNofLinesPrevious; isOff = true; } }
@Override public void format(DiffEntry ent) throws IOException { currentPath = diffStat.addPath(ent); nofLinesCurrent = 0; isOff = false; entry = ent; if (!truncated) { totalNofLinesPrevious = totalNofLinesCurrent; if (globalDiffLimit > 0 && totalNofLinesPrevious > globalDiffLimit) { truncated = true; isOff = true; } truncateTo = os.size(); } else { isOff = true; } if (truncated) { skipped.add(ent); } else { // Produce a header here and now String path; String id; if (ChangeType.DELETE.equals(ent.getChangeType())) { path = ent.getOldPath(); id = ent.getOldId().name(); } else { path = ent.getNewPath(); id = ent.getNewId().name(); } StringBuilder sb = new StringBuilder( MessageFormat.format( "<div class='header'><div class=\"diffHeader\" id=\"n{0}\"><i class=\"icon-file\"></i> ", id)); sb.append(StringUtils.escapeForHtml(path, false)).append("</div></div>"); sb.append("<div class=\"diff\"><table cellpadding='0'><tbody>\n"); os.write(sb.toString().getBytes()); } // Keep formatting, but if off, don't produce anything anymore. We just keep on counting. super.format(ent); if (!truncated) { // Close the table os.write("</tbody></table></div>\n".getBytes()); } }
/** * Output a hunk header * * @param aStartLine within first source * @param aEndLine within first source * @param bStartLine within second source * @param bEndLine within second source * @throws IOException */ @Override protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine) throws IOException { if (nofLinesCurrent++ == 0) { handleChange(); startCurrent = os.size(); } if (!isOff) { totalNofLinesCurrent++; if (nofLinesCurrent > maxDiffLinesPerFile && maxDiffLinesPerFile > 0) { reset(); } else { os.write( "<tr><th class='diff-line' data-lineno='..'></th><th class='diff-line' data-lineno='..'></th><th class='diff-state'></th><td class='hunk_header'>" .getBytes()); os.write('@'); os.write('@'); writeRange('-', aStartLine + 1, aEndLine - aStartLine); writeRange('+', bStartLine + 1, bEndLine - bStartLine); os.write(' '); os.write('@'); os.write('@'); os.write("</td></tr>\n".getBytes()); } } left = aStartLine + 1; right = bStartLine + 1; }
/** * Workaround function for complex private methods in DiffFormatter. This sets the html for the * diff headers. * * @return */ public String getHtml() { String html = RawParseUtils.decode(os.toByteArray()); String[] lines = html.split("\n"); StringBuilder sb = new StringBuilder(); for (String line : lines) { if (line.startsWith("index")) { // skip index lines } else if (line.startsWith("new file") || line.startsWith("deleted file")) { // skip new file lines } else if (line.startsWith("\\ No newline")) { // skip no new line } else if (line.startsWith("---") || line.startsWith("+++")) { // skip --- +++ lines } else if (line.startsWith("diff")) { // skip diff lines } else { boolean gitLinkDiff = line.length() > 0 && line.substring(1).startsWith("Subproject commit"); if (gitLinkDiff) { sb.append("<tr><th class='diff-line'></th><th class='diff-line'></th>"); if (line.charAt(0) == '+') { sb.append("<th class='diff-state diff-state-add'></th><td class=\"diff-cell add2\">"); } else { sb.append( "<th class='diff-state diff-state-sub'></th><td class=\"diff-cell remove2\">"); } line = StringUtils.escapeForHtml(line.substring(1), false); } sb.append(line); if (gitLinkDiff) { sb.append("</td></tr>"); } sb.append('\n'); } } if (truncated) { sb.append( MessageFormat.format( "<div class='header'><div class='diffHeader'>{0}</div></div>", StringUtils.escapeForHtml( getMsg("gb.diffTruncated", "Diff truncated after the above file"), false))); // List all files not shown. We can be sure we do have at least one path in skipped. sb.append( "<div class='diff'><table cellpadding='0'><tbody><tr><td class='diff-cell' colspan='4'>"); String deletedSuffix = StringUtils.escapeForHtml(getMsg("gb.diffDeletedFileSkipped", "(deleted)"), false); boolean first = true; for (DiffEntry entry : skipped) { if (!first) { sb.append('\n'); } if (ChangeType.DELETE.equals(entry.getChangeType())) { sb.append( "<span id=\"n" + entry.getOldId().name() + "\">" + StringUtils.escapeForHtml(entry.getOldPath(), false) + ' ' + deletedSuffix + "</span>"); } else { sb.append( "<span id=\"n" + entry.getNewId().name() + "\">" + StringUtils.escapeForHtml(entry.getNewPath(), false) + "</span>"); } first = false; } skipped.clear(); sb.append("</td></tr></tbody></table></div>"); } return sb.toString(); }
@Override protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException { if (nofLinesCurrent++ == 0) { handleChange(); startCurrent = os.size(); } // update entry diffstat currentPath.update(prefix); if (isOff) { return; } totalNofLinesCurrent++; if (nofLinesCurrent > maxDiffLinesPerFile && maxDiffLinesPerFile > 0) { reset(); } else { // output diff os.write("<tr>".getBytes()); switch (prefix) { case '+': os.write( ("<th class='diff-line'></th><th class='diff-line' data-lineno='" + (right++) + "'></th>") .getBytes()); os.write("<th class='diff-state diff-state-add'></th>".getBytes()); os.write("<td class='diff-cell add2'>".getBytes()); break; case '-': os.write( ("<th class='diff-line' data-lineno='" + (left++) + "'></th><th class='diff-line'></th>") .getBytes()); os.write("<th class='diff-state diff-state-sub'></th>".getBytes()); os.write("<td class='diff-cell remove2'>".getBytes()); break; default: os.write( ("<th class='diff-line' data-lineno='" + (left++) + "'></th><th class='diff-line' data-lineno='" + (right++) + "'></th>") .getBytes()); os.write("<th class='diff-state'></th>".getBytes()); os.write("<td class='diff-cell context2'>".getBytes()); break; } os.write(encode(codeLineToHtml(prefix, text.getString(cur)))); os.write("</td></tr>\n".getBytes()); } }