JNI: establish API symmetry across QJniObject and QJniArray
We implicitly support QString, QStringList, and QByteArray, as well as C++ types that are equivalent to JNI primitive types (such as bool for jboolean, or int for jint) in QJniObject and some QJniArray APIs. Make this symmetrical for QJniArray::to/fromContainer. Add more compile- and run-time time test coverage. Change-Id: I8cc84e6181a93f889282d2d3f0a05207416c4dbe Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> (cherry picked from commit d9dd8c4986373789b8bd250d0c2b36b660fe210f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
eedbcad7a4
commit
708b84be11
@ -132,42 +132,43 @@ public:
|
||||
"QJniArray::fromContainer", "Container is too large for a Java array");
|
||||
|
||||
using ElementType = typename std::remove_reference_t<Container>::value_type;
|
||||
if constexpr (std::disjunction_v<std::is_same<ElementType, jobject>,
|
||||
std::is_same<ElementType, QJniObject>,
|
||||
std::is_same<ElementType, QString>,
|
||||
std::is_base_of<QtJniTypes::JObjectBase, ElementType>
|
||||
>) {
|
||||
if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>,
|
||||
std::remove_pointer_t<ElementType>>) {
|
||||
return makeObjectArray(std::forward<Container>(container));
|
||||
} else if constexpr (std::is_same_v<ElementType, jfloat>) {
|
||||
} else if constexpr (std::disjunction_v<std::is_same<ElementType, QJniObject>,
|
||||
std::is_same<ElementType, QString>,
|
||||
std::is_base_of<QtJniTypes::JObjectBase, ElementType>
|
||||
>) {
|
||||
return QJniArray<ElementType>(makeObjectArray(std::forward<Container>(container)));
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jfloat>) {
|
||||
return makeArray<jfloat>(std::forward<Container>(container), &JNIEnv::NewFloatArray,
|
||||
&JNIEnv::SetFloatArrayRegion);
|
||||
} else if constexpr (std::is_same_v<ElementType, jdouble>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jdouble>) {
|
||||
return makeArray<jdouble>(std::forward<Container>(container), &JNIEnv::NewDoubleArray,
|
||||
&JNIEnv::SetDoubleArrayRegion);
|
||||
} else if constexpr (std::disjunction_v<std::is_same<ElementType, jboolean>,
|
||||
std::is_same<ElementType, bool>>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jboolean>) {
|
||||
return makeArray<jboolean>(std::forward<Container>(container), &JNIEnv::NewBooleanArray,
|
||||
&JNIEnv::SetBooleanArrayRegion);
|
||||
} else if constexpr (std::disjunction_v<std::is_same<ElementType, jbyte>,
|
||||
std::is_same<ElementType, char>>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jbyte>
|
||||
|| std::is_same_v<ElementType, char>) {
|
||||
return makeArray<jbyte>(std::forward<Container>(container), &JNIEnv::NewByteArray,
|
||||
&JNIEnv::SetByteArrayRegion);
|
||||
} else if constexpr (std::disjunction_v<std::is_same<ElementType, jchar>,
|
||||
std::is_same<ElementType, QChar>>) {
|
||||
return makeArray<jchar>(std::forward<Container>(container), &JNIEnv::NewCharArray,
|
||||
&JNIEnv::SetCharArrayRegion);
|
||||
} else if constexpr (std::is_same_v<ElementType, jshort>
|
||||
|| sizeof(ElementType) == sizeof(jshort)) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jshort>) {
|
||||
return makeArray<jshort>(std::forward<Container>(container), &JNIEnv::NewShortArray,
|
||||
&JNIEnv::SetShortArrayRegion);
|
||||
} else if constexpr (std::is_same_v<ElementType, jint>
|
||||
|| sizeof(ElementType) == sizeof(jint)) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jint>) {
|
||||
return makeArray<jint>(std::forward<Container>(container), &JNIEnv::NewIntArray,
|
||||
&JNIEnv::SetIntArrayRegion);
|
||||
} else if constexpr (std::is_same_v<ElementType, jlong>
|
||||
|| sizeof(ElementType) == sizeof(jlong)) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jlong>) {
|
||||
return makeArray<jlong>(std::forward<Container>(container), &JNIEnv::NewLongArray,
|
||||
&JNIEnv::SetLongArrayRegion);
|
||||
} else {
|
||||
static_assert(QtPrivate::type_dependent_false<ElementType>(),
|
||||
"Don't know how to make QJniArray for this element type");
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,6 +206,8 @@ class QJniArray : public QJniArrayBase
|
||||
template <typename E> struct ToContainerHelper { using type = QList<E>; };
|
||||
template <> struct ToContainerHelper<jstring> { using type = QStringList; };
|
||||
template <> struct ToContainerHelper<jbyte> { using type = QByteArray; };
|
||||
template <> struct ToContainerHelper<char> { using type = QByteArray; };
|
||||
|
||||
template <typename E>
|
||||
using ToContainerType = typename ToContainerHelper<E>::type;
|
||||
|
||||
@ -255,21 +258,23 @@ public:
|
||||
{
|
||||
if constexpr (std::is_convertible_v<jobject, T>)
|
||||
return object<jobjectArray>();
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
else if constexpr (std::is_same_v<T, QString>)
|
||||
return object<jobjectArray>();
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>)
|
||||
return object<jbyteArray>();
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>)
|
||||
return object<jcharArray>();
|
||||
else if constexpr (std::is_same_v<T, jboolean>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>)
|
||||
return object<jbooleanArray>();
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>)
|
||||
return object<jshortArray>();
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jint>)
|
||||
return object<jintArray>();
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>)
|
||||
return object<jlongArray>();
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>)
|
||||
return object<jfloatArray>();
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>)
|
||||
return object<jdoubleArray>();
|
||||
else
|
||||
return object<jarray>();
|
||||
@ -299,26 +304,33 @@ public:
|
||||
return T::fromLocalRef(element);
|
||||
else
|
||||
return T{element};
|
||||
} else if constexpr (std::is_same_v<QString, T>) {
|
||||
jstring string = static_cast<jstring>(env->GetObjectArrayElement(arrayObject(), i));
|
||||
const auto length = env->GetStringLength(string);
|
||||
QString res(length, Qt::Uninitialized);
|
||||
env->GetStringRegion(string, 0, length, reinterpret_cast<jchar *>(res.data()));
|
||||
env->DeleteLocalRef(string);
|
||||
return res;
|
||||
} else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
|
||||
// jstring, jclass etc
|
||||
return static_cast<T>(env->GetObjectArrayElement(object<jobjectArray>(), i));
|
||||
} else {
|
||||
T res = {};
|
||||
if constexpr (std::is_same_v<T, jbyte>)
|
||||
if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>)
|
||||
env->GetByteArrayRegion(object<jbyteArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>)
|
||||
env->GetCharArrayRegion(object<jcharArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jboolean>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>)
|
||||
env->GetBooleanArrayRegion(object<jbooleanArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>)
|
||||
env->GetShortArrayRegion(object<jshortArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jint>)
|
||||
env->GetIntArrayRegion(object<jintArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>)
|
||||
env->GetLongArrayRegion(object<jlongArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>)
|
||||
env->GetFloatArrayRegion(object<jfloatArray>(), i, 1, &res);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>)
|
||||
env->GetDoubleArrayRegion(object<jdoubleArray>(), i, 1, &res);
|
||||
return res;
|
||||
}
|
||||
@ -336,36 +348,40 @@ public:
|
||||
|
||||
container.reserve(sz);
|
||||
if constexpr (std::is_same_v<typename ContainerType::value_type, QString>) {
|
||||
for (auto element : *this)
|
||||
container.emplace_back(QJniObject(element).toString());
|
||||
for (auto element : *this) {
|
||||
if constexpr (std::is_same_v<decltype(element), QString>)
|
||||
container.emplace_back(element);
|
||||
else
|
||||
container.emplace_back(QJniObject(element).toString());
|
||||
}
|
||||
} else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
|
||||
for (auto element : *this)
|
||||
container.emplace_back(element);
|
||||
} else if constexpr (QJniArrayBase::isContiguousContainer<ContainerType>) {
|
||||
container.resize(sz);
|
||||
if constexpr (std::is_same_v<T, jbyte>) {
|
||||
if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>) {
|
||||
env->GetByteArrayRegion(object<jbyteArray>(),
|
||||
0, sz,
|
||||
reinterpret_cast<jbyte *>(container.data()));
|
||||
} else if constexpr (std::is_same_v<T, jchar>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>) {
|
||||
env->GetCharArrayRegion(object<jcharArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jboolean>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>) {
|
||||
env->GetBooleanArrayRegion(object<jbooleanArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jshort>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>) {
|
||||
env->GetShortArrayRegion(object<jshortArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jint>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jint>) {
|
||||
env->GetIntArrayRegion(object<jintArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jlong>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>) {
|
||||
env->GetLongArrayRegion(object<jlongArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jfloat>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>) {
|
||||
env->GetFloatArrayRegion(object<jfloatArray>(),
|
||||
0, sz, container.data());
|
||||
} else if constexpr (std::is_same_v<T, jdouble>) {
|
||||
} else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>) {
|
||||
env->GetDoubleArrayRegion(object<jdoubleArray>(),
|
||||
0, sz, container.data());
|
||||
} else {
|
||||
|
@ -165,8 +165,14 @@
|
||||
\li QJniArray<jbyte>
|
||||
\li QByteArray
|
||||
\row
|
||||
\li QJniArray<char>
|
||||
\li QByteArray
|
||||
\row
|
||||
\li QJniArray<jstring>
|
||||
\li QStringList
|
||||
\row
|
||||
\li QJniArray<QString>
|
||||
\li QStringList
|
||||
\endtable
|
||||
//! [type-mapping]
|
||||
|
||||
|
@ -76,21 +76,61 @@ VERIFY_RETURN_FOR_TYPE(QList<jdouble>, QList<jdouble>);
|
||||
VERIFY_RETURN_FOR_TYPE(QList<double>, QList<double>);
|
||||
|
||||
VERIFY_RETURN_FOR_TYPE(QString, QString);
|
||||
VERIFY_RETURN_FOR_TYPE(QJniArray<QString>, QJniArray<QString>);
|
||||
|
||||
VERIFY_RETURN_FOR_TYPE(List, List);
|
||||
VERIFY_RETURN_FOR_TYPE(List[], QJniArray<List>);
|
||||
VERIFY_RETURN_FOR_TYPE(QJniArray<List>, QJniArray<List>);
|
||||
|
||||
#define VERIFY_CONTAINER_FOR_TYPE(In, Out) \
|
||||
static_assert(std::is_same_v<decltype(std::declval<In>().toContainer()), Out>)
|
||||
static_assert(std::is_same_v<decltype(std::declval<In>().toContainer()), Out>);
|
||||
|
||||
#define VERIFY_ARRAY_FOR_CONTAINER(In, Out) \
|
||||
static_assert(std::is_same_v<decltype(QJniArrayBase::fromContainer(std::declval<In>())), Out>);
|
||||
|
||||
// primitive types, both JNI and C++ equivalent types
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jchar>, QList<jchar>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jchar>, QJniArray<jchar>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jint>, QList<jint>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jint>, QJniArray<jint>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<int>, QList<int>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<int>, QJniArray<int>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jfloat>, QList<jfloat>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jbyte>, QByteArray);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jstring>, QStringList);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jfloat>, QJniArray<jfloat>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<float>, QList<float>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<float>, QJniArray<float>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jdouble>, QList<jdouble>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jdouble>, QJniArray<jdouble>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<double>, QList<double>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<double>, QJniArray<double>);
|
||||
|
||||
// jobject and derivatives
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jobject>, QList<jobject>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jobject>, QJniArray<jobject>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jclass>, QList<jclass>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jclass>, QJniArray<jclass>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jthrowable>, QList<jthrowable>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jthrowable>, QJniArray<jthrowable>);
|
||||
|
||||
// QJniObject-ish classes
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<QJniObject>, QList<QJniObject>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<QJniObject>, QJniArray<QJniObject>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<List>, QList<List>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<List>, QJniArray<List>);
|
||||
|
||||
// Special case: jbyte, (u)char, and QByteArray
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jbyte>, QByteArray);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QByteArray, QJniArray<jbyte>);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jbyte>, QJniArray<jbyte>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<char>, QByteArray);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<char>, QJniArray<jbyte>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<uchar>, QList<uchar>);
|
||||
|
||||
// Special case: jstring, QString, and QStringList
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<jstring>, QStringList);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QStringList, QJniArray<QString>);
|
||||
VERIFY_CONTAINER_FOR_TYPE(QJniArray<QString>, QStringList);
|
||||
VERIFY_ARRAY_FOR_CONTAINER(QList<jstring>, QJniArray<jstring>);
|
||||
|
||||
void tst_QJniArray::construct()
|
||||
{
|
||||
@ -101,6 +141,8 @@ void tst_QJniArray::construct()
|
||||
strings << QString::number(i);
|
||||
QJniArray<QString> list(strings);
|
||||
QCOMPARE(list.size(), 10000);
|
||||
QCOMPARE(list.at(500), QString::number(500));
|
||||
QCOMPARE(list.toContainer(), strings);
|
||||
}
|
||||
{
|
||||
QJniArray bytes = QJniArrayBase::fromContainer(QByteArray("abc"));
|
||||
|
@ -152,6 +152,11 @@ public class QtJniObjectTestClass
|
||||
public Object[] reverseObjectArray(Object[] array)
|
||||
{ return staticReverseObjectArray(array); }
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
public static String[] staticStringArrayMethod()
|
||||
{ String[] array = { "First", "Second", "Third" }; return array; }
|
||||
public String[] stringArrayMethod() { return staticStringArrayMethod(); }
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
public static boolean[] staticBooleanArrayMethod()
|
||||
{ boolean[] array = { true, true, true }; return array; }
|
||||
|
@ -1563,6 +1563,26 @@ void tst_QJniObject::templateApiCheck()
|
||||
QCOMPARE(QJniObject::fromLocalRef(reverse.at(2)).toString(), u"one"_s);
|
||||
}
|
||||
|
||||
// jstringArray ------------------------------------------------------------------------------
|
||||
{
|
||||
const QStringList strings{"First", "Second", "Third"};
|
||||
const auto array = TestClass::callStaticMethod<QJniArray<QtJniTypes::String>>("staticStringArrayMethod");
|
||||
QVERIFY(array.isValid());
|
||||
QCOMPARE(array.size(), 3);
|
||||
QCOMPARE(array.at(0).toString(), strings.first());
|
||||
QCOMPARE(array.toContainer<QStringList>(), strings);
|
||||
}
|
||||
|
||||
// jstringArray via implicit QString support -------------------------------------------------
|
||||
{
|
||||
const QStringList strings{"First", "Second", "Third"};
|
||||
const auto array = TestClass::callStaticMethod<QJniArray<QString>>("staticStringArrayMethod");
|
||||
QVERIFY(array.isValid());
|
||||
QCOMPARE(array.size(), 3);
|
||||
QCOMPARE(array.at(0), strings.first());
|
||||
QCOMPARE(array.toContainer(), strings);
|
||||
}
|
||||
|
||||
// jbooleanArray ------------------------------------------------------------------------------
|
||||
{
|
||||
QJniObject res = QJniObject::callStaticObjectMethod<jbooleanArray>(testClassName,
|
||||
|
Loading…
x
Reference in New Issue
Block a user