;;; jde-gen-extra.el --- Autocoding additions to the JDEE
;;; $Id: jde-gen-extra.el,v 1.2 2004/09/28 20:50:46 ole Exp $

;; Author: Ole Arndt <ole at sugarshark dot com>
;; Maintainer: Ole Arndt <ole at sugarshark dot com>
;; Created: Mon Apr 19 01:12:01 2004
;; Homepage: http://sugarshark.com/elisp/mylisp/jde-gen-extra.el
;; Keywords: java, code generation, tools

;; This file is *NOT* part of GNU Emacs.

;; Copyright (C) 2004 Ole Arndt

;; 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
;; GNU Emacs; see the file COPYING.  If not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
;;
;;; Commentary:
;;
;; This file provides additional autocode templates for the JDEE.
;; The provided functions are:
;;
;;     jde-gen-exception        -- Generate exception class
;;     jde-gen-exception-buffer -- Generate buffer with exception class
;;     jde-gen-hashcode-method  -- Generate a hashCode method.
;;     jde-gen-equals-method    -- Generate an equals method.
;;
;; jde-gen-extra.el has been tested with GNU Emacs 21.3.50, JDE 2.3.3, and
;; Cedet 1.0beta3. 
;;
;;; Usage:
;;
;; To use the function provided by this file put it in a directory in
;; your load path and write:
;;
;;    (require 'jde-gen-extra)
;;
;; into your .emacs. I also recommend that you:
;;
;; a) add the calls to `jde-gen-hashcode-method' and
;;   `jde-gen-hashcode-method' to the jde-gen-bean-template
;;   (you might also needto add a `semantic-fetch-tags' call)
;; 
;; b) add `jde-gen-hashcode-method' and `jde-gen-hashcode-method'
;;    to the`jde-gen-code-templates' list.
;;
;; c) add `jde-gen-exception' to `jde-gen-buffer-templates'
;;
;;; Code:

(require 'jde-gen)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* equals method generator
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;###autoload
(defcustom jde-gen-equals-trailing-and-operators nil
  "Specifies whether the '&&' operators in a generated equals
method are added at the end of the line or at the beginning.  If
this variable is t, the operator will be added at the end of the
line, else on the next line before the comparison.  With
`jde-gen-equals-trailing-and-operators' set to nil:

    return (a == o.a)
        && (b == o.b)
        && (s == null ? o.s == null : s.equals(o.s));

Or, with `jde-gen-equals-trailing-and-operators' set to t:

    return (a == o.a) &&
        (b == o.b) &&
        (s == null ? o.s == null : s.equals(o.s));
"
  :group 'jde-gen
  :type 'boolean)

;;;###autoload
(defcustom jde-gen-equals-parens-around-expression nil
  "Specifies whether the generated equals expression should be 
surrounded by parentheses.
With `jde-gen-equals-trailing-and-operators' set to nil:

    return ((a == o.a)
            && (b == o.b)
            && (s == null ? o.s == null : s.equals(o.s)));

Or, with `jde-gen-equals-trailing-and-operators' set to t:

    return ((a == o.a) &&
            (b == o.b) &&
            (s == null ? o.s == null : s.equals(o.s)));
"
  :group 'jde-gen
  :type 'boolean)

;;;###autoload
(defcustom jde-gen-equals-method-template
  '("'>"
    "\"/**\" '> 'n"
    "\" * Check if this object is equal to another object.\" '> 'n"
    "\" * \" '> 'n"
    "\" * <p>For the definition of the object equivalence relation\" '> 'n"
    "\" * see {@link java.lang.Object#equals(Object)}.</p>\" '> 'n"
    "\" * \" '> 'n"
    "\" * @param obj another, possibly equal object.\" '> 'n"
    "\" * \" '> 'n"
    "\" * @return true if the objects are equal, false otherwise.\" '> 'n"
    "\" * \" '> 'n"
    "\" * @see java.lang.Object#equals(Object)\" '> 'n"
    "\" */\" '> 'n"
    "(jde-gen-method-signature \"public\" \"boolean\" \"equals\" \"Object obj\")"
    "(jde-gen-electric-brace)"
    "\"if (obj == this)\" '> 'n"
    "\"return true;\" '> 'n '> 'n"
    "\"if (obj == null || getClass() != obj.getClass())\" '> 'n"
    "\"return false;\" '> 'n '> 'n"
    "(jde-gen-equals-return \"obj\" \"o\") '> 'n"
    "\"}\" '> 'n '>")
  "*Template for creating an equals method.
Setting this variable defines a template instantiation command
`jde-gen-equals-method', as a side-effect."
  :group 'jde-gen
  :type '(repeat string)
  :set '(lambda (sym val)
          (defalias 'jde-gen-equals-method
            (tempo-define-template
             "java-equals-method"
             (jde-gen-read-template val)
             nil
             "Create an equals method at the current point."))
          (set-default sym val)))

;;;###autoload
(defun jde-gen-equals-return (&optional parm-name var-name class)
  "Generate a body of an appropriate override for the
java.lang.Object#equals(Object) function. This function gets the
list of member variables from`jde-gen-get-serializable-members'.

The first optional parameter `parm-name' is the parameter name of
the Object argument of the equals function.  Default is \"obj\".

The second optional parameter `var-name' denotes the variable
name used to cast the \"obj\" argument to. The default is \"o\".

The third optional parameter `class' can be a semantic tag, which
is then used instead of the result of `semantic-current-tag'.

Example:
    class Bean {
        int a;
        long b;
        String s;
    } 

Result:
    Bean o = (Bean) obj;

    return (a == o.a)
        && (b == o.b)
        && (s == null ? o.s == null : s.equals(o.s));

Or, with `jde-gen-equals-trailing-and-operators' set to t:
    Bean o = (Bean) obj;

    return (a == o.a) &&
        (b == o.b) &&
        (s == null ? o.s == null : s.equals(o.s));
"
  (interactive)
  (let* ((parm (or parm-name "obj"))
         (var (or var-name "o"))
         (class-tag (or class (semantic-current-tag)))
         (class-name (semantic-tag-name class-tag))
         (members (sort (jde-gen-get-serializable-members class-tag)
                        'jde-gen-get-primitives-first))
         (super (car (semantic-tag-type-superclasses class-tag)))
         (extends (and super (not (string= "Object" super)))))
    (list 'l '>
          class-name " " var " = (" class-name ") " parm ";" '>'n '>'n
          "return "
          (if jde-gen-equals-parens-around-expression "(")
          (if extends (list 'l "super.equals(" var ")")) '>
          (cons 'l (mapcar
              (lambda (tag)
                (let ((name (semantic-tag-name tag))
                      (type (semantic-tag-type tag)))
                  (list 'l (if extends (jde-gen-equals-add-and-operator)
                             (setq extends t) nil)
                        (cond
                         ;; primitive arrays
                         ((and (string-match "\\`\\(byte\\|char\\|short\\|int\\|long\\|float\\|double\\|boolean\\)" type)
                             (or (string-match "\\[.*\\]" name) (string-match "\\[.*\\]" type)))
                          (let ((array (replace-regexp-in-string "\\[.*\\]" "" name)))
                            (concat "java.util.Arrays.equals(" array ", " var "." array ")")))

                         ;; object arrays
                         ((or (string-match "\\[.*\\]" name) (string-match "\\[.*\\]" type))
                          (let ((array (replace-regexp-in-string "\\[.*\\]" "" name)))
                            (concat "java.util.Arrays.deepEquals(" array ", " var "." array ")")))

                         ;; primitives
                         ((or (semantic-tag-of-type-p tag "byte")
                              (semantic-tag-of-type-p tag "char")
                              (semantic-tag-of-type-p tag "short")
                              (semantic-tag-of-type-p tag "int")
                              (semantic-tag-of-type-p tag "long")
                              (semantic-tag-of-type-p tag "boolean"))
                          (concat "(" name " == " var "." name ")"))

                         ;; floating point; use epsilon?
                         ((or (semantic-tag-of-type-p tag "float")
                              (semantic-tag-of-type-p tag "double"))
                          (concat "(" name " == " var "." name ")"))

                         ;; object references
                         (t (concat "(" name " == null ? " var "." name " == null : "
                                    name ".equals(" var "." name "))" )))
                        '>))) members))
          (if jde-gen-equals-parens-around-expression ")") ";")))

