public Swagger getSwagger()

in juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java [96:568]


	public Swagger getSwagger() throws Exception {

		InputStream is = ff.getStream(rci.getSimpleName() + ".json", locale).orElse(null);

		Predicate<String> ne = StringUtils::isNotEmpty;
		Predicate<Collection<?>> nec = CollectionUtils::isNotEmpty;
		Predicate<Map<?,?>> nem = CollectionUtils::isNotEmpty;

		// Load swagger JSON from classpath.
		JsonMap omSwagger = Json5.DEFAULT.read(is, JsonMap.class);
		if (omSwagger == null)
			omSwagger = new JsonMap();

		// Combine it with @Rest(swagger)
		for (Rest rr : rci.getAnnotations(context, Rest.class)) {

			JsonMap sInfo = omSwagger.getMap("info", true);

			sInfo
				.appendIf(ne, "title",
					firstNonEmpty(
						sInfo.getString("title"),
						resolve(rr.title())
					)
				)
				.appendIf(ne, "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)) {
				JsonMap info = omSwagger.getMap("info", true);

				info
					.appendIf(ne, "title", resolve(r.title()))
					.appendIf(ne, "description", resolve(r.description()))
					.appendIf(ne, "version", resolve(r.version()))
					.appendIf(ne, "termsOfService", resolve(r.termsOfService()))
					.appendIf(nem, "contact",
						merge(
							info.getMap("contact"),
							toMap(r.contact(), "@Swagger(contact) on class {0}", c)
						)
					)
					.appendIf(nem, "license",
						merge(
							info.getMap("license"),
							toMap(r.license(), "@Swagger(license) on class {0}", c)
						)
					);
			}

			omSwagger
				.appendIf(nem, "externalDocs",
					merge(
						omSwagger.getMap("externalDocs"),
						toMap(r.externalDocs(), "@Swagger(externalDocs) on class {0}", c)
					)
				)
				.appendIf(nec, "tags",
					merge(
						omSwagger.getList("tags"),
						toList(r.tags(), "@Swagger(tags) on class {0}", c)
					)
				);
		}

		omSwagger.appendIf(nem, "externalDocs", parseMap(mb.findFirstString("externalDocs"), "Messages/externalDocs on class {0}", c));

		JsonMap info = omSwagger.getMap("info", true);

		info
			.appendIf(ne, "title", resolve(mb.findFirstString("title")))
			.appendIf(ne, "description", resolve(mb.findFirstString("description")))
			.appendIf(ne, "version", resolve(mb.findFirstString("version")))
			.appendIf(ne, "termsOfService", resolve(mb.findFirstString("termsOfService")))
			.appendIf(nem, "contact", parseMap(mb.findFirstString("contact"), "Messages/contact on class {0}", c))
			.appendIf(nem, "license", parseMap(mb.findFirstString("license"), "Messages/license on class {0}", c));

		if (info.isEmpty())
			omSwagger.remove("info");

		JsonList
			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,JsonMap> tagMap = map();
		if (omSwagger.containsKey("tags")) {
			for (JsonMap om : omSwagger.getList("tags").elements(JsonMap.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 (JsonMap m : parseListOrCdl(s, "Messages/tags on class {0}", c).elements(JsonMap.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.
		JsonMap definitions = omSwagger.getMap("definitions", true);
		for (String defId : definitions.keySet())
			js.addBeanDef(defId, new JsonMap(definitions.getMap(defId)));

		// Iterate through all the @RestOp methods.
		for (RestOpContext sm : context.getRestOperations().getOpContexts()) {

			BeanSession bs = sm.getBeanContext().getSession();

			Method m = sm.getJavaMethod();
			MethodInfo mi = MethodInfo.of(m);
			AnnotationList al = mi.getAnnotationList(REST_OP_GROUP);
			String mn = m.getName();

			// Get the operation from the existing swagger so far.
			JsonMap op = getOperation(omSwagger, sm.getPathPattern(), sm.getHttpMethod().toLowerCase());

			// Add @RestOp(swagger)
			Value<OpSwagger> _ms = Value.empty();
			al.forEachValue(OpSwagger.class, "swagger", OpSwaggerAnnotation::notEmpty, x -> _ms.set(x));
			OpSwagger ms = _ms.orElseGet(()->OpSwaggerAnnotation.create().build());

			op.append(parseMap(ms.value(), "@OpSwagger(value) on class {0} method {1}", c, m));
			op.appendIf(ne, "operationId",
				firstNonEmpty(
					resolve(ms.operationId()),
					op.getString("operationId"),
					mn
				)
			);

			Value<String> _summary = Value.empty();
			al.forEachValue(String.class, "summary", NOT_EMPTY, x -> _summary.set(x));
			op.appendIf(ne, "summary",
				firstNonEmpty(
					resolve(ms.summary()),
					resolve(mb.findFirstString(mn + ".summary")),
					op.getString("summary"),
					resolve(_summary.orElse(null))
				)
			);

			Value<String[]> _description = Value.empty();
			al.forEachValue(String[].class, "description",x -> x.length > 0, x -> _description.set(x));
			op.appendIf(ne, "description",
				firstNonEmpty(
					resolve(ms.description()),
					resolve(mb.findFirstString(mn + ".description")),
					op.getString("description"),
					resolve(_description.orElse(new String[0]))
				)
			);
			op.appendIf(ne, "deprecated",
				firstNonEmpty(
					resolve(ms.deprecated()),
					(m.getAnnotation(Deprecated.class) != null || m.getDeclaringClass().getAnnotation(Deprecated.class) != null) ? "true" : null
				)
			);
			op.appendIf(nec, "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.appendIf(nec, "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.appendIf(nec, "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.appendIf(nec, "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.appendIf(nec, "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.appendIf(nem, "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.appendIf(nem, "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, JsonMap.of("name", tag));

			JsonMap paramMap = new JsonMap();
			if (op.containsKey("parameters"))
				for (JsonMap param : op.getList("parameters").elements(JsonMap.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(Content.class) || pt.hasAnnotation(Content.class)) {
					JsonMap param = paramMap.getMap(BODY + ".body", true).append("in", BODY);
					JsonMap schema = getSchema(param.getMap("schema"), type, bs);
					mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
					mpi.forEachAnnotation(Content.class, x -> true, x -> merge(schema, x.schema()));
					pushupSchemaFields(BODY, param, schema);
					param.appendIf(nem, "schema", schema);
					param.putIfAbsent("required", true);
					addBodyExamples(sm, param, false, type, locale);

				} else if (mpi.hasAnnotation(Query.class) || pt.hasAnnotation(Query.class)) {
					String name = QueryAnnotation.findName(mpi).orElse(null);
					JsonMap param = paramMap.getMap(QUERY + "." + name, true).append("name", name).append("in", QUERY);
					mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
					mpi.forEachAnnotation(Query.class, x -> true, x -> merge(param, x.schema()));
					pushupSchemaFields(QUERY, param, getSchema(param.getMap("schema"), type, bs));
					addParamExample(sm, param, QUERY, type);

				} else if (mpi.hasAnnotation(FormData.class) || pt.hasAnnotation(FormData.class)) {
					String name = FormDataAnnotation.findName(mpi).orElse(null);
					JsonMap param = paramMap.getMap(FORM_DATA + "." + name, true).append("name", name).append("in", FORM_DATA);
					mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
					mpi.forEachAnnotation(FormData.class, x -> true, x -> merge(param, x.schema()));
					pushupSchemaFields(FORM_DATA, 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 = HeaderAnnotation.findName(mpi).orElse(null);
					JsonMap param = paramMap.getMap(HEADER + "." + name, true).append("name", name).append("in", HEADER);
					mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
					mpi.forEachAnnotation(Header.class, x -> true, x -> merge(param, x.schema()));
					pushupSchemaFields(HEADER, param, getSchema(param.getMap("schema"), type, bs));
					addParamExample(sm, param, HEADER, type);

				} else if (mpi.hasAnnotation(Path.class) || pt.hasAnnotation(Path.class)) {
					String name = PathAnnotation.findName(mpi).orElse(null);
					JsonMap param = paramMap.getMap(PATH + "." + name, true).append("name", name).append("in", PATH);
					mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
					mpi.forEachAnnotation(Path.class, x -> true, x -> merge(param, x.schema()));
					pushupSchemaFields(PATH, param, getSchema(param.getMap("schema"), type, bs));
					addParamExample(sm, param, PATH, type);
					param.putIfAbsent("required", true);
				}
			}

			if (! paramMap.isEmpty())
				op.put("parameters", paramMap.values());

			JsonMap responses = op.getMap("responses", true);

			for (ClassInfo eci : mi.getExceptionTypes()) {
				if (eci.hasAnnotation(Response.class)) {
					List<Response> la = eci.getAnnotations(context, Response.class);
					List<StatusCode> la2 = eci.getAnnotations(context, StatusCode.class);
					Set<Integer> codes = getCodes(la2, 500);
					for (Response a : la) {
						for (Integer code : codes) {
							JsonMap om = responses.getMap(String.valueOf(code), true);
							merge(om, a);
							JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
							eci.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
							pushupSchemaFields(RESPONSE, om, schema);
							om.appendIf(nem, "schema", schema);
						}
					}
					List<MethodInfo> methods = eci.getMethods();
					for (int i = methods.size()-1; i>=0; i--) {
						MethodInfo ecmi = methods.get(i);
						Header a = ecmi.getAnnotation(Header.class);
						if (a == null)
							a = ecmi.getReturnType().unwrap(Value.class,Optional.class).getAnnotation(Header.class);
						if (a != null && ! isMulti(a)) {
							String ha = a.name();
							for (Integer code : codes) {
								JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true);
								ecmi.forEachAnnotation(context, Schema.class, x-> true, x -> merge(header, x));
								ecmi.getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
								pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header.getMap("schema"), ecmi.getReturnType().unwrap(Value.class,Optional.class).innerType(), bs));
							}
						}
					}
				}
			}

			if (mi.hasAnnotation(Response.class) || mi.getReturnType().unwrap(Value.class,Optional.class).hasAnnotation(Response.class)) {
				List<Response> la = list();
				mi.forEachAnnotation(context, Response.class, x -> true, x -> la.add(x));
				List<StatusCode> la2 = list();
				mi.forEachAnnotation(context, StatusCode.class, x -> true, x -> la2.add(x));
				Set<Integer> codes = getCodes(la2, 200);
				for (Response a : la) {
					for (Integer code : codes) {
						JsonMap om = responses.getMap(String.valueOf(code), true);
						merge(om, a);
						JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
						mi.forEachAnnotation(context, Schema.class, x -> true, x -> merge(schema, x));
						pushupSchemaFields(RESPONSE, om, schema);
						om.appendIf(nem, "schema", schema);
						addBodyExamples(sm, om, true, m.getGenericReturnType(), locale);
					}
				}
				if (mi.getReturnType().hasAnnotation(Response.class)) {
					List<MethodInfo> methods = mi.getReturnType().getMethods();
					for (int i = methods.size()-1; i>=0; i--) {
						MethodInfo ecmi = methods.get(i);
						if (ecmi.hasAnnotation(Header.class)) {
							Header a = ecmi.getAnnotation(Header.class);
							String ha = a.name();
							if (! isMulti(a)) {
								for (Integer code : codes) {
									JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true);
									ecmi.forEachAnnotation(context, Schema.class, x -> true, x -> merge(header, x));
									ecmi.getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
									merge(header, a.schema());
									pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header, ecmi.getReturnType().innerType(), bs));
								}
							}
						}
					}
				}
			} else if (m.getGenericReturnType() != void.class) {
				JsonMap om = responses.getMap("200", true);
				ClassInfo pt2 = ClassInfo.of(m.getGenericReturnType());
				JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
				pt2.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
				pushupSchemaFields(RESPONSE, om, schema);
				om.appendIf(nem, "schema", schema);
				addBodyExamples(sm, om, true, m.getGenericReturnType(), locale);
			}

			// Finally, look for Value @Header parameters defined on method.
			for (ParamInfo mpi : mi.getParams()) {

				ClassInfo pt = mpi.getParameterType();

				if (pt.is(Value.class) && (mpi.hasAnnotation(Header.class) || pt.hasAnnotation(Header.class))) {
					List<Header> la = list();
					mpi.forEachAnnotation(Header.class, x -> true, x -> la.add(x));
					pt.forEachAnnotation(Header.class, x -> true, x -> la.add(x));
					List<StatusCode> la2 = list();
					mpi.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
					pt.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
					Set<Integer> codes = getCodes(la2, 200);
					String name = HeaderAnnotation.findName(mpi).orElse(null);
					Type type = Value.unwrap(mpi.getParameterType().innerType());
					for (Header a : la) {
						if (! isMulti(a)) {
							for (Integer code : codes) {
								JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(name, true);
								mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
								merge(header, a.schema());
								pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header, type, bs));
							}
						}
					}

				} else if (mpi.hasAnnotation(Response.class) || pt.hasAnnotation(Response.class)) {
					List<Response> la = list();
					mpi.forEachAnnotation(Response.class, x -> true, x -> la.add(x));
					pt.forEachAnnotation(Response.class, x -> true, x -> la.add(x));
					List<StatusCode> la2 = list();
					mpi.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
					pt.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
					Set<Integer> codes = getCodes(la2, 200);
					Type type = Value.unwrap(mpi.getParameterType().innerType());
					for (Response a : la) {
						for (Integer code : codes) {
							JsonMap om = responses.getMap(String.valueOf(code), true);
							merge(om, a);
							JsonMap schema = getSchema(om.getMap("schema"), type, bs);
							mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
							la.forEach(x -> merge(schema, x.schema()));
							pushupSchemaFields(RESPONSE, om, schema);
							om.appendIf(nem, "schema", schema);
						}
					}
				}
			}

			// Add default response descriptions.
			for (Map.Entry<String,Object> e : responses.entrySet()) {
				String key = e.getKey();
				JsonMap val = responses.getMap(key);
				if (StringUtils.isDecimal(key))
					val.appendIfAbsentIf(ne, "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,JsonMap> 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(Json5Serializer.DEFAULT_READABLE));
//			throw e1;
//		}

		try {
			String swaggerJson = Json5Serializer.DEFAULT_READABLE.toString(omSwagger);
//			System.err.println(swaggerJson);
			return jp.parse(swaggerJson, Swagger.class);
		} catch (Exception e) {
			throw new ServletException("Error detected in swagger.", e);
		}
	}