method = DocletHelper.getAnnotationParam(annotationMirror, "method");
+ if (method.isPresent()) {
+ RequestMethod[] methods = (RequestMethod[]) method.get().getValue();
+ //TODO Make the simple assumption that there is only one method...
+ return methods[0].name();
+ }
+ }
+
+ for (SpringRequestMappings value : values()) {
+ if (value.annotationClass.getName().contentEquals(annotationClassName)) {
+ RequestMethod[] requestMethod = value.annotationClass.getAnnotationsByType(RequestMapping.class)[0].method();
+ return requestMethod[0].name();
+ }
+ }
+
+ return defaultRequestMethod;
+ }
+
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SpringfoxDocletException.java b/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SpringfoxDocletException.java
new file mode 100644
index 0000000..aaf90ee
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SpringfoxDocletException.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.doclet;
+
+class SpringfoxDocletException extends RuntimeException {
+ SpringfoxDocletException(Exception exception) {
+ super(exception);
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java b/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java
new file mode 100644
index 0000000..6aa0320
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java
@@ -0,0 +1,234 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.doclet;
+
+import com.sun.source.doctree.*;
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+import springfox.javadoc.plugin.JavadocParameterBuilderPlugin;
+
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.*;
+import javax.tools.Diagnostic;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+
+/**
+ * Generate properties file based on Javadoc.
+ *
+ * The generated properties file will then be read by the
+ * {@link JavadocParameterBuilderPlugin} to enhance the Swagger documentation.
+ *
+ * @author rgoers
+ * @author MartinNeumannBeTSE
+ */
+public class SwaggerPropertiesDoclet implements Doclet {
+
+ private static final String NEWLINE = "\n";
+ private static final String EMPTY = "";
+
+ private ClassDirectoryOption classDirectoryOption = new ClassDirectoryOption();
+ private Reporter reporter;
+ private OutputStream springfoxPropertiesOutputStream;
+ private DocletEnvironment environment;
+ private MethodProcessingContextFactory methodProcessingContextFactory;
+
+ private static void appendPath(StringBuilder rootPath, String path) {
+ String value = path.replaceAll("\"$|^\"", "");
+ if (value.startsWith("/")) {
+ rootPath.append(value).append(".");
+ } else {
+ rootPath.append("/").append(value).append(".");
+ }
+ }
+
+ private static void saveProperty(
+ Properties properties,
+ String key,
+ String value) {
+
+ value = value.replaceAll(NEWLINE, EMPTY);
+ if (value.length() > 0) {
+ properties.setProperty(key, value);
+ }
+ }
+
+ @Override
+ public void init(Locale locale, Reporter reporter) {
+ reporter.print(Diagnostic.Kind.NOTE, "Doclet using locale: " + locale);
+ this.reporter = reporter;
+ }
+
+ @Override
+ public String getName() {
+ return "springfox-javadoc-doclet";
+ }
+
+ @Override
+ public Set extends Option> getSupportedOptions() {
+ return new HashSet<>(Arrays.asList(new DummyOption(1, "-doctitle"),
+ new DummyOption(1, "-windowtitle"),
+ new DummyOption(1, "-author"), new DummyOption(1, "-d"),
+ classDirectoryOption));
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.RELEASE_9;
+ }
+
+ @Override
+ public boolean run(DocletEnvironment environment) {
+ this.environment = environment;
+ this.methodProcessingContextFactory = new MethodProcessingContextFactory(environment);
+ try {
+ preparePropertiesOutputStream();
+ environment.getIncludedElements()
+ .stream()
+ .filter(element -> element.getKind().isClass())
+ .map(element -> (TypeElement) element)
+ .forEach(this::processClass);
+ return true;
+ } finally {
+ closeSpringfoxPropertiesStream();
+ }
+ }
+
+ private void closeSpringfoxPropertiesStream() {
+ try {
+ if (springfoxPropertiesOutputStream != null) {
+ springfoxPropertiesOutputStream.close();
+ }
+ } catch (IOException e) {
+ reporter.print(Diagnostic.Kind.WARNING, "Could not close output stream: " + e.getMessage());
+ }
+ }
+
+ private void preparePropertiesOutputStream() {
+ Path outputFile = classDirectoryOption.getClassDirectory();
+ reporter.print(Diagnostic.Kind.NOTE, "Writing output to " + outputFile.toAbsolutePath().toString());
+ try {
+ Files.createDirectories(outputFile.getParent());
+ springfoxPropertiesOutputStream = new FileOutputStream(classDirectoryOption.getClassDirectory().toFile());
+ } catch (IOException e) {
+ throw new SpringfoxDocletException(e);
+ }
+ }
+
+ private void processClass(TypeElement typeElement) {
+ Properties properties = new Properties();
+ storeClassJavadocAsProperty(typeElement, properties);
+ storeClassFieldJavadocAsProperties(typeElement, properties);
+ storeClassMethodAsProperties(typeElement, properties);
+ storeProperties(typeElement, properties);
+ }
+
+ private void storeClassFieldJavadocAsProperties(TypeElement typeElement, Properties properties) {
+ for (Element classMember : environment.getElementUtils().getAllMembers(typeElement)) {
+ if (classMember.getKind().isField()) {
+ VariableElement variableElement = (VariableElement) classMember;
+ DocletHelper.getElementDoc(environment, variableElement)
+ .ifPresent(variableDoc -> {
+ String classKey =
+ typeElement.getQualifiedName().toString() + "." + variableElement.getSimpleName().toString();
+ properties.put(classKey, variableDoc);
+ });
+ }
+ }
+ }
+
+ /**
+ * For Legacy only methods with request mappings are handled.
+ * This could be change to store the javadoc for all to be more generic and have
+ * simpler logic.
+ */
+ private void storeClassMethodAsProperties(TypeElement typeElement, Properties properties) {
+ methodProcessingContextFactory.from(typeElement)
+ .ifPresent(methodProcessingContext -> {
+ environment.getElementUtils().getAllMembers(typeElement).stream()
+ .filter(element -> element.getKind() == ElementKind.METHOD)
+ .forEach(methodElement -> this.processMethod(properties, methodProcessingContext, methodElement));
+ });
+ }
+
+ private void storeClassJavadocAsProperty(TypeElement typeElement, Properties properties) {
+ DocletHelper.getElementDoc(environment, typeElement)
+ .ifPresent(typeElementDoc -> properties.put(typeElement.getQualifiedName().toString(), typeElementDoc));
+ }
+
+ private void storeProperties(TypeElement typeElement, Properties properties) {
+ try {
+ properties.store(springfoxPropertiesOutputStream, "Class = " + typeElement.getQualifiedName().toString());
+ } catch (IOException e) {
+ throw new SpringfoxDocletException(e);
+ }
+ }
+
+ private void processMethod(Properties properties, MethodProcessingContext methodProcessingContext,
+ Element methodElement) {
+ for (AnnotationMirror annotationMirror : environment.getElementUtils().getAllAnnotationMirrors(methodElement)) {
+ if (SpringRequestMappings.isMapping(annotationMirror)) {
+ String path = getMappingFullPath(methodProcessingContext, annotationMirror);
+ String requestMethod = SpringRequestMappings.getRequestMethod(annotationMirror,
+ methodProcessingContext.getDefaultRequestMethod());
+ if (requestMethod != null) {
+ path = path + requestMethod;
+ DocCommentTree docCommentTree = environment.getDocTrees().getDocCommentTree(methodElement);
+ saveProperty(properties, path + ".notes", docCommentTree.getFullBody().toString());
+ int throwIndex = 0;
+ for (DocTree docTree : docCommentTree.getBlockTags()) {
+ if (docTree instanceof ParamTree) {
+ ParamTree paramTree = (ParamTree) docTree;
+ saveProperty(properties, path + ".param." + paramTree.getName(),
+ paramTree.getDescription().toString());
+ }
+ if (docTree instanceof ReturnTree) {
+ ReturnTree returnTree = (ReturnTree) docTree;
+ saveProperty(properties, path + ".return", returnTree.getDescription().toString());
+ }
+ if (docTree instanceof ThrowsTree) {
+ ThrowsTree throwTree = (ThrowsTree) docTree;
+ String key = path + ".throws." + throwIndex;
+ String value =
+ throwTree.getExceptionName().getSignature() + "-" + throwTree.getDescription().toString();
+ saveProperty(properties, key, value);
+ throwIndex++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private String getMappingFullPath(MethodProcessingContext methodProcessingContext,
+ AnnotationMirror annotationMirror) {
+ StringBuilder path = new StringBuilder(methodProcessingContext.getRootPath());
+ Optional mappingPath = SpringMappingsHelper.getPath(annotationMirror);
+ mappingPath.ifPresent(mappingAnnotationPath -> appendPath(path, mappingAnnotationPath));
+ if (!path.substring(path.length() - 1).equals(".")) {
+ path.append(".");
+ }
+ return path.toString();
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/AnnotationHelper.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/AnnotationHelper.java
new file mode 100644
index 0000000..4b4d8cd
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/AnnotationHelper.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+class AnnotationHelper {
+
+ static boolean hasValue(Annotation annotation) {
+ for (Method method : annotation.annotationType().getDeclaredMethods()) {
+ if (method.getName().equals("value")) {
+ try {
+ method.invoke(annotation, (Object) null);
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocApiListingBuilderPlugin.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocApiListingBuilderPlugin.java
new file mode 100644
index 0000000..2552ecf
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocApiListingBuilderPlugin.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.core.env.Environment;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.ApiListingBuilderPlugin;
+import springfox.documentation.spi.service.contexts.ApiListingContext;
+
+public class JavadocApiListingBuilderPlugin implements ApiListingBuilderPlugin {
+
+ private Environment environment;
+
+ public JavadocApiListingBuilderPlugin(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public void apply(ApiListingContext apiListingContext) {
+ apiListingContext.getResourceGroup().getControllerClass().ifPresent(controllerClass -> {
+ String notes = environment.getProperty(controllerClass.getName());
+ if (notes != null) {
+ apiListingContext.apiListingBuilder().description(notes);
+ }
+ });
+ }
+
+ @Override
+ public boolean supports(DocumentationType documentationType) {
+ return true;
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelBuilderPlugin.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelBuilderPlugin.java
new file mode 100644
index 0000000..1bc2425
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelBuilderPlugin.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.core.env.Environment;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.schema.ModelBuilderPlugin;
+import springfox.documentation.spi.schema.contexts.ModelContext;
+
+public class JavadocModelBuilderPlugin implements ModelBuilderPlugin {
+
+ private final Environment environment;
+
+ public JavadocModelBuilderPlugin(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public void apply(ModelContext context) {
+ String notes = environment.getProperty(context.getType().getTypeName());
+ if (notes != null) {
+ context.getBuilder().description(notes);
+ }
+ }
+
+ @Override
+ public boolean supports(DocumentationType documentationType) {
+ return true;
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelPropertyBuilderPlugin.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelPropertyBuilderPlugin.java
new file mode 100644
index 0000000..a9e0e70
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocModelPropertyBuilderPlugin.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.core.env.Environment;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
+import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
+
+public class JavadocModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
+
+ private final Environment environment;
+
+ public JavadocModelPropertyBuilderPlugin(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public void apply(ModelPropertyContext context) {
+ if (context.getBeanPropertyDefinition().isPresent()) {
+ com.fasterxml.jackson.databind.introspect.AnnotatedField field =
+ context.getBeanPropertyDefinition().get().getField();
+ String key = field.getDeclaringClass().getName() + "." + field.getName();
+ String notes = environment.getProperty(key);
+ if (notes != null) {
+ context.getBuilder().description(notes);
+ }
+ }
+ }
+
+ @Override
+ public boolean supports(DocumentationType documentationType) {
+ return true;
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocOperationBuilderPlugin.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocOperationBuilderPlugin.java
new file mode 100644
index 0000000..e45e054
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocOperationBuilderPlugin.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.core.env.Environment;
+import org.springframework.util.StringUtils;
+import springfox.documentation.builders.ResponseMessageBuilder;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.schema.ModelReference;
+import springfox.documentation.service.ResponseMessage;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.OperationBuilderPlugin;
+import springfox.documentation.spi.service.contexts.OperationContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Plugin to generate the @@ApiOperation values from the properties
+ * file generated by the {@link springfox.javadoc.doclet.SwaggerPropertiesDoclet}
+ */
+public class JavadocOperationBuilderPlugin implements OperationBuilderPlugin {
+
+ private static final String PERIOD = ".";
+ private final Environment environment;
+
+ public JavadocOperationBuilderPlugin(Environment environment) {
+ this.environment = environment;
+ }
+
+
+ @Override
+ public void apply(OperationContext context) {
+
+ String notes = context.requestMappingPattern() + PERIOD + context.httpMethod().toString() + ".notes";
+ if (StringUtils.hasText(notes) && StringUtils.hasText(environment.getProperty(notes))) {
+ context.operationBuilder().notes("" + context.getName() + "
" + environment.getProperty(notes));
+ }
+ String returnDescription = context.requestMappingPattern() + PERIOD + context.httpMethod().toString()
+ + ".return";
+ if (StringUtils.hasText(returnDescription) && StringUtils.hasText(environment.getProperty(returnDescription))) {
+ context.operationBuilder().summary("returns " + environment.getProperty(returnDescription));
+ }
+ String throwsDescription = context.requestMappingPattern() + PERIOD + context.httpMethod().toString()
+ + ".throws.";
+ int i = 0;
+ Set responseMessages = new HashSet<>();
+ while (StringUtils.hasText(throwsDescription + i)
+ && StringUtils.hasText(environment.getProperty(throwsDescription + i))) {
+ String[] throwsValues = StringUtils.split(environment.getProperty(throwsDescription + i), "-");
+ if (throwsValues != null && throwsValues.length == 2) {
+ // TODO[MN]: proper mapping once
+ // https://github.com/springfox/springfox/issues/521 is solved
+ String thrownExceptionName = throwsValues[0];
+ String throwComment = throwsValues[1];
+ ModelReference model = new ModelRef(thrownExceptionName);
+ ResponseMessage message = new ResponseMessageBuilder().code(500).message(throwComment)
+ .responseModel(model).build();
+ responseMessages.add(message);
+ }
+ i++;
+ }
+ context.operationBuilder().responseMessages(responseMessages);
+ }
+
+ @Override
+ public boolean supports(DocumentationType documentationType) {
+ return true;
+ }
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocParameterBuilderPlugin.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocParameterBuilderPlugin.java
new file mode 100644
index 0000000..dd29b6c
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocParameterBuilderPlugin.java
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.core.env.Environment;
+import springfox.documentation.service.ResolvedMethodParameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.ParameterBuilderPlugin;
+import springfox.documentation.spi.service.contexts.ParameterContext;
+import springfox.javadoc.doclet.SwaggerPropertiesDoclet;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Optional;
+
+import static springfox.javadoc.plugin.AnnotationHelper.hasValue;
+
+/**
+ * Plugin to generate the @ApiParam and @ApiOperation values from the properties
+ * file generated by the {@link SwaggerPropertiesDoclet}.
+ *
+ * @author rgoers
+ * @author MartinNeumannBeTSE
+ */
+public class JavadocParameterBuilderPlugin implements ParameterBuilderPlugin {
+
+ private static final String PERIOD = ".";
+ private static final String API_PARAM = "io.swagger.annotations.ApiParam";
+ private static final String REQUEST_PARAM = "org.springframework.web.bind.annotation.RequestParam";
+ private static final String PATH_VARIABLE = "org.springframework.web.bind.annotation.PathVariable";
+ private final Environment environment;
+
+ public JavadocParameterBuilderPlugin(Environment environment) {
+ this.environment = environment;
+ }
+
+ private static Annotation annotationFromField(ParameterContext context, String annotationType) {
+
+ ResolvedMethodParameter methodParam = context.resolvedMethodParameter();
+
+ for (Annotation annotation : methodParam.getAnnotations()) {
+ if (annotation.annotationType().getName().equals(annotationType)) {
+ return annotation;
+ }
+ }
+ return null;
+
+ }
+
+ @Override
+ public boolean supports(DocumentationType delimiter) {
+ return true;
+ }
+
+ @Override
+ public void apply(ParameterContext context) {
+ String description = null;
+ Optional parameterName = context.resolvedMethodParameter().defaultName();
+ Annotation apiParam = annotationFromField(context, API_PARAM);
+ if (apiParam != null) {
+ java.util.Optional isRequired = isParamRequired(apiParam, context);
+ isRequired.ifPresent(aBoolean -> context.parameterBuilder().required(aBoolean));
+ }
+ if (parameterName.isPresent() && (apiParam == null || !hasValue(apiParam))) {
+ String key = context.getOperationContext().requestMappingPattern() + PERIOD
+ + context.getOperationContext().httpMethod().name() + ".param." + parameterName.get();
+ description = environment.getProperty(key);
+ }
+ if (description != null) {
+ context.parameterBuilder().description(description);
+ }
+ }
+
+ private java.util.Optional isParamRequired(Annotation apiParam, ParameterContext context) {
+ if (apiParam != null) {
+ java.util.Optional required = isRequired(apiParam);
+ if (required.isPresent()) {
+ return required;
+ }
+ }
+ Annotation annotation = annotationFromField(context, REQUEST_PARAM);
+ if (annotation == null) {
+ annotation = annotationFromField(context, PATH_VARIABLE);
+ }
+ return annotation != null ? isRequired(annotation) : java.util.Optional.empty();
+ }
+
+ private java.util.Optional isRequired(Annotation annotation) {
+ for (Method method : annotation.annotationType().getDeclaredMethods()) {
+ if (method.getName().equals("required")) {
+ try {
+ return java.util.Optional.of((Boolean) method.invoke(annotation, (Object) null));
+ } catch (Exception ex) {
+ return java.util.Optional.empty();
+ }
+ }
+ }
+ return java.util.Optional.empty();
+ }
+
+}
diff --git a/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocPluginConfiguration.java b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocPluginConfiguration.java
new file mode 100644
index 0000000..3204efa
--- /dev/null
+++ b/springfox-javadoc/src/main/java/springfox/javadoc/plugin/JavadocPluginConfiguration.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.plugin;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import springfox.javadoc.doclet.SwaggerPropertiesDoclet;
+
+import static springfox.javadoc.doclet.ClassDirectoryOption.SPRINGFOX_JAVADOC_PROPERTIES;
+
+/**
+ * Spring configuration that adds the properties file generated by the {@link SwaggerPropertiesDoclet} as property
+ * source and also adds the {@link JavadocParameterBuilderPlugin} to the Spring context.
+ *
+ * @author MartinNeumannBeTSE
+ */
+@Configuration
+@PropertySource(value = "classpath:/"
+ + SPRINGFOX_JAVADOC_PROPERTIES, ignoreResourceNotFound = true)
+public class JavadocPluginConfiguration {
+
+ @Bean
+ public JavadocApiListingBuilderPlugin javadocApiListingBuilderPlugin(Environment environment) {
+ return new JavadocApiListingBuilderPlugin(environment);
+ }
+
+ @Bean
+ public JavadocOperationBuilderPlugin javadocOperationBuilderPlugin(Environment environment) {
+ return new JavadocOperationBuilderPlugin(environment);
+ }
+
+ @Bean
+ public JavadocParameterBuilderPlugin javadocParameterBuilderPlugin(Environment environment) {
+ return new JavadocParameterBuilderPlugin(environment);
+ }
+
+ @Bean
+ public JavadocModelBuilderPlugin javadocModelBuilderPlugin(Environment environment) {
+ return new JavadocModelBuilderPlugin(environment);
+ }
+
+ @Bean
+ public JavadocModelPropertyBuilderPlugin javadocModelPropertyBuilderPlugin(Environment environment) {
+ return new JavadocModelPropertyBuilderPlugin(environment);
+ }
+}
diff --git a/springfox-javadoc/src/main/resources/META-INF/spring.factories b/springfox-javadoc/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2f14e47
--- /dev/null
+++ b/springfox-javadoc/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+springfox.javadoc.plugin.JavadocPluginConfiguration
diff --git a/springfox-javadoc/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java b/springfox-javadoc/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java
new file mode 100644
index 0000000..e7fe54a
--- /dev/null
+++ b/springfox-javadoc/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.doclet;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import springfox.javadoc.example.SecretAgent;
+import springfox.javadoc.example.TestController;
+
+import javax.tools.DocumentationTool;
+import javax.tools.ToolProvider;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static springfox.javadoc.doclet.ClassDirectoryOption.SPRINGFOX_JAVADOC_PROPERTIES;
+
+public class SwaggerPropertiesDocletTest {
+
+ private static final String BUILD_PROPERTY_FILE_LOCATION = "./build/property-file-location";
+ private static final String GENERATED_PROPERTY_FILE =
+ String.format("%s/%s", BUILD_PROPERTY_FILE_LOCATION, SPRINGFOX_JAVADOC_PROPERTIES);
+
+ @BeforeClass
+ public static void deletePropertyFile() {
+ File propertyFile = new File(GENERATED_PROPERTY_FILE);
+ if (propertyFile.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ propertyFile.delete();
+ }
+ }
+
+ @Test
+ public void testPropertiesGeneration() throws IOException {
+ DocumentationTool systemDocumentationTool = ToolProvider.getSystemDocumentationTool();
+ String[] args = new String[] {
+ "-sourcepath",
+ "./src/test/java",
+ "-subpackages",
+ "springfox.javadoc.doclet",
+ "springfox.javadoc.example",
+ "-d",
+ "whatever not used just to show compatibility",
+ "-author",
+ "whatever not used just to show compatibility",
+ "-doctitle",
+ "whatever not used just to show compatibility",
+ "-windowtitle",
+ "whatever not used just to show compatibility",
+ "-classdir",
+ BUILD_PROPERTY_FILE_LOCATION
+ };
+ DocumentationTool.DocumentationTask task = systemDocumentationTool.getTask(null, null, null,
+ SwaggerPropertiesDoclet.class, Arrays.asList(args), null);
+
+ task.call();
+
+ Properties props = generatedProperties();
+ assertEquals("test controller class", props.getProperty(TestController.class.getName()));
+ assertEquals("Secret Agent, it can be James Bond!", props.getProperty(SecretAgent.class.getName()));
+ assertEquals("Secret agent name, probably something badass", props.getProperty(SecretAgent.class.getName()+".secretAgentName"));
+ assertEquals("test method", props.getProperty("/test/test.GET.notes"));
+ assertEquals("dummy value", props.getProperty("/test/test.GET.return"));
+ assertEquals("dummy param", props.getProperty("/test/test.GET.param.param"));
+ assertEquals("without value or path", props.getProperty("/test.POST.notes"));
+ assertEquals("other without value or path", props.getProperty("/other.POST.notes"));
+ assertEquals("other without value or path other line in delete mapping", props.getProperty("/other.DELETE.notes"));
+ assertEquals("InvalidNameException-when parameter smaller than 1", props.getProperty("/other/test.GET.throws.1"));
+ assertEquals("retval", props.getProperty("/test.POST.return"));
+ assertEquals("param", props.getProperty("/test.POST.param.bar"));
+ }
+
+ private Properties generatedProperties() throws IOException {
+ // read in the properties file created by the SwaggerPropertiesDoclet
+ InputStream inputStream = new FileInputStream(GENERATED_PROPERTY_FILE);
+ assertNotNull(inputStream);
+
+ // check that the properties match the example sources
+ Properties props = new Properties();
+ props.load(inputStream);
+ return props;
+ }
+
+}
diff --git a/springfox-javadoc/src/test/java/springfox/javadoc/example/OtherController.java b/springfox-javadoc/src/test/java/springfox/javadoc/example/OtherController.java
new file mode 100644
index 0000000..a1188bc
--- /dev/null
+++ b/springfox-javadoc/src/test/java/springfox/javadoc/example/OtherController.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.example;
+
+import org.springframework.web.bind.annotation.*;
+
+import javax.naming.InvalidNameException;
+import java.io.IOException;
+
+/**
+ * test controller class
+ *
+ * @author Matthieu Ghilain
+ */
+@RequestMapping(path = "/other", method = RequestMethod.PUT)
+public class OtherController {
+
+ /**
+ * other test method
+ *
+ * @param param
+ * dummy param
+ * @return dummy value
+ * @throws IOException some exception
+ * @throws InvalidNameException when parameter smaller than 1
+ */
+ @GetMapping("test")
+ public String test(String param) throws IOException, InvalidNameException {
+ if (param.length() > 5) {
+ throw new IOException("Just to test :)");
+ }
+ if (param.length() < 1) {
+ throw new InvalidNameException();
+ }
+ return "dummy " + param;
+ }
+
+ /**
+ * other without value or path
+ *
+ * @param bar
+ * param
+ * @return retval
+ */
+ @PostMapping
+ public String bla(@RequestBody String bar) {
+ return "foo" + bar;
+ }
+
+ /**
+ * other without value or path
+ * other line in delete mapping
+ *
+ */
+ @DeleteMapping
+ public void delete() {
+
+ }
+}
diff --git a/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgent.java b/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgent.java
new file mode 100644
index 0000000..597ac98
--- /dev/null
+++ b/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgent.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.example;
+
+/**
+ * Secret Agent, it can be James Bond!
+ */
+public class SecretAgent {
+
+ /**
+ * Secret agent name, probably something badass
+ */
+ private String secretAgentName;
+
+ public String getSecretAgentName() {
+ return secretAgentName;
+ }
+
+ public void setSecretAgentName(String secretAgentName) {
+ this.secretAgentName = secretAgentName;
+ }
+}
diff --git a/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgentController.java b/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgentController.java
new file mode 100644
index 0000000..e11b312
--- /dev/null
+++ b/springfox-javadoc/src/test/java/springfox/javadoc/example/SecretAgentController.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package springfox.javadoc.example;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Service to retrieve secret agents (could replace M in the future).
+ *
+ * @author Matthieu Ghilain
+ */
+@RestController
+@RequestMapping(path = "/agent", method = RequestMethod.PUT)
+public class SecretAgentController {
+
+
+ /**
+ * Retrieve James Bond...
+ * @return James Bond of course :)
+ */
+ @GetMapping("jamesBond")
+ public SecretAgent getJamesBond() {
+ SecretAgent secretAgent = new SecretAgent();
+ secretAgent.setSecretAgentName("James Bond");
+ return secretAgent;
+ }
+
+}
diff --git a/src/test/java/springfox/javadoc/example/TestController.java b/springfox-javadoc/src/test/java/springfox/javadoc/example/TestController.java
similarity index 100%
rename from src/test/java/springfox/javadoc/example/TestController.java
rename to springfox-javadoc/src/test/java/springfox/javadoc/example/TestController.java
diff --git a/src/test/java/springfox/javadoc/example/package-info.java b/springfox-javadoc/src/test/java/springfox/javadoc/example/package-info.java
similarity index 97%
rename from src/test/java/springfox/javadoc/example/package-info.java
rename to springfox-javadoc/src/test/java/springfox/javadoc/example/package-info.java
index 8bd3fb5..7050ee3 100644
--- a/src/test/java/springfox/javadoc/example/package-info.java
+++ b/springfox-javadoc/src/test/java/springfox/javadoc/example/package-info.java
@@ -1,25 +1,25 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-
-/**
- * contains example REST controller classes that are only used to test (and
- * therefore demonstrate) how the
- * {@link springfox.javadoc.doclet.SwaggerPropertiesDoclet} works.
- */
-package springfox.javadoc.example;
+/*
+ *
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+/**
+ * contains example REST controller classes that are only used to test (and
+ * therefore demonstrate) how the
+ * {@link springfox.javadoc.doclet.SwaggerPropertiesDoclet} works.
+ */
+package springfox.javadoc.example;
diff --git a/src/main/java/springfox/javadoc/configuration/JavadocPluginConfiguration.java b/src/main/java/springfox/javadoc/configuration/JavadocPluginConfiguration.java
deleted file mode 100644
index 8511cd8..0000000
--- a/src/main/java/springfox/javadoc/configuration/JavadocPluginConfiguration.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.configuration;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import springfox.javadoc.doclet.SwaggerPropertiesDoclet;
-import springfox.javadoc.plugin.JavadocBuilderPlugin;
-
-/**
- * Spring configuration that adds the properties file generated by the {@link SwaggerPropertiesDoclet} as property
- * source and also adds the {@link JavadocBuilderPlugin} to the Spring context.
- *
- * @author MartinNeumannBeTSE
- */
-@Configuration
-@PropertySource(value = "classpath:/"
- + SwaggerPropertiesDoclet.SPRINGFOX_JAVADOC_PROPERTIES, ignoreResourceNotFound = true)
-@ComponentScan("springfox.javadoc.plugin")
-public class JavadocPluginConfiguration {
-
- @Autowired
- JavadocBuilderPlugin javadocBuilderPlugin;
-
- @Bean
- public JavadocBuilderPlugin javadocBuilder() {
- return javadocBuilderPlugin;
- }
-}
diff --git a/src/main/java/springfox/javadoc/doclet/DocletOptionParser.java b/src/main/java/springfox/javadoc/doclet/DocletOptionParser.java
deleted file mode 100644
index 20579eb..0000000
--- a/src/main/java/springfox/javadoc/doclet/DocletOptionParser.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.doclet;
-
-class DocletOptionParser {
- static final String CLASS_DIR_OPTION = "-classdir";
- static final String EXCEPTION_REF_OPTION = "-exceptionRef";
- private final String[][] options;
-
- DocletOptionParser(String[][] options) {
- this.options = options;
- }
-
- DocletOptions parse() {
- String propertyFilePath = "";
- Boolean documentExceptions = false;
- for (String[] each : options) {
- if (CLASS_DIR_OPTION.equalsIgnoreCase(each[0])) {
- propertyFilePath = each[1];
- }
- if (EXCEPTION_REF_OPTION.equalsIgnoreCase(each[0])) {
- documentExceptions = Boolean.valueOf(each[1]);
- }
- }
-
- return new DocletOptionsBuilder()
- .withPropertyFilePath(propertyFilePath)
- .withDocumentExceptions(documentExceptions)
- .build();
- }
-}
diff --git a/src/main/java/springfox/javadoc/doclet/DocletOptionsBuilder.java b/src/main/java/springfox/javadoc/doclet/DocletOptionsBuilder.java
deleted file mode 100644
index 10920e5..0000000
--- a/src/main/java/springfox/javadoc/doclet/DocletOptionsBuilder.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.doclet;
-
-import com.google.common.base.Strings;
-
-public class DocletOptionsBuilder {
- private String propertyFilePath;
- private boolean documentExceptions;
-
- DocletOptionsBuilder withPropertyFilePath(String propertyFilePath) {
- this.propertyFilePath = propertyFilePath;
- return this;
- }
-
- DocletOptionsBuilder withDocumentExceptions(boolean documentExceptions) {
- this.documentExceptions = documentExceptions;
- return this;
- }
-
- DocletOptions build() {
- if (Strings.isNullOrEmpty(propertyFilePath)) {
- throw new IllegalStateException("Usage: javadoc -classdir classes directory [-exceptionRef true|false (generate references to exception"
- + " classes)] -doclet ...");
- }
- return new DocletOptions(propertyFilePath, documentExceptions);
- }
-}
diff --git a/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java b/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java
deleted file mode 100644
index 1746d66..0000000
--- a/src/main/java/springfox/javadoc/doclet/SwaggerPropertiesDoclet.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.doclet;
-
-import com.sun.javadoc.AnnotationDesc;
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.DocErrorReporter;
-import com.sun.javadoc.MethodDoc;
-import com.sun.javadoc.ParamTag;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.Tag;
-import com.sun.javadoc.ThrowsTag;
-import springfox.javadoc.plugin.JavadocBuilderPlugin;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Properties;
-
-import static springfox.javadoc.doclet.DocletOptionParser.*;
-
-// the NOSONAR comment is added to ignore sonar warning about usage of Sun classes
-// because doclets can only be written using Sun classes
-
-/**
- * Generate properties file based on Javadoc.
- *
- * The generated properties file will then be read by the
- * {@link JavadocBuilderPlugin} to enhance the Swagger documentation.
- *
- * @author rgoers
- * @author MartinNeumannBeTSE
- */
-public class SwaggerPropertiesDoclet {
-
- public static final String SPRINGFOX_JAVADOC_PROPERTIES = "META-INF/springfox.javadoc.properties";
- private static final String REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";
- private static final String REQUEST_GET_MAPPING = "org.springframework.web.bind.annotation.RequestMethod.GET";
- private static final String REQUEST_POST_MAPPING = "org.springframework.web.bind.annotation.RequestMethod.POST";
- private static final String REQUEST_PUT_MAPPING = "org.springframework.web.bind.annotation.RequestMethod.PUT";
- private static final String REQUEST_PATCH_MAPPING = "org.springframework.web.bind.annotation.RequestMethod.PATCH";
- private static final String REQUEST_DELETE_MAPPING = "org.springframework.web.bind.annotation.RequestMethod.DELETE";
- private static final String DELETE_MAPPING = "org.springframework.web.bind.annotation.DeleteMapping";
- private static final String GET_MAPPING = "org.springframework.web.bind.annotation.GetMapping";
- private static final String PATCH_MAPPING = "org.springframework.web.bind.annotation.PatchMapping";
- private static final String POST_MAPPING = "org.springframework.web.bind.annotation.PostMapping";
- private static final String PUT_MAPPING = "org.springframework.web.bind.annotation.PutMapping";
- private static final String RETURN = "@return";
- private static final String PATH = "path";
- private static final String VALUE = "value";
- private static final String NEWLINE = "\n";
- private static final String EMPTY = "";
- private static final String METHOD = "method";
-
- private static final String[] MAPPINGS = new String[] {
- DELETE_MAPPING,
- GET_MAPPING,
- PATCH_MAPPING,
- POST_MAPPING,
- PUT_MAPPING,
- REQUEST_MAPPING };
-
- private static final String[][] REQUEST_MAPPINGS = new String[][] {
- { REQUEST_DELETE_MAPPING, "DELETE" },
- { REQUEST_GET_MAPPING, "GET" },
- { REQUEST_PATCH_MAPPING, "PATCH" },
- { REQUEST_POST_MAPPING, "POST" },
- { REQUEST_PUT_MAPPING, "PUT" }
- };
-
- private static DocletOptions docletOptions;
-
- private SwaggerPropertiesDoclet() {
- throw new UnsupportedOperationException();
- }
-
-
- /**
- * See Using
- * custom command-line options
- * @param option option evaluate an expected length for a given option
- * @return number of options
- */
- @SuppressWarnings("WeakerAccess")
- public static int optionLength(String option) {
- int length = 0;
- if (option.equalsIgnoreCase(CLASS_DIR_OPTION)) {
- length = 2;
- }
- if (option.equalsIgnoreCase(EXCEPTION_REF_OPTION)) {
- length = 2;
- }
- return length;
- }
-
- /**
- * See Using
- * custom command-line options
- * @param options command line options split as key value pairs on index 0 and 1
- * @param reporter reporter for errors
- * @return true if options are valid
- */
- @SuppressWarnings("WeakerAccess")
- public static boolean validOptions(
- String[][] options,
- DocErrorReporter reporter) {
-
- DocletOptionParser parser = new DocletOptionParser(options);
-
- try {
- docletOptions = parser.parse();
- return true;
- } catch (IllegalStateException e) {
- reporter.printError(e.getMessage());
- }
- return false;
- }
-
- /**
- * See A
- * Simple Example Doclet
- * @param root {@link RootDoc}
- * @return true if it started successfully
- */
- @SuppressWarnings({ "unused", "WeakerAccess", "UnusedReturnValue" })
- public static boolean start(RootDoc root) {
-
- String propertyFilePath = docletOptions.getPropertyFilePath();
- if (propertyFilePath == null || propertyFilePath.length() == 0) {
- root.printError("No output location was specified");
- return false;
- } else {
- StringBuilder sb = new StringBuilder(propertyFilePath);
- if (!propertyFilePath.endsWith("/")) {
- sb.append("/");
- }
- sb.append(SPRINGFOX_JAVADOC_PROPERTIES);
- String out = sb.toString();
- root.printNotice("Writing output to " + out);
- File file = new File(out);
- //noinspection ResultOfMethodCallIgnored
- file.getParentFile().mkdirs();
- OutputStream javadoc = null;
- try {
- javadoc = new FileOutputStream(file);
- Properties properties = new Properties();
-
- for (ClassDoc classDoc : root.classes()) {
- sb.setLength(0);
- String defaultRequestMethod = processClass(classDoc, sb);
- String pathRoot = sb.toString();
- for (MethodDoc methodDoc : classDoc.methods()) {
- processMethod(
- properties,
- methodDoc,
- defaultRequestMethod,
- pathRoot,
- docletOptions.isDocumentExceptions());
- }
- }
- properties.store(javadoc, "Springfox javadoc properties");
- } catch (IOException e) {
- root.printError(e.getMessage());
- } finally {
- if (javadoc != null) {
- try {
- javadoc.close();
- } catch (IOException e) {
- // close for real
- }
- }
- }
- }
- return true;
- }
-
- private static String processClass(
- ClassDoc classDoc,
- StringBuilder pathRoot) {
-
- String defaultRequestMethod = null;
- for (AnnotationDesc annotationDesc : classDoc.annotations()) {
- if (REQUEST_MAPPING.equals(annotationDesc.annotationType().qualifiedTypeName())) {
- for (AnnotationDesc.ElementValuePair pair : annotationDesc.elementValues()) {
-
- if (VALUE.equals(pair.element().name()) || PATH.equals(pair.element().name())) {
- setRoot(pathRoot, pair);
- }
- if (METHOD.equals(pair.element().name())) {
- defaultRequestMethod = pair.value().toString();
- }
- }
- break;
- }
- }
- return defaultRequestMethod;
- }
-
- private static void setRoot(
- StringBuilder pathRoot,
- AnnotationDesc.ElementValuePair pair) {
-
- String value = pair.value().toString().replaceAll("\"$|^\"", "");
- if (!value.startsWith("/")) {
- pathRoot.append("/");
- }
- if (value.endsWith("/")) {
- pathRoot.append(value, 0, value.length() - 1);
- } else {
- pathRoot.append(value);
- }
- }
-
- private static void processMethod(
- Properties properties,
- MethodDoc methodDoc,
- String defaultRequestMethod,
- String pathRoot,
- boolean exceptionRef) {
-
- for (AnnotationDesc annotationDesc : methodDoc.annotations()) {
- String annotationType = annotationDesc.annotationType().toString();
- if (isMapping(annotationType)) {
- StringBuilder path = new StringBuilder(pathRoot);
- for (AnnotationDesc.ElementValuePair pair : annotationDesc.elementValues()) {
- if (VALUE.equals(pair.element().name()) || PATH.equals(pair.element().name())) {
- appendPath(path, pair);
- break;
- }
- }
- if (!path.substring(path.length() - 1).equals(".")) {
- path.append(".");
- }
- String requestMethod = getRequestMethod(annotationDesc, annotationType, defaultRequestMethod);
- if (requestMethod != null) {
- path.append(requestMethod);
- saveProperty(properties, path.toString() + ".notes", methodDoc.commentText());
-
- for (ParamTag paramTag : methodDoc.paramTags()) {
- saveProperty(properties, path.toString() + ".param." + paramTag.parameterName(),
- paramTag.parameterComment());
- }
- for (Tag tag : methodDoc.tags()) {
- if (tag.name().equals(RETURN)) {
- saveProperty(properties, path.toString() + ".return", tag.text());
- break;
- }
- }
- if (exceptionRef) {
- processThrows(properties, methodDoc.throwsTags(), path);
- }
- }
- }
- }
- }
-
- private static void appendPath(
- StringBuilder path,
- AnnotationDesc.ElementValuePair pair) {
-
- String value = pair.value().toString().replaceAll("\"$|^\"", "");
- if (value.startsWith("/")) {
- path.append(value).append(".");
- } else {
- path.append("/").append(value).append(".");
- }
- }
-
- private static boolean isMapping(String name) {
- for (String mapping : MAPPINGS) {
- if (mapping.equals(name)) {
- return true;
- }
- }
- return false;
- }
-
- private static String getRequestMethod(
- AnnotationDesc annotationDesc,
- String name,
- String defaultRequestMethod) {
-
- if (REQUEST_MAPPING.equals(name)) {
- for (AnnotationDesc.ElementValuePair pair : annotationDesc.elementValues()) {
- if (METHOD.equals(pair.element().name())) {
- return resolveRequestMethod(pair, defaultRequestMethod);
- }
- }
- } else if (PUT_MAPPING.equals(name)) {
- return "PUT";
- } else if (POST_MAPPING.equals(name)) {
- return "POST";
- } else if (PATCH_MAPPING.equals(name)) {
- return "PATCH";
- } else if (GET_MAPPING.equals(name)) {
- return "GET";
- } else if (DELETE_MAPPING.equals(name)) {
- return "DELETE";
- }
- return defaultRequestMethod;
- }
-
- private static String resolveRequestMethod(
- AnnotationDesc.ElementValuePair pair,
- String defaultRequestMethod) {
-
- String value = pair.value().toString();
- for (String[] each : REQUEST_MAPPINGS) {
- if (each[0].equals(value)) {
- return each[1];
- }
- }
- return defaultRequestMethod;
- }
-
- private static void processThrows(
- Properties properties,
- ThrowsTag[] throwsTags,
- StringBuilder path) {
-
- for (int i = 0; i < throwsTags.length; i++) {
- String key = path.toString() + ".throws." + i;
- String value = throwsTags[i].exceptionType().typeName() + "-" + throwsTags[i].exceptionComment();
- saveProperty(properties, key, value);
- }
- }
-
- private static void saveProperty(
- Properties properties,
- String key,
- String value) {
-
- value = value.replaceAll(NEWLINE, EMPTY);
- if (value.length() > 0) {
- properties.setProperty(key, value);
- }
- }
-}
diff --git a/src/main/java/springfox/javadoc/plugin/JavadocBuilderPlugin.java b/src/main/java/springfox/javadoc/plugin/JavadocBuilderPlugin.java
deleted file mode 100644
index 779e7c9..0000000
--- a/src/main/java/springfox/javadoc/plugin/JavadocBuilderPlugin.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.plugin;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.core.env.Environment;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import springfox.documentation.builders.ResponseMessageBuilder;
-import springfox.documentation.schema.ModelRef;
-import springfox.documentation.schema.ModelReference;
-import springfox.documentation.service.ResolvedMethodParameter;
-import springfox.documentation.service.ResponseMessage;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spi.service.OperationBuilderPlugin;
-import springfox.documentation.spi.service.ParameterBuilderPlugin;
-import springfox.documentation.spi.service.contexts.OperationContext;
-import springfox.documentation.spi.service.contexts.ParameterContext;
-import springfox.javadoc.doclet.SwaggerPropertiesDoclet;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Plugin to generate the @ApiParam and @ApiOperation values from the properties
- * file generated by the {@link SwaggerPropertiesDoclet}.
- *
- * @author rgoers
- * @author MartinNeumannBeTSE
- */
-@Component
-@Order(Ordered.LOWEST_PRECEDENCE)
-public class JavadocBuilderPlugin implements OperationBuilderPlugin, ParameterBuilderPlugin {
-
- private static final String PERIOD = ".";
- private static final String API_PARAM = "io.swagger.annotations.ApiParam";
- private static final String REQUEST_PARAM = "org.springframework.web.bind.annotation.RequestParam";
- private static final String PATH_VARIABLE = "org.springframework.web.bind.annotation.PathVariable";
- @Autowired
- private Environment environment;
-
- private static Annotation annotationFromField(ParameterContext context, String annotationType) {
-
- ResolvedMethodParameter methodParam = context.resolvedMethodParameter();
-
- for (Annotation annotation : methodParam.getAnnotations()) {
- if (annotation.annotationType().getName().equals(annotationType)) {
- return annotation;
- }
- }
- return null;
-
- }
-
- @Override
- public boolean supports(DocumentationType delimiter) {
- return true;
- }
-
- @Override
- public void apply(OperationContext context) {
-
- String notes = context.requestMappingPattern() + PERIOD + context.httpMethod().toString() + ".notes";
- if (StringUtils.hasText(notes) && StringUtils.hasText(environment.getProperty(notes))) {
- context.operationBuilder().notes("" + context.getName() + "
" + environment.getProperty(notes));
- }
- String returnDescription = context.requestMappingPattern() + PERIOD + context.httpMethod().toString()
- + ".return";
- if (StringUtils.hasText(returnDescription) && StringUtils.hasText(environment.getProperty(returnDescription))) {
- context.operationBuilder().summary("returns " + environment.getProperty(returnDescription));
- }
- String throwsDescription = context.requestMappingPattern() + PERIOD + context.httpMethod().toString()
- + ".throws.";
- int i = 0;
- Set responseMessages = new HashSet();
- while (StringUtils.hasText(throwsDescription + i)
- && StringUtils.hasText(environment.getProperty(throwsDescription + i))) {
- String[] throwsValues = StringUtils.split(environment.getProperty(throwsDescription + i), "-");
- if (throwsValues.length == 2) {
- // TODO[MN]: proper mapping once
- // https://github.com/springfox/springfox/issues/521 is solved
- String thrownExceptionName = throwsValues[0];
- String throwComment = throwsValues[1];
- ModelReference model = new ModelRef(thrownExceptionName);
- ResponseMessage message = new ResponseMessageBuilder().code(500).message(throwComment)
- .responseModel(model).build();
- responseMessages.add(message);
- }
- i++;
- }
- context.operationBuilder().responseMessages(responseMessages);
-
- }
-
- @Override
- public void apply(ParameterContext context) {
- String description = null;
- Optional parmName = context.resolvedMethodParameter().defaultName();
- Annotation apiParam = annotationFromField(context, API_PARAM);
- if (apiParam != null) {
- Optional isRequired = isParamRequired(apiParam, context);
- if (isRequired.isPresent()) {
- context.parameterBuilder().required(isRequired.get());
- }
- }
- if (parmName.isPresent() && (apiParam == null || !hasValue(apiParam, context))) {
- String key = context.getOperationContext().requestMappingPattern() + PERIOD
- + context.getOperationContext().httpMethod().name() + ".param." + parmName.get();
- description = environment.getProperty(key);
- }
- if (description != null) {
- context.parameterBuilder().description(description);
- }
- }
-
- @VisibleForTesting
- String extractApiParamDescription(Annotation annotation) {
- return annotation != null ? annotation.annotationType().getName() : null;
- }
-
- @VisibleForTesting
- Optional isParamRequired(Annotation apiParam, ParameterContext context) {
- if (apiParam != null) {
- Optional required = isRequired(apiParam, context);
- if (required.isPresent()) {
- return required;
- }
- }
- Annotation annotation = annotationFromField(context, REQUEST_PARAM);
- if (annotation == null) {
- annotation = annotationFromField(context, PATH_VARIABLE);
- }
- return annotation != null ? isRequired(annotation, context) : Optional.absent();
- }
-
- @VisibleForTesting
- Optional isRequired(Annotation annotation, ParameterContext context) {
- for (Method method : annotation.annotationType().getDeclaredMethods()) {
- if (method.getName().equals("required")) {
- try {
- return Optional.of((Boolean) method.invoke(annotation, (Object) null));
- } catch (Exception ex) {
- return Optional.absent();
- }
- }
- }
- return Optional.absent();
- }
-
- @VisibleForTesting
- boolean hasValue(Annotation annotation, ParameterContext context) {
- for (Method method : annotation.annotationType().getDeclaredMethods()) {
- if (method.getName().equals("value")) {
- try {
- Optional value = Optional.of((String) method.invoke(annotation, (Object) null));
- return value.isPresent();
- } catch (Exception ex) {
- return false;
- }
- }
- }
- return false;
- }
-}
diff --git a/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java b/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java
deleted file mode 100644
index 36dc34d..0000000
--- a/src/test/java/springfox/javadoc/doclet/SwaggerPropertiesDocletTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *
- * Copyright 2018-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- */
-package springfox.javadoc.doclet;
-
-import com.sun.javadoc.DocErrorReporter;
-import com.sun.javadoc.SourcePosition;
-import com.sun.tools.javadoc.Main;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Properties;
-
-import static org.junit.Assert.*;
-import static springfox.javadoc.doclet.SwaggerPropertiesDoclet.*;
-
-public class SwaggerPropertiesDocletTest {
-
- private static final String BUILD_PROPERTY_FILE_LOCATION = "./build/property-file-location";
- private static final String GENERATED_PROPERTY_FILE =
- String.format("%s/%s", BUILD_PROPERTY_FILE_LOCATION, SPRINGFOX_JAVADOC_PROPERTIES);
-
- @BeforeClass
- public static void setupFixture() {
- deletePropertyFile();
- }
-
- @AfterClass
- public static void cleanupFixture() {
- deletePropertyFile();
- }
-
- @Test
- public void testValidOptionLength() {
- assertEquals(2, optionLength("-classdir"));
- }
-
- @Test
- public void testInvalidOptionLength() {
- assertEquals(0, optionLength("dummy"));
- }
-
- @Test
- public void testValidOptions() {
- String[][] options = new String[][] { new String[] { "foo", "bar" }, new String[] { "-classdir", "dummy" } };
- DummyDocErrorReporter reporter = new DummyDocErrorReporter();
- assertTrue(validOptions(options, reporter));
- assertTrue(reporter.getErrors().isEmpty());
- }
-
- @Test
- public void testInvalidOptions() {
- String[][] options = new String[][] { new String[] { "foo", "bar" }, new String[] { "baz", "dummy" } };
- DummyDocErrorReporter reporter = new DummyDocErrorReporter();
- assertFalse(validOptions(options, reporter));
- assertTrue(reporter.getErrors().contains("-classdir"));
- }
-
- @Test
- public void testPropertiesGeneration() throws IOException {
-
- StringWriter err = new StringWriter();
- StringWriter warn = new StringWriter();
- StringWriter notice = new StringWriter();
-
- String[] args = new String[] {
- "-sourcepath",
- "./src/test/java",
- "-subpackages",
- "springfox.javadoc",
- "springfox.javadoc",
- "-classdir",
- BUILD_PROPERTY_FILE_LOCATION
- };
-
- Main.execute(
- "SwaggerPropertiesDoclet",
- new PrintWriter(err),
- new PrintWriter(warn),
- new PrintWriter(notice),
- SwaggerPropertiesDoclet.class.getName(),
- args);
-
- Properties props = generatedProperties();
- assertEquals("test method", props.getProperty("/test/test.GET.notes"));
- assertEquals("dummy value", props.getProperty("/test/test.GET.return"));
- assertEquals("dummy param", props.getProperty("/test/test.GET.param.param"));
- assertEquals("without value or path", props.getProperty("/test.POST.notes"));
- assertEquals("retval", props.getProperty("/test.POST.return"));
- assertEquals("param", props.getProperty("/test.POST.param.bar"));
- }
-
- private Properties generatedProperties() throws IOException {
- // read in the properties file created by the SwaggerPropertiesDoclet
- InputStream inputStream = new FileInputStream(GENERATED_PROPERTY_FILE);
- assertNotNull(inputStream);
-
- // check that the properties match the example sources
- Properties props = new Properties();
- props.load(inputStream);
- return props;
- }
-
- private static void deletePropertyFile() {
- File propertyFile = new File(GENERATED_PROPERTY_FILE);
- if (propertyFile.exists()) {
- propertyFile.delete();
- }
- }
-
- public class DummyDocErrorReporter implements DocErrorReporter {
-
-
- private final StringBuilder errors = new StringBuilder();
- private final StringBuilder notices = new StringBuilder();
- private final StringBuilder warnings = new StringBuilder();
-
- @Override
- public void printError(String error) {
- errors.append(error).append("\n");
- }
-
- @Override
- public void printError(SourcePosition position, String error) {
- errors.append(error).append("\n");
- }
-
- @Override
- public void printNotice(String notice) {
- notices.append(notice).append("\n");
- }
-
- @Override
- public void printNotice(SourcePosition position, String notice) {
- notices.append(notice).append("\n");
- }
-
- @Override
- public void printWarning(String warning) {
- warnings.append(warning).append("\n");
- }
-
- @Override
- public void printWarning(SourcePosition position, String warning) {
- warnings.append(warning).append("\n");
- }
-
- public String getErrors() {
- return errors.toString();
- }
-
- public String getNotices() {
- return notices.toString();
- }
-
- public String getWarnings() {
- return warnings.toString();
- }
- }
-}