org.eclipse.egit.ui.internal.pull.PullWizardPage.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.ui.internal.pull.PullWizardPage.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Red Hat Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.egit.ui.internal.pull;

import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.eclipse.egit.core.op.CreateLocalBranchOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.components.BranchRebaseModeCombo;
import org.eclipse.egit.ui.internal.components.RefContentAssistProvider;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.IRemoteSelectionListener;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.SelectionType;
import org.eclipse.egit.ui.internal.push.AddRemoteWizard;
import org.eclipse.egit.ui.internal.push.PushBranchPage;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.lib.BranchConfig;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

/**
 * This Wizard Page allows to configure a Push operation (remote, reference,
 * rebase/merge)
 *
 * It is heavily inspired/copy-pasted from the {@link PushBranchPage} and a lot
 * of code could be factorized.
 */
public class PullWizardPage extends WizardPage {

    private RemoteSelectionCombo remoteSelectionCombo;

    private List<RemoteConfig> remoteConfigs;
    private RemoteConfig remoteConfig;

    private RefContentAssistProvider assist;

    private Repository repository;

    private String fullBranch;

    private Button rememberConfigForBranch;

    private BranchRebaseModeCombo upstreamConfigComponent;

    private BranchRebaseMode upstreamConfig;

    private Ref head;

    private Text remoteBranchNameText;

    private ControlDecoration missingBranchDecorator;

    private boolean configureUpstream;

    /**
     * Create the page.
     *
     * @param repository
     */
    public PullWizardPage(Repository repository) {
        super(UIText.PullWizardPage_PageName);
        setTitle(UIText.PullWizardPage_PageTitle);
        setMessage(UIText.PullWizardPage_PageMessage);
        setImageDescriptor(UIIcons.WIZBAN_PULL);
        this.repository = repository;
        try {
            this.head = repository.findRef(Constants.HEAD);
            this.fullBranch = repository.getFullBranch();
        } catch (IOException ex) {
            Activator.logError(ex.getMessage(), ex);
        }
    }

