Skip to content

Commit 34e381e

Browse files
committed
Add constructor support to jdoc, jdoc-data, and sigs
1 parent 9ac3758 commit 34e381e

File tree

3 files changed

+134
-16
lines changed

3 files changed

+134
-16
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ Returns all the structured data instead of printing the description.
9494
;; => {:classname "java.lang.String"
9595
;; :class-description-html "..."
9696
;; :class-description-md "..."
97+
;; :constructors [{:signature "String()"
98+
;; :description "Initializes a newly created String..."
99+
;; :clojure-call "String/new"}
100+
;; ...]
97101
;; :methods [{:signature "valueOf(int i)"
98102
;; :description "Returns the string representation..."
99103
;; :static? true
@@ -105,6 +109,7 @@ Returns all the structured data instead of printing the description.
105109
;; => {:classname "java.lang.String"
106110
;; :class-description-html "..."
107111
;; :class-description-md "..."
112+
;; :constructors [...]
108113
;; :methods [...]
109114
;; :selected-method [{:signature "valueOf(char[] data)"
110115
;; :description "Returns the string representation..."
@@ -113,6 +118,19 @@ Returns all the structured data instead of printing the description.
113118
;; :clojure-call "^[char/1] String/valueOf"
114119
;; :method-description-html "..."
115120
;; :method-description-md "..."}]}
121+
122+
(jdoc-data String/new)
123+
;; => {:classname "java.lang.String"
124+
;; :class-description-html "..."
125+
;; :class-description-md "..."
126+
;; :constructors [...]
127+
;; :methods [...]
128+
;; :selected-constructor [{:signature "String()"
129+
;; :description "Initializes a newly created String..."
130+
;; :clojure-call "String/.new"
131+
;; :constructor-description-html "..."
132+
;; :constructor-description-md "..."}
133+
;; ...]}
116134
```
117135

118136
## Requirements

src/clojure/java/doc/impl.clj

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@
141141
:method-description-md (html-to-md method-html)))
142142
method)))
143143

144+
(defn- get-constructor-detail [^org.jsoup.nodes.Document doc constructor]
145+
(let [param-types (extract-params (:signature constructor))
146+
detail-section (find-method-section doc "<init>" param-types)]
147+
(if detail-section
148+
(let [html (.outerHtml ^org.jsoup.nodes.Element detail-section)]
149+
(assoc constructor
150+
:constructor-description-html html
151+
:constructor-description-md (html-to-md html)))
152+
constructor)))
153+
144154
(defn- expand-array-syntax
145155
"expands array syntax for matching javadoc format: String/2 -> String[][]"
146156
[type-str]
@@ -233,6 +243,11 @@
233243
(str "^[" (str/join " " clojure-types) "] " class-part separator method-name))
234244
(str class-part separator method-name))))
235245

246+
(defn- clojure-constructor-call-syntax [class-part constructor-signature]
247+
(let [param-types (extract-params constructor-signature)
248+
prefix (when param-types (str "^[" (str/join " " (mapv compress-array-syntax param-types)) "] "))]
249+
(str prefix class-part "/.new")))
250+
236251
(defn parse-javadoc
237252
"Parse the javadoc HTML for a class or method into a data structure:
238253
{:classname 'java.lang.String'
@@ -263,35 +278,50 @@
263278
:static? is-static?
264279
:return-type (clojure-return-type modifier-text)
265280
:clojure-call (clojure-call-syntax class-part signature is-static?)})))
281+
constructor-rows (.select ^org.jsoup.nodes.Document doc "div.col-constructor-name")
282+
all-constructors (vec (for [^org.jsoup.nodes.Element ctor-div constructor-rows]
283+
(let [desc-div ^org.jsoup.nodes.Element (.nextElementSibling ctor-div)
284+
signature (.text (.select ctor-div "code"))]
285+
{:signature signature
286+
:description (.text (.select desc-div ".block"))
287+
:clojure-call (clojure-constructor-call-syntax class-part signature)})))
266288
class-html (when class-desc-section (.outerHtml ^org.jsoup.nodes.Element class-desc-section))
267289
result {:classname class-name
268290
:class-description-html class-html
269291
:class-description-md (when class-html (html-to-md class-html))
270-
:methods all-methods}]
271-
(if method-part
272-
(let [filtered (filter-methods all-methods method-part param-tags)
273-
declaring-class (when (empty? filtered)
274-
(find-declaring-class class-name method-part param-tags))]
275-
(assoc result :selected-method
276-
(cond
277-
(seq filtered) (mapv #(get-method-detail doc %) filtered)
278-
declaring-class (:selected-method (parse-javadoc (str declaring-class "/." method-part) param-tags))
279-
:else [])))
280-
result)))
292+
:methods all-methods
293+
:constructors all-constructors}]
294+
(cond
295+
(nil? method-part) result
296+
(= method-part "new") (let [filtered (if param-tags
297+
(filterv #(params-match? (extract-params (:signature %)) param-tags) all-constructors)
298+
all-constructors)]
299+
(assoc result :selected-constructor (mapv #(get-constructor-detail doc %) filtered)))
300+
:else (let [filtered (filter-methods all-methods method-part param-tags)
301+
declaring-class (when (empty? filtered) (find-declaring-class class-name method-part param-tags))]
302+
(assoc result :selected-method
303+
(cond
304+
(seq filtered) (mapv #(get-method-detail doc %) filtered)
305+
declaring-class (:selected-method (parse-javadoc (str declaring-class "/." method-part) param-tags))
306+
:else []))))))
281307

282-
(defn print-javadoc [{:keys [classname class-description-md selected-method]}]
308+
(defn print-javadoc [{:keys [classname class-description-md selected-method selected-constructor]}]
283309
(let [condense-lines (fn [s] (str/replace s #"\n{3,}" "\n\n"))]
284310
(cond
311+
selected-constructor (doseq [{:keys [constructor-description-md]} selected-constructor]
312+
(if constructor-description-md
313+
(println (condense-lines constructor-description-md))
314+
(println "No javadoc description available for this constructor.")))
285315
selected-method (doseq [{:keys [method-description-md]} selected-method]
286316
(if method-description-md
287317
(println (condense-lines method-description-md))
288318
(println "No javadoc description available for this method.")))
289319
class-description-md (println (condense-lines class-description-md))
290320
:else (println (str "No javadoc description available for class: " classname)))))
291321

292-
(defn print-signatures [{:keys [classname methods selected-method]}]
293-
(let [methods-to-print (or selected-method methods)]
294-
(if (seq methods-to-print)
295-
(doseq [{:keys [clojure-call]} methods-to-print]
322+
(defn print-signatures [{:keys [classname methods constructors selected-method selected-constructor]}]
323+
(let [items-to-print (or selected-method selected-constructor (concat constructors methods))]
324+
(if (seq items-to-print)
325+
(doseq [{:keys [clojure-call]} items-to-print]
296326
(println clojure-call))
297327
(println (str "No method signatures available for: " classname)))))

test/clojure/java/doc/impl_test.clj

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,76 @@
444444
(testing "nonexistent method returns nil"
445445
(is (nil? (#'sut/find-declaring-class "java.lang.String" "totallyFakeMethod" nil)))))
446446

447+
(deftest clojure-constructor-call-syntax-test
448+
449+
(testing "constructor with single parameter"
450+
(is (= "^[Reader] PushbackReader/.new"
451+
(#'sut/clojure-constructor-call-syntax "PushbackReader" "PushbackReader(Reader in)"))))
452+
453+
(testing "constructor with multiple parameters"
454+
(is (= "^[Reader int] PushbackReader/.new"
455+
(#'sut/clojure-constructor-call-syntax "PushbackReader" "PushbackReader(Reader in, int size)"))))
456+
457+
(testing "constructor with no parameters"
458+
(is (= "StringBuilder/.new"
459+
(#'sut/clojure-constructor-call-syntax "StringBuilder" "StringBuilder()"))))
460+
461+
(testing "constructor with array parameter"
462+
(is (= "^[char/1] String/.new"
463+
(#'sut/clojure-constructor-call-syntax "String" "String(char[] value)")))))
464+
465+
(deftest get-constructor-detail-test
466+
467+
(testing "finds constructor detail by <init> section ID"
468+
(let [html "<html><body>
469+
<section id='&lt;init&gt;(java.io.Reader,int)'>
470+
<h3>PushbackReader</h3>
471+
<div class='member-signature'>
472+
<span>public PushbackReader(Reader in, int size)</span>
473+
</div>
474+
<div class='block'>Creates a new pushback reader with a pushback buffer of the given size.</div>
475+
</section>
476+
</body></html>"
477+
doc (Jsoup/parse html)
478+
constructor {:signature "PushbackReader(Reader in, int size)" :description "test"}
479+
actual (#'sut/get-constructor-detail doc constructor)
480+
expected-html "<section id=\"&lt;init&gt;(java.io.Reader,int)\">\n <h3>PushbackReader</h3>\n <div class=\"member-signature\">\n <span>public PushbackReader(Reader in, int size)</span>\n </div>\n <div class=\"block\">\n Creates a new pushback reader with a pushback buffer of the given size.\n </div>\n</section>"
481+
expected-md "### PushbackReader {#<init>(java.io.Reader,int)}\n\npublic PushbackReader(Reader in, int size) \nCreates a new pushback reader with a pushback buffer of the given size.\n"]
482+
(is (= expected-html (:constructor-description-html actual)))
483+
(is (= expected-md (:constructor-description-md actual)))
484+
(is (= "PushbackReader(Reader in, int size)" (:signature actual)))))
485+
486+
(testing "returns original constructor when detail not found"
487+
(let [html "<html><body>
488+
<section id='&lt;init&gt;(java.io.Reader)'>
489+
<h3>PushbackReader</h3>
490+
</section>
491+
</body></html>"
492+
doc (Jsoup/parse html)
493+
constructor {:signature "PushbackReader(String s)" :description "test"}
494+
actual (#'sut/get-constructor-detail doc constructor)]
495+
(is (= constructor actual)))))
496+
497+
(testing "finds constructor section with <init> ID"
498+
(let [html "<html><body>
499+
<section id='&lt;init&gt;(java.io.Reader)'>
500+
<h3>PushbackReader</h3>
501+
</section>
502+
<section id='&lt;init&gt;(java.io.Reader,int)'>
503+
<h3>PushbackReader</h3>
504+
</section>
505+
</body></html>"
506+
doc (Jsoup/parse html)
507+
section-1 (#'sut/find-method-section doc "<init>" ["Reader"])
508+
section-2 (#'sut/find-method-section doc "<init>" ["Reader" "int"])]
509+
(is (= "<init>(java.io.Reader)" (.attr section-1 "id")))
510+
(is (= "<init>(java.io.Reader,int)" (.attr section-2 "id")))))
511+
512+
(testing "parses <init> constructor IDs"
513+
(is (= ["java.io.Reader" "int"] (#'sut/extract-id-params "<init>(java.io.Reader,int)" "<init>")))
514+
(is (= ["java.io.Reader"] (#'sut/extract-id-params "<init>(java.io.Reader)" "<init>")))
515+
(is (= [] (#'sut/extract-id-params "<init>()" "<init>"))))
516+
447517
(testing "distinguishes between overloads with different param counts"
448518
(let [html "<html><body>
449519
<section id='run(java.util.Map)'>

0 commit comments

Comments
 (0)