1 /***
2 * OverView.java
3 *
4 * Project: Dependency Tool
5 *
6 * WHEN WHO WHAT
7 * 06.06.2003 pko initial public release
8 * 08.01.2002 pko modification
9 * 22.01.2002 ctr modification
10 * 08.01.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 java.awt.*;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.awt.event.KeyEvent;
38 import java.io.File;
39 import java.io.FileNotFoundException;
40 import java.io.IOException;
41 import java.util.*;
42 import javax.swing.*;
43 import javax.swing.border.Border;
44 import javax.swing.border.CompoundBorder;
45 import javax.swing.border.TitledBorder;
46 import javax.swing.event.TreeSelectionEvent;
47 import javax.swing.event.TreeSelectionListener;
48 import javax.swing.tree.*;
49
50 import org.apache.log4j.Logger;
51
52 import ch.elca.dependency.core.DependencyContext;
53 import ch.elca.dependency.core.DependencyModel;
54 import ch.elca.dependency.util.IOManager;
55 import ch.elca.dependency.util.IconGrabber;
56
57 import info.clearthought.layout.TableLayout;
58
59 /***
60 * A view class for diplaying the list and tree representations of the
61 * analyzed project. The display method of the <code>View</code>
62 * superclass is impemented in this specific view. This method is
63 * responsible for getting the needed data from the model and then
64 * show this actual data to the user.<br> All the user interaction
65 * which concerns only this view is also managed by this class.<br>
66 * Here a tree with all the packages and classes of the project is
67 * diplayed. Also some interaction with the graph view willbe added.
68 *
69 * @see ch.elca.dependency.view.View
70 *
71 * @author Christoph Trutmann
72 * @version 1.0-beta
73 */
74 public final class OverView extends View implements TreeSelectionListener, ActionListener {
75
76 //****************************************************************************************/
77 // static fields
78 //****************************************************************************************/
79
80 /***
81 * Log4j Logger.
82 */
83 private final static Logger LOG = Logger.getLogger(OverView.class);
84
85 /***
86 * Title used for this Frame
87 */
88 private final static String FRAME_TITLE = "Project Structure View";
89
90 //****************************************************************************************/
91 // instance fields
92 //****************************************************************************************/
93
94 /***
95 * Abstract location of the input class structure file location.
96 */
97 private File m_input;
98
99 /***
100 * Tree holding the class and package overview.
101 */
102 private JTree m_tree;
103
104 /***
105 * Root node of the overview tree.
106 */
107 private final DefaultMutableTreeNode m_treeRoot
108 = new DefaultMutableTreeNode("Project structure");
109
110 /***
111 * Signals the first directory parsed by the analyze method.
112 */
113 private boolean m_firstDir = true;
114
115 /***
116 * Textfield for the search expression.
117 */
118 private JTextField m_searchField = new JTextField();
119
120 /***
121 * List containing all the nodes of the tree in the pre order.
122 */
123 private ArrayList m_preOrderList;
124
125 /***
126 * This flag is set false for each full iteration through the pre order
127 * list. If the flag is false again when the list is empty --> no match for
128 * this search string.
129 */
130 private boolean m_foundOne;
131
132 /***
133 * ScrollPane with the tree.
134 */
135 private JScrollPane m_treeScrollPane;
136
137 /***
138 * Constructor - Makes a <code>JInternalFrame</code> with the specified
139 * parameters.
140 *
141 * @param inputStructure Location of the input location of the class
142 * structure.
143 */
144 public OverView(DependencyModel dependencyModel) {
145 super(dependencyModel, FRAME_TITLE);
146 setDefaultBounds(new Rectangle(943, 441, 266, 415));
147 recallConfig();
148 }
149
150 protected void internalInitData() {
151
152 // get data from the dependency context
153 //
154 DependencyContext dependencyCtx = m_dependencyModel.getDependencyContext();
155 m_input = (File)dependencyCtx.get(DependencyContext.ROOT_FILE_KEY);
156
157 // if m_input is a file, we want to show the unpacked structure instead
158 //
159 if (m_input.isFile()) {
160 m_input = IOManager.getTempDir();
161 }
162 }
163
164
165 protected void initView() {
166
167 // configure TableLayout
168 //
169 double border = 10;
170 double fill = TableLayout.FILL;
171 double pref = TableLayout.PREFERRED;
172 double size[][] =
173 {{border, fill, border}, // Columns
174 {border, pref, fill, border}}; // Rows
175
176 this.getContentPane().setLayout(new TableLayout(size));
177
178 // create panel with search mechanism
179 //
180 JPanel searchPanel = new JPanel();
181 Border etched = BorderFactory.createEtchedBorder();
182 CompoundBorder compBorder
183 = BorderFactory.createCompoundBorder(etched,
184 BorderFactory.createEmptyBorder(0, 10, 10, 10));
185 TitledBorder titBorder
186 = BorderFactory.createTitledBorder(compBorder,
187 " Search ");
188 titBorder.setTitleJustification(TitledBorder.LEFT);
189 titBorder.setTitlePosition(TitledBorder.DEFAULT_POSITION);
190
191 // set the border and layout of the panel
192 //
193 searchPanel.setBorder(titBorder);
194 searchPanel.setLayout(new BorderLayout());
195
196 // textfield for the search
197 //
198 m_searchField = new JTextField();
199 m_searchField.setPreferredSize(new Dimension(150,25));
200
201 m_searchField.addActionListener(this);
202
203 searchPanel.add(m_searchField, BorderLayout.WEST);
204
205 // button to start the search
206 //
207 JButton searchButton = new JButton("Search");
208 searchButton.setMnemonic(KeyEvent.VK_ENTER);
209 searchButton.setPreferredSize(new Dimension(100,25));
210
211 searchButton.addActionListener(this);
212
213 searchButton.setDefaultCapable(true);
214 this.getRootPane().setDefaultButton(searchButton);
215 searchPanel.add(searchButton, BorderLayout.EAST);
216
217 // add the search panel to the content pane
218 //
219 this.getContentPane().add(searchPanel, "1, 1, F, F");
220
221 // construct the overview tree
222 //
223 try {
224 constructOverviewTree(m_treeRoot);
225 } catch (Exception e) {
226 JOptionPane.showMessageDialog(this,
227 "The class structure cannot be read!\n" + e.getMessage(),
228 "OverView", JOptionPane.ERROR_MESSAGE);
229 }
230
231 m_tree = new JTree(m_treeRoot);
232
233 // only one node can be selected at the same time
234 //
235 m_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
236 m_tree.addTreeSelectionListener(this);
237
238 m_tree.putClientProperty("JTree.lineStyle", "Angled");
239 m_tree.setCellRenderer(new MyRenderer());
240
241 // Create the scroll pane and add the tree to it.
242 //
243 m_treeScrollPane = new JScrollPane(m_tree);
244
245 // add the scroll pane to the frame
246 //
247 getContentPane().add(m_treeScrollPane, "1, 2, F, F");
248
249 // set the parameters of the internal frame
250 //
251 setBackground(searchPanel.getBackground());
252 setVisible(true);
253 validate();
254 }
255
256 /***
257 * Displays the <code>OverView</code> after the data has changed.
258 * This mehtod is individually implemented in each view.
259 *
260 * @tbd improve display method
261 */
262 public void display() {
263
264 // display: reread classes according to the current perspective, which perhaps has changed since last display...
265
266 validate();
267 setVisible(true);
268 }
269
270 /***
271 * Selects all the packages in the specified list for all the views
272 * implementing the <code>Observer</code> interface.
273 *
274 * @param selectList List containing all the selected packages.
275 */
276 public void select(ArrayList selectList){
277
278 // iterator over all the selected packages
279 ListIterator iter = selectList.listIterator();
280 // list with all the packages converted to the tree path
281 ArrayList paths = new ArrayList();
282 // determines whether a package is in the project
283 // if not nothing is selected
284 boolean foundOne = false;
285
286 while ( iter.hasNext() ) {
287 // full qualified package statement
288 String tmp = (String)iter.next();
289 StringTokenizer tokenizer = new StringTokenizer(tmp, ".", false);
290
291 // list for the toxkens -> package part
292 ArrayList list = new ArrayList();
293 while ( tokenizer.hasMoreTokens() ) {
294 String tokTmp = tokenizer.nextToken();
295 list.add(tokTmp);
296 }
297 // number of package parts
298 int size = list.size() + 1; // + root node
299 TreeNode[] nodes = new TreeNode[size];
300 // root node always at the beginning of the path
301 nodes[0] = m_treeRoot;
302
303 // iterator over the list with the tokens
304 ListIterator tokenIter = list.listIterator();
305
306 // find the child node of the appropriate package token
307 int i = 1;
308 DefaultMutableTreeNode currentParent = m_treeRoot;
309 while ( tokenIter.hasNext() ) {
310 String packToken = (String)tokenIter.next();
311
312 Enumeration childEnum = currentParent.children();
313 while ( childEnum.hasMoreElements() ) {
314 DefaultMutableTreeNode tnTmp
315 = (DefaultMutableTreeNode)childEnum.nextElement();
316 String info = (String)tnTmp.getUserObject();
317
318 if (info.equals(packToken)) {
319 nodes[i] = tnTmp;
320 foundOne = true;
321 currentParent = tnTmp;
322 continue;
323 }
324 }
325 i++;
326 }
327 TreePath newPath = new TreePath(nodes);
328 paths.add(newPath);
329 }
330
331 // no match
332 if ( ! foundOne ) {
333 clearSelection();
334 return;
335 }
336
337 // fill the tree paths in the array
338 ListIterator pathIter = paths.listIterator();
339 TreePath[] treePaths = new TreePath[paths.size()];
340 int i = 0;
341 while ( pathIter.hasNext() ) {
342 treePaths[i] = (TreePath)pathIter.next();
343 i++;
344 }
345
346 // add the new selection to the tree
347 //
348 try {
349 m_tree.getSelectionModel().addSelectionPaths(treePaths);
350 if (treePaths.length >= 1) {
351 m_tree.scrollPathToVisible(treePaths[0]);
352 }
353 } catch ( Exception e ) {
354 //
355 // ignore if not existing
356 //
357 }
358 }
359
360 /***
361 * Clears all the selections in all the views.
362 */
363 public void clearSelection(){
364 try {
365 m_tree.clearSelection();
366 } catch (Exception e){
367 //
368 // deliberately left empty
369 //
370 }
371 }
372
373 /***
374 * Helper method for creating the overview tree.
375 */
376 private void constructOverviewTree(DefaultMutableTreeNode top)
377 throws IOException, FileNotFoundException{
378
379 // start constructing the overview tree in the root of the class structure
380 //
381 analyze(m_input, top);
382 }
383
384 /***
385 * Helper method for reading the project structure in to the tree.
386 */
387 private void analyze(File file, DefaultMutableTreeNode parent)
388 throws FileNotFoundException, IOException {
389
390 String fileName = file.getName();
391
392 // it is a directory
393 //
394 if ( file.isDirectory() ) {
395 DefaultMutableTreeNode nodeTmp;
396
397 // don't add the first directory
398 //
399 if ( m_firstDir ) {
400 m_firstDir = false;
401 nodeTmp = parent;
402 } else {
403 nodeTmp = new DefaultMutableTreeNode(fileName);
404 parent.add(nodeTmp);
405 }
406
407 File[] fileArr = file.listFiles();
408 int elems = fileArr.length, i=0;
409
410 // do not show empty directories
411 //
412 if ( elems == 0 && nodeTmp.isNodeChild(parent) ) {
413 LOG.debug("remove " + nodeTmp);
414 parent.remove(nodeTmp);
415 }
416
417 // recursive calls for each abstract file in the current directory
418 //
419 boolean hasClassFile = false;
420 boolean hasDir = false;
421 while ( (elems > 0 && i<elems) ) {
422 File tmp = fileArr[i];
423 if ( isClassFile(tmp) ) {
424 hasClassFile = true;
425 }
426 if ( tmp.isDirectory() ) {
427 hasDir = true;
428 }
429 analyze(tmp, nodeTmp);
430 i++;
431 }
432 if ( ! hasClassFile && ! hasDir ) {
433 parent.remove(nodeTmp);
434 }
435 }
436
437 // it is a file
438 //
439 else {
440
441 // test if the file is a class file and not an inner class
442 //
443 if ( isClassFile(file) && (fileName.lastIndexOf('$') == -1) ) {
444
445 // remove the file extension .class
446 //
447 fileName = fileName.substring(0, fileName.lastIndexOf("."));
448
449 DefaultMutableTreeNode classNodeTmp = new DefaultMutableTreeNode(fileName);
450 parent.add(classNodeTmp);
451
452 } else if ( isJarFile(file) ) {
453 analyze(IOManager.getTempDir(), parent);
454 }
455 }
456 }
457
458
459 /***
460 * Helper method for testing whether the specified abstract file is a class
461 * file.
462 *
463 * @param file Abstract file object pointing to the location where the
464 * physical location of the file to inqure is.
465 * @return True if it is a java .class file.<br>
466 * If the file has no file extension, false is returned.
467 *
468 * @tbd provide a generic isFiletype(File f, String suffix) method that recognizes
469 * filetypes
470 */
471 private boolean isClassFile(File file){
472 return file.getName().endsWith(".class");
473 }
474
475 /***
476 * Helper method for testing whether a given file is a jar file.
477 *
478 * @tbd provide a generic isFiletype(File f, String suffix) method that recognizes
479 * filetypes
480 */
481 private boolean isJarFile(File file) {
482 return file.getName().endsWith(".jar");
483 }
484
485 /***
486 * Listener for the tree.
487 *
488 * @param e TreeSelectionEvent thrown by the
489 * <code>TreeSelectionListener</code>.
490 */
491 public void valueChanged(TreeSelectionEvent e) {
492 DefaultMutableTreeNode nodeTmp = (DefaultMutableTreeNode)m_tree.getLastSelectedPathComponent();
493
494 // nothing is selected
495 if ( nodeTmp == null ) {
496 return;
497 }
498
499 // make full qualified package string
500 Object[] path = e.getPath().getPath();
501 int count = e.getPath().getPathCount();
502
503 // selected node is a class
504 if ( nodeTmp.isLeaf() ) {
505 count--; // don't add class name
506 }
507
508 // only the root with the title
509 if ( count <= 1 ) {
510 return;
511 }
512 StringBuffer fullQualified = null;
513 for ( int i=1 ; i<count ; i++) {
514 String tmp = (String)((DefaultMutableTreeNode)path[i])
515 .getUserObject();
516 if ( i == 1 ) {
517 fullQualified = new StringBuffer(tmp);
518 }
519 else {
520 fullQualified.append(".");
521 fullQualified.append(tmp);
522 }
523 }
524 String name = fullQualified.toString();
525
526 // make the selection consistent
527 ArrayList selectList = new ArrayList();
528 selectList.add(name);
529 m_dependencyModel.selectPackages(selectList);
530 }
531
532 /***
533 * Actions from the search button.
534 */
535 public void actionPerformed(ActionEvent e) {
536 search(m_searchField.getText().trim());
537 }
538
539 /***
540 * Helper method for the search action.<br>
541 * The first match is marked and the search is stopped.
542 *
543 * @param searchStr String to search for.
544 */
545 private void search(String searchStr) {
546
547 // can't search empty string
548 //
549 if ( searchStr.equals("") ) {
550 return;
551 }
552
553 DefaultMutableTreeNode startNode = null;
554 DefaultMutableTreeNode currentNode = null;
555 TreePath currentPath = null;
556
557 // take a selected node for analysis
558 // if none is selected, take the root
559 //
560 if ((currentPath = m_tree.getSelectionPath()) == null) {
561 startNode = m_treeRoot;
562 } else {
563 startNode = (DefaultMutableTreeNode)currentPath.getLastPathComponent();
564 }
565
566 // determine next node
567 //
568
569 // search the node
570 //
571 for (currentNode = startNode.getNextNode();
572 currentNode != startNode;
573 currentNode = currentNode.getNextNode()) {
574
575 if (currentNode == null) {
576 if (startNode == m_treeRoot) {
577 break;
578 }
579 currentNode = m_treeRoot;
580 }
581
582 // node containig the search string found
583 //
584 if (isNodeFound(currentNode, searchStr)) {
585 selectNodeInTree(currentNode);
586 return;
587 }
588 }
589
590 // now check whether this wasn't the only node containig the string
591 //
592 if (isNodeFound(startNode, searchStr)) {
593 selectNodeInTree(startNode);
594 return;
595 }
596
597 // the whole tree is traversed and nothing is found
598 //
599 JOptionPane.showMessageDialog(this,
600 "There was no match for the search string '" + searchStr + "'!",
601 "No match", JOptionPane.WARNING_MESSAGE);
602 }
603
604 private boolean isNodeFound(DefaultMutableTreeNode currentNode, String searchString) {
605 return ((String)currentNode.getUserObject()).toLowerCase().
606 indexOf(searchString.toLowerCase()) != -1;
607 }
608
609 private void selectNodeInTree(DefaultMutableTreeNode currentNode) {
610 TreePath currentPath = new TreePath(currentNode.getPath());
611
612 // this is necessary to have the node selected, not only a package
613 //
614 m_tree.setSelectionPath(currentPath);
615 m_tree.scrollPathToVisible(currentPath);
616 m_tree.setSelectionPath(currentPath);
617 m_tree.scrollPathToVisible(currentPath);
618 return;
619 }
620
621 /***
622 * Class for rendering the tree Particularly for setting the icons.
623 */
624 private class MyRenderer extends DefaultTreeCellRenderer {
625
626 private ImageIcon m_packageIcon = IconGrabber.getIcon("package.gif");
627 private ImageIcon m_classIcon = IconGrabber.getIcon("class.gif");
628
629 /***
630 * Configures the renderer based on the passed in components.
631 * The value is set from messaging the tree with
632 * <code>convertValueToText</code>, which ultimately invokes
633 * <code>toString</code> on <code>value</code>.
634 * The foreground color is set based on the selection and the icon
635 * is set based on on leaf and expanded.
636 */
637 public Component getTreeCellRendererComponent(JTree tree,
638 Object value,
639 boolean sel,
640 boolean expanded,
641 boolean leaf,
642 int row,
643 boolean hasFocus) {
644
645 super.getTreeCellRendererComponent(tree, value, sel,
646 expanded, leaf, row,
647 hasFocus);
648 // class icon for classes
649 //
650 if ( leaf ) {
651 setIcon(m_classIcon);
652 }
653 // package icon for packages
654 //
655 else {
656 setIcon(m_packageIcon);
657 }
658
659 return this;
660 }
661 }
662 }
This page was automatically generated by Maven