/******************************************************************************* * Copyright (C) 2015, Max Hohenegger <eclipse@hohenegger.eu> * * 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.gitflow.ui.internal.dialogs; import static org.eclipse.core.databinding.UpdateValueStrategy.POLICY_ON_REQUEST; import static org.eclipse.core.databinding.beans.PojoProperties.value; import static org.eclipse.egit.gitflow.InitParameters.DEVELOP_BRANCH_PROPERTY; import static org.eclipse.egit.gitflow.InitParameters.FEATURE_BRANCH_PREFIX_PROPERTY; import static org.eclipse.egit.gitflow.InitParameters.HOTFIX_BRANCH_PREFIX_PROPERTY; import static org.eclipse.egit.gitflow.InitParameters.MASTER_BRANCH_PROPERTY; import static org.eclipse.egit.gitflow.InitParameters.RELEASE_BRANCH_PREFIX_PROPERTY; import static org.eclipse.egit.gitflow.InitParameters.VERSION_TAG_PROPERTY; import static org.eclipse.egit.gitflow.ui.Activator.error; import static org.eclipse.egit.gitflow.ui.Activator.warning; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_branchDoesNotExistYetAndWillBeCreated; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_chooseBranchNamesAndPrefixes; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_developBranch; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_featureBranchPrefix; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_hotfixBranchPrefix; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_initializeRepository; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_invalidBranchName; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_invalidPrefix; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_masterBranch; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_masterBranchIsMissing; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_releaseBranchPrefix; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_selectedMasterBranchDoesNotExistCreateNow; import static org.eclipse.egit.gitflow.ui.internal.UIText.InitDialog_versionTagPrefix; import static org.eclipse.jface.databinding.swt.WidgetProperties.text; import static org.eclipse.jface.dialogs.IDialogConstants.OK_ID; import static org.eclipse.jface.dialogs.IMessageProvider.ERROR; import static org.eclipse.jface.dialogs.MessageDialog.openQuestion; import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.eclipse.jgit.lib.Repository.isValidRefName; import static org.eclipse.swt.SWT.Modify; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.core.databinding.Binding; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.ValidationStatusProvider; import org.eclipse.core.databinding.validation.IValidator; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.egit.core.op.CreateLocalBranchOperation; import org.eclipse.egit.gitflow.GitFlowRepository; import org.eclipse.egit.gitflow.InitParameters; import org.eclipse.egit.gitflow.WrongGitFlowStateException; import org.eclipse.jface.databinding.dialog.TitleAreaDialogSupport; import org.eclipse.jface.databinding.dialog.ValidationMessageProvider; import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; /** * Dialog to gather inputs for the git flow init operation. */ public class InitDialog extends TitleAreaDialog { private static final class BranchValidator implements IValidator { @Override public IStatus validate(Object value) { if (value == null || !isValidRefName(R_HEADS + value)) { return error(NLS.bind(InitDialog_invalidBranchName, value)); } return Status.OK_STATUS; } } private static final class BranchExistsValidator implements IValidator { private List<String> list; public BranchExistsValidator(List<Ref> branchList) { list = new ArrayList<>(); for (Ref ref : branchList) { list.add(ref.getName()); } } @Override public IStatus validate(Object value) { if (value == null || !list.contains(R_HEADS + value)) { return warning(NLS.bind(InitDialog_branchDoesNotExistYetAndWillBeCreated, value)); } return Status.OK_STATUS; } } private Text developText; private InitParameters gitflowInitConfig = new InitParameters(); private Text masterText; private Text featureText; private Text releaseText; private Text hotfixText; private Text versionTagText; private static final String DUMMY_POSTFIX = "dummy"; //$NON-NLS-1$ private static final int TEXT_WIDTH = 100; private GitFlowRepository gfRepo; private List<Ref> branchList; /** * @param parentShell * @param gfRepo * @param branchList */ public InitDialog(Shell parentShell, GitFlowRepository gfRepo, List<Ref> branchList) { super(parentShell); this.gfRepo = gfRepo; this.branchList = branchList; } @Override public void create() { super.create(); setTitle(InitDialog_initializeRepository); setMessage(InitDialog_chooseBranchNamesAndPrefixes); } @Override protected Control createDialogArea(Composite parent) { Composite area = (Composite) super.createDialogArea(parent); Composite container = new Composite(area, SWT.NONE); container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayoutFactory.swtDefaults().numColumns(4).applyTo(container); createInputs(container); DataBindingContext bindingContext = initDataBinding(); TitleAreaDialogSupport.create(this, bindingContext).setValidationMessageProvider(new ValidationMessageProvider() { @Override public String getMessage(ValidationStatusProvider statusProvider) { if (statusProvider == null) { return InitDialog_chooseBranchNamesAndPrefixes; } return super.getMessage(statusProvider); } @Override public int getMessageType(ValidationStatusProvider statusProvider) { int type = super.getMessageType(statusProvider); Button okButton = getButton(OK_ID); if(okButton != null) { okButton.setEnabled(type != ERROR); } return type; } }); return area; } private void createInputs(Composite container) { developText = createLabeledText(container, InitDialog_developBranch); masterText = createLabeledText(container, InitDialog_masterBranch); featureText = createLabeledText(container, InitDialog_featureBranchPrefix); releaseText = createLabeledText(container, InitDialog_releaseBranchPrefix); hotfixText = createLabeledText(container, InitDialog_hotfixBranchPrefix); versionTagText = createLabeledText(container, InitDialog_versionTagPrefix); } private Text createLabeledText(Composite container, String label) { new Label(container, SWT.NONE).setText(label); Text result = new Text(container, SWT.BORDER); GridDataFactory.swtDefaults().hint(TEXT_WIDTH, SWT.DEFAULT).applyTo(result); return result; } private DataBindingContext initDataBinding() { DataBindingContext context = new DataBindingContext(); UpdateValueStrategy noModelToTarget = new UpdateValueStrategy(false, POLICY_ON_REQUEST); UpdateValueStrategy developUpdateStrategy = new UpdateValueStrategy(); developUpdateStrategy.setBeforeSetValidator(new BranchValidator()); bind(context, noModelToTarget, developUpdateStrategy, DEVELOP_BRANCH_PROPERTY, developText); UpdateValueStrategy masterUpdateStrategy = new UpdateValueStrategy(); masterUpdateStrategy.setBeforeSetValidator(new BranchValidator()); masterUpdateStrategy.setAfterConvertValidator(new BranchExistsValidator(branchList)); bind(context, noModelToTarget, masterUpdateStrategy, MASTER_BRANCH_PROPERTY, masterText); UpdateValueStrategy prefixTargetToModel = new UpdateValueStrategy(); prefixTargetToModel.setBeforeSetValidator(new IValidator() { @Override public IStatus validate(Object value) { if (value == null || !isValidRefName(R_HEADS + value + DUMMY_POSTFIX)) { return error(NLS.bind(InitDialog_invalidPrefix, value)); } return Status.OK_STATUS; } }); bind(context, noModelToTarget, prefixTargetToModel, FEATURE_BRANCH_PREFIX_PROPERTY, featureText); bind(context, noModelToTarget, prefixTargetToModel, RELEASE_BRANCH_PREFIX_PROPERTY, releaseText); bind(context, noModelToTarget, prefixTargetToModel, HOTFIX_BRANCH_PREFIX_PROPERTY, hotfixText); bind(context, noModelToTarget, prefixTargetToModel, VERSION_TAG_PROPERTY, versionTagText); context.updateTargets(); return context; } private void bind(DataBindingContext dataBindingContext, UpdateValueStrategy noModelToTargetUpdate, UpdateValueStrategy targetToModel, String modelProperty, Text widget) { Binding binding = dataBindingContext.bindValue( text(Modify).observe(widget), value(modelProperty).observe(gitflowInitConfig), targetToModel, noModelToTargetUpdate); ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT); } @Override protected Control createButtonBar(Composite parent) { // TODO: we should have options to persist the selected configuration return super.createButtonBar(parent); } @Override public boolean isHelpAvailable() { return false; } @Override protected boolean isResizable() { return true; } @Override protected void okPressed() { String master = gitflowInitConfig.getMaster(); Repository repository = gfRepo.getRepository(); if (isMasterBranchAvailable(master, repository)) { super.okPressed(); return; } boolean createBranch = openQuestion(getShell(), InitDialog_masterBranchIsMissing, NLS.bind(InitDialog_selectedMasterBranchDoesNotExistCreateNow, master)); if (!createBranch) { return; } try { RevCommit head = gfRepo.findHead(); new CreateLocalBranchOperation(repository, master, head).execute(null); } catch (CoreException | WrongGitFlowStateException e) { throw new RuntimeException(e); } super.okPressed(); } private boolean isMasterBranchAvailable(String master, Repository repository) { try { return repository.exactRef(R_HEADS + master) != null; } catch (IOException e) { throw new RuntimeException(e); } } /** * @return User inputs */ @NonNull public InitParameters getResult() { return gitflowInitConfig; } }