All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.anthavio.httl.api.HttlApiBuilder Maven / Gradle / Ivy

The newest version!
package net.anthavio.httl.api;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.anthavio.httl.HttlBodyMarshaller;
import net.anthavio.httl.HttlBuilderVisitor;
import net.anthavio.httl.HttlConstants;
import net.anthavio.httl.HttlExecutionFilter;
import net.anthavio.httl.HttlResponseExtractor;
import net.anthavio.httl.HttlSender;
import net.anthavio.httl.HttlSender.Multival;
import net.anthavio.httl.api.HttlBody.NullSurrogateHttlBodyWriter;
import net.anthavio.httl.api.HttlCall.HttpMethod;
import net.anthavio.httl.api.HttlVar.NoopParamSetter;
import net.anthavio.httl.api.VarSetter.BeanMetaVarSetter;
import net.anthavio.httl.api.VarSetter.FieldApiVarMeta;
import net.anthavio.httl.util.HttlUtil;

/**
 * 
 * @author martin.vanek
 *
 */
public class HttlApiBuilder {

	public static HttlApiBuilder with(HttlSender sender) {
		return new HttlApiBuilder(sender);
	}

	private final HttlSender sender;

	private Multival headers;

	private Multival params;

	public HttlApiBuilder(HttlSender sender) {
		if (sender == null) {
			throw new IllegalArgumentException("Null sender");
		}
		this.sender = sender;
		params = new Multival();
	}

	/**
	 * Add this header into every request
	 */
	public HttlApiBuilder addHeader(String name, String value) {
		if (headers == null) {
			headers = new Multival();
		}
		headers.add(name, value);
		return this;
	}

	/**
	 * Add this parameter into every request
	 */
	public HttlApiBuilder addParam(String name, String value) {
		if (params == null) {
			params = new Multival();
		}
		params.add(name, value);
		return this;
	}

	public  T build(Class apiInterface) throws HttlApiException {
		return build(apiInterface, sender, headers, params);
	}

	public static  T build(Class apiInterface, HttlSender sender) throws HttlApiException {
		return build(apiInterface, sender, null, null);
	}

	public static  T build(Class apiInterface, HttlSender sender, Multival headers, Multival params) {
		final HttlApi httlApi = apiInterface.getAnnotation(HttlApi.class);

		final String urlPathPrefix = (httlApi != null) ? getRestApiUri(httlApi) : "";
		Map> sharedSetters = new HashMap>();
		if (httlApi != null) {
			//Initialize shared setters
			Class[] setters = httlApi.setters();
			if (setters.length != 0) {
				for (Class setterClass : setters) {
					ParameterizedType parametrized = (ParameterizedType) setterClass.getGenericInterfaces()[0];
					Type targetType = parametrized.getActualTypeArguments()[0];
					VarSetter setter;
					try {
						setter = setterClass.newInstance();
					} catch (Exception x) {
						throw new HttlApiException("Failed to instantiate shared VarSetter " + setterClass, apiInterface);
					}
					sharedSetters.put(targetType, setter);
				}
			}
		}

		Map methods = doApiMethods(apiInterface, urlPathPrefix, sharedSetters);
		InvocationHandler handler = new HttlApiHandler(apiInterface, sender, headers, params, methods);
		return (T) Proxy.newProxyInstance(apiInterface.getClassLoader(), new Class[] { apiInterface }, handler);
	}

