RequestObjectParamDispatcherMixin.java
/***************************************************************************
Copyright 2014 Emily Estes
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 net.metanotion.web.concrete;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.metanotion.util.ChainedDictionary;
import net.metanotion.util.Dictionary;
import net.metanotion.util.Dispatcher;
import net.metanotion.util.Message;
import net.metanotion.util.Pair;
import net.metanotion.util.Unknown;
import net.metanotion.web.HttpMethod;
import net.metanotion.web.RequestObject;
/** This dispatcher assumes the data type is a pair containing a method name and an instance of
{@link net.metanotion.web.RequestObject}, {@link net.metanotion.util.Dictionary}, or {@link java.util.Map} attempting
to dispatch against a method which has some parameters which are instances of {@link net.metanotion.web.RequestObject}.
This class populates the parameter map with the variables from the dictionary/map/requestobject payload, and passes an
instance of RequestObject through to any underlying parameters which need the RequestObject.
@param <O> The type of the object the delegating dispatcher evaluates against.
*/
public final class RequestObjectParamDispatcherMixin<O> implements Dispatcher<Unknown, Map.Entry<String,Object>> {
private static final Logger logger = LoggerFactory.getLogger(RequestObjectParamDispatcherMixin.class);
/** The keys to his map are method names, and the values are method info objects containing information extracted
from annotations. The set is all of the parameters that are mapped directly to the RequestObject instance
instead of a key-value from the {@link net.metanotion.web.RequestObject#get} method.
*/
private final Map<String,Set<String>> methods = new HashMap<>();
/** The keys to this map are method names, and the values are a list of parameter names for the method in
parameter order. This is used to map parameter names to parameter order. */
private final Map<String,Iterable<String>> paramMap;
private final Dispatcher<O, Map.Entry<String,Object>> mapDisp;
/** Create an instance that dispatches the RequestObject parameters of the provided class using the provided
parameter map(see {@link net.metanotion.util.NamedParameterMapper}) and delegating to the provided dispatcher.
@param klazz the class to search for RequestObject parameters.
@param paramMap the map of parameter names to order.
@param mapDisp The dispatcher to delegate to.
*/
public RequestObjectParamDispatcherMixin(final Class klazz,
final Map<String,Iterable<String>> paramMap,
final Dispatcher<O, Map.Entry<String,Object>> mapDisp) {
this.paramMap = paramMap;
this.mapDisp = mapDisp;
logger.debug("Finding RequestObject params");
for(final Method m: klazz.getMethods()) {
final String name = m.getName();
if(!paramMap.containsKey(name)) { continue; }
final Set<String> requestObjectParams = new HashSet<>();
methods.put(name, requestObjectParams);
final Class[] pTypes = m.getParameterTypes();
int i = 0;
for(final String pName: paramMap.get(name)) {
if(RequestObject.class.isAssignableFrom(pTypes[i])) { requestObjectParams.add(pName); }
i++;
if(i >= pTypes.length) { break; }
}
}
}
private static final class Msg<O> implements Message<Unknown> {
private final Map.Entry<String,Object> data;
private final Dispatcher<O, Map.Entry<String,Object>> mapDisp;
public Msg(Map.Entry<String,Object> data, Dispatcher<O, Map.Entry<String,Object>> mapDisp) {
this.data = data;
this.mapDisp = mapDisp;
}
@Override public Class<Unknown> receiverType() { return Unknown.class; }
@Override public Object call(final Unknown o) {
final Message<O> m = mapDisp.dispatch(data);
logger.debug("Type: {}", m.receiverType());
logger.debug("O: {}", o);
return m.call(o.lookupInterface(m.receiverType()));
}
}
private static final RequestObject convert(final String method, final Object o) {
if(o instanceof RequestObject) { return (RequestObject) o; }
if(o instanceof Dictionary) { return new BasicRequestObject(HttpMethod.GET, method, (Dictionary) o); }
if(o instanceof Map) { return new BasicRequestObject(HttpMethod.GET, method, (Map) o); }
return new BasicRequestObject(method);
}
@Override public Message<Unknown> dispatch(final Map.Entry<String,Object> data) {
final String method = data.getKey();
logger.debug("Dispatch {}", method);
final Iterable<String> methodInfo = paramMap.get(method);
if(methodInfo == null) { return new Msg<O>(data, mapDisp); }
final RequestObject ro = convert(method, data.getValue());
final Set<String> requestObjectParams = methods.get(method);
final Map<String,Object> params = new HashMap<>();
for(final String param: methodInfo) {
if(requestObjectParams.contains(param)) {
params.put(param, ro);
} else {
params.put(param, ro.get(param));
}
}
return new Msg<O>(new Pair<String,Object>(method, new ChainedDictionary<String,Object>(params, ro)), mapDisp);
}
}