com.google.gerrit.client.changes.ChangeScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.client.changes.ChangeScreen.java

Source

// 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.changes;

import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.account.AccountInfo;
import com.google.gerrit.client.projects.ConfigInfoCache;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gerrit.client.ui.CommentPanel;
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
import com.google.gerrit.client.ui.ExpandAllCommand;
import com.google.gerrit.client.ui.LinkMenuBar;
import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.common.data.AccountInfoCache;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.ChangeInfo;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.CommentVisibilityStrategy;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.globalkey.client.KeyCommand;
import com.google.gwtexpui.globalkey.client.KeyCommandSet;

import java.sql.Timestamp;
import java.util.List;

public class ChangeScreen extends Screen implements ValueChangeHandler<ChangeDetail> {
    private final Change.Id changeId;
    private final PatchSet.Id openPatchSetId;
    private ChangeDetailCache detailCache;
    private com.google.gerrit.client.changes.ChangeInfo changeInfo;

    private ChangeDescriptionBlock descriptionBlock;
    private ApprovalTable approvals;

    private IncludedInTable includedInTable;
    private DisclosurePanel includedInPanel;
    private ComplexDisclosurePanel dependenciesPanel;
    private ChangeTable dependencies;
    private ChangeTable.Section dependsOn;
    private ChangeTable.Section neededBy;

    private PatchSetsBlock patchSetsBlock;

    private Panel comments;
    private CommentLinkProcessor commentLinkProcessor;

    private KeyCommandSet keysNavigation;
    private KeyCommandSet keysAction;
    private HandlerRegistration regNavigation;
    private HandlerRegistration regAction;

    private Grid patchesGrid;
    private ListBox patchesList;

    /**
     * The change id for which the old version history is valid.
     */
    private static Change.Id currentChangeId;

    /**
     * Which patch set id is the diff base.
     */
    private static PatchSet.Id diffBaseId;

    public ChangeScreen(final Change.Id toShow) {
        changeId = toShow;
        openPatchSetId = null;
    }

    public ChangeScreen(final PatchSet.Id toShow) {
        changeId = toShow.getParentKey();
        openPatchSetId = toShow;
    }

    public ChangeScreen(final ChangeInfo c) {
        this(c.getId());
    }

    @Override
    protected void onLoad() {
        super.onLoad();
        detailCache.refresh();
    }

    @Override
    protected void onUnload() {
        if (regNavigation != null) {
            regNavigation.removeHandler();
            regNavigation = null;
        }
        if (regAction != null) {
            regAction.removeHandler();
            regAction = null;
        }
        super.onUnload();
    }

    @Override
    public void registerKeys() {
        super.registerKeys();
        regNavigation = GlobalKey.add(this, keysNavigation);
        regAction = GlobalKey.add(this, keysAction);
        if (openPatchSetId != null) {
            patchSetsBlock.activate(openPatchSetId);
        }
    }

