Here is the latest version of jde-import-all, which works with
jde-2.3.3.
Cheers
Phil
;;; jde-import-all.el --- Import everything at once.
;; Copyright (C) 2004 Phillip Lord
;; Author: Phillip Lord <[EMAIL PROTECTED]>
;; COPYRIGHT NOTICE
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;; Build imports for all types in the buffer. This package has two
;; entry points. `jde-import-all' finds all declared types in the
;; buffer, then presents the user with a nice dialog which they can
;; then choose as many as they wish. `jde-import-all-unambiguous'
;; finds all the declared types in the buffer, and then imports
;; without asking the user any imports which identify a class
;; unambigiously.
;;
;; There are not user options other than those in `jde-import' which
;; this respects.
;;
;; This package is really meant for rolling into jde-import. There's
;; also a dialog near the end which uses the efc namespace, because it
;; probably belongs there.
;;
;; This package uses regexp searching to find all the types, as the
;; semantic grammar doesn't appear to go deep enough for use
;; here. Like other parts of JDE it depends on use of coding standards
;; (upper case for types). It will miss types not conforming to this.
;;; Bugs:
;;
;; This does not correctly identifer InnerClasses which need to be
;; imported via their parent. Probably requires both lisp and
;; beanshell support.
;;
;;; History:
;;
;; - fixed bug with regexp, which failed to pick up "implements"
;; types, or multiple types after extends
;;
;;; Code:
(require 'sregex)
(require 'jde-import)
(defun jde-import-all-regexp()
"Get the regexp set to find all types."
(let ((white-nl
'(1+ (or (syntax ?-) "\n")))
(white-nl-0
'(0+ (or (syntax ?-) "\n")))
(non-identifier
'(1+ wordchar))
;; match an upper case begining word, which we will take as a
;; type.
(identifier
'(group
(char (?A . ?Z))
(1+ wordchar))))
(list
(cons
;; match anything following instanceof, new, or extends. These
;; are keywords, so are guarenteed to be classes
(sregex '(or "new" "instanceof" "throws")
white-nl
'(group (1+ wordchar)))
1)
(cons
;; match anything following extends or implements. This will
;; then be split on commas. Both extends and implements can
;; carry multiple Class names after them, as Interfaces can
;; extend multiple other interfaces.
(sregex '(or "extends" "implements")
white-nl-0
`(group
(1+ (or ,identifier "," (syntax ?-)))))
'jde-import-match-comma-split)
(cons
;; match anything in between parens and starting with upper
;; case. This picks up casts. This will also pick up things like
;; debugGraphics.setDebugOptions(FLASH_OPTIONS); where
;; FLASH_OPTIONS is actually a static. Generally this won't be a
;; problem because it shouldn't resolve as a class. `
(sregex '(char ?( )
identifier
'(char ?)))
1)
;; match an .class type thing..
(cons
(sregex identifier ".class")
1)
;; match variable declaration
(cons
(sregex white-nl
identifier
white-nl
non-identifier
white-nl-0
'(or ";" "="))
1)
;; match formal parameters
(cons
(sregex '(or "(" ",")
white-nl-0
identifier
white-nl
non-identifier
white-nl-0
'(or "," ")"))
1)
)))
(defun jde-import-all-get-types()
"Return all the types in the current buffer.
The result is a list with no duplicates."
(let ((elements))
(mapcar
(lambda(matcher)
(let ((matches
(jde-import-all-gather matcher)))
(mapcar
(lambda(match)
(add-to-list 'elements match))
matches)))
(jde-import-all-regexp))
elements))
(defun jde-import-all-gather(matcher)
"Get all matches in the current buffer.
MATCHER is a cons (regexp . group). Looks for matches to the regexp
and stores the match in the group. Only matches not in strings are
accepted. This list can contain duplicates."
(let ((regexp (car matcher))
(group (cdr matcher))
(case-fold-search nil)
(retn))
(save-excursion
(goto-char (point-min))
(while (re-search-forward
regexp
(point-max) t)
(setq retn
(append
(if (numberp group)
(jde-import-default-match group)
(funcall group))
retn))))
retn))
(defun jde-import-default-match(group)
(goto-char (match-end group))
(unless
(jde-parse-comment-or-quoted-p)
(list
(match-string-no-properties group))))
(defun jde-import-match-comma-split()
(goto-char (match-end 1))
(unless
(jde-parse-comment-or-quoted-p)
(split-string
(match-string-no-properties 1)
"[ ,]+")))
(defun jde-import-all-import-types(list)
"Import all types.
Accepts a list of Strings of unqualified classes"
;; we have a list of strings
(setq list
(jde-import-all-expand-strip-exclude list))
;; so we now have a list of lists of strings. We want to over a
;; dialog showing all of these one after the other..
(let ((dialog
(jde-import-all-dialog
"Multi Classes Option"
:options list
:text "Select imports to insert."))
(selection))
(efc-dialog-show dialog)
(setq selection
(delq nil (oref dialog selection)))
(if (and selection
(< 0 (length selection)))
(jde-import-insert-imports-into-buffer selection))))
(defun jde-import-all-expand-strip-exclude(list)
"Qualifies, strips and excludes imports.
This function takes in a list of imports. It removes all the existing
imports, qualifies them and then strips excluded imports. It returns a
list of lists of strings. And all in one function."
(delq nil
(mapcar
(lambda(unqualified-class)
;; we want to remove any imports which already exists
(if (jde-import-get-existing-import unqualified-class)
nil
(jde-import-exclude-imports
;; we want to kill of the java.lang
;; packges. exclude-imports will probably do this for
;; us. But this is not good as it will leave the other
;; possibilities. So "String" will be imported as
;; "apache.xpath.String" (on my machine) which is almost
;; exactly the wrong behaviour
;;
;; I'm not really happy with this but what can you do.
(jde-import-all-kill-lang-classes
;; we want to expand any unqualified names, this returns
;; a list of string possibilities
(jde-jeval-r
(concat "jde.util.JdeUtilities.getQualifiedName(\""
unqualified-class "\");"))))))
list)))
(defun jde-import-all-kill-lang-classes(list)
(let ((ismatch nil))
(mapc
(lambda(item)
(if (string-match "^java\\.lang\\.[^.]*$" item)
(setq ismatch t)))
list)
(if ismatch
nil
list)))
(defun jde-import-all-unambiguous()
"Import all unambigious imports.
This function imports all imports for which the name identifies a
unique class in the classpath. This avoids presenting the user with a
large dialog, which can take a while to build if there are many
imports."
(interactive)
(let ((list
(jde-import-all-expand-strip-exclude (jde-import-all-get-types)))
(retn))
(delq nil
;; take single length sublists, and return item..
(mapcar
(lambda(item)
(if (= 1 (length item))
(add-to-list 'retn
(car item))))
list))
(if (< 0 (length retn))
(jde-import-insert-imports-into-buffer retn))))
(defun jde-import-all()
"Attempt to import all types in the current buffer.
The user will be prompted with an interactive dialog, prompting
for alternatives."
(interactive)
(jde-import-all-import-types
(jde-import-all-get-types)))
;; define a nifty option dialog for asking for multiple options.
;; I've never used eieio before. Very cute...
(defclass efc-multi-option-dialog (efc-option-dialog)
((options :initarg :options
:documentation
"Option of options from which to choose")
(radio-buttons :initarg :radio-buttons
:documentation
"Buttons for selecting options")
(text :initarg :text
:type string
:initform "Select option."
:documentation
"Text at top")
(build-message :initarg :text
:type string
:initform "Building Dialog"
:documentation
"Warning message while building dialog, as this can be slow")
(selection :initarg :selection
:documentation
"Options choosen."))
"Provides a dialog with several sets of OPTIONS.
The dialog sets SELECTION to the options selected by the user.")
(defmethod initialize-instance ((this efc-multi-option-dialog) &rest fields)
"Dialog constructor."
(call-next-method))
(defmethod efc-dialog-create ((this efc-multi-option-dialog))
(message "%s..." (oref this build-message))
(widget-insert (oref this text))
(widget-insert "\n\n")
;; use radio buttons slot as list of radio buttons rather than.
(oset this radio-buttons
(mapcar
(lambda(list)
(prog1
(widget-create
(list
'radio-button-choice
:value
(efc-multi-option-dialog-default this list)
:args (mapcar
(lambda (x)
(list 'item x))
list)))
(widget-insert "\n")))
(efc-multi-option-dialog-sort this
(oref this options))))
(widget-insert "\n")
(message "%s...done" (oref this text)))
(defmethod efc-dialog-ok((this efc-multi-option-dialog))
;; set the selection up as a list rather a simple result
(oset this selection
(mapcar
(lambda(widget)
(widget-value widget))
(oref this radio-buttons)))
(delete-window)
(set-buffer (oref this initbuf))
(pop-to-buffer (oref this initbuf))
(kill-buffer (oref this buf))
(exit-recursive-edit))
(defmethod efc-multi-option-dialog-default ((this efc-multi-option-dialog) list)
"Pick the default from a collection of options."
(if (= 1 (length list))
(car list)))
(defmethod efc-multi-option-dialog-sort ((this efc-multi-option-dialog) list)
"Sort the options."
;; sort the ones with the most options first...
(sort list
(lambda(a b)
(> (length a)
(length b)))))
;; define a more specific dialog, which searches things differently.
(defclass jde-import-all-dialog(efc-multi-option-dialog) nil)
(defmethod initialize-instance ((this jde-import-all-dialog) &rest fields)
"Dialog constructor."
(call-next-method))
(defmethod efc-multi-option-dialog-sort ((this jde-import-all-dialog) list)
"Sort the options."
;; sort the ones with the most options first...
(sort list
(lambda(a b)
;; sort lexically
(if (= (length a)
(length b))
(string< (car a)
(car b))
;; or by length
(> (length a)
(length b))))))
;;; code to kill lots of extra imports in many different buffers. This
;;; is a bit of a hack at the moment.
(defun jde-import-all-search-path()
"Find source root directories.
This code is chopped straight from `jde-find' which is a little
unfortunate."
(read-from-minibuffer
"Search directories: "
(cons
(mapconcat
(lambda (x) x)
(cond
(jde-sourcepath
(mapcar
(lambda (path)
(jde-normalize-path path 'jde-sourcepath))
jde-sourcepath))
(jde-compile-option-sourcepath
(mapcar
(lambda (path)
(jde-normalize-path path 'jde-compile-option-sourcepath))
jde-compile-option-sourcepath))
(jde-compile-option-classpath
(mapcar
(lambda (path)
(jde-normalize-path path 'jde-compile-option-classpath))
jde-compile-option-classpath))
(jde-global-classpath
(mapcar
(lambda (path)
(jde-normalize-path path 'jde-global-classpath))
jde-global-classpath))
(t
(list default-directory)))
" ")
0)
nil nil 'jde-find-root-history))
(defun jde-import-all-find-internal (buffer &optional dirs)
(if (not (executable-find
(if (eq system-type 'windows-nt) "find.exe" "find")))
(error (list "This command requires the Unix find utility.")))
(let* ((directories-option
(if dirs dirs "."))
(cmd
(format "find %s -type f -name \"%s\""
directories-option
(car jde-find-file-regexp))))
(shell-command cmd buffer)))
(put 'jde-import-all-kill-extra-imports 'disabled
"It is not possible to undo this action")
(defun jde-import-all-kill-extra-imports()
"Kill extraneous imports in many buffers.
This function uses the unix find command to discover all java files
in the current project and then removes all extranous import statements
from them.
This function is a little bit scary and may not behave correctly.
Even if it does it can't be undone. And even if you don't want to
undo it, its user interface is very poor as it's very noisy telling
you lots of things you don't want to know, and nothing that you
actually want to know. In short its a hack."
(interactive)
(save-some-buffers)
(with-temp-buffer
(let ((files-buffer (current-buffer))
(current-buffers (buffer-list))
(total-files)
(onfile 0))
(message "Discovering files...")
(jde-import-all-find-internal
files-buffer
(jde-import-all-search-path))
(message "Discovering files...done")
(setq total-files
(count-lines (point-min) (point-max)))
(dolist (file (split-string (buffer-string)))
(message "On %s from %s file %s"
(incf onfile) total-files
file)
(jde-import-all-kill-imports file current-buffers)))))
(defun jde-import-all-kill-imports(file current-buffers)
(let ((file-buffer (find-file-noselect file)))
(save-excursion
(set-buffer file-buffer)
(jde-import-kill-extra-imports)
(save-buffer)
(if (not (member file-buffer current-buffers))
(kill-buffer (current-buffer))))))
;; test functions...
(defun jde-import-find-all-java()
(interactive)
(jde-import-all-find-internal
(jde-import-all-search-path)))
(defun jde-import-all-search-path-test()
(interactive)
(message "search path is %s"
(jde-import-all-search-path)))
(defun jde-import-all-show-gather()
"Show all of the type declarations shown in a special buffer"
(interactive)
(save-excursion
(set-buffer (get-buffer-create "*jde import*"))
(erase-buffer))
(mapcar
(lambda(matcher)
(let ((matches
(jde-import-all-gather matcher)))
(save-excursion
(set-buffer "*jde import*")
(insert (format "Matching %s" matcher))
(insert "\n")
(mapcar
(lambda(match)
(insert (concat match "\n")))
matches))))
(jde-import-all-regexp))
(switch-to-buffer-other-window "*jde import*"))
(defun jde-import-all-show()
"Show all of the type declarations"
(interactive)
(let ((import
(jde-import-all-get-types)))
(save-excursion
(set-buffer (get-buffer-create "*jde import*"))
(erase-buffer)
(mapcar
(lambda(match)
(insert match)
(insert "\n"))
import))))
(provide 'jde-import-all)
;;; jde-import-all.el ends here