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

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KANDROIDEXTRAS_JNIPROPERTIES_H
0008 #define KANDROIDEXTRAS_JNIPROPERTIES_H
0009 
0010 #include "jniargument.h"
0011 #include "jniobject.h"
0012 #include "jnisignature.h"
0013 #include "jnitypes.h"
0014 #include "jnitypetraits.h"
0015 
0016 #include <type_traits>
0017 
0018 namespace KAndroidExtras {
0019 
0020 namespace Jni {
0021 template <typename T> class Array;
0022 }
0023 
0024 /** @cond internal */
0025 namespace Internal {
0026 
0027 /** Wrapper for static properties. */
0028 template <typename PropType, typename ClassType, typename NameHolder, bool PrimitiveType> struct StaticProperty {};
0029 template <typename PropType, typename ClassType, typename NameHolder>
0030 struct StaticProperty<PropType, ClassType, NameHolder, false> {
0031     static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
0032     inline QJniObject get() const
0033     {
0034         return QJniObject::getStaticObjectField(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>(), Jni::signature<PropType>());
0035     }
0036     inline operator QJniObject() const
0037     {
0038         return get();
0039     }
0040     inline operator typename Jni::converter<PropType>::type() const
0041     {
0042         return Jni::converter<PropType>::convert(get());
0043     }
0044     template <typename RetT = PropType, typename = std::enable_if_t<Jni::is_generic_wrapper<PropType>::value, RetT>>
0045     inline operator Jni::Object<PropType>() const
0046     {
0047         return Jni::Object<PropType>(get());
0048     }
0049 };
0050 
0051 template <typename PropType, typename ClassType, typename NameHolder>
0052 struct StaticProperty<PropType, ClassType, NameHolder, true> {
0053     inline operator auto() const
0054     {
0055         return primitive_value<PropType>::fromJni(QJniObject::getStaticField<typename primitive_value<PropType>::JniType>(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>()));
0056     }
0057 };
0058 
0059 /** Shared code for non-static property wrappers. */
0060 template <typename ClassType, typename OffsetHolder>
0061 class PropertyBase {
0062 protected:
0063     inline QJniObject handle() const {
0064         const auto owner = reinterpret_cast<const ClassType*>(reinterpret_cast<const char*>(this) - OffsetHolder::offset());
0065         return owner->jniHandle();
0066     }
0067 };
0068 
0069 /** Wrapper for non-static properties. */
0070 template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder, bool PrimitiveType> struct Property {};
0071 template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
0072 class Property<PropType, ClassType, NameHolder, OffsetHolder, false> : public PropertyBase<ClassType, OffsetHolder> {
0073 private:
0074     struct _jni_NoType {};
0075     static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
0076 public:
0077     inline QJniObject get() const
0078     {
0079         return this->handle().getObjectField(Jni::typeName<NameHolder>(), Jni::signature<PropType>());
0080     }
0081     inline operator QJniObject() const
0082     {
0083         return get();
0084     }
0085     inline operator typename Jni::converter<PropType>::type() const
0086     {
0087         return Jni::converter<PropType>::convert(get());
0088     }
0089     template <typename RetT = PropType, typename = std::enable_if_t<Jni::is_array<PropType>::value, RetT>>
0090     inline operator PropType() const
0091     {
0092         return PropType(get());
0093     }
0094     template <typename RetT = PropType, typename = std::enable_if_t<Jni::is_generic_wrapper<PropType>::value, RetT>>
0095     inline operator Jni::Object<PropType>() const
0096     {
0097         return Jni::Object<PropType>(get());
0098     }
0099 
0100     inline Property& operator=(typename Internal::argument<PropType>::type value)
0101     {
0102         this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), Internal::argument<PropType>::toCallArgument(value));
0103         return *this;
0104     }
0105     inline Property& operator=(const QJniObject &value)
0106     {
0107         this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), value.object());
0108         return *this;
0109     }
0110     inline Property& operator=(const typename std::conditional<std::is_same_v<typename Jni::converter<PropType>::type, void>, _jni_NoType, typename Jni::converter<PropType>::type>::type &value)
0111     {
0112         this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), Jni::reverse_converter<PropType>::type::convert(value).object());
0113         return *this;
0114     }
0115 
0116     // special case for string comparison, which is often done against different types and thus the implicit conversion operator
0117     // isn't going to be enough
0118     template <typename CmpT, typename = std::enable_if_t<std::is_same_v<PropType, java::lang::String>, CmpT>>
0119     inline bool operator==(const CmpT &other) const
0120     {
0121         return QString(*this) == other;
0122     }
0123 };
0124 
0125 template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
0126 class Property<PropType, ClassType, NameHolder, OffsetHolder, true> : public PropertyBase<ClassType, OffsetHolder> {
0127 public:
0128     inline operator auto() const
0129     {
0130         return primitive_value<PropType>::fromJni(this->handle().template getField<typename primitive_value<PropType>::JniType>(Jni::typeName<NameHolder>()));
0131     }
0132     inline Property& operator=(PropType value)
0133     {
0134         this->handle().setField(Jni::typeName<NameHolder>(), primitive_value<PropType>::toJni(value));
0135         return *this;
0136     }
0137 };
0138 
0139 // TODO KF6: can be replaced by QT_WARNING_DISABLE_INVALID_OFFSETOF
0140 #if defined(Q_CC_CLANG)
0141 #define JNI_WARNING_DISABLE_INVALID_OFFSETOF QT_WARNING_DISABLE_CLANG("-Winvalid-offsetof")
0142 #elif defined(Q_CC_GNU)
0143 #define JNI_WARNING_DISABLE_INVALID_OFFSETOF QT_WARNING_DISABLE_GCC("-Winvalid-offsetof")
0144 #else
0145 #define JNI_WARNING_DISABLE_INVALID_OFFSETOF
0146 #endif
0147 
0148 /** @endcond */
0149 }
0150 
0151 /**
0152  * Wrap a static final property.
0153  * This will add a public static member named @p name to the current class. This member defines an
0154  * implicit conversion operator which will trigger the corresponding a JNI read operation.
0155  * Can only be placed in classes with a @c JNI_OBJECT.
0156  *
0157  * @note Make sure to access this member with a specific type, assigning to an @c auto variable will
0158  * copy the wrapper type, not read the property value.
0159  *
0160  * @param type The data type of the property.
0161  * @param name The name of the property.
0162  */
0163 #define JNI_CONSTANT(type, name) \
0164 private: \
0165     struct _jni_ ## name ## __NameHolder { static constexpr const char* jniName() { return "" #name; } }; \
0166 public: \
0167     static inline const KAndroidExtras::Internal::StaticProperty<type, _jni_ThisType, _jni_ ## name ## __NameHolder, Jni::is_primitive_type<type>::value> name;
0168 
0169 /**
0170  * Wrap a member property.
0171  * This will add a public zero-size member named @p name to the current class. This member defines an
0172  * implicit conversion operator which will trigger the corresponding a JNI read operation, as well
0173  * as an overloaded assignment operator for the corresponding write operation.
0174  * Can only be placed in classes with a @c JNI_OBJECT.
0175  *
0176  * @note Make sure to access this member with a specific type, assigning to an @c auto variable will
0177  * copy the wrapper type, not read the property value.
0178  *
0179  * @param type The data type of the property.
0180  * @param name The name of the property.
0181  */
0182 #define JNI_PROPERTY(type, name) \
0183 private: \
0184     struct _jni_ ## name ## __NameHolder { static constexpr const char* jniName() { return "" #name; } }; \
0185     struct _jni_ ## name ## __OffsetHolder { \
0186         static constexpr std::size_t offset() { \
0187             QT_WARNING_PUSH JNI_WARNING_DISABLE_INVALID_OFFSETOF \
0188             return offsetof(_jni_ThisType, name); \
0189             QT_WARNING_POP \
0190         } \
0191     }; \
0192     friend class KAndroidExtras::Internal::PropertyBase<_jni_ThisType, _jni_ ## name ## __OffsetHolder>; \
0193 public: \
0194     [[no_unique_address]] KAndroidExtras::Internal::Property<type, _jni_ThisType, _jni_ ## name ## __NameHolder, _jni_ ## name ## __OffsetHolder, KAndroidExtras::Jni::is_primitive_type<type>::value> name;
0195 }
0196 
0197 #endif // KANDROIDEXTRAS_JNIPROPERTIES_H