/* * Copyright 2014 Eric F. Savage, code@efsavage.com * * 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 com.ajah.swagger; import lombok.AllArgsConstructor; import lombok.Data; import org.springframework.stereotype.Service; /** * Scans a class for Swagger annotations. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, <a * href="mailto:code@efsavage.com">code@efsavage.com</a>. */ @Service @Data @AllArgsConstructor public class SwaggerScanner { // /** // * Adds errors based on thrown exceptions. // * // * @param api // * The API we're documenting. // * @param exceptionTypes // * The exceptions that are declared. // */ // private static void addErrors(final SwaggerApi api, final Class<?>[] exceptionTypes) { // if (exceptionTypes == null || exceptionTypes.length == 0) { // return; // } // for (final Class<?> exceptionType : exceptionTypes) { // log.fine("Adding exception: " + exceptionType.getName()); // api.description += "<br />Throws " + exceptionType.getSimpleName(); // } // // // final SwaggerModel typeModel = new // // SwaggerModel(type.getSimpleName()); // // if (this.models.get(api.path, typeModel.getId()) != null) { // // return; // // } // // this.models.put(api.path, typeModel.getId(), typeModel); // // log.fine("Storing models under: " + api.path); // // final Field[] fields = type.getFields(); // // log.fine(fields.length + " fields"); // // for (final Field field : fields) { // // log.fine("Field: " + field.getName()); // // log.fine("\tAccessible: " + field.isAccessible()); // // log.fine("\tSynthetic: " + field.isSynthetic()); // // log.fine("\tModifiers: " + field.getModifiers()); // // log.fine("\tPublic: " + Modifier.isPublic(field.getModifiers())); // // if (Modifier.isPublic(field.getModifiers())) { // // final SwaggerModelProperty fieldProp = new // // SwaggerModelProperty(convert(field.getType()), false); // // // lookupResponseProp.items = new SwaggerItemsRef("League"); // // typeModel.getProperties().put(field.getName(), fieldProp); // // // api.models.put(lookupResponseModel.getId(), // // // lookupResponseModel); // // if (!isBasic(field.getType())) { // // addModels(api, field.getType()); // // } // // } // // } // } // // private static String convert(final Class<?> type) { // if (ToStringable.class.isAssignableFrom(type)) { // return "string"; // } // switch (type.getSimpleName()) { // case "String": // return "string"; // case "BigDecimal": // return "integer"; // case "Date": // return "integer"; // case "double": // return "double"; // case "boolean": // return "boolean"; // case "int": // return "integer"; // case "List": // return "array"; // default: // return type.getSimpleName(); // } // } // // private static boolean isBasic(final Class<?> type) { // switch (type.getSimpleName()) { // case "String": // return true; // case "BigDecimal": // return true; // case "Date": // return true; // case "double": // return true; // case "boolean": // return true; // case "int": // return true; // default: // return false; // } // } // // private boolean scanned = false; // private String basePackage; // private ClassPathScanningCandidateComponentProvider scanner; // private ListMap<String, SwaggerApi> apis = new ListMap<>(); // // private MapMap<String, String, SwaggerModel> models = new MapMap<>(); // // List<SwaggerApiShort> apiShorts = new ArrayList<>(); // // Paranamer paranamer = new CachingParanamer(); // // /** // * Constructs a scanner looking for {@link Api} annotated classes. // */ // public SwaggerScanner() { // this.scanner = new ClassPathScanningCandidateComponentProvider(false); // this.scanner.addIncludeFilter(new AnnotationTypeFilter(Api.class)); // } // // private void addModels(final SwaggerApi api, final Class<?> type) { // log.fine("Adding model: " + type.getName()); // final SwaggerModel typeModel = new SwaggerModel(type.getSimpleName()); // if (this.models.get(api.path, typeModel.getId()) != null) { // return; // } // this.models.put(api.path, typeModel.getId(), typeModel); // log.fine("Storing models under: " + api.path); // final Field[] fields = type.getFields(); // log.fine(fields.length + " fields"); // for (final Field field : fields) { // log.fine("Field: " + field.getName()); // log.fine("\tAccessible: " + field.isAccessible()); // log.fine("\tSynthetic: " + field.isSynthetic()); // log.fine("\tModifiers: " + field.getModifiers()); // log.fine("\tPublic: " + Modifier.isPublic(field.getModifiers())); // if (Modifier.isPublic(field.getModifiers())) { // final SwaggerModelProperty fieldProp = new SwaggerModelProperty(convert(field.getType()), false); // // lookupResponseProp.items = new SwaggerItemsRef("League"); // typeModel.getProperties().put(field.getName(), fieldProp); // // api.models.put(lookupResponseModel.getId(), // // lookupResponseModel); // if (field.getType().equals("array")) { // fieldProp.items = new SwaggerItemsRef("stuff"); // } else if (!isBasic(field.getType())) { // addModels(api, field.getType()); // } // } // } // } // // /** // * Fetches an api by its path. // * // * @param path // * The path to this API. // * @return The API, or null if none match. // */ // public List<SwaggerApi> getApi(final String path) { // if (this.apis.size() == 0) { // scanForApi(); // } // return this.apis.get(path); // } // // /** // * Fetches models for an api by its path. // * // * @param path // * The path to this API. // * @return The API, or null if none match. // */ // public Map<String, SwaggerModel> getModels(final String path) { // log.fine("Fetching models for " + path); // if (this.models.size() == 0) { // scanForApi(); // } // return this.models.get(path); // } // // private List<SwaggerParameter> getParameters(final Method method) { // final List<SwaggerParameter> swaggerParameters = new ArrayList<>(); // final Class<?>[] params = method.getParameterTypes(); // String[] parameterNames = this.paranamer.lookupParameterNames(method); // if (parameterNames.length != params.length) { // parameterNames = null; // } // int index = 0; // for (int i = 0; i < params.length; i++) { // if (HttpServletRequest.class.isAssignableFrom(params[i]) || HttpServletResponse.class.isAssignableFrom(params[i])) { // continue; // } // final SwaggerParameter swaggerParameter = new SwaggerParameter(parameterNames == null ? (params[i].getSimpleName() + "(" + index++ + ")") : parameterNames[i], convert(params[i]), "?"); // swaggerParameters.add(swaggerParameter); // } // return swaggerParameters; // } // // /** // * Scans for {@link Api} annotated classes. // * // * @return A list of short descriptor beans of the annotated classes. // */ // public List<SwaggerApiShort> scanForApi() { // if (this.scanned) { // return this.apiShorts; // } // for (final BeanDefinition bd : this.scanner.findCandidateComponents(this.basePackage)) { // Api annotation; // Class<?> clazz; // try { // clazz = Class.forName(bd.getBeanClassName()); // annotation = AnnotationUtils.findAnnotation(clazz, Api.class); // } catch (final ClassNotFoundException e) { // // This really shouldn't happen since we just got the class // // name. // log.log(Level.SEVERE, e.getMessage(), e); // continue; // } // final String path = annotation.basePath(); // final String description = annotation.description(); // final SwaggerApiShort apiShort = new SwaggerApiShort("/" + path, description); // this.apiShorts.add(apiShort); // // for (final Method method : clazz.getMethods()) { // final ApiOperation apiOperation = AnnotationUtils.findAnnotation(method, ApiOperation.class); // if (apiOperation == null) { // continue; // } // final RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); // if (requestMapping == null) { // log.warning("Found @ApiOperation on " + clazz.getName() + "." + method.getName() + " but no @RequestMapping"); // continue; // } // RequestMethod[] requestMethods = requestMapping.method(); // if (requestMethods == null || requestMethods.length == 0) { // requestMethods = new RequestMethod[] { RequestMethod.GET, RequestMethod.POST }; // } // for (final RequestMethod requestMethod : requestMapping.method()) { // // TODO Handle multiple request mappings? // String apiPath = requestMapping.value()[0]; // if (apiPath.startsWith(path)) { // apiPath = apiPath.substring(path.length()); // } else { // log.warning("API Path " + apiPath + " does not start with base path " + path); // } // final SwaggerApi api = new SwaggerApi(apiPath, description); // this.apis.putValue(path, api); // log.fine("Found API " + path + " in class" + bd.getBeanClassName()); // // final SwaggerOperation operation = new SwaggerOperation(); // operation.method = requestMethod == null ? "GET" : requestMethod.name(); // operation.responseClass = method.getReturnType().getSimpleName(); // operation.notes = apiOperation.notes(); // operation.nickname = apiOperation.nickname(); // operation.summary = apiOperation.value(); // // operation.parameters = getParameters(method); // // api.operations.add(operation); // // // SwaggerModelProperty lookupResponseProp = new // // SwaggerModelProperty("array", false); // // lookupResponseProp.items = new SwaggerItemsRef("League"); // // lookupResponseModel.getProperties().put("leagues", // // lookupResponseProp); // // addModels(api, method.getReturnType()); // addErrors(api, method.getExceptionTypes()); // // } // } // // } // this.scanned = true; // return this.apiShorts; // } }