emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [PATCH] ob-java support for variables, functional mode, tramp, add tests
@ 2020-06-29  0:11 ian martins
  0 siblings, 0 replies; only message in thread
From: ian martins @ 2020-06-29  0:11 UTC (permalink / raw)
  To: emacs-orgmode


[-- Attachment #1.1: Type: text/plain, Size: 483 bytes --]

Hello. Attached is a patch to update the ob-java.el implementation. This
allows package, class and imports to be provided in the code or header
arguments or omitted, accepts variables, supports functional mode (:results
value), writes temp files to the temp directory instead of current dir,
works with tramp, and adds tests.

The "writes temp files to the temp directory" forced changes in ob-core in
order to write and delete directories to the temp directory.

Please review.
Ian

[-- Attachment #1.2: Type: text/html, Size: 593 bytes --]

[-- Attachment #2: 0001-Update-ob-java-to-allow-functional-mode-accept-varia.patch --]
[-- Type: text/x-patch, Size: 43718 bytes --]

From 3d66247e3aabead7cf1bda4930f0b7a84e3ee324 Mon Sep 17 00:00:00 2001
From: Ian Martins <ianxm@jhu.edu>
Date: Sun, 28 Jun 2020 19:27:08 -0400
Subject: [PATCH] Update ob-java to allow functional mode, accept variables,
 work with tramp, etc

* ob-core.el (org-babel-temp-file,
org-babel-remove-temporary-directory): Create and remove temp
directories.
* ob-java.el: Add support for functional mode, variables, make class
and main method definitions optional, write files in temporary
directory, work with tramp.
* ob-java-test.org: New test data.
* test-ob-java.el: New tests.

Java requires source files names and directories to match their class
and package names.  The old implementation wrote these directories and
files in the current directory.  We move these files to the temporary
directory to work with tramp and avoid polluting the current
directory.  In order to move these files to the temporary directory we
have to modify ob-core to write and remove diretories.
---
 lisp/ob-core.el                   |  55 ++---
 lisp/ob-java.el                   | 340 ++++++++++++++++++++++++++----
 testing/examples/ob-java-test.org | 310 +++++++++++++++++++++++++++
 testing/lisp/test-ob-java.el      | 294 ++++++++++++++++++++++++++
 4 files changed, 932 insertions(+), 67 deletions(-)
 create mode 100644 testing/examples/ob-java-test.org
 create mode 100644 testing/lisp/test-ob-java.el

diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index e798595bd..4c22522e3 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -3078,17 +3078,17 @@ Emacs shutdown."))
 	   ,@table-forms)))))
 (def-edebug-spec org-babel-result-cond (form form body))
 
