1 /*
   2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * This source code is provided to illustrate the usage of a given feature
  28  * or technique and has been deliberately simplified. Additional steps
  29  * required for a production-quality application, such as security checks,
  30  * input validation and proper error handling, might not be present in
  31  * this sample code.
  32  */
  33 
  34 
  35 package com.sun.tools.example.debug.gui;
  36 
  37 import java.io.*;
  38 import java.util.*;
  39 
  40 import javax.swing.*;
  41 import javax.swing.tree.*;
  42 import java.awt.*;
  43 import java.awt.event.*;
  44 
  45 import com.sun.tools.example.debug.bdi.*;
  46 
  47 public class SourceTreeTool extends JPanel {
  48 
  49     private static final long serialVersionUID = 3336680912107956419L;
  50 
  51     private Environment env;
  52 
  53     private ExecutionManager runtime;
  54     private SourceManager sourceManager;
  55     private ClassManager classManager;
  56 
  57     private JTree tree;
  58     private SourceTreeNode root;
  59     private SearchPath sourcePath;
  60     private CommandInterpreter interpreter;
  61 
  62     private static String HEADING = "SOURCES";
  63 
  64     public SourceTreeTool(Environment env) {
  65 
  66         super(new BorderLayout());
  67 
  68         this.env = env;
  69         this.runtime = env.getExecutionManager();
  70         this.sourceManager = env.getSourceManager();
  71 
  72         this.interpreter = new CommandInterpreter(env);
  73 
  74         sourcePath = sourceManager.getSourcePath();
  75         root = createDirectoryTree(HEADING);
  76 
  77         // Create a tree that allows one selection at a time.
  78         tree = new JTree(new DefaultTreeModel(root));
  79         tree.setSelectionModel(new SingleLeafTreeSelectionModel());
  80 
  81         /******
  82         // Listen for when the selection changes.
  83         tree.addTreeSelectionListener(new TreeSelectionListener() {
  84             public void valueChanged(TreeSelectionEvent e) {
  85                 SourceTreeNode node = (SourceTreeNode)
  86                     (e.getPath().getLastPathComponent());
  87                 interpreter.executeCommand("view " + node.getRelativePath());
  88             }
  89         });
  90         ******/
  91 
  92         MouseListener ml = new MouseAdapter() {
  93             @Override
  94             public void mouseClicked(MouseEvent e) {
  95                 int selRow = tree.getRowForLocation(e.getX(), e.getY());
  96                 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
  97                 if(selRow != -1) {
  98                     if(e.getClickCount() == 1) {
  99                         SourceTreeNode node =
 100                             (SourceTreeNode)selPath.getLastPathComponent();
 101                         // If user clicks on leaf, select it, and issue 'view' command.
 102                         if (node.isLeaf()) {
 103                             tree.setSelectionPath(selPath);
 104                             interpreter.executeCommand("view " + node.getRelativePath());
 105                         }
 106                     }
 107                 }
 108             }
 109         };
 110         tree.addMouseListener(ml);
 111 
 112         JScrollPane treeView = new JScrollPane(tree);
 113         add(treeView);
 114 
 115         // Create listener for source path changes.
 116 
 117         SourceTreeToolListener listener = new SourceTreeToolListener();
 118         sourceManager.addSourceListener(listener);
 119 
 120         //### remove listeners on exit!
 121     }
 122 
 123     private class SourceTreeToolListener implements SourceListener {
 124 
 125         @Override
 126         public void sourcepathChanged(SourcepathChangedEvent e) {
 127             sourcePath = sourceManager.getSourcePath();
 128             root = createDirectoryTree(HEADING);
 129             tree.setModel(new DefaultTreeModel(root));
 130         }
 131 
 132     }
 133 
 134     private static class SourceOrDirectoryFilter implements FilenameFilter {
 135         @Override
 136         public boolean accept(File dir, String name) {
 137             return (name.endsWith(".java") ||
 138                     new File(dir, name).isDirectory());
 139         }
 140     }
 141 
 142     private static FilenameFilter filter = new SourceOrDirectoryFilter();
 143 
 144     SourceTreeNode createDirectoryTree(String label) {
 145         try {
 146             return new SourceTreeNode(label, null, "", true);
 147         } catch (SecurityException e) {
 148             env.failure("Cannot access source file or directory");
 149             return null;
 150         }
 151     }
 152 
 153 
 154     class SourceTreeNode implements TreeNode {
 155 
 156         private String name;
 157         private boolean isDirectory;
 158         private SourceTreeNode parent;
 159         private SourceTreeNode[] children;
 160         private String relativePath;
 161         private boolean isExpanded;
 162 
 163         private SourceTreeNode(String label,
 164                                SourceTreeNode parent,
 165                                String relativePath,
 166                                boolean isDirectory) {
 167             this.name = label;
 168             this.relativePath = relativePath;
 169             this.parent = parent;
 170             this.isDirectory = isDirectory;
 171         }
 172 
 173         @Override
 174         public String toString() {
 175             return name;
 176         }
 177 
 178         public String getRelativePath() {
 179             return relativePath;
 180         }
 181 
 182         private void expandIfNeeded() {
 183             try {
 184                 if (!isExpanded && isDirectory) {
 185                     String[] files = sourcePath.children(relativePath, filter);
 186                     children = new SourceTreeNode[files.length];
 187                     for (int i = 0; i < files.length; i++) {
 188                         String childName =
 189                             (relativePath.equals(""))
 190                             ? files[i]
 191                             : relativePath + File.separator + files[i];
 192                         File file = sourcePath.resolve(childName);
 193                         boolean isDir = (file != null && file.isDirectory());
 194                         children[i] =
 195                             new SourceTreeNode(files[i], this, childName, isDir);
 196                     }
 197                 }
 198                 isExpanded = true;
 199             } catch (SecurityException e) {
 200                 children = null;
 201                 env.failure("Cannot access source file or directory");
 202             }
 203         }
 204 
 205         // -- interface TreeNode --
 206 
 207         /*
 208          * Returns the child <code>TreeNode</code> at index
 209          * <code>childIndex</code>.
 210          */
 211         @Override
 212         public TreeNode getChildAt(int childIndex) {
 213             expandIfNeeded();
 214             return children[childIndex];
 215         }
 216 
 217         /**
 218          * Returns the number of children <code>TreeNode</code>s the receiver
 219          * contains.
 220          */
 221         @Override
 222         public int getChildCount() {
 223             expandIfNeeded();
 224             return children.length;
 225         }
 226 
 227         /**
 228          * Returns the parent <code>TreeNode</code> of the receiver.
 229          */
 230         @Override
 231         public TreeNode getParent() {
 232             return parent;
 233         }
 234 
 235         /**
 236          * Returns the index of <code>node</code> in the receivers children.
 237          * If the receiver does not contain <code>node</code>, -1 will be
 238          * returned.
 239          */
 240         @Override
 241         public int getIndex(TreeNode node) {
 242             expandIfNeeded();
 243             for (int i = 0; i < children.length; i++) {
 244                 if (children[i] == node) {
 245                     return i;
 246             }
 247             }
 248             return -1;
 249         }
 250 
 251         /**
 252          * Returns true if the receiver allows children.
 253          */
 254         @Override
 255         public boolean getAllowsChildren() {
 256             return isDirectory;
 257         }
 258 
 259         /**
 260          * Returns true if the receiver is a leaf.
 261          */
 262         @Override
 263         public boolean isLeaf() {
 264             expandIfNeeded();
 265             return !isDirectory;
 266         }
 267 
 268         /**
 269          * Returns the children of the receiver as an Enumeration.
 270          */
 271         @Override
 272         public Enumeration children() {
 273             expandIfNeeded();
 274             return new Enumeration() {
 275                 int i = 0;
 276                 @Override
 277                 public boolean hasMoreElements() {
 278                     return (i < children.length);
 279                 }
 280                 @Override
 281                 public Object nextElement() throws NoSuchElementException {
 282                     if (i >= children.length) {
 283                         throw new NoSuchElementException();
 284                     }
 285                     return children[i++];
 286                 }
 287             };
 288         }
 289 
 290     }
 291 
 292 }