	private static Map doApiMethods(Class apiInterface, String urlPathPrefix,
			Map> sharedSetters) {
		Map metaMap = new HashMap();
		for (Method method : apiInterface.getDeclaredMethods()) {
			HttlCall operation = method.getAnnotation(HttlCall.class);
			if (operation == null) {
				String mname = method.getName();
				if ("equals".equals(mname) || "hashCode".equals(mname)) {
					continue;
				}
				//Maybe just ignore those ???
				throw new HttlApiException("@RestCall not found ", method);
			}

			HttpMethod http = operation.method();
			String urlPath = operation.value();
			if (http == HttpMethod.PATH_DERIVED) {
				String[] segments = urlPath.split(" ");
				if (segments.length != 2) {
					throw new HttlApiException("Invalid @RestCall format: " + urlPath, method);
				}
				//maybe nicer exceptions here
				http = HttpMethod.valueOf(segments[0]);
				urlPath = segments[1];
			}
			if (urlPathPrefix != null) {
				urlPath = HttlUtil.joinUrlParts(urlPathPrefix, urlPath);
			}

			//@RestVar annotations
			ApiVarMeta[] params = doApiParameters(method, sharedSetters);
			Map paramMap = new HashMap();
			for (ApiVarMeta param : params) {
				paramMap.put(param.name, param);
			}

			//search for {placehoders} in url path
			int lastOpn = -1;
			while ((lastOpn = urlPath.indexOf('{', lastOpn + 1)) != -1) {
				int lastCls = urlPath.indexOf('}', lastOpn + 1);
				if (lastCls == -1) {
					throw new HttlApiException("Unpaired bracket in path " + urlPath, method);
				}

				String pathParam = urlPath.substring(lastOpn + 1, lastCls);
				ApiVarMeta param = paramMap.get(pathParam);
				if (param == null) {
					throw new HttlApiException("Parameter '" + pathParam + "' not found for path placeholder", method);
				} else {
					param.setTarget(VarTarget.PATH);
				}
			}
			//XXX maybe some prevention against duplicate path/header placeholders

			//@Header annotations
			ApiHeaderMeta[] headers = doApiHeaders(apiInterface, method);
			Map headerMap = new HashMap();
			for (ApiHeaderMeta header : headers) {
				if (header.parameter != null) {
					ApiVarMeta param = paramMap.get(header.parameter); //Templated @Header needs @Param with value
					if (param != null) {
						param.setTargetHeader(header.name);
					} else {
						throw new HttlApiException("Templated Header '" + header.parameter + "' not found as parameter", method);
					}
				}
				headerMap.put(header.name, header);
			}

			//Some sanity checks...

			ApiVarMeta body = paramMap.get(BODY);

			if (body != null) {
				if (http.getMethod().isBodyAllowed() == false) {
					throw new HttlApiException("Body not allowed on HTTP " + http.getMethod(), method);
				}

				if (body.variable == null && headerMap.get(HttlConstants.Content_Type) == null) {
					throw new HttlApiException("Content-Type not specified. Neither as a @Header nor as @Body parameter", method);
				}

				//maybe set priority instead of exception... maybe
				if (body.variable != null && headerMap.get(HttlConstants.Content_Type) != null) {
					throw new HttlApiException("Content-Type specified twice. As a @Header and as @Body parameter", method);
				}

			} else { // @Body param does not exist
				if (paramMap.get("#" + HttlBodyMarshaller.class.getSimpleName()) != null) {
					throw new HttlApiException("@Body parameter is required when using RequestBodyMarshaller", method);
				}
			}
			// parameters not found as path or header placeholder are left as query parameters

			//return type is Builder subinterface
			BuilderMeta builderMeta = null;
			if (method.getReturnType().isInterface() && method.getReturnType().getInterfaces().length > 0
					&& method.getReturnType().getInterfaces()[0] == HttlCallBuilder.class) {
				//XXX this will fail if user will build some hierarchy of interfaces
				Class builderInterface = (Class) method.getReturnType();
				ParameterizedType parametrized = (ParameterizedType) builderInterface.getGenericInterfaces()[0];
				Type returnType = parametrized.getActualTypeArguments()[0];
				Map builderMethods = doBuilderMethods(builderInterface);
				builderMeta = new BuilderMeta(builderInterface, returnType, builderMethods);
			}

			ApiMethodMeta methodMeta = new ApiMethodMeta(method, http, urlPath, params, headers, builderMeta);
			metaMap.put(method, methodMeta);
		}
		return metaMap;
	}

	private static Map doBuilderMethods(Class builderInterface) {
		Map metaMap = new HashMap();
		Method[] methods = builderInterface.getDeclaredMethods();
		for (Method method : methods) {

			if (method.getName().equals("execute") && method.getParameterTypes().length == 0) {
				continue;
			}

			if (method.getReturnType() != builderInterface) {
				throw new HttlApiException("Invalid return: " + method.getReturnType() + " expected: " + builderInterface,
						method);
			}
			//We may support more parameters in future if it makes sense
			if (method.getParameterTypes().length != 1) {
				throw new HttlApiException("Single parameter is expected", method);
			}

			String paramName = method.getName();
			boolean replace = false;
			if (paramName.startsWith("set")) {
				replace = true;
				paramName = toPropName(paramName);
			} else if (paramName.startsWith("add")) {
				replace = false;
				paramName = toPropName(paramName);
			}

			ApiVarMeta varMeta = doParameter(0, method, null, paramName);
			BuilderMethodMeta methodMeta = new BuilderMethodMeta(method, varMeta, replace);
			metaMap.put(method, methodMeta);
		}
		return metaMap;
	}

