/*
* #%L restdoc-doclet %% Copyright (C) 2012 IG Group %% 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. #L%
*/
package com.iggroup.oss.restdoclet.doclet.type.builder;
import static com.iggroup.oss.restdoclet.doclet.util.AnnotationUtils.elementValue;
import static com.iggroup.oss.restdoclet.doclet.util.AnnotationUtils.isAnnotated;
import static com.iggroup.oss.restdoclet.doclet.util.AnnotationUtils.parseValueAnnotation;
import java.util.ArrayList;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.iggroup.oss.restdoclet.doclet.type.BodyParameter;
import com.iggroup.oss.restdoclet.doclet.type.Method;
import com.iggroup.oss.restdoclet.doclet.type.ModelParameter;
import com.iggroup.oss.restdoclet.doclet.type.PathParameter;
import com.iggroup.oss.restdoclet.doclet.type.RequestParameter;
import com.iggroup.oss.restdoclet.doclet.type.ResponseParameter;
import com.iggroup.oss.restdoclet.doclet.type.RestParameter;
import com.iggroup.oss.restdoclet.doclet.type.Uri;
import com.iggroup.oss.restdoclet.doclet.util.DocTypeUtils;
import com.iggroup.oss.restdoclet.doclet.util.DocletUtils;
import com.iggroup.oss.restdoclet.doclet.util.NameValuePair;
import com.iggroup.oss.restdoclet.doclet.util.ParameterNamePredicate;
import com.iggroup.oss.restdoclet.doclet.util.RequestMappingParamsParser;
import com.sun.javadoc.AnnotationValue;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.Parameter;
/**
* This class populates a Method class from JavaDoc types.
*/
public class MethodBuilder {
/**
* Logger
*/
private static final Logger LOG = Logger.getLogger(MethodBuilder.class);
/**
* Populate a Method type
*
* @param method type to populate
* @param methodDoc method doc
* @param baseUri controller base uri
* @return populated type
*/
public Method build(Method method, final MethodDoc methodDoc,
final String baseUri) {
assert method != null;
assert methodDoc != null;
LOG.info("Initialising method: " + methodDoc);
MethodDoc md = DocletUtils.findMethodDocumentation(methodDoc);
initName(method, methodDoc);
initRequestMethod(method, methodDoc);
initProduces(method, methodDoc);
initConsumes(method, methodDoc);
initHeaders(method, methodDoc);
initJavadoc(method, md);
initRequestParams(method, methodDoc.parameters(), md.paramTags());
initPathParams(method, methodDoc.parameters(), md.paramTags());
initModelParams(method, methodDoc.parameters(), md.paramTags());
initBodyParams(method, methodDoc.parameters(), md.paramTags());
initRestParams(method, methodDoc, baseUri);
initResponseParams(method, methodDoc);
return method;
}
/**
* Initialises the name of this method.
*
* @param methodDoc the method's Java documentation object.
*/
private void initName(Method method, final MethodDoc methodDoc) {
method.setName(methodDoc.name());
LOG.debug(methodDoc.name());
}
/**
* Initialises the HTTP request-method this method is mapped to.
*
* @param methodDoc the method's Java documentation object.
*/
private void initRequestMethod(Method method, final MethodDoc methodDoc) {
final AnnotationValue value =
elementValue(methodDoc, RequestMapping.class, "method");
LOG.debug(method.getName());
if (value == null) {
/* default */
LOG.debug("No method found.... defaulting to GET");
method.setRequestMethod(RequestMethod.GET.toString());
} else {
/*
* Java 1.6: AnnotationValue.toString() returns qualified classname
* example:
* org.springframework.web.bind.annotation.RequestMethod.GET Java
* 1.5: AnnotationValue.toString() returns simple classname example:
* GET
*/
if (value.toString().contains(".")) {
method.setRequestMethod(value.toString().substring(
value.toString().lastIndexOf(".") + 1));
} else {
method.setRequestMethod(value.toString());
}
}
LOG.debug(method.getRequestMethod());
}
/**
* Initialises the documentation of this method.
*
* @param methodDoc the method's Java documentation object.
*/
private void initJavadoc(Method method, final MethodDoc methodDoc) {
LOG.debug(method.getName());
method.setJavadoc(DocletUtils.preserveJavadocFormatting(methodDoc
.commentText()));
}
private void initProduces(Method method, final MethodDoc methodDoc) {
final AnnotationValue value =
elementValue(methodDoc, RequestMapping.class, "produces");
LOG.debug(method.getName());
if (value != null) {
method.setProduces(value.toString().replace("\"", ""));
LOG.debug("PRODUCES " + method.getProduces());
}
}
private void initConsumes(Method method, final MethodDoc methodDoc) {
final AnnotationValue value =
elementValue(methodDoc, RequestMapping.class, "consumes");
LOG.debug(method.getName());
if (value != null) {
method.setConsumes(value.toString().replace("\"", ""));
LOG.debug("CONSUMES " + method.getConsumes());
}
}
private void initHeaders(Method method, final MethodDoc methodDoc) {
final AnnotationValue value =
elementValue(methodDoc, RequestMapping.class, "headers");
LOG.debug(method.getName());
if (value != null) {
method.setHeaders(value.toString().replace("\"", ""));
LOG.debug("HEADERS " + method.getHeaders());
}
}
/**
* Initialises the request-parameters of this method.
*
* @param params the method's parameters.
* @param tags the parameters' Java documentation tags.
*/
private void initRequestParams(Method method, final Parameter[] params,
final ParamTag[] tags) {
LOG.debug(method.getName());
ArrayList<RequestParameter> requestParams =
new ArrayList<RequestParameter>();
for (Parameter param : params) {
if (isAnnotated(param, RequestParam.class)) {
requestParams.add(new RequestParameterBuilder().build(
new RequestParameter(), param, tags));
}
}
method.setRequestParams(requestParams);
}
/**
* Initialises the path-parameters of this method.
*
* @param params the method's path parameters.
* @param tags the parameters' Java documentation tags.
*/
private void initPathParams(Method method, final Parameter[] params,
final ParamTag[] tags) {
LOG.debug(method.getName());
ArrayList<PathParameter> pathParams = new ArrayList<PathParameter>();
for (Parameter param : params) {
if (isAnnotated(param, PathVariable.class)) {
pathParams.add(new PathParameterBuilder().build(
new PathParameter(), param, tags));
}
}
method.setPathParams(pathParams);
}
/**
* Initialises the model-parameters of this method.
*
* @param params the method's parameters.
* @param tags the parameters' Java documentation tags.
*/
private void initModelParams(Method method, final Parameter[] params,
final ParamTag[] tags) {
LOG.debug(method.getName());
ArrayList<ModelParameter> modelParams = new ArrayList<ModelParameter>();
for (Parameter param : params) {
if (isAnnotated(param, ModelAttribute.class)) {
modelParams.add(new ModelParameterBuilder().build(
new ModelParameter(), param, tags));
}
}
method.setModelParams(modelParams);
}
/**
* Initialises the request body parameters of this method.
*
* @param params the method's parameters.
* @param tags the parameters' Java documentation tags.
*/
private void initBodyParams(Method method, final Parameter[] params,
final ParamTag[] tags) {
LOG.debug(method.getName());
ArrayList<BodyParameter> bodyParams = new ArrayList<BodyParameter>();
for (Parameter param : params) {
if (isAnnotated(param, RequestBody.class)) {
LOG.debug("YYYYYYYYYYYYYYYYYY");
bodyParams.add(new BodyParameterBuilder().build(
new BodyParameter(), param, tags));
}
}
method.setBodyParams(bodyParams);
}
/**
* Initialises the REST-parameters of this method.
*
* @param method method to initialise
* @param methodDoc the method's Java documentation object.
* @param baseUri the controller base uri
*/
private void initRestParams(Method method, final MethodDoc methodDoc,
final String baseUri) {
LOG.debug(method.getName());
ArrayList<RestParameter> restParams = new ArrayList<RestParameter>();
for (NameValuePair pair : new RequestMappingParamsParser(elementValue(
methodDoc, RequestMapping.class, "params")).parse()) {
final Predicate predicate =
new ParameterNamePredicate(pair.getName());
if (!CollectionUtils.exists(method.getRequestParams(), predicate)) {
LOG.debug(pair.getName() + " - " + pair.getValue());
restParams.add(new RestParameter(pair));
}
}
AnnotationValue urlAnnotation =
elementValue(methodDoc, RequestMapping.class, "value");
if (urlAnnotation != null) {
Boolean deprecatedMatch = false;
String[] methodUris = parseValueAnnotation(urlAnnotation);
String[] deprecatedURIs = DocTypeUtils.getDeprecatedURIs(methodDoc);
for (final String uri : methodUris) {
LOG.debug("uri:" + baseUri + uri);
boolean deprecated = false;
if (deprecatedURIs != null) {
for (final String deprecatedUri : deprecatedURIs) {
LOG.debug("deprecated:" + deprecatedUri);
if (StringUtils.equals(deprecatedUri, uri)) {
LOG.debug("=DEPRECATED");
deprecated = true;
deprecatedMatch = true;
break;
}
}
}
method.getUris().add(new Uri(baseUri + uri, deprecated));
}
if (deprecatedURIs != null && !deprecatedMatch) {
LOG.warn("Deprecated URI tag on method " + methodDoc.name()
+ " does not match any service URIs.");
}
}
method.setRestParams(restParams);
}
private void initResponseParams(Method method, final MethodDoc methodDoc) {
ArrayList<ResponseParameter> responseParams =
new ArrayList<ResponseParameter>();
// Add return type
if (methodDoc.returnType() != null) {
responseParams.add(new ResponseParameter(DocTypeUtils
.getTypeName(methodDoc.returnType()), DocTypeUtils
.getTypeName(methodDoc.returnType()), DocletUtils
.preserveJavadocFormatting(DocTypeUtils.getReturnDoc(methodDoc))));
}
// Add any checked exceptions
for (ClassDoc exceptionDoc : methodDoc.thrownExceptions()) {
responseParams.add(new ResponseParameter(DocTypeUtils
.getTypeName(exceptionDoc),
DocTypeUtils.getTypeName(exceptionDoc), DocletUtils
.preserveJavadocFormatting(DocTypeUtils
.getTypeDoc(exceptionDoc))));
}
method.setResponseParams(responseParams);
}
}