File indexing completed on 2024-06-23 05:08:44

0001 /*
0002     SPDX-FileCopyrightText: 2019-2021 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KANDROIDEXTRAS_JNIOBJECT_H
0008 #define KANDROIDEXTRAS_JNIOBJECT_H
0009 
0010 #include "jnitypetraits.h"
0011 
0012 namespace KAndroidExtras {
0013 
0014 namespace java { namespace lang { struct String; } }
0015 
0016 namespace Internal { struct FromHandleTag{}; }
0017 
0018 namespace Jni {
0019 
0020 template <typename T> class Object;
0021 
0022 template <typename T>
0023 inline QJniObject handle(const T &wrapper)
0024 {
0025     return wrapper.jniHandle();
0026 }
0027 
0028 /** Convert an untyped JNI object handle to a typed wrapper.
0029  *  This is essentially the JNI equivalent to a reinterpret_cast from a void*.
0030  *  Only use this if you are sure @p handle has the correct type, otherwise
0031  *  things will horribly go wrong.
0032  */
0033 template <typename T>
0034 inline auto fromHandle(const QJniObject &handle)
0035 {
0036     if constexpr (Jni::is_object_wrapper<T>::value) {
0037         return T(handle, Internal::FromHandleTag());
0038     } else {
0039         return Jni::Object<T>(handle);
0040     }
0041 }
0042 
0043 template <typename T>
0044 inline auto fromHandle(jobject handle)
0045 {
0046     return fromHandle<T>(QJniObject(handle));
0047 }
0048 
0049 /** Wrapper for JNI objects with a convertible C++ type.
0050  *  This provides implicit on-demand conversion to the C++ type, for types
0051  *  that don't have a manually defined wrapper.
0052  */
0053 template <typename T>
0054 class Object {
0055 private:
0056     template <typename DummyT> class _dummy_t {};
0057 public:
0058     inline explicit Object(const QJniObject &v) : m_handle(v) {}
0059 
0060     // implicit conversion from a compatible C++ type
0061     inline Object(const std::conditional_t<std::is_same_v<typename Jni::converter<T>::type, void>,
0062                   _dummy_t<T>, typename Jni::converter<T>::type> &v)
0063         : m_handle(Jni::reverse_converter<T>::type::convert(v))
0064     {}
0065 
0066     // implicit conversion from a custom wrapper for the same type
0067     template <typename WrapperT, typename = std::enable_if_t<std::is_same_v<typename WrapperT::_jni_BaseType, T>, WrapperT>>
0068     inline Object(const WrapperT &w)
0069         : m_handle(Jni::handle(w))
0070     {}
0071 
0072     // special-case QString, as that often comes out of implicitly converted types,
0073     // and thus can't be consumed by another implicit conversion step
0074     template <typename StrT = QString, typename = std::enable_if_t<std::is_same_v<T, java::lang::String>, StrT>>
0075     inline Object(const QString &s)
0076         : m_handle(QJniObject::fromString(s))
0077     {}
0078 
0079     // special case for null values
0080     inline Object(std::nullptr_t)
0081         : m_handle((jobject)nullptr)
0082     {}
0083 
0084     inline operator QJniObject() const {
0085         return m_handle;
0086     }
0087     inline operator typename Jni::converter<T>::type() const {
0088         return Jni::converter<T>::convert(m_handle);
0089     }
0090 
0091     inline QJniObject jniHandle() const {
0092         return m_handle;
0093     }
0094 
0095     // forward basic QJniObject API
0096     inline bool isValid() const {
0097         return m_handle.isValid();
0098     }
0099     template <typename StrT = QString, typename = std::enable_if_t<std::is_same_v<T, java::lang::String>, StrT>>
0100     inline QString toString() const {
0101         return m_handle.toString();
0102     }
0103 private:
0104     QJniObject m_handle;
0105 };
0106 
0107 /** Annotates a class for holding JNI method or property wrappers.
0108  *
0109  *  Use this if the class only has static methods or constants. For methods
0110  *  and properties, you either need to use @c JNI_OBJECT instead, or make
0111  *  sure there is a @p jniHandle() method returning a @c QJniObject
0112  *  representing the current instance.
0113  *
0114  *  @param Class the name of the class this is added to.
0115  *  @param BaseType the Java type this class represents, defined by the
0116  *  @c JNI_TYPE or @c JNI_NESTED_TYPE macros.
0117  */
0118 #define JNI_UNMANAGED_OBJECT(Class, BaseType) \
0119 public: \
0120     typedef Class _jni_ThisType; \
0121     typedef BaseType _jni_BaseType; \
0122 private: \
0123     static inline constexpr const char *jniName() { return KAndroidExtras::Jni::typeName<BaseType>(); } \
0124     friend constexpr const char* KAndroidExtras::Jni::typeName<Class>();
0125 
0126 /** Annotates a class for holding JNI method or property wrappers.
0127  *
0128  *  @param Class the name of the class this is added to.
0129  *  @param BaseType the Java type this class represents, defined by the
0130  *  @c JNI_TYPE or @c JNI_NESTED_TYPE macros.
0131  */
0132 #define JNI_OBJECT(Class, BaseType) \
0133     JNI_UNMANAGED_OBJECT(Class, BaseType) \
0134 private: \
0135     QJniObject _m_jni_handle; \
0136     inline QJniObject jniHandle() const { return _m_jni_handle; } \
0137     inline void setJniHandle(const QJniObject &h) { _m_jni_handle = h; } \
0138     friend QJniObject KAndroidExtras::Jni::handle<Class>(const Class&); \
0139     friend auto KAndroidExtras::Jni::fromHandle<Class>(const QJniObject&); \
0140     explicit inline Class(const QJniObject &handle, KAndroidExtras::Internal::FromHandleTag) : _m_jni_handle(handle) {}
0141 
0142 }
0143 }
0144 
0145 #endif