com.heroku.api.parser.Json Maven / Gradle / Ivy
package com.heroku.api.parser;
import com.heroku.api.exception.ParseException;
import com.heroku.api.http.HttpUtil;
import com.heroku.api.request.Request;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Json {
static class Holder {
static Parser parser;
static {
ServiceLoader loader = ServiceLoader.load(Parser.class, Parser.class.getClassLoader());
Iterator iterator = loader.iterator();
if (iterator.hasNext()) {
parser = iterator.next();
} else {
throw new IllegalStateException("Unable to load a JSONProvider, please make sure you have a com.heroku.api.json.JSONParser implementation" +
"on your classpath that can be discovered and loaded via java.util.ServiceLoader");
}
}
}
/**
* Calls Parser.parse() using the generic type T for Request given Request is the interface for the
* classType parameter. If it can't find an appropriate type, it errors out with a ParseException.
*
* The following code sample illustrates typical usage in the context of a request to Heroku's API.
* The byte array is provided from a connection.execute(request) call, which is a JSON response from
* the server. getClass() provides the classType, which in this case extends Request. The return
* value from the parse method will be App.
*
* public class SampleRequest implements Request {
* ...
* public App getResponse(byte[] data, int status) {
* return Json.parse(data, getClass());
* }
* }
*
*
* @param data JSON byte array to be parsed
* @param classType The Request implementation class type. This is typically given the calling class as
* an argument.
* @return T
*/
public static T parse(byte[] data, Class extends Request> classType) {
Type type = doResolveTypeArguments(classType, classType, Request.class)[0];
if (type == null) {
throw new ParseException("Request was not found for " + classType.toString());
}
try {
return Holder.parser.parse(data, type);
} catch (RuntimeException e) {
String json = HttpUtil.getUTF8String(data);
throw new RuntimeException("Failed to parse JSON:" + json, e);
}
}
/*
* slightly modded version of spring GenericTypeResolver methods
*/
private static Type[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) {
while (classToIntrospect != null) {
if (genericIfc.isInterface()) {
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
Type[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
if (result != null) {
return result;
}
}
} else {
Type[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
if (result != null) {
return result;
}
}
classToIntrospect = classToIntrospect.getSuperclass();
}
return null;
}
private static Type[] doResolveTypeArguments(Class ownerClass, Type ifc, Class genericIfc) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (genericIfc.equals(rawType)) {
return paramIfc.getActualTypeArguments();
} else if (genericIfc.isAssignableFrom((Class) rawType)) {
return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
}
} else if (ifc != null && genericIfc.isAssignableFrom((Class) ifc)) {
return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
}
return null;
}
}