Skip to content

Commit a22e916

Browse files
committed
Code spike for retrieving javadocs from source files
A spike to experiment with retrieving (building) javadocs from source files. The advantage to this approach is it would support documentation from 3rd party libraries. The challenge is all of this needs to be built manually the javadocs do all of the assembling of information @inheritdoc, methods, parent, sibling, children navigation, etc. It is possible but becomes very difficult to read and reason about. If the JDK html javadoc retrieval becomes too limiting this is a valid alternative path.
0 parents  commit a22e916

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

src/java_javadocs/source_spike.clj

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
(ns java-javadocs.source-spike)
2+
; (:require [clojure.string :as str]
3+
; [clojure.tools.deps :as deps]
4+
; [clojure.java.io :as io])
5+
; (:import [java.util.jar JarFile]
6+
; [com.github.javaparser StaticJavaParser ParserConfiguration ParserConfiguration$LanguageLevel]
7+
; [com.github.javaparser.ast CompilationUnit]
8+
; [com.github.javaparser.ast.body ClassOrInterfaceDeclaration MethodDeclaration]
9+
; [com.github.javaparser.ast.nodeTypes NodeWithJavadoc]
10+
; [com.github.javaparser.javadoc Javadoc]
11+
; [com.vladsch.flexmark.html2md.converter FlexmarkHtmlConverter]))
12+
13+
; (set! *warn-on-reflection* true)
14+
15+
; (defn- find-jar-coords [jar-url-str]
16+
; (let [libs (:libs (deps/create-basis {:aliases []}))]
17+
; (first (for [[lib-sym lib-info] libs
18+
; path (:paths lib-info)
19+
; :when (str/includes? jar-url-str path)]
20+
; {:protocol :jar
21+
; :lib lib-sym
22+
; :version (select-keys lib-info [:mvn/version])}))))
23+
24+
; (defn- find-javadoc-coords [^Class c]
25+
; (let [class-name (.getName c)
26+
; url (.getResource c (str (.getSimpleName c) ".class"))]
27+
; (merge
28+
; {:class-name class-name}
29+
; (case (.getProtocol url)
30+
; "jar" (find-jar-coords (.toString url))
31+
; "jrt" {:protocol :jrt
32+
; :lib 'java/java
33+
; :version {:mvn/version (System/getProperty "java.version")}}
34+
; "file" nil))))
35+
36+
; (defn- find-source-jar-path [{:keys [lib version]}]
37+
; (let [group-path (str/replace (namespace lib) "." "/")
38+
; artifact (name lib)
39+
; version-str (:mvn/version version)
40+
; jar-name (str artifact "-" version-str "-sources.jar")
41+
; home (System/getProperty "user.home")]
42+
; (str home "/.m2/repository/" group-path "/" artifact "/" version-str "/" jar-name)))
43+
44+
; (defn- download-source-jar [{:keys [lib version] :as coords}]
45+
; (let [source-jar-path (find-source-jar-path coords)]
46+
; (when-not (.exists (io/file source-jar-path))
47+
; (deps/resolve-deps {:deps {(symbol (str lib "$sources")) version}} nil))
48+
; source-jar-path))
49+
50+
; (defn- source-path [class-name]
51+
; (str (str/replace class-name "." "/") ".java"))
52+
53+
; (defn- extract-source-from-jar [jar-path class-name]
54+
; (with-open [jar (JarFile. ^String jar-path)]
55+
; (with-open [is (.getInputStream jar (.getJarEntry jar (source-path class-name)))]
56+
; (slurp is))))
57+
58+
; (defn- extract-source-from-jrt [class-name]
59+
; (let [src-zip (str (System/getProperty "java.home") "/lib/src.zip")
60+
; entry-path (str "java.base/" (source-path class-name))]
61+
; (with-open [jar (JarFile. ^String src-zip)]
62+
; (with-open [is (.getInputStream jar (.getJarEntry jar entry-path))]
63+
; (slurp is)))))
64+
65+
; (def highest-java-language-level
66+
; (let [levels (ParserConfiguration$LanguageLevel/values)
67+
; sorted (sort-by #(.ordinal ^ParserConfiguration$LanguageLevel %) > levels)]
68+
; (first sorted)))
69+
70+
; (defn- parse-java-source [^String source-code]
71+
; (let [config (.setLanguageLevel (ParserConfiguration.) highest-java-language-level)]
72+
; (StaticJavaParser/setConfiguration config)
73+
; (StaticJavaParser/parse source-code)))
74+
75+
; (defn- find-class-declaration [^CompilationUnit cu class-name]
76+
; (let [simple-name (last (str/split class-name #"\."))]
77+
; (first (filter #(= simple-name (.getNameAsString ^ClassOrInterfaceDeclaration %))
78+
; (.findAll cu ClassOrInterfaceDeclaration)))))
79+
80+
; (defn- find-method [^ClassOrInterfaceDeclaration class-decl method-name]
81+
; (first (filter #(= method-name (.getNameAsString ^MethodDeclaration %))
82+
; (.getMethods class-decl))))
83+
84+
; (defn- extract-javadoc [^NodeWithJavadoc node]
85+
; (when-let [javadoc-opt (.getJavadoc node)]
86+
; (when (.isPresent javadoc-opt)
87+
; (.toText ^Javadoc (.get javadoc-opt)))))
88+
89+
; (defn- first-sentence [text]
90+
; (when text
91+
; (first (str/split text #"\.\s"))))
92+
93+
; (defn- inherits-javadoc? [text]
94+
; (and text (str/includes? text "{@inheritDoc}")))
95+
96+
; (defn- build-import-map [^CompilationUnit cu]
97+
; (let [imports (.getImports cu)]
98+
; (reduce (fn [acc ^com.github.javaparser.ast.ImportDeclaration import-decl]
99+
; (let [import-name (.getNameAsString import-decl)]
100+
; (if (.isAsterisk import-decl)
101+
; acc
102+
; (let [simple-name (last (str/split import-name #"\."))]
103+
; (assoc acc simple-name import-name)))))
104+
; {}
105+
; imports)))
106+
107+
; (defn- resolve-type-name [import-map package-name simple-name]
108+
; (cond
109+
; (str/includes? simple-name ".") simple-name
110+
; (get import-map simple-name) (get import-map simple-name)
111+
; package-name (str package-name "." simple-name)
112+
; :else (str "java.lang." simple-name)))
113+
114+
; (defn- get-parent-types [^CompilationUnit cu ^ClassOrInterfaceDeclaration class-decl]
115+
; (let [import-map (build-import-map cu)
116+
; package-name (when-let [pkg (.orElse ^java.util.Optional (.getPackageDeclaration cu) nil)]
117+
; (.getNameAsString pkg))
118+
; extended (.getExtendedTypes class-decl)
119+
; implemented (.getImplementedTypes class-decl)
120+
; all-parents (concat extended implemented)]
121+
; (map (fn [^com.github.javaparser.ast.type.ClassOrInterfaceType parent]
122+
; (let [name (.getNameAsString parent)
123+
; base-name (first (str/split name #"<"))]
124+
; (resolve-type-name import-map package-name base-name)))
125+
; all-parents)))
126+
127+
; (defn- print-method-summary [^ClassOrInterfaceDeclaration class-decl]
128+
; (let [methods (.getMethods class-decl)
129+
; public-methods (filter #(.isPublic ^MethodDeclaration %) methods)]
130+
; (doseq [^MethodDeclaration method public-methods]
131+
; (let [method-name (.getNameAsString method)
132+
; javadoc-text (extract-javadoc method)
133+
; summary (first-sentence javadoc-text)]
134+
; (when summary
135+
; (println (str "* " method-name " - " summary)))))))
136+
137+
; (defn- handle-error [^Exception e ^Class class]
138+
; (if (str/includes? (.getMessage e) "Could not find artifact")
139+
; (println "No source JAR available for:" class)
140+
; (println "Error:" (.getMessage e))))
141+
142+
; (defn- print-member-javadoc [cu class-decl member-name]
143+
; (let [javadoc-text (extract-javadoc (find-method class-decl member-name))]
144+
; (if (inherits-javadoc? javadoc-text)
145+
; (let [parents (get-parent-types cu class-decl)]
146+
; (println "Inherited from parent. See:")
147+
; (doseq [parent parents]
148+
; (println (str " " parent "/" member-name))))
149+
; (println (.convert (.build (FlexmarkHtmlConverter/builder)) ^String javadoc-text)))))
150+
151+
; (defn- print-class-javadoc [class-decl]
152+
; (when-let [class-javadoc (extract-javadoc class-decl)]
153+
; (println (.convert (.build (FlexmarkHtmlConverter/builder)) ^String class-javadoc))
154+
; (println "\n--- Methods ---\n"))
155+
; (print-method-summary class-decl))
156+
157+
; (defn javadoc* [^Class class member-name]
158+
; (try
159+
; (let [coords (find-javadoc-coords class)
160+
; class-name (:class-name coords)
161+
; source-code (case (:protocol coords)
162+
; :jrt (extract-source-from-jrt class-name)
163+
; :jar (extract-source-from-jar (download-source-jar coords) class-name))
164+
; cu (parse-java-source source-code)
165+
; class-decl (find-class-declaration cu class-name)]
166+
; (if member-name
167+
; (print-member-javadoc cu class-decl member-name)
168+
; (print-class-javadoc class-decl)))
169+
; (catch Exception e
170+
; (handle-error e class))))
171+
172+
; (defmacro javadoc
173+
; "Get javadoc for a class or class member.
174+
; Usage:
175+
; (javadoc String) ; class javadoc
176+
; (javadoc String/valueOf) ; method javadoc
177+
; (javadoc java.lang.String) ; fully qualified class
178+
; (javadoc java.lang.String/valueOf) ; fully qualified with method
179+
; (javadoc StringUtils/isEmpty) ; 3rd party class method"
180+
; [class-or-member]
181+
; (let [class-or-member-str (str class-or-member)
182+
; parts (str/split class-or-member-str #"/")
183+
; class-sym (symbol (first parts))
184+
; member-name (second parts)]
185+
; `(javadoc* ~class-sym ~member-name)))

0 commit comments

Comments
 (0)