View Javadoc

1   /*
2    * jGuild Project: jRPM
3    * Released under the Apache License ( http://www.apache.org/LICENSE )
4    */
5   package com.jguild.jrpm.ui;
6   
7   import java.awt.BorderLayout;
8   import java.awt.Component;
9   import java.awt.Container;
10  import java.awt.Dimension;
11  import java.awt.GridBagConstraints;
12  import java.awt.GridBagLayout;
13  import java.awt.event.ActionEvent;
14  import java.awt.event.ActionListener;
15  import java.awt.event.KeyEvent;
16  import java.awt.event.WindowAdapter;
17  import java.awt.event.WindowEvent;
18  import java.io.File;
19  import java.io.FileFilter;
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Comparator;
23  import java.util.Enumeration;
24  import java.util.Hashtable;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Set;
28  import java.util.StringTokenizer;
29  import java.util.TreeSet;
30  import java.util.Vector;
31  
32  import javax.swing.BoxLayout;
33  import javax.swing.DefaultComboBoxModel;
34  import javax.swing.JComboBox;
35  import javax.swing.JDialog;
36  import javax.swing.JFileChooser;
37  import javax.swing.JFrame;
38  import javax.swing.JLabel;
39  import javax.swing.JMenu;
40  import javax.swing.JMenuBar;
41  import javax.swing.JMenuItem;
42  import javax.swing.JOptionPane;
43  import javax.swing.JPanel;
44  import javax.swing.JProgressBar;
45  import javax.swing.JScrollPane;
46  import javax.swing.JSplitPane;
47  import javax.swing.JTextArea;
48  import javax.swing.JTree;
49  import javax.swing.ToolTipManager;
50  import javax.swing.event.TreeModelEvent;
51  import javax.swing.event.TreeModelListener;
52  import javax.swing.event.TreeSelectionEvent;
53  import javax.swing.event.TreeSelectionListener;
54  import javax.swing.tree.DefaultTreeCellRenderer;
55  import javax.swing.tree.TreeModel;
56  import javax.swing.tree.TreePath;
57  
58  import org.apache.log4j.Logger;
59  import org.apache.log4j.varia.NullAppender;
60  
61  import com.jguild.jrpm.io.RPMFile;
62  import com.jguild.jrpm.io.datatype.DataTypeIf;
63  
64  /***
65   * A browser to search for rpm files in a given directory. The rpms are schown
66   * in a tree. The tree display the in rpm defined groups. So every rpm that
67   * point to the same group are sorted into the same node in the tree. This
68   * browser should demonstrate the functionality of jRPM.
69   * 
70   * @author kuss
71   */
72  public class RPMBrowser extends JFrame implements ActionListener {
73  
74      private InfoPanel infoPanel = new InfoPanel(new String[] { "name",
75              "version", "release", "summary", "description", "vendor", "url",
76              "copyright", "distribution", "disturl", "filenames"});
77  
78      private RPMGroupTreeModel treeModel = new RPMGroupTreeModel();
79  
80      /***
81       * Creates a new RPMBrowser object.
82       */
83      public RPMBrowser() {
84          setTitle("RPM Browser");
85          setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
86          setSize(new Dimension(600, 400));
87          setJMenuBar(createMenuBar());
88  
89          Container content = getContentPane();
90          JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
91                  new JScrollPane(createGroupTree()), new JScrollPane(
92                          createRPMInfoPane()));
93          splitPane.setDividerLocation(200);
94          content.add(splitPane);
95  
96          //Center on screen
97          setLocationRelativeTo(null);
98  
99          addWindowListener(new WindowAdapter() {
100 
101             public void windowClosing(WindowEvent e) {
102                 quit();
103             }
104         });
105     }
106 
107     /*
108      * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
109      */
110     public void actionPerformed(ActionEvent e) {
111         if (e.getActionCommand().equals("Exit")) {
112             quit();
113         } else if (e.getActionCommand().equals("ChooseDir")) {
114             chooseBaseDir();
115         }
116     }
117 
118     /***
119      * Main method to start the RPMBrowser
120      * 
121      * @param args
122      *           The start arguments
123      */
124     public static void main(String[] args) {
125         Locale.setDefault(Locale.ENGLISH);
126 
127         if (!Logger.getLogger("com.jguild.jrpm").getAllAppenders()
128                 .hasMoreElements()) {
129             Logger.getLogger("com.jguild.jrpm").addAppender(new NullAppender());
130         }
131 
132         RPMBrowser frame = new RPMBrowser();
133         frame.setVisible(true);
134     }
135 
136     private List getRPMFiles(File dir, MyMonitor monitor) {
137         List rpms = new ArrayList();
138         File[] list = dir.listFiles(new FileFilter() {
139 
140             public boolean accept(File f) {
141                 return f.isDirectory()
142                         || f.getName().toLowerCase().endsWith(".rpm");
143             }
144         });
145 
146         monitor.setMaximum(monitor.getMaximum() + list.length);
147 
148         for (int pos = 0; pos < list.length; pos++) {
149             if (list[pos].isDirectory()) {
150                 rpms.addAll(getRPMFiles(list[pos], monitor));
151             } else {
152                 rpms.add(list[pos]);
153             }
154 
155             monitor.incProgress();
156         }
157 
158         Thread.yield();
159 
160         return rpms;
161     }
162 
163     private void chooseBaseDir() {
164         JFileChooser dirChooser = new JFileChooser();
165         dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
166 
167         if (dirChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
168             createRPMTree(dirChooser.getSelectedFile());
169         }
170     }
171 
172     private JTree createGroupTree() {
173         JTree tree = new JTree(treeModel);
174         ToolTipManager.sharedInstance().registerComponent(tree);
175         tree.setCellRenderer(new MyRenderer());
176         tree.addTreeSelectionListener(new TreeSelectionListener() {
177 
178             public void valueChanged(TreeSelectionEvent e) {
179                 JTree tree = (JTree) e.getSource();
180                 Object node = tree.getLastSelectedPathComponent();
181 
182                 if (node instanceof RPMNode) {
183                     infoPanel.displayRPM(((RPMNode) node).getRPM());
184                 }
185             }
186         });
187 
188         return tree;
189     }
190 
191     private JMenuBar createMenuBar() {
192         JMenuBar menuBar;
193         JMenu menu;
194         JMenuItem menuItem;
195 
196         menuBar = new JMenuBar();
197 
198         menu = new JMenu("File");
199         menu.setMnemonic(KeyEvent.VK_F);
200         menuBar.add(menu);
201 
202         menuItem = new JMenuItem("Choose dir ...", KeyEvent.VK_C);
203         menuItem.setActionCommand("ChooseDir");
204         menuItem.addActionListener(this);
205         menu.add(menuItem);
206         menu.addSeparator();
207 
208         menuItem = new JMenuItem("Exit", KeyEvent.VK_E);
209         menuItem.setActionCommand("Exit");
210         menuItem.addActionListener(this);
211         menu.add(menuItem);
212 
213         return menuBar;
214     }
215 
216     private JPanel createRPMInfoPane() {
217         return infoPanel;
218     }
219 
220     /***
221      * @param file
222      */
223     private void createRPMTree(final File f) {
224         setEnabled(false);
225 
226         final Thread thread = new Thread() {
227 
228             public void run() {
229                 MyMonitor monitor = new MyMonitor(RPMBrowser.this, "Progress",
230                         "<html>Searching rpm files in '" + f.getPath()
231                                 + "'<br>and all subdirectories ...<br></html>",
232                         1);
233                 List rpms = getRPMFiles(f, monitor);
234                 treeModel.setRoot(new GroupNode(f.getPath()));
235                 monitor.setVisible(false);
236 
237                 monitor.setMaximum(rpms.size());
238                 monitor.setProgress(0);
239                 monitor
240                         .setMessage("<html>Reading RPM informations ...<br></html>");
241                 monitor.pack();
242                 monitor.setVisible(true);
243 
244                 for (int pos = 0; pos < rpms.size(); pos++) {
245                     monitor.incProgress();
246 
247                     try {
248                         RPMFile rpm = new RPMFile((File) rpms.get(pos));
249                         rpm.parse();
250                         GroupNode node = treeModel.putGroup(rpm.getTag("group")
251                                 .toString());
252 
253                         if (node.addRPMNode(new RPMNode(rpm, (File) rpms
254                                 .get(pos)))) {
255                             treeModel.fireTreeStructureChanged(node);
256                         }
257                     } catch (IOException e) {
258                         //Ignore this
259                     }
260                 }
261 
262                 monitor.setVisible(false);
263                 RPMBrowser.this.setEnabled(true);
264             }
265         };
266 
267         thread.start();
268     }
269 
270     private void quit() {
271         if (quitConfirmed()) {
272             System.exit(0);
273         }
274     }
275 
276     private boolean quitConfirmed() {
277         String s1 = "Quit";
278         String s2 = "Cancel";
279         Object[] options = { s1, s2};
280         int n = JOptionPane.showOptionDialog(this,
281                 "Do you really want to quit?", "Quit Confirmation",
282                 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
283                 options, s1);
284 
285         if (n == JOptionPane.YES_OPTION) {
286             return true;
287         } else {
288             return false;
289         }
290     }
291 
292     class MyRenderer extends DefaultTreeCellRenderer {
293 
294         public MyRenderer() {
295         }
296 
297         public Component getTreeCellRendererComponent(JTree tree, Object value,
298                 boolean sel, boolean expanded, boolean leaf, int row,
299                 boolean hasFocus) {
300             super.getTreeCellRendererComponent(tree, value, sel, expanded,
301                     leaf, row, hasFocus);
302 
303             if (value instanceof RPMNode) {
304                 RPMNode node = (RPMNode) value;
305                 File[] file = node.getPath();
306                 StringBuffer buf = new StringBuffer();
307                 buf.append("<html><B>Locations:</B><BR>");
308 
309                 for (int pos = 0; pos < file.length; pos++) {
310                     buf.append(file[pos]);
311 
312                     if (pos < (file.length - 1)) {
313                         buf.append("<BR>");
314                     }
315                 }
316 
317                 buf.append("<html>");
318                 setToolTipText(buf.toString());
319             } else {
320                 setToolTipText(null);
321             }
322 
323             return this;
324         }
325     }
326 
327     private class GroupNode {
328 
329         private Comparator comp = new Comparator() {
330 
331             public int compare(Object o1, Object o2) {
332                 if ((o1 == null) && (o2 == null)) { return 0; }
333 
334                 if (o1 == null) { return -1; }
335 
336                 if (o2 == null) { return 1; }
337 
338                 return o1.toString().compareTo(o2.toString());
339             }
340         };
341 
342         private Set groups = new TreeSet(comp);
343 
344         private Set rpms = new TreeSet(comp);
345 
346         private String name;
347 
348         public GroupNode(String name) {
349             this.name = name;
350         }
351 
352         public Object getChild(int count) {
353             Object ret = null;
354 
355             if (count < groups.size()) {
356                 ret = groups.toArray()[count];
357             } else {
358                 ret = rpms.toArray()[count - groups.size()];
359             }
360 
361             return ret;
362         }
363 
364         public int getChildCount() {
365             return rpms.size() + groups.size();
366         }
367 
368         public boolean isLeaf() {
369             return (rpms.size() == 0) && (groups.size() == 0);
370         }
371 
372         public boolean addGroupNode(GroupNode node) {
373             if (groups.contains(node)) { return false; }
374 
375             groups.add(node);
376 
377             return true;
378         }
379 
380         public boolean addRPMNode(RPMNode node) {
381             if (rpms.contains(node)) {
382                 ArrayList rpmList = new ArrayList(rpms);
383                 RPMNode rpmnode = (RPMNode) rpmList.get(rpmList.indexOf(node));
384                 File[] paths = node.getPath();
385 
386                 for (int pos = 0; pos < paths.length; pos++) {
387                     rpmnode.addPath(paths[pos]);
388                 }
389 
390                 return false;
391             }
392 
393             rpms.add(node);
394 
395             return true;
396         }
397 
398         public boolean equals(Object obj) {
399             return name.equals(obj.toString());
400         }
401 
402         public int hashCode() {
403             return name.hashCode();
404         }
405 
406         /***
407          * @param child
408          * @return
409          */
410         public int indexOf(Object child) {
411             int ret = -1;
412 
413             if ((ret = (new ArrayList(groups)).indexOf(child)) < 0) {
414                 ret = (new ArrayList(rpms)).indexOf(child);
415 
416                 if (ret >= 0) {
417                     ret += groups.size();
418                 }
419             }
420 
421             return ret;
422         }
423 
424         public String toString() {
425             return name;
426         }
427     }
428 
429     private class InfoPanel extends JPanel implements ActionListener {
430 
431         private DefaultComboBoxModel model = new DefaultComboBoxModel();
432 
433         private Hashtable tagLabels = new Hashtable();
434 
435         private JComboBox langBox = new JComboBox();
436 
437         private RPMFile rpmFile = null;
438 
439         public InfoPanel(String[] tags) {
440             langBox.setModel(model);
441 
442             GridBagLayout gridLayout = new GridBagLayout();
443             GridBagConstraints c = new GridBagConstraints();
444             c.fill = GridBagConstraints.VERTICAL;
445             c.anchor = GridBagConstraints.NORTHWEST;
446 
447             setLayout(gridLayout);
448 
449             langBox.setEditable(false);
450             langBox.addActionListener(this);
451             c.gridy = 0;
452 
453             JLabel langLabel = new JLabel("Language : ");
454             gridLayout.setConstraints(langLabel, c);
455             add(langLabel);
456             c.gridx = 1;
457             gridLayout.setConstraints(langBox, c);
458             add(langBox);
459             c.gridx = 0;
460             c.gridwidth = 2;
461             c.fill = GridBagConstraints.HORIZONTAL;
462             c.weightx = 1.0;
463 
464             for (int pos = 0; pos < tags.length; pos++) {
465                 c.gridy = pos + 1;
466 
467                 TagLabel tagLabel = new TagLabel(tags[pos]);
468                 gridLayout.setConstraints(tagLabel, c);
469                 add(tagLabel);
470                 tagLabels.put(tags[pos], tagLabel);
471             }
472 
473             c.weightx = 0;
474             c.weighty = 1.0;
475 
476             JPanel fill = new JPanel();
477             gridLayout.setConstraints(fill, c);
478             add(fill);
479         }
480 
481         /*
482          * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
483          */
484         public void actionPerformed(ActionEvent e) {
485             if (rpmFile != null) {
486                 String locale = (String) langBox.getSelectedItem();
487 
488                 if (locale != null) {
489                     rpmFile.setLocale(locale);
490                 }
491 
492                 updateView();
493             }
494         }
495 
496         public void displayRPM(RPMFile rpm) {
497             rpmFile = rpm;
498 
499             langBox.setSelectedIndex(-1);
500             model.removeAllElements();
501 
502             String[] langs = rpmFile.getLocales();
503 
504             for (int pos = 0; pos < langs.length; pos++) {
505                 model.addElement(langs[pos]);
506             }
507 
508             updateView();
509         }
510 
511         private void updateView() {
512             Enumeration keys = tagLabels.keys();
513 
514             while (keys.hasMoreElements()) {
515                 String tagName = (String) keys.nextElement();
516                 TagLabel label = (TagLabel) tagLabels.get(tagName);
517                 DataTypeIf tagValue = rpmFile.getTag(tagName);
518 
519                 if (tagValue == null) {
520                     label.setValue("(none)");
521                 } else {
522                     label.setValue(tagValue.toString());
523                 }
524             }
525         }
526     }
527 
528     private class MyMonitor extends JDialog {
529 
530         private JLabel label;
531 
532         private JProgressBar bar;
533 
534         public MyMonitor(JFrame parentComponent, String title, String message,
535                 int max) {
536             super(parentComponent);
537             setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
538             setTitle(title);
539 
540             Container content = getContentPane();
541             content.setLayout(new BorderLayout());
542             bar = new JProgressBar(0, max);
543             content.add(bar, BorderLayout.CENTER);
544             label = new JLabel(message);
545             content.add(label, BorderLayout.NORTH);
546             pack();
547             setLocationRelativeTo(parentComponent);
548             setVisible(true);
549         }
550 
551         public void setMaximum(int max) {
552             bar.setMaximum(max);
553         }
554 
555         public int getMaximum() {
556             return bar.getMaximum();
557         }
558 
559         public void setMessage(String message) {
560             label.setText(message);
561         }
562 
563         public void setProgress(int p) {
564             bar.setValue(p);
565         }
566 
567         public void incProgress() {
568             bar.setValue(bar.getValue() + 1);
569         }
570     }
571 
572     private class RPMGroupTreeModel implements TreeModel {
573 
574         private GroupNode root = null;
575 
576         private Hashtable groups = new Hashtable();
577 
578         private Vector treeModelListeners = new Vector();
579 
580         public Object getChild(Object parent, int index) {
581             Object ret = null;
582 
583             if (parent instanceof GroupNode) {
584                 ret = ((GroupNode) parent).getChild(index);
585             }
586 
587             return ret;
588         }
589 
590         public int getChildCount(Object parent) {
591             int count = 0;
592 
593             if (parent instanceof GroupNode) {
594                 count = ((GroupNode) parent).getChildCount();
595             }
596 
597             return count;
598         }
599 
600         public int getIndexOfChild(Object parent, Object child) {
601             int ret = -1;
602 
603             if (parent instanceof GroupNode) {
604                 ret = ((GroupNode) parent).indexOf(child);
605             }
606 
607             return ret;
608         }
609 
610         public boolean isLeaf(Object node) {
611             boolean isLeaf = true;
612 
613             if (node instanceof GroupNode) {
614                 isLeaf = ((GroupNode) node).isLeaf();
615             }
616 
617             return isLeaf;
618         }
619 
620         public void setRoot(GroupNode root) {
621             GroupNode oldRoot = this.root;
622             this.root = root;
623             groups = new Hashtable();
624 
625             if (oldRoot != null) {
626                 fireTreeStructureChanged(oldRoot);
627             }
628         }
629 
630         public Object getRoot() {
631             return root;
632         }
633 
634         public void addTreeModelListener(TreeModelListener l) {
635             treeModelListeners.addElement(l);
636         }
637 
638         public void fireTreeStructureChanged(Object obj) {
639             int len = treeModelListeners.size();
640             TreeModelEvent e = new TreeModelEvent(this, new Object[] { obj});
641 
642             for (int i = 0; i < len; i++) {
643                 ((TreeModelListener) treeModelListeners.elementAt(i))
644                         .treeStructureChanged(e);
645             }
646         }
647 
648         public GroupNode putGroup(String path) {
649             GroupNode group = null;
650             StringTokenizer pathToken = new StringTokenizer(path, "/");
651 
652             //try to get existing group
653             GroupNode parent = root;
654             String tmp = "";
655             String groupName = null;
656 
657             while (pathToken.hasMoreTokens()) {
658                 groupName = pathToken.nextToken();
659                 tmp += groupName;
660 
661                 if (!groups.containsKey(tmp)) {
662                     group = new GroupNode(groupName);
663                     groups.put(tmp, group);
664                 } else {
665                     group = (GroupNode) groups.get(tmp);
666                 }
667 
668                 if (parent.addGroupNode(group)) {
669                     fireTreeStructureChanged(parent);
670                 }
671 
672                 parent = group;
673                 tmp += "/";
674             }
675 
676             return group;
677         }
678 
679         public void removeTreeModelListener(TreeModelListener l) {
680             treeModelListeners.removeElement(l);
681         }
682 
683         public void valueForPathChanged(TreePath path, Object newValue) {
684             throw new UnsupportedOperationException();
685         }
686     }
687 
688     private class RPMNode {
689 
690         private RPMFile rpm;
691 
692         private Vector paths = new Vector();
693 
694         public RPMNode(RPMFile rpm, File path) {
695             this.rpm = rpm;
696             paths.add(path);
697         }
698 
699         public File[] getPath() {
700             return (File[]) paths.toArray(new File[0]);
701         }
702 
703         public RPMFile getRPM() {
704             return rpm;
705         }
706 
707         public void addPath(File f) {
708             if (!paths.contains(f)) {
709                 paths.add(f);
710             }
711         }
712 
713         public boolean equals(Object obj) {
714             return toString().equals(obj.toString());
715         }
716 
717         public int hashCode() {
718             return toString().hashCode();
719         }
720 
721         public String toString() {
722             return rpm.getTag("name").toString() + "-"
723                     + rpm.getTag("version").toString() + "-"
724                     + rpm.getTag("release").toString();
725         }
726     }
727 
728     private class TagLabel extends JPanel {
729 
730         private JLabel label = new JLabel();
731 
732         private JTextArea value = new JTextArea();
733 
734         public TagLabel(String tagName) {
735             setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
736             label.setText(tagName);
737             label.setAlignmentX(LEFT_ALIGNMENT);
738             value.setAlignmentX(LEFT_ALIGNMENT);
739             add(label);
740             add(value);
741             setAlignmentX(LEFT_ALIGNMENT);
742         }
743 
744         public void setValue(String valueStr) {
745             value.setText(valueStr);
746         }
747     }
748 }