forked from dotnet/android
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJavaObjectExtensions.cs
More file actions
143 lines (123 loc) · 5.48 KB
/
JavaObjectExtensions.cs
File metadata and controls
143 lines (123 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Android.Runtime;
namespace Java.Interop {
public static class JavaObjectExtensions {
[Obsolete ("Use Android.Runtime.JavaCollection.ToLocalJniHandle()")]
public static JavaCollection ToInteroperableCollection (this ICollection instance)
{
return instance is JavaCollection ? (JavaCollection) instance : new JavaCollection (instance);
}
[Obsolete ("Use Android.Runtime.JavaCollection<T>.ToLocalJniHandle()")]
public static JavaCollection<T> ToInteroperableCollection<T> (this ICollection<T> instance)
{
return instance is JavaCollection<T> ? (JavaCollection<T>) instance : new JavaCollection<T> (instance);
}
[Obsolete ("Use Android.Runtime.JavaList.ToLocalJniHandle()")]
public static JavaList ToInteroperableCollection (this IList instance)
{
return instance is JavaList ? (JavaList) instance : new JavaList (instance);
}
[Obsolete ("Use Android.Runtime.JavaList<T>.ToLocalJniHandle()")]
public static JavaList<T> ToInteroperableCollection<T> (this IList<T> instance)
{
return instance is JavaList<T> ? (JavaList<T>) instance : new JavaList<T> (instance);
}
[Obsolete ("Use Android.Runtime.JavaDictionary.ToLocalJniHandle()")]
public static JavaDictionary ToInteroperableCollection (this IDictionary instance)
{
return instance is JavaDictionary ? (JavaDictionary) instance : new JavaDictionary (instance);
}
[Obsolete ("Use Android.Runtime.JavaDictionary<K, V>.ToLocalJniHandle()")]
public static JavaDictionary<K,V> ToInteroperableCollection<K,V> (this IDictionary<K,V> instance)
{
return instance is JavaDictionary<K,V> ? (JavaDictionary<K,V>) instance : new JavaDictionary<K,V> (instance);
}
[return: NotNullIfNotNull ("instance")]
public static TResult? JavaCast<TResult> (this IJavaObject? instance)
where TResult : class, IJavaObject
{
return _JavaCast<TResult> (instance);
}
[return: MaybeNull]
internal static TResult _JavaCast<TResult> (this IJavaObject? instance)
{
if (instance == null)
return default (TResult);
if (instance is TResult)
return (TResult) instance;
if (instance.Handle == IntPtr.Zero)
throw new ObjectDisposedException (instance.GetType ().FullName);
Type resultType = typeof (TResult);
if (resultType.IsClass) {
return (TResult) CastClass (instance, resultType);
}
else if (resultType.IsInterface) {
return (TResult) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType);
}
else
throw new NotSupportedException (FormattableString.Invariant ($"Unable to convert type '{instance.GetType ().FullName}' to '{resultType.FullName}'."));
}
static IJavaObject CastClass (IJavaObject instance, Type resultType)
{
var klass = JNIEnv.FindClass (resultType);
try {
if (klass == IntPtr.Zero)
throw new ArgumentException ("Unable to determine JNI class for '" + resultType.FullName + "'.", "TResult");
if (!JNIEnv.IsInstanceOf (instance.Handle, klass))
throw new InvalidCastException (
FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{resultType.FullName}'."));
} finally {
JNIEnv.DeleteGlobalRef (klass);
}
if (resultType.IsAbstract) {
// TODO: keep in sync with TypeManager.CreateInstance() algorithm
var invokerType = GetInvokerType (resultType);
if (invokerType == null)
throw new ArgumentException ("Unable to get Invoker for abstract type '" + resultType.FullName + "'.", "TResult");
resultType = invokerType;
}
return (IJavaObject) TypeManager.CreateProxy (resultType, instance.Handle, JniHandleOwnership.DoNotTransfer);
}
internal static IJavaObject? JavaCast (IJavaObject? instance, Type resultType)
{
if (resultType == null)
throw new ArgumentNullException ("resultType");
if (instance == null)
return null;
if (resultType.IsAssignableFrom (instance.GetType ()))
return instance;
if (resultType.IsClass) {
return CastClass (instance, resultType);
}
else if (resultType.IsInterface) {
return (IJavaObject?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType);
}
else
throw new NotSupportedException (FormattableString.Invariant ($"Unable to convert type '{instance.GetType ().FullName}' to '{resultType.FullName}'."));
}
// typeof(Foo) -> FooInvoker
// typeof(Foo<>) -> FooInvoker`1
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "*Invoker types are preserved by the MarkJavaObjects linker step.")]
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = "*Invoker types are preserved by the MarkJavaObjects linker step.")]
internal static Type? GetInvokerType (Type type)
{
const string suffix = "Invoker";
Type[] arguments = type.GetGenericArguments ();
if (arguments.Length == 0)
return type.Assembly.GetType (type + suffix);
Type definition = type.GetGenericTypeDefinition ();
int bt = definition.FullName!.IndexOf ("`", StringComparison.Ordinal);
if (bt == -1)
throw new NotSupportedException ("Generic type doesn't follow generic type naming convention! " + type.FullName);
Type? suffixDefinition = definition.Assembly.GetType (
definition.FullName.Substring (0, bt) + suffix + definition.FullName.Substring (bt));
if (suffixDefinition == null)
return null;
return suffixDefinition.MakeGenericType (arguments);
}
}
}