/** * 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.mvc; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.Map; import java.util.Optional; import javax.inject.Named; import org.jooby.Cookie; import org.jooby.Err; import org.jooby.Mutant; import org.jooby.Request; import org.jooby.Response; import org.jooby.Route; import org.jooby.Session; import org.jooby.mvc.Body; import org.jooby.mvc.Flash; import org.jooby.mvc.Header; import org.jooby.mvc.Local; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; @SuppressWarnings({"rawtypes", "unchecked" }) public class RequestParam { private interface GetValue { Object apply(Request req, Response rsp, RequestParam param) throws Exception; } private static final TypeLiteral<Header> headerType = TypeLiteral.get(Header.class); private static final TypeLiteral<Body> bodyType = TypeLiteral.get(Body.class); private static final TypeLiteral<Local> localType = TypeLiteral.get(Local.class); private static final TypeLiteral<Flash> flashType = TypeLiteral.get(Flash.class); private static final Map<Object, GetValue> injector; static { Builder<Object, GetValue> builder = ImmutableMap.<Object, GetValue> builder(); /** * Body */ builder.put(bodyType, (req, rsp, param) -> req.body().to(param.type)); /** * Request */ builder.put(TypeLiteral.get(Request.class), (req, rsp, param) -> req); /** * Route */ builder.put(TypeLiteral.get(Route.class), (req, rsp, param) -> req.route()); /** * Response */ builder.put(TypeLiteral.get(Response.class), (req, rsp, param) -> rsp); /** * Session */ builder.put(TypeLiteral.get(Session.class), (req, rsp, param) -> req.session()); builder.put(TypeLiteral.get(Types.newParameterizedType(Optional.class, Session.class)), (req, rsp, param) -> req.ifSession()); /** * Cookie */ builder.put(TypeLiteral.get(Cookie.class), (req, rsp, param) -> req.cookies().stream() .filter(c -> c.name().equalsIgnoreCase(param.name)).findFirst().get()); builder.put(TypeLiteral.get(Types.listOf(Cookie.class)), (req, rsp, param) -> req.cookies()); builder.put(TypeLiteral.get(Types.newParameterizedType(Optional.class, Cookie.class)), (req, rsp, param) -> req.cookies().stream() .filter(c -> c.name().equalsIgnoreCase(param.name)).findFirst()); /** * Header */ builder.put(headerType, (req, rsp, param) -> req.header(param.name).to(param.type)); /** * Local */ builder.put(localType, (req, rsp, param) -> { if (param.type.getRawType() == Map.class) { return req.attributes(); } Optional local = req.ifGet(param.name); if (param.optional) { return local; } return local.get(); }); /** * Flash */ builder.put(flashType, (req, rsp, param) -> { if (param.type.getRawType() == Map.class) { return req.flash(); } return param.optional? req.ifFlash(param.name) : req.flash(param.name); }); injector = builder.build(); } public final String name; public final TypeLiteral type; private final GetValue strategy; private boolean optional; public RequestParam(final Parameter parameter, final String name) { this(parameter, name, parameter.getParameterizedType()); } public RequestParam(final AnnotatedElement elem, final String name, final Type type) { this.name = name; this.type = TypeLiteral.get(type); this.optional = this.type.getRawType() == Optional.class; final TypeLiteral strategyType; if (elem.getAnnotation(Header.class) != null) { strategyType = headerType; } else if (elem.getAnnotation(Body.class) != null) { strategyType = bodyType; } else if (elem.getAnnotation(Local.class) != null) { strategyType = localType; } else if (elem.getAnnotation(Flash.class) != null) { strategyType = flashType; } else { strategyType = this.type; } this.strategy = injector.getOrDefault(strategyType, param()); } public Object value(final Request req, final Response rsp) throws Throwable { return strategy.apply(req, rsp, this); } public static String nameFor(final Parameter param) { String name = findName(param); return name == null ? (param.isNamePresent() ? param.getName() : null) : name; } private static String findName(final AnnotatedElement elem) { Named named = elem.getAnnotation(Named.class); if (named == null) { com.google.inject.name.Named gnamed = elem .getAnnotation(com.google.inject.name.Named.class); if (gnamed == null) { Header header = elem.getAnnotation(Header.class); if (header == null) { return null; } return Strings.emptyToNull(header.value()); } return gnamed.value(); } return Strings.emptyToNull(named.value()); } private static final GetValue param() { return (req, rsp, param) -> { Mutant mutant = req.param(param.name); if (mutant.isSet() || param.optional) { return mutant.to(param.type); } try { return req.params().to(param.type); } catch (Err ex) { // force parsing return mutant.to(param.type); } }; } }