blob: b2be30a1b6c62bba238a6ffb35e1298c22981613 [file] [log] [blame]
// Copyright (C) 2008 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.patches;
import static com.google.gerrit.client.patches.PatchLine.Type.CONTEXT;
import static com.google.gerrit.client.patches.PatchLine.Type.DELETE;
import static com.google.gerrit.client.patches.PatchLine.Type.INSERT;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
import com.google.gerrit.prettify.common.EditList;
import com.google.gerrit.prettify.common.SparseHtmlFile;
import com.google.gerrit.prettify.common.EditList.Hunk;
import com.google.gerrit.reviewdb.Patch;
import com.google.gerrit.reviewdb.PatchLineComment;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import com.google.gwtorm.client.KeyUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class UnifiedDiffTable extends AbstractPatchContentTable {
private static final int PC = 3;
private static final Comparator<PatchLineComment> BY_DATE =
new Comparator<PatchLineComment>() {
public int compare(final PatchLineComment o1, final PatchLineComment o2) {
return o1.getWrittenOn().compareTo(o2.getWrittenOn());
}
};
@Override
protected void onCellDoubleClick(final int row, final int column) {
if (getRowItem(row) instanceof PatchLine) {
final PatchLine pl = (PatchLine) getRowItem(row);
switch (pl.getType()) {
case DELETE:
case CONTEXT:
createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
break;
case INSERT:
createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
break;
}
}
}
@Override
protected void onInsertComment(final PatchLine pl) {
final int row = getCurrentRow();
switch (pl.getType()) {
case DELETE:
case CONTEXT:
createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
break;
case INSERT:
createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
break;
}
}
private void appendImgTag(SafeHtmlBuilder nc, String url) {
nc.openElement("img");
nc.setAttribute("src", url);
nc.closeElement("img");
}
@Override
protected void render(final PatchScript script) {
final SparseHtmlFile a = script.getSparseHtmlFileA();
final SparseHtmlFile b = script.getSparseHtmlFileB();
final SafeHtmlBuilder nc = new SafeHtmlBuilder();
// Display the patch header
for (final String line : script.getPatchHeader()) {
appendFileHeader(nc, line);
}
if (script.getDisplayMethodA() == DisplayMethod.IMG
|| script.getDisplayMethodB() == DisplayMethod.IMG) {
final String rawBase = GWT.getHostPageBaseURL() + "cat/";
nc.openTr();
nc.setAttribute("valign", "center");
nc.setAttribute("align", "center");
nc.openTd();
nc.nbsp();
nc.closeTd();
nc.openTd();
nc.nbsp();
nc.closeTd();
nc.openTd();
nc.nbsp();
nc.closeTd();
nc.openTd();
if (script.getDisplayMethodA() == DisplayMethod.IMG) {
if (idSideA == null) {
appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^1");
} else {
Patch.Key k = new Patch.Key(idSideA, patchKey.get());
appendImgTag(nc, rawBase + KeyUtil.encode(k.toString()) + "^0");
}
}
if (script.getDisplayMethodB() == DisplayMethod.IMG) {
appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^0");
}
nc.closeTd();
nc.closeTr();
}
final boolean syntaxHighlighting =
script.getSettings().getPrettySettings().isSyntaxHighlighting();
final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
for (final EditList.Hunk hunk : script.getHunks()) {
appendHunkHeader(nc, hunk);
while (hunk.next()) {
if (hunk.isContextLine()) {
openLine(nc);
appendLineNumber(nc, hunk.getCurA());
appendLineNumber(nc, hunk.getCurB());
appendLineText(nc, false, CONTEXT, a, hunk.getCurA());
closeLine(nc);
hunk.incBoth();
lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
} else if (hunk.isDeletedA()) {
openLine(nc);
appendLineNumber(nc, hunk.getCurA());
padLineNumber(nc);
appendLineText(nc, syntaxHighlighting, DELETE, a, hunk.getCurA());
closeLine(nc);
hunk.incA();
lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
if (a.size() == hunk.getCurA()
&& script.getA().isMissingNewlineAtEnd()) {
appendNoLF(nc);
}
} else if (hunk.isInsertedB()) {
openLine(nc);
padLineNumber(nc);
appendLineNumber(nc, hunk.getCurB());
appendLineText(nc, syntaxHighlighting, INSERT, b, hunk.getCurB());
closeLine(nc);
hunk.incB();
lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
if (b.size() == hunk.getCurB()
&& script.getB().isMissingNewlineAtEnd()) {
appendNoLF(nc);
}
}
}
}
resetHtml(nc);
initScript(script);
int row = script.getPatchHeader().size();
final CellFormatter fmt = table.getCellFormatter();
final Iterator<PatchLine> iLine = lines.iterator();
while (iLine.hasNext()) {
final PatchLine l = iLine.next();
final String n;
switch (l.getType()) {
case CONTEXT:
n = Gerrit.RESOURCES.css().diffTextCONTEXT();
break;
case DELETE:
n = Gerrit.RESOURCES.css().diffTextDELETE();
break;
case INSERT:
n = Gerrit.RESOURCES.css().diffTextINSERT();
break;
default:
continue;
}
while (!fmt.getStyleName(row, PC).contains(n)) {
row++;
}
setRowItem(row++, l);
}
}
@Override
public void display(final CommentDetail cd) {
if (cd.isEmpty()) {
return;
}
setAccountInfoCache(cd.getAccounts());
final ArrayList<PatchLineComment> all = new ArrayList<PatchLineComment>();
for (int row = 0; row < table.getRowCount();) {
if (getRowItem(row) instanceof PatchLine) {
final PatchLine pLine = (PatchLine) getRowItem(row);
final List<PatchLineComment> fora = cd.getForA(pLine.getLineA());
final List<PatchLineComment> forb = cd.getForB(pLine.getLineB());
row++;
if (!fora.isEmpty() && !forb.isEmpty()) {
all.clear();
all.addAll(fora);
all.addAll(forb);
Collections.sort(all, BY_DATE);
row = insert(all, row);
} else if (!fora.isEmpty()) {
row = insert(fora, row);
} else if (!forb.isEmpty()) {
row = insert(forb, row);
}
} else {
row++;
}
}
}
@Override
protected void insertRow(final int row) {
super.insertRow(row);
final CellFormatter fmt = table.getCellFormatter();
fmt.addStyleName(row, PC - 2, Gerrit.RESOURCES.css().lineNumber());
fmt.addStyleName(row, PC - 1, Gerrit.RESOURCES.css().lineNumber());
fmt.addStyleName(row, PC, Gerrit.RESOURCES.css().diffText());
}
private int insert(final List<PatchLineComment> in, int row) {
for (Iterator<PatchLineComment> ci = in.iterator(); ci.hasNext();) {
final PatchLineComment c = ci.next();
insertRow(row);
bindComment(row, PC, c, !ci.hasNext());
row++;
}
return row;
}
private void appendFileHeader(final SafeHtmlBuilder m, final String line) {
openLine(m);
padLineNumber(m);
padLineNumber(m);
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().diffText());
m.addStyleName(Gerrit.RESOURCES.css().diffTextFileHeader());
m.append(line);
m.closeTd();
closeLine(m);
}
private void appendHunkHeader(final SafeHtmlBuilder m, final Hunk hunk) {
openLine(m);
padLineNumber(m);
padLineNumber(m);
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().diffText());
m.addStyleName(Gerrit.RESOURCES.css().diffTextHunkHeader());
m.append("@@ -");
appendRange(m, hunk.getCurA() + 1, hunk.getEndA() - hunk.getCurA());
m.append(" +");
appendRange(m, hunk.getCurB() + 1, hunk.getEndB() - hunk.getCurB());
m.append(" @@");
m.closeTd();
closeLine(m);
}
private void appendRange(final SafeHtmlBuilder m, final int begin,
final int cnt) {
switch (cnt) {
case 0:
m.append(begin - 1);
m.append(",0");
break;
case 1:
m.append(begin);
break;
default:
m.append(begin);
m.append(',');
m.append(cnt);
break;
}
}
private void appendLineText(final SafeHtmlBuilder m,
boolean syntaxHighlighting, final PatchLine.Type type,
final SparseHtmlFile src, final int i) {
final SafeHtml text = src.getSafeHtmlLine(i);
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().diffText());
switch (type) {
case CONTEXT:
m.addStyleName(Gerrit.RESOURCES.css().diffTextCONTEXT());
m.nbsp();
m.append(text);
break;
case DELETE:
m.addStyleName(Gerrit.RESOURCES.css().diffTextDELETE());
if (syntaxHighlighting) {
m.addStyleName(Gerrit.RESOURCES.css().fileLineDELETE());
}
m.append("-");
m.append(text);
break;
case INSERT:
m.addStyleName(Gerrit.RESOURCES.css().diffTextINSERT());
if (syntaxHighlighting) {
m.addStyleName(Gerrit.RESOURCES.css().fileLineINSERT());
}
m.append("+");
m.append(text);
break;
}
m.closeTd();
}
private void appendNoLF(final SafeHtmlBuilder m) {
openLine(m);
padLineNumber(m);
padLineNumber(m);
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().diffText());
m.addStyleName(Gerrit.RESOURCES.css().diffTextNoLF());
m.append("\\ No newline at end of file");
m.closeTd();
closeLine(m);
}
private void openLine(final SafeHtmlBuilder m) {
m.openTr();
m.setAttribute("valign", "top");
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().iconCell());
m.closeTd();
}
private void closeLine(final SafeHtmlBuilder m) {
m.closeTr();
}
private void padLineNumber(final SafeHtmlBuilder m) {
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().lineNumber());
m.closeTd();
}
private void appendLineNumber(final SafeHtmlBuilder m, final int idx) {
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().lineNumber());
m.append(idx + 1);
m.closeTd();
}
}