(require "CLOS") (require "JAVA") (require "EXTENSIBLE-SEQUENCES") (require "PRINT-OBJECT") (in-package :java) (let* ((jclass (jclass "java.util.List")) (class (%find-java-class jclass))) (if class (error "java.util.List is already registered as a Lisp class; since JAVA-CLASSes can't be redefined, I can't inject SEQUENCE in its class precedence list. Ensure that you require :java-collections before specializing any method on java.util.List and in general before using java.util.List as a CLOS class.") ;;The code below is adapted from ensure-java-class in java.lisp (%register-java-class jclass (mop::ensure-class (make-symbol (jclass-name jclass)) :metaclass (find-class 'java-class) :direct-superclasses (let ((supers (mapcar #'ensure-java-class (delete nil (concatenate 'list (list (jclass-superclass jclass)) (jclass-interfaces jclass)))))) (append supers (list (find-class 'sequence)) (jclass-additional-superclasses jclass))) :java-class jclass)))) (defmethod print-object ((coll (jclass "java.util.Collection")) stream) (print-unreadable-object (coll stream :type t :identity t) (format stream "~A ~A" (jclass-of coll) (jcall "toString" coll)))) ;;Lists (java.util.List) are the Java counterpart to Lisp SEQUENCEs. (defun jlist-add (list item) (jcall (jmethod "java.util.List" "add" "java.lang.Object") list item)) (defun jlist-set (list index item) (jcall (jmethod "java.util.List" "set" "int" "java.lang.Object") list index item)) (defun jlist-get (list index) (jcall (jmethod "java.util.List" "get" "int") list index)) (defmethod sequence:length ((s (jclass "java.util.List"))) (jcall (jmethod "java.util.Collection" "size") s)) (defmethod sequence:elt ((s (jclass "java.util.List")) index) (jlist-get s index)) (defmethod (setf sequence:elt) (value (list (jclass "java.util.List")) index) (jlist-set list index value) value) (defmethod sequence:make-sequence-like ((s (jclass "java.util.List")) length &rest args &key initial-element initial-contents) (declare (ignorable initial-element initial-contents)) (apply #'make-jsequence-like s length #'jlist-add args)) (defun make-jsequence-like (s length add-fn &key (initial-element nil iep) (initial-contents nil icp)) (let ((seq (jnew (jclass-of s)))) (cond ((and icp iep) (error "Can't specify both :initial-element and :initial-contents")) (icp (dotimes (i length) (funcall add-fn seq (elt initial-contents i)))) ;;TODO inefficient, use iterator (t (dotimes (i length) (funcall add-fn seq initial-element)))) seq)) ;;TODO: destruct doesn't signal an error for too-many-args for its options ;;e.g. this didn't complain: ;;(defstruct (jlist-iterator (:type list :conc-name #:jlist-it-)) (defstruct (jlist-iterator (:type list) (:conc-name #:jlist-it-)) (native-iterator (error "Native iterator required") :read-only t) element index) (defmethod sequence:make-simple-sequence-iterator ((s (jclass "java.util.List")) &key from-end (start 0) end) (let* ((end (or end (length s))) (index (if from-end end start)) (it (jcall "listIterator" s index)) (iter (make-jlist-iterator :native-iterator it :index (if from-end (1+ index) (1- index)))) (limit (if from-end (1+ start) (1- end)))) ;;CL iterator semantics are that first element is present from the start (unless (sequence:iterator-endp s iter limit from-end) (sequence:iterator-step s iter from-end)) (values iter limit from-end))) ;;Collection, and not List, because we want to reuse this for Set when applicable (defmethod sequence:iterator-step ((s (jclass "java.util.Collection")) it from-end) (let ((native-it (jlist-it-native-iterator it))) (if from-end (progn (setf (jlist-it-element it) (when (jcall "hasPrevious" native-it) (jcall "previous" native-it))) (decf (jlist-it-index it))) (progn (setf (jlist-it-element it) (when (jcall "hasNext" native-it) (jcall "next" native-it))) (incf (jlist-it-index it))))) it) (defmethod sequence:iterator-endp ((s (jclass "java.util.Collection")) it limit from-end) (if from-end (< (jlist-it-index it) limit) (> (jlist-it-index it) limit))) (defmethod sequence:iterator-element ((s (jclass "java.util.Collection")) iterator) (declare (ignore s)) (jlist-it-element iterator)) (defmethod (setf sequence:iterator-element) (new-value (s (jclass "java.util.Collection")) it) (jcall "set" (jlist-it-native-iterator it) new-value)) (defmethod sequence:iterator-index ((s (jclass "java.util.Collection")) iterator) (declare (ignore s)) (jlist-it-index iterator)) (defmethod sequence:iterator-copy ((s (jclass "java.util.Collection")) iterator) (declare (ignore s iterator)) (error "iterator-copy not supported for Java iterators.")) ;;It makes sense to have some sequence functions available for Sets ;;(java.util.Set) too, even if they're not sequences. (defun jset-add (set item) (jcall (jmethod "java.util.Set" "add" "java.lang.Object") set item)) (defmethod sequence:length ((s (jclass "java.util.Set"))) (jcall (jmethod "java.util.Collection" "size") s)) (defmethod sequence:make-sequence-like ((s (jclass "java.util.Set")) length &rest args &key initial-element initial-contents) (declare (ignorable initial-element initial-contents)) (apply #'make-jsequence-like s length #'jset-add args)) (defmethod sequence:make-simple-sequence-iterator ((s (jclass "java.util.Set")) &key from-end (start 0) end) (when (or from-end (not (= start 0))) (error "Java Sets can only be iterated from the start.")) (let* ((end (or end (length s))) (it (jcall "iterator" s)) (iter (make-jlist-iterator :native-iterator it :index -1)) (limit (1- end))) ;;CL iterator semantics are that first element is present from the start (unless (sequence:iterator-endp s iter limit nil) (sequence:iterator-step s iter nil)) (values iter limit nil))) (provide :java-collections)