-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathcollections.lisp
211 lines (191 loc) · 10.1 KB
/
collections.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
(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 <a spliterator>)) -> 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))