;; -*- emacs-lisp -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; $Id: java.el,v 1.21 2006/03/06 12:07:06 ole Exp $
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; java development initialization
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; (add-to-list 'load-path (expand-file-name "jde/lisp/" emacs-packages-dir))
(require 'jde)
(require 'jde-junit-extra)

;; (require 'jde-docindex)
;; (require 'jde-jode)
;; (require 'jde-jswat)
(require 'decompile)

;;java
(add-to-list 'auto-mode-alist '("\\.java$" . jde-mode))
(add-to-list 'auto-mode-alist '("\\.bsh$" . jde-mode))
(add-to-list 'auto-mode-alist '("\\.djava$" . jde-mode))
(add-to-list 'auto-mode-alist '("\\.properties$" . java-properties-generic-mode))
(add-to-list 'auto-mode-alist '("\\.[Mm][Ff]$" . java-manifest-generic-mode))

(add-to-list 'file-coding-system-alist '("\\.java\\'" utf-8 . utf-8))

;;javascript
(add-to-list 'auto-mode-alist '("\\.js$" . javascript-generic-mode))

(global-set-key [(meta f9)] 'jde-run)
(global-set-key [(control f9)] 'jde-build)

;; highlight 81'th character
(font-lock-add-keywords 'jde-mode '(("^................................................................................\\(.\\)"
                                     1 'font-lock-warning-face prepend)))

;; advice jde to jump to defining line when opening
;; class buffer
(defadvice jde-open-class-source (after fix pre act comp)
  (senator-jump (ad-get-arg 0)))

;; ignore code with ispell-comments-and-strings
(setq ispell-skip-region-alist 
      (nconc ispell-skip-region-alist (list '("<code>" . "</code>") '("<pre>" . "</pre>") '("{@link" . "}"))))

(defun jde-indent-complete ()
  "A special indent/complete function. I calls three different
functions depending on context:

- The region is active:
        reindent the region.

- The point is in front of the text on this line:
        try to reindent the line. 

- The point is directly after a dot character (\".\") or in/at
   the end of a word starting with a dot:
        call `jde-complete'

- Otherwise:
        call `hippie-expand'
"
  (interactive)
  (if (mark-or-region-active)
      (indent-region (mark) (point) nil)
    (if (save-excursion (skip-chars-backward " \t") (bolp))
        (indent-for-tab-command)
      (if (not (equal major-mode 'jde-mode))
          (hippie-expand 1)
        (if (or (char-equal (char-before) ?.)
                (save-excursion (backward-word 1) 
                                (char-equal (char-before) ?.)))
            (jde-complete)
          (hippie-expand 1))))))

(def-slime-selector-method ?j
    "Latest jde-mode buffer"
  (let ((currbuf (current-buffer)))
    (next-buffer)
    (while (not (or (eq currbuf (current-buffer))
                    (eq major-mode 'jde-mode)))
      (next-buffer))
    (current-buffer)))

(def-slime-selector-method ?x
    "Latest xml-mode buffer"
  (let ((currbuf (current-buffer)))
    (next-buffer)
    (while (not (or (eq currbuf (current-buffer))
                    (eq major-mode 'nxml-mode)))
      (next-buffer))
    (current-buffer)))

;; override

(defun jde-debug-cont-to-here ()
  "continue program up to current cursor position"
  (interactive)
  (jde-debug-set-breakpoint)
  (jde-debug-cont)
  (jde-debug-clear-breakpoint))

(defun jde-import-delete-blank-lines ()
  "delete blank lines around import statements"
  (interactive "*")
  (beginning-of-buffer)
  (when (re-search-forward "^import " nil t)
    (beginning-of-line)
    (forward-line -1)
    (newline)
    (newline)
    (delete-blank-lines))
  (end-of-buffer)
  (when (re-search-backward "^import " nil t)
    (beginning-of-line)
    (forward-line 1)
    (newline)
    (newline)
    (delete-blank-lines))
  )

(defadvice jde-import-organize (after first nil activate)
  "Kill unused import statements after organizing"
  (save-excursion 
    (jde-import-kill-extra-imports)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;*_ JDE-Usages
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun jde-bsh-exit-nicely ()
  "Tells the beanshell process to terminate and waits until the process finishes up."
  (interactive)
  (if (jde-bsh-running-p)
      (let ((process (bsh-get-process (oref 'jde-bsh the-bsh))))
        (message "Shutting down beanshell process")
        (if (and
             (boundp 'jde-ant-invocation-method) ;; ant package may not be loaded.
             (string= (car jde-ant-invocation-method) "Ant Server"))
            (bsh-eval (oref 'jde-bsh the-bsh) "jde.util.JdeUtilities.exit();\n")
          (bsh-eval (oref 'jde-bsh the-bsh) "exit();\n"))
        (message ""))
    (message "The beanshell is not running")))

(add-hook 'kill-emacs-hook 'jde-bsh-exit-nicely)

(define-key jde-mode-map "\C-cu" nil)
(define-key jde-mode-map "\C-cup" 'jde-usages-display-call-tree-for-thing-at-point)
(define-key jde-mode-map "\C-cui" 'jde-usages-display-call-tree-for-specified-class)
(define-key jde-mode-map "\C-cum" 'jde-usages-display-call-tree)
(define-key jde-mode-map "\C-cur" 'jde-usages-display-subs-implementing-method)
(define-key jde-mode-map "\C-cun" 'jde-usages-next-pos)
(define-key jde-mode-map "\C-cuh" 'jde-usages-display-subs-and-implementers)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;*_ JDE-Flymake
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'jde-flymake)
(require 'flymake-helper)

(defun ole-flymake-goto-next-error ()
  "Goto next flymake error and show the error message."
  (interactive)
  (flymakeh-goto-next-error)
  (flymakeh-save-as-kill-err-messages-for-current-line))

(defun ole-flymake-goto-prev-error ()
  "Goto previous flymake error and show the error message."
  (interactive)
  (flymakeh-goto-prev-error)
  (flymakeh-save-as-kill-err-messages-for-current-line))

(defun ole-flymake-open-compilation (&optional other-files)
  "Open Flymake errors in a compilation buffer.
Shows only errors for the current buffer.
With the optional prefix argument it also shows messages for other
files."
  (interactive "P")
  (let ((errs (flymake-get-buffer-err-info (current-buffer)))
        (buf-name (buffer-name))
        (comp-buffer (get-buffer-create "*compilation*"))
        (have-error nil))
    (switch-to-buffer-other-window comp-buffer)
    (setq buffer-read-only nil)
    (erase-buffer)
    (if (null errs)
        (insert "\nCompilation finished at " (current-time-string)
                "\n")
      (dolist (fileline errs)
        (dolist (errmsg (cadr fileline))
          (let ((file (nth 0 errmsg))
                (line (nth 1 errmsg))
                (severity (nth 2 errmsg))
                (msg (nth 3 errmsg))
                (path (nth 4 errmsg)))
            (and (and (or other-files (null file)) (> line 0))
                 (setq have-error t)
                 (insert path ":" (number-to-string line) ":"
                         (replace-regexp-in-string "\\`[:0-9]+: \\(Semantic \\|Syntax \\)*" "" msg)
                         "\n")))))
      (insert "\nCompilation exited abnormally with code 1 at \n"
              (current-time-string) "\n"))
    (compilation-mode)
    (if have-error (next-error 0 t) (delete-window) (message "No errors"))))

(define-key jde-mode-map [(control x) f10] 'ole-flymake-goto-next-error)
(define-key jde-mode-map [(control x) (control f10)] 'ole-flymake-goto-prev-error)
(define-key jde-mode-map [(control x) f11] 'flymakeh-save-as-kill-err-messages-for-current-line)
(define-key jde-mode-map [(control f11)] 'ole-flymake-open-compilation)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; minor mode for log files: jump from stack trace to source
;; from Philip Lord
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar jde-run-etrace-mode-map (make-sparse-keymap))

(define-key  jde-run-etrace-mode-map "\C-c\C-v8" 'jde-run-etrace-prev)
(define-key  jde-run-etrace-mode-map "\C-c\C-v9" 'jde-run-etrace-next)
(define-key  jde-run-etrace-mode-map [mouse-2] 'jde-run-etrace-show-at-mouse)


(easy-mmode-define-minor-mode jde-etrace-mode
                              "Jump to stack trace"
                              nil " etrace" jde-run-etrace-mode-map)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BSH misc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun jde-gen-beaninfo (classname)
  "Generate a beaninfo class for the given full qualified classname."
  (interactive "sEnter classname: ")
  (let ((class (concat classname ".class"))
        (filename (jde-open-get-java-file-name (concat classname "BeanInfo"))))
    (find-file filename)
    (jde-bsh-insert-return (concat "beanInfoMaker(" class ")"))))

(defun jde-bsh (command)
  "call `command' in the beanshell and display result in minibuffer."
  (interactive "sCommand: ")
  (jde-jeval command nil))

(defun jde-bsh-eval (command)
  "call `command' in the beanshell and evaluate return."
  (interactive "sCommand: ")
  (jde-jeval command t))

(defun jde-bsh-insert-return (command)
  "call `command' in the beanshell and insert result at point."
  (interactive "sCommand: ")
  (insert (jde-jeval (concat "print(" command ");") nil)))

(defun jde-bsh-region (start end)
  "evaluate region in the beanshell and display result in minibuffer."
  (interactive "r")
  (message (jde-jeval (buffer-substring start end) nil)))

(defun jde-bsh-region-eval (start end)
  "evaluate region in the beanshell and display result in minibuffer."
  (interactive "r")
  (jde-jeval (buffer-substring start end) t))

(defun jde-bsh-region-insert-return (start end)
  "evaluate region in the beanshell and insert result at point."
  (interactive "r")
  (insert (jde-jeval (buffer-substring start end) nil)))

;; programmer friendly mapping for german keybords
(define-key jde-mode-map [?ö] "{")
(define-key jde-mode-map [?ä] "}")
(define-key jde-mode-map [?Ö] "[")
(define-key jde-mode-map [?Ä] "]")
(define-key jde-mode-map [?Ü] "@")
(define-key jde-mode-map [?ü] "|")

(defun ole-jde-mode-hook ()
  "my jde mode entering java buffer hook"
  (interactive)
  (ispell-change-dictionary "english")
  (disable-abbrev-mode)
  (set-fill-column 80)
  (turn-on-auto-fill)
  (define-key jde-mode--map "\C-m" 'c-context-line-break))


;; ask for parameters
(setq tempo-interactive t)

(defconst jde-java-buffer-header
  (list "/*" 'n 
        " * " '(file-name-nondirectory buffer-file-name) 'n 
        " *" 'n
        " * Copyright (c) 2006 Ole Arndt, all rights reserved." 'n
        " *" 'n
        " * Author: " '(user-full-name) " <" '(user-mail-address) ">" 'n
        " * Created " '(current-time-string) 'n " */" 'n 'n
        )
)

(defun jde-gen-java-buffer-header ()
  (cons 'l jde-java-buffer-header))

(defun jde-gen-cegedim-buffer-header ()
  '(l ("/*" n 
       " * " (file-name-nondirectory buffer-file-name) n 
       " *" n
       " * Copyright (c) 2006 Cegedim, all rights reserved." n
       " *" n
       " * Author:  Ole Arndt <oliver.arndt@cegedim.fr>" n
       " * Created " (current-time-string) n " */" n n)))

(defalias 'jde-gen-cegedim-header
  (tempo-define-template "cegedim-buffer-header" (jde-gen-cegedim-buffer-header)
                         "cegedim-header" "Generate a copyright header for cegedim source buffers."))

(defalias 'jde-gen-buffer-header
  (tempo-define-template "java-buffer-header" jde-java-buffer-header
                         "buffer-header" "Generate a copyright header for java source buffers."))

(defconst jde-gen-enumeration-list
  (list "/**" 'n>
        " * A typesave enumeration class." 'n>
        " *" 'n>
        " * @author <a href=\"mailto:" '(user-mail-address) "\">" (user-full-name) "</a>" 'n>
        " * @version $Id: java.el,v 1.21 2006/03/06 12:07:06 ole Exp $" 'n>
        " */" 'n>
        "public abstract class " '(jde-gen-get-class-buffer-name)
        " implements java.io.Serializable {" 'n>
        "private final transient String name;" 'n> 
        'n>
        "/**" 'n>
        " * Constructs a new <code>" '(jde-gen-get-class-buffer-name) "</code> with "'n>
        " * a name." 'n>
        " * @param name the name o the enumeration." 'n>
        " */" 'n>
        "protected " '(jde-gen-get-class-buffer-name) "(String name) {" 'n>
        "this.name = name;" 'n>
        "}" 'n>
        'n>
        "/**" 'n>
        " * Example abstract operation." 'n>
        " */" 'n>
        "public abstract void eval();" 'n>
        'n>
        "// Inner classes as instances" 'n>
        'n>
        "public static final ONE = new " '(jde-gen-get-class-buffer-name) "(\"one\") {" 'n>
        "public void eval() {" 'n>
        "}" 'n>
        "};" 'n>
        'n>
        "// Handle identity and serialisation" 'n>
        'n>
        "/**" 'n>
        " * Return the name." 'n>
        " */" 'n>
        "public final String toString() {" 'n>
        "return name;" 'n>
        "}" 'n>
        'n>
        "/**" 'n>
        " * Ensure the equals method of <code>java.lang.Object<code> is called." 'n>
        " */" 'n>
        "public final boolean equals(Object o) {" 'n>
        "    return super.equals(o);" 'n>
        "}" 'n>
        'n>
        "/**" 'n>
        " * Ensure the hashCode method of <code>java.lang.Object<code> is called." 'n>
        " */" 'n>
        "public final int hashCode() {" 'n>
        "   return super.hashCode();" 'n>
        "}" 'n>
        'n>
        "private static int nextId = 0;" 'n>
        "private final int id = nextId++;" 'n>
        "private static final " '(jde-gen-get-class-buffer-name) "[] instances = { ONE };" 'n>
        "Object readResolve() throws java.io.ObjectStreamException {" 'n>
        "    return instances[id];" 'n>
        "}" 'n>
        "} // " '(jde-gen-get-class-buffer-name) 'n>
        ))

(defalias 'jde-gen-enumeration
  (tempo-define-template "enumeration" jde-gen-enumeration-list
                         "enumeration" "Generate an enumeration skeleton."))

(defalias 'jde-gen-enumeration-buffer
  (tempo-define-template "enumeration-buffer" 
                         (list '(jde-gen-java-buffer-header) (cons 'l jde-gen-enumeration-list) '(jde-package-update))
                         "enumeration-buffer" "Generate an enumeration skeleton buffer."))

(defconst jde-gen-derived-enumeration-list
  (list "/**" 'n>
        " * A typesave enumeration class." 'n>
        " *" 'n>
        " * @author <a href=\"mailto:" '(user-mail-address) "\">" (user-full-name) "</a>" 'n>
        " * @version $Id: java.el,v 1.21 2006/03/06 12:07:06 ole Exp $" 'n>
        " */" 'n>
        "public abstract class " '(jde-gen-get-class-buffer-name)
        " " '(jde-gen-get-extend-class) "{" 'n>
        'n>
        "/**" 'n>
        " * Constructs a new <code>" '(jde-gen-get-class-buffer-name) "</code> with "'n>
        " * a name." 'n>
        " * @param name The name of the enumeration." 'n>
        " */" 'n>
        "protected " '(jde-gen-get-class-buffer-name) "(String name) {" 'n>
        "super(name);" 'n>
        "}" 'n>
        'n>
        'n>
        "// Inner classes as instances" 'n>
        'n>
        "public static final ONE = new " '(jde-gen-get-class-buffer-name) "(\"one\") {" 'n>
        "};" 'n>
        'n>
        "private static int nextId = 0;" 'n>
        "private final int id = nextId++;" 'n>
        "private static final " '(jde-gen-get-class-buffer-name) "[] instances = { ONE };" 'n>
        "Object readResolve() throws java.io.ObjectStreamException {" 'n>
        "    return instances[id];" 'n>
        "}" 'n>
        "} // " '(jde-gen-get-class-buffer-name) 'n>
        ))

(defalias 'jde-gen-derived-enumeration
  (tempo-define-template "derived-enumeration" jde-gen-derived-enumeration-list
                         "derived-enumeration" "Generate an derived-enumeration skeleton."))

(defalias 'jde-gen-derived-enumeration-buffer
  (tempo-define-template "derived-enumeration-buffer" 
                         (list '(jde-gen-java-buffer-header) 
                               (cons 'l jde-gen-derived-enumeration-list) '(jde-package-update))
                         "derived-enumeration-buffer" "Generate an derived-enumeration skeleton buffer."))

(unless (fboundp 'jde-gen-class-name)
  (defun jde-gen-class-name ()
    (jde-gen-get-class-buffer-name)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Run test with maven
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro def-maven-action (name args docstring)
  "Generate a JDE maven command."
  (let ((funcname (intern (concat "jde-maven-" (symbol-name name)))))
    `(eval-and-compile
       (defun ,funcname ()
         ,docstring
         (interactive)
         (require 'jde-ant)
         (let* ((dir (file-name-directory (buffer-file-name)))
                (project-file (jde-ant-find-build-file dir)))
           (jde-ant-build project-file ,args))))))

(def-maven-action compile "-Dmaven.test.skip=true jar:install"
  "Compile this project with maven")

(def-maven-action compile-tests "-Dmaven.test.skip=false test:compile"
  "Compile this project's tests with maven")

(def-maven-action run-tests "-Dmaven.test.skip=false test"
  "Compile this project's tests with maven")

(defun jde-maven-run-single-test ()
  "Run test belonging to this source file via maven"
  (interactive)
  (require 'jde-ant)
  (let* ((dir (file-name-directory (buffer-file-name)))
         (class-name (file-name-sans-extension (file-name-nondirectory (buffer-file-name))))
         (test-name  (concat class-name (if (not (string-match "Test" class-name)) "Test")))
         (project-file (jde-ant-find-build-file dir)))
    (jde-ant-build project-file (concat "-Dmaven.test.skip=false -Dtestmatch='"test-name "' test:match"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Additional wiz methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;

(defun jde-wiz-bean-methods (prefix)
  "Generate getters and setters for all properties.
With prefix generate only setters, with duble prefix only setters."
  (interactive "p")
  (let ((jde-wiz-get-set-methods-include
         (list (case prefix (4 "Set only") (8 "Get only") (t "Both")))))
    (jde-wiz-get-set-methods)))