|
| 1 | +(ns aidbox-sdk.generator.java |
| 2 | + (:require |
| 3 | + [aidbox-sdk.generator.helpers :refer [->pascal-case uppercase-first-letter]] |
| 4 | + [aidbox-sdk.generator.utils :as u] |
| 5 | + [clojure.java.io :as io] |
| 6 | + [clojure.string :as str]) |
| 7 | + (:import |
| 8 | + [aidbox_sdk.generator CodeGenerator])) |
| 9 | + |
| 10 | +(defn datatypes-file-path [] |
| 11 | + (io/file "datatypes.java")) |
| 12 | + |
| 13 | +(defn package->directory |
| 14 | + "Generates directory name from package name. |
| 15 | +
|
| 16 | + Example: |
| 17 | + hl7.fhir.r4.core -> hl7-fhir-r4-core" |
| 18 | + [x] |
| 19 | + (str/replace x #"[\.#]" "-")) |
| 20 | + |
| 21 | +(defn url->resource-name [reference] |
| 22 | + (last (str/split (str reference) #"/"))) |
| 23 | + |
| 24 | +(defn class-name |
| 25 | + "Generate class name from schema url." |
| 26 | + [url] |
| 27 | + (uppercase-first-letter (url->resource-name url))) |
| 28 | + |
| 29 | +(defn resource-file-path [ir-schema] |
| 30 | + (io/file (package->directory (:package ir-schema)) |
| 31 | + (str (->pascal-case (:name ir-schema)) ".java"))) |
| 32 | + |
| 33 | +(defn ->lang-type [fhir-type] |
| 34 | + (case fhir-type |
| 35 | + ;; Primitive Types |
| 36 | + "boolean" "boolean" |
| 37 | + "instant" "String" |
| 38 | + "time" "String" |
| 39 | + "date" "String" |
| 40 | + "dateTime" "String" |
| 41 | + "decimal" "float" |
| 42 | + |
| 43 | + "integer" "int" |
| 44 | + "unsignedInt" "int" |
| 45 | + "positiveInt" "int" |
| 46 | + |
| 47 | + "integer64" "int" |
| 48 | + "base64Binary" "String" |
| 49 | + |
| 50 | + "uri" "String" |
| 51 | + "url" "String" |
| 52 | + "canonical" "String" |
| 53 | + "oid" "String" |
| 54 | + "uuid" "String" |
| 55 | + |
| 56 | + "string" "String" |
| 57 | + "code" "String" |
| 58 | + "markdown" "String" |
| 59 | + "id" "String" |
| 60 | + |
| 61 | + ;; hardcoded just in case |
| 62 | + "Meta" "Meta" |
| 63 | + ;; else |
| 64 | + fhir-type)) |
| 65 | + |
| 66 | +(defn generate-accessor [{:keys [name array required type base]}] |
| 67 | + (let [lang-type (->lang-type type)] |
| 68 | + (str |
| 69 | + (str "public " lang-type " get" (uppercase-first-letter name) "() {\n" |
| 70 | + u/indent "return " name ";" |
| 71 | + "\n}") |
| 72 | + "\n\n" |
| 73 | + (str "public void set" (uppercase-first-letter name) "(" lang-type " " name ") {\n" |
| 74 | + "this." name " = " name ";" |
| 75 | + "\n}")))) |
| 76 | + |
| 77 | +(defn generate-property [{:keys [name array required type base]}] |
| 78 | + (let [lang-type (->lang-type type) |
| 79 | + type (if array |
| 80 | + (format "List<%s>" lang-type) |
| 81 | + lang-type)] |
| 82 | + (str "private " type " " name ";"))) |
| 83 | + |
| 84 | +(defn generate-class [ir-schema & [inner-classes]] |
| 85 | + (let [base-class (url->resource-name (:base ir-schema)) |
| 86 | + schema-name (or (:url ir-schema) (:name ir-schema)) |
| 87 | + class-name' (class-name schema-name) |
| 88 | + properties (->> (:elements ir-schema) |
| 89 | + (map generate-property) |
| 90 | + (remove nil?) |
| 91 | + (map u/add-indent) |
| 92 | + (str/join "\n")) |
| 93 | + |
| 94 | + accessors (->> (:elements ir-schema) |
| 95 | + (map generate-accessor) |
| 96 | + (remove nil?) |
| 97 | + (map u/add-indent) |
| 98 | + (str/join "\n"))] |
| 99 | + (str "public class " class-name' " extends " base-class " {\n" |
| 100 | + (when (and inner-classes |
| 101 | + (seq inner-classes)) |
| 102 | + "\n") |
| 103 | + (str/join "\n\n" (map #(->> % str/split-lines (map u/add-indent) (str/join "\n")) inner-classes)) |
| 104 | + (when (and inner-classes |
| 105 | + (seq inner-classes)) |
| 106 | + "\n") |
| 107 | + |
| 108 | + properties |
| 109 | + "\n" |
| 110 | + accessors |
| 111 | + "\n}"))) |
| 112 | + |
| 113 | +(defn generate-deps [deps] |
| 114 | + (->> deps |
| 115 | + (map (fn [{:keys [module members]}] |
| 116 | + (if (seq members) |
| 117 | + (str "import { " (str/join ", " members) " } from '" module "';") |
| 118 | + (str "import " module ";")))) |
| 119 | + (str/join "\n"))) |
| 120 | + |
| 121 | +(defn generate-module |
| 122 | + [& {name' :name |
| 123 | + :keys [deps classes interfaces structs enums delegates] |
| 124 | + :or {classes [] |
| 125 | + interfaces [] |
| 126 | + structs [] |
| 127 | + enums [] |
| 128 | + delegates []}}] |
| 129 | + (->> (conj [] |
| 130 | + (str "package " name' ";") |
| 131 | + (generate-deps deps) |
| 132 | + classes) |
| 133 | + (flatten) |
| 134 | + (str/join "\n\n"))) |
| 135 | + |
| 136 | +(defn generate-backbone-classes |
| 137 | + "Generates classes from schema's backbone elements." |
| 138 | + [ir-schema] |
| 139 | + (->> (ir-schema :backbone-elements) |
| 140 | + (map #(assoc % :base "BackboneElement")) |
| 141 | + (map generate-class))) |
| 142 | + |
| 143 | +(defrecord JavaCodeGenerator [] |
| 144 | + CodeGenerator |
| 145 | + (generate-datatypes [_ ir-schemas] |
| 146 | + [{:path (datatypes-file-path) |
| 147 | + :content (generate-module |
| 148 | + :deps [] |
| 149 | + :classes (map (fn [ir-schema] |
| 150 | + (generate-class ir-schema |
| 151 | + (generate-backbone-classes ir-schema))) |
| 152 | + ir-schemas))}]) |
| 153 | + (generate-resource-module [_ ir-schema] |
| 154 | + {:path (resource-file-path ir-schema) |
| 155 | + :content (generate-module |
| 156 | + {:name "aidbox.fhir.r4" |
| 157 | + :deps [{:module "aidbox.fhir.datatypes.*" :members []} |
| 158 | + {:module "java.util.List" :members []}] |
| 159 | + :classes [(generate-class ir-schema |
| 160 | + (generate-backbone-classes ir-schema))]})}) |
| 161 | + (generate-search-params [_ ir-schemas] []) |
| 162 | + (generate-constraints [_ ir-schemas] []) |
| 163 | + (generate-sdk-files [this] [])) |
| 164 | + |
| 165 | +(def generator (->JavaCodeGenerator)) |
0 commit comments