/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.jaxrs.internal.wrappers.params; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Logger; import javax.ws.rs.CookieParam; import javax.ws.rs.DefaultValue; import javax.ws.rs.Encoded; import javax.ws.rs.FormParam; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.reflect.ConstructorUtils; import org.apache.commons.lang.reflect.MethodUtils; import org.restlet.data.Form; import org.restlet.data.Header; import org.restlet.data.Parameter; import org.restlet.data.Reference; import org.restlet.engine.converter.ConverterHelper; import org.restlet.engine.converter.ConverterUtils; import org.restlet.engine.resource.VariantInfo; import org.restlet.ext.jaxrs.internal.core.CallContext; import org.restlet.ext.jaxrs.internal.core.PathSegmentImpl; import org.restlet.ext.jaxrs.internal.core.ThreadLocalizedContext; import org.restlet.ext.jaxrs.internal.core.ThreadLocalizedUriInfo; import org.restlet.ext.jaxrs.internal.exceptions.ConvertCookieParamException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertHeaderParamException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertMatrixParamException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertParameterException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertPathParamException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertQueryParamException; import org.restlet.ext.jaxrs.internal.exceptions.ConvertRepresentationException; import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathParamTypeException; import org.restlet.ext.jaxrs.internal.exceptions.IllegalTypeException; import org.restlet.ext.jaxrs.internal.exceptions.MissingAnnotationException; import org.restlet.ext.jaxrs.internal.todo.NotYetImplementedException; import org.restlet.ext.jaxrs.internal.util.Converter; import org.restlet.ext.jaxrs.internal.util.Util; import org.restlet.ext.jaxrs.internal.wrappers.WrapperUtil; import org.restlet.ext.jaxrs.internal.wrappers.provider.ExtensionBackwardMapping; import org.restlet.ext.jaxrs.internal.wrappers.provider.JaxRsProviders; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.util.Series; /** * Contains a list of parameters for JAX-RS constructors, (sub) resource methods * and sub resource locators. * * @author Stephan Koops */ public class ParameterList { /** * Abstract super class for access to @*Param. */ abstract static class AbstractParamGetter implements ParamGetter { /** * The type of the collection. null, if this parameter do not represent * a collection. */ protected final Class<Collection<?>> collType; /** * The class to convert to. If this object getter represents an *Param * annotated parameter, and it should be to an array or collection of * something, this value contains not the collection/array type, but the * generic type of it. */ protected final Class<?> convertTo; /** * The default value for this parameter (if given) */ protected final DefaultValue defaultValue; /** * True, if this parameter should be an array, otherwise false. If true, * the {@link #collType} must be set to a {@link List}. */ protected final boolean isArray; protected final ThreadLocalizedContext tlContext; @SuppressWarnings({ "unchecked", "rawtypes" }) AbstractParamGetter(DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext) { this.tlContext = tlContext; this.defaultValue = defaultValue; if (convToCl.isArray()) { this.convertTo = convToCl.getComponentType(); this.collType = (Class) ArrayList.class; this.isArray = true; } else if (convToGen instanceof ParameterizedType) { final ParameterizedType parametrizedType = (ParameterizedType) convToGen; final Type[] argTypes = parametrizedType .getActualTypeArguments(); if (argTypes[0] instanceof Class) { this.convertTo = (Class<?>) argTypes[0]; } else { throw new NotYetImplementedException( "Sorry, only Class is supported, but is " + argTypes[0]); } // TEST @*Param with array/collection and generic parameter this.collType = collType(parametrizedType); this.isArray = false; } else { this.convertTo = convToCl; this.collType = null; this.isArray = false; } } protected Object convertParamValue(String firstHeader) throws ConvertParameterException { return convertParamValue(firstHeader, this.defaultValue); } /** * Converts the given paramValue (found in the path, query, matrix or * header) into the given paramClass. * * @param paramValue * @param defaultValue * @return * @throws ConvertParameterException * @see PathParam * @see MatrixParam * @see QueryParam * @see HeaderParam * @see CookieParam */ protected Object convertParamValue(String paramValue, DefaultValue defaultValue) throws ConvertParameterException { if (decoding() && (paramValue != null)) { paramValue = Reference.decode(paramValue); } else if ((paramValue == null) && (defaultValue != null)) { paramValue = defaultValue.value(); } if (this.convertTo.equals(String.class)) { return paramValue; } if (this.convertTo.isPrimitive()) { if ((paramValue != null) && (paramValue.length() <= 0)) { paramValue = defaultValue.value(); } return getParamValueForPrimitive(paramValue); } return convertParamValueInner(paramValue, defaultValue); } /** * Converts the given value without any decoding. * * @param paramValue * @param defaultValue * @return * @throws ConvertParameterException * @throws WebApplicationException * if the conversion method throws an * WebApplicationException. */ private Object convertParamValueInner(String paramValue, DefaultValue defaultValue) throws ConvertParameterException, WebApplicationException { Object convertWithConverterUtils = convertWithConverterUtils(paramValue); if (convertWithConverterUtils != null) { return convertWithConverterUtils; } String value = paramValue; if (StringUtils.isEmpty(paramValue)) { if (defaultValue == null || defaultValue.value() == null) { return null; } value = defaultValue.value(); } try { return ConstructorUtils.invokeConstructor(convertTo, value); } catch (Exception e) { handleExceptionOnInvocation(value, e); } // fixes for: // https://github.com/restlet/restlet-framework-java/issues/645 try { return MethodUtils.invokeStaticMethod(convertTo, convertTo.isEnum() ? "fromString" : "valueOf", value); } catch (Exception e) { handleExceptionOnInvocation(value, e); } try { return MethodUtils.invokeStaticMethod(convertTo, convertTo.isEnum() ? "valueOf" : "fromString", value); } catch (Exception e) { handleExceptionOnInvocation(value, e); } throw ConvertParameterException .object(this.convertTo, value, new Exception( "Target object has no String constructor, valueOf or fromString method.")); } protected Object convertParamValues(Iterator<String> paramValueIter) throws ConvertParameterException { final Collection<Object> coll = createColl(); while (paramValueIter.hasNext()) { final String queryParamValue = paramValueIter.next(); final Object convertedValue = convertParamValue( queryParamValue, null); if (convertedValue != null) { coll.add(convertedValue); } } if (coll.isEmpty()) { coll.add(convertParamValue(null)); } if (this.isArray) { return Util.toArray(coll, this.convertTo); } return unmodifiable(coll); } private Object convertWithConverterUtils(String paramValue) { Object result = null; if (this.tlContext.get().getRequest().getEntity() != null && paramValue != null) { try { ConverterHelper converterHelper = ConverterUtils .getBestHelper(this.tlContext.get().getRequest() .getEntity(), this.convertTo, null); List<VariantInfo> variants = converterHelper .getVariants(this.convertTo); for (int i = 0; result == null && i < variants.size(); i++) { result = converterHelper.toObject( new StringRepresentation(paramValue, variants .get(i).getMediaType()), this.convertTo, null); } } catch (Exception exception) { // -- don't worry about it...proceed with reflective calls } } return result; } /** * @return an new created instance of {@link #collType}. Returns null, * if collType is null. */ @SuppressWarnings("unchecked") protected <A> Collection<A> createColl() { try { if (this.collType != null) { return (Collection<A>) this.collType.newInstance(); } return null; } catch (Exception e) { throw new RuntimeException( "Could not instantiate the collection type " + this.collType, e); } } protected abstract boolean decoding(); /** * @return the concrete value of this parameter for the current request. */ public abstract Object getParamValue(); protected Object getParamValueForPrimitive(String paramValue) throws ConvertParameterException { try { if (this.convertTo == Integer.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_INT; } return new Integer(paramValue); } if (this.convertTo == Double.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_DOUBLE; } return new Double(paramValue); } if (this.convertTo == Float.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_FLOAT; } return new Float(paramValue); } if (this.convertTo == Byte.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_BYTE; } return new Byte(paramValue); } if (this.convertTo == Long.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_LONG; } return new Long(paramValue); } if (this.convertTo == Short.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_SHORT; } return new Short(paramValue); } if (this.convertTo == Character.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_CHAR; } if (paramValue.length() == 1) { return paramValue.charAt(0); } throw ConvertParameterException.primitive(this.convertTo, paramValue, null); } if (this.convertTo == Boolean.TYPE) { if (((paramValue == null) || (paramValue.length() <= 0))) { return DEFAULT_BOOLEAN; } if (paramValue.equalsIgnoreCase("true")) { return Boolean.TRUE; } if (paramValue.equalsIgnoreCase("false")) { return Boolean.FALSE; } throw ConvertParameterException.primitive(this.convertTo, paramValue, null); } } catch (IllegalArgumentException e) { throw ConvertParameterException.primitive(this.convertTo, paramValue, e); } String warning; if (this.convertTo == Void.TYPE) { warning = "an object should be converted to a void; but this could not be here"; } else { warning = "an object should be converted to a " + this.convertTo + ", but here are only primitives allowed."; } localLogger.warning(warning); final ResponseBuilder rb = javax.ws.rs.core.Response.serverError(); rb.entity(warning); throw new WebApplicationException(rb.build()); } public Object getValue() { return getParamValue(); } private void handleExceptionOnInvocation(String value, Exception e) throws ConvertParameterException { final Throwable cause = e.getCause(); if (e instanceof WebApplicationException || cause instanceof WebApplicationException) { throw (WebApplicationException) cause; // swallow the typical invocation exceptions, convert real // exceptions to ConvertParameterException } else if (!(e instanceof NoSuchMethodException) && !(e instanceof IllegalAccessException) && !(e instanceof InvocationTargetException) && !(e instanceof InstantiationException)) { throw ConvertParameterException .object(this.convertTo, value, e); } } protected <A> Collection<A> unmodifiable(Collection<A> coll) { if (coll instanceof List<?>) return Collections.unmodifiableList((List<A>) coll); if (coll instanceof SortedSet<?>) return Collections.unmodifiableSortedSet((SortedSet<A>) coll); if (coll instanceof Set<?>) return Collections.unmodifiableSet((Set<A>) coll); return Collections.unmodifiableCollection(coll); } } static class CookieParamGetter extends NoEncParamGetter { private final CookieParam cookieParam; /** * @param annoSaysLeaveClassEncoded * to check if the annotation is available, but should not * be. */ CookieParamGetter(CookieParam cookieParam, DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean annoSaysLeaveClassEncoded) { super(defaultValue, convToCl, convToGen, tlContext, annoSaysLeaveClassEncoded); this.cookieParam = cookieParam; } @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public Object getParamValue() { String cookieName = this.cookieParam.value(); Series<org.restlet.data.Cookie> cookies; cookies = this.tlContext.get().getRequest().getCookies(); if (this.convertTo.equals(Cookie.class)) { Collection<Cookie> coll = createColl(); for (org.restlet.data.Cookie rc : cookies) { if (!rc.getName().equals(cookieName)) { continue; } Cookie cookie = Converter.toJaxRsCookie(rc); if (coll == null) { return cookie; } coll.add(cookie); } if (coll == null) { return null; } if (coll.isEmpty()) { String value = this.defaultValue.value(); coll.add(new Cookie(cookieName, value)); } if (this.isArray) { return Util.toArray(coll, Cookie.class); } return coll; } try { if (this.collType == null) { // no collection parameter String firstCookieValue = WrapperUtil.getValue(cookies .getFirst(cookieName)); return convertParamValue(firstCookieValue); } return convertParamValues(new NamedValuesIter( (Series) cookies.subList(cookieName))); } catch (ConvertParameterException e) { throw new ConvertCookieParamException(e); } } } /** * Abstract super class for access to the entity or to @*Param where * encoded is allowed (@{@link PathParam}, @{@link MatrixParam} and * @{@link QueryParam}). */ abstract static class EncParamGetter extends AbstractParamGetter { private final boolean decoding; EncParamGetter(DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext); this.decoding = !leaveEncoded; } @Override protected boolean decoding() { return this.decoding; } } static abstract class FormOrQueryParamGetter extends EncParamGetter { FormOrQueryParamGetter(DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext, leaveEncoded); } /** * @param params * @param paramName * @return * @throws ConvertQueryParamException */ Object getParamValue(final Series<Parameter> params, final String paramName) throws ConvertParameterException { Series<Parameter> parameters = params.subList(paramName); if (this.collType == null) { // no collection parameter Parameter firstFormParam = params.getFirst(paramName); String queryParamValue = WrapperUtil.getValue(firstFormParam); return convertParamValue(queryParamValue); } NamedValuesIter queryParamValueIter; queryParamValueIter = new NamedValuesIter(parameters); return convertParamValues(queryParamValueIter); } } static class FormParamGetter extends FormOrQueryParamGetter { private static Form form; private final FormParam formParam; FormParamGetter(FormParam formParam, DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext, leaveEncoded); this.formParam = formParam; } @Override public Object getParamValue() { Representation entity = this.tlContext.get().getRequest() .getEntity(); if (entity != null && entity.isAvailable()) { form = new Form(entity, false); } final String paramName = this.formParam.value(); try { return super.getParamValue(form, paramName); } catch (ConvertParameterException e) { throw new ConvertQueryParamException(e); } } } static class HeaderParamGetter extends NoEncParamGetter { private final HeaderParam headerParam; /** * @param annoSaysLeaveClassEncoded * to check if the annotation is available. */ HeaderParamGetter(HeaderParam headerParam, DefaultValue defaultValue, Class<?> convToCl, Type paramGenericType, ThreadLocalizedContext tlContext, boolean annoSaysLeaveClassEncoded) { super(defaultValue, convToCl, paramGenericType, tlContext, annoSaysLeaveClassEncoded); this.headerParam = headerParam; } @Override public Object getParamValue() { Series<Header> httpHeaders = Util.getHttpHeaders(this.tlContext .get().getRequest()); String headerName = this.headerParam.value(); try { if (this.collType == null) { // no collection parameter final String firstHeader = WrapperUtil.getValue(httpHeaders .getFirst(headerName, true)); return convertParamValue(firstHeader); } return convertParamValues(new NamedValuesIter( httpHeaders.subList(headerName, true))); } catch (ConvertParameterException e) { throw new ConvertHeaderParamException(e); } } } static class MatrixParamGetter extends EncParamGetter { private final MatrixParam matrixParam; MatrixParamGetter(MatrixParam matrixParam, DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext, leaveEncoded); this.matrixParam = matrixParam; } @Override public Object getParamValue() { final CallContext callContext = this.tlContext.get(); try { if (this.collType == null) { // no collection parameter final String matrixParamValue = callContext .getLastMatrixParamEnc(this.matrixParam); return convertParamValue(matrixParamValue); } Iterator<String> matrixParamValues; matrixParamValues = callContext .matrixParamEncIter(this.matrixParam); return convertParamValues(matrixParamValues); } catch (ConvertParameterException e) { throw new ConvertMatrixParamException(e); } } } /** * Abstract super class for access to the entity or to @*Param where * encoded is allowed (@{@link PathParam}, @{@link MatrixParam} and * @{@link QueryParam}). */ abstract static class NoEncParamGetter extends AbstractParamGetter { /** * @param annoSaysLeaveEncoded * to check if the annotation is available. */ NoEncParamGetter(DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean annoSaysLeaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext); checkForEncodedAnno(annoSaysLeaveEncoded); } /** * Checks if the annotation @{@link Encoded} is available on the * given field or bean setter. If yes, a warning is logged. */ void checkForEncodedAnno(AccessibleObject fieldOrBeanSetter) { checkForEncodedAnno(fieldOrBeanSetter .isAnnotationPresent(Encoded.class)); } /** * Checks if the annotation @{@link Encoded} is available on the * given field or bean setter. If yes, this method logs a warning. */ void checkForEncodedAnno(boolean annoSaysLeaveEncoded) { if (annoSaysLeaveEncoded) { localLogger .warning("You should not use @Encoded on a @HeaderParam or @CookieParam. Will ignore it"); } } @Override protected boolean decoding() { return false; } } static interface ParamGetter { /** * Returns the value for this param. * * @return the value for this param. * @throws InvocationTargetException * @throws ConvertRepresentationException * @throws WebApplicationException */ public Object getValue() throws InvocationTargetException, ConvertRepresentationException, WebApplicationException; } static class PathParamGetter extends EncParamGetter { private final PathParam pathParam; PathParamGetter(PathParam pathParam, DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) throws IllegalPathParamTypeException { super(defaultValue, convToCl, convToGen, tlContext, leaveEncoded); if ((this.collType != null) && (!this.convertTo.equals(PathSegment.class))) { throw new IllegalPathParamTypeException( "The type of a @PathParam annotated parameter etc. must not be a collection type or array, if the type parameter is not PathSegment"); } this.pathParam = pathParam; } /** * Creates a {@link PathSegment}. * * @param pathSegmentEnc * @return * @throws IllegalArgumentException */ private PathSegment createPathSegment(final String pathSegmentEnc) throws IllegalArgumentException { return new PathSegmentImpl(pathSegmentEnc, this.decoding(), -1); } @Override public Object getParamValue() { final CallContext callContext = this.tlContext.get(); if (this.convertTo.equals(PathSegment.class)) { if (this.collType == null) { // no collection parameter final String pathSegmentEnc = callContext .getLastPathSegmentEnc(this.pathParam); return createPathSegment(pathSegmentEnc); } final Iterator<String> pathSegmentEncIter; pathSegmentEncIter = callContext .pathSegementEncIter(this.pathParam); final Collection<Object> coll = createColl(); while (pathSegmentEncIter.hasNext()) { final String pathSegmentEnc = pathSegmentEncIter.next(); coll.add(createPathSegment(pathSegmentEnc)); } if (this.isArray) { return Util.toArray(coll, this.convertTo); } return unmodifiable(coll); } try { final String pathParamValue; pathParamValue = callContext.getLastPathParamEnc(pathParam); return convertParamValue(pathParamValue); } catch (ConvertParameterException e) { throw new ConvertPathParamException(e); } } } static class QueryParamGetter extends FormOrQueryParamGetter { private final QueryParam queryParam; QueryParamGetter(QueryParam queryParam, DefaultValue defaultValue, Class<?> convToCl, Type convToGen, ThreadLocalizedContext tlContext, boolean leaveEncoded) { super(defaultValue, convToCl, convToGen, tlContext, leaveEncoded); this.queryParam = queryParam; } @Override public Object getParamValue() { final Reference resourceRef = this.tlContext.get().getRequest() .getResourceRef(); final String queryString = resourceRef.getQuery(); final Form form = Converter.toFormEncoded(queryString); final String paramName = this.queryParam.value(); try { return super.getParamValue(form, paramName); } catch (ConvertParameterException e) { throw new ConvertQueryParamException(e); } } } /** * @author Stephan Koops */ private static class UriInfoGetter implements ParamGetter { private final boolean availableMandatory; private final ThreadLocalizedUriInfo uriInfo; private UriInfoGetter(ThreadLocalizedContext tlContext, boolean availableMandatory) { this.uriInfo = new ThreadLocalizedUriInfo(tlContext); this.availableMandatory = availableMandatory; } public Object getValue() throws InvocationTargetException, ConvertRepresentationException, WebApplicationException { this.uriInfo.saveStateForCurrentThread(this.availableMandatory); return this.uriInfo; } } private static final String COLL_PARAM_NOT_DEFAULT = "The collection type Collection is not supported for parameters. Use List, Set or SortedSet"; private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE; private static final Byte DEFAULT_BYTE = (byte) 0; private static final Character DEFAULT_CHAR = new Character('\0'); private static final Double DEFAULT_DOUBLE = 0d; private static final Float DEFAULT_FLOAT = 0.0f; private static final Integer DEFAULT_INT = 0; private static final Long DEFAULT_LONG = new Long(0); private static final Short DEFAULT_SHORT = 0; private static final Logger localLogger = org.restlet.Context .getCurrentLogger(); private static final Collection<Class<? extends Annotation>> VALID_ANNOTATIONS = createValidAnnotations(); /** * @return the collection type for the given {@link ParameterizedType * parametrized Type}.<br> * If the given type do not represent an collection, null is * returned. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private static Class<Collection<?>> collType(ParameterizedType type) { final Type rawType = type.getRawType(); if (rawType.equals(List.class)) { return (Class) ArrayList.class; } else if (rawType.equals(Set.class)) { return (Class) HashSet.class; } else if (rawType.equals(SortedSet.class)) { return (Class) TreeSet.class; } else if (rawType.equals(Collection.class)) { localLogger.config(ParameterList.COLL_PARAM_NOT_DEFAULT); return (Class) ArrayList.class; } return null; } static Collection<Class<? extends Annotation>> createValidAnnotations() { return Arrays.asList(Context.class, HeaderParam.class, MatrixParam.class, QueryParam.class, PathParam.class, CookieParam.class); } /** * Returns the given annotation, if it is available in the given array of * annotations. */ @SuppressWarnings("unchecked") static <A extends Annotation> A getAnno(Annotation[] annotations, Class<A> annoType) { for (final Annotation annot : annotations) { final Class<? extends Annotation> annotationType = annot .annotationType(); if (annotationType.equals(annoType)) { return (A) annot; } } return null; } /** * Returns true, if one of the annotations is @{@link Encoded} */ static boolean getLeaveEncoded(Annotation[] annotations) { for (final Annotation annot : annotations) { final Class<? extends Annotation> annotationType = annot .annotationType(); if (annotationType.equals(Encoded.class)) { return true; } } return false; } /** * must call the {@link EntityGetter} first, if @{@link FormParam} is * used. A value less than zero means, that no special handling is needed. */ private final int entityPosition; /** shortcut for {@link #parameters}.length */ private final int paramCount; /** @see #paramCount */ private final ParamGetter[] parameters; /** * @param parameterTypes * @param genParamTypes * @param paramAnnoss * @param tlContext * @param leaveAllEncoded * @param jaxRsProviders * @param extensionBackwardMapping * @param paramsAllowed * true, if @*Params are allowed as parameter, otherwise * false. * @param entityAllowed * true, if the entity is allowed as parameter, otherwise false. * @param logger * @param allMustBeAvailable * if true, all values must be available (for singeltons creation * it must be false) * @throws MissingAnnotationException * @throws IllegalTypeException * if the given class is not valid to be annotated with @ * {@link Context}. * @throws IllegalPathParamTypeException */ private ParameterList(Class<?>[] parameterTypes, Type[] genParamTypes, Annotation[][] paramAnnoss, ThreadLocalizedContext tlContext, boolean leaveAllEncoded, JaxRsProviders jaxRsProviders, ExtensionBackwardMapping extensionBackwardMapping, boolean paramsAllowed, boolean entityAllowed, Logger logger, boolean allMustBeAvailable) throws MissingAnnotationException, IllegalTypeException, IllegalPathParamTypeException { this.paramCount = parameterTypes.length; this.parameters = new ParamGetter[this.paramCount]; boolean entityAlreadyRead = false; int entityPosition = -1; for (int i = 0; i < this.paramCount; i++) { final Class<?> parameterType = parameterTypes[i]; final Type genParamType = genParamTypes[i]; final Annotation[] paramAnnos = paramAnnoss[i]; final Context conntextAnno = getAnno(paramAnnos, Context.class); if (conntextAnno != null) { if (parameterType.equals(UriInfo.class)) { this.parameters[i] = new UriInfoGetter(tlContext, allMustBeAvailable); } else { this.parameters[i] = new ContextHolder( ContextInjector.getInjectObject(parameterType, tlContext, jaxRsProviders, extensionBackwardMapping)); } continue; } if (paramsAllowed) { final boolean leaveThisEncoded = getLeaveEncoded(paramAnnos); final DefaultValue defValue = getAnno(paramAnnos, DefaultValue.class); final CookieParam cookieParam = getAnno(paramAnnos, CookieParam.class); final HeaderParam headerParam = getAnno(paramAnnos, HeaderParam.class); final MatrixParam matrixParam = getAnno(paramAnnos, MatrixParam.class); final PathParam pathParam = getAnno(paramAnnos, PathParam.class); final QueryParam queryParam = getAnno(paramAnnos, QueryParam.class); final FormParam formParam = getAnno(paramAnnos, FormParam.class); if (pathParam != null) { this.parameters[i] = new PathParamGetter(pathParam, defValue, parameterType, genParamType, tlContext, leaveAllEncoded || leaveThisEncoded); continue; } else if (cookieParam != null) { this.parameters[i] = new CookieParamGetter(cookieParam, defValue, parameterType, genParamType, tlContext, leaveThisEncoded); continue; } else if (headerParam != null) { this.parameters[i] = new HeaderParamGetter(headerParam, defValue, parameterType, genParamType, tlContext, leaveThisEncoded); continue; } else if (matrixParam != null) { this.parameters[i] = new MatrixParamGetter(matrixParam, defValue, parameterType, genParamType, tlContext, leaveAllEncoded || leaveThisEncoded); continue; } else if (queryParam != null) { this.parameters[i] = new QueryParamGetter(queryParam, defValue, parameterType, genParamType, tlContext, leaveAllEncoded || leaveThisEncoded); continue; } else if (formParam != null) { this.parameters[i] = new FormParamGetter(formParam, defValue, parameterType, genParamType, tlContext, leaveAllEncoded || leaveThisEncoded); continue; } } // could only be the entity here if (!entityAllowed) { throw new MissingAnnotationException( "All parameters requires one of the following annotations: " + VALID_ANNOTATIONS); } if (entityAlreadyRead) { throw new MissingAnnotationException( "The entity is already read. The " + i + ". parameter requires one of " + "the following annotations: " + VALID_ANNOTATIONS); } if (Representation.class.isAssignableFrom(parameterType)) { this.parameters[i] = ReprEntityGetter.create(parameterType, genParamType, logger); } if (this.parameters[i] == null) { this.parameters[i] = new EntityGetter(parameterType, genParamType, tlContext, jaxRsProviders, paramAnnos); } entityPosition = i; entityAlreadyRead = true; } this.entityPosition = entityPosition; } /** * @param constr * @param tlContext * @param leaveEncoded * @param jaxRsProviders * @param extensionBackwardMapping * @param paramsAllowed * @param logger * @param allMustBeAvailable * @throws MissingAnnotationException * @throws IllegalTypeException * if one of the parameters contains a @{@link Context} on * an type that must not be annotated with @{@link Context}. * @throws IllegalPathParamTypeException */ public ParameterList(Constructor<?> constr, ThreadLocalizedContext tlContext, boolean leaveEncoded, JaxRsProviders jaxRsProviders, ExtensionBackwardMapping extensionBackwardMapping, boolean paramsAllowed, Logger logger, boolean allMustBeAvailable) throws MissingAnnotationException, IllegalTypeException, IllegalPathParamTypeException { this(constr.getParameterTypes(), constr.getGenericParameterTypes(), constr.getParameterAnnotations(), tlContext, leaveEncoded, jaxRsProviders, extensionBackwardMapping, paramsAllowed, false, logger, allMustBeAvailable); } /** * @param executeMethod * @param annotatedMethod * @param tlContext * @param leaveEncoded * @param jaxRsProviders * @param extensionBackwardMapping * @param entityAllowed * @param logger * @throws MissingAnnotationException * @throws IllegalTypeException * if one of the parameters contains a @{@link Context} on * an type that must not be annotated with @{@link Context}. * @throws IllegalPathParamTypeException */ public ParameterList(Method executeMethod, Method annotatedMethod, ThreadLocalizedContext tlContext, boolean leaveEncoded, JaxRsProviders jaxRsProviders, ExtensionBackwardMapping extensionBackwardMapping, boolean entityAllowed, Logger logger) throws MissingAnnotationException, IllegalTypeException, IllegalPathParamTypeException { this(executeMethod.getParameterTypes(), executeMethod .getGenericParameterTypes(), annotatedMethod .getParameterAnnotations(), tlContext, leaveEncoded, jaxRsProviders, extensionBackwardMapping, true, entityAllowed, logger, true); } /** * Returns the concrete parameter array for the current request. * * @return the concrete parameter array for the current request. * @throws InvocationTargetException * @throws ConvertRepresentationException * @throws WebApplicationException */ public Object[] get() throws ConvertRepresentationException, InvocationTargetException, WebApplicationException { final Object[] args = new Object[this.parameters.length]; if (this.entityPosition >= 0) { args[entityPosition] = this.parameters[entityPosition].getValue(); } for (int i = 0; i < this.paramCount; i++) { if (i != this.entityPosition) { args[i] = this.parameters[i].getValue(); } } return args; } }