Mercurial > hg > xemacs-beta
diff lisp/oobr/br-java.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 4103f0995bd7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/oobr/br-java.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,400 @@ +;;!emacs +;; +;; FILE: br-java.el +;; SUMMARY: Support routines for Java inheritance browsing. +;; USAGE: GNU Emacs Lisp Library +;; KEYWORDS: c, oop, tools +;; +;; AUTHOR: Bob Weiner +;; ORG: Motorola Inc. +;; +;; ORIG-DATE: 01-Aug-95 +;; LAST-MOD: 4-Oct-95 at 13:31:43 by Bob Weiner +;; +;; Copyright (C) 1995 Free Software Foundation, Inc. +;; See the file BR-COPY for license information. +;; +;; This file is part of the OO-Browser. +;; +;; DESCRIPTION: +;; DESCRIP-END. + +;;; ************************************************************************ +;;; Other required Elisp libraries +;;; ************************************************************************ + +(mapcar 'require '(br-lib hypb hasht)) + +;;; ************************************************************************ +;;; User visible variables +;;; ************************************************************************ + +(defvar java-class-keyword + "\\(class\\|interface\\)[ \t\n]+" + "*Keyword regexp preceding a java class declaration or definition.") + +(defvar java-lib-search-dirs nil + "List of directories below which java Library source files are found. +Subdirectories of Library source are also searched. A Library is a stable +group of classes.") + +(defvar java-sys-search-dirs nil + "List of directories below which java System source files are found. +Subdirectories of System source are also searched. A System class is one +that is not yet reusable and is likely to change before release.") + +(defvar java-package-name nil + "Name of current packge if any. Nil otherwise.") + +(defconst java-narrow-view-to-class nil + "*Non-nil means narrow buffer to just the matching class definition when displayed.") + +;;; ************************************************************************ +;;; Internal functions +;;; ************************************************************************ + +(defun java-get-classes-from-source (filename &optional skip-tags + skip-tags-cleanup) + "Scans FILENAME and returns cons of class list with parents-class alist. +Handles multiple inheritance. Assumes file existence and readability have +already been checked. + With optional SKIP-TAGS non-nil, does not compute and store lookup tags +for member definitions. If SKIP-TAGS is nil, normally a cleanup +function is called after scanning the members. SKIP-TAGS-CLEANUP +non-nil suppresses this action." + (let ((no-kill (get-file-buffer filename)) + class-name-end classes class has-parents open-brace-point end + parents-start parents parent-cons parent-list signatures) + (if no-kill + (set-buffer no-kill) + (funcall br-view-file-function filename)) + ;; Don't bother saving anything for this temporary buffer + (buffer-disable-undo (current-buffer)) + (setq buffer-auto-save-file-name nil) + ;; Make life simpler + (br-lang-mode) + ;; Static initializers confuse the parser and don't define anything + ;; that we need, so remove them. + (java-strip-static-code) + ;; Is more than one package statement allowed? + (setq java-package-name (java-get-package-name)) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (while (re-search-forward java-class-def-regexp nil t) + (setq has-parents + (not (= ?{ (char-after + (match-beginning java-class-def-derived-grpn)))) + parents-start (match-beginning java-class-def-derived-grpn) + end (match-end 0) + class-name-end (match-end java-class-def-name-grpn) + ;; + ;; Now since we've saved all the match expressions we need + ;; from our last regexp match, we can call functions which + ;; change the match data below here. + class (java-normalize-class-match)) + (goto-char parents-start) + (setq parent-list (if has-parents + ;; Return parents as a list. + (java-scan-parents end))) + (if (and (null parent-list) + (not (equal class "Object"))) + ;; All classes have Object as an ancestor, so if + ;; no parents are listed, make Object the sole parent. + (setq parent-list '("Object"))) + ;; Ensure class name not found within a comment + (if (c-within-comment-p) + (progn (search-forward "*/" nil t) + (setq class nil parent-cons nil)) + (setq parent-cons (cons parent-list class) + classes (cons class classes) + parents (cons parent-cons parents)) + (or skip-tags + ;; Scan members defined within class + (progn (goto-char class-name-end) + (if (search-forward "{" nil t) + (progn (setq open-brace-point (point)) + (backward-char) + ;; Move to class close brace but ignore + ;; any error if braces are unbalanced. + ;; Let the compiler tell the user about + ;; this. + (if (condition-case () + (progn (forward-sexp) t) + (error nil)) + (setq signatures + (append + signatures + (java-scan-features + class open-brace-point + (point))))))))))))) + (if skip-tags + nil + (java-get-feature-tags buffer-file-name (java-sort-features signatures)) + (or skip-tags-cleanup (br-feature-tags-save))) + (or no-kill + (progn (set-buffer-modified-p nil) + (kill-buffer (current-buffer)))) + (cons classes (delq nil parents)))) + +(defun java-get-package-name() + "Return the package name of the current file." + (save-excursion + (goto-char (point-min)) + (if (re-search-forward java-package-name-regexp nil t) + (buffer-substring (match-beginning java-package-name-grpn) + (match-end java-package-name-grpn)) + ""))) + +(defun java-split-identifier (name) + "Return list of component words (in reverse order) of the given NAME." + (or (hash-lookup name java-package-htable) + (let ((s name) + start words tmp) + (while (and (not (null s)) (> (length s) 0)) + (setq start (string-match java-package-word-regexp s)) + (if start + (progn + (setq tmp (substring s (match-beginning 1) (match-end 1))) + (setq s (substring s (match-end 0))) + (setq words (cons tmp words))))) + (hash-add words java-package-name java-package-htable)))) + +(defun java-normalize-class-name (name) + "Convert class NAME to make it globally unique using current package." + ;; Currently incomplete. THe defined class has a package name, but + ;; the parents do not. How do we match the parents to the correct + ;; class if there are multiple matches? + (or (car (java-split-identifier name)) + (if (null java-package-name) + (car (java-split-identifier name)) + ;; Note: maybe allow user to pick how many words to prepend. + (let ((prefix (car (java-split-identifier java-package-name)))) + (if (and prefix (> (length prefix) 0)) + (concat prefix "." (car (java-split-identifier name))) + (car (java-split-identifier name))))))) + +(defun java-class-definition-regexp (class &optional regexp-flag) + "Return regexp to uniquely match the definition of CLASS name. +Optional REGEXP-FLAG non-nil means CLASS has already been quoted for use in a +regular expression." + (concat "[ \t]*" + java-class-keyword + (if regexp-flag + class + (regexp-quote class)) + java-class-name-after)) + +(defun java-normalize-class-match () + "After a regexp match to a class definition, return the matching class name." + (java-normalize-class-name + (buffer-substring (match-beginning java-class-def-name-grpn) + (match-end java-class-def-name-grpn)))) + +(defun java-scan-parents (end) + "Return list of parent names from a java class definition. +Since java permits only single inheritance, the list will include at most one +parent name. Point must be before the implements or extends keyword that +precedes the parent class name." + (let (parent-list parent) + (while (re-search-forward java-parent-regexp end t) + (setq parent (java-normalize-class-name + (buffer-substring (match-beginning java-parent-name-grpn) + (match-end java-parent-name-grpn))) + parent-list (cons parent parent-list))) + (nreverse parent-list))) + +(defun java-get-parents-from-source (filename class-name) + "Scan source in FILENAME and return list of parents of CLASS-NAME. +Assume file existence has already been checked." + (cond ((null class-name) nil) + ((equal filename br-null-path) + ;; This means there is no source for this class, so + ;; since all classes have Object as an ancestor and there is no + ;; where to look for parents, make Object the sole parent. + '("Object")) + (t (car (car (br-rassoc + class-name + (cdr (java-get-classes-from-source filename t)))))))) + +(defun java-select-path (paths-htable-elt &optional feature-p) + "Select proper pathname from PATHS-HTABLE-ELT based upon value of optional FEATURE-P. +Selection is between path of class definition and path for features associated +with the class." + (let ((elt (cdr paths-htable-elt))) + (if (consp elt) + (if feature-p (cdr elt) (car elt)) + ;; Both paths are the same. + elt))) + +(defun java-set-case (type) + "Return string TYPE identifier for use as a class name." + type) + +(defun java-set-case-type (class-name) + "Return string CLASS-NAME for use as a type identifier." + class-name) + +(defun java-to-class-end () + "Assuming point is at start of class, move to start of line after end of class." + (interactive) + (condition-case () + (forward-list) + (error (progn (or (re-search-forward "^}" nil t) + (goto-char (point-max)))))) + (forward-line 1)) + +(defun java-to-comments-begin () + "Skip back from current point past any preceding blank lines and comments. +Presumes no \"/*\" strings are nested within multi-line comments." + (let ((opoint)) + (while (progn (setq opoint (point)) + ;; To previous line + (if (= 0 (forward-line -1)) + (cond + ;; If begins with "//" or ends with "*/", then is a + ;; comment. + ((looking-at "[ \t]*\\(//\\|$\\)")) + ((looking-at ".*\\*/[ \t]*$") + (end-of-line) + ;; Avoid //*** single line comments here. + (re-search-backward "\\(^\\|[^/]\\)/\\*" nil t)) + ((looking-at "[ \t]*$")))))) + (goto-char opoint) + ;; Skip past whitespace + (skip-chars-forward " \t\n") + (beginning-of-line))) + +;; Static initializers confuse the parser, and don't define anything +;; that we need +(defun java-strip-static-code () + "Strip the static initializers from this buffer." + (let (buffer-read-only) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward java-static-init-regexp (point-max) t) + (goto-char (1- (match-end 0))) + (let ((start (point))) + (if (= (following-char) ?{) + (condition-case () + (forward-sexp) + (error nil))) + (delete-region start (point)) + (delete-region (match-beginning 0) (1- (match-end 0))) + ))))) + +;;; ************************************************************************ +;;; Internal variables +;;; ************************************************************************ + +(defconst java-class-modifier-keyword + "\\(public\\|protected\\|final\\|abstract\\|[ \t\n\^M]+\\)*") + +(defconst java-class-name-before + (concat "^[ \t]*" java-class-modifier-keyword java-class-keyword) + "Regexp preceding the class name in a class definition.") + +(defconst java-class-name-after + "[ \t\n]+\\({\\|extends\\|implements\\)" + "Regexp following the class name in a class definition. +Last character matched is either the colon preceding the list of class +parents, or the curly brace beginning the class body definition.") + +(defconst java-identifier-chars "_$.a-zA-Z0-9" + "String of chars and char ranges that may be used within a Java identifier.") + +(defconst java-return-type-chars java-identifier-chars + "String of chars and char ranges that may be used within a Java return type identifier.") + +(defconst java-identifier (concat "\\([_$a-zA-Z][" java-identifier-chars "]*\\)") + "Regular expression matching a Java identifier.") + +(defconst java-class-def-regexp + (concat java-class-name-before java-identifier java-class-name-after + "[^{(;]+") + "Regular expression used to match to class definitions in source text. +Class name identifier is grouping 'java-class-def-name-grpn'. +':' derived class indicator begins grouping 'java-class-def-derived-grpn,' +unless the class is not derived, in which case this grouping begins with +'{'.") + +(defconst java-class-def-name-grpn 3) +(defconst java-class-def-derived-grpn 4) + +(defconst java-lang-prefix "java-" + "Prefix string that starts \"br-java.el\" symbol names.") + +(defconst java-parent-regexp + (concat "\\(\\(implements\\|extends\\|,\\)?[ \t\n]+\\)*[ \t\n]+" + java-identifier "[ \t\n]*[ {;]") + "Parent identifier is group 'java-parent-name-grpn'.") +;; part 2 of original +;; "\\(\\(public\\|private\\|protected\\|final\||abstract\\|implements\\|extends\\)[,]?[ \t\n]+\\)?\\)?" + +(defconst java-parent-name-grpn 3) + +(defconst java-package-name-regexp + (concat "[ \t\n]*" java-identifier "[ \t\n]*;") + "Regexp matching a package statement. Package name is java-package-name-grpn.") + +(defconst java-package-name-grpn 1) + +(defconst java-package-word-regexp + "\\([a-zA-z_0-9]*\\)\\.?" + "Return a single component of a package name.") + +(defconst java-static-init-regexp + "[ \t\n]*static[ \t\n]+{" + "Regexp matching start of static initializer block.") + +(defvar java-package-htable + (hash-make 7) + "Hash table of split package names.") + +(defconst java-src-file-regexp "[^.]\\.\\(java\\)$" + "Regular expression matching a unique part of java source or header file name and no others.") + +(defvar java-children-htable nil + "Htable whose elements are of the form: (LIST-OF-CHILD-CLASSES . CLASS-NAME). +Used to traverse java inheritance graph. 'br-build-children-htable' builds +this list.") +(defvar java-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Used to traverse java inheritance graph. 'br-build-parents-htable' builds +this list.") +(defvar java-paths-htable nil + "Htable whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +'br-build-paths-htable' builds this list.") + + +(defvar java-lib-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Only classes from stable software libraries are used to build the list.") +(defvar java-lib-paths-htable nil + "Htable whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +Only classes from stable software libraries are used to build the list.") + +(defvar java-sys-parents-htable nil + "Htable whose elements are of the form: (LIST-OF-PARENT-CLASSES . CLASS-NAME). +Only classes from systems that are likely to change are used to build the +list.") +(defvar java-sys-paths-htable nil + "Alist whose elements are of the form: (LIST-OF-CLASS-NAMES . FILE-PATH). +FILE-PATH gives the location of classes found in LIST-OF-CLASS-NAMES. +Only classes from systems that are likely to change are used to build the +list.") + +(defvar java-lib-prev-search-dirs nil + "Used to check if 'java-lib-classes-htable' must be regenerated.") +(defvar java-sys-prev-search-dirs nil + "Used to check if 'java-sys-classes-htable' must be regenerated.") + +(defvar java-env-spec nil + "Non-nil value means Environment specification has been given but not yet built. +Nil means current Environment has been built, though it may still require +updating.") + +(provide 'br-java)