org.jooby.internal.spec.RouteParamCollector.java Source code

Java tutorial

Introduction

Here is the source code for org.jooby.internal.spec.RouteParamCollector.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.jooby.internal.spec;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;

import org.jooby.spec.RouteParam;
import org.jooby.spec.RouteParamType;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.google.common.collect.Maps;
import com.google.inject.util.Types;

public class RouteParamCollector extends VoidVisitorAdapter<Context> {

    private static final String BODY = "<body>";

    private List<RouteParam> params = new ArrayList<>();

    private Set<String> names = new LinkedHashSet<>();

    private Map<String, Object> doc;

    private String method;

    private String pattern;

    public RouteParamCollector(final Map<String, Object> doc, final String method, final String pattern) {
        this.doc = doc;
        this.method = method;
        this.pattern = pattern;
    }

    public RouteParamCollector() {
        this(Collections.emptyMap(), "", "");
    }

    public List<RouteParam> accept(final Node node, final Context ctx) {
        if (node instanceof LambdaExpr) {
            ((LambdaExpr) node).getParameters().forEach(p -> {
                names.add(p.getId().toStringWithoutComments());
            });
        }
        node.accept(this, ctx);
        return params;
    }

    @Override
    public void visit(final MethodCallExpr node, final Context ctx) {
        List<MethodCallExpr> nodes = dump(node);
        for (MethodCallExpr n : nodes) {
            List<MethodCallExpr> call = call(n);
            if (call.size() > 0) {
                MethodCallExpr cparam = call.get(0);
                String cname = cparam.getName();
                String pname = cparam.getArgs().stream().findFirst().map(it -> ((StringLiteralExpr) it).getValue())
                        .orElse(BODY);
                Entry<Type, Object> typeDef = type(call.get(1), ctx);
                String doc = (String) this.doc.get(pname.equals(BODY) ? "body" : pname);
                params.add(new RouteParamImpl(pname, typeDef.getKey(), type(typeDef.getKey(), pname, cname),
                        typeDef.getValue(), doc));
            }
        }
    }

    private RouteParamType type(final Type type, final String name, final String cname) {
        switch (cname) {
        case "header":
            return RouteParamType.HEADER;
        case "body":
            return RouteParamType.BODY;
        default: {
            if (type.getTypeName() == "org.jooby.Upload") {
                return RouteParamType.FILE;
            }
            if (pattern.indexOf(":" + name) >= 0 || pattern.indexOf("{" + name + "}") >= 0) {
                return RouteParamType.PATH;
            } else {
                if (method.equals("POST")) {
                    return RouteParamType.FORM;
                } else {
                    return RouteParamType.QUERY;
                }
            }
        }
        }
    }

    @SuppressWarnings("rawtypes")
    private Entry<Type, Object> type(final MethodCallExpr expr, final Context ctx) {
        String name = expr.getName();
        Type type = null;
        switch (name) {
        case "charValue": {
            type = char.class;
        }
            break;
        case "byteValue": {
            type = byte.class;
        }
            break;
        case "booleanValue": {
            type = boolean.class;
        }
            break;
        case "shortValue": {
            type = short.class;
        }
            break;
        case "intValue": {
            type = int.class;
        }
            break;
        case "longValue": {
            type = long.class;
        }
            break;
        case "floatValue": {
            type = float.class;
        }
            break;
        case "doubleValue": {
            type = double.class;
        }
            break;
        case "value": {
            type = String.class;
        }
            break;
        case "toList": {
            type = List.class;
        }
            break;
        case "toSet": {
            type = Set.class;
        }
            break;
        case "toSortedSet": {
            type = SortedSet.class;
        }
            break;
        case "toOptional": {
            type = Optional.class;
        }
            break;
        case "toUpload": {
            type = ctx.resolveType(expr, "org.jooby.Upload").get();
        }
            break;
        }
        Object defaultValue = null;
        List<Expression> args = expr.getArgs();
        if (args.size() > 0) {
            Expression arg = args.get(0);
            Object result = arg.accept(new LiteralCollector(), ctx);
            if (result instanceof Type) {
                if (type == null) {
                    type = (Type) result;
                } else {
                    type = Types.newParameterizedType(type, (Type) result);
                }
            } else if (result instanceof Enum) {
                Enum e = (Enum) result;
                type = e.getDeclaringClass();
                defaultValue = e.name();
            } else {
                if (result != null) {
                    defaultValue = result;
                } else {
                    Map<String, Object> vals = new StaticValueCollector().accept(expr, ctx);
                    defaultValue = arg.toStringWithoutComments();
                    defaultValue = vals.getOrDefault(defaultValue, defaultValue);
                }
            }
        } else if (name.startsWith("to") && name.length() > 2 && !name.equals("toUpload")) {
            type = Types.newParameterizedType(type, String.class);
        }
        return Maps.immutableEntry(type, defaultValue);
    }

    private List<MethodCallExpr> dump(final Node n) {
        List<MethodCallExpr> dump = new ArrayList<>();
        if (n instanceof MethodCallExpr) {
            dump.add((MethodCallExpr) n);
        }
        List<Node> children = n.getChildrenNodes();
        for (Node c : children) {
            dump.addAll(dump(c));
        }
        return dump;
    }

    private List<MethodCallExpr> call(final MethodCallExpr n) {
        LinkedList<MethodCallExpr> call = new LinkedList<>();
        Expression it = n;
        Expression prev = it;
        while (it instanceof MethodCallExpr) {
            MethodCallExpr local = (MethodCallExpr) it;
            call.addFirst(local);
            prev = it;
            it = local.getScope();
        }
        // req.
        it = it == null ? prev : it;
        if (names.contains(it.toStringWithoutComments())) {
            // param(id).value
            if (call.size() == 2) {
                String cname = call.get(0).getName();
                if ("param".equals(cname) || "header".equals(cname)) {
                    List<Expression> args = call.get(0).getArgs();
                    if (args.size() == 1 && args.get(0) instanceof StringLiteralExpr) {
                        return call;
                    }
                } else if ("body".equals(call.get(0).getName())) {
                    List<Expression> args = call.get(0).getArgs();
                    if (args.size() == 0) {
                        return call;
                    }
                }
            }
        }
        return Collections.emptyList();
    }

}