summaryrefslogtreecommitdiff
path: root/lisp/cedet/semantic/java.el
blob: f60f6e87ab76154354e48060553ad102e9c8ad7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
;;; semantic/java.el --- Semantic functions for Java

;;; Copyright (C) 1999-2021 Free Software Foundation, Inc.

;; Author: David Ponce <david@dponce.com>

;; This file is part of GNU Emacs.

;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Common function for Java parsers.

;;; Code:
(require 'semantic)
(require 'semantic/ctxt)
(require 'semantic/doc)
(require 'semantic/format)

(eval-when-compile
  (require 'semantic/find)
  (require 'semantic/dep))


;;; Lexical analysis
;;
(defconst semantic-java-number-regexp
  (eval-when-compile
    (concat "\\("
            "\\<[0-9]+[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>"
            "\\|"
            "\\<[0-9]+[.][eE][-+]?[0-9]+[fFdD]?\\>"
            "\\|"
            "\\<[0-9]+[.][fFdD]\\>"
            "\\|"
            "\\<[0-9]+[.]"
            "\\|"
            "[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>"
            "\\|"
            "\\<[0-9]+[eE][-+]?[0-9]+[fFdD]?\\>"
            "\\|"
            "\\<0[xX][[:xdigit:]]+[lL]?\\>"
            "\\|"
            "\\<[0-9]+[lLfFdD]?\\>"
            "\\)"
            ))
  "Lexer regexp to match Java number terminals.
Following is the specification of Java number literals.

DECIMAL_LITERAL:
    [1-9][0-9]*
  ;
HEX_LITERAL:
    0[xX][[:xdigit:]]+
  ;
OCTAL_LITERAL:
    0[0-7]*
  ;
INTEGER_LITERAL:
    <DECIMAL_LITERAL>[lL]?
  | <HEX_LITERAL>[lL]?
  | <OCTAL_LITERAL>[lL]?
  ;
EXPONENT:
    [eE][+-]?[09]+
  ;
FLOATING_POINT_LITERAL:
    [0-9]+[.][0-9]*<EXPONENT>?[fFdD]?
  | [.][0-9]+<EXPONENT>?[fFdD]?
  | [0-9]+<EXPONENT>[fFdD]?
  | [0-9]+<EXPONENT>?[fFdD]
  ;")

;;; Parsing
;;
(defsubst semantic-java-dim (id)
  "Split ID string into a pair (NAME . DIM).
NAME is ID without trailing brackets: \"[]\".
DIM is the dimension of NAME deduced from the number of trailing
brackets, or 0 if there is no trailing brackets."
  (let ((dim (string-match "\\(\\[]\\)+\\'" id)))
    (if dim
        (cons (substring id 0 dim)
              (/ (length (match-string 0 id)) 2))
      (cons id 0))))

(defsubst semantic-java-type (tag)
  "Return the type of TAG, taking care of array notation."
  (let ((type (semantic-tag-type tag))
        (dim  (semantic-tag-get-attribute tag :dereference)))
    (when dim
      (while (> dim 0)
        (setq type (concat type "[]")
              dim (1- dim))))
    type))

(defun semantic-java-expand-tag (tag)
  "Expand compound declarations found in TAG into separate tags.
TAG contains compound declarations when its class is `variable', and
its name is a list of elements (NAME START . END), where NAME is a
compound variable name, and START/END are the bounds of the
corresponding compound declaration."
  (let* ((class (semantic-tag-class tag))
         (elts (semantic-tag-name tag))
         dim type dim0 elt clone start end xpand)
    (cond
     ((and (eq class 'function)
           (> (cdr (setq dim (semantic-java-dim elts))) 0))
      (setq clone (semantic-tag-clone tag (car dim))
            xpand (cons clone xpand))
      (semantic-tag-put-attribute clone :dereference (cdr dim)))

     ((eq class 'variable)
      (or (consp elts) (setq elts (list (list elts))))
      (setq dim  (semantic-java-dim (semantic-tag-get-attribute tag :type))
            type (car dim)
            dim0 (cdr dim))
      (while elts
        ;; For each compound element, clone the initial tag with the
        ;; name and bounds of the compound variable declaration.
        (setq elt   (car elts)
              elts  (cdr elts)
              start (if elts  (cadr elt) (semantic-tag-start tag))
              end   (if xpand (cddr elt) (semantic-tag-end   tag))
              dim   (semantic-java-dim (car elt))
              clone (semantic-tag-clone tag (car dim))
              xpand (cons clone xpand))
        (semantic-tag-put-attribute clone :type type)
        (semantic-tag-put-attribute clone :dereference (+ dim0 (cdr dim)))
        (semantic-tag-set-bounds clone start end)))

     ((and (eq class 'type) (string-match "\\." (semantic-tag-name tag)))
      ;; javap outputs files where the package name is stuck onto the class or interface
      ;; name.  To make this more regular, we extract the package name into a package statement,
      ;; then make the class name regular.
      (let* ((name (semantic-tag-name tag))
	     (rsplit (nreverse (split-string name "\\." t)))
	     (newclassname (car rsplit))
	     (newpkg (mapconcat 'identity (reverse (cdr rsplit)) ".")))
	(semantic-tag-set-name tag newclassname)
	(setq xpand
	      (list tag
		    (semantic-tag-new-package newpkg nil))))
      ))
    xpand))

;;; Environment
;;
(defcustom-mode-local-semantic-dependency-system-include-path
  java-mode semantic-java-dependency-system-include-path
  ;; @todo - Use JDEE to get at the include path, or something else?
  nil
  "The system include path used by Java language.")

;; Local context
;;
(define-mode-local-override semantic-ctxt-scoped-types
  java-mode (&optional point)
  "Return a list of type names currently in scope at POINT."
  (mapcar 'semantic-tag-name
          (semantic-find-tags-by-class
           'type (semantic-find-tag-by-overlay point))))

;; Tag Protection
;;
(define-mode-local-override semantic-tag-protection
  java-mode (tag &optional parent)
  "Return the protection of TAG in PARENT.
Override function for `semantic-tag-protection'."
  (let ((prot (semantic-tag-protection-default tag parent)))
    (or prot 'package)))

;; Prototype handler
;;
(defun semantic-java-prototype-function (tag &optional parent color)
  "Return a function (method) prototype for TAG.
Optional argument PARENT is a parent (containing) item.
Optional argument COLOR indicates that color should be mixed in.
See also `semantic-format-tag-prototype'."
  (let ((name (semantic-tag-name tag))
        (type (semantic-java-type tag))
        (tmpl (semantic-tag-get-attribute tag :template-specifier))
        (args (semantic-tag-function-arguments tag))
        (argp "")
        arg argt)
    (while args
      (setq arg  (car args)
            args (cdr args))
      (if (semantic-tag-p arg)
          (setq argt (if color
                         (semantic--format-colorize-text
                          (semantic-java-type arg) 'type)
                       (semantic-java-type arg))
                argp (concat argp argt (if args "," "")))))
    (when color
      (when type
        (setq type (semantic--format-colorize-text type 'type)))
      (setq name (semantic--format-colorize-text name 'function)))
    (concat (or tmpl "") (if tmpl " " "")
            (or type "") (if type " " "")
            name "(" argp ")")))

(defun semantic-java-prototype-variable (tag &optional parent color)
  "Return a variable (field) prototype for TAG.
Optional argument PARENT is a parent (containing) item.
Optional argument COLOR indicates that color should be mixed in.
See also `semantic-format-tag-prototype'."
  (let ((name (semantic-tag-name tag))
        (type (semantic-java-type tag)))
    (concat (if color
                (semantic--format-colorize-text type 'type)
              type)
            " "
            (if color
                (semantic--format-colorize-text name 'variable)
              name))))

(defun semantic-java-prototype-type (tag &optional parent color)
  "Return a type (class/interface) prototype for TAG.
Optional argument PARENT is a parent (containing) item.
Optional argument COLOR indicates that color should be mixed in.
See also `semantic-format-tag-prototype'."
  (let ((name (semantic-tag-name tag))
        (type (semantic-tag-type tag))
        (tmpl (semantic-tag-get-attribute tag :template-specifier)))
    (concat type " "
            (if color
                (semantic--format-colorize-text name 'type)
              name)
            (or tmpl ""))))

(define-mode-local-override semantic-format-tag-prototype
  java-mode (tag &optional parent color)
  "Return a prototype for TOKEN.
Optional argument PARENT is a parent (containing) item.
Optional argument COLOR indicates that color should be mixed in."
  (let ((f (intern-soft (format "semantic-java-prototype-%s"
                                (semantic-tag-class tag)))))
    (funcall (if (fboundp f)
                 f
               'semantic-format-tag-prototype-default)
             tag parent color)))

;; Include Tag Name
;;

;; Thanks Bruce Stephens
(define-mode-local-override semantic-tag-include-filename java-mode (tag)
  "Return a suitable path for (some) Java imports."
  (let ((name (semantic-tag-name tag)))
    (concat (mapconcat 'identity (split-string name "\\.") "/") ".java")))

;; Documentation handler
;;
(defsubst semantic-java-skip-spaces-backward ()
  "Move point backward, skipping Java whitespaces."
  (skip-chars-backward " \n\r\t"))

(defsubst semantic-java-skip-spaces-forward ()
  "Move point forward, skipping Java whitespaces."
  (skip-chars-forward " \n\r\t"))

(define-mode-local-override semantic-documentation-for-tag
  java-mode (&optional tag nosnarf)
  "Find documentation from TAG and return it as a clean string.
Java have documentation set in a comment preceding TAG's definition.
Attempt to strip out comment syntactic sugar, unless optional argument
NOSNARF is non-nil.
If NOSNARF is `lex', then return the semantic lex token."
  (when (or tag (setq tag (semantic-current-tag)))
    (with-current-buffer (semantic-tag-buffer tag)
      (save-excursion
        ;; Move the point at token start
        (goto-char (semantic-tag-start tag))
        (semantic-java-skip-spaces-forward)
        ;; If the point already at "/**" (this occurs after a doc fix)
        (if (looking-at "/\\*\\*")
            nil
          ;; Skip previous spaces
          (semantic-java-skip-spaces-backward)
          ;; Ensure point is after "*/" (javadoc block comment end)
          (condition-case nil
              (backward-char 2)
            (error nil))
          (when (looking-at "\\*/")
            ;; Move the point backward across the comment
            (forward-char 2)              ; return just after "*/"
            (forward-comment -1)          ; to skip the entire block
            ))
        ;; Verify the point is at "/**" (javadoc block comment start)
        (if (looking-at "/\\*\\*")
            (let ((p (point))
                  (c (semantic-doc-snarf-comment-for-tag 'lex)))
              (when c
                ;; Verify that the token just following the doc
                ;; comment is the current one!
                (goto-char (semantic-lex-token-end c))
                (semantic-java-skip-spaces-forward)
                (when (eq tag (semantic-current-tag))
                  (goto-char p)
                  (semantic-doc-snarf-comment-for-tag nosnarf)))))
        ))))

;;; Javadoc facilities
;;

;; Javadoc elements
;;
(defvar semantic-java-doc-line-tags nil
  "Valid javadoc line tags.
Ordered following Sun's Tag Convention at
<https://java.sun.com/products/jdk/javadoc/writingdoccomments/index.html>")

(defvar semantic-java-doc-with-name-tags nil
  "Javadoc tags which have a name.")

(defvar semantic-java-doc-with-ref-tags nil
  "Javadoc tags which have a reference.")

;; Optional javadoc tags by classes of semantic tag
;;
(defvar semantic-java-doc-extra-type-tags nil
  "Optional tags used in class/interface documentation.
Ordered following Sun's Tag Convention.")

(defvar semantic-java-doc-extra-function-tags nil
  "Optional tags used in method/constructor documentation.
Ordered following Sun's Tag Convention.")

(defvar semantic-java-doc-extra-variable-tags nil
  "Optional tags used in field documentation.
Ordered following Sun's Tag Convention.")

;; All javadoc tags by classes of semantic tag
;;
(defvar semantic-java-doc-type-tags nil
  "Tags allowed in class/interface documentation.
Ordered following Sun's Tag Convention.")

(defvar semantic-java-doc-function-tags nil
  "Tags allowed in method/constructor documentation.
Ordered following Sun's Tag Convention.")

(defvar semantic-java-doc-variable-tags nil
  "Tags allowed in field documentation.
Ordered following Sun's Tag Convention.")

;; Access to Javadoc elements
;;
(defmacro semantic-java-doc-tag (name)
  "Return doc tag from NAME.
That is @NAME."
  `(concat "@" ,name))

(defsubst semantic-java-doc-tag-name (tag)
  "Return name of the doc TAG symbol.
That is TAG `symbol-name' without the leading `@'."
  (substring (symbol-name tag) 1))

(defun semantic-java-doc-keyword-before-p (k1 k2)
  "Return non-nil if javadoc keyword K1 is before K2."
  (let* ((t1   (semantic-java-doc-tag k1))
         (t2   (semantic-java-doc-tag k2))
         (seq1 (and (semantic-lex-keyword-p t1)
                    (plist-get (semantic-lex-keyword-get t1 'javadoc)
                               'seq)))
         (seq2 (and (semantic-lex-keyword-p t2)
                    (plist-get (semantic-lex-keyword-get t2 'javadoc)
                               'seq))))
    (if (and (numberp seq1) (numberp seq2))
        (<= seq1 seq2)
      ;; Unknown tags (probably custom ones) are always after official
      ;; ones and are not themselves ordered.
      (or (numberp seq1)
          (and (not seq1) (not seq2))))))

(defun semantic-java-doc-keywords-map (fun &optional property)
  "Run function FUN for each javadoc keyword.
Return the list of FUN results.  If optional PROPERTY is non-nil only
call FUN for javadoc keywords which have a value for PROPERTY.  FUN
receives two arguments: the javadoc keyword and its associated
'javadoc property list.  It can return any value.  All nil values are
removed from the result list."
  (delq nil
        (mapcar
         #'(lambda (k)
             (let* ((tag   (semantic-java-doc-tag k))
                    (plist (semantic-lex-keyword-get tag 'javadoc)))
               (if (or (not property) (plist-get plist property))
                   (funcall fun k plist))))
         semantic-java-doc-line-tags)))


;;; Mode setup
;;

(defun semantic-java-doc-setup ()
  "Lazy initialization of javadoc elements."
  (or semantic-java-doc-line-tags
      (setq semantic-java-doc-line-tags
            (sort (mapcar #'semantic-java-doc-tag-name
                          (semantic-lex-keywords 'javadoc))
                  #'semantic-java-doc-keyword-before-p)))

  (or semantic-java-doc-with-name-tags
      (setq semantic-java-doc-with-name-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 k)
             'with-name)))

  (or semantic-java-doc-with-ref-tags
      (setq semantic-java-doc-with-ref-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 k)
             'with-ref)))

  (or semantic-java-doc-extra-type-tags
      (setq semantic-java-doc-extra-type-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'type (plist-get p 'usage))
                     k))
             'opt)))

  (or semantic-java-doc-extra-function-tags
      (setq semantic-java-doc-extra-function-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'function (plist-get p 'usage))
                     k))
             'opt)))

  (or semantic-java-doc-extra-variable-tags
      (setq semantic-java-doc-extra-variable-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'variable (plist-get p 'usage))
                     k))
             'opt)))

  (or semantic-java-doc-type-tags
      (setq semantic-java-doc-type-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'type (plist-get p 'usage))
                     k)))))

  (or semantic-java-doc-function-tags
      (setq semantic-java-doc-function-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'function (plist-get p 'usage))
                     k)))))

  (or semantic-java-doc-variable-tags
      (setq semantic-java-doc-variable-tags
            (semantic-java-doc-keywords-map
             #'(lambda (k p)
                 (if (memq 'variable (plist-get p 'usage))
                     k)))))

  )

(provide 'semantic/java)

;; Local variables:
;; generated-autoload-load-name: "semantic/java"
;; End:

;;; semantic/java.el ends here