001package io.avaje.http.generator.core; 002 003import io.avaje.http.api.BeanParam; 004import io.avaje.http.api.Cookie; 005import io.avaje.http.api.Default; 006import io.avaje.http.api.Form; 007import io.avaje.http.api.FormParam; 008import io.avaje.http.api.Header; 009import io.avaje.http.api.QueryParam; 010import io.avaje.http.generator.core.openapi.MethodDocBuilder; 011import io.avaje.http.generator.core.openapi.MethodParamDocBuilder; 012 013import javax.lang.model.element.Element; 014import javax.lang.model.element.TypeElement; 015 016public class ElementReader { 017 018 private final ProcessingContext ctx; 019 private final Element element; 020 private final String rawType; 021 private final String shortType; 022 private final TypeHandler typeHandler; 023 private final String varName; 024 private final String snakeName; 025 private final boolean formMarker; 026 private final boolean contextType; 027 028 private String paramName; 029 private ParamType paramType; 030 private boolean impliedParamType; 031 private String paramDefault; 032 033 private boolean notNullKotlin; 034 //private boolean notNullJavax; 035 036 ElementReader(Element element, ProcessingContext ctx, ParamType defaultType, boolean formMarker) { 037 this(element, Util.typeDef(element.asType()), ctx, defaultType, formMarker); 038 } 039 040 ElementReader(Element element, String rawType, ProcessingContext ctx, ParamType defaultType, boolean formMarker) { 041 this.ctx = ctx; 042 this.element = element; 043 this.rawType = rawType; 044 this.shortType = Util.shortName(rawType); 045 this.contextType = ctx.platform().isContextType(rawType); 046 this.typeHandler = TypeMap.get(rawType); 047 this.formMarker = formMarker; 048 this.varName = element.getSimpleName().toString(); 049 this.snakeName = Util.snakeCase(varName); 050 this.paramName = varName; 051 if (!contextType) { 052 readAnnotations(element, defaultType); 053 } else { 054 paramType = ParamType.CONTEXT; 055 } 056 } 057 058 private void readAnnotations(Element element, ParamType defaultType) { 059 060 notNullKotlin = (element.getAnnotation(org.jetbrains.annotations.NotNull.class) != null); 061 //notNullJavax = (element.getAnnotation(javax.validation.constraints.NotNull.class) != null); 062 063 Default defaultVal = element.getAnnotation(Default.class); 064 if (defaultVal != null) { 065 this.paramDefault = defaultVal.value(); 066 } 067 Form form = element.getAnnotation(Form.class); 068 if (form != null) { 069 this.paramType = ParamType.FORM; 070 return; 071 } 072 BeanParam beanParam = element.getAnnotation(BeanParam.class); 073 if (beanParam != null) { 074 this.paramType = ParamType.BEANPARAM; 075 return; 076 } 077 QueryParam queryParam = element.getAnnotation(QueryParam.class); 078 if (queryParam != null) { 079 this.paramName = nameFrom(queryParam.value(), varName); 080 this.paramType = ParamType.QUERYPARAM; 081 return; 082 } 083 FormParam formParam = element.getAnnotation(FormParam.class); 084 if (formParam != null) { 085 this.paramName = nameFrom(formParam.value(), varName); 086 this.paramType = ParamType.FORMPARAM; 087 return; 088 } 089 Cookie cookieParam = element.getAnnotation(Cookie.class); 090 if (cookieParam != null) { 091 this.paramName = nameFrom(cookieParam.value(), varName); 092 this.paramType = ParamType.COOKIE; 093 this.paramDefault = null; 094 return; 095 } 096 Header headerParam = element.getAnnotation(Header.class); 097 if (headerParam != null) { 098 this.paramName = nameFrom(headerParam.value(), Util.initcapSnake(snakeName)); 099 this.paramType = ParamType.HEADER; 100 this.paramDefault = null; 101 return; 102 } 103 if (paramType == null) { 104 this.impliedParamType = true; 105 if (typeHandler != null) { 106 // a scalar type that we know how to convert 107 this.paramType = defaultType; 108 } else { 109 this.paramType = formMarker ? ParamType.FORM : ParamType.BODY; 110 } 111 } 112 } 113 114 @Override 115 public String toString() { 116 return varName + " type:" + rawType + " paramType:" + paramType + " dft:" + paramDefault; 117 } 118 119 private String nameFrom(String name, String defaultName) { 120 if (name != null && !name.isEmpty()) { 121 return name; 122 } 123 return defaultName; 124 } 125 126 public String getVarName() { 127 return varName; 128 } 129 130 private boolean hasParamDefault() { 131 return paramDefault != null && !paramDefault.isEmpty(); 132 } 133 134 private boolean isPlatformContext() { 135 return contextType; 136 } 137 138 private String platformVariable() { 139 return ctx.platform().platformVariable(rawType); 140 } 141 142 private String shortType() { 143 if (typeHandler != null) { 144 return typeHandler.shortName(); 145 } else { 146 return shortType; 147 } 148 } 149 150 void addImports(ControllerReader bean) { 151 if (typeHandler != null) { 152 String importType = typeHandler.getImportType(); 153 if (importType != null) { 154 bean.addImportType(rawType); 155 } 156 } else { 157 bean.addImportType(rawType); 158 } 159 } 160 161 void writeParamName(Append writer) { 162 if (isPlatformContext()) { 163 writer.append(platformVariable()); 164 } else { 165 writer.append(varName); 166 } 167 } 168 169 /** 170 * Build the OpenAPI documentation for this parameter. 171 */ 172 void buildApiDocumentation(MethodDocBuilder methodDoc) { 173 if (!isPlatformContext()) { 174 new MethodParamDocBuilder(methodDoc, this).build(); 175 } 176 } 177 178 void writeValidate(Append writer) { 179 if (!isPlatformContext() && typeHandler == null) { 180 writer.append("validator.validate(%s);", varName).eol(); 181 writer.append(" "); 182 } 183 } 184 185 void writeCtxGet(Append writer, PathSegments segments) { 186 if (isPlatformContext()) { 187 // no conversion for this parameter 188 return; 189 } 190 if (paramType == ParamType.BODY && ctx.platform().isBodyMethodParam()) { 191 // body passed as method parameter (Helidon) 192 return; 193 } 194 String shortType = shortType(); 195 writer.append("%s %s %s = ", ctx.platform().indent(), shortType, varName); 196 if (setValue(writer, segments, shortType)) { 197 writer.append(";").eol(); 198 } 199 } 200 201 void setValue(Append writer) { 202 setValue(writer, PathSegments.EMPTY, shortType()); 203 } 204 205 private boolean setValue(Append writer, PathSegments segments, String shortType) { 206// if (formMarker && impliedParamType && typeHandler == null) { 207// if (ParamType.FORM != paramType) { 208// throw new IllegalStateException("Don't get here?"); 209// } 210//// // @Form on method and this type is a "bean" so treat is as a form bean 211//// writeForm(writer, shortType, varName, ParamType.FORMPARAM); 212//// paramType = ParamType.FORM; 213//// return false; 214// } 215 if (ParamType.FORM == paramType) { 216 writeForm(writer, shortType, varName, ParamType.FORMPARAM); 217 return false; 218 } 219 if (ParamType.BEANPARAM == paramType) { 220 writeForm(writer, shortType, varName, ParamType.QUERYPARAM); 221 return false; 222 } 223 if (impliedParamType) { 224 PathSegments.Segment segment = segments.segment(varName); 225 if (segment != null) { 226 // path or matrix parameter 227 boolean requiredParam = segment.isRequired(varName); 228 String asMethod = (typeHandler == null) ? null : (requiredParam) ? typeHandler.asMethod() : typeHandler.toMethod(); 229 if (asMethod != null) { 230 writer.append(asMethod); 231 } 232 segment.writeGetVal(writer, varName, ctx.platform()); 233 if (asMethod != null) { 234 writer.append(")"); 235 } 236 paramType = ParamType.PATHPARAM; 237 return true; 238 } 239 } 240 241 String asMethod = (typeHandler == null) ? null : typeHandler.toMethod(); 242 if (asMethod != null) { 243 writer.append(asMethod); 244 } 245 246 if (typeHandler == null) { 247 // this is a body (POST, PATCH) 248 writer.append(ctx.platform().bodyAsClass(shortType)); 249 250 } else { 251 if (hasParamDefault()) { 252 ctx.platform().writeReadParameter(writer, paramType, paramName, paramDefault); 253 } else { 254 boolean checkNull = notNullKotlin || (paramType == ParamType.FORMPARAM && typeHandler.isPrimitive()); 255 if (checkNull) { 256 writer.append("checkNull("); 257 } 258 ctx.platform().writeReadParameter(writer, paramType, paramName); 259 //writer.append("ctx.%s(\"%s\")", paramType, paramName); 260 if (checkNull) { 261 writer.append(", \"%s\")", paramName); 262 } 263 } 264 } 265 266 if (asMethod != null) { 267 writer.append(")"); 268 } 269 return true; 270 } 271 272 private void writeForm(Append writer, String shortType, String varName, ParamType defaultParamType) { 273 TypeElement formBeanType = ctx.getTypeElement(rawType); 274 BeanParamReader form = new BeanParamReader(ctx, formBeanType, varName, shortType, defaultParamType); 275 form.write(writer); 276 } 277 278 public ParamType getParamType() { 279 return paramType; 280 } 281 282 public String getParamName() { 283 return paramName; 284 } 285 286 public String getShortType() { 287 return shortType; 288 } 289 290 public String getRawType() { 291 return rawType; 292 } 293 294 public Element getElement() { 295 return element; 296 } 297}