(defun jde-gen-equals-add-and-operator ()
  (if jde-gen-equals-trailing-and-operators
      (list 'l " &&" '> 'n '>)
    (list 'l '> 'n '> "&& ")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* hashCode method generator
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;###autoload
(defcustom jde-gen-hashcode-method-template
  '("'>"
    "\"/**\" '> 'n"
    "\" * Calculate the hash code for this object.\" '> 'n"
    "\" * \" '> 'n"
    "\" * <p>The rules laid out in J. Blosh's Effective Java are used\" '> 'n"
    "\" * for the hash code calculation.</p>\" '> 'n"
    "\" * \" '> 'n"
    "\" * @return the hash code.\" '> 'n"
    "\" * \" '> 'n"
    "\" * @see java.lang.Object#hashCode\" '> 'n"
    "\" */\" '> 'n"
    "(jde-gen-method-signature \"public\"\ \"int\" \"hashCode\" nil)"
    "(jde-gen-electric-brace)"
    "(jde-gen-hashcode-body) '> 'n"
    "\"}\" '> 'n '>" )
  "*Template for creating a hashCode method.
Setting this variable defines a template instantiation command
`jde-gen-hashcode-method', as a side-effect."
  :group 'jde-gen
  :type '(repeat string)
  :set '(lambda (sym val)
          (defalias 'jde-gen-hashcode-method
            (tempo-define-template
             "java-hashcode-method"
             (jde-gen-read-template val)
             nil
             "Create a hashCode method at the current point."))
          (set-default sym val)))

(defvar jde-gen-hashcode-primes
  '(11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)
    "a list of all two digit prime numbers")

(defvar jde-gen-hashcode-current-prime 0
  "a prime number")

(defun jde-gen-hashcode-next-prime ()
  "Get the next prime number"
  (let ((last jde-gen-hashcode-current-prime))
    (setq jde-gen-hashcode-current-prime (% (+ 1 last)
                                            (length jde-gen-hashcode-primes)))
    (int-to-string (nth last jde-gen-hashcode-primes))))

;;;###autoload
(defun jde-gen-hashcode-body (&optional var-name class)
  "Generate a body of a hashCode function.
This function gets the list of member variables of the current
class from `jde-gen-get-serializable-members'.

The first optional parameter `var-name' denotes the variable name used
to calculate the hash code, the default is \"code\".

The second optional parameter `class' can be a semantic tag, which is
then used instead of the result of `semantic-current-tag'.
"
  (interactive)
  (let* ((var (or var-name "code"))
         (class-tag (or class (semantic-current-tag)))
         (members (sort (jde-gen-get-serializable-members class-tag)
                        'jde-gen-get-primitives-first))
         (super (car (semantic-tag-type-superclasses class-tag)))
         (extends (and super (not (string= "Object" super)))))
    (list 'l "int " var " = "
          (if extends "super.hashCode()" (jde-gen-hashcode-next-prime)) ";"
          '> 'n '> 'n
          (cons
           'l
           (mapcar
            (lambda (tag)
              (let ((name (semantic-tag-name tag))
                    (type (semantic-tag-type tag)))
                (list 'l var " = " var " * 37 + "
                      (cond
                       ;; arrays must be first

                       ;; primitive arrays
                       ((and (string-match "\\`\\(byte\\|char\\|short\\|int\\|long\\|float\\|double\\|boolean\\)" type)
                             (or (string-match "\\[.*\\]" name) (string-match "\\[.*\\]" type)))
                        (let ((array (replace-regexp-in-string "\\[.*\\]" "" name)))
                          (concat "java.util.Arrays.hashCode(" array ")")))
                       ;; object arrays
                       ((or (string-match "\\[.*\\]" name) (string-match "\\[\\]" type))
                        (let ((array (replace-regexp-in-string "\\[.*\\]" "" name)))
                          (concat "java.util.Arrays.deepHashCode(" array ")")))

                       ;; smaller types
                       ((or (semantic-tag-of-type-p tag "byte")
                            (semantic-tag-of-type-p tag "char")
                            (semantic-tag-of-type-p tag "short"))
                        (concat "(int) " name))
                       ;; integers
                       ((semantic-tag-of-type-p tag "int") name)
                       ((semantic-tag-of-type-p tag "long")
                        (concat "(int) (" name " ^ (" name " >> 32))" ))
                       ;; booleans
                       ((semantic-tag-of-type-p tag "boolean")
                        (concat "(" name " ? 1 : 0)"))

                       ;; floating point
                       ((semantic-tag-of-type-p tag "float")
                        (concat "Float.floatToIntBits(" name ")" ))
                       ((semantic-tag-of-type-p tag "double")
                        (concat "(int) (Double.doubleToLongBits(" name ") ^ (Double.doubleToLongBits(" name ") >> 32))" ))


                       ;; object references
                       (t
                        (concat "(" name " == null ? " (jde-gen-hashcode-next-prime)
                                " : " name ".hashCode())"))) ";"
                      '> 'n))) members))
          '> 'n
          "return " var ";")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* toString method generator
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;###autoload
(defcustom jde-gen-tostring-method-template
  '("'>"
    "\"/**\" '> 'n"
    "\" * Get a string representation of this object.\" '> 'n"
    "\" * \" '> 'n"
    "\" * @return a string representation of this object.\" '> 'n"
    "\" * \" '> 'n"
    "\" * @see java.lang.Object#toString\" '> 'n"
    "\" */\" '> 'n"
    "(jde-gen-method-signature \"public\" \"String\" \"toString\" \"\")"
    "(jde-gen-electric-brace)"
    "(jde-gen-tostring-return) '> 'n"
    "\"}\" '> 'n '>"
    "(jde-import-one-class \"org.apache.commons.lang.builder.ToStringBuilder\")")
  "*Template for creating an toString method.
