com.bugvm.rt.bro.MarshalerLookup Maven / Gradle / Ivy
/*
* Copyright (C) 2014 RoboVM AB
*
* 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 com.bugvm.rt.bro;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import com.bugvm.rt.bro.annotation.MarshalsPointer;
/**
* Looks up marshaler methods for converting {@code long} handles to objects
* given a class extending {@link NativeObject}.
*/
public class MarshalerLookup {
private static final ConcurrentHashMap, Method> TO_OBJECT_CACHE = new ConcurrentHashMap, Method>();
/**
* Convenience method which finds a marshaler method and runs it
* converting the specified handle to the specified type.
*
* @param type the type to convert to.
* @param handle the handle (pointer).
* @return the marshaled instance.
* @throws {@link Error} if no marshaler method could be found.
*/
@SuppressWarnings("unchecked")
public static S toObject(Class type, long handle) {
try {
Method toObject = findMarshaler(type);
return (S) toObject.invoke(null, type, handle, MarshalerFlags.CALL_TYPE_PTR);
} catch (InvocationTargetException e) {
throw new Error(e);
} catch (IllegalAccessException e) {
throw new Error(e);
}
}
/**
* Finds a {@code T toObject(Class>, long, long)} marshaler method which can
* convert handles into the specified type.
*
* @return the {@link Method}
* @throws {@link Error} if no such method could be found.
*/
public static Method findMarshaler(Class extends NativeObject> type) {
Method toObject = TO_OBJECT_CACHE.get(type);
if (toObject == null) {
toObject = findMarshaler(type, type);
TO_OBJECT_CACHE.put(type, toObject);
}
return toObject;
}
private static Method find(Class> findClass, Class> inClass, com.bugvm.rt.bro.annotation.Marshaler anno) {
Class> marshalerClass = anno.value();
for (Method method : marshalerClass.getMethods()) {
if (method.isAnnotationPresent(MarshalsPointer.class)) {
// Is this a valid marshaler method? The signature should match
// T toObject(Class, long, long)
Class> returnType = method.getReturnType();
Class>[] paramTypes = method.getParameterTypes();
if (!returnType.isPrimitive()
&& paramTypes.length == 3 && paramTypes[0] == Class.class
&& paramTypes[1] == long.class && paramTypes[2] == long.class) {
if (returnType.isAssignableFrom(findClass)) {
return method;
}
}
}
}
return null;
}
private static Method findMarshaler0(Class> findClass, Class> inClass) {
com.bugvm.rt.bro.annotation.Marshaler anno1 =
inClass.getAnnotation(com.bugvm.rt.bro.annotation.Marshaler.class);
com.bugvm.rt.bro.annotation.Marshalers anno2 =
inClass.getAnnotation(com.bugvm.rt.bro.annotation.Marshalers.class);
if (anno1 != null) {
Method method = find(findClass, inClass, anno1);
if (method != null) {
return method;
}
}
if (anno2 != null) {
for (com.bugvm.rt.bro.annotation.Marshaler m : anno2.value()) {
Method method = find(findClass, inClass, m);
if (method != null) {
return method;
}
}
}
return null;
}
private static Method findMarshaler(Class> findClass, Class> inClass) {
// Search for a marshaler on the class and its superclasses.
Class> c = inClass;
while (c != null) {
Method marshaler = findMarshaler0(findClass, c);
if (marshaler != null) {
return marshaler;
}
c = c.getSuperclass();
}
for (Class> intf : inClass.getInterfaces()) {
Method marshaler = findMarshaler(findClass, intf);
if (marshaler != null) {
return marshaler;
}
}
Method marshaler = findMarshaler0(findClass, BuiltinMarshalers.class);
if (marshaler != null) {
return marshaler;
}
throw new Error("No marshaler found for class " + findClass.getName());
}
}