com.google.gerrit.client.change.RelatedChanges.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.client.change.RelatedChanges.java

Source

// Copyright (C) 2013 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.change;

import static com.google.gerrit.common.PageLinks.op;

import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeList;
import com.google.gerrit.client.info.ChangeInfo;
import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.TabBar;
import com.google.gwt.user.client.ui.TabPanel;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

public class RelatedChanges extends TabPanel {
    static final RelatedChangesResources R = GWT.create(RelatedChangesResources.class);

    interface RelatedChangesResources extends ClientBundle {
        @Source("related_changes.css")
        RelatedChangesCss css();
    }

    interface RelatedChangesCss extends CssResource {
        String activeRow();

        String current();

        String gitweb();

        String indirect();

        String notCurrent();

        String pointer();

        String row();

        String subject();

        String strikedSubject();

        String submittable();

        String tabPanel();
    }

    enum Tab {
        RELATED_CHANGES(Resources.C.relatedChanges(), Resources.C.relatedChangesTooltip()) {
            @Override
            String getTitle(int count) {
                return Resources.M.relatedChanges(count);
            }

            @Override
            String getTitle(String count) {
                return Resources.M.relatedChanges(count);
            }
        },

        SUBMITTED_TOGETHER(Resources.C.submittedTogether(), Resources.C.submittedTogether()) {
            @Override
            String getTitle(int count) {
                return Resources.M.submittedTogether(count);
            }

            @Override
            String getTitle(String count) {
                return Resources.M.submittedTogether(count);
            }
        },

        SAME_TOPIC(Resources.C.sameTopic(), Resources.C.sameTopicTooltip()) {
            @Override
            String getTitle(int count) {
                return Resources.M.sameTopic(count);
            }

            @Override
            String getTitle(String count) {
                return Resources.M.sameTopic(count);
            }
        },

        CONFLICTING_CHANGES(Resources.C.conflictingChanges(), Resources.C.conflictingChangesTooltip()) {
            @Override
            String getTitle(int count) {
                return Resources.M.conflictingChanges(count);
            }

            @Override
            String getTitle(String count) {
                return Resources.M.conflictingChanges(count);
            }
        },

        CHERRY_PICKS(Resources.C.cherryPicks(), Resources.C.cherryPicksTooltip()) {
            @Override
            String getTitle(int count) {
                return Resources.M.cherryPicks(count);
            }

            @Override
            String getTitle(String count) {
                return Resources.M.cherryPicks(count);
            }
        };

        final String defaultTitle;
        final String tooltip;

        abstract String getTitle(int count);

        abstract String getTitle(String count);

        Tab(String defaultTitle, String tooltip) {
            this.defaultTitle = defaultTitle;
            this.tooltip = tooltip;
        }
    }

    private static Tab savedTab;

    private final List<RelatedChangesTab> tabs;
    private int maxHeightWithHeader;
    private int selectedTab;
    private int outstandingCallbacks;

    RelatedChanges() {
        tabs = new ArrayList<>(Tab.values().length);
        selectedTab = -1;

        setVisible(false);
        addStyleName(R.css().tabPanel());
        initTabBar();
    }

    private void initTabBar() {
        TabBar tabBar = getTabBar();
        tabBar.addSelectionHandler(new SelectionHandler<Integer>() {
            @Override
            public void onSelection(SelectionEvent<Integer> event) {
                if (selectedTab >= 0) {
                    tabs.get(selectedTab).registerKeys(false);
                }
                selectedTab = event.getSelectedItem();
                tabs.get(selectedTab).registerKeys(true);
            }
        });

        for (Tab tabInfo : Tab.values()) {
            RelatedChangesTab panel = new RelatedChangesTab(tabInfo);
            add(panel, tabInfo.defaultTitle);
            tabs.add(panel);

            TabBar.Tab tab = tabBar.getTab(tabInfo.ordinal());
            tab.setWordWrap(false);
            ((Composite) tab).setTitle(tabInfo.tooltip);

            setTabEnabled(tabInfo, false);
        }
        getTab(Tab.RELATED_CHANGES).setShowIndirectAncestors(true);
        getTab(Tab.CHERRY_PICKS).setShowBranches(true);
        getTab(Tab.SAME_TOPIC).setShowBranches(true);
        getTab(Tab.SAME_TOPIC).setShowProjects(true);
        getTab(Tab.SAME_TOPIC).setShowSubmittable(true);
        getTab(Tab.SUBMITTED_TOGETHER).setShowBranches(true);
        getTab(Tab.SUBMITTED_TOGETHER).setShowProjects(true);
        getTab(Tab.SUBMITTED_TOGETHER).setShowSubmittable(true);
    }

