/**
* Copyright 2014 Bayes Technologies
*
* 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 me.bayes.vertx.vest.binding;
import javax.ws.rs.Consumes;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
/**
* All the properties that the context should contain.
*
* TODO: Extract this to a concern on its own. This should have a builder so it
* can be substituted for a custom implementation.
*
* @author Kevin Bayes
* @version 1.1
* @since 1.1
*/
public class RouteBindingHolder {
// private static final String KEY_TEMPLATE = "%s %s";
private final Map<String, List<MethodBinding>> getBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> putBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> postBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> deleteBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> optionsBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> headBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> traceBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> connectBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> patchBindings = new HashMap<>(0);
private final Map<String, List<MethodBinding>> emptyMap = Collections.unmodifiableMap(new HashMap<String, List<MethodBinding>>(0));
public void foreach(Function function) {
foreach("GET", function, getBindings);
foreach("PUT", function, putBindings);
foreach("POST", function, postBindings);
foreach("DELETE", function, deleteBindings);
foreach("OPTIONS", function, optionsBindings);
foreach("HEAD", function, headBindings);
foreach("TRACE", function, traceBindings);
foreach("CONNECT", function, connectBindings);
foreach("PATCH", function, patchBindings);
}
private void foreach(String method, Function function, Map<String, List<MethodBinding>> bindings) {
for(Entry<String, List<MethodBinding>> binding : bindings.entrySet()) {
applyFunction(method, function, binding);
}
}
private void applyFunction(String method, Function function, Entry<String, List<MethodBinding>> binding) {
try {
function.apply(method, binding.getKey(), binding.getValue());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void addBinding(HttpMethod httpMethod, String path,
Consumes consumes, Produces produces, Object instance,
Class<?> clazz, Method method) {
addBinding(getMethodBindingMap(httpMethod), path, consumes, produces, instance, clazz, method);
}
private void addBinding(Map<String, List<MethodBinding>> bindings, String path,
Consumes consumes, Produces produces, Object instance,
Class<?> clazz, Method method) {
if(emptyMap == bindings) {
return;
}
List<MethodBinding> bindings_ = null;
if(bindings.containsKey(path)) {
bindings_ = bindings.get(path);
} else {
bindings_ = new ArrayList<>(1);
bindings.put(path, bindings_);
}
bindings_.add(new MethodBinding(instance, clazz, method,
consumes == null ? new String[]{MediaType.TEXT_PLAIN} : consumes.value(),
produces == null ? new String[]{MediaType.TEXT_PLAIN} : produces.value()));
}
private Map<String, List<MethodBinding>> getMethodBindingMap(HttpMethod method) {
return getMethodBindingMap(method.value());
}
private Map<String, List<MethodBinding>> getMethodBindingMap(String verb) {
switch (verb) {
case HttpMethod.DELETE:
return deleteBindings;
case HttpMethod.GET:
return getBindings;
case HttpMethod.HEAD:
return headBindings;
case HttpMethod.OPTIONS:
return optionsBindings;
case HttpMethod.POST:
return postBindings;
case HttpMethod.PUT:
return putBindings;
case "TRACE":
return traceBindings;
case "CONNECT":
return connectBindings;
case "PATCH":
return patchBindings;
default:
return emptyMap;
}
}
public MethodBinding getBinding(String method, String path, String contentType, String accepts){
List<MethodBinding> bindings = getMethodBindingMap(method).get(path);
if(bindings != null) {
for(MethodBinding binding : bindings) {
if(binding.hasConsumes(contentType) && binding.hasProduces(accepts)) {
return binding;
}
}
}
return null;
}
/**
* This is just a holder for object to method bindings with the produces and consumes available.
* This will help decide which object to call when receiving a request.
*
* @author Kevin Bayes
*/
public static class MethodBinding {
private final String[] consumes;
private final String[] produces;
private Object delegate;
private Class<?> clazz;
private Method method;
public MethodBinding(Object delegate, Class<?> clazz, Method method, String[] consumes, String[] produces) {
this.delegate = delegate;
this.consumes = consumes;
this.produces = produces;
this.clazz = clazz;
this.method = method;
}
public Object getDelegate() {
return delegate;
}
public Class<?> getClazz() {
return clazz;
}
public Method getMethod() {
return method;
}
public String[] getConsumes() {
return consumes;
}
public String[] getProduces() {
return produces;
}
public boolean hasConsumes(String contentType) {
for(String consume : consumes) {
if(consume.equals(contentType)) {
return true;
}
}
return false;
}
public boolean hasProduces(String accepts) {
for(String produce : produces) {
if(produce.equals(accepts)) {
return true;
}
}
return false;
}
}
}