    @Override
    public void createControl(Composite parent) {
        try {
            this.remoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
            Collections.sort(remoteConfigs, new Comparator<RemoteConfig>() {
                @Override
                public int compare(RemoteConfig first, RemoteConfig second) {
                    return String.CASE_INSENSITIVE_ORDER.compare(first.getName(), second.getName());
                }
            });
            setDefaultUpstreamConfig();
        } catch (URISyntaxException e) {
            this.remoteConfigs = new ArrayList<>();
            handleError(e);
        }

        Composite res = new Composite(parent, SWT.NONE);
        res.setLayout(new GridLayout(3, false));

        Label remoteLabel = new Label(res, SWT.NONE);
        remoteLabel.setText(UIText.PushBranchPage_RemoteLabel);

        this.remoteSelectionCombo = new RemoteSelectionCombo(res, SWT.NONE, SelectionType.PUSH);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(remoteSelectionCombo);
        setRemoteConfigs();
        remoteSelectionCombo.addRemoteSelectionListener(new IRemoteSelectionListener() {
            @Override
            public void remoteSelected(RemoteConfig rc) {
                remoteConfig = rc;
                setRefAssist(rc);
                checkPage();
            }
        });

        Button newRemoteButton = new Button(res, SWT.PUSH);
        newRemoteButton.setText(UIText.PushBranchPage_NewRemoteButton);
        GridDataFactory.fillDefaults().applyTo(newRemoteButton);
        newRemoteButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                showNewRemoteDialog();
            }
        });

        Label branchNameLabel = new Label(res, SWT.NONE);
        branchNameLabel.setText(UIText.PullWizardPage_referenceLabel);
        branchNameLabel.setToolTipText(UIText.PullWizardPage_referenceTooltip);

        remoteBranchNameText = new Text(res, SWT.BORDER);
        GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(remoteBranchNameText);
        UIUtils.addRefContentProposalToText(remoteBranchNameText, this.repository, () -> {
            if (PullWizardPage.this.assist != null) {
                return PullWizardPage.this.assist.getRefsForContentAssist(false, true);
            }
            return Collections.emptyList();
        });
        remoteBranchNameText.setText(getSuggestedBranchName());
        remoteBranchNameText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                checkPage();
            }
        });

        this.upstreamConfigComponent = new BranchRebaseModeCombo(res);
        GridDataFactory.fillDefaults().grab(true, false).span(2, 1).align(SWT.BEGINNING, SWT.CENTER)
                .applyTo(upstreamConfigComponent.getViewer().getCombo());
        this.upstreamConfigComponent.getViewer()
                .addSelectionChangedListener((event) -> upstreamConfig = upstreamConfigComponent.getRebaseMode());
        if (upstreamConfig != null) {
            upstreamConfigComponent.setRebaseMode(upstreamConfig);
        }
        if (this.fullBranch != null && this.fullBranch.startsWith(Constants.R_HEADS)) {
            this.rememberConfigForBranch = new Button(res, SWT.CHECK);
            GridData checkboxLayoutData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 3, 1);
            checkboxLayoutData.verticalIndent = 20;
            this.rememberConfigForBranch.setText(UIText.UpstreamConfigComponent_ConfigureUpstreamCheck);
            this.rememberConfigForBranch.setToolTipText(UIText.UpstreamConfigComponent_ConfigureUpstreamToolTip);
            this.rememberConfigForBranch.setLayoutData(checkboxLayoutData);
            this.rememberConfigForBranch.setSelection(this.configureUpstream);
            this.rememberConfigForBranch.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    configureUpstream = rememberConfigForBranch.getSelection();
                    checkPage();
                }
            });
        }

        setPageComplete(isPageComplete());
        setControl(res);
    }

    private void setRemoteConfigs() {
        remoteSelectionCombo.setItems(remoteConfigs);
        if (this.head != null) {
            String branchName = Repository.shortenRefName(this.head.getName());
            BranchConfig branchConfig = new BranchConfig(repository.getConfig(), branchName);
            String remoteName = branchConfig.getRemote();
            if (remoteName != null) {
                for (RemoteConfig rc : remoteConfigs) {
                    if (remoteName.equals(rc.getName()))
                        remoteSelectionCombo.setSelectedRemote(rc);
                }
            }
        }

        remoteConfig = remoteSelectionCombo.getSelectedRemote();
        setRefAssist(remoteConfig);
    }

    private void showNewRemoteDialog() {
        AddRemoteWizard wizard = new AddRemoteWizard(repository);
        WizardDialog dialog = new WizardDialog(getShell(), wizard);
        int result = dialog.open();
        if (result == Window.OK) {
            URIish uri = wizard.getUri();
            String remoteName = wizard.getRemoteName();
            try {
                StoredConfig repoConfig = repository.getConfig();
                RemoteConfig newRemoteConfig = new RemoteConfig(repoConfig, remoteName);
                newRemoteConfig.addURI(uri);
                RefSpec defaultFetchSpec = new RefSpec().setForceUpdate(true).setSourceDestination(
                        Constants.R_HEADS + "*", //$NON-NLS-1$
                        Constants.R_REMOTES + remoteName + "/*"); //$NON-NLS-1$
                newRemoteConfig.addFetchRefSpec(defaultFetchSpec);
                newRemoteConfig.update(repoConfig);
                repoConfig.save();
                List<RemoteConfig> allRemoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
                remoteSelectionCombo.setItems(allRemoteConfigs);
                // find the new remote in the list, as the initial
                // newRemoteConfig object
                // isn't what's stored and returned by getAllRemoteConfigs
                for (RemoteConfig current : allRemoteConfigs) {
                    if (newRemoteConfig.getName().equals(current.getName())) {
                        setSelectedRemote(current);
                    }
                }
            } catch (URISyntaxException ex) {
                Activator.logError(ex.getMessage(), ex);
            } catch (IOException ex) {
                Activator.logError(ex.getMessage(), ex);
            }
        }
    }

    private void setRefAssist(RemoteConfig config) {
        if (config != null && config.getURIs().size() > 0) {
            this.assist = new RefContentAssistProvider(PullWizardPage.this.repository, config.getURIs().get(0),
                    getContainer());
        }
    }

    void setSelectedRemote(RemoteConfig config) {
        remoteSelectionCombo.setSelectedRemote(config);
        this.remoteConfig = config;
        setRefAssist(this.remoteConfig);
        checkPage();
    }

    @Override
    public boolean isPageComplete() {
        return remoteConfig != null && remoteBranchNameText.getText() != null
                && !remoteBranchNameText.getText().isEmpty();
    }

    private void checkPage() {
        try {
            if (remoteConfig == null) {
                setErrorMessage(UIText.PushBranchPage_ChooseRemoteError);
                return;
            }
            String branchName = remoteBranchNameText.getText();
            String branchNameMessage = null;
            if (branchName.length() == 0) {
                branchNameMessage = MessageFormat.format(UIText.PullWizardPage_ChooseReference,
                        remoteConfig.getName());
            }
            if (branchNameMessage != null) {
                setErrorMessage(branchNameMessage);
                if (this.missingBranchDecorator == null) {
                    this.missingBranchDecorator = new ControlDecoration(this.remoteBranchNameText,
                            SWT.TOP | SWT.LEFT);
                    this.missingBranchDecorator.setImage(FieldDecorationRegistry.getDefault()
                            .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage());
                }
                this.missingBranchDecorator.setDescriptionText(branchNameMessage);
                this.missingBranchDecorator.show();
                return;
            } else if (this.missingBranchDecorator != null) {
                this.missingBranchDecorator.hide();
            }

            if (overrideUpstreamConfiguration() && hasDifferentUpstreamConfiguration()) {
                setMessage(UIText.PushBranchPage_UpstreamConfigOverwriteWarning, IMessageProvider.WARNING);
            } else {
                setMessage(UIText.PullWizardPage_PageMessage);
            }
            setErrorMessage(null);
        } finally {
            setPageComplete(getErrorMessage() == null);
        }
    }

    private void handleError(URISyntaxException e) {
        Activator.handleError(e.getMessage(), e, false);
        setErrorMessage(e.getMessage());
    }

    private String getSuggestedBranchName() {
        if (fullBranch != null) {
            String branchName = Repository.shortenRefName(fullBranch);
            StoredConfig config = repository.getConfig();
            BranchConfig branchConfig = new BranchConfig(config, branchName);
            String merge = branchConfig.getMerge();
            if (!branchConfig.isRemoteLocal() && merge != null && merge.startsWith(Constants.R_HEADS)) {
                return Repository.shortenRefName(merge);
            }
        }
        return ""; //$NON-NLS-1$
    }

    boolean overrideUpstreamConfiguration() {
        return this.configureUpstream;
    }

    BranchRebaseMode getUpstreamConfig() {
        return this.upstreamConfig;
    }

    private boolean hasDifferentUpstreamConfiguration() {
        String branchName = Repository.shortenRefName(this.fullBranch);
        BranchConfig branchConfig = new BranchConfig(repository.getConfig(), branchName);

        String remote = branchConfig.getRemote();
        // No upstream config -> don't show warning
        if (remote == null) {
            return false;
        }
        if (!remote.equals(remoteConfig.getName())) {
            return true;
        }
        String merge = branchConfig.getMerge();
        if (merge == null || !merge.equals(getFullRemoteReference())) {
            return true;
        }
        if (branchConfig.getRebaseMode() != getUpstreamConfig()) {
            return true;
        }
        return false;
    }

    private void setDefaultUpstreamConfig() {
        String branchName = Repository.shortenRefName(this.fullBranch);
        BranchConfig branchConfig = new BranchConfig(repository.getConfig(), branchName);
        boolean alreadyConfigured = branchConfig.getMerge() != null;
        BranchRebaseMode config;
        if (alreadyConfigured) {
            config = PullCommand.getRebaseMode(branchName, repository.getConfig());
        } else {
            config = CreateLocalBranchOperation.getDefaultUpstreamConfig(repository,
                    Constants.R_REMOTES + Constants.DEFAULT_REMOTE_NAME + "/" + branchName); //$NON-NLS-1$
        }
        this.upstreamConfig = config;
    }

    /**
     * @return the chosen short name of the branch on the remote
     */
    String getFullRemoteReference() {
        if (!remoteBranchNameText.getText().startsWith(Constants.R_REFS))
            return Constants.R_HEADS + remoteBranchNameText.getText();
        else
            return remoteBranchNameText.getText();
    }

    RemoteConfig getRemoteConfig() {
        return this.remoteConfig;
    }

    @Override
    public void dispose() {
        super.dispose();
        if (this.missingBranchDecorator != null) {
            this.missingBranchDecorator.dispose();
        }
    }
}