|
| 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