	private final static String BODY = "#body";

	private static ApiHeaderMeta[] doApiHeaders(Class clazz, Method method) {
		List headerList = new ArrayList();
		Map headerMap = new HashMap();

		//Process Type declared (global) annotations first
		HttlHeaders cheaders = clazz.getAnnotation(HttlHeaders.class);
		if (cheaders != null && cheaders.value().length != 0) {
			for (String header : cheaders.value()) {
				if (header.indexOf('{') != -1 && header.indexOf('}') != -1) {
					throw new HttlApiException("Global headers cannot be templated: " + header, clazz);
				}
				int idx = header.indexOf(':');
				if (idx == -1 || idx > header.length() - 2) {
					throw new HttlApiException("Global header syntax error: " + header, clazz);
				}
				String name = header.substring(0, idx).trim();
				String value = header.substring(idx + 1).trim();
				ApiHeaderMeta metaHeader = new ApiHeaderMeta(name, value, null);
				headerList.add(metaHeader);
				headerMap.put(name, headerList.size() - 1);
			}
		}

		//Process Method declared (local) annotations now
		HttlHeaders mheaders = method.getAnnotation(HttlHeaders.class);
		if (mheaders != null && mheaders.value().length != 0) {
			for (String header : mheaders.value()) {
				int idx = header.indexOf(':');
				if (idx == -1 || idx > header.length() - 2) {
					throw new HttlApiException("Method header syntax error: " + header, method);
				}
				String name = header.substring(0, idx).trim();
				String value = header.substring(idx + 1).trim();
				String parameter = null;

				int idxOpn = value.indexOf('{');
				int idxCls = value.indexOf('}', idxOpn + 1);
				if (idxOpn != -1) {
					if (idxCls != -1) {
						parameter = value.substring(idxOpn + 1, idxCls);
					} else {
						throw new HttlApiException("Unpaired brackets in method header: " + header, method);
					}
				}
				ApiHeaderMeta metaHeader = new ApiHeaderMeta(name, value, parameter);
				Integer index = headerMap.get(name);
				if (index != null) {
					headerList.set(index, metaHeader); //replace shared if exists
				} else {
					headerList.add(metaHeader);
				}
			}
		}
		return headerList.toArray(new ApiHeaderMeta[headerList.size()]);
	}

	private static ApiVarMeta[] doApiParameters(Method method, Map> sharedSetters) {
		Map map = new HashMap();//only for duplicate name checks
		Class[] types = method.getParameterTypes();
		List metaList = new ArrayList();
		for (int i = 0; i < types.length; i++) {
			Class type = types[i];
			String name;
			ApiVarMeta meta;
			// interceptors/marshallers/extractors first - they need no annotation with name
			if (HttlBuilderVisitor.class.isAssignableFrom(type)) {
				name = "#" + HttlBuilderVisitor.class.getSimpleName() + "-" + i;
				meta = new ApiVarMeta(i, type, name, true, null, null, null, VarTarget.BLDR_VISITOR);
				metaList.add(meta);

			} else if (HttlExecutionFilter.class.isAssignableFrom(type)) {
				name = "#" + HttlExecutionFilter.class.getSimpleName() + "-" + i;
				meta = new ApiVarMeta(i, type, name, true, null, null, null, VarTarget.EXEC_FILTER);
				metaList.add(meta);

			} else if (HttlBodyWriter.class.isAssignableFrom(type)) {
				name = "#" + HttlBodyWriter.class.getSimpleName();
				if (map.containsKey(name)) { //TODO also add check if body writer is not defined via attribute
					throw new HttlApiException("Multiple HttlBodyWriter parameters found", method);
				}
				meta = new ApiVarMeta(i, type, name, true, null, null, null, VarTarget.BODY_WRITER);
				metaList.add(meta);

			} else if (HttlResponseExtractor.class.isAssignableFrom(type)) {
				//When parameter is declared as HttlResponseExtractor, generic information is erased...
				if (type.getGenericInterfaces().length != 0) { //real implementation like DateExtractor
					Type typeArg = ((ParameterizedType) type.getGenericInterfaces()[0]).getActualTypeArguments()[0];
					if (typeArg != method.getReturnType()) {
						throw new HttlApiException("Incompatible Extractor type: " + typeArg + " method returns : "
								+ method.getReturnType(), method);
					}
				}
				// versus method.getReturnType();
				// unfortunately type check of method return type and ResponseExtractor generic parameter
				// cannot be performed here...because of generic type erasure
				name = "#" + HttlResponseExtractor.class.getSimpleName();
				if (map.containsKey(name)) {
					throw new HttlApiException("Multiple ResponseExtractor parameters found", method);
				}
				meta = new ApiVarMeta(i, type, name, true, null, null, null, VarTarget.RESP_EXTRACTOR);
				metaList.add(meta);

			} else {
				// normal parameters here...
				meta = doParameter(i, method, sharedSetters, null);
				if (map.containsKey(meta.name)) {
					throw new HttlApiException("Duplicate parameter named '" + meta.name + "' found", method);
				}
				map.put(meta.name, meta);
				metaList.add(meta);
			}

		}
		return metaList.toArray(new ApiVarMeta[metaList.size()]);
	}