    @Override
    protected void onInitUI() {
        super.onInitUI();

        ChangeCache cache = ChangeCache.get(changeId);

        detailCache = cache.getChangeDetailCache();
        detailCache.addValueChangeHandler(this);

        addStyleName(Gerrit.RESOURCES.css().changeScreen());
        addStyleName(Gerrit.RESOURCES.css().screenNoHeader());

        keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
        keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
        keysNavigation.add(new UpToListKeyCommand(0, 'u', Util.C.upToChangeList()));
        keysNavigation
                .add(new ExpandCollapseDependencySectionKeyCommand(0, 'd', Util.C.expandCollapseDependencies()));

        if (Gerrit.isSignedIn()) {
            keysAction.add(new PublishCommentsKeyCommand(0, 'r', Util.C.keyPublishComments()));
        }

        descriptionBlock = new ChangeDescriptionBlock(keysAction);
        add(descriptionBlock);

        approvals = new ApprovalTable();
        add(approvals);

        includedInPanel = new DisclosurePanel(Util.C.changeScreenIncludedIn());
        includedInTable = new IncludedInTable(changeId);

        includedInPanel.setContent(includedInTable);
        add(includedInPanel);

        dependencies = new ChangeTable() {
            {
                table.setWidth("auto");
            }
        };
        dependsOn = new ChangeTable.Section(Util.C.changeScreenDependsOn());
        dependsOn.setChangeRowFormatter(new ChangeTable.ChangeRowFormatter() {
            @Override
            public String getRowStyle(ChangeInfo c) {
                if (!c.isLatest() || Change.Status.ABANDONED.equals(c.getStatus())) {
                    return Gerrit.RESOURCES.css().outdated();
                }
                return null;
            }

            @Override
            public String getDisplayText(final ChangeInfo c, final String displayText) {
                if (!c.isLatest()) {
                    return displayText + " [OUTDATED]";
                }
                return displayText;
            }
        });
        neededBy = new ChangeTable.Section(Util.C.changeScreenNeededBy());
        dependencies.addSection(dependsOn);
        dependencies.addSection(neededBy);

        dependenciesPanel = new ComplexDisclosurePanel(Util.C.changeScreenDependencies(), false);
        dependenciesPanel.setContent(dependencies);
        add(dependenciesPanel);

        patchesList = new ListBox();
        patchesList.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                final int index = patchesList.getSelectedIndex();
                final String selectedPatchSet = patchesList.getValue(index);
                if (index == 0) {
                    diffBaseId = null;
                } else {
                    diffBaseId = PatchSet.Id.parse(selectedPatchSet);
                }
                if (patchSetsBlock != null) {
                    patchSetsBlock.refresh(diffBaseId);
                }
            }
        });

        patchesGrid = new Grid(1, 2);
        patchesGrid.setStyleName(Gerrit.RESOURCES.css().selectPatchSetOldVersion());
        patchesGrid.setText(0, 0, Util.C.referenceVersion());
        patchesGrid.setWidget(0, 1, patchesList);
        add(patchesGrid);

        patchSetsBlock = new PatchSetsBlock();
        add(patchSetsBlock);

        comments = new FlowPanel();
        comments.setStyleName(Gerrit.RESOURCES.css().changeComments());
        add(comments);
    }

    private void displayTitle(final Change.Key changeId, final String subject) {
        final StringBuilder titleBuf = new StringBuilder();
        if (LocaleInfo.getCurrentLocale().isRTL()) {
            if (subject != null) {
                titleBuf.append(subject);
                titleBuf.append(" :");
            }
            titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
        } else {
            titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
            if (subject != null) {
                titleBuf.append(": ");
                titleBuf.append(subject);
            }
        }
        setPageTitle(titleBuf.toString());
        setHeaderVisible(false);
    }

    @Override
    public void onValueChange(final ValueChangeEvent<ChangeDetail> event) {
        if (isAttached()) {
            // Until this screen is fully migrated to the new API, these calls must
            // happen sequentially after the ChangeDetail lookup, because we can't
            // start an async get at the source of every call that might trigger a
            // value change.
            CallbackGroup cbs = new CallbackGroup();
            ConfigInfoCache.get(event.getValue().getChange().getProject(),
                    cbs.add(new GerritCallback<ConfigInfoCache.Entry>() {
                        @Override
                        public void onSuccess(ConfigInfoCache.Entry result) {
                            commentLinkProcessor = result.getCommentLinkProcessor();
                            setTheme(result.getTheme());
                        }

                        @Override
                        public void onFailure(Throwable caught) {
                            // Handled by last callback's onFailure.
                        }
                    }));
            ChangeApi.detail(event.getValue().getChange().getId().get(),
                    cbs.add(new GerritCallback<com.google.gerrit.client.changes.ChangeInfo>() {
                        @Override
                        public void onSuccess(com.google.gerrit.client.changes.ChangeInfo result) {
                            changeInfo = result;
                            display(event.getValue());
                        }
                    }));
        }
    }

    private void display(final ChangeDetail detail) {
        displayTitle(detail.getChange().getKey(), detail.getChange().getSubject());
        discardDiffBaseIfNotApplicable(detail.getChange().getId());

        if (Status.MERGED == detail.getChange().getStatus()) {
            includedInPanel.setVisible(true);
            includedInPanel.addOpenHandler(includedInTable);
        } else {
            includedInPanel.setVisible(false);
        }

        dependencies.setAccountInfoCache(detail.getAccounts());

        descriptionBlock.display(detail.getChange(), detail.isStarred(), detail.canEditCommitMessage(),
                detail.getCurrentPatchSetDetail().getInfo(), detail.getAccounts(), detail.getSubmitTypeRecord(),
                commentLinkProcessor);
        dependsOn.display(detail.getDependsOn());
        neededBy.display(detail.getNeededBy());
        approvals.display(changeInfo);

        patchesList.clear();
        if (detail.getCurrentPatchSetDetail().getInfo().getParents().size() > 1) {
            patchesList.addItem(Util.C.autoMerge());
        } else {
            patchesList.addItem(Util.C.baseDiffItem());
        }
        for (PatchSet pId : detail.getPatchSets()) {
            if (patchesList != null) {
                patchesList.addItem(Util.M.patchSetHeader(pId.getPatchSetId()), pId.getId().toString());
            }
        }

        if (diffBaseId != null && patchesList != null) {
            patchesList.setSelectedIndex(diffBaseId.get());
        }

        patchSetsBlock.display(detail, diffBaseId);
        addComments(detail);

        // If any dependency change is still open, or is outdated,
        // or the change is needed by a change that is new or submitted,
        // show our dependency list.
        //
        boolean depsOpen = false;
        int outdated = 0;
        if (!detail.getChange().getStatus().isClosed()) {
            final List<ChangeInfo> dependsOn = detail.getDependsOn();
            if (dependsOn != null) {
                for (final ChangeInfo ci : dependsOn) {
                    if (!ci.isLatest()) {
                        depsOpen = true;
                        outdated++;
                    } else if (ci.getStatus() != Change.Status.MERGED) {
                        depsOpen = true;
                    }
                }
            }
        }
        final List<ChangeInfo> neededBy = detail.getNeededBy();
        if (neededBy != null) {
            for (final ChangeInfo ci : neededBy) {
                if ((ci.getStatus() == Change.Status.NEW) || (ci.getStatus() == Change.Status.SUBMITTED)
                        || (ci.getStatus() == Change.Status.DRAFT)) {
                    depsOpen = true;
                }
            }
        }

        dependenciesPanel.setOpen(depsOpen);

        dependenciesPanel.getHeader().clear();
        if (outdated > 0) {
            dependenciesPanel.getHeader().add(new InlineLabel(Util.M.outdatedHeader(outdated)));
        }

        if (!isCurrentView()) {
            display();
        }
        patchSetsBlock.setRegisterKeys(true);
    }

    private static void discardDiffBaseIfNotApplicable(final Change.Id toShow) {
        if (currentChangeId != null && !currentChangeId.equals(toShow)) {
            diffBaseId = null;
        }
        currentChangeId = toShow;
    }

    private void addComments(final ChangeDetail detail) {
        comments.clear();

        final AccountInfoCache accts = detail.getAccounts();
        final List<ChangeMessage> msgList = detail.getMessages();

        HorizontalPanel title = new HorizontalPanel();
        title.setWidth("100%");
        title.add(new Label(Util.C.changeScreenComments()));
        if (msgList.size() > 1) {
            title.add(messagesMenuBar());
        }
        title.setStyleName(Gerrit.RESOURCES.css().blockHeader());
        comments.add(title);

        final long AGE = 7 * 24 * 60 * 60 * 1000L;
        final Timestamp aged = new Timestamp(System.currentTimeMillis() - AGE);

        CommentVisibilityStrategy commentVisibilityStrategy = CommentVisibilityStrategy.EXPAND_RECENT;
        if (Gerrit.isSignedIn()) {
            commentVisibilityStrategy = Gerrit.getUserAccount().getGeneralPreferences()
                    .getCommentVisibilityStrategy();
        }

        for (int i = 0; i < msgList.size(); i++) {
            final ChangeMessage msg = msgList.get(i);

            AccountInfo author;
            if (msg.getAuthor() != null) {
                author = FormatUtil.asInfo(accts.get(msg.getAuthor()));
            } else {
                author = AccountInfo.create(0, Util.C.messageNoAuthor(), null);
            }

            boolean isRecent;
            if (i == msgList.size() - 1) {
                isRecent = true;
            } else {
                // TODO Instead of opening messages by strict age, do it by "unread"?
                isRecent = msg.getWrittenOn().after(aged);
            }

            final CommentPanel cp = new CommentPanel(author, msg.getWrittenOn(), msg.getMessage(),
                    commentLinkProcessor);
            cp.setRecent(isRecent);
            cp.addStyleName(Gerrit.RESOURCES.css().commentPanelBorder());
            if (i == msgList.size() - 1) {
                cp.addStyleName(Gerrit.RESOURCES.css().commentPanelLast());
            }
            boolean isOpen = false;
            switch (commentVisibilityStrategy) {
            case COLLAPSE_ALL:
                break;
            case EXPAND_ALL:
                isOpen = true;
                break;
            case EXPAND_MOST_RECENT:
                isOpen = i == msgList.size() - 1;
                break;
            case EXPAND_RECENT:
            default:
                isOpen = isRecent;
                break;
            }
            cp.setOpen(isOpen);
            comments.add(cp);
        }

        final Button b = new Button(Util.C.changeScreenAddComment());
        b.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final ClickEvent event) {
                PatchSet.Id currentPatchSetId = patchSetsBlock.getCurrentPatchSet().getId();
                Gerrit.display(Dispatcher.toPublish(currentPatchSetId));
            }
        });
        comments.add(b);
        comments.setVisible(msgList.size() > 0);
    }

    private LinkMenuBar messagesMenuBar() {
        final Panel c = comments;
        final LinkMenuBar menuBar = new LinkMenuBar();
        menuBar.addItem(Util.C.messageExpandRecent(), new ExpandAllCommand(c, true) {
            @Override
            protected void expand(final CommentPanel w) {
                w.setOpen(w.isRecent());
            }
        });
        menuBar.addItem(Util.C.messageExpandAll(), new ExpandAllCommand(c, true));
        menuBar.addItem(Util.C.messageCollapseAll(), new ExpandAllCommand(c, false));
        menuBar.addStyleName(Gerrit.RESOURCES.css().commentPanelMenuBar());
        return menuBar;
    }

    public class UpToListKeyCommand extends KeyCommand {
        public UpToListKeyCommand(int mask, char key, String help) {
            super(mask, key, help);
        }

        @Override
        public void onKeyPress(final KeyPressEvent event) {
            Gerrit.displayLastChangeList();
        }
    }

    public class ExpandCollapseDependencySectionKeyCommand extends KeyCommand {
        public ExpandCollapseDependencySectionKeyCommand(int mask, char key, String help) {
            super(mask, key, help);
        }

        @Override
        public void onKeyPress(KeyPressEvent event) {
            dependenciesPanel.setOpen(!dependenciesPanel.isOpen());
        }
    }

    public class PublishCommentsKeyCommand extends NeedsSignInKeyCommand {
        public PublishCommentsKeyCommand(int mask, char key, String help) {
            super(mask, key, help);
        }

        @Override
        public void onKeyPress(final KeyPressEvent event) {
            PatchSet.Id currentPatchSetId = patchSetsBlock.getCurrentPatchSet().getId();
            Gerrit.display(Dispatcher.toPublish(currentPatchSetId));
        }
    }
}