io.inverno.mod.web.compiler.internal.WebServerControllerConfigurerOpenApiGenerationContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of inverno-web-compiler Show documentation
Show all versions of inverno-web-compiler Show documentation
Inverno web compiler module providing an Inverno compiler plugin to aggregate module's web routes and web router configurers into a single web router configurer
/*
* Copyright 2021 Jeremy KUHN
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.inverno.mod.web.compiler.internal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.type.UnionType;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import com.sun.source.doctree.AttributeTree;
import com.sun.source.doctree.AuthorTree;
import com.sun.source.doctree.CommentTree;
import com.sun.source.doctree.DeprecatedTree;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocRootTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.DocTree.Kind;
import com.sun.source.doctree.DocTreeVisitor;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.HiddenTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.IndexTree;
import com.sun.source.doctree.InheritDocTree;
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ProvidesTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.doctree.ReturnTree;
import com.sun.source.doctree.SeeTree;
import com.sun.source.doctree.SerialDataTree;
import com.sun.source.doctree.SerialFieldTree;
import com.sun.source.doctree.SerialTree;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.StartElementTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.ThrowsTree;
import com.sun.source.doctree.UnknownBlockTagTree;
import com.sun.source.doctree.UnknownInlineTagTree;
import com.sun.source.doctree.UsesTree;
import com.sun.source.doctree.ValueTree;
import com.sun.source.doctree.VersionTree;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTrees;
import io.netty.buffer.ByteBuf;
import io.inverno.core.compiler.spi.ModuleQualifiedName;
import io.inverno.core.compiler.spi.support.AbstractSourceGenerationContext;
import io.inverno.mod.http.base.BadRequestException;
import io.inverno.mod.http.base.ForbiddenException;
import io.inverno.mod.http.base.InternalServerErrorException;
import io.inverno.mod.http.base.MethodNotAllowedException;
import io.inverno.mod.http.base.NotAcceptableException;
import io.inverno.mod.http.base.NotFoundException;
import io.inverno.mod.http.base.ServiceUnavailableException;
import io.inverno.mod.http.base.UnauthorizedException;
import io.inverno.mod.http.base.UnsupportedMediaTypeException;
import io.inverno.mod.http.base.HttpException;
import io.inverno.mod.web.compiler.spi.WebRouteInfo;
/**
*
* Represents a generation context used by the {@link WebRouterConfigurerOpenApiGenerator} during the generation of an Open API specification.
*
*
* @author Jeremy Kuhn
* @since 1.0
*/
class WebServerControllerConfigurerOpenApiGenerationContext extends AbstractSourceGenerationContext {
protected static final String DEFAULT_INDENT = " ";
public static enum GenerationMode {
ROUTER_SPEC,
CONTROLLER_TAG,
ROUTE_PATH,
ROUTE_PARAMETER,
ROUTE_BODY
}
private static enum DocGenerationMode {
DESCRIPTION,
SUMMARY,
VERSION,
CONTACT,
PARAMETER,
RESPONSE,
RICH_TEXT,
PLAIN_TEXT
}
private static class SchemaGenerationOptions {
boolean inList;
boolean useReference;
SchemaGenerationOptions(boolean inList, boolean useReference) {
this.inList = inList;
this.useReference = useReference;
}
}
private final DocTrees docUtils;
private final JavadocToOpenApi javadocToOpenApi;
private final OpenApiSchemaGenerator openApiSchemaGenerator;
private final Map componentSchemaTypes;
private final TypeHierarchyExtractor typeHierarchyExtractor;
private TypeMirror webExceptionType;
private TypeMirror badRequestExceptionType;
private TypeMirror forbiddenExceptionType;
private TypeMirror internalServerErrorExceptionType;
private TypeMirror methodNotAllowedExceptionType;
private TypeMirror notAcceptableExceptionType;
private TypeMirror notFoundExceptionType;
private TypeMirror serviceUnavailableExceptionType;
private TypeMirror unauthorizedExceptionType;
private TypeMirror unsupportedMediaTypeExceptionType;
private TypeMirror classType;
private TypeMirror objectType;
private TypeMirror charSequenceType;
private TypeMirror localDateType;
private TypeMirror localDateTimeType;
private TypeMirror zonedDateTimeType;
private TypeMirror enumType;
private TypeMirror collectionType;
private TypeMirror mapType;
private TypeMirror byteBufType;
private DocGenerationMode docMode;
private DocGenerationMode docInheritMode;
private List docCommentTrees;
private String docParameterName;
private String docExceptionName;
private SchemaGenerationOptions schemaOptions;
private WebRouteInfo webRoute;
private String indentList;
public WebServerControllerConfigurerOpenApiGenerationContext(Types typeUtils, Elements elementUtils, DocTrees docUtils, GenerationMode mode) {
super(typeUtils, elementUtils, mode, DEFAULT_INDENT);
this.docUtils = docUtils;
this.javadocToOpenApi = new JavadocToOpenApi();
this.openApiSchemaGenerator = new OpenApiSchemaGenerator();
this.componentSchemaTypes = new HashMap<>();
this.typeHierarchyExtractor = new TypeHierarchyExtractor(this.typeUtils);
this.webExceptionType = this.elementUtils.getTypeElement(HttpException.class.getCanonicalName()).asType();
this.badRequestExceptionType = this.elementUtils.getTypeElement(BadRequestException.class.getCanonicalName()).asType();
this.forbiddenExceptionType = this.elementUtils.getTypeElement(ForbiddenException.class.getCanonicalName()).asType();
this.internalServerErrorExceptionType = this.elementUtils.getTypeElement(InternalServerErrorException.class.getCanonicalName()).asType();
this.methodNotAllowedExceptionType = this.elementUtils.getTypeElement(MethodNotAllowedException.class.getCanonicalName()).asType();
this.notAcceptableExceptionType = this.elementUtils.getTypeElement(NotAcceptableException.class.getCanonicalName()).asType();
this.notFoundExceptionType = this.elementUtils.getTypeElement(NotFoundException.class.getCanonicalName()).asType();
this.serviceUnavailableExceptionType = this.elementUtils.getTypeElement(ServiceUnavailableException.class.getCanonicalName()).asType();
this.unauthorizedExceptionType = this.elementUtils.getTypeElement(UnauthorizedException.class.getCanonicalName()).asType();
this.unsupportedMediaTypeExceptionType = this.elementUtils.getTypeElement(UnsupportedMediaTypeException.class.getCanonicalName()).asType();
this.classType = this.typeUtils.erasure(this.elementUtils.getTypeElement(Class.class.getCanonicalName()).asType());
this.objectType = this.elementUtils.getTypeElement(Object.class.getCanonicalName()).asType();
this.charSequenceType = this.elementUtils.getTypeElement(CharSequence.class.getCanonicalName()).asType();
this.localDateType = this.elementUtils.getTypeElement(LocalDate.class.getCanonicalName()).asType();
this.localDateTimeType = this.elementUtils.getTypeElement(LocalDateTime.class.getCanonicalName()).asType();
this.zonedDateTimeType = this.elementUtils.getTypeElement(ZonedDateTime.class.getCanonicalName()).asType();
this.enumType = this.typeUtils.erasure(this.elementUtils.getTypeElement(Enum.class.getCanonicalName()).asType());
this.collectionType = this.typeUtils.erasure(this.elementUtils.getTypeElement(Collection.class.getCanonicalName()).asType());
this.mapType = this.typeUtils.erasure(this.elementUtils.getTypeElement(Map.class.getCanonicalName()).asType());
this.byteBufType = this.elementUtils.getTypeElement(ByteBuf.class.getCanonicalName()).asType();
}
private WebServerControllerConfigurerOpenApiGenerationContext(WebServerControllerConfigurerOpenApiGenerationContext parentGeneration) {
super(parentGeneration);
this.docUtils = parentGeneration.docUtils;
this.javadocToOpenApi = parentGeneration.javadocToOpenApi;
this.openApiSchemaGenerator = parentGeneration.openApiSchemaGenerator;
this.componentSchemaTypes = parentGeneration.componentSchemaTypes;
this.typeHierarchyExtractor = parentGeneration.typeHierarchyExtractor;
this.webRoute = parentGeneration.webRoute;
this.docMode = parentGeneration.docMode;
this.docInheritMode = parentGeneration.docInheritMode;
this.docCommentTrees = parentGeneration.docCommentTrees;
this.docParameterName = parentGeneration.docParameterName;
this.docExceptionName = parentGeneration.docExceptionName;
this.schemaOptions = parentGeneration.schemaOptions;
}
@Override
public void setIndent(String indent) {
super.setIndent(indent);
if(this.indent.length() < 2) {
throw new IllegalStateException("Can't insert '-' with a less than 2 spaces indent");
}
char[] indentChars = this.indent.toCharArray();
char[] indentListChars = new char[this.indent.length()];
for(int i=0;i ElementFilter.methodsIn(typeElement.getEnclosedElements()).stream()
.filter(methodElement -> methodElement.equals(routeElement) || this.elementUtils.overrides(routeElement, methodElement, controllerElement))
.findFirst()
)
.filter(Optional::isPresent)
.map(Optional::get)
.map(this.docUtils::getDocCommentTree)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
else {
DocCommentTree docCommentTree = this.docUtils.getDocCommentTree(element);
context.docCommentTrees = docCommentTree != null ? List.of(docCommentTree) : List.of();
}
return context;
}
private WebServerControllerConfigurerOpenApiGenerationContext withDocMode(DocGenerationMode docMode) {
WebServerControllerConfigurerOpenApiGenerationContext context = new WebServerControllerConfigurerOpenApiGenerationContext(this);
if(docMode == DocGenerationMode.DESCRIPTION) {
context.docInheritMode = DocGenerationMode.DESCRIPTION;
}
else if(docMode == DocGenerationMode.PARAMETER) {
context.docInheritMode = DocGenerationMode.PARAMETER;
}
else if(docMode == DocGenerationMode.RESPONSE) {
context.docInheritMode = DocGenerationMode.RESPONSE;
}
context.docMode = docMode;
return context;
}
private WebServerControllerConfigurerOpenApiGenerationContext withDocCommentTrees(List docCommentTrees) {
WebServerControllerConfigurerOpenApiGenerationContext context = new WebServerControllerConfigurerOpenApiGenerationContext(this);
context.docCommentTrees = docCommentTrees;
return context;
}
private WebServerControllerConfigurerOpenApiGenerationContext withDocParameterName(String parameterName) {
WebServerControllerConfigurerOpenApiGenerationContext context = new WebServerControllerConfigurerOpenApiGenerationContext(this);
context.docParameterName = parameterName;
return context;
}
private WebServerControllerConfigurerOpenApiGenerationContext withDocExceptionName(String exceptionName) {
WebServerControllerConfigurerOpenApiGenerationContext context = new WebServerControllerConfigurerOpenApiGenerationContext(this);
context.docExceptionName = exceptionName;
return context;
}
private WebServerControllerConfigurerOpenApiGenerationContext withSchemaOptions(SchemaGenerationOptions schemaOptions) {
WebServerControllerConfigurerOpenApiGenerationContext context = new WebServerControllerConfigurerOpenApiGenerationContext(this);
context.schemaOptions = schemaOptions;
return context;
}
public WebRouteInfo getWebRoute() {
return webRoute;
}
public List getDocCommentTrees() {
return this.docCommentTrees;
}
private TypeMirror getWebExceptionType() {
return webExceptionType != null ? webExceptionType : this.parentGeneration.getWebExceptionType();
}
private TypeMirror getBadRequestExceptionType() {
return badRequestExceptionType != null ? badRequestExceptionType : this.parentGeneration.getBadRequestExceptionType();
}
private TypeMirror getForbiddenExceptionType() {
return forbiddenExceptionType != null ? forbiddenExceptionType : this.parentGeneration.getForbiddenExceptionType();
}
private TypeMirror getInternalServerErrorExceptionType() {
return internalServerErrorExceptionType != null ? internalServerErrorExceptionType : this.parentGeneration.getInternalServerErrorExceptionType();
}
private TypeMirror getMethodNotAllowedExceptionType() {
return methodNotAllowedExceptionType != null ? methodNotAllowedExceptionType : this.parentGeneration.getMethodNotAllowedExceptionType();
}
private TypeMirror getNotAcceptableExceptionType() {
return notAcceptableExceptionType != null ? notAcceptableExceptionType : this.parentGeneration.getNotAcceptableExceptionType();
}
private TypeMirror getNotFoundExceptionType() {
return notFoundExceptionType != null ? notFoundExceptionType : this.parentGeneration.getNotFoundExceptionType();
}
private TypeMirror getServiceUnavailableExceptionType() {
return serviceUnavailableExceptionType != null ? serviceUnavailableExceptionType : this.parentGeneration.getServiceUnavailableExceptionType();
}
private TypeMirror getUnauthorizedExceptionType() {
return unauthorizedExceptionType != null ? unauthorizedExceptionType : this.parentGeneration.getUnauthorizedExceptionType();
}
private TypeMirror getUnsupportedMediaTypeExceptionType() {
return unsupportedMediaTypeExceptionType != null ? unsupportedMediaTypeExceptionType : this.parentGeneration.getUnsupportedMediaTypeExceptionType();
}
private TypeMirror getClassType() {
return classType != null ? classType : this.parentGeneration.getClassType();
}
private TypeMirror getObjectType() {
return objectType != null ? objectType : this.parentGeneration.getObjectType();
}
private TypeMirror getCharSequenceType() {
return charSequenceType != null ? charSequenceType : this.parentGeneration.getCharSequenceType();
}
private TypeMirror getLocalDateType() {
return localDateType != null ? charSequenceType : this.parentGeneration.getCharSequenceType();
}
private TypeMirror getLocalDateTimeType() {
return localDateTimeType != null ? localDateTimeType : this.parentGeneration.getLocalDateTimeType();
}
private TypeMirror getZonedDateTimeType() {
return zonedDateTimeType != null ? zonedDateTimeType : this.parentGeneration.getZonedDateTimeType();
}
private TypeMirror getEnumType() {
return enumType != null ? enumType : this.parentGeneration.getEnumType();
}
private TypeMirror getCollectionType() {
return collectionType != null ? collectionType : this.parentGeneration.getCollectionType();
}
private TypeMirror getMapType() {
return mapType != null ? mapType : this.parentGeneration.getMapType();
}
private TypeMirror getByteBufType() {
return byteBufType != null ? byteBufType : this.parentGeneration.getByteBufType();
}
public Optional getSummary() {
if(this.docCommentTrees == null || this.docCommentTrees.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(this.docCommentTrees.get(0).accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.SUMMARY)));
}
public Optional getDescription() {
if(this.docCommentTrees == null || this.docCommentTrees.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(this.docCommentTrees.get(0).accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.DESCRIPTION)));
}
public Optional getVersion() {
if(this.docCommentTrees == null || this.docCommentTrees.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(this.docCommentTrees.get(0).accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.VERSION)));
}
public Optional getContact() {
if(this.docCommentTrees == null || this.docCommentTrees.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(this.docCommentTrees.get(0).accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.CONTACT)));
}
public Optional getParameterDescription(String parameterName) {
if(parameterName == null || this.docCommentTrees == null || this.docCommentTrees.isEmpty()) {
return Optional.empty();
}
for(DocCommentTree dct : this.docCommentTrees) {
Optional paramTreeOptional = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.PARAM && ((ParamTree)docTree).getName().toString().equals(parameterName))
.findFirst()
.map(docTree -> (ParamTree)docTree);
if(paramTreeOptional.isPresent()) {
return paramTreeOptional.map(paramTree -> paramTree.accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.PARAMETER)));
}
}
return Optional.empty();
}
public List getResponses(ExecutableElement routeElement, TypeMirror responseBodyType) {
if(routeElement == null || responseBodyType == null) {
return List.of();
}
List returnResponses = new ArrayList<>();
Map thrownResponsesByType = new HashMap<>();
if(this.docCommentTrees != null && !this.docCommentTrees.isEmpty()) {
// @return
for(DocCommentTree dct : this.docCommentTrees) {
List returnTags = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.RETURN)
.map(docTree -> (ReturnTree)docTree)
.collect(Collectors.toList());
if(!returnTags.isEmpty()) {
for(ReturnTree returnTag: returnTags) {
String status = returnTag.getDescription().stream()
.filter(docTree -> docTree.getKind() == Kind.UNKNOWN_INLINE_TAG && ((UnknownInlineTagTree)docTree).getTagName().equalsIgnoreCase("inverno.web.status"))
.findFirst()
.map(docTree -> docTree.accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.RESPONSE)))
.map(StringBuilder::toString)
.orElse("200");
StringBuilder description = returnTag.accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.RESPONSE));
returnResponses.add(new ResponseSpec(status, description, responseBodyType));
}
break;
}
}
// @throws
for(DocCommentTree dct : this.docCommentTrees) {
List throwsTags = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.THROWS)
.map(docTree -> (ThrowsTree)docTree)
.collect(Collectors.toList());
if(!throwsTags.isEmpty()) {
for(ThrowsTree throwsTag: throwsTags) {
DocTreePath thrownPath = DocTreePath.getPath(this.docUtils.getPath(routeElement), dct, throwsTag.getExceptionName());
TypeElement thrownElement = (TypeElement)this.docUtils.getElement(thrownPath);
if(thrownElement != null) {
TypeMirror thrownType = thrownElement.asType();
String status = throwsTag.getDescription().stream()
.filter(docTree -> docTree.getKind() == Kind.UNKNOWN_INLINE_TAG && ((UnknownInlineTagTree)docTree).getTagName().equalsIgnoreCase("inverno.web.status"))
.findFirst()
.map(docTree -> docTree.accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.RESPONSE)))
.map(StringBuilder::toString)
.orElse(this.getResponseStatus(thrownType));
StringBuilder description = throwsTag.accept(this.javadocToOpenApi, this.withDocMode(DocGenerationMode.RESPONSE));
thrownResponsesByType.put(thrownType.toString(), new ResponseSpec(status, description, thrownType));
}
}
break;
}
}
}
if(returnResponses.isEmpty()) {
returnResponses.add(new ResponseSpec("200", new StringBuilder("''"), responseBodyType));
}
// throws
for(TypeMirror thrownType : routeElement.getThrownTypes()) {
thrownResponsesByType.putIfAbsent(thrownType.toString(), new ResponseSpec(this.getResponseStatus(thrownType), new StringBuilder("''"), thrownType));
}
return Stream.concat(returnResponses.stream(), thrownResponsesByType.values().stream()).collect(Collectors.toList());
}
private String getResponseStatus(TypeMirror type) {
String status = "500";
if(this.typeUtils.isAssignable(type, this.getWebExceptionType())) {
if(this.typeUtils.isAssignable(type, this.getBadRequestExceptionType())) {
status = "400";
}
else if(this.typeUtils.isAssignable(type, this.getForbiddenExceptionType())) {
status = "403";
}
else if(this.typeUtils.isAssignable(type, this.getInternalServerErrorExceptionType())) {
status = "500";
}
else if(this.typeUtils.isAssignable(type, this.getMethodNotAllowedExceptionType())) {
status = "405";
}
else if(this.typeUtils.isAssignable(type, this.getNotAcceptableExceptionType())) {
status = "406";
}
else if(this.typeUtils.isAssignable(type, this.getNotFoundExceptionType())) {
status = "404";
}
else if(this.typeUtils.isAssignable(type, this.getServiceUnavailableExceptionType())) {
status = "503";
}
else if(this.typeUtils.isAssignable(type, this.getUnauthorizedExceptionType())) {
status = "401";
}
else if(this.typeUtils.isAssignable(type, this.getUnsupportedMediaTypeExceptionType())) {
status = "415";
}
}
return status;
}
public Optional getSchema(TypeMirror type, boolean inList) {
return Optional.ofNullable(type.accept(this.openApiSchemaGenerator, this.withSchemaOptions(new SchemaGenerationOptions(inList, true)))).filter(sb -> sb.length() > 0);
}
public Optional getComponentsSchemas() {
StringBuilder result = new StringBuilder();
Set generatedSchemas = new HashSet<>();
while(generatedSchemas.size() < this.componentSchemaTypes.size()) {
for(Map.Entry e : new HashMap<>(this.componentSchemaTypes).entrySet()) {
if(generatedSchemas.add(e.getValue().toString())) {
StringBuilder schema = e.getValue().accept(this.openApiSchemaGenerator, this.withIndentDepthAdd(1).withSchemaOptions(new SchemaGenerationOptions(false, false)));
if(schema.length() > 0) {
result.append(this.indent(0)).append(e.getKey()).append(":").append(System.lineSeparator());
result.append(schema).append(System.lineSeparator());
}
}
}
}
return Optional.of(result).filter(sb -> sb.length() > 0);
}
private static class JavadocToOpenApi implements DocTreeVisitor {
@Override
public StringBuilder visitAttribute(AttributeTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RICH_TEXT) {
return new StringBuilder(node.getName().toString()).append("=\"").append(node.getValue().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT))).collect(context.joining())).append("\"");
}
return new StringBuilder();
}
@Override
public StringBuilder visitAuthor(AuthorTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.CONTACT) {
StringBuilder result = new StringBuilder();
StringBuilder nameBuilder = new StringBuilder(context.indent(1)).append("name: '");
Optional linkBuilderOptional = Optional.empty();
for(DocTree namePart : ((AuthorTree)node).getName()) {
if(!linkBuilderOptional.isPresent() && namePart.getKind() == Kind.START_ELEMENT && ((StartElementTree)namePart).getName().toString().equalsIgnoreCase("a")) {
linkBuilderOptional = ((StartElementTree)namePart).getAttributes().stream()
.filter(attribute -> attribute.getKind() == Kind.ATTRIBUTE && ((AttributeTree)attribute).getName().toString().equalsIgnoreCase("href"))
.findFirst()
.map(attribute -> {
StringBuilder linkBuilder = new StringBuilder();
String value = ((AttributeTree)attribute).getValue().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT))).collect(context.joining()).toString();
if(value.startsWith("mailto:")) {
linkBuilder.append(context.indent(1)).append("email: '").append(value.substring(7)).append("'");
}
else {
linkBuilder.append(context.indent(1)).append("url: '").append(value).append("'");
}
return linkBuilder;
});
}
else {
nameBuilder.append(namePart.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT)));
}
}
result.append(nameBuilder).append("'");
linkBuilderOptional.ifPresent(link -> result.append(System.lineSeparator()).append(link));
return result;
}
return new StringBuilder();
}
@Override
public StringBuilder visitComment(CommentTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
// HTML comment
return new StringBuilder();
}
@Override
public StringBuilder visitDeprecated(DeprecatedTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitDocComment(DocCommentTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.SUMMARY) {
return new StringBuilder().append("'").append(node.getFirstSentence().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT))).collect(context.joining())).append("'");
}
else if(context.docMode == DocGenerationMode.DESCRIPTION) {
return new StringBuilder().append("'").append(node.getFullBody().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.RICH_TEXT))).collect(context.joining())).append("'");
}
else if(context.docMode == DocGenerationMode.CONTACT) {
return node.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.AUTHOR).findFirst()
.map(authorTree -> authorTree.accept(this, context)).orElse(null);
}
else if(context.docMode == DocGenerationMode.VERSION) {
return node.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.VERSION).findFirst()
.map(versionTree -> versionTree.accept(this, context)).orElse(null);
}
return new StringBuilder();
}
@Override
public StringBuilder visitDocRoot(DocRootTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitEndElement(EndElementTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RICH_TEXT) {
return new StringBuilder("").append(node.getName().toString()).append(">");
}
return new StringBuilder();
}
@Override
public StringBuilder visitEntity(EntityTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder(node.getName().toString());
}
@Override
public StringBuilder visitErroneous(ErroneousTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitHidden(HiddenTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitIdentifier(IdentifierTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
// TODO seems to be the identifier after the @parameter tag for instance
return new StringBuilder(node.getName().toString());
}
@Override
public StringBuilder visitIndex(IndexTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitInheritDoc(InheritDocTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
// This basically just indicates that the doc is inherited but it doesn't provide anything...
// We could maybe navigate here in the type hierarchy but this would require to have:
// - the executable element for a method description
// - the executable element AND the parameter name for a parameter description
// - we have it from the ParameterTree
// - the executable element for a return description
// - the executable element AND the exception name for a throws description
// - we have it from the ThrowsTree
if(context.docCommentTrees.size() > 1) {
if(context.docInheritMode == DocGenerationMode.DESCRIPTION) {
StringBuilder inheritDescription = this.visitDocComment(context.docCommentTrees.get(1), context.withDocMode(DocGenerationMode.DESCRIPTION).withDocCommentTrees(context.docCommentTrees.subList(1, context.docCommentTrees.size())));
return inheritDescription.deleteCharAt(0).deleteCharAt(inheritDescription.length() - 1);
}
else if(context.docInheritMode == DocGenerationMode.PARAMETER) {
if(context.docParameterName != null) {
for(int i=1;i paramTreeOptional = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.PARAM && ((ParamTree)docTree).getName().toString().equals(context.docParameterName))
.findFirst()
.map(docTree -> (ParamTree)docTree);
if(paramTreeOptional.isPresent()) {
StringBuilder inheritParameterDescription = this.visitParam(paramTreeOptional.get(), context.withDocMode(DocGenerationMode.PARAMETER).withDocCommentTrees(context.docCommentTrees.subList(i, context.docCommentTrees.size())));
return inheritParameterDescription.deleteCharAt(0).deleteCharAt(inheritParameterDescription.length() - 1);
}
}
}
}
else if(context.docInheritMode == DocGenerationMode.RESPONSE) {
if(context.docExceptionName != null) {
// @throws
for(int i=1;i throwsTreeOptional = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.THROWS && ((ThrowsTree)docTree).getExceptionName().toString().equals(context.docExceptionName))
.findFirst()
.map(docTree -> (ThrowsTree)docTree);
if(throwsTreeOptional.isPresent()) {
StringBuilder inheritReponseDescription = this.visitThrows(throwsTreeOptional.get(), context.withDocMode(DocGenerationMode.RESPONSE).withDocCommentTrees(context.docCommentTrees.subList(i, context.docCommentTrees.size())));
return inheritReponseDescription.deleteCharAt(0).deleteCharAt(inheritReponseDescription.length() - 1);
}
}
}
else {
// @return
// There can be more than one to specify multiple response status (xdoclint actualy breaks javadoc generation) but we consider only the first one
for(int i=1;i returnTreeOptional = dct.getBlockTags().stream()
.filter(docTree -> docTree.getKind() == Kind.RETURN)
.findFirst()
.map(docTree -> (ReturnTree)docTree);
if(returnTreeOptional.isPresent()) {
StringBuilder inheritReponseDescription = this.visitReturn(returnTreeOptional.get(), context.withDocMode(DocGenerationMode.RESPONSE).withDocCommentTrees(context.docCommentTrees.subList(i, context.docCommentTrees.size())));
return inheritReponseDescription.deleteCharAt(0).deleteCharAt(inheritReponseDescription.length() - 1);
}
}
}
}
}
return new StringBuilder();
}
@Override
public StringBuilder visitLink(LinkTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
// {@link reference label}
StringBuilder result = new StringBuilder();
if(node.getLabel().isEmpty()) {
result.append(node.getReference().toString());
}
else {
result.append(node.getLabel().stream().map(docTree -> docTree.accept(this, context)).collect(context.joining()));
}
return result;
}
@Override
public StringBuilder visitLiteral(LiteralTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
// {@literal ac}
return node.getBody().accept(this, context);
}
@Override
public StringBuilder visitParam(ParamTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.PARAMETER) {
return new StringBuilder("'").append(node.getDescription().stream().map(docTree -> docTree.accept(this, context.withDocParameterName(node.getName().toString()).withDocMode(DocGenerationMode.RICH_TEXT))).collect(context.joining())).append("'");
}
return new StringBuilder();
}
@Override
public StringBuilder visitProvides(ProvidesTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitReference(ReferenceTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitReturn(ReturnTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RESPONSE) {
// @return {@inverno.web.status 201} dslgdfjgdf {@inverno.web.status 200} dslgdfjgdf
StringBuilder result = new StringBuilder();
result.append("'");
if(!node.getDescription().isEmpty()) {
result.append(node.getDescription().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.RICH_TEXT))).collect(context.joining()));
}
result.append("'");
return result;
}
return new StringBuilder();
}
@Override
public StringBuilder visitSee(SeeTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitSerial(SerialTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitSerialData(SerialDataTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitSerialField(SerialFieldTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitSince(SinceTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitStartElement(StartElementTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RICH_TEXT) {
StringBuilder result = new StringBuilder();
result.append("<").append(node.getName().toString());
if(!node.getAttributes().isEmpty()) {
result.append(" ");
result.append(node.getAttributes().stream().map(docTree -> docTree.accept(this, context)).collect(context.joining(" ")));
}
if(node.isSelfClosing()) {
result.append("/>");
}
else {
result.append(">");
}
return result;
}
return new StringBuilder();
}
@Override
public StringBuilder visitText(TextTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder().append(node.getBody().replace("\'", "''"));
}
@Override
public StringBuilder visitThrows(ThrowsTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RESPONSE) {
// @throws Exception {@inverno.web.status 500} dsfdg
StringBuilder result = new StringBuilder();
result.append("'");
if(!node.getDescription().isEmpty()) {
result.append(node.getDescription().stream().map(docTree -> docTree.accept(this, context.withDocExceptionName(node.getExceptionName().toString()).withDocMode(DocGenerationMode.RICH_TEXT))).collect(context.joining()));
}
result.append("'");
return result;
}
return new StringBuilder();
}
@Override
public StringBuilder visitUnknownBlockTag(UnknownBlockTagTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitUnknownInlineTag(UnknownInlineTagTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.RESPONSE) {
if(node.getTagName().equalsIgnoreCase("inverno.web.status")) {
// TODO support reference?: {@inverno.web.status Status#UNSUPPORTED_MEDIA_TYPE}
return new StringBuilder().append(node.getContent().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT))).collect(context.joining()));
}
}
return new StringBuilder();
}
@Override
public StringBuilder visitUses(UsesTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitValue(ValueTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitVersion(VersionTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
if(context.docMode == DocGenerationMode.VERSION) {
return new StringBuilder().append("'").append(node.getBody().stream().map(docTree -> docTree.accept(this, context.withDocMode(DocGenerationMode.PLAIN_TEXT))).collect(Collectors.joining())).append("'");
}
return new StringBuilder();
}
@Override
public StringBuilder visitOther(DocTree node, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
}
private static class OpenApiSchemaGenerator implements TypeVisitor {
@Override
public StringBuilder visit(TypeMirror type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
if(type instanceof PrimitiveType) {
return this.visitPrimitive((PrimitiveType)type, context);
}
else if(type instanceof ArrayType) {
return this.visitArray((ArrayType)type, context);
}
else if(type instanceof DeclaredType) {
return this.visitDeclared((DeclaredType)type, context);
}
else if(type instanceof WildcardType) {
return this.visitWildcard((WildcardType)type, context);
}
else if(type instanceof IntersectionType) {
return this.visitIntersection((IntersectionType)type, context);
}
else if(type instanceof NoType) {
return this.visitNoType((NoType)type, context);
}
return new StringBuilder();
}
@Override
public StringBuilder visitPrimitive(PrimitiveType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ");
if(type.getKind() == TypeKind.CHAR) {
result.append("string");
}
else if(type.getKind() == TypeKind.BOOLEAN) {
result.append("boolean");
}
else if(type.getKind() == TypeKind.BYTE || type.getKind() == TypeKind.SHORT || type.getKind() == TypeKind.INT) {
result.append("integer").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("int32");
}
else if(type.getKind() == TypeKind.LONG) {
result.append("integer").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("int64");
}
else if(type.getKind() == TypeKind.FLOAT) {
result.append("number").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("float");
}
else if(type.getKind() == TypeKind.DOUBLE) {
result.append("number").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("double");
}
return result;
}
@Override
public StringBuilder visitNull(NullType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitArray(ArrayType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ");
result.append("array").append(System.lineSeparator());
result.append(context.indent(0)).append("items: ").append(System.lineSeparator());
StringBuilder componentSchema = this.visit(type.getComponentType(), context.withIndentDepthAdd(1).withSchemaOptions(new SchemaGenerationOptions(false, true)));
if(componentSchema.length() > 0) {
result.append(componentSchema);
}
else {
result.append(context.indent(1)).append("type: object");
}
return result;
}
@Override
public StringBuilder visitDeclared(DeclaredType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
try {
return this.visit(context.typeUtils.unboxedType(type), context);
}
catch (Exception e) {
// type is not a primitive wrapper
}
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ");
if(context.typeUtils.isAssignable(context.typeUtils.erasure(type), context.getClassType())) {
result.append("string");
}
else if(context.typeUtils.isAssignable(type, context.getCharSequenceType())) {
result.append("string");
}
else if(context.typeUtils.isSameType(type, context.getByteBufType())) {
result.append("string");
}
else if(context.typeUtils.isSameType(type, context.getLocalDateType())) {
result.append("string").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("date");
}
else if(context.typeUtils.isSameType(type, context.getLocalDateTimeType())) {
result.append("string").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("date-time");
}
else if(context.typeUtils.isSameType(type, context.getZonedDateTimeType())) {
result.append("string").append(System.lineSeparator());
result.append(context.indent(0)).append("format: ").append("date-time");
}
else if(context.typeUtils.isAssignable(context.typeUtils.erasure(type), context.getEnumType())) {
if(context.schemaOptions.useReference) {
StringBuilder reference = new StringBuilder();
if(context.schemaOptions.inList) {
reference.append(context.indentList(0));
}
else {
reference.append(context.indent(0));
}
reference.append("$ref: '#/components/schemas/").append(type.toString()).append("'");
context.componentSchemaTypes.put(type.toString(), type);
return reference;
}
else {
result.append("string").append(System.lineSeparator());
result.append(context.indent(0)).append("enum: ").append(System.lineSeparator());
result.append(context.typeUtils.asElement(type).getEnclosedElements().stream()
.filter(element -> element.getKind() == ElementKind.ENUM_CONSTANT)
.map(enumConstant -> new StringBuilder(context.indentList(1)).append(enumConstant.toString()))
.collect(context.joining(System.lineSeparator()))
);
}
}
else if(context.typeUtils.isAssignable(context.typeUtils.erasure(type), context.getCollectionType())) {
result.append("array").append(System.lineSeparator());
result.append(context.indent(0)).append("items: ").append(System.lineSeparator());
StringBuilder componentSchema = this.visit(((DeclaredType)type).getTypeArguments().get(0), context.withIndentDepthAdd(1).withSchemaOptions(new SchemaGenerationOptions(false, true)));
if(componentSchema.length() > 0) {
result.append(componentSchema);
}
else {
result.append(context.indent(1)).append("type: object");
}
}
else if(context.typeUtils.isAssignable(context.typeUtils.erasure(type), context.getMapType())) {
result.append("object").append(System.lineSeparator());
result.append(context.indent(0)).append("additionalProperties: ").append(System.lineSeparator());
StringBuilder componentSchema = this.visit(((DeclaredType)type).getTypeArguments().get(1), context.withIndentDepthAdd(1).withSchemaOptions(new SchemaGenerationOptions(false, true)));
if(componentSchema.length() > 0) {
result.append(componentSchema);
}
else {
result.append(context.indent(1)).append("type: object");
}
}
else {
if(context.schemaOptions.useReference) {
StringBuilder reference = new StringBuilder();
if(context.schemaOptions.inList) {
reference.append(context.indentList(0));
}
else {
reference.append(context.indent(0));
}
reference.append("$ref: '#/components/schemas/").append(type.toString()).append("'");
context.componentSchemaTypes.put(type.toString(), type);
return reference;
}
else {
result.append("object");
// Look for accessor methods and public field and iterate...
List extends Element> typeMemberElements = context.elementUtils.getAllMembers((TypeElement)context.typeUtils.asElement(type)).stream()
.filter(element -> !context.typeUtils.isSameType(element.getEnclosingElement().asType(), context.getObjectType()))
.collect(Collectors.toList());
StringBuilder publicFieldsProperties = ElementFilter.fieldsIn(typeMemberElements).stream()
.filter(element -> element.getModifiers().contains(Modifier.PUBLIC) && !element.getModifiers().contains(Modifier.STATIC))
.map(element -> {
StringBuilder property = new StringBuilder();
TypeMirror propertyType = ((ExecutableType)context.typeUtils.asMemberOf(type, element)).getReturnType();
property.append(context.indent(1)).append(element.getSimpleName().toString()).append(": ").append(System.lineSeparator());
StringBuilder propertySchema = this.visit(propertyType, context.withIndentDepthAdd(2).withSchemaOptions(new SchemaGenerationOptions(false, true)));
if(propertySchema.length() > 0) {
property.append(propertySchema);
}
else {
property.append(context.indent(1)).append("type: object");
}
return property;
})
.collect(context.joining(System.lineSeparator()));
Map> accessorsByPropertyName = ElementFilter.methodsIn(typeMemberElements).stream()
.filter(element -> element.getParameters().size() <= 1)
.filter(element -> element.getModifiers().contains(Modifier.PUBLIC) && !element.getModifiers().contains(Modifier.ABSTRACT) && !element.getModifiers().contains(Modifier.STATIC))
.filter(element -> {
String elementName = element.getSimpleName().toString();
if(elementName.startsWith("get")) {
return element.getParameters().isEmpty() && element.getReturnType().getKind() != TypeKind.VOID;
}
else if(elementName.startsWith("set")) {
return element.getParameters().size() == 1 && element.getReturnType().getKind() == TypeKind.VOID;
}
return false;
})
.collect(Collectors.groupingBy(element -> element.getSimpleName().toString().substring(3)));
StringBuilder accessorsProperties = accessorsByPropertyName.entrySet().stream()
.map(e -> {
String propertyName = e.getKey();
propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
boolean hasGetter = false;
boolean hasSetter = false;
Map propertyTypeByName = new HashMap<>();
for(ExecutableElement element : e.getValue()) {
TypeMirror propertyType;
String accessorName = element.getSimpleName().toString();
if(accessorName.startsWith("get")) {
hasGetter = true;
propertyType = ((ExecutableType)context.typeUtils.asMemberOf(type, element)).getReturnType();
}
else if(accessorName.startsWith("set")) {
hasSetter = true;
propertyType = ((ExecutableType)context.typeUtils.asMemberOf(type, element)).getParameterTypes().get(0);
}
else {
throw new IllegalStateException("Element should be an accessor");
}
propertyTypeByName.put(propertyType.toString(), propertyType);
}
int propertyTypesCount = propertyTypeByName.size();
if(propertyTypesCount > 0) {
StringBuilder property = new StringBuilder();
property.append(context.indent(1)).append(propertyName).append(": ").append(System.lineSeparator());
if(hasGetter && !hasSetter) {
property.append(context.indent(2)).append("readOnly: true").append(System.lineSeparator());
}
if(!hasGetter && hasSetter) {
property.append(context.indent(2)).append("writeOnly: true").append(System.lineSeparator());
}
if(propertyTypesCount > 1) {
StringBuilder propertySchema = propertyTypeByName.values().stream()
.map(propertyType -> this.visit(propertyType, context.withIndentDepthAdd(3).withSchemaOptions(new SchemaGenerationOptions(true, true))))
.filter(sb -> sb.length() > 0)
.collect(context.joining(System.lineSeparator()));
if(propertySchema.length() > 0) {
property.append(context.indent(2)).append("oneOf: ").append(System.lineSeparator());
property.append(propertySchema);
}
else {
property.append(context.indent(1)).append("type: object");
}
}
else {
StringBuilder propertySchema = this.visit(propertyTypeByName.values().iterator().next(), context.withIndentDepthAdd(2).withSchemaOptions(new SchemaGenerationOptions(false, true)));
if(propertySchema.length() > 0) {
property.append(propertySchema);
}
else {
property.append(context.indent(1)).append("type: object");
}
}
return property;
}
else {
return null;
}
})
.filter(Objects::nonNull)
.collect(context.joining(System.lineSeparator()));
StringBuilder properties = Stream.of(publicFieldsProperties, accessorsProperties).filter(sb -> sb.length() > 0).collect(context.joining(System.lineSeparator()));
if(properties.length() > 0) {
result.append(System.lineSeparator()).append(context.indent(0)).append("properties: ").append(System.lineSeparator());
result.append(properties);
}
}
}
return result;
}
@Override
public StringBuilder visitError(ErrorType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
return new StringBuilder();
}
@Override
public StringBuilder visitTypeVariable(TypeVariable type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
if(type.getLowerBound() != null && type.getLowerBound().getKind() != TypeKind.NULL) {
return this.visit(type.getLowerBound(), context);
}
else {
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ").append("object");
return result;
}
}
@Override
public StringBuilder visitWildcard(WildcardType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
if(type.getExtendsBound() != null) {
return this.visit(type.getExtendsBound(), context);
}
else {
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ").append("object");
return result;
}
}
@Override
public StringBuilder visitExecutable(ExecutableType t, WebServerControllerConfigurerOpenApiGenerationContext p) {
return new StringBuilder();
}
@Override
public StringBuilder visitNoType(NoType t, WebServerControllerConfigurerOpenApiGenerationContext p) {
return new StringBuilder();
}
@Override
public StringBuilder visitUnknown(TypeMirror t, WebServerControllerConfigurerOpenApiGenerationContext p) {
return new StringBuilder();
}
@Override
public StringBuilder visitUnion(UnionType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
Objects.requireNonNull(type, "type");
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ").append("object");
return result;
}
@Override
public StringBuilder visitIntersection(IntersectionType type, WebServerControllerConfigurerOpenApiGenerationContext context) {
StringBuilder result = new StringBuilder();
if(context.schemaOptions.inList) {
result.append(context.indentList(0));
}
else {
result.append(context.indent(0));
}
result.append("type: ").append("object");
return result;
}
}
public static class ResponseSpec {
private final String status;
private final StringBuilder description;
private final TypeMirror type;
private ResponseSpec (String status, StringBuilder description, TypeMirror type) {
this.status = status;
this.type = type;
this.description = description;
}
public String getStatus() {
return status;
}
public StringBuilder getDescription() {
return description;
}
public TypeMirror getType() {
return type;
}
}
}