1 package de.matthias_burbach.deputy.swing;
2
3 import java.awt.event.ActionEvent;
4 import java.awt.event.MouseAdapter;
5 import java.awt.event.MouseEvent;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.Comparator;
9 import java.util.Enumeration;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import javax.swing.AbstractAction;
14 import javax.swing.Action;
15 import javax.swing.JOptionPane;
16 import javax.swing.JPopupMenu;
17 import javax.swing.JTree;
18 import javax.swing.tree.TreeModel;
19 import javax.swing.tree.TreeNode;
20 import javax.swing.tree.TreePath;
21 import javax.swing.tree.TreeSelectionModel;
22
23 import de.matthias_burbach.deputy.core.Deputy;
24 import de.matthias_burbach.deputy.core.project.Project;
25 import de.matthias_burbach.deputy.core.project.ProjectHolderTreeNode;
26 import de.matthias_burbach.deputy.core.repository.RepositoryArtifact;
27 import de.matthias_burbach.deputy.core.rule.DeprecationRule;
28 import de.matthias_burbach.deputy.core.rule.EnforcementRule;
29 import de.matthias_burbach.deputy.core.rule.RemovalRule;
30 import de.matthias_burbach.deputy.core.rule.ReplacementRule;
31 import de.matthias_burbach.deputy.core.rule.RetentionRule;
32 import de.matthias_burbach.deputy.core.rule.Rule;
33 import de.matthias_burbach.deputy.core.rule.RuleSet;
34 import de.matthias_burbach.deputy.core.util.Log;
35
36 /***
37 * Is used as the project tree, the repositories tree, and the second
38 * repositories tree that pops up in a modal dialog to choose the replacement
39 * project for a replacement rule.
40 * <p/>
41 * Features various popup menus whose action items depend on the type of the
42 * selected node.
43 *
44 * @author Matthias Burbach
45 */
46 public class DeputyTree extends JTree {
47 /***
48 * Collapses the selected node deeply, i. e.
49 * all descendant nodes are collapsed as well.
50 * <p/>
51 * Is available on all types of nodes on both the project and the
52 * repositories tree (but not on the replacement chooser tree).
53 *
54 * @author Matthias Burbach
55 */
56 private class CollapseAllAction extends AbstractAction {
57 /***
58 * Constructs this action.
59 */
60 public CollapseAllAction() {
61 super("Collapse All");
62 }
63
64
65
66
67
68
69 /***
70 * {@inheritDoc}
71 */
72 public void actionPerformed(final ActionEvent e) {
73 TreePath treePath = getSelectionPath();
74 if (treePath != null) {
75 Enumeration descsEnum = getDescendantToggledPaths(treePath);
76 List descsList = enumToList(descsEnum);
77 sortListByPathCountDescending(descsList);
78 Iterator iter = descsList.iterator();
79 while (iter.hasNext()) {
80 TreePath descTreePath = (TreePath) iter.next();
81 collapsePath(descTreePath);
82 }
83 }
84 }
85
86 /***
87 * @param anEnum The enumeration to be converted to a list.
88 * @return The list holding the elements of <code>enum</code>.
89 */
90 private List enumToList(final Enumeration anEnum) {
91 List result = new ArrayList();
92 while (anEnum.hasMoreElements()) {
93 result.add(anEnum.nextElement());
94 }
95 return result;
96 }
97
98 /***
99 * @param treePaths The tree paths to be sorted.
100 */
101 private void sortListByPathCountDescending(final List treePaths) {
102 Comparator comparator = new Comparator() {
103 public int compare(final Object o1, final Object o2) {
104 TreePath tp1 = (TreePath) o1;
105 TreePath tp2 = (TreePath) o2;
106 return tp2.getPathCount() - tp1.getPathCount();
107 }
108 };
109 Collections.sort(treePaths, comparator);
110 }
111 }
112
113 /***
114 * Searches the selected project in the repositories tree.
115 * If it finds it, it will expand the repositories tree and select the node
116 * found.<br/>
117 * Else, a message box pops up, which notifies the user that the project
118 * couldn't be found in the repositories.
119 *
120 * @author Matthias Burbach
121 */
122 private class FindInRepositoriesAction extends AbstractAction {
123 /***
124 * Constructs this action.
125 */
126 public FindInRepositoriesAction() {
127 super("Find In Repositories");
128 }
129
130
131
132
133
134
135 /***
136 * {@inheritDoc}
137 */
138 public void actionPerformed(final ActionEvent e) {
139 TreeNode node = getSelectedNode();
140 if (node instanceof ProjectTreeNode) {
141 Project project = ((ProjectTreeNode) node).getProject();
142 deputyFrame.findInRepositories(
143 project.getGroupId(),
144 project.getArtifactId(),
145 project.getVersion());
146 }
147 }
148 }
149
150 /***
151 * Launches the default browser to open the project's documentation web
152 * site.
153 *
154 * @author Matthias Burbach
155 */
156 private class OpenSiteAction extends AbstractAction {
157 /***
158 * Constructs this action.
159 */
160 public OpenSiteAction() {
161 super("Open Site");
162 }
163
164
165
166
167
168
169 /***
170 * {@inheritDoc}
171 */
172 public void actionPerformed(final ActionEvent e) {
173 TreeNode node = getSelectedNode();
174 if (node instanceof ProjectHolderTreeNode) {
175 ProjectHolderTreeNode projectNode =
176 (ProjectHolderTreeNode) node;
177 deputyFrame.openProjectSite(
178 projectNode.getProject());
179 }
180 }
181 }
182
183 /***
184 * Changes the default rule of the dependency recursion to the configured
185 * value.
186 *
187 * @author Matthias Burbach
188 */
189 private class ChangeDefaultRuleAction extends AbstractAction {
190 /***
191 * The value to change the default rule to.
192 */
193 private String value = RuleSet.DEFAULT_RULE_LATEST_RELEASE;
194
195 /***
196 * Constructs this action.
197 *
198 * @param value The value to change the default rule to.
199 * @param text The menu item text of this action.
200 */
201 public ChangeDefaultRuleAction(final String value, final String text) {
202 super("Change default to " + text);
203 this.value = value;
204 }
205
206
207
208
209
210
211 /***
212 * {@inheritDoc}
213 */
214 public void actionPerformed(final ActionEvent e) {
215 deputy.getRootProject().getRuleSet().setDefaultRule(value);
216 }
217 }
218
219 /***
220 * Is the base class for adding a rule.
221 *
222 * @author Matthias Burbach
223 */
224 private abstract class AddRuleAction extends AbstractAction {
225 /***
226 * @param name The name of this action to be displayed on menu items
227 */
228 public AddRuleAction(final String name) {
229 super(name);
230 }
231
232 /***
233 * @return Whether adding a rule is possible. Is called by sub classes.
234 */
235 protected boolean mayAdd() {
236 boolean result = true;
237 if (deputy.getRootProject() == null) {
238 JOptionPane.showMessageDialog(
239 DeputyTree.this,
240 "You need to open a project "
241 + "before you can add a rule.",
242 "Info",
243 JOptionPane.INFORMATION_MESSAGE);
244 result = false;
245 }
246 return result;
247 }
248
249 /***
250 * @param rule The rule to add to the rule set.
251 */
252 protected void addRule(final Rule rule) {
253 boolean doAdd = false;
254 RuleSet ruleSet = deputy.getRootProject().getRuleSet();
255 if (ruleSet.hasSameRule(rule)) {
256 JOptionPane.showMessageDialog(
257 DeputyTree.this,
258 "The rule '" + rule + "' already exists. "
259 + "It cannot be added twice.",
260 "Info",
261 JOptionPane.INFORMATION_MESSAGE);
262 doAdd = false;
263 } else if (ruleSet.hasRuleForKey(rule)) {
264 Rule oldRule = ruleSet.getRuleForKey(rule);
265 int selectedOption = JOptionPane.showOptionDialog(
266 DeputyTree.this,
267 "Remove rule "
268 + "'" + oldRule + "'\n"
269 + "in favour of rule "
270 + "'" + rule + "'"
271 + "?",
272 "Replace Existing Rule",
273 JOptionPane.OK_CANCEL_OPTION,
274 JOptionPane.QUESTION_MESSAGE,
275 null,
276 new Object[] {"OK", "Cancel"},
277 null);
278 if (selectedOption == 0) {
279 doAdd = true;
280 } else {
281 doAdd = false;
282 }
283 } else {
284 doAdd = true;
285 }
286 if (doAdd) {
287 ruleSet.add(rule);
288 }
289 }
290 }
291
292 /***
293 * Adds an enforcement rule for the selected projects.
294 *
295 * @author Matthias Burbach
296 */
297 private class AddEnforcementRuleAction extends AddRuleAction {
298 /***
299 * Constructs this action.
300 */
301 public AddEnforcementRuleAction() {
302 super("Add Enforcement Rule");
303 }
304
305
306
307
308
309
310 /***
311 * {@inheritDoc}
312 */
313 public void actionPerformed(final ActionEvent e) {
314 if (mayAdd()) {
315 Iterator iter =
316 getSelectedProjectHolderTreeNodes().iterator();
317 while (iter.hasNext()) {
318 ProjectHolderTreeNode projectNode =
319 (ProjectHolderTreeNode) iter.next();
320 Project project = projectNode.getProject();
321 EnforcementRule rule = new EnforcementRule();
322 rule.setGroupId(project.getGroupId());
323 rule.setArtifactId(project.getArtifactId());
324 rule.setVersion(project.getVersion());
325 addRule(rule);
326 }
327 }
328 }
329 }
330
331 /***
332 * Adds an enforcement rule for the selected projects.
333 *
334 * @author Matthias Burbach
335 */
336 private class AddSnapshotEnforcementRuleAction extends AddRuleAction {
337 /***
338 * Constructs this action.
339 */
340 public AddSnapshotEnforcementRuleAction() {
341 super("Add Enforcement Rule (SNAPSHOT)");
342 }
343
344
345
346
347
348
349 /***
350 * {@inheritDoc}
351 */
352 public void actionPerformed(final ActionEvent e) {
353 if (mayAdd()) {
354 Iterator iter =
355 getSelectedProjectHolderTreeNodes().iterator();
356 boolean warn = false;
357 while (iter.hasNext()) {
358 ProjectHolderTreeNode projectNode =
359 (ProjectHolderTreeNode) iter.next();
360 Project project = projectNode.getProject();
361 if (deputyFrame.existsInRepositories(
362 project.getGroupId(),
363 project.getArtifactId(),
364 "SNAPSHOT")) {
365 EnforcementRule rule = new EnforcementRule();
366 rule.setGroupId(project.getGroupId());
367 rule.setArtifactId(project.getArtifactId());
368 rule.setVersion("SNAPSHOT");
369 addRule(rule);
370 } else {
371 Log log = deputy.getLog();
372 if (log != null) {
373 log.log(Log.SEVERITY_WARNING,
374 "No SNAPSHOT in repositories for "
375 + project);
376 }
377 warn = true;
378 }
379 }
380 if (warn) {
381 JOptionPane.showMessageDialog(
382 DeputyTree.this,
383 "Could not create SNAPSHOT enforcement rules"
384 + " for all selected projects"
385 + " because such SNAPSHOTs don't exist.\n"
386 + "See log for affected projects.",
387 "Warning",
388 JOptionPane.WARNING_MESSAGE);
389 }
390 }
391 }
392 }
393
394 /***
395 * Adds a deprecation rule for the selected project.
396 *
397 * @author Matthias Burbach
398 */
399 private class AddDeprecationRuleAction extends AddRuleAction {
400 /***
401 * Constructs this action.
402 */
403 public AddDeprecationRuleAction() {
404 super("Add Deprecation Rule");
405 }
406
407
408
409
410
411
412 /***
413 * {@inheritDoc}
414 */
415 public void actionPerformed(final ActionEvent e) {
416 if (mayAdd()) {
417 Iterator iter =
418 getSelectedProjectHolderTreeNodes().iterator();
419 while (iter.hasNext()) {
420 ProjectHolderTreeNode projectNode =
421 (ProjectHolderTreeNode) iter.next();
422 Project project = projectNode.getProject();
423 DeprecationRule rule = new DeprecationRule();
424 rule.setGroupId(project.getGroupId());
425 rule.setArtifactId(project.getArtifactId());
426 rule.setVersion(project.getVersion());
427 addRule(rule);
428 }
429 }
430 }
431 }
432
433 /***
434 * Adds a replacement rule for the two selected projects (or artifacts).
435 *
436 * @author Matthias Burbach
437 */
438 private class AddReplacementRuleAction extends AddRuleAction {
439 /***
440 * if <code>true</code> replacement rules will be created
441 * without specific version of the artifact to be removed.
442 */
443 private boolean noVersion = false;
444
445 /***
446 * Constructs this action.
447 * @param noVersion if <code>true</code> replacement rules will be
448 * created without specific version of the artifact to
449 * be replaced.
450 */
451 public AddReplacementRuleAction(final boolean noVersion) {
452 super("Add Replacement Rule");
453 this.noVersion = noVersion;
454 if (noVersion) {
455 putValue(Action.NAME, "Add Replacement Rule (no version)");
456 }
457 }
458
459
460
461
462
463
464 /***
465 * {@inheritDoc}
466 */
467 public void actionPerformed(final ActionEvent e) {
468 if (mayAdd()) {
469 TreeNode node = getSelectedNode();
470 String groupId = null;
471 String artifactId = null;
472 String version = null;
473 if (node instanceof RepositoryArtifact) {
474 RepositoryArtifact artifact = (RepositoryArtifact) node;
475 groupId = artifact.getGroupId();
476 artifactId = artifact.getArtifactId();
477 } else if (node instanceof ProjectHolderTreeNode) {
478 Project project =
479 ((ProjectHolderTreeNode) node).getProject();
480 groupId = project.getGroupId();
481 artifactId = project.getArtifactId();
482 if (!noVersion) {
483 version = project.getVersion();
484 }
485 }
486 if (groupId != null && artifactId != null) {
487 ReplacementRule rule = new ReplacementRule();
488 rule.setDisplacedGroupId(groupId);
489 rule.setDisplacedArtifactId(artifactId);
490 rule.setDisplacedVersion(version);
491 replacementChooserDialog.setRule(rule);
492 replacementChooserDialog.activate();
493 if (rule.getGroupId() != null
494 && rule.getArtifactId() != null
495 && rule.getVersion() != null) {
496 addRule(rule);
497 }
498 }
499 }
500 }
501 }
502
503 /***
504 * Adds a removal rule for the two selected projects (or artifacts).
505 *
506 * @author Matthias Burbach
507 */
508 private class AddRemovalRuleAction extends AddRuleAction {
509 /***
510 * if <code>true</code> removal rules will be created
511 * without specific version of the artifact to be removed.
512 */
513 private boolean noVersion = false;
514
515 /***
516 * Constructs this action.
517 * @param noVersion if <code>true</code> removal rules will be created
518 * without specific version of the artifact to be removed
519 */
520 public AddRemovalRuleAction(final boolean noVersion) {
521 super("Add Removal Rule");
522 this.noVersion = noVersion;
523 if (noVersion) {
524 putValue(Action.NAME, "Add Removal Rule (no version)");
525 }
526 }
527
528
529
530
531
532
533 /***
534 * {@inheritDoc}
535 */
536 public void actionPerformed(final ActionEvent e) {
537 if (mayAdd()) {
538 Iterator iter =
539 getSelectedNodes().iterator();
540 String groupId = null;
541 String artifactId = null;
542 String version = null;
543 while (iter.hasNext()) {
544 TreeNode node = (TreeNode) iter.next();
545 if (node instanceof RepositoryArtifact) {
546 RepositoryArtifact artifact = (RepositoryArtifact) node;
547 groupId = artifact.getGroupId();
548 artifactId = artifact.getArtifactId();
549 } else if (node instanceof ProjectHolderTreeNode) {
550 Project project =
551 ((ProjectHolderTreeNode) node).getProject();
552 groupId = project.getGroupId();
553 artifactId = project.getArtifactId();
554 if (!noVersion) {
555 version = project.getVersion();
556 }
557 }
558 if (groupId != null && artifactId != null) {
559 RemovalRule rule = new RemovalRule();
560 rule.setGroupId(groupId);
561 rule.setArtifactId(artifactId);
562 rule.setVersion(version);
563 addRule(rule);
564 }
565 }
566 }
567 }
568 }
569
570 /***
571 * Adds a retention rule for the selected project (or artifact).
572 *
573 * @author Matthias Burbach
574 */
575 private class AddRetentionRuleAction extends AddRuleAction {
576 /***
577 * if <code>true</code> retention rules will be created
578 * without specific version of the artifact to be removed.
579 */
580 private boolean noVersion = false;
581
582 /***
583 * Constructs this action.
584 * @param noVersion if <code>true</code> retention rules will be created
585 * without specific version of the artifact to be removed
586 */
587 public AddRetentionRuleAction(final boolean noVersion) {
588 super("Add Retention Rule");
589 this.noVersion = noVersion;
590 if (noVersion) {
591 putValue(Action.NAME, "Add Retention Rule (no version)");
592 }
593 }
594
595
596
597
598
599
600 /***
601 * {@inheritDoc}
602 */
603 public void actionPerformed(final ActionEvent e) {
604 if (mayAdd()) {
605 Iterator iter =
606 getSelectedNodes().iterator();
607 String groupId = null;
608 String artifactId = null;
609 String version = null;
610 while (iter.hasNext()) {
611 TreeNode node = (TreeNode) iter.next();
612 if (node instanceof RepositoryArtifact) {
613 RepositoryArtifact artifact = (RepositoryArtifact) node;
614 groupId = artifact.getGroupId();
615 artifactId = artifact.getArtifactId();
616 } else if (node instanceof ProjectHolderTreeNode) {
617 Project project =
618 ((ProjectHolderTreeNode) node).getProject();
619 groupId = project.getGroupId();
620 artifactId = project.getArtifactId();
621 if (!noVersion) {
622 version = project.getVersion();
623 }
624 }
625 if (groupId != null && artifactId != null) {
626 RetentionRule rule = new RetentionRule();
627 rule.setGroupId(groupId);
628 rule.setArtifactId(artifactId);
629 rule.setVersion(version);
630 addRule(rule);
631 }
632 }
633 }
634 }
635 }
636
637 /***
638 * Removes all selected rules.
639 *
640 * @author Matthias Burbach
641 */
642 private class RemoveRuleAction extends AbstractAction {
643 /***
644 * Constructs this action.
645 */
646 public RemoveRuleAction() {
647 super("Remove");
648 }
649
650
651
652
653
654
655 /***
656 * {@inheritDoc}
657 */
658 public void actionPerformed(final ActionEvent e) {
659 Iterator iter = getSelectedRuleNodes().iterator();
660 RuleSet ruleSet = deputy.getRootProject().getRuleSet();
661 while (iter.hasNext()) {
662 RuleTreeNode ruleTreeNode = (RuleTreeNode) iter.next();
663 Rule rule = ruleTreeNode.getRule();
664 ruleSet.remove(rule);
665 }
666 }
667 }
668
669 /***
670 * Removes all selected top level dependencies.
671 *
672 * @author Matthias Burbach
673 */
674 private class RemoveDependencyAction extends AbstractAction {
675 /***
676 * Constructs this action.
677 */
678 public RemoveDependencyAction() {
679 super("Remove");
680 }
681
682
683
684
685
686
687 /***
688 * {@inheritDoc}
689 */
690 public void actionPerformed(final ActionEvent e) {
691 Iterator iter =
692 getSelectedDirectDependencyNodes().iterator();
693 Project rootProject = deputy.getRootProject();
694 while (iter.hasNext()) {
695 ProjectTreeNode projectTreeNode = (ProjectTreeNode) iter.next();
696 Project dependency = projectTreeNode.getProject();
697 if (rootProject.hasDependency(dependency)) {
698 rootProject.removeDependency(dependency);
699 }
700 }
701 }
702 }
703
704 /***
705 * Adds a top level dependency.
706 *
707 * @author Matthias Burbach
708 */
709 private class AddDependencyAction extends AbstractAction {
710 /***
711 * Constructs this action.
712 */
713 public AddDependencyAction() {
714 super("Add Dependency");
715 }
716
717
718
719
720
721
722 /***
723 * {@inheritDoc}
724 */
725 public void actionPerformed(final ActionEvent e) {
726 if (deputy.getRootProject() == null) {
727 JOptionPane.showMessageDialog(
728 DeputyTree.this,
729 "You need to open a project "
730 + "before you can add a dependency.",
731 "Info",
732 JOptionPane.INFORMATION_MESSAGE);
733 return;
734 }
735 Iterator nodeIter = getSelectedNodes().iterator();
736 while (nodeIter.hasNext()) {
737 TreeNode node = (TreeNode) nodeIter.next();
738 handleNode(node);
739 }
740 }
741
742 /***
743 * @param node The node to handle.
744 */
745 private void handleNode(final TreeNode node) {
746 if (node instanceof ProjectHolderTreeNode) {
747 boolean doAdd = true;
748 boolean doRemoveFromDependencies = false;
749 boolean doRemoveFromIndirectDependencies = false;
750 ProjectHolderTreeNode projectNode =
751 (ProjectHolderTreeNode) node;
752 Project dependency = projectNode.getProject();
753 Project topProject = deputy.getRootProject();
754 if (isRootProject(dependency)) {
755 JOptionPane.showMessageDialog(
756 DeputyTree.this,
757 dependency + " is the top project. "
758 + "It cannot be made a dependency of itself.",
759 "Info",
760 JOptionPane.INFORMATION_MESSAGE);
761 doAdd = false;
762 } else if (topProject.hasDependency(dependency)) {
763 JOptionPane.showMessageDialog(
764 DeputyTree.this,
765 "The dependency '" + dependency + "' already exists. "
766 + "It cannot be added twice.",
767 "Info",
768 JOptionPane.INFORMATION_MESSAGE);
769 doAdd = false;
770 } else if (topProject.hasDependencyToSameArtifact(dependency)
771 && !topProject.getRuleSet().isRetained(
772 dependency.getArtifactId(),
773 dependency.getVersion())) {
774 int selectedOption = JOptionPane.showOptionDialog(
775 DeputyTree.this,
776 "The dependency '" + dependency + "' already exists "
777 + "except for the version.\n"
778 + "If you really need to change the version, "
779 + "better use rules to do so, if possible!\n"
780 + "If you want to add multiple versions of the same "
781 + "artifact you must create a retention rule first.",
782 "Really change the version of this dependency "
783 + "this way?",
784 JOptionPane.OK_CANCEL_OPTION,
785 JOptionPane.QUESTION_MESSAGE,
786 null,
787 new Object[] {
788 "Yes, change the version now",
789 "No, cancel" },
790 null);
791 if (selectedOption == 0) {
792 doRemoveFromDependencies = true;
793 } else {
794 doAdd = false;
795 }
796 } else if (topProject.isAssembly()
797 && topProject.hasIndirectDependencyToSameArtifact(
798 dependency)) {
799 int selectedOption = JOptionPane.showOptionDialog(
800 DeputyTree.this,
801 dependency + " is currently an indirect dependency. "
802 + "If you really must, you can change it to a direct "
803 + "dependency. ",
804 "Really change this to a direct dependency?",
805 JOptionPane.OK_CANCEL_OPTION,
806 JOptionPane.QUESTION_MESSAGE,
807 null,
808 new Object[] {
809 "Yes, change to a direct dependency",
810 "No, cancel" },
811 null);
812 if (selectedOption == 0) {
813 doRemoveFromIndirectDependencies = true;
814 } else {
815 doAdd = false;
816 }
817 }
818 Project removable = null;
819 if (doRemoveFromDependencies) {
820 removable =
821 topProject.getDependencyToSameArtifact(
822 dependency);
823 topProject.removeDependency(removable);
824 }
825 if (doRemoveFromIndirectDependencies) {
826 removable =
827 topProject.getIndirectDependencyToSameArtifact(
828 dependency);
829 topProject.removeIndirectDependency(removable);
830 }
831 if (doAdd) {
832 if (removable != null) {
833
834
835
836 dependency.setType(removable.getType());
837 dependency.setJar(removable.getJar());
838 dependency.setUrl(removable.getUrl());
839 }
840 topProject.addDependency(dependency);
841 }
842 }
843 }
844
845 /***
846 * @param project The project to check.
847 * @return <code>true</code> if the project is the root project.
848 */
849 private boolean isRootProject(final Project project) {
850 boolean result = project.isRootProject();
851 if (!result) {
852 Project topProject = deputy.getRootProject();
853 if (topProject.getGroupId().equals(project.getGroupId())
854 && topProject.getArtifactId().equals(
855 project.getArtifactId())) {
856 result = true;
857 }
858 }
859 return result;
860 }
861 }
862
863 /***
864 * Pops up the appropriate menu depending on the type of node selected.
865 *
866 * @author Matthias Burbach
867 */
868 private class PopupTrigger extends MouseAdapter {
869
870
871
872
873
874 /***
875 * {@inheritDoc}
876 */
877 public void mouseReleased(final MouseEvent e) {
878 if (e.isPopupTrigger()) {
879
880
881
882
883 directDependencyNodePopupMenu.enableAllItems();
884 indirectDependencyNodePopupMenu.enableAllItems();
885
886 int x = e.getX();
887 int y = e.getY();
888
889
890
891
892
893 int numberOfSelectedNodes = getNumberOfSelectedNodes();
894 if (numberOfSelectedNodes > 0
895 && numberOfSelectedNodes
896 == getSelectedRuleNodes().size()) {
897 ruleNodePopupMenu.show(DeputyTree.this, x, y);
898
899
900
901 return;
902 } else if (getSelectedDirectDependencyNodes().size()
903 == numberOfSelectedNodes
904 && numberOfSelectedNodes > 1) {
905 directDependencyNodePopupMenu.disableSingleSelectionItems();
906 directDependencyNodePopupMenu.show(DeputyTree.this, x, y);
907
908
909
910 return;
911 } else if (getSelectedIndirectDependencyNodes().size()
912 == numberOfSelectedNodes
913 && numberOfSelectedNodes > 1) {
914 indirectDependencyNodePopupMenu
915 .disableSingleSelectionItems();
916 indirectDependencyNodePopupMenu.show(DeputyTree.this, x, y);
917
918
919
920 return;
921 }
922
923
924
925
926
927 int selectedRow =
928 DeputyTree.this.getClosestRowForLocation(x, y);
929 DeputyTree.this.setSelectionRow(selectedRow);
930 TreeNode selectedNode = DeputyTree.this.getSelectedNode();
931 if (selectedNode instanceof ProjectHolderTreeNode) {
932 boolean showTopDependencyPopupMenu = false;
933 TreeNode parentNode = selectedNode.getParent();
934 if (parentNode instanceof DependenciesTreeNode) {
935 parentNode = parentNode.getParent();
936 if (parentNode instanceof ProjectTreeNode) {
937 Project project =
938 ((ProjectTreeNode) parentNode).getProject();
939 if (project.isRootProject()) {
940 showTopDependencyPopupMenu = true;
941 }
942 }
943 }
944 if (showTopDependencyPopupMenu) {
945 directDependencyNodePopupMenu.show(
946 DeputyTree.this, x, y);
947 } else {
948 indirectDependencyNodePopupMenu.show(
949 DeputyTree.this, x, y);
950 }
951 } else if (selectedNode instanceof RuleTreeNode) {
952 ruleNodePopupMenu.show(DeputyTree.this, x, y);
953 } else if (selectedNode instanceof RuleSetTreeNode) {
954 ruleSetNodePopupMenu.show(DeputyTree.this, x, y);
955 } else if (selectedNode instanceof RepositoryArtifact) {
956 artifactNodePopupMenu.show(DeputyTree.this, x, y);
957 } else {
958 treeNodePopupMenu.show(DeputyTree.this, x, y);
959 }
960 }
961 }
962 }
963
964 /***
965 * @author Matthias Burbach
966 */
967 private class DependencyPopupMenu extends JPopupMenu {
968 /***
969 * Holds all items of this menu which must not be enabled when
970 * multiple nodes are selected at the same time.
971 */
972 private List singleSelectionItems = new ArrayList();
973
974 /***
975 * Constructs this menu.
976 * @param direct Whether this menu is for direct or indirect
977 * dependencies.
978 */
979 public DependencyPopupMenu(final boolean direct) {
980 Action action = new OpenSiteAction();
981 singleSelectionItems.add(action);
982 add(action);
983
984 addSeparator();
985
986 action = new AddEnforcementRuleAction();
987
988 add(action);
989
990 action = new AddSnapshotEnforcementRuleAction();
991
992 add(action);
993
994 action = new AddDeprecationRuleAction();
995
996 add(action);
997
998 action = new AddReplacementRuleAction(false);
999 singleSelectionItems.add(action);
1000 add(action);
1001
1002 action = new AddReplacementRuleAction(true);
1003 singleSelectionItems.add(action);
1004 add(action);
1005
1006 action = new AddRemovalRuleAction(false);
1007
1008 add(action);
1009
1010 action = new AddRemovalRuleAction(true);
1011
1012 add(action);
1013
1014 action = new AddRetentionRuleAction(true);
1015
1016 add(action);
1017
1018 addSeparator();
1019
1020 action = new FindInRepositoriesAction();
1021 singleSelectionItems.add(action);
1022 add(action);
1023
1024 action = new CollapseAllAction();
1025 singleSelectionItems.add(action);
1026 add(action);
1027
1028 addSeparator();
1029
1030 if (direct) {
1031 add(new RemoveDependencyAction());
1032 } else {
1033 add(new AddDependencyAction());
1034 }
1035 }
1036
1037 /***
1038 * Enables all menu items of this popup menu.
1039 */
1040 public void enableAllItems() {
1041 Iterator iter = singleSelectionItems.iterator();
1042 while (iter.hasNext()) {
1043 Action action = (Action) iter.next();
1044 action.setEnabled(true);
1045 }
1046 }
1047
1048 /***
1049 * Disables all menu items of this menu which are only useful for
1050 * single tree node selections.
1051 */
1052 public void disableSingleSelectionItems() {
1053 Iterator iter = singleSelectionItems.iterator();
1054 while (iter.hasNext()) {
1055 Action action = (Action) iter.next();
1056 action.setEnabled(false);
1057 }
1058 }
1059 }
1060
1061 /***
1062 * The popup menu on project tree nodes which are direct dependencies of the
1063 * top project.
1064 */
1065 private DependencyPopupMenu directDependencyNodePopupMenu;
1066
1067 /***
1068 * The popup menu on project tree nodes which are projects but not
1069 * direct dependencies the top project.
1070 */
1071 private DependencyPopupMenu indirectDependencyNodePopupMenu;
1072
1073 /***
1074 * The popup menu on the artifact id displaying nodes.
1075 */
1076 private JPopupMenu artifactNodePopupMenu;
1077
1078 /***
1079 * The popup menu on rule nodes.
1080 */
1081 private JPopupMenu ruleNodePopupMenu;
1082
1083 /***
1084 * The popup menu on the rule set node.
1085 */
1086 private JPopupMenu ruleSetNodePopupMenu;
1087
1088 /***
1089 * The popup menu on general tree nodes.
1090 */
1091 private JPopupMenu treeNodePopupMenu;
1092
1093 /***
1094 * The main GUI application object.
1095 */
1096 private DeputyFrame deputyFrame;
1097
1098 /***
1099 * The core application object.
1100 */
1101 private Deputy deputy;
1102
1103 /***
1104 * The modal dialog to choose an artifact replacement as part of a
1105 * replacement rule.
1106 */
1107 private ReplacementChooserDialog replacementChooserDialog;
1108
1109 /***
1110 * @param deputy The core application object.
1111 * @param deputyFrame The gui application object.
1112 * @param treeModel The tree model to delegate change operations to.
1113 * @param replacementChooserDialog The modal dialog to choose an artifact
1114 * replacement as part of a replacement
1115 * rule.
1116 */
1117 public DeputyTree(
1118 final Deputy deputy,
1119 final DeputyFrame deputyFrame,
1120 final TreeModel treeModel,
1121 final ReplacementChooserDialog replacementChooserDialog) {
1122 this.deputy = deputy;
1123 this.deputyFrame = deputyFrame;
1124 this.replacementChooserDialog = replacementChooserDialog;
1125
1126 setModel(treeModel);
1127 getSelectionModel().setSelectionMode(
1128 TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
1129
1130 DeputyTreeCellRenderer treeCellRenderer =
1131 new DeputyTreeCellRenderer(deputy);
1132 setCellRenderer(treeCellRenderer);
1133
1134 if (replacementChooserDialog != null) {
1135 artifactNodePopupMenu = new JPopupMenu();
1136 artifactNodePopupMenu.add(new AddReplacementRuleAction(true));
1137 artifactNodePopupMenu.add(new AddRemovalRuleAction(true));
1138 artifactNodePopupMenu.add(new AddRetentionRuleAction(true));
1139 add(artifactNodePopupMenu);
1140
1141 directDependencyNodePopupMenu = new DependencyPopupMenu(true);
1142 add(directDependencyNodePopupMenu);
1143
1144 indirectDependencyNodePopupMenu = new DependencyPopupMenu(false);
1145 add(indirectDependencyNodePopupMenu);
1146
1147 ruleSetNodePopupMenu = new JPopupMenu();
1148 ruleSetNodePopupMenu.add(
1149 new ChangeDefaultRuleAction(
1150 RuleSet.DEFAULT_RULE_SNAPSHOT,
1151 "SNAPSHOT"));
1152 ruleSetNodePopupMenu.add(
1153 new ChangeDefaultRuleAction(
1154 RuleSet.DEFAULT_RULE_LATEST_RELEASE,
1155 "LATEST RELEASE"));
1156 ruleSetNodePopupMenu.add(
1157 new ChangeDefaultRuleAction(
1158 RuleSet.DEFAULT_RULE_LATEST_RELEASE_NO_SCAN,
1159 "LATEST RELEASE (no scan)"));
1160 ruleSetNodePopupMenu.add(
1161 new ChangeDefaultRuleAction(
1162 RuleSet.DEFAULT_RULE_PRESENT_RELEASE,
1163 "PRESENT RELEASE"));
1164 add(ruleSetNodePopupMenu);
1165
1166 ruleNodePopupMenu = new JPopupMenu();
1167 ruleNodePopupMenu.add(new RemoveRuleAction());
1168 add(ruleNodePopupMenu);
1169
1170 treeNodePopupMenu = new JPopupMenu();
1171 treeNodePopupMenu.add(new CollapseAllAction());
1172 add(treeNodePopupMenu);
1173
1174 addMouseListener(new PopupTrigger());
1175 }
1176 }
1177
1178 /***
1179 * @return The currently selected tree node or <code>null</code>.
1180 */
1181 public TreeNode getSelectedNode() {
1182 TreeNode result = null;
1183 TreePath treePath = getSelectionPath();
1184 if (treePath != null) {
1185 result = (TreeNode) treePath.getLastPathComponent();
1186 }
1187 return result;
1188 }
1189
1190 /***
1191 * @return The list of currently selected nodes of type {@link TreeNode}.
1192 */
1193 public List getSelectedNodes() {
1194 List result = new ArrayList();
1195 TreePath[] treePaths = getSelectionPaths();
1196 if (treePaths != null) {
1197 for (int i = 0; i < treePaths.length; i++) {
1198 TreeNode candidateNode =
1199 (TreeNode) treePaths[i].getLastPathComponent();
1200 result.add(candidateNode);
1201 }
1202 }
1203 return result;
1204 }
1205
1206 /***
1207 * @return The list of currently selected rule nodes of type
1208 * {@link RuleTreeNode}.
1209 */
1210 public List getSelectedRuleNodes() {
1211 List result = new ArrayList();
1212 TreePath[] treePaths = getSelectionPaths();
1213 if (treePaths != null) {
1214 for (int i = 0; i < treePaths.length; i++) {
1215 TreeNode candidateNode =
1216 (TreeNode) treePaths[i].getLastPathComponent();
1217 if (candidateNode instanceof RuleTreeNode) {
1218 result.add(candidateNode);
1219 }
1220 }
1221 }
1222 return result;
1223 }
1224
1225 /***
1226 * @return The number of currently selected rule nodes.
1227 */
1228 private int getNumberOfSelectedNodes() {
1229 int result = 0;
1230 TreePath[] treePaths = getSelectionPaths();
1231 if (treePaths != null) {
1232 result = treePaths.length;
1233 }
1234 return result;
1235 }
1236
1237 /***
1238 * @return The list of currently selected direct dependency nodes of the top
1239 * project of type {@link Project}.
1240 */
1241 private List getSelectedDirectDependencyNodes() {
1242 List result = new ArrayList();
1243 TreePath[] treePaths = getSelectionPaths();
1244 if (treePaths != null) {
1245 for (int i = 0; i < treePaths.length; i++) {
1246 TreeNode candidateNode =
1247 (TreeNode) treePaths[i].getLastPathComponent();
1248 if (candidateNode instanceof ProjectTreeNode) {
1249 TreeNode parentNode = candidateNode.getParent();
1250 if (parentNode instanceof DependenciesTreeNode) {
1251 TreeNode grandParentNode = parentNode.getParent();
1252 if (grandParentNode instanceof ProjectTreeNode) {
1253 Project project =
1254 ((ProjectTreeNode) grandParentNode)
1255 .getProject();
1256 if (project.isRootProject()) {
1257 result.add(candidateNode);
1258 }
1259 }
1260 }
1261 }
1262 }
1263 }
1264 return result;
1265 }
1266
1267 /***
1268 * @return The list of currently selected indirect dependency nodes of the
1269 * top project of type {@link Project}.
1270 */
1271 private List getSelectedIndirectDependencyNodes() {
1272 List result = new ArrayList();
1273 TreePath[] treePaths = getSelectionPaths();
1274 if (treePaths != null) {
1275 for (int i = 0; i < treePaths.length; i++) {
1276 TreeNode candidateNode =
1277 (TreeNode) treePaths[i].getLastPathComponent();
1278 if (candidateNode instanceof ProjectTreeNode) {
1279 TreeNode parentNode = candidateNode.getParent();
1280 if (parentNode instanceof IndirectDependenciesTreeNode) {
1281 TreeNode grandParentNode = parentNode.getParent();
1282 if (grandParentNode instanceof ProjectTreeNode) {
1283 Project project =
1284 ((ProjectTreeNode) grandParentNode)
1285 .getProject();
1286 if (project.isRootProject()) {
1287 result.add(candidateNode);
1288 }
1289 }
1290 }
1291 }
1292 }
1293 }
1294 return result;
1295 }
1296
1297 /***
1298 * @return The list of currently selected dependency nodes
1299 * of type {@link ProjectHolderTreeNode}.
1300 */
1301 private List getSelectedProjectHolderTreeNodes() {
1302 List result = new ArrayList();
1303 TreePath[] treePaths = getSelectionPaths();
1304 if (treePaths != null) {
1305 for (int i = 0; i < treePaths.length; i++) {
1306 TreeNode candidateNode =
1307 (TreeNode) treePaths[i].getLastPathComponent();
1308 if (candidateNode instanceof ProjectHolderTreeNode) {
1309 result.add(candidateNode);
1310 }
1311 }
1312 }
1313 return result;
1314 }
1315 }