/*** * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource * All rights reserved. * * 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 br.com.caelum.vraptor.http.ognl; import static com.google.common.base.Predicates.containsPattern; import static com.google.common.collect.Maps.filterKeys; import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import br.com.caelum.vraptor.converter.ConversionError; import br.com.caelum.vraptor.http.InvalidParameterException; import br.com.caelum.vraptor.http.ParameterNameProvider; import br.com.caelum.vraptor.http.ParametersProvider; import br.com.caelum.vraptor.ioc.Container; import br.com.caelum.vraptor.ioc.RequestScoped; import br.com.caelum.vraptor.resource.ResourceMethod; import br.com.caelum.vraptor.validator.Message; import br.com.caelum.vraptor.validator.ValidationMessage; import com.google.common.base.Defaults; /** * Provides parameters using ognl to parse expression values into parameter * values. * * @author guilherme silveira */ @RequestScoped public class OgnlParametersProvider implements ParametersProvider { private final ParameterNameProvider provider; private static final Logger logger = LoggerFactory.getLogger(OgnlParametersProvider.class); private final HttpServletRequest request; private final Container container; private final OgnlFacade ognl; public OgnlParametersProvider(ParameterNameProvider provider, HttpServletRequest request, Container container, OgnlFacade ognl) { this.provider = provider; this.request = request; this.container = container; this.ognl = ognl; } public Object[] getParametersFor(ResourceMethod method, List<Message> errors, ResourceBundle bundle) { String[] names = provider.parameterNamesFor(method.getMethod()); Type[] types = method.getMethod().getGenericParameterTypes(); Class[] classes = method.getMethod().getParameterTypes(); Object[] result = new Object[types.length]; for (int i = 0; i < types.length; i++) { Map<String, String[]> requestNames = parametersThatStartWith(names[i]); result[i] = createParameter(new Parameter(types[i], classes[i], names[i], method), requestNames, bundle, errors); } return result; } protected static class Parameter { public Type type; public Class clazz; public String name; public ResourceMethod method; public Parameter(Type type, Class clazz, String name, ResourceMethod method) { this.type = type; this.clazz = clazz; this.name = name; this.method = method; } public Class actualType() { if (type instanceof TypeVariable) { ParameterizedType superclass = (ParameterizedType) method.getResource().getType().getGenericSuperclass(); return (Class) superclass.getActualTypeArguments()[0]; } return clazz; } } protected Object createParameter(Parameter param, Map<String, String[]> requestNames, ResourceBundle bundle, List<Message> errors) { Object root; if (request.getAttribute(param.name) != null) { return request.getAttribute(param.name); } else if (param.clazz.isInterface() && container.canProvide(param.clazz)) { return container.instanceFor(param.clazz); } else if (requestNames.isEmpty()) { return Defaults.defaultValue(param.actualType()); } else { root = createRoot(param, requestNames, bundle, errors); if (root == null) { return null; } } ognl.startContext(param.name, param.type, root, bundle); for (Entry<String, String[]> parameter : requestNames.entrySet()) { String key = parameter.getKey().replaceFirst('^' + param.name + "\\.?", ""); String[] values = parameter.getValue(); setProperty(param.name, key, values, errors); } return ognl.get(param.name); } protected Object createRoot(Parameter param, Map<String, String[]> requestNames, ResourceBundle bundle, List<Message> errors) { if (requestNames.containsKey(param.name)) { String[] values = requestNames.get(param.name); try { return createSimpleParameter(param, values, bundle); } catch(ConversionError ex) { errors.add(new ValidationMessage(ex.getMessage(), param.name)); return null; } } try { return ognl.nullHandler().instantiate(param.actualType()); } catch (Exception ex) { throw new InvalidParameterException("unable to instantiate type " + param.type, ex); } } protected void setProperty(String name, String key, String[] values, List<Message> errors) { try { logger.debug("Applying {} with {}",key, values); ognl.setValue(name, key, values); } catch (ConversionError ex) { errors.add(new ValidationMessage(ex.getMessage(), key)); } } protected Object createSimpleParameter(Parameter param, String[] values, ResourceBundle bundle) { if (param.actualType().isArray()) { return createArray(param.actualType(), values, bundle); } if (List.class.isAssignableFrom(param.actualType())) { return createList(param.type, bundle, values); } return convert(param.actualType(), values[0], bundle); } protected Object convert(Class clazz, String value, ResourceBundle bundle) { return ognl.createAdapter(bundle).convert(value, clazz); } protected List createList(Type type, ResourceBundle bundle, String[] values) { List list = new ArrayList(); Class actual = getActualType(type); for (String value : values) { list.add(convert(actual, value, bundle)); } return list; } protected Object createArray(Class clazz, String[] values, ResourceBundle bundle) { Class arrayType = clazz.getComponentType(); Object array = Array.newInstance(arrayType, values.length); for (int i = 0; i < values.length; i++) { Array.set(array, i, convert(arrayType, values[i], bundle)); } return array; } protected Class getActualType(Type type) { return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } protected Map<String, String[]> parametersThatStartWith(String name) { Map<String, String[]> requestNames = filterKeys(request.getParameterMap(), containsPattern('^' + name)); return new TreeMap<String, String[]>(requestNames); } }