blob: d6744f636f91b7deb0622ecd7804f8011075e090 [file] [log] [blame]
// ***************************************************************************************************************************
// * 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);
}
}