io.qt.core.QFunctionPointerUtil Maven / Gradle / Ivy
The newest version!
/****************************************************************************
**
** Copyright (C) 2009-2022 Dr. Peter Droste, Omix Visualization GmbH & Co. KG. All rights reserved.
**
** This file is part of Qt Jambi.
**
** ** $BEGIN_LICENSE$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
** $END_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
package io.qt.core;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.nio.Buffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.qt.NativeAccess;
import io.qt.QNativePointer;
import io.qt.QtMetaType;
import io.qt.QtObjectInterface;
import io.qt.QtPointerType;
import io.qt.QtReferenceType;
import io.qt.QtUtilities;
import io.qt.core.QMetaType;
import io.qt.internal.QtJambiInternal;
final class QFunctionPointerUtil {
private QFunctionPointerUtil() {}
@NativeAccess
private static T createProxy(Class functionalInterface) {
if(!functionalInterface.isInterface())
throw new IllegalArgumentException(functionalInterface.getName().replace('$', '.')+" is not an interface.");
if(!functionalInterface.isAnnotationPresent(FunctionalInterface.class))
throw new IllegalArgumentException(functionalInterface.getName().replace('$', '.')+" is not a functional interface. Annotation @FunctionalInterface expected.");
Method functionMethod = null;
for(Method method : functionalInterface.getDeclaredMethods()) {
if(!Modifier.isStatic(method.getModifiers()) && !method.isDefault()) {
if(functionMethod!=null)
throw new IllegalArgumentException(functionalInterface.getName().replace('$', '.')+" is not a functional interface.");
functionMethod = method;
}
}
if(functionMethod==null)
throw new IllegalArgumentException(functionalInterface.getName().replace('$', '.')+" is not a functional interface.");
return functionalInterface.cast(Proxy.newProxyInstance(
functionalInterface.getClassLoader(),
new Class[] {functionalInterface},
invocationHandlers.computeIfAbsent(functionMethod, JavaToCppInvocationHandler::new)
));
}
private static final Map invocationHandlers = Collections.synchronizedMap(new HashMap<>());
private static abstract class AbstractInvocationHandler implements InvocationHandler{
final QGenericReturnType> returnType;
final QGenericArgumentType>[] argumentTypes;
AbstractInvocationHandler(Method functionMethod) {
super();
QtMetaType metaTypeDecl = functionMethod.getAnnotatedReturnType().getAnnotation(QtMetaType.class);
if(metaTypeDecl==null)
metaTypeDecl = functionMethod.getAnnotation(QtMetaType.class);
QtPointerType pointerType = functionMethod.getAnnotatedReturnType().getAnnotation(QtPointerType.class);
QtReferenceType referenceType = functionMethod.getAnnotatedReturnType().getAnnotation(QtReferenceType.class);
QGenericReturnType> returnType;
if(metaTypeDecl!=null) {
QMetaType metaType;
if(metaTypeDecl.id()!=0) {
metaType = new QMetaType(metaTypeDecl.id());
}else if(metaTypeDecl.type()!=QMetaType.Type.UnknownType){
metaType = new QMetaType(metaTypeDecl.type());
}else {
int id = QtJambiInternal.findMetaType(metaTypeDecl.name());
if(id==0) {
if(metaTypeDecl.name().isEmpty())
throw new IllegalArgumentException("Incomplete @QtMetaType declaration. Either use type, id or name to specify meta type.");
else
throw new IllegalArgumentException("Unable to detect meta type "+metaTypeDecl.name());
}
metaType = new QMetaType(id);
}
returnType = new QGenericReturnType<>(functionMethod.getReturnType(), metaType, 0);
if(pointerType!=null)
returnType = returnType.asPointer();
else if(referenceType!=null) {
if(referenceType.isConst())
returnType = returnType.asConstRef();
else
returnType = returnType.asRef();
}
}else if(functionMethod.getReturnType().isArray()
|| (functionMethod.getReturnType().isPrimitive() && pointerType==null && referenceType==null)
|| Buffer.class.isAssignableFrom(functionMethod.getReturnType())
|| functionMethod.getReturnType().getName().startsWith("com.sun.jna.")) {
returnType = new QGenericReturnType<>(functionMethod.getReturnType(), null, 0);
}else {
if(functionMethod.getReturnType().isInterface()
&& QtObjectInterface.class.isAssignableFrom(functionMethod.getReturnType())
&& functionMethod.getReturnType().isAnnotationPresent(FunctionalInterface.class)
&& QtJambiInternal.findGeneratedSuperclass(functionMethod.getReturnType())==null) {
returnType = new QGenericReturnType<>(functionMethod.getReturnType(), null, 2);
}else {
int metaType = QtJambiInternal.registerMetaType(functionMethod.getReturnType(), functionMethod.getGenericReturnType(), functionMethod.getAnnotatedReturnType(), false, false);
if(metaType!=0) {
returnType = new QGenericReturnType<>(functionMethod.getReturnType(), new QMetaType(metaType), 0);
if(referenceType!=null && referenceType.isConst()) {
returnType = returnType.asConstRef();
}
}else {
returnType = new QGenericReturnType<>(functionMethod.getReturnType(), null, 0);
if(pointerType!=null)
returnType = returnType.asPointer();
else if(referenceType!=null) {
if(referenceType.isConst())
returnType = returnType.asConstRef();
else
returnType = returnType.asRef();
}
}
}
}
QGenericArgumentType>[] argumentTypes = new QGenericArgumentType[functionMethod.getParameterCount()];
Class>[] parameterTypes = functionMethod.getParameterTypes();
Type[] genericParameterTypes = functionMethod.getGenericParameterTypes();
AnnotatedType[] annotatedParameterTypes = functionMethod.getAnnotatedParameterTypes();
for (int i = 0; i < argumentTypes.length; i++) {
pointerType = annotatedParameterTypes[i].getAnnotation(QtPointerType.class);
referenceType = annotatedParameterTypes[i].getAnnotation(QtReferenceType.class);
metaTypeDecl = annotatedParameterTypes[i].getAnnotation(QtMetaType.class);
QGenericArgumentType> argumentType;
Class> parameterType = parameterTypes[i];
Type genericParameterType = genericParameterTypes[i];
if(functionMethod.isVarArgs() && parameterType.isArray()) {
parameterType = parameterType.getComponentType();
if(genericParameterType instanceof GenericArrayType) {
genericParameterType = ((GenericArrayType)genericParameterType).getGenericComponentType();
}else {
genericParameterType = parameterType;
}
}
if(metaTypeDecl!=null) {
QMetaType metaType;
if(metaTypeDecl.id()!=0) {
metaType = new QMetaType(metaTypeDecl.id());
}else if(metaTypeDecl.type()!=QMetaType.Type.UnknownType){
metaType = new QMetaType(metaTypeDecl.type());
}else {
int id = QtJambiInternal.findMetaType(metaTypeDecl.name());
if(id==0) {
if(metaTypeDecl.name().isEmpty())
throw new IllegalArgumentException("Incomplete @QtMetaType declaration. Either use type, id or name to specify meta type.");
else
throw new IllegalArgumentException("Unable to detect meta type "+metaTypeDecl.name());
}
metaType = new QMetaType(id);
}
argumentType = new QGenericReturnType<>(parameterType, metaType, 0);
if(pointerType!=null)
argumentType = argumentType.asPointer();
else if(referenceType!=null) {
if(referenceType.isConst())
argumentType = argumentType.asConstRef();
else
argumentType = argumentType.asRef();
}
}else if(parameterType.isArray()
|| (parameterType.isPrimitive() && pointerType==null && referenceType==null)
|| Buffer.class.isAssignableFrom(parameterType)
|| parameterType.getName().startsWith("com.sun.jna.")) {
argumentType = new QGenericReturnType<>(parameterType, null, 0);
}else {
if(parameterType.isInterface()
&& QtObjectInterface.class.isAssignableFrom(parameterType)
&& parameterType.isAnnotationPresent(FunctionalInterface.class)
&& QtJambiInternal.findGeneratedSuperclass(parameterType)==null) {
argumentType = new QGenericReturnType<>(parameterType, null, 2);
}else {
int metaType = QtJambiInternal.registerMetaType(parameterType, genericParameterType, annotatedParameterTypes[i], false, false);
if(metaType!=0) {
argumentType = new QGenericReturnType<>(parameterType, new QMetaType(metaType), 0);
if(referenceType!=null && referenceType.isConst()) {
argumentType = argumentType.asConstRef();
}
}else {
argumentType = new QGenericReturnType<>(parameterType, null, 0);
if(pointerType!=null)
argumentType = argumentType.asPointer();
else if(referenceType!=null) {
if(referenceType.isConst())
argumentType = argumentType.asConstRef();
else
argumentType = argumentType.asRef();
}
}
}
}
argumentTypes[i] = argumentType;
}
this.returnType = returnType;
this.argumentTypes = argumentTypes;
}
}
private static final class JavaToCppInvocationHandler extends AbstractInvocationHandler{
private final Class> declaringClass;
JavaToCppInvocationHandler(Method functionMethod) {
super(functionMethod);
declaringClass = functionMethod.getDeclaringClass();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(proxy instanceof QtObjectInterface) {
QtObjectInterface iface = (QtObjectInterface)proxy;
if(Modifier.isAbstract(method.getModifiers())) {
if(method.isVarArgs()) {
List arguments = new ArrayList<>();
int j = argumentTypes.length-1;
for (int i = 0; i < (args==null ? 0 : args.length); i++) {
if(i returnType(QGenericReturnType> returnType);
private static native void getParameterTypes(QGenericArgumentType>[] argumentTypes, Class>[] parameterTypes, boolean isVarArgs);
private static native void convertParameters(QGenericArgumentType>[] argumentTypes, Object[] args, Object[] convertedArgs, boolean isVarArgs, boolean forward);
private static native Object convertReturn(QGenericReturnType> returnType, Object result);
private static native void dispose(long callback);
private static final class CppToJavaInvocationHandler extends AbstractInvocationHandler{
private final boolean isVarArgs;
private final MethodHandle functionMethod;
private final QtObjectInterface functionalInterface;
@NativeAccess Object proxy;
@NativeAccess private long peer;
private final Class> returnType;
private final Class>[] parameterTypes;
public CppToJavaInvocationHandler(Method functionMethod, QtObjectInterface functionalInterface) throws IllegalAccessException {
super(functionMethod);
isVarArgs = functionMethod.isVarArgs();
this.functionMethod = QtJambiInternal.getMethodHandle(functionMethod);
this.functionalInterface = functionalInterface;
returnType = returnType(super.returnType);
parameterTypes = new Class[argumentTypes.length];
getParameterTypes(argumentTypes, parameterTypes, isVarArgs);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getDeclaringClass()==Object.class){
return method.invoke(functionalInterface, args);
}else {
switch(method.getName()) {
case "callback":
Object[] newArgs = new Object[argumentTypes.length + 1];
newArgs[0] = functionalInterface;
args = (Object[])args[0];
convertParameters(argumentTypes, args, newArgs, isVarArgs, true);
try {
Object result = functionMethod.invokeWithArguments(newArgs);
result = convertReturn(super.returnType, result);
//convertParameters(argumentTypes, args, newArgs, functionMethod.isVarArgs(), false);
return result;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
case "getParameterTypes":
return Arrays.copyOf(parameterTypes, parameterTypes.length);
case "getReturnType":
return returnType;
}
}
return null;
}
}
@NativeAccess
private static CppToJavaInvocationHandler createCallbackProxy(Class> callbackProxyInterface, Class> functionalInterfaceClass, QtObjectInterface functionalInterface) throws IllegalAccessException {
if(!functionalInterfaceClass.isInterface())
throw new IllegalArgumentException(functionalInterfaceClass.getName().replace('$', '.')+" is not an interface.");
if(!functionalInterfaceClass.isAnnotationPresent(FunctionalInterface.class))
throw new IllegalArgumentException(functionalInterfaceClass.getName().replace('$', '.')+" is not a functional interface. Annotation @FunctionalInterface expected.");
Method functionMethod = null;
for(Method method : functionalInterfaceClass.getDeclaredMethods()) {
if(!Modifier.isStatic(method.getModifiers()) && !method.isDefault()) {
if(functionMethod!=null)
throw new IllegalArgumentException(functionalInterfaceClass.getName().replace('$', '.')+" is not a functional interface.");
functionMethod = method;
}
}
if(functionMethod==null)
throw new IllegalArgumentException(functionalInterfaceClass.getName().replace('$', '.')+" is not a functional interface.");
CppToJavaInvocationHandler invocationHandler = new CppToJavaInvocationHandler(functionMethod, functionalInterface);
invocationHandler.proxy = Proxy.newProxyInstance(callbackProxyInterface.getClassLoader(), new Class[] {callbackProxyInterface}, invocationHandler);
return invocationHandler;
}
@NativeAccess
private static void registerCleanup(QtObjectInterface functionalInterface, CppToJavaInvocationHandler invocationHandler) {
QtUtilities.getSignalOnDispose(functionalInterface).connect(()->{
dispose(invocationHandler.peer);
});
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy