blob: b3d46909b3015c11f16c816cba850879f7419a87 [file] [log] [blame]
// Copyright (C) 2014 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.googlesource.gerrit.plugins.xdocs.client;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.plugin.client.Plugin;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineHyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import com.googlesource.gerrit.plugins.xdocs.client.ChangeInfo.EditInfo;
import com.googlesource.gerrit.plugins.xdocs.client.ChangeInfo.RevisionInfo;
import java.util.List;
public abstract class XDocDiffScreen extends VerticalPanel {
protected final String changeId;
protected final String path;
protected String revisionA;
protected String revisionB;
protected int patchSet;
protected Integer base;
private FlowPanel iconPanel;
private FlowPanel additionalIconPanel;
XDocDiffScreen(String changeId, final String patchSet, String path) {
setStyleName("xdocs-panel");
this.changeId = changeId;
this.path = path;
ChangeApi.getChangeInfo(changeId, new AsyncCallback<ChangeInfo>() {
@Override
public void onSuccess(final ChangeInfo change) {
change.revisions().copyKeysIntoChildren("name");
if (Plugin.get().isSignedIn()) {
ChangeApi.edit(change._number(), new AsyncCallback<EditInfo>() {
@Override
public void onSuccess(EditInfo edit) {
if (edit != null) {
change.set_edit(edit);
change.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
}
initRevisionsAndShow(change);
}
@Override
public void onFailure(Throwable caught) {
// never invoked
}
});
} else {
initRevisionsAndShow(change);
}
}
private void initRevisionsAndShow(final ChangeInfo change) {
parseRevisions(change, patchSet);
if (revisionA == null) {
ProjectApi.getCommitInfo(change.project(), change.current_revision(),
new AsyncCallback<CommitInfo>() {
@Override
public void onSuccess(CommitInfo commit) {
if (commit.parents() != null) {
List<CommitInfo> parents = Natives.asList(commit.parents());
if (!parents.isEmpty()) {
revisionA = parents.get(0).commit();
}
}
show(change);
}
@Override
public void onFailure(Throwable caught) {
// never invoked
}
});
} else {
show(change);
}
}
private void show(ChangeInfo change) {
addHeader(change);
init();
display(change);
}
@Override
public void onFailure(Throwable caught) {
showError("Unable to load change " + XDocDiffScreen.this.changeId
+ ": " + caught.getMessage());
}
});
}
protected abstract void display(ChangeInfo change);
protected String getPath() {
return path;
}
protected String getRevisionA() {
return revisionA;
}
protected String getRevisionB() {
return revisionB;
}
private void parseRevisions(ChangeInfo change, String patchSetString) {
int i = patchSetString.indexOf("..");
if (i > 0) {
base = parsePatchSet(patchSetString.substring(0, i));
revisionA = getRevision(change, base);
if (patchSetString.length() > i + 2) {
patchSet = parsePatchSet(patchSetString.substring(i + 2));
revisionB = getRevision(change, patchSet);
} else {
throw new IllegalArgumentException("Invalid patch set: " + patchSetString);
}
} else {
patchSet = parsePatchSet(patchSetString);
revisionB = getRevision(change, patchSet);
}
}
private static String getRevision(ChangeInfo change, int patchSet) {
for (RevisionInfo rev : Natives.asList(change.revisions().values())) {
if (rev.is_edit()) {
return rev.commit().commit();
}
if (rev._number() == patchSet) {
return rev.ref();
}
}
throw new IllegalArgumentException("Patch set " + patchSet + " not found.");
}
private static int parsePatchSet(String patchSet) {
try {
return Integer.valueOf(patchSet);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid patch set: " + patchSet);
}
}
private void addHeader(ChangeInfo change) {
HorizontalPanel p = new HorizontalPanel();
p.setStyleName("xdocs-header");
p.add(getPathHeader(change));
iconPanel = new FlowPanel();
iconPanel.setStyleName("xdocs-icon-panel");
p.add(iconPanel);
additionalIconPanel = new FlowPanel();
iconPanel.add(additionalIconPanel);
addNavigationButtons(change);
add(p);
}
protected void init() {
}
private Widget getPathHeader(ChangeInfo change) {
HorizontalPanel p = new HorizontalPanel();
p.setStyleName("xdocs-file-header");
p.add(new InlineHyperlink(change.project(), "/admin/projects/" + change.project()));
p.add(new Label("/"));
SafeHtmlBuilder html = new SafeHtmlBuilder();
if (FileInfo.COMMIT_MSG.equals(path)) {
html.append("Commit Message");
} else {
int s = path.lastIndexOf('/') + 1;
html.append(path.substring(0, s));
html.openElement("b");
html.append(path.substring(s));
html.closeElement("b");
}
p.add(new InlineHTML(html.toSafeHtml()));
return p;
}
private void addNavigationButtons(final ChangeInfo change) {
DiffApi.list(changeId, patchSet, base,
new AsyncCallback<NativeMap<FileInfo>>() {
@Override
public void onSuccess(NativeMap<FileInfo> result) {
JsArray<FileInfo> files = result.values();
FileInfo.sortFileInfoByPath(files);
int index = 0;
for (int i = 0; i < files.length(); i++) {
if (path.equals(files.get(i).path())) {
index = i;
break;
}
}
FileInfo prevInfo = index == 0 ? null : files.get(index - 1);
if (prevInfo != null) {
iconPanel.add(createNavLink(XDocsPlugin.RESOURCES.goPrev(),
change, patchSet, base, prevInfo));
}
iconPanel.add(createIcon(XDocsPlugin.RESOURCES.goUp(),
"Up to change", toChange(change)));
FileInfo nextInfo = index == files.length() - 1
? null
: files.get(index + 1);
if (nextInfo != null) {
iconPanel.add(createNavLink(XDocsPlugin.RESOURCES.goNext(),
change, patchSet, base, nextInfo));
}
}
@Override
public void onFailure(Throwable caught) {
showError("Unable to load files of change " + changeId + ": "
+ caught.getMessage());
}
});
}
private InlineHyperlink createNavLink(ImageResource res,
final ChangeInfo change, final int patchSet, final Integer base,
final FileInfo file) {
final InlineHyperlink link = createIcon(
res, FileInfo.getFileName(file.path()),
toFile(change, patchSet, base, file));
XDocApi.checkHtml(XDocApi.getUrl(change.project(),
getRevision(change, patchSet), file.path()),
new AsyncCallback<VoidResult>() {
@Override
public void onSuccess(VoidResult result) {
link.setTargetHistoryToken(
toPreview(change, patchSet, base, file));
}
@Override
public void onFailure(Throwable caught) {
}
});
return link;
}
protected static InlineHyperlink createIcon(ImageResource res, String tooltip, String target) {
InlineHyperlink l = new InlineHyperlink(
AbstractImagePrototype.create(res).getHTML(), true, target);
if (tooltip != null) {
l.setTitle(tooltip);
}
return l;
}
protected void addIcon(InlineHyperlink icon) {
additionalIconPanel.add(icon);
}
private String toPreview(ChangeInfo change, int patchSet,
Integer base, FileInfo file) {
String panel = getPanel();
return "/x/" + Plugin.get().getName()
+ toPatchSet(change, patchSet, base)
+ file.path()
+ (panel != null ? "," + panel : "");
}
private String toFile(ChangeInfo change, int patchSet, Integer base,
FileInfo file) {
String panel = file.binary() ? "unified" : getPanel();
return toPatchSet(change, patchSet, base)
+ file.path()
+ (panel != null ? "," + panel : "");
}
protected String getPanel() {
return null;
}
private static String toPatchSet(ChangeInfo change, int patchSet, Integer base) {
return toChange(change)
+ (base != null ? patchSet + ".." + base : patchSet) + "/";
}
private static String toChange(ChangeInfo change) {
return "/c/" + change._number() + "/";
}
protected void showError(String message) {
Label l = new Label(message);
l.setStyleName("xdocs-error");
add(l);
}
protected static int addRow(FlexTable table) {
int row = table.getRowCount();
table.insertRow(row);
return row;
}
}