    void set(final ChangeInfo info, final String revision) {
        if (info.status().isOpen()) {
            setForOpenChange(info, revision);
        }

        ChangeApi.revision(info.legacyId().get(), revision).view("related")
                .get(new TabCallback<RelatedInfo>(Tab.RELATED_CHANGES, info.project(), revision) {
                    @Override
                    public JsArray<ChangeAndCommit> convert(RelatedInfo result) {
                        return result.changes();
                    }
                });

        StringBuilder cherryPicksQuery = new StringBuilder();
        cherryPicksQuery.append(op("project", info.project()));
        cherryPicksQuery.append(" ").append(op("change", info.changeId()));
        cherryPicksQuery.append(" ").append(op("-change", info.legacyId().get()));
        cherryPicksQuery.append(" -is:abandoned");
        ChangeList.query(cherryPicksQuery.toString(),
                EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
                new TabChangeListCallback(Tab.CHERRY_PICKS, info.project(), revision));

        if (info.currentRevision() != null && info.currentRevision().equals(revision)) {
            ChangeApi.change(info.legacyId().get()).view("submitted_together")
                    .get(new TabChangeListCallback(Tab.SUBMITTED_TOGETHER, info.project(), revision));
        }

        if (!Gerrit.info().change().isSubmitWholeTopicEnabled() && info.topic() != null
                && !"".equals(info.topic())) {
            StringBuilder topicQuery = new StringBuilder();
            topicQuery.append("status:open");
            topicQuery.append(" ").append(op("topic", info.topic()));
            ChangeList.query(topicQuery.toString(),
                    EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT,
                            ListChangesOption.DETAILED_LABELS, ListChangesOption.LABELS),
                    new TabChangeListCallback(Tab.SAME_TOPIC, info.project(), revision));
        }
    }

    private void setForOpenChange(final ChangeInfo info, final String revision) {
        if (info.mergeable()) {
            StringBuilder conflictsQuery = new StringBuilder();
            conflictsQuery.append("status:open");
            conflictsQuery.append(" is:mergeable");
            conflictsQuery.append(" ").append(op("conflicts", info.legacyId().get()));
            ChangeList.query(conflictsQuery.toString(),
                    EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
                    new TabChangeListCallback(Tab.CONFLICTING_CHANGES, info.project(), revision));
        }
    }

    @Override
    protected void onLoad() {
        super.onLoad();
        R.css().ensureInjected();
    }

    static void setSavedTab(Tab subject) {
        savedTab = subject;
    }

    private RelatedChangesTab getTab(Tab tabInfo) {
        return tabs.get(tabInfo.ordinal());
    }

    private void setTabTitle(Tab tabInfo, String title) {
        getTabBar().setTabText(tabInfo.ordinal(), title);
    }

    private void setTabEnabled(Tab tabInfo, boolean enabled) {
        getTabBar().setTabEnabled(tabInfo.ordinal(), enabled);
    }

    void setMaxHeight(int height) {
        maxHeightWithHeader = height;
        if (isVisible()) {
            applyMaxHeight();
        }
    }

    private void applyMaxHeight() {
        int header = getTabBar().getOffsetHeight() + 2 /* padding */;
        for (int i = 0; i < getTabBar().getTabCount(); i++) {
            tabs.get(i).setMaxHeight(maxHeightWithHeader - header);
        }
    }

    private abstract class TabCallback<T> implements AsyncCallback<T> {
        private final Tab tabInfo;
        private final String project;
        private final String revision;

        TabCallback(Tab tabInfo, String project, String revision) {
            this.tabInfo = tabInfo;
            this.project = project;
            this.revision = revision;
            outstandingCallbacks++;
        }

        protected abstract JsArray<ChangeAndCommit> convert(T result);

        @Override
        public void onSuccess(T result) {
            if (isAttached()) {
                JsArray<ChangeAndCommit> changes = convert(result);
                if (changes.length() > 0) {
                    setTabTitle(tabInfo, tabInfo.getTitle(changes.length()));
                    getTab(tabInfo).setChanges(project, revision, changes);
                }
                onDone(changes.length() > 0);
            }
        }

        @Override
        public void onFailure(Throwable err) {
            if (isAttached()) {
                setTabTitle(tabInfo, tabInfo.getTitle(Resources.C.notAvailable()));
                getTab(tabInfo).setError(err.getMessage());
                onDone(true);
            }
        }

        private void onDone(boolean enabled) {
            setTabEnabled(tabInfo, enabled);
            outstandingCallbacks--;
            if (outstandingCallbacks == 0 || (enabled && tabInfo == Tab.RELATED_CHANGES)) {
                outstandingCallbacks = 0; // Only execute this block once
                for (int i = 0; i < getTabBar().getTabCount(); i++) {
                    if (getTabBar().isTabEnabled(i)) {
                        selectTab(i);
                        setVisible(true);
                        applyMaxHeight();
                        break;
                    }
                }
            }

            if (tabInfo == savedTab && enabled) {
                selectTab(savedTab.ordinal());
            }
        }
    }

    private class TabChangeListCallback extends TabCallback<ChangeList> {
        TabChangeListCallback(Tab tabInfo, String project, String revision) {
            super(tabInfo, project, revision);
        }

        @Override
        protected JsArray<ChangeAndCommit> convert(ChangeList l) {
            JsArray<ChangeAndCommit> arr = JavaScriptObject.createArray().cast();
            for (ChangeInfo i : Natives.asList(l)) {
                if (i.currentRevision() != null && i.revisions().containsKey(i.currentRevision())) {
                    RevisionInfo currentRevision = i.revision(i.currentRevision());
                    ChangeAndCommit c = ChangeAndCommit.create();
                    c.setId(i.id());
                    c.setCommit(currentRevision.commit());
                    c.setChangeNumber(i.legacyId().get());
                    c.setRevisionNumber(currentRevision._number());
                    c.setBranch(i.branch());
                    c.setProject(i.project());
                    c.setSubmittable(i.submittable() && i.mergeable());
                    c.setStatus(i.status().asChangeStatus().toString());
                    arr.push(c);
                }
            }
            return arr;
        }
    }

    public static class RelatedInfo extends JavaScriptObject {
        public final native JsArray<ChangeAndCommit> changes() /*-{ return this.changes }-*/;

        protected RelatedInfo() {
        }
    }

    public static class ChangeAndCommit extends JavaScriptObject {
        static ChangeAndCommit create() {
            return (ChangeAndCommit) createObject();
        }

        public final native String id() /*-{ return this.change_id }-*/;

        public final native CommitInfo commit() /*-{ return this.commit }-*/;

        final native String branch() /*-{ return this.branch }-*/;

        final native String project() /*-{ return this.project }-*/;

        final native boolean submittable() /*-{ return this._submittable ? true : false; }-*/;

        final Change.Status status() {
            String s = statusRaw();
            return s != null ? Change.Status.valueOf(s) : null;
        }

        private native String statusRaw() /*-{ return this.status; }-*/;

        final native void setId(String i) /*-{ if(i)this.change_id=i; }-*/;

        final native void setCommit(CommitInfo c) /*-{ if(c)this.commit=c; }-*/;

        final native void setBranch(String b) /*-{ if(b)this.branch=b; }-*/;

        final native void setProject(String b) /*-{ if(b)this.project=b; }-*/;

        public final Change.Id legacyId() {
            return hasChangeNumber() ? new Change.Id(_changeNumber()) : null;
        }

        public final PatchSet.Id patchSetId() {
            return hasChangeNumber() && hasRevisionNumber() ? new PatchSet.Id(legacyId(), _revisionNumber()) : null;
        }

        public final native boolean hasChangeNumber()
        /*-{ return this.hasOwnProperty('_change_number') }-*/ ;

        final native boolean hasRevisionNumber()
        /*-{ return this.hasOwnProperty('_revision_number') }-*/ ;

        final native boolean hasCurrentRevisionNumber()
        /*-{ return this.hasOwnProperty('_current_revision_number') }-*/ ;

        final native int _changeNumber() /*-{ return this._change_number }-*/;

        final native int _revisionNumber() /*-{ return this._revision_number }-*/;

        final native int _currentRevisionNumber() /*-{ return this._current_revision_number }-*/;

        final native void setChangeNumber(int n) /*-{ this._change_number=n; }-*/;

        final native void setRevisionNumber(int n) /*-{ this._revision_number=n; }-*/;

        final native void setCurrentRevisionNumber(int n) /*-{ this._current_revision_number=n; }-*/;

        final native void setSubmittable(boolean s) /*-{ this._submittable=s; }-*/;

        final native void setStatus(String s) /*-{ if(s)this.status=s; }-*/;

        protected ChangeAndCommit() {
        }
    }
}