Initial Commit
[packages] / xemacs-packages / jde / lisp / jde-xref.el
1 ;; JDE-XREF.EL --- Class cross-reference commands for the JDEE.
2 ;; $Revision: 1.24 $ $Date: 2004/06/06 14:19:23 $
3 ;;
4 ;; Copyright (C) 2002, 2003 Andrew Hyatt
5 ;;
6 ;; Author: Andrew Hyatt <andy_jde@thehyatts.net>
7 ;; Maintainers: Andrew Hyatt and Paul Kinnucan
8 ;; Keywords: java, tools
9 ;; 
10 ;;
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; A copy of the GNU General Public License can be obtained from this
22 ;; program's author (send electronic mail to
23 ;; ) or from the Free Software
24 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 ;; LCD Archive Entry:
27 ;; jde-xref|Andrew Hyatt|
28 ;; |Java class cross-referencing commands for the JDEE
29 ;; |$Date: 2004/06/06 14:19:23 $|$Revision: 1.24 $|~/packages/jde-xref.el
30
31 ;;; Commentary:
32
33 ;; This file stores all caller-related functionality, from the
34 ;; functions to find all callers of a Java method, to those that will
35 ;; do more sophisticated things like creating a call-tree
36 ;;
37 ;; Everything works off the database, which is split into a series of
38 ;; files, one file per package.  Each package consists of three tables.
39 ;; The first is a mapping of class-function-args specifiers to a list
40 ;; of all the classes, method & linenumbers that call it, such as:
41 ;;
42 ;; (("org.foo.MyClass" "getName" nil "String") .
43 ;;   (("org.foo.MyOtherClass" "doProcessing" nil nil 42)
44 ;;    ("org.foo.YetAnotherClass" "doMoreProcessing" nil nil 32)))
45 ;;
46 ;; The specifier for a method (the first part of the above is a: fully
47 ;; qualified classname , function name, return type (nil for "void")
48 ;; and non-fully qualified arglist.  We don't want to fully qualify
49 ;; the arglist, so that we don't have to fully qualify it when doing a
50 ;; lookup.  The changes of collisions are minor, I think.
51 ;;
52 ;; The next table is a table of which class has which interfaces.
53 ;; This is used so that we can consider a call to IFoo.doSomething as
54 ;; a call to Foo.doSomething, is Foo implements IFoo.  This
55 ;; functionality is not always wanted, so the functions that show
56 ;; caller information can either use it or not.
57 ;;
58 ;; The third table is a table of which class has which methods and
59 ;; fields.  This is necessary to determine which references to
60 ;; subclasses really are references to a superclass because the fields
61 ;; or methods have not been overriden.
62 ;;
63 ;; The fourth table is a table of classes to superclasses, necessary
64 ;; to figure out when a call to superclass might be a call to a
65 ;; subclass (when the user asks about calls to the subclass).
66 ;;
67 ;; There is also a global table across all packages, which is a
68 ;; map of which classes are subclasses of which other classes. This
69 ;; makes it possible to investigate a class's subclasses when looking
70 ;; for references.
71 ;; 
72
73 (require 'jde-parse)
74 (require 'jde-parse-class)
75 (require 'jde-class)
76 (require 'tree-widget)
77
78 (defconst jde-xref-version "1.5")
79
80 (defgroup jde-xref nil
81   "JDEE Class Cross-Reference (Refactoring) Options"
82   :group 'jde
83   :prefix "jde-xref-")
84
85
86 (defcustom jde-xref-db-base-directory "."
87   "The path to store the directory which contains the database of
88   which function calls which.  The data directory will be called
89   \"xrefdb\" and will reside in the directory pointed to at this
90   location "
91   :group 'jde-xref
92   :type 'directory)
93   
94 (defcustom jde-xref-store-prefixes nil
95   "A list of what prefixes to specify what references should be
96   tracked in the caller database.  Such as: '(\"org.apache\" \"jde\"),
97   to keep track of all references to classes that start with
98   \"org.apache\" or \"jde\"."
99   :group 'jde-xref
100   :type '(repeat (string :tag "Prefix")))
101
102 (defcustom jde-xref-cache-size 3
103   "How much package info to cache in memory at once.  The higher the
104   variable is the more memory will be used, but the faster things
105   should be."
106   :group 'jde-xref
107   :type 'integer)
108
109 (defvar jde-xref-stack nil
110   "A list of the callers of a function, to be popped one at a time
111   and shown to the user")
112
113 (defvar jde-xref-modified-classes nil
114   "A list of modified classes, to be used in updating the caller table
115   when a recompile happens")
116
117 (defvar jde-xref-parsed-classes nil
118   "A global variable that is used to hold which classes have been parsed")
119
120 (defvar jde-xref-cache nil
121   "A cache holding package information that will grow to size
122   `jde-xref-cache-size'")
123
124 (defvar jde-xref-subclasses nil
125   "A hashtable containing a list of which classes subclass which other
126 subclasses.")
127
128 (defun jde-xref-pickle-hash (hash filename)
129   "Store HASH in the file FILENAME.  The hash can be retrieved by
130 calling `jde-xref-unpickle-hash'."
131   (when (file-exists-p filename)
132     (delete-file filename))
133   (save-excursion
134     (let ((buf (find-file-noselect (jde-normalize-path filename))))
135       (set-buffer buf)
136       (goto-char (point-min))
137       (erase-buffer)
138       (insert "(")
139       (maphash (lambda (key val)
140                  (when val
141                    (insert (concat "(" (prin1-to-string key) " . "
142                                    (prin1-to-string val) ")\n" ))))
143                hash)
144       (insert ")")
145       (save-buffer)
146       (kill-buffer buf))))
147
148 (defun jde-xref-unpickle-hash (hash filename)
149   "Populate a hash created by loading the contents of FILENAME to HASH.
150 FILENAME must be created by `jde-xref-pickle-hash'"
151   (unless (file-exists-p filename)
152     (error (concat "Cannot unpickle - file " filename " does not exist.  "
153                    "The xref database may need to be recreated.")))
154   (dolist (item (with-temp-buffer
155           (insert-file-contents-literally filename)
156           (read (current-buffer))))
157     (puthash (car item) (cdr item) hash))) 
158
159 (defun jde-xref-get-db-directory ()
160   (concat (jde-normalize-path jde-xref-db-base-directory) "/xrefdb"))
161
162 (defun jde-xref-guess-and-set-prefixes ()
163   (let ((prefixes (jde-xref-guess-prefixes)))
164     (when prefixes
165       (setq jde-xref-store-prefixes prefixes))))
166
167 (defun jde-xref-guess-prefixes ()
168   "Try to guess what the prefixes are.  Return the prefix list if
169   correctly guessed, otherwise return NULL.  This works by looking at
170   the sourcepath, and putting all the top-level packages in the list,
171   where toplevel is defined as being a package from which all the
172   other packages branch out from."
173
174   (labels ((get-prefix (base-path package-path)
175              ;; if the directory contains just one directory (or two,
176              ;; one being CVS), then we can recurse down it to build
177              ;; up a proper prefix before the package tree really
178              ;; branches out
179              (let ((files (remove-if-not
180                            (lambda (dir) (and (file-directory-p
181                                                (concat base-path "/" package-path "/" dir)))
182                                               (not (equal "CVS" dir)))
183                            (directory-files 
184                             (concat base-path "/" package-path)
185                             nil "[^.]$"))))
186                (if (eq (length files) 1)
187                    (get-prefix base-path (concat package-path "/"
188                                                  (car files)))
189                  (subst-char-in-string ?/ ?. package-path)))))
190     (when (and (eq major-mode 'jde-mode) jde-sourcepath)
191       (let ((first-prefix (car (split-string (jde-parse-get-package-name)
192                                              "\\."))) (prefixes))
193         (dolist (path (remove-if-not (lambda (path) (file-exists-p path)) jde-sourcepath) prefixes)
194           (when (member first-prefix (directory-files path nil "[^.]$"))
195             (message (concat "path = " path))
196             (add-to-list 'prefixes (get-prefix path first-prefix))))))))
197
198 ;;;###autoload
199 (defun jde-xref-make-xref-db ()
200   "Create a database of caller to callee (and the reverse) from the
201 classes in `jde-built-class-path' and store the data in the location
202 specified by `jde-xref-db-file'"
203   (interactive)
204   (when (null jde-xref-db-base-directory)
205     (error "The variable `jde-xref-db-base-directory' must be defined to make a caller database"))
206   (when (null jde-built-class-path)
207     (error "The variable `jde-built-class-path' must be defined to make a caller database"))
208   (when (null jde-xref-store-prefixes)
209     (error "The variable `jde-xref-store-prefixes' must be defined to make a caller database"))
210   (unless (file-exists-p (jde-xref-get-db-directory))
211     (make-directory (jde-xref-get-db-directory)))
212   (jde-xref-update-xref-db )
213   (message "Finished creating xref database")
214   (add-hook 'after-save-hook 'jde-xref-file-saved))
215
216 (defun jde-xref-substring-member (str prefixlist)
217   "Like `member' but works with strings and will return true if any of
218   the prefixes in PREFIXLIST match STR"
219   (member-if (lambda (item) (string=
220                              (substring str 0 (min (length item)
221                                                    (length str)))
222                              item)) prefixlist))
223
224 (defun jde-xref-get-package-data ()
225   (let ((data (make-hash-table :test 'equal :size 10))
226         (caller-files (directory-files (jde-xref-get-db-directory)
227                                        t "caller$")))
228     (dolist (caller-file caller-files)
229       (let* ((package (mapconcat (lambda (x) x)
230                                  (butlast 
231                                   (split-string (car (last
232                                                       (split-string caller-file
233                                                                     "/")))
234                                                 "-")) "-"))
235              (package-data (jde-xref-load-package-hashes package)))
236         (puthash package package-data data)))
237     data))
238
239 (defun jde-xref-update-xref-db (&optional only-classes)
240   (let ((package-data (if only-classes
241                         (jde-xref-get-package-data)
242                         (make-hash-table :test 'equal :size 10)))
243         (subclasses (make-hash-table :test 'equal :size 500)))
244     ;; Remove all occurances of classes to be updated from the package-data's caller-hashes
245     (when only-classes
246       (maphash (lambda (package single-package-data)
247                  (maphash (lambda (callee callers)
248                             (puthash callee
249                                      (remove-if (lambda (item)
250                                                   (member (car item)
251                                                           only-classes))
252                                                 callers)
253                                      (nth 0 single-package-data)))
254                           (nth 0 single-package-data)))
255                package-data))
256     (with-all-class-infos-when (info)
257                                (lambda (class-file)
258                                  (or (null only-classes)
259                                      (jde-class-path-in-classes-p
260                                       class-file only-classes)))
261                                (jde-xref-add-class-info-to-db info package-data
262                                                               subclasses))
263     (setq jde-xref-parsed-classes nil)
264     (jde-xref-pickle-hash subclasses (jde-xref-get-subclass-file))
265     (setq jde-xref-subclasses subclasses)
266     (maphash (lambda (package data)
267                (jde-xref-pickle-hash (nth 0 data) 
268                                      (jde-xref-get-caller-file package))
269                (jde-xref-pickle-hash (nth 1 data)
270                                      (jde-xref-get-interface-file package))
271                (jde-xref-pickle-hash (nth 2 data)
272                                      (jde-xref-get-member-file package))
273                (jde-xref-pickle-hash (nth 3 data)
274                                      (jde-xref-get-superclass-file package)))
275              package-data)
276     (setq jde-xref-cache nil)))
277
278 (defun jde-xref-create-package-hashes (&optional fake)
279   "Returns a list of the three hashes that are in a package's data.
280 The hashes are for the caller-hash, the interface-hash, the
281 member-hash, and the superclass hash.  FAKE determines if we are just
282 creating them so that there is something to check against.  In those
283 circumstance we just create tiny hashes to conserve memory."
284   (list (make-hash-table :test 'equal :size (if fake 1 100))
285         (make-hash-table :test 'equal :size (if fake 1 20))
286         (make-hash-table :test 'equal :size (if fake 1 100))
287         (make-hash-table :test 'equal :size (if fake 1 20))))
288
289 (defun jde-xref-load-package-hashes (package)
290   (let ((data (jde-xref-create-package-hashes)))
291     (jde-xref-unpickle-hash (nth 0 data)
292                             (jde-xref-get-caller-file package))
293     (jde-xref-unpickle-hash (nth 1 data)
294                             (jde-xref-get-interface-file package))
295     (jde-xref-unpickle-hash (nth 2 data)
296                             (jde-xref-get-member-file package))
297     (jde-xref-unpickle-hash (nth 3 data)
298                             (jde-xref-get-superclass-file package))
299     data))
300
301 (defun jde-xref-append-hash (key value hash)
302   "Like `puthash' but appends VALUE to the HASH at KEY"
303   (puthash key (append (gethash key hash) (if (listp value)
304                                             value
305                                             (list value))) hash))
306
307 (defun jde-xref-add-class-info-to-db (info package-data subclasses)
308   (message (concat "Parsing class " (jde-parse-class-extract-classname info)))
309   (add-to-list 'jde-xref-parsed-classes
310                (jde-parse-class-extract-classname info))
311   (let ((package (jde-parse-get-package-from-name
312                   (jde-parse-class-extract-classname info))))
313     ;; If there is no existing package data
314     (unless (gethash package package-data)
315       (puthash package
316                ;; package-data's values are (caller-hash
317                ;; interface-hash method-and-field-hash)
318                (jde-xref-create-package-hashes)
319                package-data))
320     (destructuring-bind (caller-hash interface-hash
321                                      method-and-field-hash superclass-hash)
322         (gethash package package-data)
323       (puthash (jde-parse-class-extract-classname info)
324                (jde-parse-class-extract-interfaces info)
325                interface-hash)
326       (puthash (jde-parse-class-extract-classname info)
327                (append (jde-parse-class-extract-method-signatures info)
328                        (jde-parse-class-extract-field-signatures info))
329                method-and-field-hash)
330       (puthash (jde-parse-class-extract-classname info)
331                (jde-parse-class-extract-superclass info)
332                superclass-hash)
333       (jde-xref-append-hash
334        (jde-parse-class-extract-superclass info)
335        (jde-parse-class-extract-classname info) subclasses)
336       (dolist (call (nreverse
337                      (jde-parse-class-extract-method-calls info)))
338         (let ((calls (car call))
339               (called (cadr call)))
340           (if (or (not jde-xref-store-prefixes)
341                   (and
342                    (jde-xref-substring-member (car calls)
343                                               jde-xref-store-prefixes)
344                    (jde-xref-substring-member (car called)
345                                               jde-xref-store-prefixes)))
346               (let* ((dqcalled (list (car called)
347                                      (nth 1 called)
348                                      (when (nth 2 called)
349                                        (jde-parse-get-unqualified-name
350                                         (nth 2 called)))
351                                      ;; We don't want to need to
352                                      ;; know the constructor args
353                                      ;; for anonymous classes
354                                      (unless (jde-xref-is-class-anonymous (car called))
355                                        (mapcar 'jde-parse-get-unqualified-name
356                                                (nth 3 called)))))
357                      (called-package (jde-parse-get-package-from-name
358                                       (car dqcalled))))
359                 ;; Create the package data if needed
360                 (unless (gethash called-package package-data)
361                   (puthash called-package (jde-xref-create-package-hashes)
362                            package-data))
363                 (let* ((called-package-hashes
364                         (gethash called-package package-data))
365                        (called-package-caller-hash
366                         (car called-package-hashes)))
367                   ;; add things to the table - making sure there are no duplicates
368                   (puthash dqcalled
369                            (if (member calls (gethash
370                                               dqcalled
371                                               called-package-caller-hash))
372                                (gethash dqcalled called-package-caller-hash)
373                              (cons calls
374                                    (gethash dqcalled
375                                             called-package-caller-hash)))
376                            called-package-caller-hash)))))))))
377
378 (defun jde-xref-class-and-token-to-signature (class token)
379   (let ((ttype  (semantic-token-type token))
380         (tclass (semantic-token-token token))
381         (tname  (semantic-token-name token))) 
382     (list tclass
383           class
384           (if (equal tname (jde-parse-get-unqualified-name class))
385               "<init>"
386             tname)
387           (when (eq tclass 'function)
388             (if (or (not ttype) (equal ttype "void"))
389                 nil
390               (jde-parse-get-unqualified-name ttype)))
391           (if (eq tclass 'function)
392               (mapcar (lambda (arg)
393                         (jde-parse-get-unqualified-name
394                          (semantic-token-type arg)))
395                       (semantic-token-function-args token))
396             (list (jde-parse-get-unqualified-name ttype))))))
397
398 (defun jde-xref-get-current-class ()
399   (let ((package-name (jde-parse-get-package-name)))
400     (concat package-name (when  package-name ".") (replace-regexp-in-string "\\." "$" (jde-parse-get-class-at-point)))))
401
402 (defun jde-xref-get-current-signature ()
403   (unless (member
404            (semantic-token-token (semantic-current-nonterminal))
405                 '(function variable))
406     (error "The cursor must be in a function or class variable to get the callers"))
407   (jde-xref-class-and-token-to-signature
408    (jde-xref-get-current-class)
409    (semantic-current-nonterminal)))
410
411 ;;;###autoload
412 (defun jde-xref-first-caller (strict)
413   "Put the list of who calls the current function on the stack and
414 display the first caller.  Subsequent callers are displayed through
415 `jde-xref-show-next-caller'.  STRICT should be true if the callers of
416 interfaces to a function, or calls to a superclass which may result in
417 a virtual function call to the subclass should not be considered.  In
418 other words, if STRICT is true, then only calls that are definitely to
419 the requested function are considered."
420   (interactive "P")
421   (jde-xref-load-subclasses-table-if-necessary)
422   (setq jde-xref-stack (jde-xref-get-callers
423                             (jde-xref-get-current-signature) strict))
424   (jde-xref-next-caller))
425
426 (defun jde-xref-goto-caller (caller)
427   (jde-find-class-source (car caller))
428   (goto-line (nth 4 caller)))
429
430 ;;;###autoload
431 (defun jde-xref-next-caller ()
432   "If there are items still on the caller stack, pop the first one off
433 and show it"
434   (interactive)
435   (if (not jde-xref-stack)
436       (message "No more calls")
437     (unless (listp (car jde-xref-stack))
438       (pop jde-xref-stack)) ;; skip over called classname
439     (jde-xref-goto-caller (pop jde-xref-stack))))
440
441
442 (defun jde-xref-load-subclasses-table-if-necessary ()
443   (unless jde-xref-subclasses
444     (setq jde-xref-subclasses (make-hash-table :test 'equal :size 500))
445     (jde-xref-unpickle-hash jde-xref-subclasses
446                             (jde-xref-get-subclass-file))
447     ;; if subclasses were empty, then it's the first time this is run,
448     ;; so do our one-time initializations
449     (add-hook 'after-save-hook 'jde-xref-file-saved)))
450
451 (defun jde-xref-signature-to-string (sig)
452   (concat (or (nth 3 sig) "void") " " (cadr sig) "."
453           (if (equal (nth 2 sig) "<init>")
454             (jde-parse-get-unqualified-name (cadr sig))
455             (nth 2 sig))
456           (when (eq (car sig) 'function)
457             (concat "("
458                     (mapconcat (lambda (x) x) (nth 4 sig) ",") ")"))))
459
460 (defun jde-xref-find-package-in-cache (package cache)
461   (when cache
462     (if (equal (caar cache) package)
463       (cdar cache)
464       (jde-xref-find-package-in-cache package (cdr cache)))))
465
466 (defun jde-xref-get-caller-file (package)
467   (concat (jde-xref-get-db-directory) "/" package "-caller"))
468
469 (defun jde-xref-get-interface-file (package)
470   (concat (jde-xref-get-db-directory) "/" package "-interfaces"))
471
472 (defun jde-xref-get-member-file (package)
473   (concat (jde-xref-get-db-directory) "/" package "-members"))
474
475 (defun jde-xref-get-superclass-file (package)
476   (concat (jde-xref-get-db-directory) "/" package "-superclasses"))
477
478 (defun jde-xref-get-subclass-file ()
479   (concat (jde-xref-get-db-directory) "/subclasses"))
480
481 (defun jde-xref-find-or-create-package-in-cache (package)
482   (unless jde-xref-db-base-directory
483     (error "The variable `jde-xref-db-base-directory' must be specified to load the xref db"))
484   (if (file-exists-p (jde-xref-get-caller-file package))
485     (or (jde-xref-find-package-in-cache package jde-xref-cache)
486         ;; Or we need to get the new package and put it in the cache
487         (let ((data (jde-xref-load-package-hashes package)))
488         (setq jde-xref-cache (cons (cons package data)
489                                    (if (> (length jde-xref-cache)
490                                           jde-xref-cache-size)
491                                        (cdr jde-xref-cache)
492                                      jde-xref-cache)))
493         data))
494     (jde-xref-create-package-hashes t)))
495
496 (defun jde-xref-get-caller-hash (package)
497   (nth 0 (jde-xref-find-or-create-package-in-cache package)))
498
499 (defun jde-xref-get-interface-hash (package)
500   (nth 1 (jde-xref-find-or-create-package-in-cache package)))
501
502 (defun jde-xref-get-member-hash (package)
503   (nth 2 (jde-xref-find-or-create-package-in-cache package)))
504
505 (defun jde-xref-get-superclass-hash (package)
506   (nth 3 (jde-xref-find-or-create-package-in-cache package)))
507
508 (defun jde-xref-get-basic-caller (sig)
509   (gethash (cdr sig) (jde-xref-get-caller-hash (jde-parse-get-package-from-name
510                                                 (nth 1 sig)))))
511
512 (defun jde-xref-get-members (class)
513   (gethash class (jde-xref-get-member-hash (jde-parse-get-package-from-name
514                                             class))))
515
516 (defun jde-xref-get-superclass (class)
517   (gethash class (jde-xref-get-superclass-hash (jde-parse-get-package-from-name
518                                                 class))))
519
520 (defun jde-xref-is-class-anonymous (class)
521   (string-match "\\$[0-9]+$" class))
522
523 (defun jde-xref-is-caller-anonymous-class (caller)
524   (jde-xref-is-class-anonymous (nth 0 caller)))
525
526 (defun jde-xref-is-sig-anonymous-class (sig)
527   (jde-xref-is-class-anonymous (nth 1 sig)))
528
529 (defun jde-xref-get-callers (sig &optional strict)
530   (let ((typesig (car sig))
531     (classname (cadr sig)))
532     (append
533      ;; if we're an anonymous class, then we want to see where we are
534      ;; created, since it kind of goes along with usage of the function.
535      (when (jde-xref-is-sig-anonymous-class sig)
536        (jde-xref-get-basic-caller (list typesig classname "<init>" nil nil)))
537
538      (jde-xref-get-basic-caller sig)
539      (unless strict
540        (apply 'append
541           (mapcar
542            (lambda (classname)
543          (let* ((sig `(,typesig ,classname ,@(cddr sig)))
544             (callers-for-classname (jde-xref-get-basic-caller sig)))
545            (when callers-for-classname
546              (cons classname callers-for-classname)))) ;; include classname in the usage list
547            (jde-xref-get-subs classname sig (jde-xref-get-supers classname nil))))))))
548
549
550 (defun jde-xref-get-supers (classname collect)
551   (mapc (lambda (super)
552       (unless (member super collect)
553         (setq collect (jde-xref-get-supers super (cons super collect)))))
554     (let* ((package (jde-parse-get-package-from-name classname))
555            (superclass (jde-xref-get-superclass classname))
556            (superinterfaces (gethash classname (jde-xref-get-interface-hash package))))
557       (if superclass
558           (cons superclass superinterfaces)
559         superinterfaces)))
560   collect)
561     
562
563 (defun jde-xref-get-subs (classname sig collect)
564   (mapc (lambda (subclass)
565         (unless (or (member subclass collect) (member (cddr sig) (jde-xref-get-members subclass)))
566           (setq collect (jde-xref-get-subs subclass sig (cons subclass collect)))))
567       (gethash classname jde-xref-subclasses))
568   collect)
569
570
571 (defun jde-xref-notify (widget child &optional event)
572   (jde-xref-goto-caller (widget-get widget :caller)))
573
574 (defun jde-xref-caller-to-sig (caller)
575   (list 'function (nth 0 caller) (nth 1 caller) (when (nth 2 caller) (jde-parse-get-unqualified-name (nth 2 caller)))
576         (mapcar 'jde-parse-get-unqualified-name (nth 3 caller))))
577
578 (defun jde-xref-tree-get-children (sig)
579   (when sig
580     (mapcar
581      (lambda (caller)
582        (if (listp caller)
583        (let  ((caller-sig (jde-xref-caller-to-sig caller)))
584          (list
585           'tree-widget
586           :node `(push-button
587               :tag ,(jde-xref-signature-to-string caller-sig)
588               :format "%[%t%]\n"
589               :sig ,caller-sig
590               :caller ,caller
591               :notify jde-xref-notify)
592           :dynargs 'jde-xref-tree-get-children-from-tree
593           :sig caller-sig
594           :has-children t))
595      (list 'tree-widget :tag caller))) ;; class for next set of usages
596        (jde-xref-get-callers sig))))
597
598 (defun jde-xref-tree-get-children-from-tree (tree)
599   (jde-xref-tree-get-children (widget-get tree :sig)))
600
601 ;;;###autoload
602 (defun jde-xref-display-call-tree (strict)
603   "Display an interactive call tree of which function call the current
604   function, which can be expanded outward.  STRICT should be true if
605   the callers of interfaces to a function, or calls to a superclass
606   which may result in a virtual function call to the subclass should
607   not be considered.  In other words, if STRICT is true, then only
608   calls that are definitely to the requested function are considered. "
609   (interactive "P")
610   (jde-xref-load-subclasses-table-if-necessary)
611   (let* ((sig (jde-xref-get-current-signature))
612          (buf (get-buffer-create (concat "JDE call graph for "
613                                          (jde-xref-signature-to-string
614                                           sig)))))
615     (switch-to-buffer buf)
616     (erase-buffer)
617     (widget-create 'tree-widget
618                    :tag (jde-xref-signature-to-string sig)
619                    :dynargs 'jde-xref-tree-get-children-from-tree
620                    :has-children t
621                    :sig sig)
622     (use-local-map widget-keymap)
623     (widget-setup)))
624
625 (defun jde-xref-get-class-variables (class-token)
626   (mapcan (lambda (token)
627             (when (eq (semantic-token-token token) 'variable)
628               (list token)))
629           (semantic-nonterminal-children class-token)))
630
631 ;;;###autoload
632 (defun jde-xref-list-uncalled-functions (strict)
633   "Displays a simple list of function that are never called, at least
634 not directly.  Do not assume that this means this code can never be
635 reached, since reflection could always call any method.  Use this list
636 and your best judgement to figure out what are good candidates for
637 code cleanup.  STRICT should be true if the callers of interfaces to a
638 function, or calls to a superclass which may result in a virtual
639 function call to the subclass should not be considered.  In other
640 words, if STRICT is true, then only calls that are definitely to the
641 requested function are considered.  This function could take a
642 while. If it does, you might want to consider increasing
643 `jde-xref-cache-size'."
644   (interactive "P")
645   (jde-xref-load-subclasses-table-if-necessary)
646   (save-excursion
647     (flet ((get-unused-string (token)
648              (goto-char (semantic-token-start token))
649              (unless (jde-xref-get-callers
650                       (jde-xref-class-and-token-to-signature
651                        (jde-xref-get-current-class) token) strict)
652                (list (jde-xref-signature-to-string
653                       (jde-xref-class-and-token-to-signature
654                        (jde-xref-get-current-class) token))))))
655     (let ((uncalled-methods
656             (mapcan 'get-unused-string
657                     (semantic-find-nonterminal-by-token 'function
658                                                         (current-buffer)
659                                                         t)))
660           (unreferenced-variables
661             (mapcan 'get-unused-string
662                     (mapcan 'jde-xref-get-class-variables
663                             (semantic-find-nonterminal-by-type "class"
664                                                                (current-buffer)
665                                                                t))))
666           (outbuf (get-buffer-create "Unreferenced Methods and Members")))
667       (switch-to-buffer outbuf)
668       (erase-buffer)
669       (insert "The following is a list of methods and members that are\n")
670       (insert "uncalled directly by any Java classes that are in the\n")
671       (insert "following locations: \n")
672       (insert (mapconcat (lambda (x) x) jde-built-class-path ", "))
673       (newline)
674       (newline)
675       (if uncalled-methods
676         (progn
677           (insert "Unreferenced methods:\n")
678           (insert (mapconcat (lambda (x) x) uncalled-methods "\n")))
679         (insert "There are no uncalled methods\n\n"))
680       (if unreferenced-variables
681         (progn
682           (insert "\n\nUnreferenced class variables:\n")
683           (insert (mapconcat (lambda (x) x) unreferenced-variables "\n")))
684         (insert "\n\nThere are no unreferenced variables\n\n"))
685       (toggle-read-only)
686       (not-modified)))))
687
688 (defun jde-xref-remove-classes-from-subclasses-table (classes)
689   (maphash (lambda (key value)
690              (puthash key
691                       (remove-if (lambda (item)
692                                    (member item classes)) value)
693                       jde-xref-subclasses))
694            jde-xref-subclasses))
695
696 ;;;###autoload
697 (defun jde-xref-update (&rest ignored)
698   "Update the caller table after a recompile.  This can be called by
699 the user when they recompile outside of emacs.  It will update the
700 call list of all files modified in emacs"
701   (interactive)
702   (message "Updating xref tables")
703   (when jde-xref-modified-classes
704     (jde-xref-remove-classes-from-subclasses-table
705      jde-xref-modified-classes)
706     (jde-xref-update-xref-db jde-xref-modified-classes)
707     (message "Finished updateing xref database"))
708   (setq jde-xref-modified-classes nil))
709
710 (defun jde-xref-file-saved ()
711   (when (eq major-mode 'jde-mode)
712     (setq jde-xref-modified-classes
713           (append jde-xref-modified-classes
714                   (mapcar (lambda (class-token)
715                             (concat (jde-parse-get-package-name)
716                                     (when (jde-parse-get-package-name) ".")
717                                     (semantic-token-name class-token)))
718                           (semantic-find-nonterminal-by-type
719                            "class" (current-buffer) t))))))
720
721 ;;;###autoload
722 (defun jde-xref-customize ()
723   "Display the customization buffer for the xref package."
724   (interactive)
725   (customize-group "jde-xref"))
726
727 (global-set-key (kbd "C-c C-v a") 'jde-xref-first-caller)
728 (global-set-key (kbd "C-c C-v n") 'jde-xref-next-caller)
729
730
731 ;; Register and initialize the customization variables defined
732 ;; by this package.
733 (jde-update-autoloaded-symbols)
734
735 (provide 'jde-xref)
736
737 ;; $Log: jde-xref.el,v $
738 ;; Revision 1.24  2004/06/06 14:19:23  ahyatt
739 ;; Fixed bug with jde-xref-next-caller - discovery and fix by Raul Acevedo
740 ;;
741 ;; Revision 1.23  2003/11/07 04:27:14  ahyatt
742 ;; Invalid  hooks removed
743 ;;
744 ;; Revision 1.22  2003/10/17 03:42:57  ahyatt
745 ;; Speed improvements to jde-xref.  Thanks to Suraj Acharya.
746 ;;
747 ;; Revision 1.21  2003/10/09 05:58:55  ahyatt
748 ;; David Ponce's fixes pertaining to the upcoming Semantic 2.0, plus
749 ;; fixes of my own to get his changes working with the existing code.
750 ;;
751 ;; Revision 1.20  2003/09/27 04:53:35  ahyatt
752 ;; Put in Suraj Acharya's fixes, fixed a no-prefix bug (but still don't allow no prefixes)
753 ;;
754 ;; Revision 1.19  2003/09/02 22:05:43  ahyatt
755 ;; We weren't doing any subclass checks!  Fixed this problem, then fixed
756 ;; a problem where the subclass checks caused an infinite recursion
757 ;; problem.
758 ;;
759 ;; Revision 1.18  2003/07/15 05:19:47  ahyatt
760 ;; Fix for bug where users could not find the callers to interface functions
761 ;;
762 ;; Revision 1.17  2003/07/09 06:11:20  ahyatt
763 ;; Made jde-xref-store-prefixes non-optional.  I think if we didn't do
764 ;; this, most people would leave it blank, therefore significantly
765 ;; impacting both size of the database and the time and memory it takes
766 ;; to make it.
767 ;;
768 ;; Revision 1.16  2003/05/07 04:38:45  ahyatt
769 ;; Uncommenting jde-update-autoloaded-symbols
770 ;;
771 ;; Revision 1.15  2003/05/06 06:50:55  ahyatt
772 ;; Fixes recent regression with default-directory not getting set back after making the xref db (if the jde-built-class-path contains jars)
773 ;;
774 ;; Revision 1.14  2003/05/03 09:00:53  paulk
775 ;; Fixed bug in jde-xref-update-xref-db that cause incorrect resolution
776 ;; of db path specified relative to project file.
777 ;;
778 ;; Revision 1.13  2003/03/08 06:32:02  ahyatt
779 ;; Simplified using new jde-class functionality
780 ;;
781 ;; Revision 1.12  2003/01/07 15:27:37  ahyatt
782 ;; Needed to clear the cache after updating.
783 ;;
784 ;; Revision 1.11  2003/01/02 05:10:20  ahyatt
785 ;; Added ability for calls to superclasses to show up (under non-strict
786 ;; mode) as calls to the subclass.  So if class B inherits from class A,
787 ;; then a call to a method on a class of type A (at compile time), may in
788 ;; fact call a method on B at runtime.  This is turned off in STRICT mode.
789 ;;
790 ;; Revision 1.10  2002/12/18 04:09:15  ahyatt
791 ;; Fixes a usability problem with call-trees, and fixes a bug where some
792 ;; classes never get added to the xref database
793 ;;
794 ;; Revision 1.9  2002/12/06 03:24:45  ahyatt
795 ;; Fixed bug in call-tree, where no expansion could take place after the
796 ;; 2nd level. Also renamed the argument to jde-xref-goto-caller, which takes
797 ;; a caller, not a sig.
798 ;;
799 ;; Revision 1.8  2002/12/04 04:29:19  ahyatt
800 ;; Got the call-tree to finally work.  Also made some adjustements for
801 ;; anonymous classes.  These adjustments probably will disappear with
802 ;; some later rework I have planned.
803 ;;
804 ;; Revision 1.6  2002/12/01 02:50:48  ahyatt
805 ;; Major revision - I realized that the previous version had neglected to
806 ;; factor in subclasses of a class when computing the callers to a
807 ;; function.  I added this functionality, which meant I had to now keep
808 ;; track of all subclasses and variables and functions of each class
809 ;; (because we have to walk up the subclass tree until a function or
810 ;; variable gets overriden).  Also, I simultaneously totally redid the
811 ;; database storage.  Instead of storing the result in a huge hash table,
812 ;; we now split the file into packages which are loaded on demand and
813 ;; cached for later use.  I removed user visible variables
814 ;; `jde-xref-db-file' and `jde-xref-interface-file' and replaced it with
815 ;; `jde-xref-db-base-directory', a directory on which an "xrefdb"
816 ;; directory is created which stores everything necessary.  BTW, there is
817 ;; a reason I'm not using the beanshell to get this information -
818 ;; basically I don't see how to handle arrays properly via reflection,
819 ;; and without getting the array information I need from signatures, I
820 ;; can't match the signatures I'm getting from the class files directly.
821 ;;
822 ;; Revision 1.5  2002/11/25 00:42:34  ahyatt
823 ;; Polished jde-xref-list-uncalled-functions, making the text make more
824 ;; sense, the buffer read-only, and converting "<init>" functions back to
825 ;; their proper names.
826 ;;
827 ;; Revision 1.4  2002/11/21 04:26:33  ahyatt
828 ;; Changed my e-mail address to andy_jde@thehyatts.net
829 ;;
830 ;; Revision 1.3  2002/11/21 04:18:41  paulk
831 ;; These packages, when autoloaded, now register and initialize the customization variables
832 ;; that they define to the values specified in the current project file.
833 ;;
834 ;; Revision 1.2  2002/11/21 04:03:47  ahyatt
835 ;; Fixed a bug in jde-parse-class where two functions had the same
836 ;; definition.  The fix involved renamed a few classes to keep
837 ;; consistency.  jde-xref had to change as well.
838 ;;
839 ;; Removed a (newline) function call in jde-xref and replaced it with a "\n"
840 ;;
841 ;; In jde-xref, rewrote the parts of jde-xref-make-db-from-path which
842 ;; dealt with keeping track of the already parsed classes. Previous
843 ;; solution was really kludgey, this solution is only somewhat kludgey.
844 ;; I'm still thinking about this problem.
845 ;;
846 ;; Revision 1.1  2002/11/18 07:02:18  paulk
847 ;; Initial release.
848 ;;
849
850 ;; end of jde-xref.el