Setting this variable defines a template instantiation
command `jde-gen-tostring-method', as a side-effect."
  :group 'jde-gen
  :type '(repeat string)
  :set '(lambda (sym val)
          (defalias 'jde-gen-tostring-method
            (tempo-define-template
             "java-tostring-method"
             (jde-gen-read-template val)
             nil
             "Create an toString method at the current point."))
          (set-default sym val)))

;;;###autoload
(defun jde-gen-tostring-return (&optional class)
  "Generate a body of an appropriate override for the
java.lang.Object#toString function. This gets the member variables
of the current class from semantic via `semantic-current-tag'.

This uses the ToStringBuilder class from the jakarta commons lang project.
"
  (interactive)
  (let* ((class-tag (or class (semantic-current-tag)))
         (class-name (semantic-tag-name class-tag))
         (members (jde-gen-get-member-variables class-tag))
         (super (car (semantic-tag-type-superclasses class-tag)))
         (extends (and super (not (string= "Object" super)))))
    (list 'l '>
          "return new ToStringBuilder(this)" ' > 'n
          (if extends (list 'l ".appendSuper(super.toString())" '> 'n))
          (cons 'l (mapcar
              (lambda (tag)
                (let ((name (semantic-tag-name tag)))
                  (list 'l ".append(\"" name "\", " name ")" '> 'n))) members))
          ".toString();")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* Generate all object methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;###autoload
(defun jde-gen-object-methods ()
  "Generates an equals(), a hashCode() and a toString method."
  (interactive)
  (jde-gen-equals-method)
  (newline-and-indent)
  (jde-gen-hashcode-method)
  (newline-and-indent)
  (jde-gen-tostring-method))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* Semantic and JDEE helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(if (not (fboundp 'replace-regexp-in-string))
    (defsubst replace-regexp-in-string (REGEXP REP STRING &optional FIXEDCASE LITERAL SUBEXP START)
      "XEmacs does not have `replace-regexp-in-string'"
      (replace-in-string STRING REGEXP REP &optional LITERAL)))

(defun jde-gen-get-member-variables (&optional tag)
  "Get all member variables of the current class as a semantic tag list.
The optional parameter `tag' can be a semantic tag, which is
then used as the current class instead of the result of `semantic-current-tag'."
  (let ((curtag (or tag (semantic-current-tag))))
    (remove nil
            (mapcar
             (lambda (member)
               (if (semantic-tag-of-class-p member 'variable)
                   member
                 nil))
             (semantic-tag-type-members curtag)))))

(defun jde-gen-get-member-functions (&optional tag)
  "Get all member functions of the current class as a semantic tag list.
The optional parameter `tag' can be a semantic tag, which is
then used as the current class instead of the result of `semantic-current-tag'."
  (let ((curtag (or tag (semantic-current-tag))))
    (remove nil
            (mapcar
             (lambda (member) (if (semantic-tag-of-class-p member 'function) member nil))
             (semantic-tag-type-members curtag)))))

(defun jde-gen-get-serializable-members (&optional tag)
  "Get all serializable member variables of the current class as a semantic tag list.
The optional parameter `tag' can be a semantic tag, which is
then used as the current class instead of the result of `semantic-current-tag'."
  (let ((curtag (or tag (semantic-current-tag))))
    (remove nil
            (mapcar
             (lambda (member)
               (let ((is-variable (semantic-tag-of-class-p member 'variable))
                     (modifiers (semantic-tag-modifiers member)))
                 (if (and is-variable (not (or (member "static" modifiers)
                                               (member "transient" modifiers))))
                     member
                      nil)))
             (semantic-tag-type-members curtag)))))

(defun jde-gen-equals-member-is-primitive (tag)
  "Check if tag is of primitive type"
  (and (or (semantic-tag-of-type-p tag "byte")
           (semantic-tag-of-type-p tag "char")
           (semantic-tag-of-type-p tag "short")
           (semantic-tag-of-type-p tag "boolean")
           (semantic-tag-of-type-p tag "int")
           (semantic-tag-of-type-p tag "long"))
       (jde-gen-equals-member-isnt-array tag)))

(defun jde-gen-equals-member-is-float (tag)
  "Check if tag is of a floating point type"
  (and (or (semantic-tag-of-type-p tag "float")
           (semantic-tag-of-type-p tag "double")))
  (jde-gen-equals-member-isnt-array tag))

(defun jde-gen-equals-member-isnt-array (tag)
  "Check if tag is of an array type"
  (not (or (string-match "\\[.*\\]" (semantic-tag-name tag))
           (string-match "\\[.*\\]" (semantic-tag-type tag)))))
      
(defun jde-gen-get-primitives-first (a b)
  "List sorter for a class' member list. Primitive types will be considered
lower, so that they are returned at the head of the list. This ensures they will be
processed first and the more costly complex types later."
  (or (and (jde-gen-equals-member-is-primitive a)
           (not (jde-gen-equals-member-is-primitive b)))
      (and (jde-gen-equals-member-is-float a)
           (not (or (jde-gen-equals-member-is-primitive b)
                    (jde-gen-equals-member-is-float b))))
      (and (jde-gen-equals-member-isnt-array a)
           (not (or (jde-gen-equals-member-is-primitive b)
                    (jde-gen-equals-member-is-float b)
                    (jde-gen-equals-member-isnt-array b))))
      ))
                   
    
(defun jde-import-one-class (class)
  "Insert an import into the buffer if nor already there."
  (interactive "s")
  (if (not (jde-import-already-imports-class class (jde-import-get-imports)))
      (jde-import-insert-imports-into-buffer (list class))))

(defun jde-gen-get-current-class-name ()
  "Return the name of the class around point."
  (semantic-tag-name (semantic-current-tag)))

(defun jde-gen-get-class-buffer-name ()
  "Get the class name from the buffer filename"
  (file-name-sans-extension (file-name-nondirectory
                             (or (buffer-file-name) "Object.java"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;_* Exception class wizard
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;###autoload
(defcustom jde-gen-exception-buffer-template
  (list
   "(open-line 1) (funcall jde-gen-boilerplate-function)"
   "(jde-gen-get-package-statement)"
   "(progn (require 'jde-javadoc) (jde-javadoc-insert-start-block))"
   "\" * Exception <code>\" (jde-gen-get-class-buffer-name) \"</code>.\" '> 'n"
   "\" \" (jde-javadoc-insert-empty-line)"
   "\" * Created: \" (current-time-string) '> 'n"
   "\" \" (jde-javadoc-insert-empty-line)"
   "\" \" (jde-gen-save-excursion (jde-javadoc-insert 'tempo-template-jde-javadoc-author-tag))"
   "\" \" (jde-gen-save-excursion (jde-javadoc-insert 'tempo-template-jde-javadoc-version-tag))"
   "\" \" (jde-javadoc-insert-end-block)"
   "\"public class \""
   "(jde-gen-get-class-buffer-name)" "\" \" (jde-gen-get-extend-class)"
   "(jde-gen-electric-brace)"
   "'p'n"

   ;; Default constructor
   "'> (jde-javadoc-insert-start-block)"
    "\"* Constructs a new <code>\" (jde-gen-get-class-buffer-name) \"</code> with\" '>'n"
    "\"* <code>null</code> as its detail message.\" '>'n"
    "'> (jde-javadoc-insert-end-block)"
    "(jde-gen-method-signature \"public\" nil (jde-gen-get-class-buffer-name) nil)" 
    "(jde-gen-electric-brace)"
    "\"}\"'>'n"
    ;; leave a blank line with no indentation
    "'n"

   ;; Constructor with message
   "'> (jde-javadoc-insert-start-block)"
    "\"* Constructs a new <code>\" (jde-gen-get-class-buffer-name) \"</code> with\" '>'n"
    "\"* the specified detail message.\" '>'n"
    "'> (jde-javadoc-insert-empty-line)"
    "\"* @param message the detail message string.\" '> 'n"
    "'> (jde-javadoc-insert-end-block)"
    "(jde-gen-method-signature \"public\" nil (jde-gen-get-class-buffer-name) \"String message\")" 
    "(jde-gen-electric-brace)"
    "\"super(message);\" '> 'n"
    "\"}\" '> 'n"
    ;; leave a blank line with no indentation
    "'n"

   ;; Constructor with a cause
   "'> (jde-javadoc-insert-start-block)"
    "\"* Constructs a new <code>\" (jde-gen-get-class-buffer-name) \"</code> with\" '>'n"
    "\"* the specified cause and a detail message of\" '> 'n"
    "\"* <code>(cause == null ? null : cause.toString())</code>\" '> 'n"
    "\"* (which typically contains the class and detail message of cause).\" '> 'n"
    "'> (jde-javadoc-insert-empty-line)"
    "\"* @param cause the causing throwable. A null value is permitted\" '> 'n"
    "\"*     and indicates that the cause is nonexistent or unknown.\" '> 'n"
    "'> (jde-javadoc-insert-end-block)"
    "(jde-gen-method-signature \"public\" nil (jde-gen-get-class-buffer-name) \"Throwable cause\")" 
    "(jde-gen-electric-brace)"
    "\"super(cause == null ? (String) null : cause.toString());\" '> 'n"
    "\"initCause(cause);\" '> 'n"
    "\"}\" '> 'n"
    ;; leave a blank line with no indentation
    "'n"

    ;; Constructor with a message and a cause
   "'> (jde-javadoc-insert-start-block)"
    "\"* Constructs a new <code>\" (jde-gen-get-class-buffer-name) \"</code> with\" '>'n"
    "\"* the specified cause and message.\" '> 'n"
    "'> (jde-javadoc-insert-empty-line)"
    "\"* @param message the detail message string.\" '> 'n"
    "\"* @param cause the causing throwable. A null value is permitted\" '> 'n"
    "\"*     and indicates that the cause is nonexistent or unknown.\" '> 'n"
    "'> (jde-javadoc-insert-end-block)"
    "(jde-gen-method-signature \"public\" nil (jde-gen-get-class-buffer-name) \"String message,Throwable cause\")" 
    "(jde-gen-electric-brace)"
    "\"super(message);\" '> 'n"
    "\"initCause(cause);\" '> 'n"
    "\"}\" '> 'n"
    ;; leave a blank line with no indentation

    "\"}\" '>" "(if jde-gen-comments (concat \" // \" (jde-gen-get-class-buffer-name)))"
   "'>'n")
  "*Template for a new exception class.
Setting this variable defines a template instantiation
command `jde-gen-exception', as a side-effect."
  :group 'jde-gen
  :type '(repeat string)
  :set '(lambda (sym val)
          (tempo-define-template "java-exception-buffer-template"
                                 (jde-gen-read-template val)
                                 nil
                                 "Insert a generic Java exception buffer skeleton.")
          (defalias 'jde-gen-exception
            (list 'lambda (list)
                  (list 'interactive)
                  (list 'tempo-template-java-exception-buffer-template)))
          (set-default sym val)))

;;;###autoload
(defun jde-gen-exception-buffer (file)
  "Create a new Java buffer containing an exception class of the same name.
This command inserts the template generated by `jde-gen-exception'.
It then moves the point to the location of the first method."
  (interactive "F")
  (find-file file)
  (jde-gen-exception))

(provide 'jde-gen-extra)

;; End of jde-gen-extra.el