/**
* Copyright (C) 2003-2008 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.etk.core.rest.impl.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import javax.ws.rs.CookieParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import org.etk.core.rest.method.TypeProducer;
/**
* @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a>
* @version $Id: $
*/
public class ParameterHelper {
/**
* Collections of annotation that allowed to be used on fields on any type of
* Provider.
*
* @see javax.ws.rs.ext.Provider
* @see javax.ws.rs.ext.Providers
*/
public static final List<String> PROVIDER_FIELDS_ANNOTATIONS;
/**
* Collections of annotation than allowed to be used on constructor's
* parameters of any type of Provider.
*
* @see javax.ws.rs.ext.Provider
* @see javax.ws.rs.ext.Providers
*/
public static final List<String> PROVIDER_CONSTRUCTOR_PARAMETER_ANNOTATIONS;
/**
* Collections of annotation that allowed to be used on fields of resource
* class.
*/
public static final List<String> RESOURCE_FIELDS_ANNOTATIONS;
/**
* Collections of annotation than allowed to be used on constructor's
* parameters of resource class.
*/
public static final List<String> RESOURCE_CONSTRUCTOR_PARAMETER_ANNOTATIONS;
/**
* Collections of annotation than allowed to be used on method's parameters of
* resource class.
*/
public static final List<String> RESOURCE_METHOD_PARAMETER_ANNOTATIONS;
static {
PROVIDER_FIELDS_ANNOTATIONS = Collections.singletonList(Context.class.getName());
PROVIDER_CONSTRUCTOR_PARAMETER_ANNOTATIONS = Collections.singletonList(Context.class.getName());
List<String> tmp1 = new ArrayList<String>(6);
tmp1.add(CookieParam.class.getName());
tmp1.add(Context.class.getName());
tmp1.add(HeaderParam.class.getName());
tmp1.add(MatrixParam.class.getName());
tmp1.add(PathParam.class.getName());
tmp1.add(QueryParam.class.getName());
RESOURCE_FIELDS_ANNOTATIONS = Collections.unmodifiableList(tmp1);
RESOURCE_CONSTRUCTOR_PARAMETER_ANNOTATIONS = Collections.unmodifiableList(tmp1);
List<String> tmp2 = new ArrayList<String>(tmp1);
tmp2.add(javax.ws.rs.FormParam.class.getName());
RESOURCE_METHOD_PARAMETER_ANNOTATIONS = Collections.unmodifiableList(tmp2);
}
/**
* @param parameterClass method parameter class
* @param parameterType method parameter type
* @return TypeProducer
* @see TypeProducer
* @see Method#getParameterTypes()
* @see Method#getGenericParameterTypes()
*/
static TypeProducer createTypeProducer(Class<?> parameterClass, Type parameterType) {
if (parameterClass == List.class || parameterClass == Set.class
|| parameterClass == SortedSet.class) {
// parameter is collection
Class<?> clazz = null;
if (parameterType != null)
clazz = getGenericType(parameterType);
Method methodValueOf = null;
Constructor<?> constructor = null;
// if not parameterized then by default collection of Strings.
if (clazz == String.class || clazz == null) {
// String
return new CollectionStringProducer(parameterClass);
} else if ((methodValueOf = getStringValueOfMethod(clazz)) != null) {
// static method valueOf
return new CollectionStringValueOfProducer(parameterClass, methodValueOf);
} else if ((constructor = getStringConstructor(clazz)) != null) {
// constructor with String
return new CollectionStringConstructorProducer(parameterClass, constructor);
}
} else {
// parameters is not collection
Method methodValueOf = null;
Constructor<?> constructor = null;
if (parameterClass.isPrimitive()) {
// primitive type
return new PrimitiveTypeProducer(parameterClass);
} else if (parameterClass == String.class) {
// String
return new StringProducer();
} else if ((methodValueOf = getStringValueOfMethod(parameterClass)) != null) {
// static valueOf method
return new StringValueOfProducer(methodValueOf);
} else if ((constructor = getStringConstructor(parameterClass)) != null) {
// constructor with String
return new StringConstructorProducer(constructor);
}
}
return null;
}
/**
* The type <code>T</code> of the annotated parameter, field or property must
* either:
* <ol>
* <li>Be a primitive type</li>
* <li>Have a constructor that accepts a single <code>String</code> argument</li>
* <li>Have a static method named <code>valueOf</code> that accepts a single
* <code>String</code> argument (see, for example,
* {@link Integer#valueOf(String)})</li>
* <li>Be <code>List<T></code>, <code>Set<T></code> or
* <code>SortedSet<T></code>, where <code>T</code> satisfies 2 or 3
* above. The resulting collection is read-only.</li>
* </ol>
*
* @param parameterClass the parameter class
* @param parameterType the parameter type
* @param annotation parameter annotation
* @return true it parameter is valid, false otherwise
*/
// TODO remove this method
boolean isValidAnnotatedParameter(Class<?> parameterClass,
Type parameterType,
Annotation annotation) {
if (parameterClass == List.class || parameterClass == Set.class
|| parameterClass == SortedSet.class) {
// PathParam cann't be used on collection
// if (annotation.annotationType() == PathParam.class)
// return false;
Class<?> clazz = getGenericType(parameterType);
if (clazz == null || clazz == String.class || getStringValueOfMethod(clazz) != null
|| getStringConstructor(clazz) != null) {
// parameter is collection (List, Set or SortedSet)
return true;
} else {
// if primitive type
if (parameterClass.isPrimitive()
&& PrimitiveTypeProducer.PRIMITIVE_TYPES_MAP.get(parameterClass) != null)
return true;
if (parameterClass == String.class || getStringValueOfMethod(parameterClass) != null
|| getStringConstructor(parameterClass) != null)
return true;
}
}
// not valid parameter.
return false;
}
/**
* Get static {@link Method} with single string argument and name 'valueOf'
* for supplied class.
*
* @param clazz class for discovering to have public static method with name
* 'valueOf' and single string argument
* @return valueOf method or null if class has not it
*/
static Method getStringValueOfMethod(Class<?> clazz) {
try {
Method method = clazz.getDeclaredMethod("valueOf", String.class);
return Modifier.isStatic(method.getModifiers()) ? method : null;
} catch (Exception e) {
return null;
}
}
/**
* Get constructor with single string argument for supplied class.
*
* @param clazz class for discovering to have constructor with single string
* argument
* @return constructor or null if class has not constructor with single string
* argument
*/
static Constructor<?> getStringConstructor(Class<?> clazz) {
try {
return clazz.getConstructor(String.class);
} catch (Exception e) {
return null;
}
}
/**
* Get generic type for supplied type.
*
* @param type See {@link Type}
* @return generic type if type is {@link ParameterizedType}, null otherwise
*/
static Class<?> getGenericType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] genericTypes = pt.getActualTypeArguments();
if (genericTypes.length == 1) {
try {
// if can't be cast to java.lang.Class thrown Exception
return (Class<?>) genericTypes[0];
} catch (ClassCastException e) {
throw new RuntimeException("Unsupported type");
}
}
}
// not parameterized type
return null;
}
}