1 /***
2 * ClassDepView.java
3 *
4 * Project: Dependency Tool
5 *
6 * WHEN WHO WHAT
7 * 06.06.2003 pko initial public release
8 * 14.01.2003 pko modification
9 * 22.07.2002 ctr modification
10 * 18.06.2002 ctr creation
11 *
12 * Copyright 2003 ELCA Informatique SA
13 * Av. de la Harpe 22-24, 1000 Lausanne 13, Switzerland
14 * www.elca.ch
15 *
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public License
18 * as published by the Free Software Foundation; either version 2.1 of
19 * the License, or (at your option) any later version.
20 *
21 * This library is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
25 *
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 * USA
30 */
31
32 package ch.elca.dependency.view;
33
34 import att.grappa.*;
35 import ch.elca.dependency.core.DependencyModel;
36 import ch.elca.dependency.core.classinfo.ClassInfo;
37 import ch.elca.dependency.util.IconGrabber;
38
39 import java.awt.*;
40 import java.awt.event.*;
41 import java.util.*;
42 import javax.swing.*;
43 import javax.swing.event.*;
44 import javax.swing.tree.*;
45
46 import org.apache.log4j.Logger;
47 import info.clearthought.layout.TableLayout;
48
49 /***
50 * Tree with class dependencies.
51 *
52 * @see ch.elca.dependency.view.View
53 *
54 * @author Christoph Trutmann
55 * @version 1.0-beta
56 */
57 public class ClassDepView extends View {
58
59 //****************************************************************************************/
60 // static fields
61 //****************************************************************************************/
62
63 /***
64 * Log4j Logger.
65 */
66 private static final Logger LOG = Logger.getLogger(ClassDepView.class);
67
68 /***
69 * Title used for this Frame.
70 */
71 private final static String FRAME_TITLE = "Class Dependency View";
72
73 //****************************************************************************************/
74 // instance fields
75 //****************************************************************************************/
76
77 /***
78 * <code>ScrollPane</code> holding the tree. Supports add and
79 * remove for changing the tree.
80 */
81 private JScrollPane m_treeView;
82
83 /***
84 * Tree holding the class 'uses' dependencies.
85 */
86 private JTree m_usesTree;
87
88 /***
89 * Root of the 'uses' tree.
90 */
91 private MyTreeNode m_usesTreeRoot;
92
93 /***
94 * Tree holding the class 'is used' dependencies.
95 */
96 private JTree m_isUsedTree;
97
98 /***
99 * Root of the 'is used' tree.
100 */
101 private MyTreeNode m_isUsedTreeRoot;
102
103 /***
104 * Flag specifying whether the 'uses' or the 'is used'
105 * dependencies are shown.
106 */
107 private boolean m_uses = true;
108
109 /***
110 * HashMap containing all the <code>ClassInfo</code> objects of this
111 * project. The key is the full qualified class name.
112 */
113 private HashMap m_classInfos;
114
115 /***
116 * Reference to the raw graph.
117 */
118 private Graph m_graph;
119
120 /***
121 * Label is displayed when no package is selected.
122 */
123 private JLabel m_emptyLabel = new JLabel(" Please select a package...");
124
125 /***
126 * Map containing the package dependencies. This is used for
127 * checking if a package is intern or extern of the project.
128 */
129 private HashMap m_packDepend;
130
131 /***
132 * Creates a new <code>ClassDepView</code> instance.
133 *
134 * @param dependencyModel a <code>DependencyModel</code> value
135 */
136 public ClassDepView(DependencyModel dependencyModel) {
137 super(dependencyModel, FRAME_TITLE);
138 setDefaultBounds(new Rectangle(165, 564, 330, 297));
139 recallConfig();
140 }
141
142 protected void internalInitData() {
143
144 // create the HashMap with all the ClassInfo objects
145 //
146 m_classInfos = new HashMap();
147 Iterator infoIter = m_dependencyModel.getRawClassInfos().values().iterator();
148 while ( infoIter.hasNext() ) {
149 ListIterator infoListIter = ((ArrayList)infoIter.next()).listIterator();
150 while ( infoListIter.hasNext() ) {
151 ClassInfo infoTmp = (ClassInfo)infoListIter.next();
152 m_classInfos.put(infoTmp.getFullName(), infoTmp);
153 }
154 }
155
156 m_graph = m_dependencyModel.getRawGraph();
157 m_packDepend = m_dependencyModel.getPackDepend();
158 }
159
160 /***
161 * Init this view.
162 */
163 public void initView(){
164
165 // create the MenuBar
166 //
167 final JMenuBar menuBar = new JMenuBar();
168 setJMenuBar(menuBar);
169
170 // build the menu for the dependency direction
171 //
172 JMenu dirMenu = new JMenu("Dependency Direction");
173 dirMenu.setMnemonic(KeyEvent.VK_D);
174 menuBar.add(dirMenu);
175
176 // button group for the checkboxes
177 //
178 ButtonGroup group = new ButtonGroup();
179
180 // uses direction MenuItem
181 //
182 JRadioButtonMenuItem usesRadioItem = new JRadioButtonMenuItem("Uses");
183 usesRadioItem.setSelected(true);
184 usesRadioItem.setMnemonic(KeyEvent.VK_U);
185 group.add(usesRadioItem);
186 usesRadioItem.addActionListener(new ActionListener() {
187 public void actionPerformed(ActionEvent e) {
188 LOG.info("Change tree to 'uses'...");
189 m_uses = true;
190 changeTreeDepend();
191 }
192 });
193 dirMenu.add(usesRadioItem);
194
195 // is used direction MenuItem
196 //
197 JRadioButtonMenuItem isUsedRadioItem
198 = new JRadioButtonMenuItem("Is used");
199 isUsedRadioItem.setMnemonic(KeyEvent.VK_I);
200 group.add(isUsedRadioItem);
201 isUsedRadioItem.addActionListener(new ActionListener() {
202 public void actionPerformed(ActionEvent e){
203 LOG.info("Change tree to 'is uses'...");
204 m_uses = false;
205 changeTreeDepend();
206 }
207 });
208 dirMenu.add(isUsedRadioItem);
209
210 // build the menu for selecting something
211 //
212 JMenu selectMenu = new JMenu("Select");
213 selectMenu.setMnemonic(KeyEvent.VK_S);
214 menuBar.add(selectMenu);
215
216 // build the menu item for selecting a package
217 //
218 JMenuItem packItem = new JMenuItem("Select package", KeyEvent.VK_P);
219 packItem.addActionListener(new ActionListener() {
220 public void actionPerformed(ActionEvent e) {
221 LOG.info("Select a new package for the views.");
222 selectNewPackage();
223 }
224 });
225 selectMenu.add(packItem);
226
227 // configure TableLayout
228 //
229 double border = 10;
230 double fill = TableLayout.FILL;
231 double size[][] =
232 {{border, fill, border}, // Columns
233 {border, fill, border}}; // Rows
234
235 this.getContentPane().setLayout(new TableLayout(size));
236
237 m_treeView = new JScrollPane(m_emptyLabel);
238 getContentPane().add(m_treeView, "1, 1, F, F");
239 setBackground(menuBar.getBackground());
240
241 setVisible(true);
242 validate();
243
244 }
245
246 /***
247 * Helper method for constructing the two trees.
248 *
249 * @param root Root node of this tree.
250 * @return Tree with the appropriate parameters.
251 */
252 private JTree constructTree(MyTreeNode root, String mode){
253 JTree tree = new JTree(root);
254 // only one node can be selected at the same time
255 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
256 tree.addTreeSelectionListener(new MyTreeSelectionListener());
257 tree.addTreeExpansionListener(new MyTreeExpansionListener(mode, this));
258 // determine the style of the table
259 tree.putClientProperty("JTree.lineStyle", "Angled");
260 tree.setCellRenderer(new MyRenderer());
261 return tree;
262 }
263
264 /***
265 * Displays the <code>OverView</code> after the data has changed.
266 * This mehtod is individually implemented in each view.
267 *
268 * @tbd improve display method
269 */
270 public void display() {
271 validate();
272 setVisible(true);
273 }
274
275 /***
276 * Selects all the packages in the specified list for all the views
277 * implementing the <code>Observer</code> interface.
278 *
279 * @param selectList List containing all the selected packages.
280 */
281 public void select(ArrayList selectList){
282
283 // just selct one package
284 //
285 if ( selectList.size() < 1 ) {
286 clearSelection();
287 return;
288 }
289
290 // clear all the old selections in the uses tree
291 //
292 if ( m_usesTree != null ) {
293 m_usesTree.getSelectionModel()
294 .removeSelectionPaths(m_usesTree.getSelectionModel().getSelectionPaths());
295 }
296
297 // clear all the old selections in the is used tree
298 //
299 if ( m_isUsedTree != null ) {
300 m_isUsedTree.getSelectionModel()
301 .removeSelectionPaths(m_isUsedTree.getSelectionModel().getSelectionPaths());
302 }
303
304 LOG.info("class: " + selectList.get(0));
305
306 // create root nodes for the two trees
307 //
308 m_usesTreeRoot = new MyTreeNode("'uses' class dependencies");
309 m_isUsedTreeRoot = new MyTreeNode("'is used' class dependencies");
310
311 boolean usesFlag = m_uses; // temporary storing flag
312
313 // construct the trees with the classes of this package
314 //
315 m_uses = true;
316 constructRootNodes(m_usesTreeRoot, (String)selectList.get(0));
317 m_uses = false; // change mode for initialisation
318 constructRootNodes(m_isUsedTreeRoot, (String)selectList.get(0));
319
320 // reset flag
321 //
322 m_uses = usesFlag;
323 m_usesTree = constructTree(m_usesTreeRoot, "uses");
324 m_isUsedTree = constructTree(m_isUsedTreeRoot, "is used");
325
326 // construct uses tree and open first children
327 //
328 int childCount = m_usesTreeRoot.getChildCount();
329 if ( childCount == 0) {
330 return;
331 }
332
333 // open the tree to the first children
334 //
335 TreeNode[] nodes1 = new TreeNode[2];
336 nodes1[0] = m_usesTreeRoot;
337 nodes1[1] = m_usesTreeRoot.getChildAt(0);
338 m_usesTree.getSelectionModel().addSelectionPath(new TreePath(nodes1));
339 m_usesTree.clearSelection();
340
341 // construct is used tree and open first children
342 //
343 childCount = m_isUsedTreeRoot.getChildCount();
344 if ( childCount == 0 ) {
345 return;
346 }
347
348 // open the tree to the first children
349 //
350 TreeNode[] nodes2 = new TreeNode[2];
351 nodes2[0] = m_usesTreeRoot;
352 nodes2[1] = m_usesTreeRoot.getChildAt(0);
353 m_usesTree.getSelectionModel().addSelectionPath(new TreePath(nodes2));
354 m_usesTree.clearSelection();
355
356 // show the appropriate tree
357 //
358 if ( m_uses ) {
359 m_treeView.getViewport().remove(m_isUsedTree);
360 m_treeView.getViewport().add(m_usesTree);
361 }
362 else {
363 m_treeView.getViewport().remove(m_usesTree);
364 m_treeView.getViewport().add(m_isUsedTree);
365 }
366
367 // update the tree view
368 //
369 m_treeView.validate();
370 m_treeView.updateUI();
371 }
372
373 /***
374 * Clears all the selections in all the views.
375 */
376 public void clearSelection(){
377 m_treeView.getViewport().add(m_emptyLabel);
378 m_treeView.validate();
379 m_treeView.updateUI();
380 }
381
382 /***
383 * Helper method for creating the root nodes for the uses tree.
384 *
385 * @param top Root node of the dependency tree.
386 * @param pack Current selected package.
387 */
388 private void constructRootNodes(MyTreeNode top, String pack){
389
390 // get the class dependencies
391 //
392 HashMap classDepend = m_dependencyModel.getRawClassInfos();
393
394 // get ClassInfos contained by this package
395 ArrayList classList = (ArrayList)classDepend.get(pack);
396
397 // package doesn't contain any classes
398 if ( classList == null ) {
399 clearSelection();
400 return;
401 }
402
403 // sort the list with the root nodes
404 Collections.sort(classList);
405
406 ListIterator classIter = classList.listIterator();
407 while ( classIter.hasNext() ) {
408 ClassInfo infoTmp = (ClassInfo)classIter.next();
409
410 // skip inner and anonymous classes
411 if ( ! (infoTmp.getFullName().lastIndexOf('$') == -1) ) {
412 continue;
413 }
414
415 MyTreeNode newNode
416 = new MyTreeNode(infoTmp.getFullName());
417 top.add(newNode);
418
419 // distinct the mode
420 if ( m_uses ) {
421 addUsesChildren(newNode);
422 }
423 else {
424 addIsUsedAncestors(newNode);
425 }
426 }
427 }
428
429
430 /***
431 * Add the next children of the specified package. In the manner of the
432 * 'uses' dependency.
433 *
434 * @param node The node to add the children.
435 */
436 private void addUsesChildren(MyTreeNode node){
437 // get class name of this node to determine the children
438 String nodeInfo = (String)node.getUserObject();
439
440 // get ClassInfo of this class
441 ClassInfo info = (ClassInfo)m_classInfos.get(nodeInfo);
442 // extern classes which have certainly no ClassInfo
443 if ( info == null ) {
444 return;
445 }
446
447 // get all the classes which are used by this class
448 ArrayList usesList = info.getNeededClasses();
449 Collections.sort(usesList); // sort the list
450 ListIterator children = usesList.listIterator();
451
452 while ( children.hasNext() ) {
453 String nameTmp = (String)children.next();
454
455 // skip inner and anonymous classes
456 if ( ! (nameTmp.lastIndexOf('$') == -1) ) {
457 continue;
458 }
459
460 // add children don't add reflexive dependencies
461 if ( ! nodeInfo.equals(nameTmp) ) {
462 MyTreeNode newNode
463 = new MyTreeNode(nameTmp);
464
465 // extract package name
466 int index = 0;
467 if ((index = nameTmp.lastIndexOf(".")) == -1) {
468 index = nameTmp.length();
469 }
470 String packName = nameTmp.substring(0, index);
471
472 // check if the package is extern
473 if ( ! m_packDepend.containsKey(packName) ) {
474 newNode.iconType = MyTreeNode.EXTERN_ICON_TYPE;
475 }
476
477 try {
478 node.add(newNode);
479 // node cannot be added because of a cycle
480 } catch ( IllegalStateException ise ) {}
481 }
482 }
483 }
484
485
486 /***
487 * Add the ancestors of the the specified package. In the manner of the
488 * 'is used' dependency.
489 *
490 * @param node The node to add the ancestors.
491 */
492 private void addIsUsedAncestors(MyTreeNode node){
493 // determine current package
494 String className = (String)node.getUserObject();
495 String pack = className.substring(0, className.lastIndexOf("."));
496
497 // find node of the current package in the graph
498 Node currentPackNode = m_graph.findNodeByName(pack);
499 if ( currentPackNode == null ) {
500 return;
501 }
502
503 // get all the incoming edges of the current node
504 Enumeration inEnum = currentPackNode.inEdgeElements();
505
506 // list with all the potential 'is used' classes
507 ArrayList isUsedClasses = new ArrayList();
508
509 // class infos mapped to the package
510 HashMap classInfos = m_dependencyModel.getRawClassInfos();
511
512 while ( inEnum.hasMoreElements() ) {
513 // get 'is used' packages
514 Node nodeTmp = ((Edge)inEnum.nextElement()).getTail();
515 String packName = nodeTmp.getName();
516 // class infos of this package
517 ArrayList isUsedOfPack = (ArrayList)classInfos.get(packName);
518 // tere is no class
519 if ( isUsedOfPack == null ) {
520 continue;
521 }
522 // add to the list of all the potential packages
523 isUsedClasses.addAll(isUsedOfPack);
524 }
525
526 // sort the list
527 Collections.sort(isUsedClasses);
528
529 ListIterator isUsedIter = isUsedClasses.listIterator();
530 while ( isUsedIter.hasNext() ) {
531 ClassInfo infoTmp = (ClassInfo)isUsedIter.next();
532 // iterator for checking all the needed classes
533 ListIterator checkIter = infoTmp.getNeededClasses().listIterator();
534 while ( checkIter.hasNext() ) {
535 String potentialName = (String)checkIter.next();
536 if ( potentialName.equals(className) ) {
537 String isUsedClass = infoTmp.getFullName();
538
539 // skip inner and anonymous classes
540 if ( ! (isUsedClass.lastIndexOf('$') == -1) ) {
541 continue;
542 }
543
544 // add children don't add reflexive dependencies
545 if ( ! isUsedClass.equals(className) ) {
546 MyTreeNode newNode
547 = new MyTreeNode(isUsedClass);
548
549 String packName = className.substring(0,
550 className.lastIndexOf("."));
551
552 // check if the package is extern
553 if ( ! m_packDepend.containsKey(packName) ) {
554 newNode.iconType = MyTreeNode.EXTERN_ICON_TYPE;
555 }
556
557 try {
558 node.add(newNode);
559 // node cannot be added because of a cycle
560 } catch ( IllegalStateException ise ) {}
561 }
562 }
563 }
564 }
565 }
566
567
568 /***
569 * Changes the mode of the dependency of the package dependency tree.
570 */
571 private void changeTreeDepend(){
572 try {
573 if ( m_uses ) {
574 m_treeView.getViewport().remove(m_isUsedTree);
575 m_treeView.getViewport().add(m_usesTree);
576 }
577 else {
578 m_treeView.getViewport().remove(m_usesTree);
579 m_treeView.getViewport().add(m_isUsedTree);
580 }
581 // there is no tree in the viewport at the beginning
582 } catch ( NullPointerException npe) {
583 return; // ignore it and do nothing
584 }
585 m_treeView.validate();
586 m_treeView.updateUI();
587 }
588
589
590 /***
591 * Helper method for selecting a new package.
592 */
593 private void selectNewPackage(){
594 // determine the right tree to invoke
595 JTree tree;
596 MyTreeNode root;
597 if ( m_uses ) {
598 tree = m_usesTree;
599 root = m_usesTreeRoot;
600 }
601 else {
602 tree = m_isUsedTree;
603 root = m_isUsedTreeRoot;
604 }
605
606 // no package is selected
607 if ( tree == null ) {
608 return;
609 }
610
611 // get selected componet
612 MyTreeNode nodeTmp
613 = (MyTreeNode)tree.getLastSelectedPathComponent();
614
615 // nothing is selected or nothing which can be displayed
616 if ( nodeTmp == null || nodeTmp.equals(root) ) {
617 return;
618 }
619
620 // extern packages can't be displayed
621 if ( nodeTmp.iconType.equals(MyTreeNode.EXTERN_ICON_TYPE) ) {
622 JOptionPane.showMessageDialog(this,
623 "Package of this class cannot be selected.\n" +
624 "It is outside of the project.\n",
625 "Extern package", JOptionPane.WARNING_MESSAGE);
626 return;
627 }
628
629 // get the class name
630 String className = (String)nodeTmp.getUserObject();
631
632 // extract package
633 int lastIndex = className.lastIndexOf(".");
634 String packName = className.substring(0, lastIndex);
635
636 // make the selection consistent
637 ArrayList selectList = new ArrayList();
638 selectList.add(packName);
639 m_dependencyModel.selectPackages(selectList);
640 }
641
642
643 /***
644 * Helper class for handling the selection events of the two trees.
645 */
646 class MyTreeSelectionListener implements TreeSelectionListener {
647
648 /***
649 * Listener for the tree.
650 *
651 * @param e TreeSelectionEvent thrown by the
652 * <code>TreeSelectionListener</code>.
653 */
654 public void valueChanged(TreeSelectionEvent e) {
655 MyTreeNode node
656 = (MyTreeNode)m_usesTree
657 .getLastSelectedPathComponent();
658 // no node is selected
659 if (node == null) { return; }
660
661 Object nodeInfo = node.getUserObject();
662 LOG.info("Selected node: " + nodeInfo);
663 }
664 }
665
666 /***
667 * Helper class for handling the expansion events of the two trees.
668 */
669 class MyTreeExpansionListener implements TreeExpansionListener {
670
671 /***
672 * Specifies whether it is a 'uses' or a 'is used' dependency tree.
673 */
674 private String m_mode;
675
676 /***
677 * Owner.
678 */
679 private JInternalFrame m_frame;
680
681 /***
682 * Constructor - Initialize the appropriate tree for application.
683 */
684 public MyTreeExpansionListener(String mode, JInternalFrame frame){
685 m_mode = mode;
686 m_frame = frame;
687 }
688
689 // Required by TreeExpansionListener interface.
690 //
691 public void treeExpanded(TreeExpansionEvent e) {
692
693 // add all the children of the expanded node.
694 //
695 MyTreeNode nodeTmp = (MyTreeNode)e.getPath().getLastPathComponent();
696
697 TreePath path = e.getPath();
698 int count = path.getPathCount();
699 Object[] pathComps = path.getPath();
700
701
702 // add the children of the nodes when expanded
703 //
704 Enumeration childEnum = nodeTmp.children();
705 while ( childEnum.hasMoreElements() ) {
706 nodeTmp = (MyTreeNode)childEnum.nextElement();
707
708 String name = (String)nodeTmp.getUserObject();
709
710 for (int i=count-1 ; i>-1 ; i--) {
711 String tmp
712 = (String)((MyTreeNode)pathComps[i])
713 .getUserObject();
714
715 if ( tmp.equals(name) ) {
716
717 // mark the node
718 //
719 TreeNode[] nodes = nodeTmp.getPath();
720 TreePath treePath = new TreePath(nodes);
721
722 if ( m_uses ) {
723 m_usesTree.getSelectionModel()
724 .addSelectionPath(treePath);
725 }
726 else if ( ! m_uses ) {
727 m_isUsedTree.getSelectionModel()
728 .addSelectionPath(treePath);
729 }
730
731 // change icon of the node with the icon
732 //
733 nodeTmp.iconType = MyTreeNode.CYCLE_ICON_TYPE;
734 nodeTmp.setAllowsChildren(false);
735 }
736 }
737 if ( m_mode.equals("uses") ) {
738 addUsesChildren(nodeTmp);
739 }
740 else {
741 addIsUsedAncestors(nodeTmp);
742 }
743 }
744 }
745
746 // Required by TreeExpansionListener interface.
747 //
748 public void treeCollapsed(TreeExpansionEvent e) {
749 LOG.info("Tree collapsed!");
750 LOG.info("TreeExpansionPath " + e.getPath());
751 }
752 }
753
754 /***
755 * Class for rendering the trees. Particularly for setting the icons.
756 */
757 private class MyRenderer extends DefaultTreeCellRenderer {
758
759 private ImageIcon m_classIcon = IconGrabber.getIcon("class.gif");
760 private ImageIcon m_extClassIcon = IconGrabber.getIcon("extClass.gif");
761 private ImageIcon m_cycleIcon = IconGrabber.getIcon("sun.gif");
762
763 /***
764 * Configures the renderer based on the passed in components.
765 * The value is set from messaging the tree with
766 * <code>convertValueToText</code>, which ultimately invokes
767 * <code>toString</code> on <code>value</code>.
768 * The foreground color is set based on the selection and the icon
769 * is set based on on leaf and expanded.
770 */
771 public Component getTreeCellRendererComponent(JTree tree,
772 Object value,
773 boolean sel,
774 boolean expanded,
775 boolean leaf,
776 int row,
777 boolean hasFocus) {
778
779 super.getTreeCellRendererComponent(tree, value, sel,
780 expanded, leaf, row,
781 hasFocus);
782
783 // current tree node
784 //
785 MyTreeNode treeNode = (MyTreeNode)value;
786
787 // default icon
788 //
789 if ( treeNode.iconType.equals("") ) {
790 setIcon(m_classIcon);
791 }
792 // cycle icon
793 //
794 else if ( treeNode.iconType.equals("cycle") ) {
795 setIcon(m_cycleIcon);
796 }
797 // external package icon
798 //
799 else if ( treeNode.iconType.equals("ext") ) {
800 setIcon(m_extClassIcon);
801 }
802 return this;
803 }
804 }
805 }
This page was automatically generated by Maven