/** * Copyright © 2006-2016 Web Cohesion (info@webcohesion.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.webcohesion.enunciate.modules.jaxrs.model; import com.webcohesion.enunciate.EnunciateException; import com.webcohesion.enunciate.facets.Facet; import com.webcohesion.enunciate.facets.HasFacets; import com.webcohesion.enunciate.javac.decorations.Annotations; import com.webcohesion.enunciate.javac.decorations.TypeMirrorDecorator; import com.webcohesion.enunciate.javac.decorations.element.DecoratedExecutableElement; import com.webcohesion.enunciate.javac.decorations.type.DecoratedDeclaredType; import com.webcohesion.enunciate.javac.decorations.type.DecoratedTypeMirror; import com.webcohesion.enunciate.javac.decorations.type.TypeVariableContext; import com.webcohesion.enunciate.javac.javadoc.JavaDoc; import com.webcohesion.enunciate.javac.javadoc.ParamDocComment; import com.webcohesion.enunciate.javac.javadoc.ReturnDocComment; import com.webcohesion.enunciate.javac.javadoc.StaticDocComment; import com.webcohesion.enunciate.metadata.rs.*; import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.modules.jaxrs.EnunciateJaxrsContext; import com.webcohesion.enunciate.modules.jaxrs.model.util.JaxrsUtil; import com.webcohesion.enunciate.modules.jaxrs.model.util.RSParamDocComment; import com.webcohesion.enunciate.modules.jaxrs.model.util.ReturnWrappedDocComment; import com.webcohesion.enunciate.util.AnnotationUtils; import com.webcohesion.enunciate.util.TypeHintUtils; import io.swagger.annotations.*; import javax.annotation.security.RolesAllowed; import javax.lang.model.element.*; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.MirroredTypeException; import javax.ws.rs.*; import javax.ws.rs.core.Response; import java.util.*; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.webcohesion.enunciate.modules.jaxrs.model.Resource.extractPathComponents; /** * A JAX-RS resource method. * * @author Ryan Heaton */ public class ResourceMethod extends DecoratedExecutableElement implements HasFacets, PathContext { private static final Pattern CONTEXT_PARAM_PATTERN = Pattern.compile("\\{([^\\}]+)\\}"); private final EnunciateJaxrsContext context; private final String subpath; private final String label; private final String customParameterName; private final Set<String> httpMethods; private final Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> consumesMediaTypes; private final Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> producesMediaTypes; private final Resource parent; private final Set<ResourceParameter> resourceParameters; private final ResourceEntityParameter entityParameter; private final Map<String, Object> metaData = new HashMap<String, Object>(); private final List<? extends ResponseCode> statusCodes; private final List<? extends ResponseCode> warnings; private final Map<String, String> responseHeaders; private final ResourceRepresentationMetadata representationMetadata; private final Set<Facet> facets = new TreeSet<Facet>(); private final List<PathSegment> pathComponents; public ResourceMethod(ExecutableElement delegate, Resource parent, TypeVariableContext variableContext, EnunciateJaxrsContext context) { super(delegate, context.getContext().getProcessingEnvironment()); this.context = context; Set<String> httpMethods = loadHttpMethods(delegate); if (httpMethods.isEmpty()) { throw new IllegalStateException("A resource method must specify an HTTP method by using a request method designator annotation."); } this.httpMethods = httpMethods; this.consumesMediaTypes = loadConsumes(delegate, parent); this.producesMediaTypes = loadProduces(delegate, parent); String label = null; ResourceLabel resourceLabel = delegate.getAnnotation(ResourceLabel.class); if (resourceLabel != null) { label = resourceLabel.value(); if ("##default".equals(label)) { label = null; } } String subpath = null; Path pathInfo = delegate.getAnnotation(Path.class); if (pathInfo != null) { subpath = pathInfo.value(); } List<PathSegment> pathComponents = extractPathComponents(subpath); String customParameterName = null; ResourceEntityParameter entityParameter; Set<ResourceParameter> resourceParameters; ResourceRepresentationMetadata outputPayload; ResourceMethodSignature signatureOverride = delegate.getAnnotation(ResourceMethodSignature.class); if (signatureOverride == null) { entityParameter = null; resourceParameters = new TreeSet<ResourceParameter>(); //if we're not overriding the signature, assume we use the real method signature. for (VariableElement parameterDeclaration : getParameters()) { if (ResourceParameter.isResourceParameter(parameterDeclaration, context)) { resourceParameters.add(new ResourceParameter(parameterDeclaration, this)); } else if (ResourceParameter.isBeanParameter(parameterDeclaration)) { resourceParameters.addAll(ResourceParameter.getFormBeanParameters(parameterDeclaration, this)); } else if (!ResourceParameter.isSystemParameter(parameterDeclaration, context)) { entityParameter = new ResourceEntityParameter(this, parameterDeclaration, variableContext, context); customParameterName = parameterDeclaration.getSimpleName().toString(); } } //now resolve any type variables. DecoratedTypeMirror returnType = loadReturnType(); returnType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(variableContext.resolveTypeVariables(returnType, this.env), this.env); returnType.setDocComment(new ReturnDocComment(this)); outputPayload = returnType.isVoid() ? null : new ResourceRepresentationMetadata(returnType); } else { entityParameter = loadEntityParameter(signatureOverride); resourceParameters = loadResourceParameters(signatureOverride); outputPayload = loadOutputPayload(signatureOverride); } resourceParameters.addAll(loadExtraParameters(parent, context)); this.entityParameter = entityParameter; this.resourceParameters = resourceParameters; this.subpath = subpath; this.label = label; this.customParameterName = customParameterName; this.parent = parent; this.statusCodes = loadStatusCodes(parent); this.warnings = loadWarnings(parent); this.representationMetadata = outputPayload; this.facets.addAll(Facet.gatherFacets(delegate, context.getContext())); this.facets.addAll(parent.getFacets()); this.pathComponents = pathComponents; this.responseHeaders = loadResponseHeaders(parent); } protected Set<String> loadHttpMethods(ExecutableElement delegate) { Set<String> httpMethods = new TreeSet<String>(); List<? extends AnnotationMirror> mirrors = delegate.getAnnotationMirrors(); for (AnnotationMirror mirror : mirrors) { Element annotationDeclaration = mirror.getAnnotationType().asElement(); HttpMethod httpMethodInfo = annotationDeclaration.getAnnotation(HttpMethod.class); if (httpMethodInfo != null) { //request method designator found. httpMethods.add(httpMethodInfo.value()); } } final ApiOperation apiOperation = delegate.getAnnotation(ApiOperation.class); if (apiOperation != null && !apiOperation.httpMethod().isEmpty()) { httpMethods.clear(); //swagger annotation overrides JAX-RS annotations by definition. httpMethods.add(apiOperation.httpMethod()); } return httpMethods; } public Map<String, String> loadResponseHeaders(Resource parent) { Map<String, String> responseHeaders = new HashMap<String, String>(); ResponseHeaders responseHeaderInfo = getAnnotation(ResponseHeaders.class); if (responseHeaderInfo != null) { for (ResponseHeader header : responseHeaderInfo.value()) { responseHeaders.put(header.name(), header.description()); } } List<ResponseHeaders> inheritedResponseHeaders = AnnotationUtils.getAnnotations(ResponseHeaders.class, parent); for (ResponseHeaders inheritedResponseHeader : inheritedResponseHeaders) { for (ResponseHeader header : inheritedResponseHeader.value()) { responseHeaders.put(header.name(), header.description()); } } JavaDoc.JavaDocTagList doclets = getJavaDoc().get("ResponseHeader"); //support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; responseHeaders.put(header, doc); } } List<JavaDoc.JavaDocTagList> inheritedDoclets = AnnotationUtils.getJavaDocTags("ResponseHeader", parent); for (JavaDoc.JavaDocTagList inheritedDoclet : inheritedDoclets) { for (String doclet : inheritedDoclet) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; responseHeaders.put(header, doc); } } return responseHeaders; } public ArrayList<ResponseCode> loadWarnings(Resource parent) { ArrayList<ResponseCode> warnings = new ArrayList<ResponseCode>(); Warnings warningInfo = getAnnotation(Warnings.class); if (warningInfo != null) { for (com.webcohesion.enunciate.metadata.rs.ResponseCode code : warningInfo.value()) { ResponseCode rc = new ResponseCode(this); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } List<Warnings> inheritedWarnings = AnnotationUtils.getAnnotations(Warnings.class, parent); for (Warnings inheritedWarning : inheritedWarnings) { for (com.webcohesion.enunciate.metadata.rs.ResponseCode code : inheritedWarning.value()) { ResponseCode rc = new ResponseCode(this); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } JavaDoc.JavaDocTagList doclets = getJavaDoc().get("HTTPWarning"); if (doclets != null) { for (String doclet : doclets) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(this); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); warnings.add(rc); } catch (NumberFormatException e) { //fall through... } } } List<JavaDoc.JavaDocTagList> inheritedDoclets = AnnotationUtils.getJavaDocTags("HTTPWarning", parent); for (JavaDoc.JavaDocTagList inheritedDoclet : inheritedDoclets) { for (String doclet : inheritedDoclet) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(this); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); warnings.add(rc); } catch (NumberFormatException e) { //fall through... } } } return warnings; } protected Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> loadConsumes(ExecutableElement delegate, Resource parent) { Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> consumes; Consumes consumesInfo = delegate.getAnnotation(Consumes.class); if (consumesInfo != null) { consumes = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(JaxrsUtil.value(consumesInfo)); } else { consumes = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(parent.getConsumesMediaTypes()); } final ApiOperation apiOperation = delegate.getAnnotation(ApiOperation.class); if (apiOperation != null && !apiOperation.consumes().isEmpty()) { consumes = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(); for (String mediaType : apiOperation.consumes().split(",")) { mediaType = mediaType.trim(); if (!mediaType.isEmpty()) { consumes.add(new com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType(mediaType, 1.0F)); } } } return consumes; } protected Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> loadProduces(ExecutableElement delegate, Resource parent) { Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> produces; Produces producesInfo = delegate.getAnnotation(Produces.class); if (producesInfo != null) { produces = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(JaxrsUtil.value(producesInfo)); } else { produces = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(parent.getProducesMediaTypes()); } final ApiOperation apiOperation = delegate.getAnnotation(ApiOperation.class); if (apiOperation != null && !apiOperation.produces().isEmpty()) { produces = new TreeSet<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType>(); for (String mediaType : apiOperation.produces().split(",")) { mediaType = mediaType.trim(); if (!mediaType.isEmpty()) { produces.add(new com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType(mediaType, 1.0F)); } } } return produces; } protected DecoratedTypeMirror loadReturnType() { DecoratedTypeMirror returnType; TypeHint hintInfo = getAnnotation(TypeHint.class); if (hintInfo != null) { returnType = (DecoratedTypeMirror) TypeHintUtils.getTypeHint(hintInfo, this.env, getReturnType()); returnType.setDocComment(new ReturnDocComment(this)); } else { returnType = (DecoratedTypeMirror) getReturnType(); // in the case where the return type is com.sun.jersey.api.JResponse, // we can use the type argument to get the entity type if (returnType.isClass() && returnType.isInstanceOf("com.sun.jersey.api.JResponse")) { DecoratedDeclaredType jresponse = (DecoratedDeclaredType) returnType; if (!jresponse.getTypeArguments().isEmpty()) { DecoratedTypeMirror responseType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(jresponse.getTypeArguments().get(0), this.env); if (responseType.isDeclared()) { responseType.setDocComment(new ReturnDocComment(this)); returnType = responseType; } } } else if (returnType.isInstanceOf(Response.class) || returnType.isInstanceOf(java.io.InputStream.class)) { //generic response that doesn't have a type hint; we'll just have to assume return type of "object" DecoratedDeclaredType objectType = (DecoratedDeclaredType) TypeMirrorDecorator.decorate(this.env.getElementUtils().getTypeElement(Object.class.getName()).asType(), this.env); objectType.setDocComment(new ReturnDocComment(this)); returnType = objectType; } } final ApiOperation apiOperation = getAnnotation(ApiOperation.class); if (apiOperation != null) { DecoratedTypeMirror swaggerReturnType = Annotations.mirrorOf(new Callable<Class<?>>() { @Override public Class<?> call() throws Exception { return apiOperation.response(); } }, this.env, Void.class); if (swaggerReturnType != null) { if (!apiOperation.responseContainer().isEmpty()) { swaggerReturnType = (DecoratedTypeMirror) this.env.getTypeUtils().getArrayType(swaggerReturnType); swaggerReturnType.setDocComment(new ReturnDocComment(this)); } returnType = swaggerReturnType; } } JavaDoc localDoc = new JavaDoc(getDocComment(), null, null, this.env); if (localDoc.get("returnWrapped") != null) { //support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 String returnWrapped = localDoc.get("returnWrapped").get(0); String fqn = returnWrapped.substring(0, JavaDoc.indexOfFirstWhitespace(returnWrapped)).trim(); boolean array = false; if (fqn.endsWith("[]")) { array = true; fqn = fqn.substring(0, fqn.length() - 2); } TypeElement type = env.getElementUtils().getTypeElement(fqn); if (type != null) { returnType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getDeclaredType(type), this.env); if (array) { returnType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getArrayType(returnType), this.env); } returnType.setDocComment(new ReturnWrappedDocComment(this, returnWrapped)); } else { getContext().getContext().getLogger().info("Invalid @returnWrapped type: \"%s\" (doesn't resolve to a type).", fqn); } } return returnType; } public Set<ResourceParameter> loadExtraParameters(Resource parent, EnunciateJaxrsContext context) { Set<ResourceParameter> extraParameters = new TreeSet<ResourceParameter>(); JavaDoc localDoc = new JavaDoc(getDocComment(), null, null, this.env); JavaDoc.JavaDocTagList doclets = localDoc.get("RequestHeader"); //support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(doc), header, ResourceParameterType.HEADER, context)); } } List<JavaDoc.JavaDocTagList> inheritedDoclets = AnnotationUtils.getJavaDocTags("RequestHeader", parent); for (JavaDoc.JavaDocTagList inheritedDoclet : inheritedDoclets) { for (String doclet : inheritedDoclet) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(doc), header, ResourceParameterType.HEADER, context)); } } RequestHeaders requestHeaders = getAnnotation(RequestHeaders.class); if (requestHeaders != null) { for (RequestHeader header : requestHeaders.value()) { extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(header.description()), header.name(), ResourceParameterType.HEADER, context)); } } List<RequestHeaders> inheritedRequestHeaders = AnnotationUtils.getAnnotations(RequestHeaders.class, parent); for (RequestHeaders inheritedRequestHeader : inheritedRequestHeaders) { for (RequestHeader header : inheritedRequestHeader.value()) { extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(header.description()), header.name(), ResourceParameterType.HEADER, context)); } } final ApiOperation apiOperation = getAnnotation(ApiOperation.class); if (apiOperation != null) { io.swagger.annotations.ResponseHeader[] responseHeaders = apiOperation.responseHeaders(); for (io.swagger.annotations.ResponseHeader responseHeader : responseHeaders) { if (!responseHeader.name().isEmpty()) { extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(responseHeader.description()), responseHeader.name(), ResourceParameterType.HEADER, context)); } } } ApiImplicitParams swaggerImplicitParams = getAnnotation(ApiImplicitParams.class); if (swaggerImplicitParams != null) { for (ApiImplicitParam swaggerImplicitParam : swaggerImplicitParams.value()) { ResourceParameterType parameterType; try { parameterType = ResourceParameterType.valueOf(swaggerImplicitParam.paramType().toUpperCase()); } catch (IllegalArgumentException e) { continue; } extraParameters.add(new ExplicitResourceParameter(this, new StaticDocComment(swaggerImplicitParam.value()), swaggerImplicitParam.name(), parameterType, context)); } } return extraParameters; } public ArrayList<ResponseCode> loadStatusCodes(Resource parent) { ArrayList<ResponseCode> statusCodes = new ArrayList<ResponseCode>(); StatusCodes codes = getAnnotation(StatusCodes.class); if (codes != null) { for (com.webcohesion.enunciate.metadata.rs.ResponseCode code : codes.value()) { ResponseCode rc = new ResponseCode(this); rc.setCode(code.code()); rc.setCondition(code.condition()); for (ResponseHeader header : code.additionalHeaders()) { rc.setAdditionalHeader(header.name(), header.description()); } rc.setType((DecoratedTypeMirror) TypeHintUtils.getTypeHint(code.type(), this.env, null)); statusCodes.add(rc); } } List<StatusCodes> inheritedStatusCodes = AnnotationUtils.getAnnotations(StatusCodes.class, parent); for (StatusCodes inheritedStatusCode : inheritedStatusCodes) { for (com.webcohesion.enunciate.metadata.rs.ResponseCode code : inheritedStatusCode.value()) { ResponseCode rc = new ResponseCode(this); rc.setCode(code.code()); rc.setCondition(code.condition()); for (ResponseHeader header : code.additionalHeaders()) { rc.setAdditionalHeader(header.name(), header.description()); } rc.setType((DecoratedTypeMirror) TypeHintUtils.getTypeHint(code.type(), this.env, null)); statusCodes.add(rc); } } JavaDoc.JavaDocTagList doclets = getJavaDoc().get("HTTP"); //support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(this); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); statusCodes.add(rc); } catch (NumberFormatException e) { //fall through... } } } List<JavaDoc.JavaDocTagList> inheritedDoclets = AnnotationUtils.getJavaDocTags("HTTP", parent); for (JavaDoc.JavaDocTagList inheritedDoclet : inheritedDoclets) { for (String doclet : inheritedDoclet) { int firstspace = JavaDoc.indexOfFirstWhitespace(doclet); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(this); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); statusCodes.add(rc); } catch (NumberFormatException e) { //fall through... } } } ApiResponses swaggerResponses = getAnnotation(ApiResponses.class); if (swaggerResponses != null) { for (ApiResponse swaggerResponse : swaggerResponses.value()) { ResponseCode rc = new ResponseCode(this); rc.setCode(swaggerResponse.code()); rc.setCondition(swaggerResponse.message()); io.swagger.annotations.ResponseHeader[] headers = swaggerResponse.responseHeaders(); for (io.swagger.annotations.ResponseHeader header : headers) { if (!header.name().isEmpty()) { rc.setAdditionalHeader(header.name(), header.description()); } } statusCodes.add(rc); } } return statusCodes; } /** * Loads the explicit output payload. * * @param signatureOverride The method signature override. * @return The output payload (explicit in the signature override. */ protected ResourceRepresentationMetadata loadOutputPayload(ResourceMethodSignature signatureOverride) { DecoratedTypeMirror returnType = (DecoratedTypeMirror) getReturnType(); try { Class<?> outputType = signatureOverride.output(); if (outputType != ResourceMethodSignature.NONE.class) { TypeElement type = env.getElementUtils().getTypeElement(outputType.getName()); return new ResourceRepresentationMetadata(env.getTypeUtils().getDeclaredType(type), returnType.getDocValue()); } } catch (MirroredTypeException e) { DecoratedTypeMirror typeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror(), this.env); if (typeMirror.isDeclared()) { if (typeMirror.isInstanceOf(ResourceMethodSignature.class.getName() + ".NONE")) { return null; } return new ResourceRepresentationMetadata(typeMirror, returnType.getDocValue()); } else { throw new EnunciateException(toString() + ": Illegal output type (must be a declared type): " + typeMirror); } } return null; } @Override protected ParamDocComment createParamDocComment(VariableElement param) { return new RSParamDocComment(this, param.getSimpleName().toString()); } /** * Loads the overridden resource parameter values. * * @param signatureOverride The signature override. * @return The explicit resource parameters. */ protected Set<ResourceParameter> loadResourceParameters(ResourceMethodSignature signatureOverride) { TreeSet<ResourceParameter> params = new TreeSet<ResourceParameter>(); for (CookieParam cookieParam : signatureOverride.cookieParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, cookieParam.value()), cookieParam.value(), ResourceParameterType.COOKIE, context)); } for (MatrixParam matrixParam : signatureOverride.matrixParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, matrixParam.value()), matrixParam.value(), ResourceParameterType.MATRIX, context)); } for (QueryParam queryParam : signatureOverride.queryParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, queryParam.value()), queryParam.value(), ResourceParameterType.QUERY, context)); } for (PathParam pathParam : signatureOverride.pathParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, pathParam.value()), pathParam.value(), ResourceParameterType.PATH, context)); } for (HeaderParam headerParam : signatureOverride.headerParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, headerParam.value()), headerParam.value(), ResourceParameterType.HEADER, context)); } for (FormParam formParam : signatureOverride.formParams()) { params.add(new ExplicitResourceParameter(this, new RSParamDocComment(this, formParam.value()), formParam.value(), ResourceParameterType.FORM, context)); } return params; } /** * Loads the specified entity parameter according to the method signature override. * * @param signatureOverride The signature override. * @return The resource entity parameter. */ protected ResourceEntityParameter loadEntityParameter(ResourceMethodSignature signatureOverride) { try { Class<?> entityType = signatureOverride.input(); if (entityType != ResourceMethodSignature.NONE.class) { TypeElement type = env.getElementUtils().getTypeElement(entityType.getName()); return new ResourceEntityParameter(type, env.getTypeUtils().getDeclaredType(type), this.context.getContext().getProcessingEnvironment()); } } catch (MirroredTypeException e) { DecoratedTypeMirror typeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror(), this.context.getContext().getProcessingEnvironment()); if (typeMirror.isDeclared()) { if (typeMirror.isInstanceOf(ResourceMethodSignature.class.getName() + ".NONE")) { return null; } else { return new ResourceEntityParameter(((DeclaredType) typeMirror).asElement(), typeMirror, this.context.getContext().getProcessingEnvironment()); } } else { throw new EnunciateException(toString() + ": Illegal input type (must be a declared type): " + typeMirror); } } return null; } public EnunciateJaxrsContext getContext() { return context; } /** * The HTTP methods for invoking the method. * * @return The HTTP methods for invoking the method. */ public Set<String> getHttpMethods() { return httpMethods; } /** * Get the path components for this resource method. * * @return The path components. */ public List<PathSegment> getPathComponents() { List<PathSegment> components = new ArrayList<PathSegment>(); Resource parent = getParent(); if (parent != null) { components.addAll(parent.getPathComponents()); } components.addAll(this.pathComponents); return components; } /** * Builds the full URI path to this resource method. * * @return the full URI path to this resource method. */ public String getFullpath() { StringBuilder builder = new StringBuilder(); for (PathSegment component : getPathComponents()) { builder.append('/').append(component.getValue()); } return builder.toString(); } /** * The servlet pattern that can be applied to access this resource method. * * @return The servlet pattern that can be applied to access this resource method. */ public String getServletPattern() { StringBuilder builder = new StringBuilder(); String fullPath = getFullpath(); Matcher pathParamMatcher = CONTEXT_PARAM_PATTERN.matcher(fullPath); if (pathParamMatcher.find()) { builder.append(fullPath, 0, pathParamMatcher.start()).append("*"); } else { builder.append(fullPath); } return builder.toString(); } /** * The subpath for this resource method, if it exists. * * @return The subpath for this resource method, if it exists. */ public String getSubpath() { return subpath; } /** * The label for this resource method, if it exists. * * @return The subpath for this resource method, if it exists. */ public String getLabel() { return label; } /** * A slug for this method. * * @return A slug for this method. */ public String getSlug() { String slug = ""; Resource parent = this.parent; while (parent instanceof SubResource) { slug = parent.getSimpleName() + "_" + slug; parent = parent.getParent(); } slug = slug + getSimpleName(); return slug; } /** * The name of a custom request parameter (e.g String password -> "password"). * * @return the name of the custom parameter */ public String getCustomParameterName() { return customParameterName; } /** * The resource that holds this resource method. * * @return The resource that holds this resource method. */ public Resource getParent() { return parent; } /** * The MIME types that are consumed by this method. * * @return The MIME types that are consumed by this method. */ public Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> getConsumesMediaTypes() { return consumesMediaTypes; } /** * The MIME types that are produced by this method. * * @return The MIME types that are produced by this method. */ public Set<com.webcohesion.enunciate.modules.jaxrs.model.util.MediaType> getProducesMediaTypes() { return producesMediaTypes; } /** * The list of resource parameters that this method requires to be invoked. * * @return The list of resource parameters that this method requires to be invoked. */ public Set<ResourceParameter> getResourceParameters() { TreeSet<ResourceParameter> resourceParams = new TreeSet<ResourceParameter>(this.resourceParameters); resourceParams.addAll(getParent().getResourceParameters()); return resourceParams; } /** * The entity parameter. * * @return The entity parameter, or null if none. */ public ResourceEntityParameter getEntityParameter() { return entityParameter; } /** * The output payload for this resource. * * @return The output payload for this resource. */ public ResourceRepresentationMetadata getRepresentationMetadata() { return this.representationMetadata; } /** * The potential status codes. * * @return The potential status codes. */ public List<? extends ResponseCode> getStatusCodes() { return this.statusCodes; } /** * The potential warnings. * * @return The potential warnings. */ public List<? extends ResponseCode> getWarnings() { return this.warnings; } /** * The metadata associated with this resource. * * @return The metadata associated with this resource. */ public Map<String, Object> getMetaData() { return Collections.unmodifiableMap(this.metaData); } /** * Put metadata associated with this resource. * * @param name The name of the metadata. * @param data The data. */ public void putMetaData(String name, Object data) { this.metaData.put(name, data); } /** * The response headers that are expected on this resource method. * * @return The response headers that are expected on this resource method. */ public Map<String, String> getResponseHeaders() { return responseHeaders; } /** * The facets here applicable. * * @return The facets here applicable. */ public Set<Facet> getFacets() { return facets; } /** * The security roles for this method. * * @return The security roles for this method. */ public Set<String> getSecurityRoles() { TreeSet<String> roles = new TreeSet<String>(); RolesAllowed rolesAllowed = getAnnotation(RolesAllowed.class); if (rolesAllowed != null) { Collections.addAll(roles, rolesAllowed.value()); } Resource parent = getParent(); if (parent != null) { roles.addAll(parent.getSecurityRoles()); } return roles; } }