1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.atlassian.theplugin.idea.crucible;
17
18 import com.atlassian.theplugin.cfg.CfgUtil;
19 import com.atlassian.theplugin.commons.cfg.CrucibleServerCfg;
20 import com.atlassian.theplugin.commons.cfg.ProjectConfiguration;
21 import com.atlassian.theplugin.commons.crucible.CrucibleServerFacade;
22 import com.atlassian.theplugin.commons.crucible.ValueNotYetInitialized;
23 import com.atlassian.theplugin.commons.crucible.api.model.*;
24 import com.atlassian.theplugin.commons.exception.ServerPasswordNotProvidedException;
25 import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
26 import com.atlassian.theplugin.commons.remoteapi.ServerData;
27 import com.atlassian.theplugin.commons.util.MiscUtil;
28 import com.atlassian.theplugin.crucible.model.UpdateReason;
29 import com.atlassian.theplugin.idea.IdeaHelper;
30 import com.atlassian.theplugin.idea.config.ProjectCfgManagerImpl;
31 import com.atlassian.theplugin.idea.crucible.comboitems.RepositoryComboBoxItem;
32 import com.atlassian.theplugin.idea.ui.DialogWithDetails;
33 import com.atlassian.theplugin.util.PluginUtil;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.application.ModalityState;
36 import com.intellij.openapi.progress.ProgressIndicator;
37 import com.intellij.openapi.progress.ProgressManager;
38 import com.intellij.openapi.progress.Task;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.ui.DialogWrapper;
41 import com.intellij.openapi.ui.Messages;
42 import com.intellij.ui.ListSpeedSearch;
43 import com.jgoodies.forms.layout.CellConstraints;
44 import com.jgoodies.forms.layout.FormLayout;
45 import org.apache.commons.lang.StringUtils;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48
49 import static javax.swing.Action.NAME;
50 import javax.swing.*;
51 import javax.swing.event.DocumentEvent;
52 import javax.swing.event.DocumentListener;
53 import java.awt.*;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.MouseAdapter;
57 import java.awt.event.MouseEvent;
58 import java.util.*;
59 import java.util.List;
60
61
62 public abstract class CrucibleReviewCreateForm extends DialogWrapper {
63 private JPanel rootComponent;
64 private JTextField titleText;
65 private JComboBox crucibleServersComboBox;
66 private JTextArea statementArea;
67 private JComboBox repoComboBox;
68 private JComboBox projectsComboBox;
69 private JComboBox authorComboBox;
70 private JComboBox moderatorComboBox;
71 private JList reviewersList;
72 private JCheckBox allowCheckBox;
73 private JCheckBox leaveAsDraftCheckBox;
74 private JPanel customComponentPanel;
75 private JLabel repositoryLabel;
76 private JLabel selectedReviewers;
77 private DefaultListModel model;
78 private UserListCellRenderer cellRenderer = new UserListCellRenderer();
79
80 protected Project project;
81 protected CrucibleServerFacade crucibleServerFacade;
82 private final ProjectCfgManagerImpl projectCfgManager;
83 private int reviewCreationTimeout = -1;
84 private static final int MILLISECONDS_IN_MINUTE = 1000 * 60;
85
86 protected void setCustomComponent(JComponent component) {
87 customComponentPanel.removeAll();
88 if (component != null) {
89 customComponentPanel.add(component);
90 customComponentPanel.validate();
91 }
92 }
93
94 public CrucibleReviewCreateForm(Project project, CrucibleServerFacade crucibleServerFacade, String commitMessage,
95 @NotNull final ProjectCfgManagerImpl projectCfgManager, @NotNull String dialogTitle) {
96 super(false);
97 this.project = project;
98 this.crucibleServerFacade = crucibleServerFacade;
99 this.projectCfgManager = projectCfgManager;
100 setTitle(dialogTitle);
101
102 $$$setupUI$$$();
103 init();
104
105 if (!shouldShowRepo()) {
106 repositoryLabel.setVisible(false);
107 repoComboBox.setVisible(false);
108 }
109
110 customComponentPanel.setLayout(new BorderLayout());
111 titleText.setText(commitMessage);
112 getOKAction().putValue(NAME, "Create review...");
113 crucibleServersComboBox.addActionListener(new ActionListener() {
114 public void actionPerformed(ActionEvent e) {
115 if (crucibleServersComboBox.getItemCount() > 0 && crucibleServersComboBox.getSelectedItem() != null &&
116 crucibleServersComboBox.getSelectedItem() instanceof ServerComboBoxItem) {
117 final ServerComboBoxItem boxItem = (ServerComboBoxItem) crucibleServersComboBox.getSelectedItem();
118 fillServerRelatedCombos(boxItem.getServer());
119 }
120 }
121 });
122
123 reviewersList.addMouseListener(new MouseAdapter() {
124 @Override
125 public void mousePressed(MouseEvent e) {
126 int index = reviewersList.locationToIndex(e.getPoint());
127 setCheckboxState(index);
128 refreshUserModel();
129 reviewersList.setSelectedIndex(index);
130 }
131 });
132
133 new ListSpeedSearch(reviewersList) {
134 @Override
135 protected boolean compare(final String s, final String s1) {
136 return s != null && s1 != null ? s.toUpperCase().contains(s1.toUpperCase()) : super.compare(s, s1);
137 }
138 };
139
140
141
142
143
144
145
146
147
148
149
150
151
152 moderatorComboBox.addActionListener(new ActionListener() {
153
154 public void actionPerformed(final ActionEvent event) {
155
156 if (moderatorComboBox.getSelectedItem() instanceof UserComboBoxItem) {
157 refreshUserModel();
158 }
159 }
160 });
161
162 ActionListener enableOkActionListener = new ActionListener() {
163
164 public void actionPerformed(final ActionEvent event) {
165 getOKAction().setEnabled(isValidForm());
166 }
167 };
168
169 authorComboBox.addActionListener(enableOkActionListener);
170 repoComboBox.addActionListener(enableOkActionListener);
171 projectsComboBox.addActionListener(enableOkActionListener);
172 crucibleServersComboBox.addActionListener(enableOkActionListener);
173 titleText.getDocument().addDocumentListener(new DocumentListener() {
174 public void insertUpdate(final DocumentEvent e) {
175 getOKAction().setEnabled(isValidForm());
176 }
177
178 public void removeUpdate(final DocumentEvent e) {
179 getOKAction().setEnabled(isValidForm());
180 }
181
182 public void changedUpdate(final DocumentEvent e) {
183 getOKAction().setEnabled(isValidForm());
184 }
185 });
186
187 fillInCrucibleServers();
188 }
189
190 private void refreshUserModel() {
191 final Object selectedItem = moderatorComboBox.getSelectedItem();
192 if (selectedItem instanceof UserComboBoxItem) {
193 final UserComboBoxItem userComboBoxItem = (UserComboBoxItem) selectedItem;
194 User moderatorUser = userComboBoxItem.getUser();
195 final ArrayList<User> disabledUsers = new ArrayList<User>();
196 disabledUsers.add(moderatorUser);
197 cellRenderer.setDisabledUsers(disabledUsers);
198 for (int i = 0; i < model.size(); i++) {
199 UserListItem reviewer = (UserListItem) model.get(i);
200 if (reviewer.getUser().equals(moderatorUser)) {
201 reviewer.setSelected(false);
202 }
203 }
204 reviewersList.setModel(model);
205
206 Collection<UserListItem> displayedSelectedUsers = new ArrayList<UserListItem>();
207 Collection<UserListItem> allSelectedUsers = new ArrayList<UserListItem>();
208 for (int i = 0; i < reviewersList.getModel().getSize(); ++i) {
209 UserListItem user = (UserListItem) reviewersList.getModel().getElementAt(i);
210 if (user.isSelected()) {
211 allSelectedUsers.add(user);
212 displayedSelectedUsers.add(user);
213
214 String displayStr = prepareSelectedReviewersString(displayedSelectedUsers, allSelectedUsers);
215 int displayStrWidth = selectedReviewers.getFontMetrics(selectedReviewers.getFont()).stringWidth(displayStr);
216
217 if (displayStrWidth > reviewersList.getWidth()) {
218 displayedSelectedUsers.remove(user);
219 }
220 }
221 }
222
223 if (displayedSelectedUsers.size() == 0) {
224 this.selectedReviewers.setText("None");
225 this.selectedReviewers.setToolTipText(null);
226 } else {
227 String labelText = prepareSelectedReviewersString(displayedSelectedUsers, allSelectedUsers);
228 if (displayedSelectedUsers.size() < allSelectedUsers.size()) {
229 labelText += " ...";
230 }
231 this.selectedReviewers.setText(labelText);
232 this.selectedReviewers.setToolTipText(prepareSelectedReviewersTooltip(allSelectedUsers));
233 }
234
235 reviewersList.revalidate();
236 reviewersList.repaint();
237 }
238 getOKAction().setEnabled(isValidForm());
239 }
240
241 private String prepareSelectedReviewersTooltip(final Collection<UserListItem> selectedUsersTooltip) {
242 StringBuilder ret = new StringBuilder("<html>");
243
244 for (UserListItem user : selectedUsersTooltip) {
245 ret.append(user.getUser().getDisplayName());
246 ret.append("<br>");
247 }
248
249 ret.append("</html>");
250
251 return ret.toString();
252 }
253
254 private String prepareSelectedReviewersString(final Collection<UserListItem> selectedUsersLabel,
255 final Collection<UserListItem> allSelectedUsers) {
256 return "(" + allSelectedUsers.size() + " of " + reviewersList.getModel().getSize() + ") "
257 + StringUtils.join(selectedUsersLabel, ", ");
258 }
259
260 private void setCheckboxState(int index) {
261 if (index != -1) {
262 UserListItem pi = (UserListItem) reviewersList.getModel().getElementAt(index);
263 pi.setSelected(!pi.isSelected());
264
265 repaint();
266 }
267 }
268
269
270
271 @Override
272 public JComponent getPreferredFocusedComponent() {
273 return titleText;
274 }
275
276
277
278
279
280
281
282
283 private void $$$setupUI$$$() {
284 createUIComponents();
285 rootComponent = new JPanel();
286 rootComponent.setLayout(new FormLayout("fill:d:grow",
287 "center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,center:max(d;4px):noGrow,center:p:grow,top:3dlu:noGrow,fill:d:noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow"));
288 rootComponent.setMinimumSize(new Dimension(800, 505));
289 final JLabel label1 = new JLabel();
290 label1.setText("Title:");
291 CellConstraints cc = new CellConstraints();
292 rootComponent.add(label1, cc.xy(1, 1));
293 titleText = new JTextField();
294 rootComponent.add(titleText, cc.xy(1, 3, CellConstraints.FILL, CellConstraints.DEFAULT));
295 final JPanel panel1 = new JPanel();
296 panel1.setLayout(new FormLayout(
297 "fill:d:noGrow,left:4dlu:noGrow,fill:300px:grow,left:4dlu:noGrow,fill:max(d;4px):noGrow,left:4dlu:noGrow,fill:max(p;4px):grow",
298 "center:d:noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow"));
299 rootComponent.add(panel1, cc.xy(1, 5));
300 final JLabel label2 = new JLabel();
301 label2.setText("Server:");
302 panel1.add(label2, cc.xy(1, 1, CellConstraints.DEFAULT, CellConstraints.CENTER));
303 crucibleServersComboBox = new JComboBox();
304 panel1.add(crucibleServersComboBox, cc.xy(3, 1));
305 final JLabel label3 = new JLabel();
306 label3.setInheritsPopupMenu(false);
307 label3.setText("Project:");
308 panel1.add(label3, cc.xy(1, 3, CellConstraints.DEFAULT, CellConstraints.CENTER));
309 projectsComboBox = new JComboBox();
310 panel1.add(projectsComboBox, cc.xy(3, 3));
311 repositoryLabel = new JLabel();
312 repositoryLabel.setText("Repository:");
313 panel1.add(repositoryLabel, cc.xy(1, 5, CellConstraints.DEFAULT, CellConstraints.CENTER));
314 final JLabel label4 = new JLabel();
315 label4.setText("Moderator:");
316 panel1.add(label4, cc.xy(1, 7, CellConstraints.DEFAULT, CellConstraints.CENTER));
317 final JLabel label5 = new JLabel();
318 label5.setText("Author:");
319 panel1.add(label5, cc.xy(1, 9, CellConstraints.DEFAULT, CellConstraints.CENTER));
320 repoComboBox = new JComboBox();
321 panel1.add(repoComboBox, cc.xy(3, 5));
322 moderatorComboBox = new JComboBox();
323 panel1.add(moderatorComboBox, cc.xy(3, 7));
324 authorComboBox = new JComboBox();
325 panel1.add(authorComboBox, cc.xy(3, 9));
326 final JPanel panel2 = new JPanel();
327 panel2.setLayout(new BorderLayout(0, 0));
328 panel1.add(panel2, cc.xywh(7, 1, 1, 7, CellConstraints.DEFAULT, CellConstraints.FILL));
329 final JScrollPane scrollPane1 = new JScrollPane();
330 panel2.add(scrollPane1, BorderLayout.CENTER);
331 scrollPane1.setViewportView(reviewersList);
332 final JLabel label6 = new JLabel();
333 label6.setText("Reviewers: ");
334 panel1.add(label6, cc.xy(5, 1, CellConstraints.RIGHT, CellConstraints.TOP));
335 allowCheckBox = new JCheckBox();
336 allowCheckBox.setEnabled(true);
337 allowCheckBox.setText("Allow anyone to join");
338 panel1.add(allowCheckBox, cc.xy(7, 11));
339 final JLabel label7 = new JLabel();
340 label7.setText("Selected: ");
341 panel1.add(label7, cc.xy(5, 9, CellConstraints.RIGHT, CellConstraints.DEFAULT));
342 selectedReviewers = new JLabel();
343 selectedReviewers.setHorizontalTextPosition(2);
344 selectedReviewers.setText("None");
345 panel1.add(selectedReviewers, cc.xy(7, 9, CellConstraints.LEFT, CellConstraints.DEFAULT));
346 final JLabel label8 = new JLabel();
347 label8.setText("Statement of Objectives:");
348 rootComponent.add(label8, cc.xy(1, 7));
349 final JScrollPane scrollPane2 = new JScrollPane();
350 rootComponent.add(scrollPane2, cc.xy(1, 9, CellConstraints.FILL, CellConstraints.FILL));
351 statementArea = new JTextArea();
352 statementArea.setLineWrap(true);
353 statementArea.setRows(5);
354 scrollPane2.setViewportView(statementArea);
355 customComponentPanel = new JPanel();
356 customComponentPanel.setLayout(new BorderLayout(0, 0));
357 rootComponent.add(customComponentPanel, cc.xy(1, 11, CellConstraints.DEFAULT, CellConstraints.FILL));
358 leaveAsDraftCheckBox = new JCheckBox();
359 leaveAsDraftCheckBox.setText("Save review as Draft");
360 rootComponent.add(leaveAsDraftCheckBox, cc.xy(1, 13));
361 label1.setLabelFor(titleText);
362 label2.setLabelFor(crucibleServersComboBox);
363 label5.setLabelFor(scrollPane1);
364 label8.setLabelFor(statementArea);
365 }
366
367
368
369
370 public JComponent $$$getRootComponent$$$() {
371 return rootComponent;
372 }
373
374
375
376 private static final class ServerComboBoxItem {
377 private final ServerData server;
378
379 private ServerComboBoxItem(ServerData server) {
380 this.server = server;
381 }
382
383 @Override
384 public String toString() {
385 return server.getName();
386 }
387
388 public ServerData getServer() {
389 return server;
390 }
391
392 @Override
393 public boolean equals(final Object o) {
394 if (this == o) {
395 return true;
396 }
397 if (!(o instanceof ServerComboBoxItem)) {
398 return false;
399 }
400
401 final ServerComboBoxItem boxItem = (ServerComboBoxItem) o;
402
403
404 if (!server.equals(boxItem.server)) {
405 return false;
406 }
407
408 return true;
409 }
410
411 @Override
412 public int hashCode() {
413 return server.hashCode();
414 }
415 }
416
417 private static final class ProjectComboBoxItem {
418 private final CrucibleProject wrappedProject;
419
420 private ProjectComboBoxItem(@NotNull final CrucibleProject project) {
421 this.wrappedProject = project;
422 }
423
424 @Override
425 public String toString() {
426 return wrappedProject.getName();
427 }
428
429 public CrucibleProject getWrappedProject() {
430 return wrappedProject;
431 }
432
433 @Override
434 public boolean equals(final Object o) {
435 if (this == o) {
436 return true;
437 }
438 if (o == null || getClass() != o.getClass()) {
439 return false;
440 }
441
442 final ProjectComboBoxItem that = (ProjectComboBoxItem) o;
443
444
445 if (!wrappedProject.equals(that.wrappedProject)) {
446 return false;
447 }
448
449 return true;
450 }
451
452 @Override
453 public int hashCode() {
454 return wrappedProject.hashCode();
455 }
456 }
457
458 private static final class UserComboBoxItem {
459 private final User user;
460
461 private UserComboBoxItem(User user) {
462 this.user = user;
463 }
464
465 @Override
466 public String toString() {
467 return user.getDisplayName();
468 }
469
470 public User getUser() {
471 return user;
472 }
473 }
474
475
476 private void fillInCrucibleServers() {
477 final Collection<CrucibleServerCfg> enabledServers = projectCfgManager.getCfgManager().
478 getAllEnabledCrucibleServers(CfgUtil.getProjectId(project));
479 if (enabledServers.isEmpty()) {
480 crucibleServersComboBox.setEnabled(false);
481 crucibleServersComboBox.addItem("Enable a Crucible server first!");
482 getOKAction().setEnabled(false);
483 } else {
484 for (CrucibleServerCfg server : enabledServers) {
485 crucibleServersComboBox.addItem(new ServerComboBoxItem(projectCfgManager.getServerData(server)));
486 }
487 final ServerData defCrucServer = projectCfgManager.getDefaultCrucibleServer();
488 if (defCrucServer != null) {
489 crucibleServersComboBox.setSelectedItem(new ServerComboBoxItem(defCrucServer));
490 }
491 }
492 }
493
494 private void fillServerRelatedCombos(final ServerData server) {
495 projectsComboBox.removeAllItems();
496 if (shouldShowRepo()) {
497 repoComboBox.removeAllItems();
498 }
499 authorComboBox.removeAllItems();
500 moderatorComboBox.removeAllItems();
501 model.removeAllElements();
502 getOKAction().setEnabled(false);
503
504 final CrucibleServerData data = crucibleData.get(server.getServerId());
505
506 if (data == null) {
507 new Thread(new Runnable() {
508 public void run() {
509 List<CrucibleProject> projects = new ArrayList<CrucibleProject>();
510 List<Repository> repositories = new ArrayList<Repository>();
511 List<User> users = new ArrayList<User>();
512
513 try {
514 projects = crucibleServerFacade.getProjects(server);
515 if (shouldShowRepo()) {
516 repositories = crucibleServerFacade.getRepositories(server);
517 }
518 users = crucibleServerFacade.getUsers(server);
519 } catch (final Exception e) {
520 if (CrucibleReviewCreateForm.this.getRootComponent().isShowing()) {
521 ApplicationManager.getApplication().invokeAndWait(new Runnable() {
522 public void run() {
523 DialogWithDetails.showExceptionDialog(project, "Cannot retrieve data from Crucible server",
524 e);
525 }
526 }, ModalityState.stateForComponent(CrucibleReviewCreateForm.this.getRootComponent()));
527 }
528 }
529 final CrucibleServerData crucibleServerData = new CrucibleServerData(repositories, projects, users);
530 crucibleData.put(server.getServerId(), crucibleServerData);
531 EventQueue.invokeLater(new Runnable() {
532 public void run() {
533 updateServerRelatedCombos(server, crucibleServerData);
534 }
535 });
536 }
537 }, "atlassian-idea-plugin crucible patch upload combos refresh").start();
538 } else {
539 updateServerRelatedCombos(server, data);
540
541 }
542 }
543
544
545 protected static class CrucibleServerData {
546 private final List<CrucibleProject> projects;
547
548 private final List<Repository> repositories;
549
550 private final List<User> users;
551
552 public CrucibleServerData(final List<Repository> repositories, final List<CrucibleProject> projects,
553 final List<User> users) {
554 this.repositories = repositories;
555 this.projects = projects;
556 this.users = users;
557 }
558
559 public List<CrucibleProject> getProjects() {
560 return projects;
561 }
562
563 public List<Repository> getRepositories() {
564 return repositories;
565 }
566
567 public List<User> getUsers() {
568 return users;
569 }
570 }
571
572 private Map<String, CrucibleServerData> crucibleData = MiscUtil.buildConcurrentHashMap(5);
573
574
575 private void updateServerRelatedCombos(final ServerData server, final CrucibleServerData crucibleServerData) {
576
577 final ServerComboBoxItem selectedItem = (ServerComboBoxItem) crucibleServersComboBox.getSelectedItem();
578 if (selectedItem == null || !selectedItem.getServer().equals(server)) {
579 return;
580 }
581
582
583
584 projectsComboBox.removeAllItems();
585 if (shouldShowRepo()) {
586 repoComboBox.removeAllItems();
587 }
588 authorComboBox.removeAllItems();
589 moderatorComboBox.removeAllItems();
590 model.removeAllElements();
591
592 ProjectConfiguration prjCfg = projectCfgManager.getCfgManager().getProjectConfiguration(CfgUtil.getProjectId(project));
593 if (crucibleServerData.getProjects().isEmpty()) {
594 projectsComboBox.setEnabled(false);
595 projectsComboBox.addItem("No projects");
596 getOKAction().setEnabled(false);
597 } else {
598 projectsComboBox.setEnabled(true);
599 for (CrucibleProject myProject : crucibleServerData.getProjects()) {
600 projectsComboBox.addItem(new ProjectComboBoxItem(myProject));
601 }
602
603
604 if (prjCfg != null) {
605 final String defaultProjectKey = prjCfg.getDefaultCrucibleProject();
606 if (defaultProjectKey != null) {
607 for (int i = 0; i < projectsComboBox.getItemCount(); ++i) {
608 if (projectsComboBox.getItemAt(i) instanceof ProjectComboBoxItem) {
609 if (((ProjectComboBoxItem) projectsComboBox.getItemAt(i)).getWrappedProject().getKey()
610 .equals(defaultProjectKey)) {
611 projectsComboBox.setSelectedIndex(i);
612 break;
613 }
614 }
615 }
616 }
617 }
618 }
619
620 if (shouldShowRepo()) {
621 repoComboBox.addItem("");
622 if (!crucibleServerData.getRepositories().isEmpty()) {
623 for (Repository repo : crucibleServerData.getRepositories()) {
624 repoComboBox.addItem(new RepositoryComboBoxItem(repo));
625 }
626
627
628
629 if (prjCfg != null) {
630 final String defaultRepo = prjCfg.getDefaultCrucibleRepo();
631 if (defaultRepo != null) {
632 for (int i = 0; i < repoComboBox.getItemCount(); ++i) {
633 if (repoComboBox.getItemAt(i) instanceof RepositoryComboBoxItem) {
634 if (((RepositoryComboBoxItem) repoComboBox.getItemAt(i)).getRepository().getName()
635 .equals(defaultRepo)) {
636 repoComboBox.setSelectedIndex(i);
637 break;
638 }
639 }
640 }
641 }
642 }
643 getOKAction().setEnabled(true);
644 }
645
646 if (shouldAutoSelectRepo(crucibleServerData)) {
647 repoComboBox.setSelectedIndex(repoComboBox.getItemCount() - 1);
648 }
649 }
650 authorComboBox.addItem("");
651 moderatorComboBox.addItem("");
652 if (!crucibleServerData.getUsers().isEmpty()) {
653 int indexToSelect = -1;
654 int index = 0;
655 for (User user : crucibleServerData.getUsers()) {
656 authorComboBox.addItem(new UserComboBoxItem(user));
657 moderatorComboBox.addItem(new UserComboBoxItem(user));
658 if (user.getUserName().equals(server.getUserName())) {
659 indexToSelect = index + 1;
660 }
661
662 model.addElement(new UserListItem(user, false));
663 index++;
664 }
665 if (indexToSelect != -1) {
666 authorComboBox.setSelectedIndex(indexToSelect);
667 moderatorComboBox.setSelectedIndex(indexToSelect);
668 }
669 }
670
671 getOKAction().setEnabled(isValidForm());
672
673 }
674
675 protected boolean shouldShowRepo() {
676 return true;
677 }
678
679
680 protected boolean shouldAutoSelectRepo(CrucibleServerData crucibleServerData) {
681 return false;
682 }
683
684 public JComponent getRootComponent() {
685 return rootComponent;
686 }
687
688 @Override
689 @Nullable
690 protected JComponent createCenterPanel() {
691 return getRootComponent();
692 }
693
694 protected class ReviewProvider extends ReviewBean {
695 private final ServerData server;
696
697 public ReviewProvider(ServerData server) {
698 super(server.getUrl());
699 this.server = server;
700 }
701
702 @NotNull
703 @Override
704 public User getAuthor() {
705 if (authorComboBox.getSelectedItem() instanceof UserComboBoxItem) {
706 return ((UserComboBoxItem) authorComboBox.getSelectedItem()).getUser();
707 } else {
708 return null;
709 }
710 }
711
712 @Override
713 public User getCreator() {
714 UserBean user = new UserBean();
715 user.setUserName(server.getUserName());
716 return user;
717 }
718
719 @Override
720 public String getDescription() {
721 return statementArea.getText();
722 }
723
724 @NotNull
725 @Override
726 public User getModerator() {
727 if (moderatorComboBox.getSelectedItem() instanceof UserComboBoxItem) {
728 return ((UserComboBoxItem) moderatorComboBox.getSelectedItem()).getUser();
729 } else {
730 return null;
731 }
732 }
733
734 @Override
735 public String getName() {
736 return titleText.getText();
737 }
738
739 @Nullable
740 @Override
741 public PermId getParentReview() {
742 return null;
743 }
744
745 @Nullable
746 @Override
747 public PermId getPermId() {
748 return null;
749 }
750
751 @NotNull
752 @Override
753 public String getProjectKey() {
754 return ((ProjectComboBoxItem) projectsComboBox.getSelectedItem()).getWrappedProject().getKey();
755 }
756
757 @Nullable
758 @Override
759 public String getRepoName() {
760 if (repoComboBox.getSelectedItem() instanceof RepositoryComboBoxItem) {
761 return ((RepositoryComboBoxItem) repoComboBox.getSelectedItem()).getRepository().getName();
762 } else {
763 return null;
764 }
765 }
766
767 @Nullable
768 @Override
769 public State getState() {
770 return null;
771 }
772
773 @Override
774 public boolean isAllowReviewerToJoin() {
775 return allowCheckBox.isSelected();
776 }
777 }
778
779
780 protected abstract Review createReview(ServerData server, ReviewProvider reviewProvider)
781 throws RemoteApiException,
782 ServerPasswordNotProvidedException;
783
784 @Override
785 protected void doOKAction() {
786 runCreateReviewTask(true);
787 super.doOKAction();
788 }
789
790 protected void setReviewCreationTimeout(int reviewCreationTimeout) {
791 this.reviewCreationTimeout = reviewCreationTimeout;
792 }
793
794 protected void runCreateReviewTask(final boolean runUntilSuccessful) {
795 final ServerComboBoxItem selectedItem = (ServerComboBoxItem) crucibleServersComboBox.getSelectedItem();
796 if (selectedItem != null) {
797 final Date startDate = new Date();
798
799 final ServerData server = selectedItem.getServer();
800
801 Task.Backgroundable changesTask = new Task.Backgroundable(project, "Creating review...", runUntilSuccessful) {
802
803 public boolean isCancelled = false;
804
805 @Override
806 public void run(@NotNull final ProgressIndicator indicator) {
807
808 boolean submissionSuccess = false;
809 do {
810 indicator.setText("Attempting to create review... ");
811 ModalityState modalityState = ModalityState
812 .stateForComponent(CrucibleReviewCreateForm.this.getRootComponent());
813
814 Review newlyCreated = null;
815 try {
816 final Review draftReview = createReview(server, new ReviewProvider(server));
817 if (draftReview == null) {
818 EventQueue.invokeLater(new Runnable() {
819 public void run() {
820 Messages.showErrorDialog(
821 project, "Review not created. Null returned.", PluginUtil.PRODUCT_NAME);
822 }
823 });
824 return;
825 }
826 submissionSuccess = true;
827
828 Set<String> users = new HashSet<String>();
829 for (int i = 0; i < model.getSize(); ++i) {
830 UserListItem item = (UserListItem) model.get(i);
831 if (item.isSelected()) {
832 users.add(item.getUser().getUserName());
833 }
834 }
835
836 if (!users.isEmpty()) {
837 crucibleServerFacade.addReviewers(server, draftReview.getPermId(), users);
838 }
839
840 if (!leaveAsDraftCheckBox.isSelected()) {
841 try {
842 Review newReview = crucibleServerFacade.getReview(server, draftReview.getPermId());
843 if (newReview.getModerator().getUserName().equals(server.getUserName())) {
844 if (newReview.getActions().contains(CrucibleAction.APPROVE)) {
845 newlyCreated = crucibleServerFacade.approveReview(server, draftReview.getPermId());
846 } else {
847 Messages.showErrorDialog(project,
848 newReview.getAuthor().getDisplayName() +
849 " is authorized to approve review.\n"
850 + "Leaving review in draft state.", "Permission denied");
851 }
852 } else {
853 if (newReview.getActions().contains(CrucibleAction.SUBMIT)) {
854 newlyCreated = crucibleServerFacade.submitReview(server, draftReview.getPermId());
855 } else {
856 Messages.showErrorDialog(project,
857 newReview.getAuthor().getDisplayName() + " is authorized submit review.\n"
858 + "Leaving review in draft state.", "Permission denied");
859 }
860 }
861 } catch (ValueNotYetInitialized valueNotYetInitialized) {
862 Messages.showErrorDialog(project,
863 "Unable to change review state. Leaving review in draft state.",
864 "Permission denied");
865 }
866 } else {
867 newlyCreated = draftReview;
868 }
869
870 final Review newRevewFinal = newlyCreated != null
871 ? crucibleServerFacade.getReview(server, newlyCreated.getPermId()) : null;
872
873 ApplicationManager.getApplication().invokeLater(new Runnable() {
874 public void run() {
875 final ReviewListToolWindowPanel panel = IdeaHelper.getReviewListToolWindowPanel(project);
876 if (panel != null && newRevewFinal != null) {
877 panel.refresh(UpdateReason.REFRESH);
878 panel.openReview(new ReviewAdapter(newRevewFinal, server), true);
879 }
880 }
881 }, modalityState);
882 } catch (final Throwable e) {
883 if (!runUntilSuccessful) {
884 ApplicationManager.getApplication().invokeAndWait(new Runnable() {
885 public void run() {
886 String message = "Error creating review: " + server.getUrl();
887 if (isUnknownChangeSetException(e)) {
888 message
889 += "\nSpecified change set could not be found on server. Check selected repository";
890 }
891 DialogWithDetails.showExceptionDialog(project, message, e);
892 }
893 }, modalityState);
894 } else {
895 if (isUnknownChangeSetException(e)) {
896 try {
897 Date now = new Date();
898 if (reviewCreationTimeout > 0
899 && now.getTime() - startDate.getTime() >
900 reviewCreationTimeout * MILLISECONDS_IN_MINUTE) {
901 SwingUtilities.invokeLater(new Runnable() {
902 public void run() {
903 Messages.showErrorDialog(project,
904 "Creation of the review on server\n"
905 + selectedItem.getServer().getName()
906 + " timed out after "
907 + reviewCreationTimeout + " minutes",
908 "Review Creation Timeout");
909 }
910 });
911 break;
912 }
913 indicator.setText("Waiting for Crucible to update to newest change set...");
914 for (int i = 0; i < 10; ++i) {
915 if (indicator.isCanceled()) {
916 isCancelled = true;
917 break;
918 }
919 Thread.sleep(1000);
920 }
921 } catch (InterruptedException e1) {
922
923 }
924 } else {
925 ApplicationManager.getApplication().invokeAndWait(new Runnable() {
926 public void run() {
927 DialogWithDetails.showExceptionDialog(project,
928 "Error creating review: " + server.getUrl(), e);
929 }
930 }, modalityState);
931 isCancelled = true;
932 }
933 }
934 }
935 } while (runUntilSuccessful && !submissionSuccess && !isCancelled && !indicator.isCanceled());
936 }
937
938 private boolean isUnknownChangeSetException(Throwable e) {
939 return e != null
940 && e.getMessage() != null
941 && e.getMessage().contains("Specified change set id does not exist");
942
943 }
944 };
945 ProgressManager.getInstance().run(changesTask);
946 }
947 }
948
949 protected boolean isValid(ReviewProvider reviewProvider) {
950 return true;
951 }
952
953 private boolean isValidForm() {
954 if (crucibleServersComboBox.getSelectedItem() instanceof ServerComboBoxItem && titleText.getText().length() > 0
955 && projectsComboBox.getSelectedItem() instanceof ProjectComboBoxItem
956 && authorComboBox.getSelectedItem() instanceof UserComboBoxItem
957 && moderatorComboBox.getSelectedItem() instanceof UserComboBoxItem) {
958 final ServerComboBoxItem selectedItem = (ServerComboBoxItem) crucibleServersComboBox.getSelectedItem();
959 return isValid(new ReviewProvider(selectedItem.getServer()));
960 } else {
961 return false;
962 }
963 }
964
965 private void createUIComponents() {
966 model = new DefaultListModel();
967 reviewersList = new JList(model);
968 reviewersList.setCellRenderer(cellRenderer);
969 reviewersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
970 }
971 }
972