| // 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; |
| } |
| } |