1 package de.matthias_burbach.deputy.swing;
2
3 import java.awt.BorderLayout;
4 import java.awt.Event;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.KeyEvent;
7 import java.awt.event.WindowAdapter;
8 import java.awt.event.WindowEvent;
9 import java.io.File;
10 import java.io.FileNotFoundException;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16
17 import javax.swing.AbstractAction;
18 import javax.swing.Action;
19 import javax.swing.BorderFactory;
20 import javax.swing.Icon;
21 import javax.swing.JFileChooser;
22 import javax.swing.JFrame;
23 import javax.swing.JOptionPane;
24 import javax.swing.JScrollPane;
25 import javax.swing.JSplitPane;
26 import javax.swing.JToggleButton;
27 import javax.swing.JToolBar;
28 import javax.swing.KeyStroke;
29 import javax.swing.SwingUtilities;
30 import javax.swing.UIManager;
31 import javax.swing.WindowConstants;
32 import javax.swing.filechooser.FileFilter;
33 import javax.swing.tree.DefaultTreeModel;
34 import javax.swing.tree.TreeNode;
35 import javax.swing.tree.TreePath;
36
37 import de.matthias_burbach.deputy.core.Deputy;
38 import de.matthias_burbach.deputy.core.DeputyChangeListener;
39 import de.matthias_burbach.deputy.core.project.Project;
40 import de.matthias_burbach.deputy.core.repository.Repository;
41 import de.matthias_burbach.deputy.core.repository.RepositorySet;
42 import de.matthias_burbach.deputy.core.rule.RuleSet;
43 import de.matthias_burbach.deputy.core.util.BrowserLauncher;
44 import de.matthias_burbach.deputy.core.util.Log;
45 import de.matthias_burbach.deputy.core.util.P4;
46 import de.matthias_burbach.deputy.core.util.SimpleLog;
47
48 /***
49 * Is the main GUI class of Deputy. Holds a map of actions that can be triggered
50 * by menu bar, tool bar and popup menu items outside of this class.
51 *
52 * @author Matthias Burbach
53 */
54 public class DeputyFrame extends JFrame implements DeputyChangeListener {
55 /***
56 * The 'new Maven file' action.
57 */
58 public static final String ACTION_NEW =
59 "New";
60
61 /***
62 * The 'open Maven file' action.
63 */
64 public static final String ACTION_OPEN =
65 "Open";
66
67 /***
68 * The 'save currently opened Maven file' action.
69 */
70 public static final String ACTION_SAVE =
71 "Save";
72
73 /***
74 * The 'save currently opened Maven file as...' action.
75 */
76 public static final String ACTION_SAVE_AS =
77 "SaveAs";
78
79 /***
80 * The 'derive enforcement rules from project dependencies' action.
81 */
82 public static final String ACTION_DERIVE_ENFORCEMENTS_FROM_PROJECT =
83 "DeriveEnforcementsFromProject";
84
85 /***
86 * The 'export currently opened Maven file as dependency graph' action.
87 */
88 public static final String ACTION_EXPORT =
89 "Export";
90
91 /***
92 * The 'exit application' action.
93 */
94 public static final String ACTION_EXIT =
95 "Exit";
96
97 /***
98 * The 'apply rules on currently opened Maven file' action.
99 */
100 public static final String ACTION_APPLY_RULES =
101 "ApplyRules";
102
103 /***
104 * The 'remove all currently selected rules' action.
105 */
106 public static final String ACTION_REMOVE_SELECTED_RULES =
107 "RemoveSelectedRules";
108
109 /***
110 * The 'remove all SNAPSHOT enforcement rules' action.
111 */
112 public static final String ACTION_REMOVE_SNAPSHOT_ENFORCEMENTS =
113 "RemoveSnapshotEnforcementRules";
114
115 /***
116 * The 'remove all derived rules' action.
117 */
118 public static final String ACTION_REMOVE_DERIVED_RULES =
119 "RemoveDerivedRules";
120
121 /***
122 * The 'sort snapshots topologically' action.
123 */
124 public static final String ACTION_SORT_SNAPSHOTS_TOPOLOGICALLY =
125 "SortSnapshotsTopologically";
126
127 /***
128 * The 'edit repository configs' action.
129 */
130 public static final String ACTION_EDIT_REPOSITORY_CONFIGS =
131 "EditRepositoryConfigs";
132
133 /***
134 * The 'sort snapshots topologically' action.
135 */
136 public static final String ACTION_TOGGLE_VIRTUAL_REPOSITORY =
137 "ToggleVirtualRepository";
138
139 /***
140 * The 'refresh repository browser' action.
141 */
142 public static final String ACTION_REFRESH_REPOSITORY_BROWSER =
143 "RefreshRepositoryBrowser";
144
145 /***
146 * The 'Homepage' action.
147 */
148 public static final String ACTION_HOMEPAGE =
149 "Homepage";
150
151 /***
152 * The 'Release Notes' action.
153 */
154 public static final String ACTION_RELEASE_NOTES =
155 "ReleaseNotes";
156
157 /***
158 * The 'show about dialog' action.
159 */
160 public static final String ACTION_SHOW_ABOUT =
161 "ShowAbout";
162
163 /***
164 * Internally flags to open a project as is.
165 */
166 private static final int STRATEGY_AS_IS = 0;
167
168 /***
169 * Internally flags to apply the rules to the open project.
170 */
171 private static final int STRATEGY_APPLY_RULES = 1;
172
173 /***
174 * The deputy core application object.
175 */
176 private Deputy deputy;
177
178 /***
179 * The map of supported actions of type ActionListener.
180 */
181 private Map actionListeners;
182
183 /***
184 * The tool bar.
185 */
186 private JToolBar toolBar;
187
188 /***
189 * The button to switch on/off the usage of the virtual repository.
190 */
191 private JToggleButton virtualRepositoryToggleButton;
192
193 /***
194 * Indicates the virtual repository is active (green light).
195 */
196 private Icon virtualRepositoryOnIcon;
197
198 /***
199 * Indicates the virtual repository is inactive (red light).
200 */
201 private Icon virtualRepositoryOffIcon;
202
203 /***
204 * The cached file chooser for open and save actions.
205 * Must only be created after the look and feel has been set!
206 */
207 private JFileChooser fileChooser;
208
209 /***
210 * The cached file chooser for the export action.
211 * Must only be created after the look and feel has been set!
212 */
213 private JFileChooser exportFileChooser;
214
215 /***
216 * The file filter for the export format 'GraphML'.
217 */
218 private FileFilter graphMlFilter;
219
220 /***
221 * The file filter for the export format 'Deputy XML'.
222 */
223 private FileFilter deputyXmlFilter;
224
225 /***
226 * The log panel at the bottom of the main frame. Used to display messages.
227 */
228 private DeputyLogPanel logPanel;
229
230 /***
231 * The split pane separating the project from the repositories.
232 */
233 private JSplitPane horizontalSplitPane;
234
235 /***
236 * The split pane separating project and repositories from the log.
237 */
238 private JSplitPane verticalSplitPane;
239
240 /***
241 * The current tree of dependencies, conflicts, and rules.
242 */
243 private DeputyTree currentProjectTree;
244
245 /***
246 * The tree of repositories.
247 */
248 private DeputyTree currentRepositoriesTree;
249
250 /***
251 * The additional chooser dialog to complete the entry of a new replacement
252 * rule.
253 */
254 private ReplacementChooserDialog chooserDialog;
255
256 /***
257 * Constructs the frame.
258 *
259 * @param deputy The core application.
260 * @throws Exception if anything goes unexpectedly wrong
261 */
262 public DeputyFrame(final Deputy deputy) throws Exception {
263 this.deputy = deputy;
264 this.deputy.addChangeListener(this);
265
266 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
267
268 actionListeners = createActions();
269 fileChooser = new JFileChooser();
270
271
272
273
274 exportFileChooser = new JFileChooser();
275 graphMlFilter = new FileFilter() {
276 public boolean accept(final File f) {
277 boolean result = false;
278 if (f.isDirectory() || f.getName().endsWith(".graphml")) {
279 result = true;
280 }
281 return result;
282 }
283
284 public String getDescription() {
285 return "GraphML format (.graphml)";
286 }
287 };
288
289 deputyXmlFilter = new FileFilter() {
290 public boolean accept(final File f) {
291 boolean result = false;
292 if (f.isDirectory() || f.getName().endsWith(".xml")) {
293 result = true;
294 }
295 return result;
296 }
297
298 public String getDescription() {
299 return "Deputy XML format (.xml)";
300 }
301 };
302
303 setTitle("Deputy");
304 setJMenuBar(new DeputyMenuBar(this));
305 toolBar = createToolBar();
306
307 int x = 1;
308 try {
309 x = Integer.parseInt(deputy.getProperty("deputyFrame.x"));
310 } catch (Exception e) {
311 e.printStackTrace();
312 }
313 int y = 1;
314 try {
315 y = Integer.parseInt(deputy.getProperty("deputyFrame.y"));
316 } catch (Exception e) {
317 e.printStackTrace();
318 }
319 final int defaultWidth = 650;
320 int width = defaultWidth;
321 try {
322 width = Integer.parseInt(deputy.getProperty("deputyFrame.width"));
323 } catch (Exception e) {
324 e.printStackTrace();
325 }
326 final int defaultHeight = 600;
327 int height = defaultHeight;
328 try {
329 height = Integer.parseInt(deputy.getProperty("deputyFrame.height"));
330 } catch (Exception e) {
331 e.printStackTrace();
332 }
333 setBounds(x, y, width, height);
334
335 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
336 WindowAdapter windowListener = new WindowAdapter() {
337
338
339
340
341
342 /***
343 * {@inheritDoc}
344 */
345 public void windowClosing(final WindowEvent event) {
346 DeputyFrame.this.exit();
347 }
348 };
349 addWindowListener(windowListener);
350
351 logPanel = new DeputyLogPanel();
352 deputy.setLog(logPanel);
353
354 updateContentPane();
355 setVisible(true);
356 }
357
358 /***
359 * Fills the content pane on startup and everytime the project tree or the
360 * repositories tree was (re-)loaded.
361 */
362 private void updateContentPane() {
363
364
365
366 JScrollPane projectScrollPane = new JScrollPane();
367 if (currentProjectTree != null) {
368 projectScrollPane.getViewport().add(currentProjectTree);
369 }
370 projectScrollPane.setBorder(
371 BorderFactory.createTitledBorder("Project"));
372
373
374
375
376 JScrollPane repositoriesScrollPane = new JScrollPane();
377 if (currentRepositoriesTree != null) {
378 repositoriesScrollPane.getViewport().add(currentRepositoriesTree);
379 }
380 repositoriesScrollPane.setBorder(
381 BorderFactory.createTitledBorder("Repositories"));
382
383
384
385
386 int horizontalDividerLocation = getWidth() / 2;
387 if (horizontalSplitPane != null) {
388 horizontalDividerLocation =
389 horizontalSplitPane.getDividerLocation();
390 } else {
391 try {
392 horizontalDividerLocation =
393 Integer.parseInt(
394 deputy.getProperty(
395 "deputyFrame.horizontalSplitPane.dividerLocation"));
396 } catch (Exception e) {
397 e.printStackTrace();
398 }
399 }
400 horizontalSplitPane =
401 new JSplitPane(
402 JSplitPane.HORIZONTAL_SPLIT,
403 projectScrollPane,
404 repositoriesScrollPane);
405 horizontalSplitPane.setDividerLocation(horizontalDividerLocation);
406
407
408
409
410 final int four = 4;
411 int verticalDividerLocation = getHeight() - (getHeight() / four);
412 if (verticalSplitPane != null) {
413 verticalDividerLocation = verticalSplitPane.getDividerLocation();
414 } else {
415 try {
416 verticalDividerLocation =
417 Integer.parseInt(
418 deputy.getProperty(
419 "deputyFrame.verticalSplitPane.dividerLocation"));
420 } catch (Exception e) {
421 e.printStackTrace();
422 }
423 }
424 verticalSplitPane =
425 new JSplitPane(
426 JSplitPane.VERTICAL_SPLIT,
427 horizontalSplitPane,
428 logPanel);
429 verticalSplitPane.setDividerLocation(verticalDividerLocation);
430
431
432
433
434 getContentPane().removeAll();
435 getContentPane().add(toolBar, BorderLayout.NORTH);
436 getContentPane().add(verticalSplitPane, BorderLayout.CENTER);
437 validate();
438 }
439
440 /***
441 * @return The tool bar created.
442 */
443 private JToolBar createToolBar() {
444 JToolBar result = new JToolBar();
445
446 result.add(getAction(ACTION_OPEN));
447 result.add(getAction(ACTION_SAVE));
448 result.add(getAction(ACTION_APPLY_RULES));
449 result.add(getAction(ACTION_REMOVE_SELECTED_RULES));
450
451
452
453
454 virtualRepositoryToggleButton =
455 new JToggleButton(getAction(ACTION_TOGGLE_VIRTUAL_REPOSITORY));
456 result.add(virtualRepositoryToggleButton);
457 boolean active = deputy.isVirtualRepositoryActive();
458 virtualRepositoryToggleButton.setSelected(active);
459 setVirtualRepositoryActive(active);
460
461 return result;
462 }
463
464 /***
465 * @return The map of supported actions of type {@link Action}.
466 */
467 private Map createActions() {
468 Map result = new HashMap();
469 result.putAll(createFileMenuActions());
470 result.putAll(createEditMenuActions());
471 result.putAll(createViewMenuActions());
472 result.putAll(createHelpMenuActions());
473 return result;
474 }
475
476 /***
477 * @return The map of supported file actions of type {@link Action}.
478 */
479 private Map createFileMenuActions() {
480 Map result = new HashMap();
481
482 Icon icon = SwingHelper.createImageIcon("small-new.gif", "");
483 Action action = new AbstractAction("New...", icon) {
484 public void actionPerformed(final ActionEvent event) {
485 createProject();
486 }
487 };
488 action.putValue(Action.SHORT_DESCRIPTION, "New");
489 action.putValue(
490 Action.ACCELERATOR_KEY,
491 KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK));
492 result.put(ACTION_NEW, action);
493
494 icon = SwingHelper.createImageIcon("small-open.gif", "");
495 action = new AbstractAction("Open...", icon) {
496 public void actionPerformed(final ActionEvent event) {
497 openProject();
498 }
499 };
500 action.putValue(Action.SHORT_DESCRIPTION, "Open");
501 action.putValue(
502 Action.ACCELERATOR_KEY,
503 KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK));
504 result.put(ACTION_OPEN, action);
505
506 icon = SwingHelper.createImageIcon("small-save.gif", "");
507 action = new AbstractAction("Save", icon) {
508 public void actionPerformed(final ActionEvent event) {
509 save();
510 }
511 };
512 action.setEnabled(false);
513 action.putValue(Action.SHORT_DESCRIPTION, "Save");
514 action.putValue(
515 Action.ACCELERATOR_KEY,
516 KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK));
517 result.put(ACTION_SAVE, action);
518
519 action = new AbstractAction("Save As...") {
520 public void actionPerformed(final ActionEvent event) {
521 saveAs();
522 }
523 };
524 action.setEnabled(false);
525 result.put(ACTION_SAVE_AS, action);
526
527 action = new AbstractAction("Derive Enforcements from a Project...") {
528 public void actionPerformed(final ActionEvent event) {
529 deriveEnforcementsFromProject();
530 }
531 };
532 action.setEnabled(false);
533 action.putValue(
534 Action.SHORT_DESCRIPTION,
535 "Add a Project's Dependencies As Derived Enforcement Rules");
536 action.putValue(
537 Action.ACCELERATOR_KEY,
538 KeyStroke.getKeyStroke(KeyEvent.VK_E, Event.CTRL_MASK));
539 result.put(ACTION_DERIVE_ENFORCEMENTS_FROM_PROJECT, action);
540
541 action = new AbstractAction("Export As Dependency Graph...") {
542 public void actionPerformed(final ActionEvent event) {
543 exportDependencyGraph();
544 }
545 };
546 action.setEnabled(false);
547 action.putValue(Action.SHORT_DESCRIPTION, "Export As Dependency Graph");
548 result.put(ACTION_EXPORT, action);
549
550 action = new AbstractAction("Exit") {
551 public void actionPerformed(final ActionEvent event) {
552 exit();
553 }
554 };
555 action.putValue(Action.SHORT_DESCRIPTION, "Exit");
556 result.put(ACTION_EXIT, action);
557
558 return result;
559 }
560
561 /***
562 * @return The map of supported edit actions of type {@link Action}.
563 */
564 private Map createEditMenuActions() {
565 Map result = new HashMap();
566
567 Icon icon = SwingHelper.createImageIcon("apply-rules.gif", "");
568 Action action = new AbstractAction("Apply Rules", icon) {
569 public void actionPerformed(final ActionEvent event) {
570 applyRules();
571 }
572 };
573 action.setEnabled(false);
574 action.putValue(Action.SHORT_DESCRIPTION, "Apply Rules");
575 action.putValue(
576 Action.ACCELERATOR_KEY,
577 KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK));
578 result.put(ACTION_APPLY_RULES, action);
579
580 icon = SwingHelper.createImageIcon("small-cut.gif", "");
581 action = new AbstractAction("Remove Selected Rules", icon) {
582 public void actionPerformed(final ActionEvent event) {
583 removeSelectedRules();
584 }
585 };
586 action.setEnabled(false);
587 action.putValue(Action.SHORT_DESCRIPTION, "Remove Selected Rules");
588 result.put(ACTION_REMOVE_SELECTED_RULES, action);
589
590 action = new AbstractAction("Remove SNAPSHOT Enforcements") {
591 public void actionPerformed(final ActionEvent event) {
592 removeSnapshotEnforcementRules();
593 }
594 };
595 action.setEnabled(false);
596 action.putValue(Action.SHORT_DESCRIPTION,
597 "Remove SNAPSHOT Enforcements");
598 action.putValue(
599 Action.ACCELERATOR_KEY,
600 KeyStroke.getKeyStroke(KeyEvent.VK_K, Event.CTRL_MASK));
601 result.put(ACTION_REMOVE_SNAPSHOT_ENFORCEMENTS, action);
602
603 action = new AbstractAction("Remove Derived Rules") {
604 public void actionPerformed(final ActionEvent event) {
605 removeDerivedRules();
606 }
607 };
608 action.setEnabled(false);
609 action.putValue(Action.SHORT_DESCRIPTION,
610 "Remove Derived Rules");
611 action.putValue(
612 Action.ACCELERATOR_KEY,
613 KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK));
614 result.put(ACTION_REMOVE_DERIVED_RULES, action);
615
616 action = new AbstractAction("Sort SNAPSHOTS topologically...") {
617 public void actionPerformed(final ActionEvent event) {
618 sortSnapshotsTopologically();
619 }
620 };
621 action.setEnabled(false);
622 action.putValue(
623 Action.SHORT_DESCRIPTION,
624 "Sort SNAPSHOTs topologically");
625 action.putValue(
626 Action.ACCELERATOR_KEY,
627 KeyStroke.getKeyStroke(KeyEvent.VK_T, Event.CTRL_MASK));
628 result.put(ACTION_SORT_SNAPSHOTS_TOPOLOGICALLY, action);
629
630 action = new AbstractAction("Edit Repository Configs...") {
631 public void actionPerformed(final ActionEvent event) {
632 editRepositoryConfigs();
633 }
634 };
635 action.putValue(Action.SHORT_DESCRIPTION, "Edit Repository Configs");
636 result.put(ACTION_EDIT_REPOSITORY_CONFIGS, action);
637
638 virtualRepositoryOnIcon =
639 SwingHelper.createImageIcon("virtual-repository-on.gif", "");
640 virtualRepositoryOffIcon =
641 SwingHelper.createImageIcon("virtual-repository-off.gif", "");
642 icon = virtualRepositoryOffIcon;
643 action = new AbstractAction("Toggle Virtual Repository", icon) {
644 public void actionPerformed(final ActionEvent event) {
645 boolean active = virtualRepositoryToggleButton.isSelected();
646 setVirtualRepositoryActive(active);
647 }
648 };
649 action.setEnabled(true);
650 action.putValue(Action.SHORT_DESCRIPTION, "Toggle Virtual Repository");
651 result.put(ACTION_TOGGLE_VIRTUAL_REPOSITORY, action);
652
653 return result;
654 }
655
656 /***
657 * @return The map of supported view actions of type {@link Action}.
658 */
659 private Map createViewMenuActions() {
660 Map result = new HashMap();
661
662 Action action = new AbstractAction("Refresh Repository Browser") {
663 public void actionPerformed(final ActionEvent event) {
664 openRepositories();
665 }
666 };
667 action.putValue(Action.SHORT_DESCRIPTION, "Refresh Repository Browser");
668 action.putValue(
669 Action.ACCELERATOR_KEY,
670 KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
671 result.put(ACTION_REFRESH_REPOSITORY_BROWSER, action);
672
673 return result;
674 }
675
676 /***
677 * @return The map of supported help actions of type {@link Action}.
678 */
679 private Map createHelpMenuActions() {
680 Map result = new HashMap();
681
682 Action action = new AbstractAction("Homepage") {
683 public void actionPerformed(final ActionEvent event) {
684 openUrl("http://deputy.sourceforge.net/");
685 }
686 };
687 action.putValue(Action.SHORT_DESCRIPTION, "Homepage");
688 result.put(ACTION_HOMEPAGE, action);
689
690 action = new AbstractAction("Release Notes") {
691 public void actionPerformed(final ActionEvent event) {
692 openUrl("http://deputy.sourceforge.net/changes-report.html");
693 }
694 };
695 action.putValue(Action.SHORT_DESCRIPTION, "Release Notes");
696 result.put(ACTION_RELEASE_NOTES, action);
697
698 action = new AbstractAction("About Deputy") {
699 public void actionPerformed(final ActionEvent event) {
700 showAbout();
701 }
702 };
703 result.put(ACTION_SHOW_ABOUT, action);
704
705 return result;
706 }
707
708 /***
709 * @param name The name of the action. Must be one of the
710 * ACTION_* constants defined in this class.
711 * @return The action for the name supplied or <code>null</code>
712 * if the name is invalid.
713 */
714 public Action getAction(final String name) {
715 return (Action) actionListeners.get(name);
716 }
717
718 /***
719 * Launches the system's default browser with the site URL of the project
720 * passed in.
721 *
722 * @param project The project whose site to open in the browser.
723 */
724 public void openProjectSite(final Project project) {
725 String url = project.getUrl();
726 if (url == null) {
727 url = project.getDefaultUrl();
728 }
729 if (url != null) {
730 BrowserLauncher.openUrl(url);
731 } else {
732 JOptionPane.showMessageDialog(
733 this,
734 "Sorry, no site URL available on this node.",
735 "Info",
736 JOptionPane.INFORMATION_MESSAGE);
737 }
738 }
739
740
741
742
743
744
745 /***
746 * {@inheritDoc}
747 */
748 public void projectHasChanged() {
749 updateTitle();
750 updateMenuItems();
751 }
752
753 /***
754 * Updates the title of this main frame.
755 */
756 private void updateTitle() {
757 String prefix = "";
758 if (!deputy.isSaved()) {
759 prefix = "*";
760 }
761 setTitle(
762 "Deputy - "
763 + prefix + deputy.getProjectFile());
764 }
765
766 /***
767 * Updates the enabled state of the menu items' actions.
768 */
769 private void updateMenuItems() {
770 boolean enabled = !deputy.isSaved();
771 getAction(ACTION_SAVE).setEnabled(enabled);
772
773 enabled = deputy.getRootProject() != null;
774 getAction(ACTION_SAVE_AS).setEnabled(enabled);
775 getAction(ACTION_DERIVE_ENFORCEMENTS_FROM_PROJECT).setEnabled(true);
776 getAction(ACTION_EXPORT).setEnabled(enabled);
777 getAction(ACTION_APPLY_RULES).setEnabled(enabled);
778 getAction(ACTION_REMOVE_SELECTED_RULES).setEnabled(enabled);
779 getAction(ACTION_REMOVE_SNAPSHOT_ENFORCEMENTS).setEnabled(enabled);
780 getAction(ACTION_REMOVE_DERIVED_RULES).setEnabled(enabled);
781 getAction(ACTION_SORT_SNAPSHOTS_TOPOLOGICALLY).setEnabled(enabled);
782 }
783
784 /***
785 * Creates a new project using a template.
786 */
787 private void createProject() {
788
789 }
790
791 /***
792 * Opens the project whose file name has been set using
793 * {@link Deputy#setProjectFile(String)}.
794 */
795 private void openProject() {
796 boolean doOpen = true;
797 if (!deputy.isSaved()) {
798 int selectedOption = JOptionPane.showOptionDialog(
799 this,
800 "The current state of the project has not been saved.\n"
801 + "Do you really want to open another project without "
802 + "saving this one?",
803 "Open",
804 JOptionPane.YES_NO_OPTION,
805 JOptionPane.QUESTION_MESSAGE,
806 null,
807 new Object[] {"Yes", "No"},
808 null);
809 if (selectedOption == 0) {
810 doOpen = true;
811 } else {
812 doOpen = false;
813 }
814 }
815 if (doOpen) {
816 try {
817 String projectFile = deputy.getProjectFile();
818 if (projectFile != null) {
819 try {
820 File file = new File(projectFile);
821 fileChooser.setCurrentDirectory(file);
822 fileChooser.setSelectedFile(file);
823 } catch (Exception e) {
824
825 e.printStackTrace();
826 }
827 }
828 int result =
829 fileChooser.showOpenDialog(this);
830 if (result == JFileChooser.APPROVE_OPTION) {
831 File file = fileChooser.getSelectedFile();
832 if (isValidProjectFile(file)) {
833 logPanel.clear();
834 deputy.setProjectFile(file.getAbsolutePath());
835 doOpenProject(STRATEGY_AS_IS);
836 }
837 }
838 } catch (Exception e) {
839 e.printStackTrace();
840 JOptionPane.showMessageDialog(
841 this,
842 "Encountered '" + e.getMessage() + "'",
843 "Error",
844 JOptionPane.ERROR_MESSAGE);
845 }
846 }
847 }
848
849 /***
850 * Checks the file name of a project about to be opened for validity.
851 * Warns if it seems to be suspicious.
852 *
853 * @param file The file to validate.
854 * @return <code>true</code> if the file is unsuspicious or if the user
855 * insists to open it.
856 */
857 private boolean isValidProjectFile(final File file) {
858 boolean result = false;
859 String name = file.getName();
860 if (name.endsWith(".pom")
861 || name.equals("test.xml")
862 || name.equals("project.xml")) {
863 result = true;
864 } else {
865 int selectedOption = JOptionPane.showOptionDialog(
866 this,
867 "Guessing from the name, the file you are about to open does "
868 + "not seem to be a Maven project.\n"
869 + " Do you still want to open it?",
870 "Open " + name + "?",
871 JOptionPane.YES_NO_OPTION,
872 JOptionPane.QUESTION_MESSAGE,
873 null,
874 new Object[] {"Yes", "No"},
875 null);
876 if (selectedOption == 0) {
877 result = true;
878 }
879 }
880 return result;
881 }
882
883 /***
884 * Really does attempt to open the project. Is called both by
885 * {@link #openProject()} and by {@link #applyRules()}.
886 *
887 * @param strategy Either {@link #STRATEGY_AS_IS} or
888 * {@link #STRATEGY_APPLY_RULES}.
889 * @throws Exception if anything goes unexpectedly wrong
890 */
891 private void doOpenProject(final int strategy) throws Exception {
892 final WaitDialog waitDialog = new WaitDialog(this, true);
893 Runnable runnable = new Runnable() {
894 public void run() {
895 try {
896 if (chooserDialog == null) {
897 doOpenRepositories();
898 }
899 Project tmpProject = null;
900 if (strategy == STRATEGY_AS_IS) {
901 tmpProject = deputy.openProjectAsIs();
902 } else {
903 tmpProject = deputy.applyRulesToProject();
904 }
905 final Project topProject = tmpProject;
906 SwingUtilities.invokeAndWait(new Runnable() {
907 public void run() {
908 updateGuiAfterOpenProject(topProject);
909 }
910 });
911 } catch (Exception e) {
912 e.printStackTrace();
913 final String message = e.getMessage();
914 try {
915 SwingUtilities.invokeAndWait(new Runnable() {
916 public void run() {
917 waitDialog.dispose();
918 JOptionPane.showMessageDialog(
919 DeputyFrame.this,
920 "Encountered '" + message + "'",
921 "Error",
922 JOptionPane.ERROR_MESSAGE);
923 }
924 });
925 } catch (Exception f) {
926 f.printStackTrace();
927 }
928 } finally {
929 try {
930 SwingUtilities.invokeAndWait(new Runnable() {
931 public void run() {
932 waitDialog.dispose();
933 }
934 });
935 } catch (Exception e) {
936 e.printStackTrace();
937 }
938 }
939 }
940 };
941
942 Thread thread = new Thread(runnable);
943 thread.start();
944 waitDialog.setVisible(true);
945 }
946
947 /***
948 * Updates the GUI after a project was opened.
949 *
950 * @param topProject The top project that was loaded.
951 */
952 private void updateGuiAfterOpenProject(final Project topProject) {
953 DefaultTreeModel treeModel = new DefaultTreeModel(null);
954 TreeNode rootNode =
955 new ProjectTreeNode(
956 topProject,
957 treeModel);
958 treeModel.setRoot(rootNode);
959 currentProjectTree =
960 new DeputyTree(
961 deputy,
962 DeputyFrame.this,
963 treeModel,
964 chooserDialog);
965 final int rulesNodeRow = 5;
966 currentProjectTree.expandRow(rulesNodeRow);
967 final int dependenciesNodeRow = 2;
968 currentProjectTree.expandRow(dependenciesNodeRow);
969 updateContentPane();
970 projectHasChanged();
971 }
972
973 /***
974 * Finds the artifact in one of the repositories and expands the repository
975 * path to it accordingly. Prompts with a message if the artifact cannot
976 * be found in the repository.
977 *
978 * @param groupId The group id to look for.
979 * @param artifactId The artifact id to look for.
980 * @param version The version to look for.
981 */
982 public void findInRepositories(
983 final String groupId,
984 final String artifactId,
985 final String version) {
986 TreeNode leafNode = findVersionNode(groupId, artifactId, version);
987 if (leafNode != null) {
988 List selectionPath = new ArrayList();
989 selectionPath.add(leafNode);
990 TreeNode node = leafNode;
991 while (node.getParent() != null) {
992 selectionPath.add(0, node.getParent());
993 node = node.getParent();
994 }
995 currentRepositoriesTree.setSelectionPath(
996 new TreePath(selectionPath.toArray()));
997
998
999
1000
1001 TreePath selectionTreePath =
1002 currentRepositoriesTree.getSelectionPath();
1003 currentRepositoriesTree.scrollPathToVisible(selectionTreePath);
1004 } else {
1005 JOptionPane.showMessageDialog(
1006 this,
1007 "Sorry, didn't find it. "
1008 + "Seems it has no POM in any of the repositories.",
1009 "Deputy",
1010 JOptionPane.INFORMATION_MESSAGE);
1011 }
1012 }
1013
1014 /***
1015 * @param groupId The group id to look for.
1016 * @param artifactId The artifact id to look for.
1017 * @param version The version to look for.
1018 * @return <code>true</code> if it finds the artifact in one of the
1019 * repositories.
1020 */
1021 public boolean existsInRepositories(
1022 final String groupId,
1023 final String artifactId,
1024 final String version) {
1025 TreeNode leafNode = findVersionNode(groupId, artifactId, version);
1026 return (leafNode != null);
1027 }
1028
1029 /***
1030 * @param groupId The group id to look for.
1031 * @param artifactId The artifact id to look for.
1032 * @param version The version to look for.
1033 * @return The version node representing the version of the artifact in one
1034 * of the repositories or <code>null</code> if it wasn't found.
1035 */
1036 private TreeNode findVersionNode(
1037 final String groupId,
1038 final String artifactId,
1039 final String version) {
1040 TreeNode root = (TreeNode) currentRepositoriesTree.getModel().getRoot();
1041 TreeNode leafNode = null;
1042 for (int i = 0; i < root.getChildCount(); i++) {
1043 Repository child = (Repository) root.getChildAt(i);
1044 leafNode = child.findVersionNode(groupId, artifactId, version);
1045 if (leafNode != null) {
1046 break;
1047 }
1048 }
1049 return leafNode;
1050 }
1051
1052 /***
1053 * (Re-)opens the repositories tree. Can be called at any time during a
1054 * user session to refresh the displayed contents.
1055 */
1056 public void openRepositories() {
1057 final WaitDialog waitDialog = new WaitDialog(this, true);
1058 Runnable runnable = new Runnable() {
1059 public void run() {
1060 try {
1061 doOpenRepositories();
1062 SwingUtilities.invokeAndWait(new Runnable() {
1063 public void run() {
1064 updateContentPane();
1065 }
1066 });
1067 } catch (Exception e) {
1068 e.printStackTrace();
1069 final String message = e.getMessage();
1070 try {
1071 SwingUtilities.invokeAndWait(new Runnable() {
1072 public void run() {
1073 waitDialog.dispose();
1074 JOptionPane.showMessageDialog(
1075 DeputyFrame.this,
1076 "Encountered '" + message + "'",
1077 "Error",
1078 JOptionPane.ERROR_MESSAGE);
1079 }
1080 });
1081 } catch (Exception f) {
1082 f.printStackTrace();
1083 }
1084 } finally {
1085 try {
1086 SwingUtilities.invokeAndWait(new Runnable() {
1087 public void run() {
1088 waitDialog.dispose();
1089 }
1090 });
1091 } catch (Exception e) {
1092 e.printStackTrace();
1093 }
1094 }
1095 }
1096 };
1097
1098 Thread thread = new Thread(runnable);
1099 thread.start();
1100 waitDialog.setVisible(true);
1101 }
1102
1103 /***
1104 * Really does attempt to open the repositories.
1105 *
1106 * @throws Exception if anything goes unexpectedly wrong
1107 */
1108 private void doOpenRepositories() throws Exception {
1109 RepositorySet repositorySet =
1110 new RepositorySet(deputy.getRepositoryConfigs());
1111 DefaultTreeModel repositoriesTreeModel =
1112 new DefaultTreeModel(repositorySet);
1113
1114 DeputyTree chooserTree =
1115 new DeputyTree(deputy, this, repositoriesTreeModel, null);
1116 chooserDialog =
1117 new ReplacementChooserDialog(chooserTree);
1118 chooserTree.expandRow(1);
1119
1120 currentRepositoriesTree =
1121 new DeputyTree(deputy, this, repositoriesTreeModel, chooserDialog);
1122 currentRepositoriesTree.expandRow(1);
1123 }
1124
1125 /***
1126 * Applies the rules on the project to compute the versions of the
1127 * direct dependencies and the indirect dependencies
1128 * (including their versions).
1129 */
1130 private void applyRules() {
1131 try {
1132 String mavenProjectFile = deputy.getProjectFile();
1133 if (mavenProjectFile != null) {
1134 doOpenProject(STRATEGY_APPLY_RULES);
1135 }
1136 } catch (Exception e) {
1137 e.printStackTrace();
1138 JOptionPane.showMessageDialog(
1139 this,
1140 "Encountered '" + e.getMessage() + "'",
1141 "Error",
1142 JOptionPane.ERROR_MESSAGE);
1143 }
1144 }
1145
1146 /***
1147 * Removes all the rules selected by the user if the user confirms.
1148 * Prompts with an info message if no rules were selected.
1149 */
1150 private void removeSelectedRules() {
1151 try {
1152 List selectedRules = currentProjectTree.getSelectedRuleNodes();
1153 if (selectedRules.size() == 0) {
1154 JOptionPane.showMessageDialog(
1155 this,
1156 "No rules selected. Nothing will be removed.",
1157 "Info",
1158 JOptionPane.INFORMATION_MESSAGE);
1159 } else {
1160 String questionPart = "the selected rule?";
1161 if (selectedRules.size() > 1) {
1162 questionPart =
1163 "the " + selectedRules.size() + " selected rules?";
1164 }
1165 int selectedOption = JOptionPane.showOptionDialog(
1166 this,
1167 "Do you really want to remove "
1168 + questionPart,
1169 "Remove Selected Rules",
1170 JOptionPane.YES_NO_OPTION,
1171 JOptionPane.QUESTION_MESSAGE,
1172 null,
1173 new Object[] {"Yes", "No"},
1174 null);
1175 if (selectedOption == 0) {
1176 RuleSet ruleSet = deputy.getRootProject().getRuleSet();
1177 Iterator iter = selectedRules.iterator();
1178 while (iter.hasNext()) {
1179 RuleTreeNode ruleNode = (RuleTreeNode) iter.next();
1180 ruleSet.remove(ruleNode.getRule());
1181 }
1182 }
1183 }
1184 } catch (Exception e) {
1185 e.printStackTrace();
1186 JOptionPane.showMessageDialog(
1187 this,
1188 "Encountered '" + e.getMessage() + "'",
1189 "Error",
1190 JOptionPane.ERROR_MESSAGE);
1191 }
1192 }
1193
1194 /***
1195 * Removes all the SNAPSHOT enforcement rules if the user confirms.
1196 */
1197 private void removeSnapshotEnforcementRules() {
1198 try {
1199 RuleSet ruleSet = deputy.getRootProject().getRuleSet();
1200 int numberOfRules = ruleSet.getNumberOfSnapshotEnforcements();
1201 if (numberOfRules > 0) {
1202 int selectedOption = JOptionPane.showOptionDialog(
1203 this,
1204 "Do you really want to remove all "
1205 + "SNAPSHOT enforcements?",
1206 "Remove SNAPSHOT Enforcement Rules",
1207 JOptionPane.YES_NO_OPTION,
1208 JOptionPane.QUESTION_MESSAGE,
1209 null,
1210 new Object[] {"Yes", "No"},
1211 null);
1212 if (selectedOption == 0) {
1213 ruleSet.removeAllSnapshotEnforcements();
1214 }
1215 } else {
1216 JOptionPane.showMessageDialog(
1217 this,
1218 "There are no SNAPSHOT enforcements to remove.",
1219 "No SNAPSHOT enforcement rules",
1220 JOptionPane.INFORMATION_MESSAGE);
1221 }
1222 } catch (Exception e) {
1223 e.printStackTrace();
1224 JOptionPane.showMessageDialog(
1225 this,
1226 "Encountered '" + e.getMessage() + "'",
1227 "Error",
1228 JOptionPane.ERROR_MESSAGE);
1229 }
1230 }
1231
1232 /***
1233 * Removes all the derived rules if the user confirms.
1234 */
1235 private void removeDerivedRules() {
1236 try {
1237 RuleSet ruleSet = deputy.getRootProject().getRuleSet();
1238 int numberOfRules = ruleSet.getNumberOfDerivedRules();
1239 if (numberOfRules > 0) {
1240 int selectedOption = JOptionPane.showOptionDialog(
1241 this,
1242 "Do you really want to remove all "
1243 + "derived rules?",
1244 "Remove Derived Rules",
1245 JOptionPane.YES_NO_OPTION,
1246 JOptionPane.QUESTION_MESSAGE,
1247 null,
1248 new Object[] {"Yes", "No"},
1249 null);
1250 if (selectedOption == 0) {
1251 ruleSet.removeAllDerivedRules();
1252 }
1253 } else {
1254 JOptionPane.showMessageDialog(
1255 this,
1256 "There are no derived rules to remove.",
1257 "No Derived rules",
1258 JOptionPane.INFORMATION_MESSAGE);
1259 }
1260 } catch (Exception e) {
1261 e.printStackTrace();
1262 JOptionPane.showMessageDialog(
1263 this,
1264 "Encountered '" + e.getMessage() + "'",
1265 "Error",
1266 JOptionPane.ERROR_MESSAGE);
1267 }
1268 }
1269
1270 /***
1271 * Saves the current project under the name that has been set using
1272 * {@link Deputy#setProjectFile(String)}.
1273 */
1274 private void save() {
1275 if (reallySave()) {
1276 try {
1277 File file = new File(deputy.getProjectFile());
1278 if (isFileWritable(file)) {
1279 deputy.saveProjectAs(file.getAbsolutePath());
1280 }
1281 } catch (Exception e) {
1282 e.printStackTrace();
1283 JOptionPane.showMessageDialog(
1284 this,
1285 "Encountered '" + e.getMessage() + "'",
1286 "Error",
1287 JOptionPane.ERROR_MESSAGE);
1288 }
1289 }
1290 }
1291
1292 /***
1293 * Prompts the user with a save as dialog and saves the file under the
1294 * selected path if the user doesn't cancel the dialog.
1295 */
1296 private void saveAs() {
1297 if (reallySave()) {
1298 try {
1299 fileChooser.setCurrentDirectory(
1300 new File(deputy.getProjectFile()));
1301 fileChooser.setSelectedFile(
1302 new File(deputy.getProjectFile()));
1303 int result =
1304 fileChooser.showSaveDialog(this);
1305 if (result == JFileChooser.APPROVE_OPTION) {
1306 File file = fileChooser.getSelectedFile();
1307 if (isFileWritable(file)) {
1308 deputy.saveProjectAs(file.getAbsolutePath());
1309 }
1310 }
1311 } catch (Exception e) {
1312 e.printStackTrace();
1313 JOptionPane.showMessageDialog(
1314 this,
1315 "Encountered '" + e.getMessage() + "'",
1316 "Error",
1317 JOptionPane.ERROR_MESSAGE);
1318 }
1319 }
1320 }
1321
1322 /***
1323 * Checks whether the file exists and is writable.
1324 * <p/>
1325 * Tries some very special magic to make it writable if it isn't
1326 * (asks the user if it shall be opened for edit in Perforce
1327 * if it is a Perforce controlled file on a Perforce system).
1328 *
1329 * @param file The file to check writability for.
1330 * @return if the file is writable
1331 */
1332 private boolean isFileWritable(final File file) {
1333 boolean result = true;
1334 if (file.exists() && !file.canWrite()) {
1335 result = false;
1336 P4 p4 = new P4();
1337 if (p4.isP4Controlled(file.getAbsolutePath())) {
1338 int selectedOption = JOptionPane.showOptionDialog(
1339 this,
1340 "The file is Perforce controlled "
1341 + "but not opened for edit yet. "
1342 + "Do you want Deputy to open it for edit now?",
1343 "Open for Edit?",
1344 JOptionPane.YES_NO_OPTION,
1345 JOptionPane.QUESTION_MESSAGE,
1346 null,
1347 new Object[] {"Yes", "No"},
1348 null);
1349 if (selectedOption == 0) {
1350 if (!p4.openForEdit(file.getAbsolutePath())) {
1351 JOptionPane.showMessageDialog(
1352 this,
1353 "Could not open file for edit!",
1354 "Error",
1355 JOptionPane.ERROR_MESSAGE);
1356 } else {
1357 deputy.getLog().log(
1358 Log.SEVERITY_INFO,
1359 "Opened "
1360 + file.getAbsolutePath()
1361 + " for edit.");
1362 result = true;
1363 }
1364 }
1365 }
1366 }
1367 if (!result) {
1368 JOptionPane.showMessageDialog(
1369 this,
1370 "The file cannot be saved, it is not writable!",
1371 "Error",
1372 JOptionPane.ERROR_MESSAGE);
1373 }
1374 return result;
1375 }
1376
1377 /***
1378 * Checks if the user wants to save without having applied the rules after
1379 * changes in the dependencies or rules.
1380 *
1381 * @return <code>true</code> if the user really wants to save the project.
1382 */
1383 private boolean reallySave() {
1384 boolean result = true;
1385 boolean isApplyRulesRecommended =
1386 (deputy.getLastChangeAction()
1387 == Deputy.CHANGE_ACTION_CHANGE_DEPENDENCIES)
1388 || (deputy.getLastChangeAction()
1389 == Deputy.CHANGE_ACTION_CHANGE_RULES);
1390 if (isApplyRulesRecommended) {
1391 int selectedOption = JOptionPane.showOptionDialog(
1392 this,
1393 "You added or removed dependencies or rules,"
1394 + " but you didn't apply the rules afterwards.\n"
1395 + "This means your configuration may be inconsistent with the "
1396 + "rules or the true dependency structure."
1397 + "\n"
1398 + "Do you really want to save without "
1399 + "applying the rules before?",
1400 "Save without applying the rules?",
1401 JOptionPane.YES_NO_OPTION,
1402 JOptionPane.QUESTION_MESSAGE,
1403 null,
1404 new Object[] {
1405 "Save without applying the rules",
1406 "Don't save" },
1407 null);
1408 if (selectedOption == 1) {
1409 result = false;
1410 }
1411 }
1412 return result;
1413 }
1414
1415 /***
1416 * Prompts the user with a save as dialog and exports the project under the
1417 * selected path as a dependency graph in XML format if the user doesn't
1418 * cancel the dialog.
1419 */
1420 private void exportDependencyGraph() {
1421 try {
1422 exportFileChooser.setCurrentDirectory(
1423 new File(deputy.getDependencyGraphFile()));
1424 exportFileChooser.setSelectedFile(
1425 new File(deputy.getDependencyGraphFile()));
1426 FileFilter[] filters =
1427 exportFileChooser.getChoosableFileFilters();
1428 for (int i = 0; i < filters.length; i++) {
1429 exportFileChooser.removeChoosableFileFilter(filters[i]);
1430 }
1431 if (deputy.getDependencyGraphFile().endsWith(".graphml")) {
1432 exportFileChooser.addChoosableFileFilter(deputyXmlFilter);
1433 exportFileChooser.addChoosableFileFilter(graphMlFilter);
1434 } else {
1435 exportFileChooser.addChoosableFileFilter(graphMlFilter);
1436 exportFileChooser.addChoosableFileFilter(deputyXmlFilter);
1437 }
1438 int result =
1439 exportFileChooser.showSaveDialog(this);
1440 if (result == JFileChooser.APPROVE_OPTION) {
1441 File file = exportFileChooser.getSelectedFile();
1442 String description =
1443 exportFileChooser.getFileFilter().getDescription();
1444 int selectedFormat = Deputy.FORMAT_DEPUTYXML;
1445 if (description.indexOf("graphml") != -1) {
1446 selectedFormat = Deputy.FORMAT_GRAPHML;
1447 }
1448 String absolutePath = file.getAbsolutePath();
1449 if (selectedFormat == Deputy.FORMAT_DEPUTYXML
1450 && !file.getName().endsWith(".xml")) {
1451 absolutePath = absolutePath + ".xml";
1452 } else if (selectedFormat == Deputy.FORMAT_GRAPHML
1453 && !file.getName().endsWith(".graphml")) {
1454 absolutePath = absolutePath + ".graphml";
1455 }
1456 deputy.setDependencyGraphFile(absolutePath);
1457 deputy.exportDependencyGraph(selectedFormat);
1458 }
1459 } catch (Exception e) {
1460 e.printStackTrace();
1461 JOptionPane.showMessageDialog(
1462 this,
1463 "Encountered '" + e.getMessage() + "'",
1464 "Error",
1465 JOptionPane.ERROR_MESSAGE);
1466 }
1467 }
1468
1469 /***
1470 * Sorts all SNAPSHOT dependencies topologically
1471 * and prints them to the log in this order.
1472 */
1473 private void sortSnapshotsTopologically() {
1474 try {
1475 deputy.sortSnapshotsTopologically();
1476 } catch (Exception e) {
1477 e.printStackTrace();
1478 JOptionPane.showMessageDialog(
1479 this,
1480 "Encountered '" + e.getMessage() + "'",
1481 "Error",
1482 JOptionPane.ERROR_MESSAGE);
1483 }
1484 }
1485
1486 /***
1487 * Adds a project's dependencies as derived enforcement rules.
1488 */
1489 private void deriveEnforcementsFromProject() {
1490 try {
1491 String projectFile = deputy.getImportFile();
1492 if (projectFile != null) {
1493 try {
1494 File file = new File(projectFile);
1495 fileChooser.setCurrentDirectory(file);
1496 fileChooser.setSelectedFile(file);
1497 } catch (Exception e) {
1498
1499 e.printStackTrace();
1500 }
1501 }
1502 int result =
1503 fileChooser.showOpenDialog(this);
1504 if (result == JFileChooser.APPROVE_OPTION) {
1505 File file = fileChooser.getSelectedFile();
1506 if (isValidProjectFile(file)) {
1507 logPanel.clear();
1508 deputy.deriveEnforcementsFromProject(
1509 file.getAbsolutePath());
1510 }
1511 }
1512 } catch (Exception e) {
1513 e.printStackTrace();
1514 JOptionPane.showMessageDialog(
1515 this,
1516 "Encountered '" + e.getMessage() + "'",
1517 "Error",
1518 JOptionPane.ERROR_MESSAGE);
1519 }
1520 }
1521
1522 /***
1523 * Prompts the user with a dialog to change the current list of configs of
1524 * the Maven repositories to use.
1525 * The user is informed that using the changed repository configs requires
1526 * a re-start of Deputy.
1527 */
1528 private void editRepositoryConfigs() {
1529 try {
1530 RepositoryConfigsEditor editor =
1531 new RepositoryConfigsEditor(
1532 this,
1533 deputy.getRepositoryConfigs());
1534 editor.setVisible(true);
1535 if (!editor.wasCancelled()) {
1536 deputy.setRepositoryConfigs(editor.getRepositoryConfigs());
1537 JOptionPane.showMessageDialog(
1538 this,
1539 "You must re-start Deputy to let the change "
1540 + "of repository configs become effective.",
1541 "Re-start Required",
1542 JOptionPane.INFORMATION_MESSAGE);
1543 }
1544 } catch (FileNotFoundException e) {
1545 e.printStackTrace();
1546 JOptionPane.showMessageDialog(
1547 this,
1548 "The paths you entered are not valid. "
1549 + "Ensure all paths exist and are accessible.",
1550 "Error",
1551 JOptionPane.ERROR_MESSAGE);
1552 } catch (Exception e) {
1553 e.printStackTrace();
1554 JOptionPane.showMessageDialog(
1555 this,
1556 "Encountered '" + e.getMessage() + "'",
1557 "Error",
1558 JOptionPane.ERROR_MESSAGE);
1559 }
1560 }
1561
1562 /***
1563 * Switches on/off the usage of the virtual repository in the dependency
1564 * recursion. Updates the appaearance of the toggle button according to the
1565 * state.
1566 *
1567 * @param active The selected state to propagate.
1568 */
1569 private void setVirtualRepositoryActive(final boolean active) {
1570 if (active) {
1571 virtualRepositoryToggleButton.setIcon(virtualRepositoryOnIcon);
1572 virtualRepositoryToggleButton.setText(
1573 "Virtual Repository Active");
1574 } else {
1575 virtualRepositoryToggleButton.setIcon(virtualRepositoryOffIcon);
1576 virtualRepositoryToggleButton.setText(
1577 "Virtual Repository Inactive");
1578 }
1579 deputy.setVirtualRepositoryActive(active);
1580 }
1581
1582 /***
1583 * Exits the Deputy application. Warns if the current state of the project
1584 * has not been saved.
1585 */
1586 private void exit() {
1587 boolean doExit = true;
1588 if (!deputy.isSaved()) {
1589 int selectedOption = JOptionPane.showOptionDialog(
1590 this,
1591 "The current state of the project has not been saved.\n"
1592 + "Do you really want to exit without saving it?",
1593 "Exit",
1594 JOptionPane.YES_NO_OPTION,
1595 JOptionPane.QUESTION_MESSAGE,
1596 null,
1597 new Object[] {"Exit without saving", "Don't exit"},
1598 null);
1599 if (selectedOption == 0) {
1600 doExit = true;
1601 } else {
1602 doExit = false;
1603 }
1604 }
1605 if (doExit) {
1606 deputy.setProperty("deputyFrame.x", getX() + "");
1607 deputy.setProperty("deputyFrame.y", getY() + "");
1608 deputy.setProperty("deputyFrame.height", getHeight() + "");
1609 deputy.setProperty("deputyFrame.width", getWidth() + "");
1610 deputy.setProperty(
1611 "deputyFrame.horizontalSplitPane.dividerLocation",
1612 horizontalSplitPane.getDividerLocation() + "");
1613 deputy.setProperty(
1614 "deputyFrame.verticalSplitPane.dividerLocation",
1615 verticalSplitPane.getDividerLocation() + "");
1616 deputy.exitApplication();
1617 }
1618 }
1619
1620 /***
1621 * Launches a browser to display a URL.
1622 *
1623 * @param url The URL to display.
1624 */
1625 private void openUrl(final String url) {
1626 BrowserLauncher.openUrl(url);
1627 }
1628
1629 /***
1630 * Shows the about dialog of Deputy.
1631 */
1632 private void showAbout() {
1633 JOptionPane.showMessageDialog(
1634 this,
1635 "Version: " + deputy.getVersion() + "\n\n"
1636 + "(c) 2005, 2006, 2007 Matthias Burbach. All rights reserved.\n\n"
1637 + "This open source product is licensed under the BSD licence\n"
1638 + "and includes software developed by other open source "
1639 + "initiatives."
1640 ,
1641 "About Deputy",
1642 JOptionPane.INFORMATION_MESSAGE);
1643 }
1644
1645 /***
1646 * Starts the application.
1647 *
1648 * @param args Optional command line argument is -project=<file name>
1649 * which will immediately load that file into Deputy on start
1650 * up.
1651 */
1652 public static void main(final String[] args) {
1653 try {
1654 Deputy deputy = new Deputy(new SimpleLog());
1655 DeputyFrame deputyFrame = new DeputyFrame(deputy);
1656
1657 if (args.length > 0 && args[0] != null) {
1658 if (args[0].startsWith("-project=")) {
1659 String projectFileName =
1660 args[0].substring("-project=".length());
1661 if ((new File(projectFileName)).exists()) {
1662 deputy.setProjectFile(projectFileName);
1663 deputyFrame.doOpenProject(STRATEGY_AS_IS);
1664 } else {
1665 throw new IllegalArgumentException();
1666 }
1667 } else {
1668 throw new IllegalArgumentException();
1669 }
1670 }
1671 } catch (IllegalArgumentException e) {
1672 System.err.println(
1673 "Invalid arguments, usage is: "
1674 + DeputyFrame.class.getName()
1675 + " [-project=<absolute path of Maven project file>]");
1676 } catch (Exception e) {
1677 e.printStackTrace();
1678 }
1679 }
1680 }