<%--
$Id$
CDDL HEADER START
The contents of this file are subject to the terms of the
Common Development and Distribution License (the "License").
You may not use this file except in compliance with the License.
See LICENSE.txt included in this distribution for the specific
language governing permissions and limitations under the License.
When distributing Covered Code, include this CDDL HEADER in each
file and include the License file at LICENSE.txt.
If applicable, add the following below this CDDL HEADER, with the
fields enclosed by brackets "[]" replaced with your own identifying
information: Portions Copyright [yyyy] [name of copyright owner]
CDDL HEADER END
Copyright 2009 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
Portions Copyright 2011 Jens Elkner.
--%><%@page import="
java.util.EnumSet,
org.apache.commons.jrcs.diff.Chunk,
org.apache.commons.jrcs.diff.Delta,
org.opensolaris.opengrok.Info,
org.opensolaris.opengrok.configuration.Project,
org.opensolaris.opengrok.analysis.FileAnalyzer.Genre,
org.opensolaris.opengrok.web.DiffData,
org.opensolaris.opengrok.web.DiffType,
org.opensolaris.opengrok.web.PageConfig,
org.opensolaris.opengrok.web.Prefix,
org.opensolaris.opengrok.web.Util,
org.opensolaris.opengrok.web.WebappListener"
%><%!
private static final String getAnnotateRevision(DiffData dd) {
StringBuilder sb = new StringBuilder("<script type=\"text/javascript\">/* <![CDATA[ */ ");
// let the client side know, which revision is shown if it is a single file
if (dd.type == DiffType.OLD || dd.type == DiffType.NEW) {
sb.append("O.rev='r=")
.append(dd.type == DiffType.NEW ? dd.rev2 : dd.rev1).append("';");
} else if (dd.type == DiffType.WDIFF) {
sb.append("O.domReady.push(O.domReadyDiff);");
} else {
return "";
}
sb.append(" /* ]]> */</script>");
return sb.toString();
}
/** Info container for hidden blocks */
private class Hidden {
/** line# of the first line of the leading block */
int start;
/** number of lines in the leading block */
int leading;
/** number of lines in the hidden block */
int hidden;
/** number of lines in the trailing block */
int trailing;
/** buffer which contains the formatted line numbers after hidden lines */
StringBuilder bn;
/** buffer which contains the formatted source lines after hidden lines */
StringBuilder bs;
/** if {@code true} dump all, i.e. no hidden lines */
boolean full;
/**
* Create a new instance with initialized StringBuilders
* @param full if {@code true} dump all, i.e. no hidden lines *
*/
public Hidden(boolean full) {
bn = new StringBuilder(32);
bs = new StringBuilder(256);
this.full = full;
}
/**
* Reset and prepare buffers to take new content.
* @param start line# of the first line of the leading block
* @param leading Number of leading lines
* @param hidden Number of lines in the hidden block
* @param trailing Number of trailing lines
*/
public void reset(int start, int leading, int hidden, int trailing) {
this.start = start;
this.leading = leading;
this.hidden = hidden;
this.trailing = trailing;
bn.setLength(0);
bs.setLength(0);
}
/** {@inheritDoc} */
@Override
public String toString() {
return "{ start=" + start + "; leading=" + leading + "; hidden=" + hidden
+ "; trailing=" + trailing + "; full=" + full
+ " }";
}
}
/**
* Dump unchanged lines of a file. lines get htmlized.
* @param line Current line# (start of dump)
* @param howMany Number of lines to dump.
* @param trailing Number of trailing lines to print after a "hidden" block
* (leading is always 8).
* @param file Source code lines of the file to partially dump.
* @param bn Where to append formatted line numbers
* @param bs Where to append dumped formatted source code lines
* @param h Where to store info about leading, hidden, trailing blocks
* @param a If {code true}, line numbers get an anchor named with the line#
* (can't use IDs instead of name, because IDs need to be unique)
* @return line# + 1 of the last line dumped
* @throws IndexOutOfBoundsException if <var>file</var> contains less than
* <var>line</var> + <var>howMany</var> entries.
*/
private static final int dumpFile(int line, int howMany, int trailing,
String[] file, StringBuilder bn, StringBuilder bs, Hidden h, boolean a)
{
int max = line + howMany;
// if we have fewer lines than leading+trailing+min_hidden lines, dump all
if (h.full || howMany < 20) {
h.reset(line, howMany, 0, 0);
for (;line < max; line++) {
if (a) {
bn.append("<a name=\"").append(line).append("\">")
.append(line).append("</a>\n");
} else {
bn.append(line).append('\n');
}
bs.append(Util.htmlize(file[line-1])).append('\n');
}
} else {
// leading block (row n)
for (int j = line; j < line + 8; j++) {
if (a) {
bn.append("<a name=\"").append(j).append("\">")
.append(j).append("</a>\n");
} else {
bn.append(j).append('\n');
}
bs.append(Util.htmlize(file[j-1])).append('\n');
}
h.reset(line, 8, howMany - 8 - trailing, trailing); // hidden block (row n+1)
bs = h.bs;
bn = h.bn;
// trailing block (row n+2)
line += howMany - trailing ;
for (; line < max; line++) {
if (a) {
bn.append("<a name=\"").append(line).append("\">")
.append(line).append("</a>\n");
} else {
bn.append(line).append('\n');
}
bs.append(Util.htmlize(file[line-1])).append('\n');
}
}
return line;
}
/**
* Dump a chunk of deleted or added source lines. lines get not htmlized.
* @param cn line# of the first line in the file to format (start of dump)
* @param cl line# of the last line in the file to format (end of dump)
* @param line line# of the new file, which corresponds to <var>cn</var>
* @param file file from which the chunk should be dumped
* @param bn where to store formatted line numbers
* @param bs where to store formatted source code
* @param a if {code true}, line numbers get an anchor named with the line#
* (can't use IDs instead of name, because IDs need to be unique)
* @return line# + 1 of the last line dumped
*/
private static final int dumpChunk(int cn, int cl, int line, String[] file,
StringBuilder bn, StringBuilder bs, boolean a)
{
for (int j = cn - 1; j < cl ; j++, line++) {
if (a) {
bn.append("<a name=\"").append(line).append("\">")
.append(line).append("</a>\n");
} else {
bn.append(line).append('\n');
}
bs.append(file[j]).append('\n');
}
return line;
}
private static final String getHiddenBlockInfo(Hidden h, DiffData dd) {
return "<b>" + h.hidden + " unchanged lines hidden</b>"
+ (dd.type == DiffType.WDIFF ? "" : " (<a href=\""
+ dd.reqURI + "?r1=" + dd.rp1 + "&amp;r2=" + dd.rp2 + "&amp;format="
+ dd.type.getAbbrev() + "&amp;full=1#" + (h.start + h.leading)
+ "\">view full</a>) ");
}
/**
* Dump a sequence of numbers.
* @param n first number to dump
* @param count # of numbers to dump
* @param bn buffer to use. Gets reset before appending stuff.
* @return last number dumped + 1
*/
private static final int genNums(int n, int count, StringBuilder bn) {
bn.setLength(0);
int last = n + count;
for (; n < last ; n++) {
bn.append(n).append('\n');
}
return n;
}
private static final String basename(String path) {
int idx = path.lastIndexOf('/');
if (idx < 0) {
return path;
}
return path.substring(idx+1);
}
%><%@
include file="mast.jspf"
%><%
/* ---------------------- diff.jsp start --------------------- */
{
cfg = PageConfig.get(request);
DiffData dd = cfg.getDiffData();
if (dd.errorMsg != null) {
%>
<div class="src">
<h3 class="error">Error:</h3>
<p><%= dd.errorMsg %></p>
</div><%
} else if (dd.genre == Genre.IMAGE) {
String link = request.getContextPath() + Prefix.RAW_P;
int idx1 = dd.rp1.indexOf('@');
int idx2 = dd.rp2.indexOf('@');
%>
<div id="difftable">
<table class="dtimage">
<thead>
<tr><th title="<%= Util.htmlize(dd.fp1) %>"
><%= Util.htmlize(basename(dd.fp1)) %> (revision <%= dd.rev1 %>)</th>
<th title="<%= Util.htmlize(dd.fp2) %>"
><%= Util.htmlize(basename(dd.fp2)) %> (revision <%= dd.rev2 %>)</th>
</tr>
</thead>
<tbody>
<tr><td class="dti"><img src="<%= link + dd.rp1.substring(0, idx1)
%>?r=<%= dd.rp1.substring(idx1 + 2) %>"/></td>
<td class="dti"><img src="<%= link + dd.rp2.substring(0, idx2)
%>?r=<%= dd.rp2.substring(idx2 + 2) %>"/></td>
</tr>
</tbody>
</table>
</div><%
} else if (dd.genre != Genre.PLAIN && dd.genre != Genre.HTML) {
String link = request.getContextPath() + Prefix.RAW_P;
int idx1 = dd.rp1.indexOf('@');
int idx2 = dd.rp2.indexOf('@');
%>
<div id="src">Diffs for binary files cannot be displayed! Files are <a
href="<%= link + dd.rp1.substring(0, idx1) %>?r=<%= dd.rp1.substring(idx1 + 2)
%>" title="<%= Util.htmlize(dd.fp1) %>"
><%= Util.htmlize(basename(dd.fp1)) %>(revision <%= dd.rev1 %>)</a> and <a
href="<%= link + dd.rp2.substring(0, idx2) %>?r=<%= dd.rp2.substring(idx2 + 2)
%>" title="<%= Util.htmlize(dd.fp2) %>"
><%= Util.htmlize(basename(dd.fp2)) %>(revision <%= dd.rev2 %>)</a>.
</div><%
} else if (dd.revision.size() == 0) {
%>
<%= getAnnotateRevision(dd) %>
<b>No differences found!</b><%
} else {
//-------- Do THE DIFFS ------------
%>
<%= getAnnotateRevision(dd) %>
<div id="diffbar">
<div class="dblegend">
<span class="m">Deleted</span>
<span class="p">Added</span>
</div>
<div class="dbtabs"><%
for (DiffType t : DiffType.values()) {
if (dd.type == t) {
%> <span class="active dbtab"><%= t.toString() %><%
if (t == DiffType.OLD) {
%> ( <%= dd.rev1 %> )<%
} else if (t == DiffType.NEW) {
%> ( <%= dd.rev2 %> )<%
}
%></span><%
} else {
%> <span class="dbtab"><a href="<%= dd.reqURI %>?r1=<%= dd.rp1
%>&amp;r2=<%= dd.rp2 %>&amp;format=<%= t.getAbbrev()
%>&amp;full=<%= dd.full ? '1' : '0' %>"><%= t.toString() %><%
if (t == DiffType.OLD) {
%> ( <%= dd.rev1 %> )<%
} else if (t == DiffType.NEW) {
%> ( <%= dd.rev2 %> )<%
}
%></a></span><%
}
}
%></div>
<div class="dbformats"><%
if (!dd.full) {
%>
<span class="dbformat"><a href="<%= dd.reqURI %>?r1=<%= dd.rp1
%>&amp;r2=<%= dd.rp2 %>&amp;format=<%= dd.type.getAbbrev()
%>&amp;full=1">full</a></span>
<span class="active dbformat">compact</span><%
} else {
%>
<span class="active dbformat">full</span>
<span class="dbformat"> <a href="<%= dd.reqURI %>?r1=<%= dd.rp1
%>&amp;r2=<%= dd.rp2 %>&amp;format=<%= dd.type.getAbbrev()
%>&amp;full=0">compact</a></span><%
}
%></div>
</div>
<div id="difftable"><%
if (dd.type != DiffType.TEXT) {
%><table id="<%= "dt" + dd.type %>"><%
if (dd.type == DiffType.SIDEBYSIDE) {
%>
<thead><tr>
<th colspan="2" title="<%= Util.htmlize(dd.fp1) %>"
><%= Util.htmlize(basename(dd.fp1)) %> (<%= dd.rev1 %>)</th>
<th colspan="2" title="<%= Util.htmlize(dd.fp2) %>"
><%= Util.htmlize(basename(dd.fp2)) %> (<%= dd.rev2 %>)</th>
</tr></thead><%
} else if (dd.type == DiffType.WDIFF) {
%>
<thead class="dtk">
<tr><th class="dtm" colspan="3" title="<%= Util.htmlize(dd.fp1) %>"
><%= Util.htmlize(dd.fp1) %> (<%= dd.rev1 %>)</th></tr>
<tr><th class="dtp" colspan="3" title="<%= Util.htmlize(dd.fp2) %>"
><%= Util.htmlize(dd.fp2) %> (<%= dd.rev2 %>)</th></tr>
</thead><%
}
%>
<tbody><%
}
StringBuilder bs1 = new StringBuilder(256); // formatted source lines file1
StringBuilder bs2 = new StringBuilder(256); // formatted source lines file2
StringBuilder bn1 = new StringBuilder(32); // formatted line#s file1
StringBuilder bn2 = new StringBuilder(32); // formatted line#s file2
int ln1 = 1; // next line of file1 to process
int ln2 = 1; // next line of file2 to process
String[] file1 = dd.file[0]; // source lines of the 'old' file
String[] file2 = dd.file[1]; // source lines of the 'new' file
Hidden h1 = new Hidden(dd.full); // hidden line info + trailing stuff file1
Hidden h2 = new Hidden(dd.full); // hidden line info + trailing stuff file2
StringBuilder lna = null;
if (dd.type == DiffType.WDIFF) {
lna = new StringBuilder(32); // buffer for line#s
}
boolean like_udiff = dd.type == DiffType.UNIFIED || dd.type == DiffType.WDIFF;
for (int i=0; i < dd.revision.size(); i++) {
Delta d = dd.revision.getDelta(i);
if (dd.type == DiffType.TEXT) {
out.write(Util.htmlize(d.toString()));
continue;
}
Chunk c1 = d.getOriginal();
Chunk c2 = d.getRevised();
int cn1 = c1.first() + 1; // line# of the 1st line of chunk file1
int cl1 = c1.last() + 1; // line# of the last line of chunk file1
int cn2 = c2.first() + 1; // line# of the 1st line of chunk file2
int cl2 = c2.last() + 1; // line# of the last line of chunk file2
int i1 = cn1 - 1, i2 = cn2 - 1; // current line# in file1, file2
// changed
for (; i1 < cl1 && i2 < cl2; i1++, i2++) {
String[] ss = Util
.diffline(Util.htmlize(file1[i1]), Util.htmlize(file2[i2]));
file1[i1] = ss[0];
file2[i2] = ss[1];
}
// deleted
for (; i1 < cl1; i1++) {
bs1.setLength(0);
file1[i1] = bs1.append(Util.SPAN_D)
.append(Util.htmlize(file1[i1])).append("</span>")
.toString();
}
// added
for (; i2 < cl2; i2++) {
bs2.setLength(0);
file2[i2] = bs2.append(Util.SPAN_A)
.append(Util.htmlize(file2[i2])).append("</span>")
.toString();
}
bn2.setLength(0);
bs2.setLength(0);
// To Dump flag: if == 1 normal, if == 2 sdiff, if == 3 wdiff row
int td = 1;
String tClass = " class='dtk'";
if (dd.type == DiffType.OLD && cn1 > ln1) {
tClass = "";
ln1 = dumpFile(ln1, cn1 - ln1, 8, file1, bn2, bs2, h2, true);
} else if (dd.type == DiffType.NEW && cn2 > ln2) {
tClass = "";
ln2 = dumpFile(ln2, cn2 - ln2, 8, file2, bn2, bs2, h2, true);
} else if (like_udiff && (cn1 > ln1 || cn2 > ln2)) {
ln2 = dumpFile(ln2, cn2 - ln2, 8, file2, bn2, bs2, h2, true);
if (dd.type == DiffType.WDIFF) {
bn1.setLength(0); // hidden source
bs1.setLength(0); // line#s of hidden source
h1.full = true; // dump all
dumpFile(h2.start + h2.leading, h2.hidden, 0, file2, bn1, bs1, h1, false);
h1.full = dd.full; // restore
// h1 is not needed, so we abuse it to store linenums
ln1 = genNums(ln1, h2.leading, h1.bn);
ln1 = genNums(ln1, h2.hidden, lna);
ln1 = genNums(ln1, h2.trailing, h1.bs);
td = 3;
}
ln1 = cn1;
} else if (dd.type == DiffType.SIDEBYSIDE && (cn1 > ln1 || cn2 > ln2)) {
bn1.setLength(0);
bs1.setLength(0);
h1.full |= cn2 - ln2 + 1 < 20; // force same dump strategy for both
ln1 = dumpFile(ln1, cn1 - ln1, 8, file1, bn1, bs1, h1, false);
h1.full = dd.full; // restore
ln2 = dumpFile(ln2, cn2 - ln2, 8, file2, bn2, bs2, h2, true);
td = 2;
} else {
td = 0;
}
if (td > 0) {
// leading block
%>
<tr <%= tClass %>><%
if (td == 2) {
%>
<td class="dtn"><%= bn1 %></td>
<td class="dts"><%= bs1 %></td><%
} else if (td == 3) {
%>
<td class="dtn"><%= h1.bn %></td><%
}
%>
<td class="dtn"><%= bn2 %></td>
<td class="dts"><%= bs2 %></td>
</tr><%
// hidden block
if (h2.hidden > 0) {
%>
<tr class="dtl" id="hi_<%= i %>"><%
if (td < 3) {
String txt = getHiddenBlockInfo(h2, dd);
if (td == 2) {
%>
<td class="dth" colspan="2"><%= txt %></td><%
}
%>
<td class="dth" colspan="2"><%= txt %></td><%
} else {
// wdiff
%>
<td class="dth" colspan="3"><%= getHiddenBlockInfo(h2, dd) %></td>
</tr>
<tr class="dte" id="hs_<%= i %>">
<td class="dtn"><%= lna %></td>
<td class="dtn"><%= bn1 %></td>
<td class="dts"><%= bs1 %></td>
<%
}
%>
</tr><%
}
// trailing block
if (h2.trailing > 0) {
%>
<tr <%= tClass %>><%
if (td == 2) {
%>
<td class="dtn"><%= h1.bn %></td>
<td class="dts"><%= h1.bs %></td><%
} else if (td == 3) {
%>
<td class="dtn"><%= h1.bs %></td><%
}
%>
<td class="dtn"><%= h2.bn %></td>
<td class="dts"><%= h2.bs %></td>
</tr><%
}
}
// chunks
bn2.setLength(0);
bs2.setLength(0);
td = 1;
tClass = " class='dtk'";
String tdClass = "dtp";
if (dd.type == DiffType.OLD && cn1 <= cl1) {
tdClass = "dtm"; tClass = "";
ln1 = dumpChunk(cn1, cl1, ln1, file1, bn2, bs2, true);
} else if (dd.type == DiffType.NEW && cn2 <= cl2) {
tClass = "";
ln2 = dumpChunk(cn2, cl2, ln2, file2, bn2, bs2, true);
} else if (like_udiff) {
td = 0;
if (cn1 <= cl1) {
ln1 = dumpChunk(cn1, cl1, ln1, file1, bn2, bs2, false);
%>
<tr>
<td class="dtm"><%= dd.type == DiffType.WDIFF ? bn2 + "</td><td> " : bn2 %></td>
<td class="dts"><%= bs2 %></td>
</tr><%
}
if (cn2 <= cl2) {
bn2.setLength(0);
bs2.setLength(0);
ln2 = dumpChunk(cn2, cl2, ln2, file2, bn2, bs2, true);
td = 1;
}
} else if (dd.type == DiffType.SIDEBYSIDE
&& (cn1 <= cl1 || cn2 <= cl2))
{
bn1.setLength(0);
bs1.setLength(0);
ln1 = dumpChunk(cn1, cl1, ln1, file1, bn1, bs1, false);
ln2 = dumpChunk(cn2, cl2, ln2, file2, bn2, bs2, true);
td = 2;
} else {
td = 0;
}
if (td > 0) {
%>
<tr <%= tClass %>><%
if (td == 2) {
%>
<td class="dtm"><%= bn1 %></td>
<td class="dts"><%= bs1 %></td><%
}
if (dd.type == DiffType.WDIFF) { // important to not use <td/> !
%><td> </td><%
}
%>
<td class="<%= tdClass %>"><%= bn2 %></td>
<td class="dts"><%= bs2 %></td>
</tr><%
}
} // for revision.size(
// deltas done, dump the remaining - the same as in the first part
// of the loop, but without a trailing block
if (file1.length >= ln1) {
bn2.setLength(0);
bs2.setLength(0);
int td = 1;
if (dd.type == DiffType.OLD && file1.length >= ln1) {
ln1 = dumpFile(ln1, file1.length - ln1 + 1, 0, file1, bn2, bs2, h2, true);
} else if (dd.type == DiffType.NEW && file2.length >= ln2) {
ln2 = dumpFile(ln2, file2.length - ln2 + 1, 0, file2, bn2, bs2, h2, true);
} else if (like_udiff && file2.length >= ln2) {
ln2 = dumpFile(ln2, file2.length - ln2 + 1, 0, file2, bn2, bs2, h2, true);
if (dd.type == DiffType.WDIFF) {
bn1.setLength(0); // hidden source
bs1.setLength(0); // line#s of hidden source
h1.full = true; // dump all
dumpFile(h2.start + h2.leading, h2.hidden, 0, file2, bn1, bs1, h1, false);
h1.full = dd.full; // restore
// h1 is not needed, so we abuse it to store linenums
ln1 = genNums(ln1, h2.leading, h1.bn);
ln1 = genNums(ln1, h2.hidden, lna);
td = 3;
}
} else if (dd.type == DiffType.SIDEBYSIDE
&& (file1.length >= ln1 || file2.length >= ln2))
{
bn1.setLength(0);
bs1.setLength(0);
h1.full |= file1.length - ln1 + 1 < 20; // force same dump strategy for both
ln1 = dumpFile(ln1, file1.length - ln1 + 1, 0, file1, bn1, bs1, h1, false);
h1.full = dd.full; // restore
ln2 = dumpFile(ln2, file2.length - ln2 + 1, 0, file2, bn2, bs2, h2, true);
td = 2;
} else {
td = 0;
}
if (td > 0) {
// leading block
%>
<tr><%
if (td == 2) {
%>
<td class="dtn"><%= bn1 %></td>
<td class="dts"><%= bs1 %></td><%
} else if (td == 3) {
%>
<td class="dtn"><%= h1.bn %></td><%
}
%>
<td class="dtn"><%= bn2 %></td>
<td class="dts"><%= bs2 %></td>
</tr><%
// hidden block
if (h2.hidden > 0) {
%>
<tr class="dtl" id="hi_<%= dd.revision.size() %>"><%
if (td < 3) {
String txt = getHiddenBlockInfo(h2, dd);
if (td == 2) {
%>
<td class="dth" colspan="2"><%= txt %></td><%
}
%>
<td class="dth" colspan="2"><%= txt %></td><%
} else {
// wdiff
%>
<td class="dth" colspan="3"><%= getHiddenBlockInfo(h2, dd) %></td>
</tr>
<tr class="dte" id="hs_<%= dd.revision.size() %>">
<td class="dtn"><%= lna %></td>
<td class="dtn"><%= bn1 %></td>
<td class="dts"><%= bs1 %></td>
<%
}
%>
</tr><%
}
// trailing block is always empty, since last
}
} // EOFs
if (dd.type != DiffType.TEXT ) {
%>
</tbody>
</table><%
}
//----DIFFS Done--------
%></div><%
}
}
/* ---------------------- diff.jsp end --------------------- */
%><%@
include file="foot.jspf"
%>