org.leandreck.endpoints.processor.model.MethodNodeFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.leandreck.endpoints.processor.model.MethodNodeFactory.java

Source

/**
 * Copyright  2016 Mathias Kowalzik (Mathias.Kowalzik@leandreck.org)
 *
 * 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 org.leandreck.endpoints.processor.model;

import org.leandreck.endpoints.annotations.TypeScriptIgnore;
import org.leandreck.endpoints.processor.config.TemplateConfiguration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;

import static java.util.stream.Collectors.toList;
import static org.leandreck.endpoints.processor.model.StringUtil.definedValue;

/**
 */
class MethodNodeFactory {

    private final TypeNodeFactory typeNodeFactory;
    private final RequestMappingFactory requestMappingFactory;

    MethodNodeFactory(final TemplateConfiguration configuration, final Types typeUtils,
            final Elements elementUtils) {
        typeNodeFactory = new TypeNodeFactory(configuration, typeUtils, elementUtils);
        requestMappingFactory = new RequestMappingFactory();
    }

    MethodNode createMethodNode(final ExecutableElement methodElement, final DeclaredType containingType) {
        final RequestMapping requestMapping = requestMappingFactory.createRequestMapping(methodElement);

        final String name = defineName(methodElement);
        final boolean ignored = defineIgnored(methodElement, requestMapping);
        if (ignored) {
            return new MethodNode(name, true);
        }
        final String url = defineUrl(requestMapping);
        final List<String> httpMethods = defineHttpMethods(requestMapping);
        final TypeNode returnType = defineReturnType(methodElement, containingType);

        final List<? extends VariableElement> parameters = methodElement.getParameters();
        final TypeNode requestBodyType = defineRequestBodyType(parameters, containingType);
        final List<TypeNode> pathVariables = definePathVariableTypes(parameters, containingType);
        final List<TypeNode> queryParams = defineQueryParamsTypes(parameters, containingType);

        return new MethodNode(name, url, false, httpMethods, returnType, requestBodyType, pathVariables,
                queryParams);
    }

    private TypeNode defineReturnType(final ExecutableElement methodElement, final DeclaredType containingType) {
        final TypeMirror returnMirror = methodElement.getReturnType();
        return typeNodeFactory.createTypeNode(returnMirror, containingType);
    }

    private List<TypeNode> definePathVariableTypes(final List<? extends VariableElement> parameters,
            final DeclaredType containingType) {
        return parameters
                .stream().filter(p -> p.getAnnotation(PathVariable.class) != null).map(
                        it -> typeNodeFactory.createTypeNode(it,
                                definedValue(it.getAnnotation(PathVariable.class).name(),
                                        it.getAnnotation(PathVariable.class).value()),
                                containingType))
                .collect(toList());
    }

    private List<TypeNode> defineQueryParamsTypes(final List<? extends VariableElement> parameters,
            final DeclaredType containingType) {
        return parameters.stream().filter(p -> p.getAnnotation(RequestParam.class) != null)
                .filter(it -> !it.asType().toString().equals("org.springframework.web.multipart.MultipartFile"))
                .map(it -> typeNodeFactory
                        .createTypeNode(it,
                                definedValue(it.getAnnotation(RequestParam.class).name(),
                                        it.getAnnotation(RequestParam.class).value()),
                                containingType))
                .collect(toList());
    }

    private TypeNode defineRequestBodyType(final List<? extends VariableElement> parameters,
            final DeclaredType containingType) {
        final Optional<? extends VariableElement> optionalRequestBody = parameters.stream()
                .filter(it -> it.getAnnotation(RequestBody.class) != null
                        || it.asType().toString().equals("org.springframework.web.multipart.MultipartFile"))
                .findFirst();

        final TypeNode requestBodyType;
        if (optionalRequestBody.isPresent()) {
            final VariableElement paramElement = optionalRequestBody.get();
            requestBodyType = typeNodeFactory.createTypeNode(paramElement, null, containingType);
        } else {
            requestBodyType = null; // reverted any | null-Type => typeNodeFactory.createTypeNode("body", null, null, null);
        }

        return requestBodyType;
    }

    private static List<String> defineHttpMethods(final RequestMapping requestMapping) {

        final List<String> methods = new ArrayList<>();
        if (requestMapping != null) {
            return Arrays.stream(requestMapping.method())
                    .map(requestMethod -> requestMethod.toString().toLowerCase(Locale.ENGLISH)).collect(toList());
        }

        return methods;
    }

    private static boolean defineIgnored(final ExecutableElement methodElement,
            final RequestMapping requestMapping) {
        boolean hasIgnoreAnnotation = methodElement.getAnnotation(TypeScriptIgnore.class) != null;
        boolean hasRequestMappingAnnotation = requestMapping != null;
        boolean producesJson = hasRequestMappingAnnotation && Arrays.stream(requestMapping.produces())
                .map(value -> value.startsWith(MediaType.APPLICATION_JSON_VALUE)).reduce(false, (a, b) -> a || b);

        boolean isPublic = methodElement.getModifiers().contains(Modifier.PUBLIC);
        return hasIgnoreAnnotation || !isPublic || !hasRequestMappingAnnotation || !producesJson;
    }

    private static String defineUrl(final RequestMapping requestMapping) {
        if (requestMapping != null) {
            final String[] mappings = requestMapping.value();
            if (mappings.length > 0) {
                return mappings[0];
            }
        }
        return "";
    }

    private static String defineName(final ExecutableElement methodElement) {
        return methodElement.getSimpleName().toString();
    }
}