/**
* 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.spring_web.model;
import com.webcohesion.enunciate.javac.decorations.element.DecoratedTypeElement;
import com.webcohesion.enunciate.javac.decorations.element.DecoratedVariableElement;
import com.webcohesion.enunciate.javac.decorations.type.DecoratedTypeMirror;
import com.webcohesion.enunciate.javac.decorations.type.TypeMirrorUtils;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import com.webcohesion.enunciate.util.TypeHintUtils;
import org.springframework.web.bind.annotation.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import java.lang.annotation.IncompleteAnnotationException;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* PathParameter for a Spring Web request.
*
* @author Ryan Heaton
*/
public class SimpleRequestParameter extends RequestParameter {
private final PathContext context;
private final String parameterName;
private final String defaultValue;
private final String typeName;
private final boolean multivalued;
private final boolean required;
public SimpleRequestParameter(Element declaration, PathContext context) {
this(declaration, context, null);
}
public SimpleRequestParameter(Element declaration, PathContext context, ResourceParameterType defaultType) {
super(declaration, context.getContext().getContext().getProcessingEnvironment());
this.context = context;
String parameterName = declaration.getSimpleName().toString();
String typeName = null;
String defaultValue = null;
boolean required = false;
MatrixVariable matrixParam = declaration.getAnnotation(MatrixVariable.class);
if (matrixParam != null) {
required = matrixParam.required();
parameterName = matrixParam.value();
if (parameterName.isEmpty()) {
try {
parameterName = matrixParam.name();
}
catch (IncompleteAnnotationException e) {
//fall through; 'matrixParam.name' was added in 4.2.
}
}
if (parameterName.isEmpty()) {
parameterName = declaration.getSimpleName().toString();
}
if (!ValueConstants.DEFAULT_NONE.equals(matrixParam.defaultValue())) {
defaultValue = matrixParam.defaultValue();
required = false;
}
typeName = "matrix";
}
RequestParam queryParam = declaration.getAnnotation(RequestParam.class);
if (queryParam != null) {
required = queryParam.required();
parameterName = queryParam.value();
if (parameterName.isEmpty()) {
try {
parameterName = queryParam.name();
}
catch (IncompleteAnnotationException e) {
//fall through; 'queryParameter.name' was added in 4.2.
}
}
if (parameterName.isEmpty()) {
parameterName = declaration.getSimpleName().toString();
}
if (!ValueConstants.DEFAULT_NONE.equals(queryParam.defaultValue())) {
defaultValue = queryParam.defaultValue();
required = false;
}
typeName = "query";
}
PathVariable pathParam = declaration.getAnnotation(PathVariable.class);
if (pathParam != null) {
required = true;
parameterName = pathParam.value();
if (parameterName.isEmpty()) {
parameterName = declaration.getSimpleName().toString();
}
typeName = "path";
}
RequestHeader headerParam = declaration.getAnnotation(RequestHeader.class);
if (headerParam != null) {
required = headerParam.required();
parameterName = headerParam.value();
if (parameterName.isEmpty()) {
try {
parameterName = headerParam.name();
}
catch (IncompleteAnnotationException e) {
//fall through; 'headerParam.name' was added in 4.2.
}
}
if (parameterName.isEmpty()) {
parameterName = declaration.getSimpleName().toString();
}
if (!ValueConstants.DEFAULT_NONE.equals(headerParam.defaultValue())) {
defaultValue = headerParam.defaultValue();
required = false;
}
typeName = "header";
}
CookieValue cookieParam = declaration.getAnnotation(CookieValue.class);
if (cookieParam != null) {
required = cookieParam.required();
parameterName = cookieParam.value();
if (parameterName.isEmpty()) {
try {
parameterName = cookieParam.name();
}
catch (IncompleteAnnotationException e) {
//fall through; 'name' was added in a later spring version.
}
}
if (parameterName.isEmpty()) {
parameterName = declaration.getSimpleName().toString();
}
if (!ValueConstants.DEFAULT_NONE.equals(cookieParam.defaultValue())) {
defaultValue = cookieParam.defaultValue();
required = false;
}
typeName = "cookie";
}
RequestPart formParam = declaration.getAnnotation(RequestPart.class);
if (formParam != null) {
required = formParam.required();
parameterName = formParam.value();
if (parameterName.isEmpty()) {
try {
parameterName = formParam.name();
}
catch (IncompleteAnnotationException e) {
//fall through; 'formParam.name' was added in 4.2.
}
}
typeName = "form";
}
if (typeName == null) {
typeName = defaultType == null ? "custom" : defaultType.name().toLowerCase();
}
DecoratedTypeMirror parameterType = loadType();
this.multivalued = parameterType.isArray() || parameterType.isCollection();
this.parameterName = parameterName;
this.typeName = typeName;
this.defaultValue = defaultValue;
this.required = required;
if (delegate instanceof DecoratedVariableElement) {
getJavaDoc().setValue(((DecoratedVariableElement)delegate).getDocComment());
}
}
protected DecoratedTypeMirror loadType() {
TypeHint hint = getAnnotation(TypeHint.class);
if (hint != null) {
return (DecoratedTypeMirror) TypeHintUtils.getTypeHint(hint, context.getContext().getContext().getProcessingEnvironment(), asType());
}
return (DecoratedTypeMirror) asType();
}
/**
* The parameter name.
*
* @return The parameter name.
*/
@Override
public String getParameterName() {
return parameterName;
}
/**
* The default value.
*
* @return The default value.
*/
@Override
public String getDefaultValue() {
return defaultValue;
}
/**
* The type of the parameter.
*
* @return The type of the parameter.
*/
@Override
public String getTypeName() {
return this.typeName;
}
/**
* Whether this parameter is multi-valued.
*
* @return Whether this parameter is multi-valued.
*/
@Override
public boolean isMultivalued() {
return multivalued;
}
@Override
public boolean isRequired() {
return this.required;
}
@Override
protected ResourceParameterConstraints loadConstraints() {
for (PathSegment segment : this.context.getPathSegments()) {
if (getParameterName().equals(segment.getVariable()) && segment.getRegex() != null) {
return new ResourceParameterConstraints.Regex(segment.getRegex());
}
}
DecoratedTypeMirror type = loadType();
//unwrap it, if possible.
DecoratedTypeMirror componentType = TypeMirrorUtils.getComponentType(type, this.context.getContext().getContext().getProcessingEnvironment());
if (componentType != null) {
type = componentType;
}
//unbox it, if possible.
try {
type = (DecoratedTypeMirror) this.context.getContext().getContext().getProcessingEnvironment().getTypeUtils().unboxedType(type);
}
catch (Exception e) {
//no-op; not unboxable.
}
if (type.isPrimitive()) {
return new ResourceParameterConstraints.Primitive(type.getKind());
}
else if (type.isEnum()) {
List<VariableElement> enumConstants = ((DecoratedTypeElement) ((DeclaredType) type).asElement()).enumValues();
Set<String> values = new TreeSet<String>();
for (VariableElement enumConstant : enumConstants) {
values.add(enumConstant.getSimpleName().toString());
}
return new ResourceParameterConstraints.Enumeration(values);
}
return new ResourceParameterConstraints.UnboundString();
}
protected ResourceParameterDataType loadDataType() {
DecoratedTypeMirror type = loadType();
//unwrap it, if possible.
DecoratedTypeMirror componentType = TypeMirrorUtils.getComponentType(type, this.context.getContext().getContext().getProcessingEnvironment());
if (componentType != null) {
type = componentType;
}
//unbox it, if possible.
try {
type = (DecoratedTypeMirror) this.context.getContext().getContext().getProcessingEnvironment().getTypeUtils().unboxedType(type);
}
catch (Exception e) {
//no-op; not unboxable.
}
if (type.isPrimitive()) {
switch (type.getKind()) {
case BOOLEAN:
return ResourceParameterDataType.BOOLEAN;
case INT:
return ResourceParameterDataType.INTEGER;
case DOUBLE:
case FLOAT:
case LONG:
case SHORT:
return ResourceParameterDataType.NUMBER;
default:
return ResourceParameterDataType.STRING;
}
}
else if (type.isEnum()) {
return ResourceParameterDataType.STRING;
}
else if (getTypeName().contains("form")) {
if (type.isInstanceOf(String.class)) {
return ResourceParameterDataType.STRING;
}
else {
return ResourceParameterDataType.FILE;
}
}
else {
return ResourceParameterDataType.STRING;
}
}}