/**
* 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);
}
};
}
}