Eclipseplugins
SelectBranchesPage.java
1 package com.proalpha.pds.gitutils.mylyn;
2 
3 import java.net.URL;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.List;
8 import java.util.Set;
9 
10 import org.eclipse.core.runtime.FileLocator;
11 import org.eclipse.core.runtime.Path;
12 import org.eclipse.core.runtime.Platform;
13 import org.eclipse.jface.resource.ImageDescriptor;
14 import org.eclipse.jface.wizard.WizardPage;
15 import org.eclipse.jgit.api.Git;
16 import org.eclipse.jgit.api.errors.GitAPIException;
17 import org.eclipse.jgit.lib.Ref;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.events.SelectionAdapter;
20 import org.eclipse.swt.events.SelectionEvent;
21 import org.eclipse.swt.events.SelectionListener;
22 import org.eclipse.swt.layout.GridData;
23 import org.eclipse.swt.layout.GridLayout;
24 import org.eclipse.swt.widgets.Button;
25 import org.eclipse.swt.widgets.Combo;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Group;
28 import org.eclipse.swt.widgets.Label;
29 import org.eclipse.swt.widgets.Text;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 
33 import com.proalpha.git.util.PaBranchName;
34 import com.proalpha.git.util.PaRepository;
35 import com.proalpha.pds.gitutils.Activator;
36 
47 public class SelectBranchesPage extends WizardPage {
48 
49  private final Logger logger = LoggerFactory.getLogger(SelectBranchesPage.class);
50 
51  private static final String BRANCH_TYPE_STORY = "Story";
52  private static final String BRANCH_TYPE_SUBTASK = "Sub-task";
53  private static final String BRANCH_TYPE_EPIC = "Epic";
54  private static final String BRANCH_TYPE_SPRINT = "Sprint";
55 
56  private FeatureBranchSettings settings;
57  private Text txtSrcBranch;
58  private Text txtTargetBranch;
59  private Button btnUpdateSrcBranch;
60  private Button btnUpdateTargetBranch;
61  private Button btnEditSrcBranchname;
62  private Button btnEditTargetBranchname;
63  private Combo cmbTargetBranch;
64  private Label lblSrcBranchInfo;
65  private Label lblTargetBranchInfo;
66 
67  private TargetBranchStatus targetBranchStatus;
68 
69  private List<String> serverBranches;
70  private List<String> targetBranchSelection = new ArrayList<>();
71 
76  private enum TargetBranchStatus {
77  UNKNOWN, LOCAL, REMOTE, MISSING
78  }
79 
86  super(SelectBranchesPage.class.getName());
87 
88  this.settings = settings;
89 
90  setTitle("Branch Selection");
91  setMessage("Select the branches to checkout or rather create", INFORMATION);
92  URL url = FileLocator.find(Platform.getBundle(Activator.PLUGIN_ID), new Path("icons/pA-logox72.png"),
93  Collections.emptyMap());
94 
95  setImageDescriptor(ImageDescriptor.createFromURL(url));
96  }
97 
98  @Override
99  public void createControl(Composite parent) {
100 
101  Composite container = new Composite(parent, SWT.NULL);
102  setControl(container);
103  container.setLayout(new GridLayout(1, false));
104  container.setBackgroundMode(SWT.INHERIT_DEFAULT);
105 
106  GridData gdGrp = new GridData(SWT.CENTER, SWT.TOP, true, false, 1, 1);
107  gdGrp.heightHint = 303;
108  gdGrp.widthHint = 450;
109 
110  Group grp = new Group(container, SWT.NONE);
111  grp.setLayoutData(gdGrp);
112  grp.setText("Branch settings");
113  grp.setBackgroundMode(SWT.INHERIT_DEFAULT);
114 
115  setSourceBranchSettings(container, grp);
116  setTargetBranchSettings(container, grp);
117  }
118 
119  private void setTargetBranchSettings(Composite container, Group grp) {
120  Label lblTargetBranch = new Label(grp, SWT.WRAP);
121  lblTargetBranch.setText("Target");
122  lblTargetBranch.setBounds(10, 123, 60, 15);
123 
124  /* Target branch selector */
125  cmbTargetBranch = new Combo(grp, SWT.CHECK);
126  cmbTargetBranch.addSelectionListener(new SelectionListener() {
127  public void widgetSelected(SelectionEvent selectedItem) {
128  String targetBranchSelected = null;
129  if (selectedItem != null) {
130  targetBranchSelected = ((Combo) selectedItem.getSource()).getText();
131  if (targetBranchSelected != null) {
132  // Update the target branch according to selected entry
133  updateTargetBranchSelection(targetBranchSelected);
134  }
135  }
136  }
137 
138  @Override
139  public void widgetDefaultSelected(SelectionEvent e) {
140  // We don't need this
141  }
142  });
143  cmbTargetBranch.setBounds(76, 120, 100, 16);
144 
145  txtTargetBranch = new Text(grp, SWT.BORDER);
146  txtTargetBranch.setBounds(76, 145, 212, 21);
147  txtTargetBranch.setEnabled(false);
148  txtTargetBranch.setText((settings.getTargetBranch() == null) ? "" : settings.getTargetBranch());
149  txtTargetBranch.addModifyListener(modifyEvent -> {
150  if (txtTargetBranch.getText().length() != 0)
151  settings.setTargetBranch(txtTargetBranch.getText());
152  updateTargetBranchStatus();
153  updateDependentFields();
154 
155  setPageComplete(targetBranchStatus == TargetBranchStatus.LOCAL
156  || targetBranchStatus == TargetBranchStatus.REMOTE || isSourceBranchValid());
157  });
158 
159  btnEditTargetBranchname = new Button(grp, SWT.CHECK);
160  btnEditTargetBranchname.addSelectionListener(new SelectionAdapter() {
161  @Override
162  public void widgetSelected(SelectionEvent e) {
163  txtTargetBranch.setEnabled(btnEditTargetBranchname.getSelection());
164  if (!btnEditTargetBranchname.getSelection()) {
165  settings.deriveDefaultTargetBranch();
166  txtTargetBranch.setText(settings.getTargetBranch());
167  }
168  }
169  });
170  btnEditTargetBranchname.setBounds(314, 123, 60, 16);
171  btnEditTargetBranchname.setText("modify");
172 
173  btnUpdateTargetBranch = new Button(grp, SWT.CHECK);
174  btnUpdateTargetBranch.addSelectionListener(new SelectionAdapter() {
175  @Override
176  public void widgetSelected(SelectionEvent e) {
177  settings.setUpdateTargetBranch(btnUpdateTargetBranch.getSelection());
178 
179  updateDependentFields();
180  }
181  });
182  btnUpdateTargetBranch.setBounds(384, 123, 60, 16);
183  btnUpdateTargetBranch.setText("update");
184 
185  lblTargetBranchInfo = new Label(grp, SWT.WRAP);
186  lblTargetBranchInfo.setBounds(76, 170, 330, 60);
187  lblTargetBranchInfo.setForeground(container.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
188  }
189 
190  private void setSourceBranchSettings(Composite container, Group grp) {
191  Label lblSrcBranch = new Label(grp, SWT.WRAP);
192  lblSrcBranch.setText("Source");
193  lblSrcBranch.setBounds(10, 22, 60, 15);
194 
195  txtSrcBranch = new Text(grp, SWT.BORDER);
196  txtSrcBranch.setBounds(76, 19, 212, 21);
197  txtSrcBranch.setText((settings.getSourceBranch() == null) ? "" : settings.getSourceBranch());
198  txtSrcBranch.setEnabled(false);
199  txtSrcBranch.addModifyListener(modifyEvent -> {
200  if (txtSrcBranch.getText().length() != 0)
201  settings.setSourceBranch(txtSrcBranch.getText());
202  updateDependentFields();
203  setPageComplete(targetBranchStatus == TargetBranchStatus.LOCAL
204  || targetBranchStatus == TargetBranchStatus.REMOTE || isSourceBranchValid());
205  });
206 
207  btnEditSrcBranchname = new Button(grp, SWT.CHECK);
208  btnEditSrcBranchname.addSelectionListener(new SelectionAdapter() {
209  @Override
210  public void widgetSelected(SelectionEvent e) {
211  txtSrcBranch.setEnabled(btnEditSrcBranchname.getSelection());
212  if (!btnEditSrcBranchname.getSelection()) {
213  settings.deriveDefaultSourceBranch();
214  txtSrcBranch.setText(settings.getSourceBranch());
215  }
216  }
217  });
218  btnEditSrcBranchname.setBounds(314, 22, 60, 16);
219  btnEditSrcBranchname.setText("modify");
220 
221  btnUpdateSrcBranch = new Button(grp, SWT.CHECK);
222  btnUpdateSrcBranch.addSelectionListener(new SelectionAdapter() {
223  @Override
224  public void widgetSelected(SelectionEvent e) {
225  settings.setUpdateSourceBranch(btnUpdateSrcBranch.getSelection());
226  updateDependentFields();
227  }
228  });
229  btnUpdateSrcBranch.setBounds(384, 22, 60, 16);
230  btnUpdateSrcBranch.setText("update");
231 
232  lblSrcBranchInfo = new Label(grp, SWT.WRAP);
233  lblSrcBranchInfo.setBounds(76, 49, 330, 60);
234  lblSrcBranchInfo.setForeground(container.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
235  }
236 
237  @Override
238  public void setVisible(boolean visible) {
239  super.setVisible(visible);
240 
241  if (visible) {
242  initialize();
243  }
244  }
245 
252  private void initialize() {
253  serverBranches = getBranchesFromServer();
254  updateTargetBranchStatus();
255 
256  txtSrcBranch.setText((settings.getSourceBranch() == null) ? "" : settings.getSourceBranch());
257 
258  btnUpdateSrcBranch.setSelection(settings.isUpdateSourceBranch());
259  btnUpdateTargetBranch.setSelection(settings.isUpdateTargetBranch());
260 
261  targetBranchSelection.clear();
262 
263  if (!settings.getTask().getTaskKind().isEmpty() && settings.getTask().getTaskKind().equals(BRANCH_TYPE_SUBTASK))
264  targetBranchSelection.add(BRANCH_TYPE_SUBTASK);
265  targetBranchSelection.add(BRANCH_TYPE_STORY);
266  if (!settings.getTaskEpic().isEmpty())
267  targetBranchSelection.add(BRANCH_TYPE_EPIC);
268  if (!settings.getTaskSprint().isEmpty())
269  targetBranchSelection.add(BRANCH_TYPE_SPRINT);
270  cmbTargetBranch.setItems(targetBranchSelection.stream().toArray(String[]::new));
271  cmbTargetBranch.setText(BRANCH_TYPE_STORY);
272  updateTargetBranchSelection(BRANCH_TYPE_STORY);
273  }
274 
278  private void updateDependentFields() {
279  StringBuilder sb = new StringBuilder();
280 
281  for (String s : getSourceBranchInfo()) {
282  sb.append(s);
283  sb.append("\n");
284  }
285 
286  lblSrcBranchInfo.setText(sb.toString());
287 
288  sb = new StringBuilder();
289  for (String s : getTargetBranchInfo()) {
290  sb.append(s);
291  sb.append("\n");
292  }
293 
294  lblTargetBranchInfo.setText(sb.toString());
295 
296  }
297 
303  private boolean isSourceBranchValid() {
304  if (this.settings == null)
305  return false;
306 
307  return PaRepository.getLocalBranches(this.settings.getRepository()).contains(this.settings.getSourceBranch());
308  }
309 
314  private void updateTargetBranchSelection(String selectedBranchValue) {
315 
316  switch (selectedBranchValue) {
317 
318  case BRANCH_TYPE_SUBTASK:
319  settings.deriveDefaultTargetBranch();
320  break;
321 
322  case BRANCH_TYPE_STORY:
323  if (settings.getTask().getTaskKind().equals(BRANCH_TYPE_SUBTASK))
324  settings.deriveTargetBranch(settings.getTaskParent());
325  else
326  settings.deriveDefaultTargetBranch();
327  break;
328 
329  case BRANCH_TYPE_EPIC:
330  settings.deriveTargetBranch(settings.getTaskEpic());
331  break;
332 
333  case BRANCH_TYPE_SPRINT:
334  settings.deriveTargetBranch(settings.getTaskSprint());
335  break;
336 
337  default:
338  txtTargetBranch.setText(settings.getTargetBranch());
339  }
340 
341  txtTargetBranch.setText(settings.getTargetBranch());
342 
343  updateTargetBranchStatus();
344  updateDependentFields();
345 
346  setPageComplete(targetBranchStatus == TargetBranchStatus.LOCAL
347  || targetBranchStatus == TargetBranchStatus.REMOTE || isSourceBranchValid());
348  }
349 
354  private void updateTargetBranchStatus() {
355  if (this.settings.getRepository() == null)
356  this.targetBranchStatus = TargetBranchStatus.UNKNOWN;
357 
358  // Target branch exists as local branch
359  if (PaRepository.isValidRef(this.settings.getRepository(), settings.getTargetBranch()) && PaRepository
360  .getLocalBranches(this.settings.getRepository()).contains(this.settings.getTargetBranch())) {
361  this.targetBranchStatus = TargetBranchStatus.LOCAL;
362  } else {
363  // Target branch exists as remote-tracking branch or branch on the server
364  if (PaRepository.getRemoteBranches(this.settings.getRepository()).contains(this.settings.getTargetBranch())
365  || this.serverBranches.contains(this.settings.getTargetBranch()))
366  this.targetBranchStatus = TargetBranchStatus.REMOTE;
367 
368  else {
369  this.targetBranchStatus = TargetBranchStatus.MISSING;
370  }
371  }
372  }
373 
380  private List<String> getSourceBranchInfo() {
381  List<String> infos = new ArrayList<>();
382 
383  if (!isSourceBranchValid()) {
384  infos.add("The source branch does not exist!");
385  }
386 
387  // Target branch exists as local branch
388  if (this.targetBranchStatus == TargetBranchStatus.LOCAL
389  || this.targetBranchStatus == TargetBranchStatus.REMOTE) {
390  infos.add("The source branch will not be used, because the target branch exists.");
391 
392  }
393  // Target branch must be created
394  else if (this.targetBranchStatus == TargetBranchStatus.MISSING) {
395  infos.add("The source branch will be branched off.");
396 
397  if (!settings.isUpdateSourceBranch()) {
398  infos.add(
399  "Warning: Your new target branch is probably not based on the newest version of the source branch.");
400  }
401  }
402 
403  return infos;
404  }
405 
412  private List<String> getTargetBranchInfo() {
413  List<String> infos = new ArrayList<>();
414 
415  if (this.targetBranchStatus == TargetBranchStatus.UNKNOWN)
416  return infos;
417 
418  // Target branch exists as local branch
419  if (this.targetBranchStatus == TargetBranchStatus.LOCAL) {
420  infos.add("The target branch already exists in the local repository and will be checked out.");
421 
422  if (!settings.isUpdateTargetBranch()) {
423  infos.add("Warning: The target branch will not be updated. It is probably outdated.");
424  }
425  } else {
426  // Target branch exists as remote-tracking branch or branch on the server
427  if (this.targetBranchStatus == TargetBranchStatus.REMOTE)
428  infos.add("The target branch already exists on server and will be fetched/checked out.");
429 
430  else {
431  infos.add("The target branch does not exist and will be created.");
432  }
433  }
434 
435  return infos;
436  }
437 
451  private List<String> getBranchesFromServer() {
452 
453  List<String> branches = new ArrayList<>();
454  try {
455 
456  String remote;
457  Set<String> remotes = this.settings.getRepository().getRemoteNames();
458  if (remotes.iterator().hasNext()) {
459  remote = remotes.iterator().next();
460  String url = this.settings.getRepository().getConfig().getString("remote", remote, "url");
461  Collection<Ref> refs = Git.lsRemoteRepository().setRemote(url).setHeads(true).call();
462 
463  for (Ref ref : refs) {
464  branches.add(PaBranchName.getShortBranchName(ref.getName(), this.settings.getRepository()));
465  }
466  }
467  } catch (GitAPIException e) {
468  logger.error("InvalidRemoteException occured", e);
469  }
470  return branches;
471  }
472 
473 }