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