blob: 2939e714f2b0804ab3624e8184a866e5d2f46373 [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 static com.google.gerrit.client.patches.PatchLine.Type.REPLACE;
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.FileMode;
import com.google.gerrit.prettify.common.EditList;
import com.google.gerrit.prettify.common.SparseHtmlFile;
import com.google.gerrit.reviewdb.Patch;
import com.google.gerrit.reviewdb.PatchLineComment;
import com.google.gerrit.reviewdb.Patch.ChangeType;
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 org.eclipse.jgit.diff.Edit;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SideBySideTable extends AbstractPatchContentTable {
private static final int COL_A = 2;
private static final int COL_B = 4;
@Override
protected void onCellDoubleClick(final int row, int column) {
if (column > 0 && getRowItem(row) instanceof PatchLine) {
final PatchLine line = (PatchLine) getRowItem(row);
final short file = (short) ((column - 1) / 2);
if (column < (1 + file * 2 + 1)) {
column++;
}
switch (file) {
case 0:
createCommentEditor(row + 1, column, line.getLineA(), file);
break;
case 1:
createCommentEditor(row + 1, column, line.getLineB(), file);
break;
}
}
}
@Override
protected void onInsertComment(final PatchLine line) {
final int row = getCurrentRow();
createCommentEditor(row + 1, 4, line.getLineB(), (short) 1);
}
@Override
protected void render(final PatchScript script) {
final SparseHtmlFile a = script.getSparseHtmlFileA();
final SparseHtmlFile b = script.getSparseHtmlFileB();
final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
final SafeHtmlBuilder nc = new SafeHtmlBuilder();
final boolean intraline =
script.getDiffPrefs().isIntralineDifference()
&& script.hasIntralineDifference();
appendHeader(script, nc);
lines.add(null);
if(script.getFileModeA()!=FileMode.FILE||script.getFileModeB()!=FileMode.FILE){
openLine(nc);
appendModeLine(nc, script.getFileModeA());
appendModeLine(nc, script.getFileModeB());
closeLine(nc);
lines.add(null);
}
int lastB = 0;
final boolean ignoreWS = script.isIgnoreWhitespace();
for (final EditList.Hunk hunk : script.getHunks()) {
if (!hunk.isStartOfFile()) {
appendSkipLine(nc, hunk.getCurB() - lastB);
lines.add(null);
}
while (hunk.next()) {
if (hunk.isContextLine()) {
openLine(nc);
final SafeHtml ctx = a.getSafeHtmlLine(hunk.getCurA());
appendLineText(nc, hunk.getCurA(), CONTEXT, ctx, false, false);
if (ignoreWS && b.contains(hunk.getCurB())) {
appendLineText(nc, hunk.getCurB(), CONTEXT, b, hunk.getCurB(),
false);
} else {
appendLineText(nc, hunk.getCurB(), CONTEXT, ctx, false, false);
}
closeLine(nc);
hunk.incBoth();
lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
} else if (hunk.isModifiedLine()) {
final boolean del = hunk.isDeletedA();
final boolean ins = hunk.isInsertedB();
final boolean full =
intraline && hunk.getCurEdit().getType() != Edit.Type.REPLACE;
openLine(nc);
if (del) {
appendLineText(nc, hunk.getCurA(), DELETE, a, hunk.getCurA(), full);
hunk.incA();
} else if (hunk.getCurEdit().getType() == Edit.Type.REPLACE) {
appendLineNone(nc, DELETE);
} else {
appendLineNone(nc, CONTEXT);
}
if (ins) {
appendLineText(nc, hunk.getCurB(), INSERT, b, hunk.getCurB(), full);
hunk.incB();
} else if (hunk.getCurEdit().getType() == Edit.Type.REPLACE) {
appendLineNone(nc, INSERT);
} else {
appendLineNone(nc, CONTEXT);
}
closeLine(nc);
if (del && ins) {
lines.add(new PatchLine(REPLACE, hunk.getCurA(), hunk.getCurB()));
} else if (del) {
lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
} else if (ins) {
lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
}
}
}
lastB = hunk.getCurB();
}
if (lastB != b.size()) {
appendSkipLine(nc, b.size() - lastB);
}
resetHtml(nc);
initScript(script);
for (int row = 0; row < lines.size(); row++) {
setRowItem(row, lines.get(row));
}
}
private void appendModeLine(final SafeHtmlBuilder nc, final FileMode mode) {
nc.openTd();
nc.setStyleName(Gerrit.RESOURCES.css().lineNumber());
nc.nbsp();
nc.closeTd();
nc.openTd();
nc.addStyleName(Gerrit.RESOURCES.css().fileLine());
nc.addStyleName(Gerrit.RESOURCES.css().fileLineMode());
switch(mode){
case FILE:
nc.nbsp();
break;
case SYMLINK:
nc.append(PatchUtil.C.fileTypeSymlink());
break;
case GITLINK:
nc.append(PatchUtil.C.fileTypeGitlink());
break;
}
nc.closeTd();
}
@Override
public void display(final CommentDetail cd) {
if (cd.isEmpty()) {
return;
}
setAccountInfoCache(cd.getAccounts());
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++;
final Iterator<PatchLineComment> ai = fora.iterator();
final Iterator<PatchLineComment> bi = forb.iterator();
while (ai.hasNext() && bi.hasNext()) {
final PatchLineComment ac = ai.next();
final PatchLineComment bc = bi.next();
insertRow(row);
bindComment(row, COL_A, ac, !ai.hasNext());
bindComment(row, COL_B, bc, !bi.hasNext());
row++;
}
row = finish(ai, row, COL_A);
row = finish(bi, row, COL_B);
} else {
row++;
}
}
}
@Override
protected void insertRow(final int row) {
super.insertRow(row);
final CellFormatter fmt = table.getCellFormatter();
fmt.addStyleName(row, COL_A - 1, Gerrit.RESOURCES.css().lineNumber());
fmt.addStyleName(row, COL_A, Gerrit.RESOURCES.css().diffText());
fmt.addStyleName(row, COL_B - 1, Gerrit.RESOURCES.css().lineNumber());
fmt.addStyleName(row, COL_B, Gerrit.RESOURCES.css().diffText());
}
private int finish(final Iterator<PatchLineComment> i, int row, final int col) {
while (i.hasNext()) {
final PatchLineComment c = i.next();
insertRow(row);
bindComment(row, col, c, !i.hasNext());
row++;
}
return row;
}
private void appendHeader(PatchScript script, final SafeHtmlBuilder m) {
m.openTr();
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().iconCell());
m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
m.closeTd();
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
m.addStyleName(Gerrit.RESOURCES.css().lineNumber());
m.closeTd();
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
m.setAttribute("width", "50%");
if (script.getChangeType() == ChangeType.RENAMED
|| script.getChangeType() == ChangeType.COPIED) {
m.append(script.getOldName());
} else {
m.append(PatchUtil.C.patchHeaderOld());
}
m.br();
if (0 < script.getA().size()) {
if (idSideA == null) {
downloadLink(m, patchKey, "1");
} else {
downloadLink(m, new Patch.Key(idSideA, patchKey.get()), "0");
}
}
m.closeTd();
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
m.addStyleName(Gerrit.RESOURCES.css().lineNumber());
m.closeTd();
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
m.setAttribute("width", "50%");
m.append(PatchUtil.C.patchHeaderNew());
m.br();
if (0 < script.getB().size()) {
downloadLink(m, new Patch.Key(idSideB, patchKey.get()), "0");
}
m.closeTd();
m.closeTr();
}
private void downloadLink(final SafeHtmlBuilder m, final Patch.Key key,
final String side) {
final String base = GWT.getHostPageBaseURL() + "cat/";
m.openAnchor();
m.setAttribute("href", base + KeyUtil.encode(key.toString()) + "^" + side);
m.append(PatchUtil.C.download());
m.closeAnchor();
}
private void appendSkipLine(final SafeHtmlBuilder m, final int skipCnt) {
m.openTr();
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().iconCell());
m.addStyleName(Gerrit.RESOURCES.css().skipLine());
m.closeTd();
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().skipLine());
m.setAttribute("colspan", 4);
m.append(PatchUtil.M.patchSkipRegion(skipCnt));
m.closeTd();
m.closeTr();
}
private void openLine(final SafeHtmlBuilder m) {
m.openTr();
m.setAttribute("valign", "top");
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().iconCell());
m.closeTd();
}
private void appendLineText(final SafeHtmlBuilder m,
final int lineNumberMinusOne, final PatchLine.Type type,
final SparseHtmlFile src, final int i, final boolean fullBlock) {
appendLineText(m, lineNumberMinusOne, type, //
src.getSafeHtmlLine(i), src.hasTrailingEdit(i), fullBlock);
}
private void appendLineText(final SafeHtmlBuilder m,
final int lineNumberMinusOne, final PatchLine.Type type,
final SafeHtml lineHtml, final boolean trailingEdit,
final boolean fullBlock) {
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().lineNumber());
m.append(lineNumberMinusOne + 1);
m.closeTd();
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().fileLine());
switch (type) {
case CONTEXT:
m.addStyleName(Gerrit.RESOURCES.css().fileLineCONTEXT());
break;
case DELETE:
m.addStyleName(Gerrit.RESOURCES.css().fileLineDELETE());
if (trailingEdit || fullBlock) {
m.addStyleName("wdd");
}
break;
case INSERT:
m.addStyleName(Gerrit.RESOURCES.css().fileLineINSERT());
if (trailingEdit || fullBlock) {
m.addStyleName("wdi");
}
break;
}
m.append(lineHtml);
m.closeTd();
}
private void appendLineNone(final SafeHtmlBuilder m, final PatchLine.Type type) {
m.openTd();
m.setStyleName(Gerrit.RESOURCES.css().lineNumber());
m.closeTd();
m.openTd();
m.addStyleName(Gerrit.RESOURCES.css().fileLine());
switch (type != null ? type : PatchLine.Type.CONTEXT) {
case DELETE:
m.addStyleName(Gerrit.RESOURCES.css().fileLineDELETE());
break;
case INSERT:
m.addStyleName(Gerrit.RESOURCES.css().fileLineINSERT());
break;
default:
m.addStyleName(Gerrit.RESOURCES.css().fileLineNone());
break;
}
m.closeTd();
}
private void closeLine(final SafeHtmlBuilder m) {
m.closeTr();
}
}