	/*
		private static ApiVarMeta doApiParameter(int index, Method method) {
			Annotation[][] mannotations = method.getParameterAnnotations();
			if (mannotations.length == 0) {
				throw new HttlApiException("Cannot determine parameter's name on position " + (index + 1)
						+ ". No annotation present", method);
			}
			return doParameter(index, method, null);

		}
	*/
	private static ApiVarMeta doParameter(int index, Method method, Map> sharedSetters,
			String defaultName) {

		String name = defaultName;
		boolean required = false;
		String nullval = null;
		VarSetter setter = null;
		HttlBodyWriter writer = null;
		String variable = null;
		VarTarget target = VarTarget.QUERY;

		Annotation[] pannotations = method.getParameterAnnotations()[index];
		for (Annotation annotation : pannotations) {
			if (annotation instanceof HttlVar) {
				HttlVar var = (HttlVar) annotation;
				name = getRestVarName(var);
				required = var.required();
				if (!var.defval().equals(HttlVar.NULL_STRING_SURROGATE)) {
					nullval = var.defval();
				}
				if (var.setter() != NoopParamSetter.class) {
					try {
						setter = var.setter().newInstance();
					} catch (Exception x) {
						throw new HttlApiException("Cannot create " + var.setter().getName() + " instance ", x);
					}
				}

			} else if (annotation instanceof HttlBody) {
				HttlBody body = (HttlBody) annotation;
				name = BODY; //artificial parameter name
				target = VarTarget.BODY;
				if (!body.value().isEmpty()) {
					variable = body.value(); //Content-Type header
				}
				if (body.writer() != NullSurrogateHttlBodyWriter.class) {
					try {
						writer = body.writer().newInstance();
					} catch (Exception x) {
						throw new HttlApiException("Cannot create " + body.writer().getName() + " instance ", x);
					}
				}
			} else if (annotation.getClass().getName().equals("javax.inject.Named")) {
				try {
					Method mvalue = annotation.getClass().getMethod("value");
					name = (String) mvalue.invoke(annotation);
				} catch (Exception x) {
					throw new HttlApiException("Reflective invocation javax.inject.Named.value()", x);
				}
			} else { // Any other annotation
				String annoClassName = annotation.getClass().getName();
				if (annoClassName.equals("javax.annotation.Nonnull")
						|| annoClassName.equals("javax.validation.constraints.NotNull")) {
					required = true;
				}
			}
		}
		Class paramClass = method.getParameterTypes()[index];

		if (setter != null) {
			// check that VarSetter generic parameter type is compatible with parameter type
			Type setterArgType = ((ParameterizedType) setter.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
			if (isCompatible(paramClass, (Class) setterArgType) == false) {
				throw new HttlApiException("Incompatible VarSetter<" + setterArgType + "> for parameter '" + name + "' type "
						+ paramClass.getSimpleName(), method);
			}
		} else {

			if (paramClass.getAnnotation(HttlVar.class) != null) {
				//Custom Bean with @RestVar class anotation
				return doBeanParameter(index, paramClass, name, required);

			} else if (Map.class.isAssignableFrom(paramClass)) {
				// Map are special
				return doMapParameter(index, paramClass, name, required);
			}
		}
		//still nothing -> try shared setter
		if (setter == null && sharedSetters != null) {
			setter = sharedSetters.get(paramClass);//can be null...

		}

		//fail if 
		if (name == null && setter == null) {
			throw new HttlApiException("Missing parameter's name on position " + (index + 1), method);
		}

		ApiVarMeta meta = new ApiVarMeta(index, paramClass, name, required, nullval, setter, writer, target);
		meta.variable = variable;
		return meta;
	}

	private static ApiVarMeta doMapParameter(int index, Class paramClazz, String paramName, boolean paramNotnull) {
		return new ApiVarMeta(index, paramClazz, paramName, paramNotnull, null, VarSetter.MapVarSetter, null,
				VarTarget.QUERY);
	}

	private static ApiVarMeta doBeanParameter(int index, Class paramClazz, String paramName, boolean paramNotnull) {
		List list = new ArrayList();
		HttlVar paramVar = paramClazz.getAnnotation(HttlVar.class);
		StringBuilder name = new StringBuilder();
		if (paramName != null && !paramName.equals(HttlVar.NULL_STRING_SURROGATE)) {
			name.append(paramName);
		}
		if (getRestVarName(paramVar) != null) {
			name.append(getRestVarName(paramVar));
		}
		Field[] fields = paramClazz.getDeclaredFields();
		for (Field field : fields) {
			String localName = field.getName();
			boolean notnull = false;
			String nullval = null;
			VarSetter setter = null;
			HttlVar fieldVar = field.getAnnotation(HttlVar.class);
			if (fieldVar != null) {
				notnull = fieldVar.required();
				if (!fieldVar.defval().equals(HttlVar.NULL_STRING_SURROGATE)) {
					nullval = fieldVar.defval();
				}
				if (fieldVar.setter() != NoopParamSetter.class) {
					try {
						setter = fieldVar.setter().newInstance();
					} catch (Exception x) {
						throw new HttlApiException("Cannot create " + fieldVar.setter().getName() + " instance ", x);
					}
				}
				if (getRestVarName(fieldVar) != null) {
					localName = getRestVarName(fieldVar);
				}
			}

			ApiVarMeta meta = new ApiVarMeta(index, field.getType(), name.toString() + localName, notnull, nullval, setter,
					null, VarTarget.QUERY);
			list.add(new FieldApiVarMeta(field, meta));
		}
		if (list.size() == 0) {
			throw new HttlApiException("No fields discovered", paramClazz);
		}
		BeanMetaVarSetter setter = new BeanMetaVarSetter(paramClazz, list.toArray(new FieldApiVarMeta[list.size()]));
		return new ApiVarMeta(index, paramClazz, paramName, paramNotnull, null, setter, null, VarTarget.QUERY);
	}

	/**
	 * Converts method getter/setter/adder name to property name
	 */
	private static String toPropName(String name) {
		if (name.startsWith("set") || name.startsWith("add") || name.startsWith("get")) {
			name = name.substring(3);
		}
		return Character.toLowerCase(name.charAt(0)) + name.substring(1);
	}

	/**
	 * Reason: 
	 * value() is default Java annotation's value storage - allows nice default storage syntax @RestVar("myparam")
	 * but it's name - 'value' is very misleading, because it's semantic is 'name'
	 * 
	 * Legal usage:
	 * @RestVar("myparam")
	 * @RestVar(name = "myparam")
	 * @RestVar(value = "myparam")
	 * 
	 * Illegal use: 
	 * @RestVar(name = "myparam", value = "wrong")
	 * 
	 * This also copes with Java annotation inability to store null as value requiring null surrogates usage
	 */
	private static String getRestVarName(HttlVar var) {
		return getAnnotationValue(var.value(), var.name(), "name");
	}

	private static String getRestApiUri(HttlApi api) {
		return getAnnotationValue(api.value(), api.uri(), "uri");
	}

	private static String getAnnotationValue(String value, String preferred, String preferredName) {
		boolean blankValue = value == null || value.length() == 0 || value.equals(HttlVar.NULL_STRING_SURROGATE);
		boolean blankName = preferred == null || preferred.length() == 0 || preferred.equals(HttlVar.NULL_STRING_SURROGATE);
		if (blankName) {
			if (blankValue) {
				return null;
			} else {
				return value;
			}
		} else {
			if (blankValue) {
				return preferred;
			} else {
				throw new HttlApiException("Do not use both value='" + value + "' and " + preferredName + "='" + preferred
						+ "' in annotation", (Exception) null);
			}
		}
	}

	static class BuilderMeta {

		final Class interfacee;

		final Type returnType;

		final Map methods;

		public BuilderMeta(Class interfacee, Type returnType,
				Map methods) {
			this.interfacee = interfacee;
			this.returnType = returnType;
			this.methods = methods;
		}

	}

	static class BuilderMethodMeta {
		final Method method;
		final ApiVarMeta parameter;
		final boolean replace;

		public BuilderMethodMeta(Method method, ApiVarMeta parameter, boolean replace) {
			this.method = method;
			this.parameter = parameter;
			this.replace = replace;
		}

	}

	static class ApiMethodMeta {

		final Method method;
		final HttpMethod httpMethod;
		final String urlPath;
		final ApiVarMeta[] parameters;
		final ApiHeaderMeta[] headers;
		final Map headersMap;
		final BuilderMeta builder;

		public ApiMethodMeta(Method method, HttpMethod httpMethod, String urlPath, ApiVarMeta[] parameters,
				ApiHeaderMeta[] headers, BuilderMeta builderMeta) {
			this.method = method;
			this.httpMethod = httpMethod;
			this.urlPath = urlPath;
			this.parameters = parameters;
			this.headers = headers;
			this.headersMap = new HashMap();
			for (ApiHeaderMeta header : headers) {
				headersMap.put(header.name, header);
			}
			this.builder = builderMeta;

		}

	}

	static enum VarTarget {
		PATH, QUERY, HEADER, BODY, //normal value parameters
		BLDR_VISITOR, EXEC_FILTER, //REQ_INTERCEPTOR, RES_INTERCEPTOR, //
		BODY_WRITER, RESP_EXTRACTOR;//
	}

	static class ApiHeaderMeta {

		final String name;
		final String value;
		final String parameter; //value is {parameter}

		public ApiHeaderMeta(String name, String value, String parameter) {
			this.name = name;
			this.value = value;
			this.parameter = parameter;
		}

	}

	@SuppressWarnings("rawtypes")
	static class ApiVarMeta {

		final int index;
		final String name;
		final boolean killnull;
		final String nullval;
		final Type type;
		final VarSetter setter;
		final HttlBodyWriter writer; //only for @Body
		VarTarget target;
		String variable; //@Body content type or placeholder for urlpath

		public ApiVarMeta(int index, Type type, String name, boolean killnull, String nullval, VarSetter setter,
				HttlBodyWriter marshaller, VarTarget target) {
			this.index = index;
			this.type = type;
			this.name = name;
			this.killnull = killnull;
			this.nullval = nullval;
			this.setter = setter;
			this.writer = marshaller;
			this.target = target;
		}

		void setTarget(VarTarget target) {
			this.target = target;
		}

		void setTargetHeader(String headerName) {
			this.variable = headerName;
			this.target = VarTarget.HEADER;
		}

	}

	public static boolean isCompatible(Class fromClass, Class intoClass) {
		if (fromClass.isPrimitive()) {
			//autoboxing rules apply here
			if (fromClass == Integer.TYPE && intoClass == Integer.class) {
				return true;
			} else if (fromClass == Long.TYPE && intoClass == Long.class) {
				return true;
			} else if (fromClass == Float.TYPE && intoClass == Float.class) {
				return true;
			} else if (fromClass == Double.TYPE && intoClass == Double.class) {
				return true;
			} else if (fromClass == Byte.TYPE && intoClass == Byte.class) {
				return true;
			} else if (fromClass == Character.TYPE && intoClass == Character.class) {
				return true;
			} else if (fromClass == Boolean.TYPE && intoClass == Boolean.class) {
				return true;
			} else if (fromClass == Short.TYPE && intoClass == Short.class) {
				return true;
			}
		} else {
			if (intoClass.isAssignableFrom(fromClass)) {
				return true;
			}
		}
		return false;
	}

}