(in-package :jss) (defun set-to-list (set) "Convert the java.util.Set named in SET to a Lisp list." (declare (optimize (speed 3) (safety 0))) (with-constant-signature ((iterator "iterator" t) (hasnext "hasNext") (next "next")) (loop with iterator = (iterator set) while (hasNext iterator) for item = (next iterator) collect item))) (defun jlist-to-list (list) "Convert a LIST implementing java.util.List to a Lisp list." (declare (optimize (speed 3) (safety 0))) (loop :for i :from 0 :below (jcall "size" list) :collecting (jcall "get" list i))) (defun jarray-to-list (jarray) "Convert the Java array named by JARRARY into a Lisp list." (declare (optimize (speed 3) (safety 0))) (loop :for i :from 0 :below (jarray-length jarray) :collecting (jarray-ref jarray i))) ;;; Deprecated ;;; ;;; XXX unclear what sort of list this would actually work on, as it ;;; certainly doesn't seem to be any of the Java collection types ;;; (what implements getNext())? (defun list-to-list (list) (declare (optimize (speed 3) (safety 0))) (with-constant-signature ((isEmpty "isEmpty") (getfirst "getFirst") (getNext "getNext")) (loop until (isEmpty list) collect (getFirst list) do (setq list (getNext list))))) ;; Contribution of Luke Hope. (Thanks!) (defun iterable-to-list (iterable) "Return the items contained the java.lang.Iterable ITERABLE as a list." (declare (optimize (speed 3) (safety 0))) (let ((it (#"iterator" iterable))) (with-constant-signature ((has-next "hasNext") (next "next")) (loop :while (has-next it) :collect (next it))))) (defun vector-to-list (vector) "Return the elements of java.lang.Vector VECTOR as a list." (declare (optimize (speed 3) (safety 0))) (with-constant-signature ((has-more "hasMoreElements") (next "nextElement")) (let ((elements (#"elements" vector))) (loop :while (has-more elements) :collect (next elements))))) (defun hashmap-to-hashtable (hashmap &rest rest &key (keyfun #'identity) (valfun #'identity) (invert? nil) table &allow-other-keys ) "Converts the a HASHMAP reference to a java.util.HashMap object to a Lisp hashtable. The REST paramter specifies arguments to the underlying MAKE-HASH-TABLE call. KEYFUN and VALFUN specifies functions to be run on the keys and values of the HASHMAP right before they are placed in the hashtable. If INVERT? is non-nil than reverse the keys and values in the resulting hashtable." (let ((keyset (#"keySet" hashmap)) (table (or table (apply 'make-hash-table (loop for (key value) on rest by #'cddr unless (member key '(:invert? :valfun :keyfun :table)) collect key and collect value))))) (with-constant-signature ((iterator "iterator" t) (hasnext "hasNext") (next "next")) (loop with iterator = (iterator keyset) while (hasNext iterator) for item = (next iterator) do (if invert? (setf (gethash (funcall valfun (#"get" hashmap item)) table) (funcall keyfun item)) (setf (gethash (funcall keyfun item) table) (funcall valfun (#"get" hashmap item))))) table))) ;; **************************************************************** ;; But needing to remember is annoying ;; Here's a summary I gleaned: ;; java.util.Dictionary -> #"elements" yields java.util.Collections$3 ;; java.util.AbstractCollection -> #"iterator" yields java.util.Iterator? ;; org.apache.felix.framework.util.CompoundEnumeration -> implements java.util.Enumeration ;; java.util.Collections -> doc says #"iterator" yields java.util.Iterator ;; java.util.Collections$1) -> implements java.util.Iterator ;; java.util.Collections$2) -> implements java.util.Spliterator (#"iterator" (#"stream" 'StreamSupport )) -> java.util.Iterator ;; java.util.Collections$3) -> implements java.util.Enumeration ;; java.util.Iterator ;; ("next" "hasNext") ;; java.util.Enumeration) ;; ("nextElement" "hasMoreElements") ;; TODO: maybe do it even more MAPC-style and accept multiple sequences too? (defun jmap (function thing) "Call FUNCTION for every element in the THING. Returns NIL. THING may be a wide range of Java collection types, their common iterators or a Java array. In case the THING is a map-like object, FUNCTION will be called with two arguments, key and value." (flet ((iterator-run (iterator) (with-constant-signature ((has-next "hasNext") (next "next")) (loop :while (has-next iterator) :do (funcall function (next iterator))))) (enumeration-run (enumeration) (with-constant-signature ((has-next "hasMoreElements") (next "nextElement")) (loop :while (has-next enumeration) :do (funcall function (next enumeration))))) (map-run (map) (with-constant-signature ((has-next "hasMoreElements") (next "nextElement")) (let ((keyiterator (#"iterator" (#"keyset" map)))) (loop :while (has-next keyiterator) :for key = (next keyiterator) :do (funcall function key (#"get" map key))))))) (let ((isinstance (load-time-value (jmethod "java.lang.Class" "isInstance" "java.lang.Object")))) (cond ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.AbstractCollection"))) thing) (iterator-run (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Iterator"))) thing) (iterator-run thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.lang.Iterable"))) thing) (iterator-run (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Enumeration"))) thing) (enumeration-run thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.AbstractMap"))) thing) (map-run thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Collections"))) thing) (iterator-run (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Spliterator"))) thing) (iterator-run (#"iterator" (#"stream" 'StreamSupport thing)))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Dictionary"))) thing) (iterator-run (#"elements" thing))) (t (let ((jarray (ignore-errors (or (and (jclass-array-p (jclass-of thing)) thing) (#"toArray" thing))))) (if jarray (loop :for i :from 0 :below (jarray-length jarray) :do (funcall function (jarray-ref jarray i))) (error "yet another iteration type - fix it: ~a" (jclass-name (jobject-class thing))))))))) NIL) (defun j2list (thing) "Attempt to construct a Lisp list out of a Java THING. THING may be a wide range of Java collection types, their common iterators or a Java array." (declare (optimize (speed 3) (safety 0))) (flet ((iterator-collect (iterator) (with-constant-signature ((has-next "hasNext") (next "next")) (loop :while (has-next iterator) :collect (next iterator)))) (enumeration-collect (enumeration) (with-constant-signature ((has-next "hasMoreElements") (next "nextElement")) (loop :while (has-next enumeration) :collect (next enumeration)))) (map-collect (map) (with-constant-signature ((has-next "hasMoreElements") (next "nextElement")) (let ((keyiterator (#"iterator" (#"keyset" map)))) (loop :while (has-next keyiterator) :for key = (next keyiterator) :collect (cons key (#"get" map key))))))) (let ((isinstance (load-time-value (jmethod "java.lang.Class" "isInstance" "java.lang.Object")))) (cond ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.AbstractCollection"))) thing) (iterator-collect (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Iterator"))) thing) (iterator-collect thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.lang.Iterable"))) thing) (iterator-collect (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Enumeration"))) thing) (enumeration-collect thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.AbstractMap"))) thing) (map-collect thing)) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Collections"))) thing) (iterator-collect (#"iterator" thing))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Spliterator"))) thing) (iterator-collect (#"iterator" (#"stream" 'StreamSupport thing)))) ((jcall isinstance (load-time-value (ignore-errors (jclass "java.util.Dictionary"))) thing) (iterator-collect (#"elements" thing))) (t (let ((jarray (ignore-errors (or (and (jclass-array-p (jclass-of thing)) thing) (#"toArray" thing))))) (if jarray (loop :for i :from 0 :below (jarray-length jarray) :collect (jarray-ref jarray i)) (error "yet another iteration type - fix it: ~a" (jclass-name (jobject-class thing)))))))))) (defun to-hashset (list) "Convert LIST to the java.util.HashSet contract" (let ((set (new 'java.util.hashset))) (loop for l in list do (#"add" set l)) set))