-(defun org-babel-temp-file (prefix &optional suffix)
+(defun org-babel-temp-file (prefix &optional suffix dir-flag)
   "Create a temporary file in the `org-babel-temporary-directory'.
-Passes PREFIX and SUFFIX directly to `make-temp-file' with the
-value of `temporary-file-directory' temporarily set to the value
-of `org-babel-temporary-directory'."
+Passes PREFIX, SUFFIX and DIR-FLAG directly to `make-temp-file'
+with the value of `temporary-file-directory' temporarily set to
+the value of `org-babel-temporary-directory'."
   (if (file-remote-p default-directory)
       (let ((prefix
              (concat (file-remote-p default-directory)
                      (expand-file-name
 		      prefix org-babel-remote-temporary-directory))))
-        (make-temp-file prefix nil suffix))
+        (make-temp-file prefix dir-flag suffix))
     (let ((temporary-file-directory
 	   (or (and (boundp 'org-babel-temporary-directory)
 		    (file-exists-p org-babel-temporary-directory)
@@ -3099,25 +3099,32 @@ of `org-babel-temporary-directory'."
 (defun org-babel-remove-temporary-directory ()
   "Remove `org-babel-temporary-directory' on Emacs shutdown."
   (when (and (boundp 'org-babel-temporary-directory)
-	     (file-exists-p org-babel-temporary-directory))
-    ;; taken from `delete-directory' in files.el
-    (condition-case nil
-	(progn
-	  (mapc (lambda (file)
-		  ;; This test is equivalent to
-		  ;; (and (file-directory-p fn) (not (file-symlink-p fn)))
-		  ;; but more efficient
-		  (if (eq t (car (file-attributes file)))
-		      (delete-directory file)
-		    (delete-file file)))
-		(directory-files org-babel-temporary-directory 'full
-				 directory-files-no-dot-files-regexp))
-	  (delete-directory org-babel-temporary-directory))
-      (error
-       (message "Failed to remove temporary Org-babel directory %s"
-		(if (boundp 'org-babel-temporary-directory)
-		    org-babel-temporary-directory
-		  "[directory not defined]"))))))
+             (file-exists-p org-babel-temporary-directory))
+    ;; This is equivalent to "rm -rf * dir" so we should be really
+    ;; sure `dir' isn't /.  This is made safer but more brittle by
+    ;; filtering the first level files by the emacs tmpfile format.
+    (if (< (length org-babel-temporary-directory) 3)
+        (message (concat "Not deleting babel temp subdirectories since "
+                         "`babel-temporary-directory' doesn't appear "
+                         "to be set to a temporary directory."))
+      ;; taken from `delete-directory' in files.el
+      (condition-case nil
+          (progn
+            (mapc (lambda (file)
+                    ;; This test is equivalent to
+                    ;; (and (file-directory-p fn) (not (file-symlink-p fn)))
+                    ;; but more efficient
+                    (if (eq t (car (file-attributes file)))
+                        (delete-directory file t)
+                      (delete-file file)))
+                  (directory-files org-babel-temporary-directory 'full
+                                   "^[[:alnum:]]+-[[:alnum:]\-_]\\{5,8\\}$"))
+            (delete-directory org-babel-temporary-directory))
+        (error
+         (message "Failed to remove temporary Org-babel directory %s"
+                  (if (boundp 'org-babel-temporary-directory)
+                      org-babel-temporary-directory
+                    "[directory not defined]")))))))
 
 (add-hook 'kill-emacs-hook 'org-babel-remove-temporary-directory)
 
diff --git a/lisp/ob-java.el b/lisp/ob-java.el
index e8ac67cbd..beefe901d 100644
--- a/lisp/ob-java.el
+++ b/lisp/ob-java.el
@@ -1,8 +1,9 @@
-;;; ob-java.el --- Babel Functions for Java          -*- lexical-binding: t; -*-
+;;; ob-java.el --- org-babel functions for java evaluation -*- lexical-binding: t -*-
+
 
 ;; Copyright (C) 2011-2020 Free Software Foundation, Inc.
 
-;; Author: Eric Schulte
+;; Author: Eric Schulte, Ian Martins
 ;; Keywords: literate programming, reproducible research
 ;; Homepage: https://orgmode.org
 
@@ -23,8 +24,7 @@
 
 ;;; Commentary:
 
-;; Currently this only supports the external compilation and execution
-;; of java code blocks (i.e., no session support).
+;; Org-Babel support for evaluating java source code.
 
 ;;; Code:
 (require 'ob)
@@ -32,52 +32,306 @@
 (defvar org-babel-tangle-lang-exts)
 (add-to-list 'org-babel-tangle-lang-exts '("java" . "java"))
 
-(defcustom org-babel-java-command "java"
-  "Name of the java command.
-May be either a command in the path, like java
-or an absolute path name, like /usr/local/bin/java
-parameters may be used, like java -verbose"
+(defvar org-babel-default-header-args:java '()
+  "Default header args for java source blocks.")
+
+(defconst org-babel-header-args:java '((imports . :any))
+  "Java-specific header arguments.")
+
+(defvar org-babel-java-compiler-command "javac"
+  "Name of the command to execute the java compiler.")
+
+(defvar org-babel-java-runtime-command "java"
+  "Name of the command to run the java runtime.")
+
+(defcustom org-babel-java-hline-to "null"
+  "Replace hlines in incoming tables with this when translating to java."
   :group 'org-babel
-  :version "24.3"
+  :version "25.2"
+  :package-version '(Org . "9.3")
   :type 'string)
 
-(defcustom org-babel-java-compiler "javac"
-  "Name of the java compiler.
-May be either a command in the path, like javac
-or an absolute path name, like /usr/local/bin/javac
-parameters may be used, like javac -verbose"
+(defcustom org-babel-java-null-to 'hline
+  "Replace `null' in java tables with this before returning."
   :group 'org-babel
-  :version "24.3"
-  :type 'string)
+  :version "25.2"
+  :package-version '(Org . "9.3")
+  :type 'symbol)
 
 (defun org-babel-execute:java (body params)
-  (let* ((classname (or (cdr (assq :classname params))
-			(error
-			 "Can't compile a java block without a classname")))
-	 (packagename (file-name-directory classname))
-	 (src-file (concat classname ".java"))
-	 (cmpflag (or (cdr (assq :cmpflag params)) ""))
-	 (cmdline (or (cdr (assq :cmdline params)) ""))
-	 (cmdargs (or (cdr (assq :cmdargs params)) ""))
-	 (full-body (org-babel-expand-body:generic body params)))
-    (with-temp-file src-file (insert full-body))
-    (org-babel-eval
-     (concat org-babel-java-compiler " " cmpflag " " src-file) "")
+  "Execute a java source block with BODY code and PARAMS params."
+  (let* ((fullclassname (or (cdr (assq :classname params)) ; class and package
+                            (org-babel-java-find-classname body)))
+         (classname (if (seq-contains fullclassname ?.)    ; just class name
+                        (file-name-extension fullclassname)
+                      fullclassname))
+         (packagename (if (seq-contains fullclassname ?.)  ; just package name
+                          (file-name-base fullclassname)))
+         (tmpdir (file-name-as-directory (org-babel-temp-file "java-" nil t)))
+         (packagedir (if packagename                       ; package name as a path
+                         (concat tmpdir
+                                 (replace-regexp-in-string "\\\." "/" packagename))
+                       tmpdir))
+         (src-file (concat tmpdir
+                           (replace-regexp-in-string "\\\." "/" fullclassname)
+                           ".java"))
+         (cmdline (or (cdr (assq :cmdline params)) ""))
+         (cmd (concat org-babel-java-compiler-command " "
+                      (org-babel-process-file-name src-file 'noquote)
+                      " && " org-babel-java-runtime-command
+                      " -cp " (org-babel-process-file-name tmpdir 'noquote)
+                      " " fullclassname " " cmdline))
+         (result-type (cdr (assq :result-type params)))
+         (result-params (cdr (assq :result-params params)))
+         (tmp-file (and (eq result-type 'value)
+                        (org-babel-temp-file "java-")))
+         (full-body (org-babel-expand-body:java
+                     body params classname packagename result-type tmp-file)))
+
     ;; created package-name directories if missing
-    (unless (or (not packagename) (file-exists-p packagename))
-      (make-directory packagename 'parents))
-    (let ((results (org-babel-eval (concat org-babel-java-command
-                                           " " cmdline " " classname " " cmdargs) "")))
-      (org-babel-reassemble-table
-       (org-babel-result-cond (cdr (assq :result-params params))
-	 (org-babel-read results)
-         (let ((tmp-file (org-babel-temp-file "c-")))
-           (with-temp-file tmp-file (insert results))
-           (org-babel-import-elisp-from-file tmp-file)))
-       (org-babel-pick-name
-        (cdr (assq :colname-names params)) (cdr (assq :colnames params)))
-       (org-babel-pick-name
-        (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))))
+    (unless (or (not packagedir) (file-exists-p packagedir))
+      (make-directory packagedir 'parents))
+
+    (with-temp-file src-file (insert full-body))
+    (org-babel-reassemble-table
+     (org-babel-java-evaluate cmd result-type result-params tmp-file)
+     (org-babel-pick-name
+      (cdr (assoc :colname-names params)) (cdr (assoc :colnames params)))
+     (org-babel-pick-name
+      (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))))
+
+;; helper functions
+
+(defun org-babel-java-find-classname (body)
+  "Try to find fully qualified classname in BODY."
+  (let ((package (if (string-match "package \\\([^ ]*\\\);" body)
+                     (match-string 1 body)))
+        (class (if (string-match "public class \\\([^ \n]*\\\)" body)
+                   (match-string 1 body))))
+    (or (and package class (concat package "." class))
+        (and class class)
+        (and package (concat package ".Main"))
+        "Main")))
+
+(defun org-babel-expand-body:java (body params classname packagename
+                                        result-type tmp-file)
+  "Expand BODY with PARAMS.
+BODY could be a few statements, or could include a full class
+definition specifying package, imports, and class.  Because we
+allow this flexibility in what the source block can contain, it
+is simplest to expand the code block from the inside out.
+
+CLASSNAME name of the class, which may have been specified
+in multiple ways.
+
+PACKAGENAME name of the java package containing this class.
+
+RESULT-TYPE output or value.
+
+TMP-FILE name of tempfile to write to if value `result-type'."
+  (let* ((var-lines (org-babel-variable-assignments:java params))
+         (imports-val (assq :imports params))
+         (imports (if imports-val
+                      (split-string (org-babel-read (cdr imports-val) nil) " ")
+                    nil))
+         (package-re "^[[:space:]]*package .*;$")
+         (imports-re "^[[:space:]]*import .*;$")
+         (class-re "^public class [[:alnum:]_]+[[:space:]]*\n?[[:space:]]*{")
+         (main-re "public static void main(String\\(?:\\[]\\)? args\\(?:\\[]\\)?).*\n?[[:space:]]*{")
+         (move-past (lambda (re) (while (re-search-forward re nil t)
+                                   (goto-char (1+ (match-end 0)))))))
+    (with-temp-buffer
+      (insert body)
+
+      ;; wrap main
+      (goto-char (point-min))
+      (when (not (re-search-forward main-re nil t))
+        (funcall move-past package-re) ; if package is defined, move past it
+        (funcall move-past imports-re) ; if imports are defined, move past them
+        (insert "public static void main(String[] args) {\n")
+        (goto-char (point-max))
+        (insert "\n}"))
+
+      ;; wrap class
+      (goto-char (point-min))
+      (when (not (re-search-forward class-re nil t))
+        (funcall move-past package-re) ; if package is defined, move past it
+        (funcall move-past imports-re) ; if imports are defined, move past them
+        (insert (concat  "public class " (file-name-base classname) " {\n"))
+        (goto-char (point-max))
+        (insert "\n}"))
+
+      ;; insert variables
+      (when var-lines
+        (goto-char (point-min))
+        (funcall move-past class-re)   ; move inside class
+        (insert (mapconcat 'identity var-lines "\n"))
+        (insert "\n"))
+
+      ;; special handling to return value
+      (when (eq result-type 'value)
+        (goto-char (point-min))
+        (funcall move-past class-re)   ; move inside class
+        (insert "\n    public static String __toString(Object val) {\n")
+        (insert "        if (val instanceof String) {\n")
+        (insert "            return \"\\\"\" + val + \"\\\"\";\n")
+        (insert "        } else if (val == null) {\n")
+        (insert "            return \"null\";\n")
+        (insert "        } else if (val.getClass().isArray()) {\n")
+        (insert "            StringBuffer sb = new StringBuffer();\n")
+        (insert "            Object[] vals = (Object[])val;\n")
+        (insert "            sb.append(\"[\");\n")
+        (insert "            for (int ii=0; ii<vals.length; ii++) {\n")
+        (insert "                sb.append(__toString(vals[ii]));\n")
+        (insert "                if (ii<vals.length-1)\n")
+        (insert "                    sb.append(\",\");\n")
+        (insert "            }\n")
+        (insert "            sb.append(\"]\");\n")
+        (insert "            return sb.toString();\n")
+        (insert "        } else if (val instanceof List) {\n")
+        (insert "            StringBuffer sb = new StringBuffer();\n")
+        (insert "            List vals = (List)val;\n")
+        (insert "            sb.append(\"[\");\n")
+        (insert "            for (int ii=0; ii<vals.size(); ii++) {\n")
+        (insert "                sb.append(__toString(vals.get(ii)));\n")
+        (insert "                if (ii<vals.size()-1)\n")
+        (insert "                    sb.append(\",\");\n")
+        (insert "            }\n")
+        (insert "            sb.append(\"]\");\n")
+        (insert "            return sb.toString();\n")
+        (insert "        } else {\n")
+        (insert "            return String.valueOf(val);\n")
+        (insert "        }\n")
+        (insert "    }\n\n")
+        (insert "    public static void main(String[] args) throws IOException {\n")
+        (insert (format "        BufferedWriter output = new BufferedWriter(new FileWriter(\"%s\"));\n" (org-babel-process-file-name tmp-file 'noquote)))
+        (insert "        output.write(__toString(_main(args)));\n")
+        (insert "        output.close();\n")
+        (insert "    }\n\n")
+        (search-forward "public static void main(") ; rename existing main
+        (replace-match "public static Object _main("))
+
+      ;; add imports
+      (if (eq result-type 'value)
+          (setq imports (append '("java.io.BufferedWriter" "java.io.FileWriter" "java.io.IOException") imports)))
+      (if (seq-some (lambda (var-line) (string-match-p "List" var-line)) var-lines)
+          (setq imports (append '("java.util.Arrays") imports)))
+      (setq imports (append '("java.util.List") imports)) ; always needed for toString
+      (when imports
+        (goto-char (point-min))
+        (funcall move-past package-re) ; if package is defined, move past it
+        (insert (mapconcat (lambda (package) (concat "import " package ";")) (delete-dups imports) "\n") "\n"))
+
+      ;; add package at the top
+      (goto-char (point-min))
+      (when (and packagename (not (re-search-forward package-re nil t)))
+        (insert (concat "package " packagename ";\n\n")))
+
+      ;; return expanded body
+      (buffer-string))))
+
+(defun org-babel-variable-assignments:java (params)
+  "Return a list of java statements assigning the block's variables.
+variables are contained in PARAMS."
+  (mapcar
+   (lambda (pair)
+     (let* ((type-data (org-babel-java-val-to-type (cdr pair)))
+            (basetype (car type-data))
+            (var-to-java (lambda (var) (funcall #'org-babel-java-var-to-java var basetype))))
+       (format "    static %s %s = %s;"
+               (cdr type-data)                     ; type
+               (car pair)                          ; name
+               (funcall var-to-java (cdr pair))))) ; value
+   (org-babel--get-vars params)))
+
+(defun org-babel-java-var-to-java (var basetype)
+  "Convert an elisp value to a java variable.
+Convert an elisp value, VAR, of type BASETYPE into a string of
+java source code specifying a variable of the same value."
+  (cond ((and (sequencep var) (not (stringp var)))
+         (let ((var-to-java (lambda (var) (funcall #'org-babel-java-var-to-java var basetype))))
+           (concat "Arrays.asList(" (mapconcat var-to-java var ", ") ")")))
+        ((eq var 'hline) org-babel-java-hline-to)
+        ((eq basetype 'integerp) (format "%d" var))
+        ((eq basetype 'floatp) (format "%f" var))
+        ((eq basetype 'stringp) (if (and (stringp var) (string-match-p ".\n+." var))
+                                    (error "Java does not support multiline string literals")
+                                  (format "\"%s\"" var)))))
+
+(defun org-babel-java-val-to-type (val)
+  "Determine the type of VAL.
+Return (BASETYPE . LISTTYPE), where BASETYPE is a symbol
+representing the type of the individual items in VAL, and
+LISTTYPE is a string name of the type parameter for a container
+for BASETYPE items."
+  (let* ((basetype (org-babel-java-val-to-base-type val))
+         (basetype-str (pcase basetype
+                         (`integerp "Integer")
+                         (`floatp "Double")
+                         (`stringp "String")
+                         (_ (error "Unknown type %S" basetype)))))
+    (cond
+     ((and (listp val) (listp (car val))) ; a table
+      (cons basetype (format "List<List<%s>>" basetype-str)))
+     ((or (listp val) (vectorp val)) ; a list declared in the #+begin_src line
+      (cons basetype (format "List<%s>" basetype-str)))
+     (t ; return base type
+      (cons basetype basetype-str)))))
+
+(defun org-babel-java-val-to-base-type (val)
+  "Determine the base type of VAL.
+VAL may be
+`integerp' if all base values are integers
+`floatp' if all base values are either floating points or integers
+`stringp' otherwise."
+  (cond
+   ((integerp val) 'integerp)
+   ((floatp val) 'floatp)
+   ((or (listp val) (vectorp val))
+    (let ((type nil))
+      (mapc (lambda (v)
+              (pcase (org-babel-java-val-to-base-type v)
+                (`stringp (setq type 'stringp))
+                (`floatp
+                 (when (or (not type) (eq type 'integerp))
+                   (setq type 'floatp)))
+                (`integerp
+                 (unless type (setq type 'integerp)))))
+            val)
+      type))
+   (t 'stringp)))
+
+(defun org-babel-java-table-or-string (results)
+  "Convert RESULTS into an appropriate elisp value.
+If the results look like a list or vector, then convert them into an
+Emacs-lisp table, otherwise return the results as a string."
+  (let ((res (org-babel-script-escape results)))
+    (if (listp res)
+        (mapcar (lambda (el) (if (eq 'null el)
+                                 org-babel-java-null-to
+                               el))
+                res)
+      res)))
+
+(defun org-babel-java-evaluate (cmd result-type result-params tmp-file)
+  "Evaluate using an external java process.
+CMD the command to execute.
+
+If RESULT-TYPE equals 'output then return standard output as a
+string.  If RESULT-TYPE equals 'value then return the value
+returned by the source block, as elisp.
+
+RESULT-PARAMS input params used to format the reponse.
+
+TMP-FILE filename of the tempfile to store the returned value in
+for 'value RESULT-TYPE.  Not used for 'output RESULT-TYPE."
+  (let ((raw (cond ((eq result-type 'output)
+                    (org-babel-eval cmd ""))
+                   (t
+                    (org-babel-eval cmd "")
+                    (org-babel-eval-read-file tmp-file)))))
+    (org-babel-result-cond result-params raw
+      (org-babel-java-table-or-string raw))))
 
 (provide 'ob-java)
 
diff --git a/testing/examples/ob-java-test.org b/testing/examples/ob-java-test.org
new file mode 100644
index 000000000..655291061
--- /dev/null
+++ b/testing/examples/ob-java-test.org
@@ -0,0 +1,310 @@
+#+Title: a collection of examples for ob-java tests
+#+OPTIONS: ^:nil
+* Simple
+  :PROPERTIES:
+  :ID:       6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c
+  :END:
+#+name: simple
+#+begin_src java :results output silent
+  System.out.print(42);
+#+end_src
+
+#+name: simple_with_bracket
+#+begin_src java :results output silent
+  System.out.print("[42");
+#+end_src
+
+#+name: simple_with_quote
+#+begin_src java :results output silent
+  System.out.print("\"42\"");
+#+end_src
+
+#+name: simple_return_int
+#+begin_src java :results silent
+  return 42;
+#+end_src
+
+#+name: simple_return_float
+#+begin_src java :results silent
+  return 42.0;
+#+end_src
+
+#+name: simple_return_string
+#+begin_src java :results silent
+  return "forty two";
+#+end_src
+
+#+name: simple_with_main
+#+begin_src java :results output silent
+  public static void main(String[] args) {
+      System.out.print(42);
+  }
+#+end_src
+
+#+name: simple_with_main_args_array
+#+begin_src java :results output silent
+  public static void main(String args[]) {
+      System.out.print(42);
+  }
+#+end_src
+
+#+name: simple_with_class
+#+begin_src java :results output silent
+  public class Simple {
+      public static void main(String[] args) {
+          System.out.print(42);
+      }
+  }
+#+end_src
+
+#+name: simple_with_class_and_package
+#+begin_src java :results output silent
+  package pkg;
+  public class Simple {
+      public static void main(String[] args) {
+          System.out.print(42);
+      }
+  }
+#+end_src
+
+#+name: simple_with_class_attr
+#+begin_src java :results output silent :classname Simple
+  public static void main(String[] args) {
+      System.out.print(42);
+  }
+#+end_src
+
+#+name: simple_with_class_attr_with_package
+#+begin_src java :results output silent :classname pkg.Simple
+  public static void main(String[] args) {
+      System.out.print(42);
+  }
+#+end_src
+
+* Args
+  :PROPERTIES:
+  :ID:       0f1bb86b-9c9e-4ade-aaec-30b37965df1c
+  :END:
+#+name: one-arg
+#+begin_src java :results output silent :cmdline "fortytwo"
+  System.out.print(args[0]);
+#+end_src
+
+#+name: quoted_string_arg
+#+begin_src java :results output silent :cmdline "\"forty two\" 42"
+  System.out.println(args[0]);
+  System.out.println(args[1]);
+#+end_src
+
+* Variables
+  :PROPERTIES:
+  :ID:       d532cde5-2f7e-4415-99ba-3d99d148b9db
+  :END:
+
+#+name: integer_var
+#+begin_src java :var a=42 :results output silent
+  System.out.print(a);
+#+end_src
+
+#+name: var_with_main
+#+begin_src java :var a=42 :results output silent
+  public static void main(String[] args) {
+      System.out.print(a);
+  }
+#+end_src
+
+#+name: var_with_class
+#+begin_src java :var a=42 :results output silent
+  public class Main {
+      public static void main(String[] args) {
+          System.out.print(a);
+      }
+  }
+#+end_src
+
+#+name: var_with_class_and_package
+#+begin_src java :var a=42 :results output silent
+  package pkg;
+  public class Main {
+      public static void main(String[] args) {
+          System.out.print(a);
+      }
+  }
+#+end_src
+
+#+name: var_with_class_and_hanging_curlies
+#+begin_src java :var a=42 :results output silent
+  public class Main
+  {
+      public static void main(String[] args)
+      {
+          System.out.print(a);
+      }
+  }
+#+end_src
+
+#+name: two_vars
+#+begin_src java :var a=21 b=2 :results output silent
+  System.out.print(a*b);
+#+end_src
+
+#+name: string_var
+#+begin_src java :var a="forty two" :results output silent
+  System.out.print(String.format("%s, len=%d", a, a.length()));
+#+end_src
+
+#+name: mulitline_string_var
+#+begin_src java :var a="forty\ntwo" :results output silent
+  System.out.print(String.format("%s, len=%d", a, a.length()));
+#+end_src
+
+* Array
+  :PROPERTIES:
+  :ID:       3512119d-a2da-43c1-af7b-19a3cb72f76d
+  :END:
+
+#+name: java_list
+- forty
+- two
+
+#+name: return_vector_using_list
+#+begin_src java :results vector silent
+  import java.util.List;
+  import java.util.Arrays;
+  List<List<Integer>> a = Arrays.asList(Arrays.asList(4),
+                                        Arrays.asList(2));
+  return a;
+#+end_src
+
+#+name: return_vector_using_array
+#+begin_src java :results vector silent
+  Integer[][] a = {{4}, {2}};
+  return a;
+#+end_src
+
+#+name: read_return_list
+#+begin_src java :var a=java_list :results silent
+  import java.util.List;
+  import java.util.Arrays;
+  List<String> b = Arrays.asList(a.get(0).get(0),
+                                 a.get(1).get(0));
+  return b;
+#+end_src
+
+#+name: read_list_return_array
+#+begin_src java :var a=java_list :results silent
+  String[] b = {a.get(0).get(0), a.get(1).get(0)};
+  return b;
+#+end_src
+
+#+name: read_return_list_with_package
+#+begin_src java :var a=java_list :results silent
+  package pkg;
+  import java.util.List;
+  import java.util.Arrays;
+  List<String> b = Arrays.asList(a.get(0).get(0),
+                                 a.get(1).get(0));
+  return b;
+#+end_src
+
+#+name: write_output_list_with_spaces
+#+begin_src java :results output list raw silent
+  System.out.println("forty two");
+  System.out.println("forty two");
+#+end_src
+
+* Matrix
+  :PROPERTIES:
+  :ID:       2b99499f-c12f-44fd-9b5d-1af929ca0079
+  :END:
+#+name: java_matrix
+| 2 | 1 |
+| 4 | 2 |
+
+#+name: list_var
+#+begin_src java :var a='("forty" "two") :results silent
+  import java.util.List;
+  List<String> b = a;
+  return b;
+#+end_src
+
+#+name: vector_var
+#+begin_src java :var a='["forty" "two"] :results silent
+  import java.util.List;
+  List<String> b = a;
+  return b;
+#+end_src
+
+#+name: matrix_var
+#+begin_src java :var a=java_matrix :results silent
+  import java.util.List;
+  import java.util.Arrays;
+  List<List<Integer>> b = Arrays.asList(Arrays.asList(a.get(0).get(0), a.get(1).get(0)),
+                                        Arrays.asList(a.get(0).get(1), a.get(1).get(1)));
+  return b; // transpose
+#+end_src
+
+#+name: matrix_var_with_header
+#+begin_src java :var a=java_matrix :results value table silent
+  import java.util.List;
+  import java.util.Arrays;
+  List<List> b = Arrays.asList(Arrays.asList("col1", "col2"),
+                                       null,
+                                       Arrays.asList(a.get(0).get(0), a.get(1).get(0)),
+                                       Arrays.asList(a.get(0).get(1), a.get(1).get(1)));
+  return b; // transpose
+#+end_src
+
+#+name: output_table_with_header
+#+begin_src java :var a=java_matrix :results output raw table silent
+  System.out.println("|col1|col2|");
+  System.out.println("|-");
+  for (int ii=0; ii<a.size(); ii++) {
+      for (int jj=0; jj<a.get(0).size(); jj++) {
+          System.out.print("|" + a.get(ii).get(jj));
+      }
+      System.out.println("");
+   }
+#+end_src
+
+* Inhomogeneous Table
+  :PROPERTIES:
+  :ID:       7fc09793-68e6-421c-8245-284863d025bf
+  :END:
+
+#+name: java_table
+  | string | number |
+  |--------+--------|
+  | forty  |      2 |
+  | two    |      1 |
+
+#+name: inhomogeneous_table
+#+begin_src java :var a=java_table :results silent
+  import java.util.List;
+  import java.util.Arrays;
+  List<List> b = Arrays.asList(Arrays.asList(a.get(0).get(0),
+                                             Integer.parseInt(a.get(0).get(1))*2),
+                               Arrays.asList(a.get(1).get(0),
+                                             Integer.parseInt(a.get(1).get(1))*2));
+  return b;
+#+end_src
+
+* Library
+  :PROPERTIES:
+  :ID:       3ce0d53f-780a-4769-8614-cbdd3e265708
+  :END:
+
+#+name: import_library
+#+begin_src java :results output silent :imports java.util.Base64
+  byte[] encoded = Base64.getEncoder().encode("42".getBytes());
+  String decoded = new String(Base64.getDecoder().decode(encoded));
+  System.out.print(String.format("encoded=%s, decoded=%s", new String(encoded), decoded));
+#+end_src
+
+#+name: import_library_inline
+#+begin_src java :results output silent
+  import java.util.Base64;
+  byte[] encoded = Base64.getEncoder().encode("42".getBytes());
+  String decoded = new String(Base64.getDecoder().decode(encoded));
+  System.out.print(String.format("encoded=%s, decoded=%s", new String(encoded), decoded));
+#+end_src
diff --git a/testing/lisp/test-ob-java.el b/testing/lisp/test-ob-java.el
new file mode 100644
index 000000000..dc23b4f31
--- /dev/null
+++ b/testing/lisp/test-ob-java.el
@@ -0,0 +1,294 @@
+;;; test-ob-java.el --- tests for ob-java.el
+
+;; Copyright (c) 2020 Ian Martins
+;; Authors: Ian Martins
+
+;; This file is not part of GNU Emacs.
+
+;; 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 3 of the License, 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.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+(require 'org-test)
+
+(org-test-for-executable "java")
+(unless (featurep 'ob-java)
+  (signal 'missing-test-dependency "Support for java code blocks"))
+
+(ert-deftest ob-java/simple ()
+  "Hello world program that writes output."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 1)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-bracket ()
+  "Hello world program that outputs an open square bracket."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 2)
+              (should (string= "[42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-quote ()
+  "Hello world program that writes quotes."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 3)
+              (should (string= "\"42\"" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-return-int ()
+  "Hello world program that returns an int value."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 4)
+              (should (eq 42 (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-return-float ()
+  "Hello world program that returns a float value."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 5)
+              (should (equal 42.0 (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-return-string ()
+  "Hello world program that returns a string value."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 6)
+              (should (string= "forty two" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-main ()
+  "Hello world program that defines a main function."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 7)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-main-args-array ()
+  "Hello world program that defines a main function with the square brackets after `args'."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 8)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-class ()
+  "Hello world program that defines a class."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 9)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-class-and-package ()
+  "Hello world program that defines a class and package."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 10)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-class-attr ()
+  "Hello world program with class header attribute."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 11)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/simple-with-class-attr-with-package ()
+  "Hello world program with class attr with package."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "6abbd6a0-b1b1-4fbf-96b6-6fbc024c4a6c"
+              (org-babel-next-src-block 12)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+
+(ert-deftest ob-java/one-arg ()
+  "Command line arg."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "0f1bb86b-9c9e-4ade-aaec-30b37965df1c"
+              (org-babel-next-src-block 1)
+              (should (string= "fortytwo" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/args-quoted-string ()
+  "Two command line args, first contains a space."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "0f1bb86b-9c9e-4ade-aaec-30b37965df1c"
+              (org-babel-next-src-block 2)
+              (should (string= "forty two\n42\n" (org-babel-execute-src-block))))))
+
+
+(ert-deftest ob-java/integer-var ()
+  "Read and write an integer variable."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 1)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/var-with-main ()
+  "Read and write an integer variable, with main function provided."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 2)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/var-with-class ()
+  "Read and write an integer variable, with class provided."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 3)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/var-with-class-and-package ()
+  "Read and write an integer variable, with class and package provided."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 4)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/var-with-class-and-hanging-curlies ()
+  "Read and write an integer variable, with class with hanging curlies."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 5)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/two-vars ()
+  "Read two integer variables, combine and write them."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 6)
+              (should (string= "42" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/string-var ()
+  "Read and write a string variable."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 7)
+              (should (string= "forty two, len=9" (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/multiline-string-var ()
+  "Java doesn't support multiline string literals, so this errors."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "d532cde5-2f7e-4415-99ba-3d99d148b9db"
+              (org-babel-next-src-block 8)
+              (should-error (org-babel-execute-src-block))
+              :type 'error)))
+
+
+(ert-deftest ob-java/return-vector-using-list ()
+  "Return a vector using a list."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 1)
+              (should (equal '((4) (2)) (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/return-vector-using-array ()
+  "Return a vector using an array."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 2)
+              (should (equal '((4) (2)) (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/read-return-list ()
+  "Read and return a list."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 3)
+              (should (equal '("forty" "two") (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/read-list-return-array ()
+  "Read a list and return an array."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 4)
+              (should (equal '("forty" "two") (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/read-return-list-with-package ()
+  "Return a vector."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 5)
+              (should (equal '("forty" "two") (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/read-output-list ()
+  "Return a vector."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3512119d-a2da-43c1-af7b-19a3cb72f76d"
+              (org-babel-next-src-block 6)
+              (should (equal "forty two\nforty two\n" (org-babel-execute-src-block))))))
+
+
+(ert-deftest ob-java/list-var ()
+  "Read and write a list variable."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "2b99499f-c12f-44fd-9b5d-1af929ca0079"
+              (org-babel-next-src-block 1)
+              (should (equal '("forty" "two") (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/vector-var ()
+  "Read and write a vector variable."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "2b99499f-c12f-44fd-9b5d-1af929ca0079"
+              (org-babel-next-src-block 2)
+              (should (equal '("forty" "two") (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/matrix-var ()
+  "Read and write matrix variable."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "2b99499f-c12f-44fd-9b5d-1af929ca0079"
+              (org-babel-next-src-block 3)
+              (should (equal '((2 4) (1 2)) (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/matrix-var-with-header ()
+  "Read matrix variable and write it with header."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "2b99499f-c12f-44fd-9b5d-1af929ca0079"
+              (org-babel-next-src-block 4)
+              (should (equal '(("col1" "col2") hline (2 4) (1 2)) (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/output-table-with-header ()
+  "Write a table that includes a header."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "2b99499f-c12f-44fd-9b5d-1af929ca0079"
+              (org-babel-next-src-block 5)
+              (should (equal "|col1|col2|\n|-\n|2|1\n|4|2\n" (org-babel-execute-src-block))))))
+
+
+(ert-deftest ob-java/inhomogeneous_table ()
+  "Read and write an inhomogeneous table."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "7fc09793-68e6-421c-8245-284863d025bf"
+              (org-babel-next-src-block 1)
+              (should (equal
+                       '(("forty" 4) ("two" 2))
+                       (org-babel-execute-src-block))))))
+
+
+(ert-deftest ob-java/import_library ()
+  "Import a standard java library."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3ce0d53f-780a-4769-8614-cbdd3e265708"
+              (org-babel-next-src-block 1)
+              (should (string=
+                       "encoded=NDI=, decoded=42"
+                       (org-babel-execute-src-block))))))
+
+(ert-deftest ob-java/import_library_inline ()
+  "Import a standard java library."
+  (if (executable-find org-babel-java-compiler-command)
+      (org-test-at-id "3ce0d53f-780a-4769-8614-cbdd3e265708"
+              (org-babel-next-src-block 2)
+              (should (string=
+                       "encoded=NDI=, decoded=42"
+                       (org-babel-execute-src-block))))))
+
+
+;;; test-ob-java.el ends here
-- 
2.17.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2020-06-29  0:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-29  0:11 [PATCH] ob-java support for variables, functional mode, tramp, add tests ian martins

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).