| // *************************************************************************************************************************** |
| // * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * |
| // * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * |
| // * to you 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 org.apache.juneau.rest; |
| |
| import static org.apache.juneau.internal.ClassUtils.*; |
| import static org.apache.juneau.internal.ExceptionUtils.*; |
| import static org.apache.juneau.internal.StringUtils.*; |
| import static org.apache.juneau.rest.RestParamType.*; |
| |
| import java.io.*; |
| import java.lang.reflect.*; |
| import java.lang.reflect.Method; |
| import java.util.*; |
| |
| import org.apache.juneau.*; |
| import org.apache.juneau.collections.*; |
| import org.apache.juneau.cp.*; |
| import org.apache.juneau.dto.swagger.Swagger; |
| import org.apache.juneau.http.annotation.*; |
| import org.apache.juneau.http.annotation.Contact; |
| import org.apache.juneau.http.annotation.License; |
| import org.apache.juneau.http.header.*; |
| import org.apache.juneau.internal.*; |
| import org.apache.juneau.json.*; |
| import org.apache.juneau.jsonschema.*; |
| import org.apache.juneau.jsonschema.annotation.*; |
| import org.apache.juneau.jsonschema.annotation.Items; |
| import org.apache.juneau.jsonschema.annotation.Tag; |
| import org.apache.juneau.marshall.*; |
| import org.apache.juneau.parser.*; |
| import org.apache.juneau.reflect.*; |
| import org.apache.juneau.rest.annotation.*; |
| import org.apache.juneau.rest.util.*; |
| import org.apache.juneau.serializer.*; |
| import org.apache.juneau.svl.*; |
| |
| /** |
| * A single session of generating a Swagger document. |
| */ |
| public class BasicSwaggerProviderSession { |
| |
| private final RestContext context; |
| private final Class<?> c; |
| private final ClassInfo rci; |
| private final FileFinder ff; |
| private final Messages mb; |
| private final VarResolverSession vr; |
| private final JsonParser jp = JsonParser.create().ignoreUnknownBeanProperties().build(); |
| private final JsonSchemaGeneratorSession js; |
| private final Locale locale; |
| |
| |
| /** |
| * Constructor. |
| * |
| * @param context The context of the REST object we're generating Swagger about. |
| * @param locale The language of the swagger we're asking for. |
| * @param ff The file finder to use for finding JSON files. |
| * @param messages The messages to use for finding localized strings. |
| * @param vr The variable resolver to use for resolving variables in the swagger. |
| * @param js The JSON-schema generator to use for stuff like examples. |
| */ |
| public BasicSwaggerProviderSession(RestContext context, Locale locale, FileFinder ff, Messages messages, VarResolverSession vr, JsonSchemaGeneratorSession js) { |
| this.context = context; |
| this.c = context.getResourceClass(); |
| this.rci = ClassInfo.of(c); |
| this.ff = ff; |
| this.mb = messages; |
| this.vr = vr; |
| this.js = js; |
| this.locale = locale; |
| } |
| |
| /** |
| * Generates the swagger. |
| * |
| * @return A new {@link Swagger} object. |
| * @throws Exception If an error occurred producing the Swagger. |
| */ |
| public Swagger getSwagger() throws Exception { |
| |
| InputStream is = ff.getStream(rci.getSimpleName() + ".json", locale).orElse(null); |
| |
| // Load swagger JSON from classpath. |
| OMap omSwagger = SimpleJson.DEFAULT.read(is, OMap.class); |
| if (omSwagger == null) |
| omSwagger = new OMap(); |
| |
| // Combine it with @Rest(swagger) |
| for (Rest rr : rci.getAnnotations(Rest.class)) { |
| |
| OMap sInfo = omSwagger.getMap("info", true); |
| |
| sInfo |
| .appendSkipEmpty("title", |
| firstNonEmpty( |
| sInfo.getString("title"), |
| resolve(rr.title()) |
| ) |
| ) |
| .appendSkipEmpty("description", |
| firstNonEmpty( |
| sInfo.getString("description"), |
| resolve(rr.description()) |
| ) |
| ); |
| |
| org.apache.juneau.rest.annotation.Swagger r = rr.swagger(); |
| |
| omSwagger.append(parseMap(r.value(), "@Swagger(value) on class {0}", c)); |
| |
| if (! SwaggerAnnotation.empty(r)) { |
| OMap info = omSwagger.getMap("info", true); |
| |
| info |
| .appendSkipEmpty("title", resolve(r.title())) |
| .appendSkipEmpty("description", resolve(r.description())) |
| .appendSkipEmpty("version", resolve(r.version())) |
| .appendSkipEmpty("termsOfService", resolve(r.termsOfService())) |
| .appendSkipEmpty("contact", |
| merge( |
| info.getMap("contact"), |
| toMap(r.contact(), "@Swagger(contact) on class {0}", c) |
| ) |
| ) |
| .appendSkipEmpty("license", |
| merge( |
| info.getMap("license"), |
| toMap(r.license(), "@Swagger(license) on class {0}", c) |
| ) |
| ); |
| } |
| |
| omSwagger |
| .appendSkipEmpty("externalDocs", |
| merge( |
| omSwagger.getMap("externalDocs"), |
| toMap(r.externalDocs(), "@Swagger(externalDocs) on class {0}", c) |
| ) |
| ) |
| .appendSkipEmpty("tags", |
| merge( |
| omSwagger.getList("tags"), |
| toList(r.tags(), "@Swagger(tags) on class {0}", c) |
| ) |
| ); |
| } |
| |
| omSwagger.appendSkipEmpty("externalDocs", parseMap(mb.findFirstString("externalDocs"), "Messages/externalDocs on class {0}", c)); |
| |
| OMap info = omSwagger.getMap("info", true); |
| |
| info |
| .appendSkipEmpty("title", resolve(mb.findFirstString("title"))) |
| .appendSkipEmpty("description", resolve(mb.findFirstString("description"))) |
| .appendSkipEmpty("version", resolve(mb.findFirstString("version"))) |
| .appendSkipEmpty("termsOfService", resolve(mb.findFirstString("termsOfService"))) |
| .appendSkipEmpty("contact", parseMap(mb.findFirstString("contact"), "Messages/contact on class {0}", c)) |
| .appendSkipEmpty("license", parseMap(mb.findFirstString("license"), "Messages/license on class {0}", c)); |
| |
| if (info.isEmpty()) |
| omSwagger.remove("info"); |
| |
| OList |
| produces = omSwagger.getList("produces", true), |
| consumes = omSwagger.getList("consumes", true); |
| if (consumes.isEmpty()) |
| consumes.addAll(context.getConsumes()); |
| if (produces.isEmpty()) |
| produces.addAll(context.getProduces()); |
| |
| Map<String,OMap> tagMap = new LinkedHashMap<>(); |
| if (omSwagger.containsKey("tags")) { |
| for (OMap om : omSwagger.getList("tags").elements(OMap.class)) { |
| String name = om.getString("name"); |
| if (name == null) |
| throw new SwaggerException(null, "Tag definition found without name in swagger JSON."); |
| tagMap.put(name, om); |
| } |
| } |
| |
| String s = mb.findFirstString("tags"); |
| if (s != null) { |
| for (OMap m : parseListOrCdl(s, "Messages/tags on class {0}", c).elements(OMap.class)) { |
| String name = m.getString("name"); |
| if (name == null) |
| throw new SwaggerException(null, "Tag definition found without name in resource bundle on class {0}", c) ; |
| if (tagMap.containsKey(name)) |
| tagMap.get(name).putAll(m); |
| else |
| tagMap.put(name, m); |
| } |
| } |
| |
| // Load our existing bean definitions into our session. |
| OMap definitions = omSwagger.getMap("definitions", true); |
| for (String defId : definitions.keySet()) |
| js.addBeanDef(defId, new OMap(definitions.getMap(defId))); |
| |
| // Iterate through all the @RestOp methods. |
| for (RestOperationContext sm : context.getMethodContexts()) { |
| |
| BeanSession bs = sm.createBeanSession(); |
| |
| Method m = sm.getJavaMethod(); |
| MethodInfo mi = MethodInfo.of(m); |
| AnnotationList al = mi.getAnnotationGroupList(RestOp.class); |
| String mn = m.getName(); |
| |
| // Get the operation from the existing swagger so far. |
| OMap op = getOperation(omSwagger, sm.getPathPattern(), sm.getHttpMethod().toLowerCase()); |
| |
| // Add @RestOp(swagger) |
| OpSwagger ms = al.getValues(OpSwagger.class, "swagger").stream().filter(x -> ! OpSwaggerAnnotation.empty(x)).findFirst().orElse(OpSwaggerAnnotation.create().build()); |
| |
| op.append(parseMap(ms.value(), "@OpSwagger(value) on class {0} method {1}", c, m)); |
| op.appendSkipEmpty("operationId", |
| firstNonEmpty( |
| resolve(ms.operationId()), |
| op.getString("operationId"), |
| mn |
| ) |
| ); |
| op.appendSkipEmpty("summary", |
| firstNonEmpty( |
| resolve(ms.summary()), |
| resolve(mb.findFirstString(mn + ".summary")), |
| op.getString("summary"), |
| resolve(al.getValues(String.class, "summary").stream().filter(x -> !x.isEmpty()).findFirst().orElse(null)) |
| ) |
| ); |
| op.appendSkipEmpty("description", |
| firstNonEmpty( |
| resolve(ms.description()), |
| resolve(mb.findFirstString(mn + ".description")), |
| op.getString("description"), |
| resolve(al.getValues(String[].class, "description").stream().filter(x -> x.length > 0).findFirst().orElse(new String[0])) |
| ) |
| ); |
| op.appendSkipEmpty("deprecated", |
| firstNonEmpty( |
| resolve(ms.deprecated()), |
| (m.getAnnotation(Deprecated.class) != null || m.getDeclaringClass().getAnnotation(Deprecated.class) != null) ? "true" : null |
| ) |
| ); |
| op.appendSkipEmpty("tags", |
| merge( |
| parseListOrCdl(mb.findFirstString(mn + ".tags"), "Messages/tags on class {0} method {1}", c, m), |
| parseListOrCdl(ms.tags(), "@OpSwagger(tags) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("schemes", |
| merge( |
| parseListOrCdl(mb.findFirstString(mn + ".schemes"), "Messages/schemes on class {0} method {1}", c, m), |
| parseListOrCdl(ms.schemes(), "@OpSwagger(schemes) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("consumes", |
| firstNonEmpty( |
| parseListOrCdl(mb.findFirstString(mn + ".consumes"), "Messages/consumes on class {0} method {1}", c, m), |
| parseListOrCdl(ms.consumes(), "@OpSwagger(consumes) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("produces", |
| firstNonEmpty( |
| parseListOrCdl(mb.findFirstString(mn + ".produces"), "Messages/produces on class {0} method {1}", c, m), |
| parseListOrCdl(ms.produces(), "@OpSwagger(produces) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("parameters", |
| merge( |
| parseList(mb.findFirstString(mn + ".parameters"), "Messages/parameters on class {0} method {1}", c, m), |
| parseList(ms.parameters(), "@OpSwagger(parameters) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("responses", |
| merge( |
| parseMap(mb.findFirstString(mn + ".responses"), "Messages/responses on class {0} method {1}", c, m), |
| parseMap(ms.responses(), "@OpSwagger(responses) on class {0} method {1}", c, m) |
| ) |
| ); |
| op.appendSkipEmpty("externalDocs", |
| merge( |
| op.getMap("externalDocs"), |
| parseMap(mb.findFirstString(mn + ".externalDocs"), "Messages/externalDocs on class {0} method {1}", c, m), |
| toMap(ms.externalDocs(), "@OpSwagger(externalDocs) on class {0} method {1}", c, m) |
| ) |
| ); |
| |
| if (op.containsKey("tags")) |
| for (String tag : op.getList("tags").elements(String.class)) |
| if (! tagMap.containsKey(tag)) |
| tagMap.put(tag, OMap.of("name", tag)); |
| |
| OMap paramMap = new OMap(); |
| if (op.containsKey("parameters")) |
| for (OMap param : op.getList("parameters").elements(OMap.class)) |
| paramMap.put(param.getString("in") + '.' + ("body".equals(param.getString("in")) ? "body" : param.getString("name")), param); |
| |
| // Finally, look for parameters defined on method. |
| for (ParamInfo mpi : mi.getParams()) { |
| |
| ClassInfo pt = mpi.getParameterType(); |
| Type type = pt.innerType(); |
| |
| if (mpi.hasAnnotation(Body.class) || pt.hasAnnotation(Body.class)) { |
| OMap param = paramMap.getMap(BODY + ".body", true).a("in", BODY); |
| for (Body a : mpi.getAnnotations(Body.class)) |
| merge(param, a); |
| for (Body a : pt.getAnnotations(Body.class)) |
| merge(param, a); |
| param.putIfAbsent("required", true); |
| param.appendSkipEmpty("schema", getSchema(param.getMap("schema"), type, bs)); |
| addBodyExamples(sm, param, false, type, locale); |
| |
| } else if (mpi.hasAnnotation(Query.class) || pt.hasAnnotation(Query.class)) { |
| String name = null; |
| for (Query a : mpi.getAnnotations(Query.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| for (Query a : pt.getAnnotations(Query.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| OMap param = paramMap.getMap(QUERY + "." + name, true).a("name", name).a("in", QUERY); |
| for (Query a : mpi.getAnnotations(Query.class)) |
| merge(param, a); |
| for (Query a : pt.getAnnotations(Query.class)) |
| merge(param, a); |
| mergePartSchema(param, getSchema(param.getMap("schema"), type, bs)); |
| addParamExample(sm, param, QUERY, type); |
| |
| } else if (mpi.hasAnnotation(FormData.class) || pt.hasAnnotation(FormData.class)) { |
| String name = null; |
| for (FormData a : mpi.getAnnotations(FormData.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| for (FormData a : pt.getAnnotations(FormData.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| OMap param = paramMap.getMap(FORM_DATA + "." + name, true).a("name", name).a("in", FORM_DATA); |
| for (FormData a : mpi.getAnnotations(FormData.class)) |
| merge(param, a); |
| for (FormData a : pt.getAnnotations(FormData.class)) |
| merge(param, a); |
| mergePartSchema(param, getSchema(param.getMap("schema"), type, bs)); |
| addParamExample(sm, param, FORM_DATA, type); |
| |
| } else if (mpi.hasAnnotation(Header.class) || pt.hasAnnotation(Header.class)) { |
| String name = null; |
| for (Header a : mpi.getAnnotations(Header.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| for (Header a : pt.getAnnotations(Header.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| OMap param = paramMap.getMap(HEADER + "." + name, true).a("name", name).a("in", HEADER); |
| for (Header a : mpi.getAnnotations(Header.class)) |
| merge(param, a); |
| for (Header a : pt.getAnnotations(Header.class)) |
| merge(param, a); |
| mergePartSchema(param, getSchema(param.getMap("schema"), type, bs)); |
| addParamExample(sm, param, HEADER, type); |
| |
| } else if (mpi.hasAnnotation(Path.class) || pt.hasAnnotation(Path.class)) { |
| String name = null; |
| for (Path a : mpi.getAnnotations(Path.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| for (Path a : pt.getAnnotations(Path.class)) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| OMap param = paramMap.getMap(PATH + "." + name, true).a("name", name).a("in", PATH); |
| for (Path a : mpi.getAnnotations(Path.class)) |
| merge(param, a); |
| for (Path a : pt.getAnnotations(Path.class)) |
| merge(param, a); |
| mergePartSchema(param, getSchema(param.getMap("schema"), type, bs)); |
| addParamExample(sm, param, PATH, type); |
| param.putIfAbsent("required", true); |
| } |
| } |
| |
| if (! paramMap.isEmpty()) |
| op.put("parameters", paramMap.values()); |
| |
| OMap responses = op.getMap("responses", true); |
| |
| for (ClassInfo eci : mi.getExceptionTypes()) { |
| if (eci.hasAnnotation(Response.class)) { |
| List<Response> la = eci.getAnnotations(Response.class); |
| Set<Integer> codes = getCodes(la, 500); |
| for (Response a : la) { |
| for (Integer code : codes) { |
| OMap om = responses.getMap(String.valueOf(code), true); |
| merge(om, a); |
| if (! om.containsKey("schema")) |
| om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), eci.inner(), bs)); |
| } |
| } |
| for (MethodInfo ecmi : eci.getAllMethodsParentFirst()) { |
| ResponseHeader a = ecmi.getLastAnnotation(ResponseHeader.class); |
| if (a == null) |
| a = ecmi.getReturnType().unwrap(Value.class,Optional.class).getLastAnnotation(ResponseHeader.class); |
| if (a != null && ! isMulti(a)) { |
| String ha = a.name(); |
| for (Integer code : codes) { |
| OMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true); |
| merge(header, a); |
| mergePartSchema(header, getSchema(header, ecmi.getReturnType().innerType(), bs)); |
| } |
| } |
| } |
| } |
| } |
| |
| if (mi.hasAnnotation(Response.class) || mi.getReturnType().unwrap(Value.class,Optional.class).hasAnnotation(Response.class)) { |
| List<Response> la = mi.getAnnotations(Response.class); |
| Set<Integer> codes = getCodes(la, 200); |
| for (Response a : la) { |
| for (Integer code : codes) { |
| OMap om = responses.getMap(String.valueOf(code), true); |
| merge(om, a); |
| if (! om.containsKey("schema")) |
| om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), m.getGenericReturnType(), bs)); |
| addBodyExamples(sm, om, true, m.getGenericReturnType(), locale); |
| } |
| } |
| if (mi.getReturnType().hasAnnotation(Response.class)) { |
| for (MethodInfo ecmi : mi.getReturnType().getAllMethodsParentFirst()) { |
| if (ecmi.hasAnnotation(ResponseHeader.class)) { |
| ResponseHeader a = ecmi.getLastAnnotation(ResponseHeader.class); |
| String ha = a.name(); |
| if (! isMulti(a)) { |
| for (Integer code : codes) { |
| OMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true); |
| merge(header, a); |
| mergePartSchema(header, getSchema(header, ecmi.getReturnType().innerType(), bs)); |
| } |
| } |
| } |
| } |
| } |
| } else if (m.getGenericReturnType() != void.class) { |
| OMap om = responses.getMap("200", true); |
| if (! om.containsKey("schema")) |
| om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), m.getGenericReturnType(), bs)); |
| addBodyExamples(sm, om, true, m.getGenericReturnType(), locale); |
| } |
| |
| // Finally, look for @ResponseHeader parameters defined on method. |
| for (ParamInfo mpi : mi.getParams()) { |
| |
| ClassInfo pt = mpi.getParameterType(); |
| |
| if (mpi.hasAnnotation(ResponseHeader.class) || pt.hasAnnotation(ResponseHeader.class)) { |
| List<ResponseHeader> la = AList.of(mpi.getAnnotations(ResponseHeader.class)).a(pt.getAnnotations(ResponseHeader.class)); |
| Set<Integer> codes = getCodes2(la, 200); |
| String name = null; |
| for (ResponseHeader a : la) |
| name = firstNonEmpty(a.name(), a.n(), a.value(), name); |
| Type type = mpi.getParameterType().innerType(); |
| for (ResponseHeader a : la) { |
| if (! isMulti(a)) { |
| for (Integer code : codes) { |
| OMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(name, true); |
| merge(header, a); |
| mergePartSchema(header, getSchema(header, Value.getParameterType(type), bs)); |
| } |
| } |
| } |
| |
| } else if (mpi.hasAnnotation(Response.class) || pt.hasAnnotation(Response.class)) { |
| List<Response> la = AList.of(mpi.getAnnotations(Response.class)).a(pt.getAnnotations(Response.class)); |
| Set<Integer> codes = getCodes(la, 200); |
| Type type = mpi.getParameterType().innerType(); |
| for (Response a : la) { |
| for (Integer code : codes) { |
| OMap response = responses.getMap(String.valueOf(code), true); |
| merge(response, a); |
| } |
| } |
| type = Value.getParameterType(type); |
| if (type != null) { |
| for (String code : responses.keySet()) { |
| OMap om = responses.getMap(code); |
| if (! om.containsKey("schema")) |
| om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), type, bs)); |
| } |
| } |
| } |
| } |
| |
| // Add default response descriptions. |
| for (Map.Entry<String,Object> e : responses.entrySet()) { |
| String key = e.getKey(); |
| OMap val = responses.getMap(key); |
| if (StringUtils.isDecimal(key)) |
| val.appendIf(false, true, true, "description", RestUtils.getHttpResponseText(Integer.parseInt(key))); |
| } |
| |
| if (responses.isEmpty()) |
| op.remove("responses"); |
| else |
| op.put("responses", new TreeMap<>(responses)); |
| |
| if (! op.containsKey("consumes")) { |
| List<MediaType> mConsumes = sm.getSupportedContentTypes(); |
| if (! mConsumes.equals(consumes)) |
| op.put("consumes", mConsumes); |
| } |
| |
| if (! op.containsKey("produces")) { |
| List<MediaType> mProduces = sm.getSupportedAcceptTypes(); |
| if (! mProduces.equals(produces)) |
| op.put("produces", mProduces); |
| } |
| } |
| |
| if (js.getBeanDefs() != null) |
| for (Map.Entry<String,OMap> e : js.getBeanDefs().entrySet()) |
| definitions.put(e.getKey(), fixSwaggerExtensions(e.getValue())); |
| if (definitions.isEmpty()) |
| omSwagger.remove("definitions"); |
| |
| if (! tagMap.isEmpty()) |
| omSwagger.put("tags", tagMap.values()); |
| |
| if (consumes.isEmpty()) |
| omSwagger.remove("consumes"); |
| if (produces.isEmpty()) |
| omSwagger.remove("produces"); |
| |
| // try { |
| // if (! omSwagger.isEmpty()) |
| // assertNoEmpties(omSwagger); |
| // } catch (SwaggerException e1) { |
| // System.err.println(omSwagger.toString(SimpleJsonSerializer.DEFAULT_READABLE)); |
| // throw e1; |
| // } |
| |
| try { |
| String swaggerJson = SimpleJsonSerializer.DEFAULT_READABLE.toString(omSwagger); |
| // System.err.println(swaggerJson); |
| return jp.parse(swaggerJson, Swagger.class); |
| } catch (Exception e) { |
| throw new RestServletException(e, "Error detected in swagger."); |
| } |
| } |
| //================================================================================================================= |
| // Utility methods |
| //================================================================================================================= |
| |
| private boolean isMulti(ResponseHeader h) { |
| if ("*".equals(h.name()) || "*".equals(h.value())) |
| return true; |
| return false; |
| } |
| |
| private OMap resolve(OMap om) throws ParseException { |
| OMap om2 = null; |
| if (om.containsKey("_value")) { |
| om = om.modifiable(); |
| om2 = parseMap(om.remove("_value")); |
| } else { |
| om2 = new OMap(); |
| } |
| for (Map.Entry<String,Object> e : om.entrySet()) { |
| Object val = e.getValue(); |
| if (val instanceof OMap) { |
| val = resolve((OMap)val); |
| } else if (val instanceof OList) { |
| val = resolve((OList) val); |
| } else if (val instanceof String) { |
| val = resolve(val.toString()); |
| } |
| om2.put(e.getKey(), val); |
| } |
| return om2; |
| } |
| |
| private OList resolve(OList om) throws ParseException { |
| OList ol2 = new OList(); |
| for (Object val : om) { |
| if (val instanceof OMap) { |
| val = resolve((OMap)val); |
| } else if (val instanceof OList) { |
| val = resolve((OList) val); |
| } else if (val instanceof String) { |
| val = resolve(val.toString()); |
| } |
| ol2.add(val); |
| } |
| return ol2; |
| } |
| |
| private String resolve(String[]...s) { |
| for (String[] ss : s) { |
| if (ss.length != 0) |
| return resolve(joinnl(ss)); |
| } |
| return null; |
| } |
| |
| private String resolve(String s) { |
| if (s == null) |
| return null; |
| return vr.resolve(s.trim()); |
| } |
| |
| private OMap parseMap(String[] o, String location, Object...args) throws ParseException { |
| if (o.length == 0) |
| return OMap.EMPTY_MAP; |
| try { |
| return parseMap(o); |
| } catch (ParseException e) { |
| throw new SwaggerException(e, "Malformed swagger JSON object encountered in " + location + ".", args); |
| } |
| } |
| |
| private OMap parseMap(String o, String location, Object...args) throws ParseException { |
| try { |
| return parseMap(o); |
| } catch (ParseException e) { |
| throw new SwaggerException(e, "Malformed swagger JSON object encountered in " + location + ".", args); |
| } |
| } |
| |
| private OMap parseMap(Object o) throws ParseException { |
| if (o == null) |
| return null; |
| if (o instanceof String[]) |
| o = joinnl((String[])o); |
| if (o instanceof String) { |
| String s = o.toString(); |
| if (s.isEmpty()) |
| return null; |
| s = resolve(s); |
| if ("IGNORE".equalsIgnoreCase(s)) |
| return OMap.of("ignore", true); |
| if (! isJsonObject(s, true)) |
| s = "{" + s + "}"; |
| return OMap.ofJson(s); |
| } |
| if (o instanceof OMap) |
| return (OMap)o; |
| throw new SwaggerException(null, "Unexpected data type ''{0}''. Expected OMap or String.", className(o)); |
| } |
| |
| private OList parseList(Object o, String location, Object...locationArgs) throws ParseException { |
| try { |
| if (o == null) |
| return null; |
| String s = (o instanceof String[] ? joinnl((String[])o) : o.toString()); |
| if (s.isEmpty()) |
| return null; |
| s = resolve(s); |
| if (! isJsonArray(s, true)) |
| s = "[" + s + "]"; |
| return OList.ofJson(s); |
| } catch (ParseException e) { |
| throw new SwaggerException(e, "Malformed swagger JSON array encountered in "+location+".", locationArgs); |
| } |
| } |
| |
| private OList parseListOrCdl(Object o, String location, Object...locationArgs) throws ParseException { |
| try { |
| if (o == null) |
| return null; |
| String s = (o instanceof String[] ? joinnl((String[])o) : o.toString()); |
| if (s.isEmpty()) |
| return null; |
| s = resolve(s); |
| return StringUtils.parseListOrCdl(s); |
| } catch (ParseException e) { |
| throw new SwaggerException(e, "Malformed swagger JSON array encountered in "+location+".", locationArgs); |
| } |
| } |
| |
| private OMap newMap(OMap om, String[] value, String location, Object...locationArgs) throws ParseException { |
| if (value.length == 0) |
| return om == null ? new OMap() : om; |
| OMap om2 = parseMap(joinnl(value), location, locationArgs); |
| if (om == null) |
| return om2; |
| return om.append(om2); |
| } |
| |
| private OMap merge(OMap...maps) { |
| OMap m = maps[0]; |
| for (int i = 1; i < maps.length; i++) { |
| if (maps[i] != null) { |
| if (m == null) |
| m = new OMap(); |
| m.putAll(maps[i]); |
| } |
| } |
| return m; |
| } |
| |
| private OList merge(OList...lists) { |
| OList l = lists[0]; |
| for (int i = 1; i < lists.length; i++) { |
| if (lists[i] != null) { |
| if (l == null) |
| l = new OList(); |
| l.addAll(lists[i]); |
| } |
| } |
| return l; |
| } |
| |
| @SafeVarargs |
| private final <T> T firstNonEmpty(T...t) { |
| return ObjectUtils.firstNonEmpty(t); |
| } |
| |
| private OMap toMap(ExternalDocs a, String location, Object...locationArgs) throws ParseException { |
| if (ExternalDocsAnnotation.empty(a)) |
| return null; |
| OMap om = newMap(new OMap(), a.value(), location, locationArgs) |
| .appendSkipEmpty("description", resolve(joinnl(a.description()))) |
| .appendSkipEmpty("url", resolve(a.url())); |
| return nullIfEmpty(om); |
| } |
| |
| private OMap toMap(Contact a, String location, Object...locationArgs) throws ParseException { |
| if (ContactAnnotation.empty(a)) |
| return null; |
| OMap om = newMap(new OMap(), a.value(), location, locationArgs) |
| .appendSkipEmpty("name", resolve(a.name())) |
| .appendSkipEmpty("url", resolve(a.url())) |
| .appendSkipEmpty("email", resolve(a.email())); |
| return nullIfEmpty(om); |
| } |
| |
| private OMap toMap(License a, String location, Object...locationArgs) throws ParseException { |
| if (LicenseAnnotation.empty(a)) |
| return null; |
| OMap om = newMap(new OMap(), a.value(), location, locationArgs) |
| .appendSkipEmpty("name", resolve(a.name())) |
| .appendSkipEmpty("url", resolve(a.url())); |
| return nullIfEmpty(om); |
| } |
| |
| private OMap toMap(Tag a, String location, Object...locationArgs) throws ParseException { |
| OMap om = newMap(new OMap(), a.value(), location, locationArgs); |
| om |
| .appendSkipEmpty("name", resolve(a.name())) |
| .appendSkipEmpty("description", resolve(joinnl(a.description()))) |
| .appendSkipNull("externalDocs", merge(om.getMap("externalDocs"), toMap(a.externalDocs(), location, locationArgs))); |
| return nullIfEmpty(om); |
| } |
| |
| private OList toList(Tag[] aa, String location, Object...locationArgs) throws ParseException { |
| if (aa.length == 0) |
| return null; |
| OList ol = new OList(); |
| for (Tag a : aa) |
| ol.add(toMap(a, location, locationArgs)); |
| return nullIfEmpty(ol); |
| } |
| |
| private OMap getSchema(OMap schema, Type type, BeanSession bs) throws Exception { |
| |
| if (type == Swagger.class) |
| return null; |
| |
| schema = newMap(schema); |
| |
| ClassMeta<?> cm = bs.getClassMeta(type); |
| |
| if (schema.getBoolean("ignore", false)) |
| return null; |
| |
| if (schema.containsKey("type") || schema.containsKey("$ref")) |
| return schema; |
| |
| OMap om = fixSwaggerExtensions(schema.append(js.getSchema(cm))); |
| |
| return nullIfEmpty(om); |
| } |
| |
| /** |
| * Replaces non-standard JSON-Schema attributes with standard Swagger attributes. |
| */ |
| private OMap fixSwaggerExtensions(OMap om) { |
| om |
| .appendSkipNull("discriminator", om.remove("x-discriminator")) |
| .appendSkipNull("readOnly", om.remove("x-readOnly")) |
| .appendSkipNull("xml", om.remove("x-xml")) |
| .appendSkipNull("externalDocs", om.remove("x-externalDocs")) |
| .appendSkipNull("example", om.remove("x-example")); |
| return nullIfEmpty(om); |
| } |
| |
| private void addBodyExamples(RestOperationContext sm, OMap piri, boolean response, Type type, Locale locale) throws Exception { |
| |
| String sex = piri.getString("example"); |
| |
| if (sex == null) { |
| OMap schema = resolveRef(piri.getMap("schema")); |
| if (schema != null) |
| sex = schema.getString("example", schema.getString("example")); |
| } |
| |
| if (isEmpty(sex)) |
| return; |
| |
| Object example = null; |
| if (isJson(sex)) { |
| example = jp.parse(sex, type); |
| } else { |
| ClassMeta<?> cm = js.getClassMeta(type); |
| if (cm.hasStringMutater()) { |
| example = cm.getStringMutater().mutate(sex); |
| } |
| } |
| |
| String examplesKey = "examples"; // Parameters don't have an examples attribute. |
| |
| OMap examples = piri.getMap(examplesKey); |
| if (examples == null) |
| examples = new OMap(); |
| |
| List<MediaType> mediaTypes = response ? sm.getSerializers().getSupportedMediaTypes() : sm.getParsers().getSupportedMediaTypes(); |
| |
| for (MediaType mt : mediaTypes) { |
| if (mt != MediaType.HTML) { |
| Serializer s2 = sm.getSerializers().getSerializer(mt); |
| if (s2 != null) { |
| SerializerSessionArgs args = |
| SerializerSessionArgs |
| .create() |
| .locale(locale) |
| .mediaType(mt) |
| .useWhitespace(true) |
| ; |
| try { |
| String eVal = s2.createSession(args).serializeToString(example); |
| examples.put(s2.getPrimaryMediaType().toString(), eVal); |
| } catch (Exception e) { |
| System.err.println("Could not serialize to media type ["+mt+"]: " + e.getLocalizedMessage()); // NOT DEBUG |
| } |
| } |
| } |
| } |
| |
| if (! examples.isEmpty()) |
| piri.put(examplesKey, examples); |
| } |
| |
| private void addParamExample(RestOperationContext sm, OMap piri, RestParamType in, Type type) throws Exception { |
| |
| String s = piri.getString("example"); |
| |
| if (isEmpty(s)) |
| return; |
| |
| OMap examples = piri.getMap("examples"); |
| if (examples == null) |
| examples = new OMap(); |
| |
| String paramName = piri.getString("name"); |
| |
| if (in == QUERY) |
| s = "?" + urlEncodeLax(paramName) + "=" + urlEncodeLax(s); |
| else if (in == FORM_DATA) |
| s = paramName + "=" + s; |
| else if (in == HEADER) |
| s = paramName + ": " + s; |
| else if (in == PATH) |
| s = sm.getPathPattern().replace("{"+paramName+"}", urlEncodeLax(s)); |
| |
| examples.put("example", s); |
| |
| if (! examples.isEmpty()) |
| piri.put("examples", examples); |
| } |
| |
| |
| private OMap resolveRef(OMap m) { |
| if (m == null) |
| return null; |
| if (m.containsKey("$ref") && js.getBeanDefs() != null) { |
| String ref = m.getString("$ref"); |
| if (ref.startsWith("#/definitions/")) |
| return js.getBeanDefs().get(ref.substring(14)); |
| } |
| return m; |
| } |
| |
| private OMap getOperation(OMap om, String path, String httpMethod) { |
| if (! om.containsKey("paths")) |
| om.put("paths", new OMap()); |
| om = om.getMap("paths"); |
| if (! om.containsKey(path)) |
| om.put(path, new OMap()); |
| om = om.getMap(path); |
| if (! om.containsKey(httpMethod)) |
| om.put(httpMethod, new OMap()); |
| return om.getMap(httpMethod); |
| } |
| |
| private static OMap newMap(OMap om) { |
| if (om == null) |
| return new OMap(); |
| return om.modifiable(); |
| } |
| |
| private OMap merge(OMap om, Body a) throws ParseException { |
| if (BodyAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.value().length > 0) |
| om.putAll(parseMap(a.value())); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipEmpty("examples", parseMap(a.examples()), parseMap(a.exs())) |
| .appendSkipFalse("required", a.required() || a.r()) |
| .appendSkipEmpty("schema", merge(om.getMap("schema"), a.schema())) |
| ; |
| } |
| |
| private OMap merge(OMap om, Query a) throws ParseException { |
| if (QueryAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipFalse("allowEmptyValue", a.allowEmptyValue() || a.aev()) |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipFalse("required", a.required() || a.r()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| ; |
| } |
| |
| private OMap merge(OMap om, FormData a) throws ParseException { |
| if (FormDataAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipFalse("allowEmptyValue", a.allowEmptyValue() || a.aev()) |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipFalse("required", a.required()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| ; |
| } |
| |
| private OMap merge(OMap om, Header a) throws ParseException { |
| if (HeaderAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipFalse("required", a.required() || a.r()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| ; |
| } |
| |
| private OMap merge(OMap om, Path a) throws ParseException { |
| if (PathAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| ; |
| } |
| |
| private OMap merge(OMap om, Schema a) throws ParseException { |
| if (SchemaAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.value().length > 0) |
| om.putAll(parseMap(a.value())); |
| return om |
| .appendSkipEmpty("additionalProperties", toOMap(a.additionalProperties())) |
| .appendSkipEmpty("allOf", joinnl(a.allOf())) |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("discriminator", a.discriminator()) |
| .appendSkipEmpty("description", resolve(a.description()), resolve(a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example()), resolve(a.ex())) |
| .appendSkipEmpty("examples", parseMap(a.examples()), parseMap(a.exs())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("externalDocs", merge(om.getMap("externalDocs"), a.externalDocs())) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("ignore", a.ignore() ? "true" : null) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipMinusOne("maxProperties", a.maxProperties(), a.maxp()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipMinusOne("minProperties", a.minProperties(), a.minp()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipEmpty("properties", toOMap(a.properties())) |
| .appendSkipFalse("readOnly", a.readOnly() || a.ro()) |
| .appendSkipFalse("required", a.required() || a.r()) |
| .appendSkipEmpty("title", a.title()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| .appendSkipEmpty("xml", joinnl(a.xml())) |
| .appendSkipEmpty("$ref", a.$ref()) |
| ; |
| } |
| |
| private OMap merge(OMap om, ExternalDocs a) throws ParseException { |
| if (ExternalDocsAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.value().length > 0) |
| om.putAll(parseMap(a.value())); |
| return om |
| .appendSkipEmpty("description", resolve(a.description())) |
| .appendSkipEmpty("url", a.url()) |
| ; |
| } |
| |
| private OMap merge(OMap om, Items a) throws ParseException { |
| if (ItemsAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.value().length > 0) |
| om.putAll(parseMap(a.value())); |
| return om |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipEmpty("$ref", a.$ref()) |
| ; |
| } |
| |
| private OMap merge(OMap om, SubItems a) throws ParseException { |
| if (SubItemsAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.value().length > 0) |
| om.putAll(parseMap(a.value())); |
| return om |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", toOMap(a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| .appendSkipEmpty("$ref", a.$ref()) |
| ; |
| } |
| |
| private OMap merge(OMap om, Response a) throws ParseException { |
| if (ResponseAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipEmpty("examples", parseMap(a.examples()), parseMap(a.exs())) |
| .appendSkipEmpty("headers", merge(om.getMap("headers"), a.headers())) |
| .appendSkipEmpty("schema", merge(om.getMap("schema"), a.schema())) |
| ; |
| } |
| |
| private OMap merge(OMap om, ResponseHeader[] a) throws ParseException { |
| if (a.length == 0) |
| return om; |
| om = newMap(om); |
| for (ResponseHeader aa : a) { |
| String name = StringUtils.firstNonEmpty(aa.name(), aa.value()); |
| if (isEmpty(name)) |
| throw runtimeException("@ResponseHeader used without name or value."); |
| om.getMap(name, true).putAll(merge(null, aa)); |
| } |
| return om; |
| } |
| |
| private OMap merge(OMap om, ResponseHeader a) throws ParseException { |
| if (ResponseHeaderAnnotation.empty(a)) |
| return om; |
| om = newMap(om); |
| if (a.api().length > 0) |
| om.putAll(parseMap(a.api())); |
| return om |
| .appendSkipEmpty("collectionFormat", a.collectionFormat(), a.cf()) |
| .appendSkipEmpty("default", joinnl(a._default(), a.df())) |
| .appendSkipEmpty("description", resolve(a.description(), a.d())) |
| .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e())) |
| .appendSkipEmpty("example", resolve(a.example(), a.ex())) |
| .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum() || a.emax()) |
| .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum() || a.emin()) |
| .appendSkipEmpty("format", a.format(), a.f()) |
| .appendSkipEmpty("items", merge(om.getMap("items"), a.items())) |
| .appendSkipEmpty("maximum", a.maximum(), a.max()) |
| .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi()) |
| .appendSkipMinusOne("maxLength", a.maxLength(), a.maxl()) |
| .appendSkipEmpty("minimum", a.minimum(), a.min()) |
| .appendSkipMinusOne("minItems", a.minItems(), a.mini()) |
| .appendSkipMinusOne("minLength", a.minLength(), a.minl()) |
| .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo()) |
| .appendSkipEmpty("pattern", a.pattern(), a.p()) |
| .appendSkipEmpty("type", a.type(), a.t()) |
| .appendSkipFalse("uniqueItems", a.uniqueItems() || a.ui()) |
| .appendSkipEmpty("$ref", a.$ref()) |
| ; |
| } |
| |
| private OMap mergePartSchema(OMap param, OMap schema) { |
| if (schema != null) { |
| param |
| .appendIf(false, true, true, "collectionFormat", schema.remove("collectionFormat")) |
| .appendIf(false, true, true, "default", schema.remove("default")) |
| .appendIf(false, true, true, "description", schema.remove("enum")) |
| .appendIf(false, true, true, "enum", schema.remove("enum")) |
| .appendIf(false, true, true, "example", schema.remove("example")) |
| .appendIf(false, true, true, "exclusiveMaximum", schema.remove("exclusiveMaximum")) |
| .appendIf(false, true, true, "exclusiveMinimum", schema.remove("exclusiveMinimum")) |
| .appendIf(false, true, true, "format", schema.remove("format")) |
| .appendIf(false, true, true, "items", schema.remove("items")) |
| .appendIf(false, true, true, "maximum", schema.remove("maximum")) |
| .appendIf(false, true, true, "maxItems", schema.remove("maxItems")) |
| .appendIf(false, true, true, "maxLength", schema.remove("maxLength")) |
| .appendIf(false, true, true, "minimum", schema.remove("minimum")) |
| .appendIf(false, true, true, "minItems", schema.remove("minItems")) |
| .appendIf(false, true, true, "minLength", schema.remove("minLength")) |
| .appendIf(false, true, true, "multipleOf", schema.remove("multipleOf")) |
| .appendIf(false, true, true, "pattern", schema.remove("pattern")) |
| .appendIf(false, true, true, "required", schema.remove("required")) |
| .appendIf(false, true, true, "type", schema.remove("type")) |
| .appendIf(false, true, true, "uniqueItems", schema.remove("uniqueItems")); |
| |
| if ("object".equals(param.getString("type")) && ! schema.isEmpty()) |
| param.put("schema", schema); |
| } |
| |
| return param; |
| } |
| |
| |
| |
| private OMap toOMap(String[] ss) throws ParseException { |
| if (ss.length == 0) |
| return null; |
| String s = joinnl(ss); |
| if (s.isEmpty()) |
| return null; |
| if (! isJsonObject(s, true)) |
| s = "{" + s + "}"; |
| s = resolve(s); |
| return OMap.ofJson(s); |
| } |
| |
| private Set<String> toSet(String[] ss) throws ParseException { |
| if (ss.length == 0) |
| return null; |
| String s = joinnl(ss); |
| if (s.isEmpty()) |
| return null; |
| s = resolve(s); |
| Set<String> set = ASet.of(); |
| for (Object o : StringUtils.parseListOrCdl(s)) |
| set.add(o.toString()); |
| return set; |
| } |
| |
| static String joinnl(String[]...s) { |
| for (String[] ss : s) { |
| if (ss.length != 0) |
| return StringUtils.joinnl(ss).trim(); |
| } |
| return ""; |
| } |
| |
| private static Set<Integer> getCodes(List<Response> la, Integer def) { |
| Set<Integer> codes = new TreeSet<>(); |
| for (Response a : la) { |
| for (int i : a.value()) |
| codes.add(i); |
| for (int i : a.code()) |
| codes.add(i); |
| } |
| if (codes.isEmpty() && def != null) |
| codes.add(def); |
| return codes; |
| } |
| |
| private static Set<Integer> getCodes2(List<ResponseHeader> la, Integer def) { |
| Set<Integer> codes = new TreeSet<>(); |
| for (ResponseHeader a : la) { |
| for (int i : a.code()) |
| codes.add(i); |
| } |
| if (codes.isEmpty() && def != null) |
| codes.add(def); |
| return codes; |
| } |
| |
| private static OMap nullIfEmpty(OMap m) { |
| return (m == null || m.isEmpty() ? null : m); |
| } |
| |
| private static OList nullIfEmpty(OList l) { |
| return (l == null || l.isEmpty() ? null : l); |
| } |
| } |