Compare commits
8 Commits
master
...
dev/32-bit
Author | SHA1 | Date |
---|---|---|
![]() |
328decd147 | |
![]() |
677cf1a463 | |
![]() |
b708c7527a | |
![]() |
e4b105dc83 | |
![]() |
e4a9fc7ff7 | |
![]() |
20c43bb6ec | |
![]() |
ca0014b6eb | |
![]() |
5b7dd458e6 |
|
@ -115,7 +115,7 @@ public extension JNICore {
|
||||||
return jboolean(JNI_FALSE)
|
return jboolean(JNI_FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum JNIError: Error {
|
enum JNIError: Error, LocalizedError {
|
||||||
|
|
||||||
case classNotFoundException(String)
|
case classNotFoundException(String)
|
||||||
case methodNotFoundException(String)
|
case methodNotFoundException(String)
|
||||||
|
@ -132,6 +132,17 @@ public extension JNICore {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .classNotFoundException(let message):
|
||||||
|
return "ClassNotFoundaException: \(message)"
|
||||||
|
case .methodNotFoundException(let message):
|
||||||
|
return "MethodNotFoundException: \(message)"
|
||||||
|
case .fieldNotFoundException(let message):
|
||||||
|
return "FieldNotFoundException: \(message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Global cache functions
|
// MARK: Global cache functions
|
||||||
|
@ -254,6 +265,18 @@ public extension JNICore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CallFloatMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jfloat {
|
||||||
|
return checkArgument(args: args, { argsPtr in
|
||||||
|
api.CallFloatMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallDoubleMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jdouble {
|
||||||
|
return checkArgument(args: args, { argsPtr in
|
||||||
|
api.CallDoubleMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func CallObjectMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jobject? {
|
func CallObjectMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jobject? {
|
||||||
return checkArgument(args: args, { argsPtr in
|
return checkArgument(args: args, { argsPtr in
|
||||||
api.CallObjectMethodA(env, object, methodID, argsPtr)
|
api.CallObjectMethodA(env, object, methodID, argsPtr)
|
||||||
|
@ -335,6 +358,78 @@ public extension JNICore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CallBooleanMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jboolean {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallBooleanMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallByteMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jbyte {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallByteMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallShortMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jshort {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallShortMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallIntMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jint {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallIntMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallLongMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jlong {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallLongMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallFloatMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jfloat {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallFloatMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallDoubleMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jdouble {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallDoubleMethodA(env, object, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallStaticBooleanMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jboolean {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallStaticBooleanMethodA(env, clazz, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallStaticByteMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jbyte {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallStaticByteMethodA(env, clazz, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallStaticShortMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jshort {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallStaticShortMethodA(env, clazz, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallStaticIntMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jint {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallStaticIntMethodA(env, clazz, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallStaticLongMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jlong {
|
||||||
|
return checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
|
api.CallStaticLongMethodA(env, clazz, methodID, argsPtr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func CallVoidMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) {
|
func CallVoidMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) {
|
||||||
checkArgumentAndWrap(args: args, { argsPtr in
|
checkArgumentAndWrap(args: args, { argsPtr in
|
||||||
api.CallVoidMethodA(env, object, methodID, argsPtr)
|
api.CallVoidMethodA(env, object, methodID, argsPtr)
|
||||||
|
|
|
@ -6,8 +6,8 @@ import Foundation
|
||||||
import java_swift
|
import java_swift
|
||||||
import CJavaVM
|
import CJavaVM
|
||||||
|
|
||||||
public typealias JavaEncodableClosure = (Any) throws -> jobject
|
public typealias JavaEncodableClosure = (Any, [CodingKey]) throws -> jobject
|
||||||
public typealias JavaDecodableClosure = (jobject) throws -> Decodable
|
public typealias JavaDecodableClosure = (jobject, [CodingKey]) throws -> Decodable
|
||||||
|
|
||||||
public struct JavaCoderConfig {
|
public struct JavaCoderConfig {
|
||||||
|
|
||||||
|
@ -43,153 +43,170 @@ public struct JavaCoderConfig {
|
||||||
|
|
||||||
public static func RegisterBasicJavaTypes() {
|
public static func RegisterBasicJavaTypes() {
|
||||||
|
|
||||||
RegisterType(type: Int.self, javaClassname: IntegerClassname, encodableClosure: {
|
RegisterType(type: Int.self, javaClassname: IntegerClassname, encodableClosure: { any, codingPath in
|
||||||
// jint for macOS and Android different, that's why we make cast to jint() here
|
let value = any as! Int
|
||||||
let args = [jvalue(i: jint($0 as! Int))]
|
let primitive = try value.javaPrimitive(codingPath: codingPath)
|
||||||
|
let args = [jvalue(i: primitive)]
|
||||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return Int(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
Int(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Int8.self, javaClassname: ByteClassname, encodableClosure: {
|
RegisterType(type: Int8.self, javaClassname: ByteClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(b: $0 as! Int8)]
|
let value = any as! Int8
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(b: primitive)]
|
||||||
return JNI.NewObject(ByteClass, methodID: ByteConstructor, args: args)!
|
return JNI.NewObject(ByteClass, methodID: ByteConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return JNI.CallByteMethod($0, methodID: NumberByteValueMethod)
|
Int8(fromJavaPrimitive: JNI.CallByteMethod(value, methodID: NumberByteValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Int16.self, javaClassname: ShortClassname, encodableClosure: {
|
RegisterType(type: Int16.self, javaClassname: ShortClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(s: $0 as! Int16)]
|
let value = any as! Int16
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(s: primitive)]
|
||||||
return JNI.NewObject(ShortClass, methodID: ShortConstructor, args: args)!
|
return JNI.NewObject(ShortClass, methodID: ShortConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return JNI.CallShortMethod($0, methodID: NumberShortValueMethod)
|
Int16(fromJavaPrimitive: JNI.CallShortMethod(value, methodID: NumberShortValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Int32.self, javaClassname: IntegerClassname, encodableClosure: {
|
RegisterType(type: Int32.self, javaClassname: IntegerClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(i: jint($0 as! Int32))]
|
let value = any as! Int32
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(i: primitive)]
|
||||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return Int32(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
Int32(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Int64.self, javaClassname: LongClassname, encodableClosure: {
|
RegisterType(type: Int64.self, javaClassname: LongClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(j: $0 as! Int64)]
|
let value = any as! Int64
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(j: primitive)]
|
||||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return JNI.CallLongMethod($0, methodID: NumberLongValueMethod)
|
Int64(fromJavaPrimitive: JNI.CallLongMethod(value, methodID: NumberLongValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: UInt.self, javaClassname: LongClassname, encodableClosure: {
|
RegisterType(type: UInt.self, javaClassname: IntegerClassname, encodableClosure: { any, codingPath in
|
||||||
let args = [jvalue(j: Int64($0 as! UInt))]
|
let value = any as! UInt
|
||||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
let primitive = try value.javaPrimitive(codingPath: codingPath)
|
||||||
}, decodableClosure: {
|
let args = [jvalue(i: primitive)]
|
||||||
return UInt(JNI.CallLongMethod($0, methodID: NumberLongValueMethod))
|
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||||
|
}, decodableClosure: { value, _ in
|
||||||
|
UInt(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: UInt8.self, javaClassname: ShortClassname, encodableClosure: {
|
RegisterType(type: UInt8.self, javaClassname: ByteClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(s: Int16($0 as! UInt8))]
|
let value = any as! UInt8
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(b: primitive)]
|
||||||
|
return JNI.NewObject(ByteClass, methodID: ByteConstructor, args: args)!
|
||||||
|
}, decodableClosure: { value, _ in
|
||||||
|
UInt8(fromJavaPrimitive: JNI.CallByteMethod(value, methodID: NumberByteValueMethod))
|
||||||
|
})
|
||||||
|
|
||||||
|
RegisterType(type: UInt16.self, javaClassname: ShortClassname, encodableClosure: { any, _ in
|
||||||
|
let value = any as! UInt16
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(s: primitive)]
|
||||||
return JNI.NewObject(ShortClass, methodID: ShortConstructor, args: args)!
|
return JNI.NewObject(ShortClass, methodID: ShortConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return UInt8(JNI.CallShortMethod($0, methodID: NumberShortValueMethod))
|
UInt16(fromJavaPrimitive: JNI.CallShortMethod(value, methodID: NumberShortValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: UInt16.self, javaClassname: IntegerClassname, encodableClosure: {
|
RegisterType(type: UInt32.self, javaClassname: IntegerClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(i: jint($0 as! UInt16))]
|
let value = any as! UInt32
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(i: primitive)]
|
||||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return UInt16(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
UInt32(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: UInt32.self, javaClassname: LongClassname, encodableClosure: {
|
RegisterType(type: UInt64.self, javaClassname: LongClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(j: Int64($0 as! UInt32))]
|
let value = any as! UInt64
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(j: primitive)]
|
||||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return UInt32(JNI.CallLongMethod($0, methodID: NumberLongValueMethod))
|
UInt64(fromJavaPrimitive: JNI.CallLongMethod(value, methodID: NumberLongValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: UInt64.self, javaClassname: BigIntegerClassname, encodableClosure: {
|
RegisterType(type: Float.self, javaClassname: FloatClassname, encodableClosure: { any, _ in
|
||||||
var locals = [jobject]()
|
let value = any as! Float
|
||||||
let args = [jvalue(l: String($0 as! UInt64).localJavaObject(&locals))]
|
let primitive = try value.javaPrimitive()
|
||||||
return JNI.check(JNI.NewObject(BigIntegerClass, methodID: BigIntegerConstructor, args: args)!, &locals)
|
let args = [jvalue(f: primitive)]
|
||||||
}, decodableClosure: {
|
|
||||||
let javaString = JNI.CallObjectMethod($0, methodID: ObjectToStringMethod)
|
|
||||||
defer {
|
|
||||||
JNI.api.DeleteLocalRef(JNI.env, javaString)
|
|
||||||
}
|
|
||||||
let stringRepresentation = String(javaObject: javaString)
|
|
||||||
return UInt64(stringRepresentation)
|
|
||||||
})
|
|
||||||
|
|
||||||
RegisterType(type: Float.self, javaClassname: FloatClassname, encodableClosure: {
|
|
||||||
let args = [jvalue(f: $0 as! Float)]
|
|
||||||
return JNI.NewObject(FloatClass, methodID: FloatConstructor, args: args)!
|
return JNI.NewObject(FloatClass, methodID: FloatConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return JNI.api.CallFloatMethodA(JNI.env, $0, NumberFloatValueMethod, nil)
|
Float(fromJavaPrimitive: JNI.CallFloatMethod(value, methodID: NumberFloatValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Double.self, javaClassname: DoubleClassname, encodableClosure: {
|
RegisterType(type: Double.self, javaClassname: DoubleClassname, encodableClosure: { any, _ in
|
||||||
let args = [jvalue(d: $0 as! Double)]
|
let value = any as! Double
|
||||||
|
let primitive = try value.javaPrimitive()
|
||||||
|
let args = [jvalue(d: primitive)]
|
||||||
return JNI.NewObject(DoubleClass, methodID: DoubleConstructor, args: args)!
|
return JNI.NewObject(DoubleClass, methodID: DoubleConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return JNI.api.CallDoubleMethodA(JNI.env, $0, NumberDoubleValueMethod, nil)
|
Double(fromJavaPrimitive: JNI.CallDoubleMethod(value, methodID: NumberDoubleValueMethod))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Bool.self, javaClassname: BooleanClassname, encodableClosure: {
|
RegisterType(type: Bool.self, javaClassname: BooleanClassname, encodableClosure: { value, _ in
|
||||||
let args = [jvalue(z: $0 as! Bool ? JNI.TRUE : JNI.FALSE)]
|
let args = [jvalue(z: value as! Bool ? JNI.TRUE : JNI.FALSE)]
|
||||||
return JNI.NewObject(BooleanClass, methodID: BooleanConstructor, args: args)!
|
return JNI.NewObject(BooleanClass, methodID: BooleanConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return (JNI.CallBooleanMethod($0, methodID: NumberBooleanValueMethod) == JNI.TRUE)
|
JNI.CallBooleanMethod(value, methodID: NumberBooleanValueMethod) == JNI.TRUE
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: String.self, javaClassname: StringClassname, encodableClosure: {
|
RegisterType(type: String.self, javaClassname: StringClassname, encodableClosure: { value, _ in
|
||||||
let valueString = $0 as! String
|
let valueString = value as! String
|
||||||
var locals = [jobject]()
|
var locals = [jobject]()
|
||||||
// Locals ignored because JNIStorageObject take ownership of LocalReference
|
// Locals ignored because JNIStorageObject take ownership of LocalReference
|
||||||
return valueString.localJavaObject(&locals)!
|
return valueString.localJavaObject(&locals)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
return String(javaObject: $0)
|
String(javaObject: value)
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Date.self, javaClassname: DateClassname, encodableClosure: {
|
RegisterType(type: Date.self, javaClassname: DateClassname, encodableClosure: { value, _ in
|
||||||
let valueDate = $0 as! Date
|
let valueDate = value as! Date
|
||||||
let args = [jvalue(j: jlong(valueDate.timeIntervalSince1970 * 1000))]
|
let args = [jvalue(j: jlong(valueDate.timeIntervalSince1970 * 1000))]
|
||||||
return JNI.NewObject(DateClass, methodID: DateConstructor, args: args)!
|
return JNI.NewObject(DateClass, methodID: DateConstructor, args: args)!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
let timeInterval = JNI.api.CallLongMethodA(JNI.env, $0, DateGetTimeMethod, nil)
|
let timeInterval = JNI.api.CallLongMethodA(JNI.env, value, DateGetTimeMethod, nil)
|
||||||
// Java save TimeInterval in UInt64 milliseconds
|
// Java save TimeInterval in UInt64 milliseconds
|
||||||
return Date(timeIntervalSince1970: TimeInterval(timeInterval) / 1000.0)
|
return Date(timeIntervalSince1970: TimeInterval(timeInterval) / 1000.0)
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: URL.self, javaClassname: UriClassname, encodableClosure: {
|
RegisterType(type: URL.self, javaClassname: UriClassname, encodableClosure: { value, _ in
|
||||||
var locals = [jobject]()
|
var locals = [jobject]()
|
||||||
let javaString = ($0 as! URL).absoluteString.localJavaObject(&locals)
|
let javaString = (value as! URL).absoluteString.localJavaObject(&locals)
|
||||||
let args = [jvalue(l: javaString)]
|
let args = [jvalue(l: javaString)]
|
||||||
JNI.SaveFatalErrorMessage("UriConstructor")
|
JNI.SaveFatalErrorMessage("UriConstructor")
|
||||||
defer {
|
defer {
|
||||||
JNI.RemoveFatalErrorMessage()
|
JNI.RemoveFatalErrorMessage()
|
||||||
}
|
}
|
||||||
return JNI.check(JNI.CallStaticObjectMethod(UriClass, methodID: UriConstructor!, args: args)!, &locals)
|
return JNI.check(JNI.CallStaticObjectMethod(UriClass, methodID: UriConstructor!, args: args)!, &locals)
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
let pathString = JNI.api.CallObjectMethodA(JNI.env, $0, ObjectToStringMethod, nil)
|
let pathString = JNI.api.CallObjectMethodA(JNI.env, value, ObjectToStringMethod, nil)
|
||||||
return URL(string: String(javaObject: pathString))
|
return URL(string: String(javaObject: pathString))
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: {
|
RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: { data, codingPath in
|
||||||
let valueData = $0 as! Data
|
let valueData = data as! Data
|
||||||
let byteArray = JNI.api.NewByteArray(JNI.env, jint(valueData.count))
|
let byteArray = JNI.api.NewByteArray(JNI.env, jint(valueData.count))
|
||||||
if let throwable = JNI.ExceptionCheck() {
|
if let throwable = JNI.ExceptionCheck() {
|
||||||
throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [],
|
throw EncodingError.invalidValue(data, EncodingError.Context(codingPath: codingPath,
|
||||||
debugDescription: "Can't create NewByteArray \(valueData.count)"))
|
debugDescription: "Can't create NewByteArray \(valueData.count)"))
|
||||||
}
|
}
|
||||||
try valueData.withUnsafeBytes({ pointer in
|
try valueData.withUnsafeBytes({ pointer in
|
||||||
guard let bytes = pointer.baseAddress?.assumingMemoryBound(to: Int8.self) else {
|
guard let bytes = pointer.baseAddress?.assumingMemoryBound(to: Int8.self) else {
|
||||||
throw EncodingError.invalidValue(valueData, EncodingError.Context(codingPath: [],
|
throw EncodingError.invalidValue(valueData, EncodingError.Context(codingPath: codingPath,
|
||||||
debugDescription: "Can't get unsafeBytes \(valueData.count)"))
|
debugDescription: "Can't get unsafeBytes \(valueData.count)"))
|
||||||
}
|
}
|
||||||
JNI.api.SetByteArrayRegion(JNI.env, byteArray, 0, jint(valueData.count), bytes)
|
JNI.api.SetByteArrayRegion(JNI.env, byteArray, 0, jint(valueData.count), bytes)
|
||||||
})
|
})
|
||||||
if let throwable = JNI.ExceptionCheck() {
|
if let throwable = JNI.ExceptionCheck() {
|
||||||
throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [],
|
throw EncodingError.invalidValue(data, EncodingError.Context(codingPath: codingPath,
|
||||||
debugDescription: "SetByteArrayRegion failed \(valueData.count)"))
|
debugDescription: "SetByteArrayRegion failed \(valueData.count)"))
|
||||||
}
|
}
|
||||||
JNI.SaveFatalErrorMessage("java/nio/ByteBuffer wrap")
|
JNI.SaveFatalErrorMessage("java/nio/ByteBuffer wrap")
|
||||||
|
@ -197,8 +214,8 @@ public struct JavaCoderConfig {
|
||||||
JNI.RemoveFatalErrorMessage()
|
JNI.RemoveFatalErrorMessage()
|
||||||
}
|
}
|
||||||
return JNI.CallStaticObjectMethod(ByteBufferClass, methodID: ByteBufferWrap, args: [jvalue(l: byteArray)])!
|
return JNI.CallStaticObjectMethod(ByteBufferClass, methodID: ByteBufferWrap, args: [jvalue(l: byteArray)])!
|
||||||
}, decodableClosure: {
|
}, decodableClosure: { value, _ in
|
||||||
let byteArray = JNI.CallObjectMethod($0, methodID: ByteBufferArray)
|
let byteArray = JNI.CallObjectMethod(value, methodID: ByteBufferArray)
|
||||||
defer {
|
defer {
|
||||||
JNI.api.DeleteLocalRef(JNI.env, byteArray)
|
JNI.api.DeleteLocalRef(JNI.env, byteArray)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// Created by Andriy Druk on 19.04.2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum JavaCodingError: LocalizedError {
|
||||||
|
case notSupported(String)
|
||||||
|
case cantCreateObject(String)
|
||||||
|
case cantFindObject(String)
|
||||||
|
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .notSupported(let message):
|
||||||
|
return "Not supported: \(message)"
|
||||||
|
case .cantCreateObject(let message):
|
||||||
|
return "Can't create object: \(message)"
|
||||||
|
case .cantFindObject(let message):
|
||||||
|
return "Can't find object: \(message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need one more protocol, because we can't override description func in EncodingError & DecodingError
|
||||||
|
public protocol JavaCodingErrorDescription {
|
||||||
|
var detailedDescription: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func contextDescription(codingPath: [CodingKey],
|
||||||
|
debugDescription: String,
|
||||||
|
underlyingError: Error?) -> String {
|
||||||
|
var underlyingErrorDescription = ""
|
||||||
|
if let underlyingError = underlyingError {
|
||||||
|
underlyingErrorDescription = " with underlying error: \(underlyingError.localizedDescription)"
|
||||||
|
}
|
||||||
|
let path = codingPath.map({ $0.stringValue }).joined(separator: "/")
|
||||||
|
return "\(debugDescription) [\(path)]" + underlyingErrorDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EncodingError.Context: JavaCodingErrorDescription {
|
||||||
|
public var detailedDescription: String {
|
||||||
|
return contextDescription(codingPath: codingPath, debugDescription: debugDescription, underlyingError: underlyingError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DecodingError.Context: JavaCodingErrorDescription {
|
||||||
|
public var detailedDescription: String {
|
||||||
|
return contextDescription(codingPath: codingPath, debugDescription: debugDescription, underlyingError: underlyingError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EncodingError: JavaCodingErrorDescription {
|
||||||
|
public var detailedDescription: String {
|
||||||
|
switch self {
|
||||||
|
case .invalidValue(let value, let context):
|
||||||
|
return "Invalid value \"\(value)\": \(context.detailedDescription)"
|
||||||
|
@unknown default:
|
||||||
|
return "Not supported encoding error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DecodingError: JavaCodingErrorDescription {
|
||||||
|
public var detailedDescription: String {
|
||||||
|
switch self {
|
||||||
|
case .typeMismatch(let value, let context):
|
||||||
|
return "Type mismatch \"\(value)\": \(context.detailedDescription)"
|
||||||
|
case .valueNotFound(let value, let context):
|
||||||
|
return "Value not found \"\(value)\": \(context.detailedDescription)"
|
||||||
|
case .keyNotFound(let codingKey, let context):
|
||||||
|
return "Key not found \"\(codingKey)\": \(context.detailedDescription)"
|
||||||
|
case .dataCorrupted(let context):
|
||||||
|
return "Data corrupted: \(context.detailedDescription)"
|
||||||
|
@unknown default:
|
||||||
|
return "Not supported decoding error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import AnyCodable
|
||||||
|
|
||||||
public class JavaDecoder: Decoder {
|
public class JavaDecoder: Decoder {
|
||||||
|
|
||||||
public var codingPath = [CodingKey]()
|
public var codingPath: [CodingKey]
|
||||||
|
|
||||||
public var userInfo = [CodingUserInfoKey : Any]()
|
public var userInfo = [CodingUserInfoKey : Any]()
|
||||||
|
|
||||||
|
@ -19,14 +19,17 @@ public class JavaDecoder: Decoder {
|
||||||
fileprivate let package: String
|
fileprivate let package: String
|
||||||
fileprivate let missingFieldsStrategy: MissingFieldsStrategy
|
fileprivate let missingFieldsStrategy: MissingFieldsStrategy
|
||||||
|
|
||||||
public init(forPackage package: String, missingFieldsStrategy: MissingFieldsStrategy = .throw) {
|
public init(forPackage package: String,
|
||||||
|
missingFieldsStrategy: MissingFieldsStrategy = .throw,
|
||||||
|
codingPath: [CodingKey] = []) {
|
||||||
self.package = package
|
self.package = package
|
||||||
self.missingFieldsStrategy = missingFieldsStrategy
|
self.missingFieldsStrategy = missingFieldsStrategy
|
||||||
|
self.codingPath = codingPath
|
||||||
}
|
}
|
||||||
|
|
||||||
public func decode<T : Decodable>(_ type: T.Type, from javaObject: jobject) throws -> T {
|
public func decode<T : Decodable>(_ type: T.Type, from javaObject: jobject) throws -> T {
|
||||||
do {
|
do {
|
||||||
let value = try unbox(type: type, javaObject: javaObject)
|
let value = try unbox(type: type, javaObject: javaObject, codingPath: codingPath)
|
||||||
assert(self.storage.count == 0, "Missing decoding for \(self.storage.count) objects")
|
assert(self.storage.count == 0, "Missing decoding for \(self.storage.count) objects")
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -86,7 +89,7 @@ public class JavaDecoder: Decoder {
|
||||||
fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
||||||
typealias Key = K
|
typealias Key = K
|
||||||
|
|
||||||
var codingPath = [CodingKey]()
|
var codingPath: [CodingKey]
|
||||||
var allKeys = [K]()
|
var allKeys = [K]()
|
||||||
|
|
||||||
let decoder: JavaDecoder
|
let decoder: JavaDecoder
|
||||||
|
@ -98,6 +101,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
self.javaObject = jniStorage.javaObject
|
self.javaObject = jniStorage.javaObject
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
switch jniStorage.type {
|
switch jniStorage.type {
|
||||||
case let .object(className):
|
case let .object(className):
|
||||||
self.javaClass = className
|
self.javaClass = className
|
||||||
|
@ -111,7 +115,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeNil(forKey key: K) throws -> Bool {
|
func decodeNil(forKey key: K) throws -> Bool {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.decodeNil(forKey: \(key)")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nil not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decodeWithMissingStrategy<T>(defaultValue: T, block: () throws -> T) throws -> T {
|
private func decodeWithMissingStrategy<T>(defaultValue: T, block: () throws -> T) throws -> T {
|
||||||
|
@ -129,12 +133,102 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Decode JNI primitive fields
|
||||||
|
private func decodeBoolean(forKey key: String) throws -> Bool {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "Z")
|
||||||
|
return JNI.api.GetBooleanField(JNI.env, javaObject, fieldID) == JNI_TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeByte(forKey key: String) throws -> Int8 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "B")
|
||||||
|
return JNI.api.GetByteField(JNI.env, javaObject, fieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeShort(forKey key: String) throws -> Int16 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "S")
|
||||||
|
return JNI.api.GetShortField(JNI.env, javaObject, fieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeInteger(forKey key: String) throws -> Int32 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "I")
|
||||||
|
#if arch(x86_64) || arch(arm64)
|
||||||
|
return JNI.api.GetIntField(JNI.env, javaObject, fieldID)
|
||||||
|
#else
|
||||||
|
return Int32(JNI.api.GetIntField(JNI.env, javaObject, fieldID))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeLong(forKey key: String) throws -> Int64 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "J")
|
||||||
|
return JNI.api.GetLongField(JNI.env, javaObject, fieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeFloat(forKey key: String) throws -> Float {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "F")
|
||||||
|
return JNI.api.GetFloatField(JNI.env, javaObject, fieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decodeDouble(forKey key: String) throws -> Double {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "D")
|
||||||
|
return JNI.api.GetDoubleField(JNI.env, javaObject, fieldID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: KeyedDecodingContainerProtocol protocol
|
||||||
public func decode(_ type: Bool.Type, forKey key: K) throws -> Bool {
|
public func decode(_ type: Bool.Type, forKey key: K) throws -> Bool {
|
||||||
|
// TODO: WTF? Delete decodeWithMissingStrategy with default false -> CRASH
|
||||||
return try decodeWithMissingStrategy(defaultValue: false) {
|
return try decodeWithMissingStrategy(defaultValue: false) {
|
||||||
return try decodeJava(type, forKey: key) ?? false
|
return try self.decodeBoolean(forKey: key.stringValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int.Type, forKey key: K) throws -> Int {
|
||||||
|
return Int(try decodeInteger(forKey: key.stringValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 {
|
||||||
|
return try decodeByte(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 {
|
||||||
|
return try decodeShort(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 {
|
||||||
|
return try decodeInteger(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 {
|
||||||
|
return try decodeLong(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt.Type, forKey key: K) throws -> UInt {
|
||||||
|
return UInt(UInt32(bitPattern: try decodeInteger(forKey: key.stringValue)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 {
|
||||||
|
return UInt8(bitPattern: try decodeByte(forKey: key.stringValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 {
|
||||||
|
return UInt16(bitPattern: try decodeShort(forKey: key.stringValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 {
|
||||||
|
return UInt32(bitPattern: try decodeInteger(forKey: key.stringValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 {
|
||||||
|
return UInt64(bitPattern: try decodeLong(forKey: key.stringValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Float.Type, forKey key: K) throws -> Float {
|
||||||
|
return try decodeFloat(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Double.Type, forKey key: K) throws -> Double {
|
||||||
|
return try decodeDouble(forKey: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
// override all decodeIfPresent to prevent calling decodeNil(forKey:)
|
// override all decodeIfPresent to prevent calling decodeNil(forKey:)
|
||||||
public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
|
public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
|
||||||
return try self.decodeJava(type, forKey: key)
|
return try self.decodeJava(type, forKey: key)
|
||||||
|
@ -176,6 +270,14 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
return try self.decodeJava(type, forKey: key)
|
return try self.decodeJava(type, forKey: key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func decodeIfPresent(_ type: Float.Type, forKey key: K) throws -> Float? {
|
||||||
|
return try self.decodeJava(type, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decodeIfPresent(_ type: Double.Type, forKey key: K) throws -> Double? {
|
||||||
|
return try self.decodeJava(type, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
public func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
|
public func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
|
||||||
return try self.decodeJava(type, forKey: key)
|
return try self.decodeJava(type, forKey: key)
|
||||||
}
|
}
|
||||||
|
@ -190,7 +292,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
||||||
guard let result = try self.decodeJava(type, forKey: key) else {
|
guard let result = try self.decodeJava(type, forKey: key) else {
|
||||||
throw JavaCodingError.nilNotSupported("\(javaClass).\(key.stringValue)")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nil not supported: \(javaClass).\(key.stringValue)")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -206,7 +308,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
JNI.ExceptionReset()
|
JNI.ExceptionReset()
|
||||||
let errorMessage = "\(javaClass).\(key.stringValue): JavaDecoder uses reflection for AnyCodable, " +
|
let errorMessage = "\(javaClass).\(key.stringValue): JavaDecoder uses reflection for AnyCodable, " +
|
||||||
"probably \(key.stringValue) field not public"
|
"probably \(key.stringValue) field not public"
|
||||||
throw JavaCodingError.cantFindObject(errorMessage)
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: errorMessage)
|
||||||
}
|
}
|
||||||
let fieldClass = JNI.CallObjectMethod(field, methodID: FieldGetTypedMethod, args: [])!
|
let fieldClass = JNI.CallObjectMethod(field, methodID: FieldGetTypedMethod, args: [])!
|
||||||
let javaClassName = JNI.api.CallObjectMethodA(JNI.env, fieldClass, ClassGetNameMethod, nil)!
|
let javaClassName = JNI.api.CallObjectMethodA(JNI.env, fieldClass, ClassGetNameMethod, nil)!
|
||||||
|
@ -228,16 +330,16 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
defer {
|
defer {
|
||||||
JNI.DeleteLocalRef(object)
|
JNI.DeleteLocalRef(object)
|
||||||
}
|
}
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try self.decoder.unbox(type: type, javaObject: object, codingPath: codingPath + [key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.nestedContainer(keyedBy: \(type), forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested keyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.nestedUnkeyedContainer(forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested unkeyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder() throws -> Decoder {
|
func superDecoder() throws -> Decoder {
|
||||||
|
@ -246,14 +348,14 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder(forKey key: K) throws -> Decoder {
|
func superDecoder(forKey key: K) throws -> Decoder {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.superDecoder(forKey: \(key)")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Super decoder not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContainerProtocol {
|
fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContainerProtocol {
|
||||||
typealias Key = K
|
typealias Key = K
|
||||||
|
|
||||||
var codingPath = [CodingKey]()
|
var codingPath: [CodingKey]
|
||||||
var allKeys = [K]()
|
var allKeys = [K]()
|
||||||
|
|
||||||
private let decoder: JavaDecoder
|
private let decoder: JavaDecoder
|
||||||
|
@ -266,6 +368,7 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
self.javaObject = jniStorage.javaObject
|
self.javaObject = jniStorage.javaObject
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
|
|
||||||
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
||||||
let keyArray = JNI.api.CallObjectMethodA(JNI.env, keySet, SetToArrayMethod, nil)
|
let keyArray = JNI.api.CallObjectMethodA(JNI.env, keySet, SetToArrayMethod, nil)
|
||||||
|
@ -283,21 +386,24 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
||||||
var keySig: String?
|
var keySig: String?
|
||||||
|
|
||||||
for i in 0 ..< size {
|
for i in 0 ..< size {
|
||||||
|
guard let indexKey = K(intValue: Int(i)) else {
|
||||||
|
throw DecodingError.dataCorruptedError(forKey: K(stringValue: "init()")!, in: self, debugDescription: "Wrong array length")
|
||||||
|
}
|
||||||
guard let object = JNI.api.GetObjectArrayElement(JNI.env, keyArray, i) else {
|
guard let object = JNI.api.GetObjectArrayElement(JNI.env, keyArray, i) else {
|
||||||
throw JavaCodingError.wrongArrayLength
|
throw DecodingError.dataCorruptedError(forKey: K(stringValue: "init()")!, in: self, debugDescription: "Wrong array length")
|
||||||
}
|
}
|
||||||
if keySig == nil {
|
if keySig == nil {
|
||||||
keySig = self.decoder.getJavaClassname(from: object).sig
|
keySig = self.decoder.getJavaClassname(from: object).sig
|
||||||
}
|
}
|
||||||
if keySig == "Ljava/lang/String;" {
|
if keySig == "Ljava/lang/String;" {
|
||||||
let stringKey = try self.decoder.unbox(type: String.self, javaObject: object)
|
let stringKey = try self.decoder.unbox(type: String.self, javaObject: object, codingPath: codingPath + [indexKey])
|
||||||
if let key = K(stringValue: stringKey) {
|
if let key = K(stringValue: stringKey) {
|
||||||
javaKeys[stringKey] = object
|
javaKeys[stringKey] = object
|
||||||
allKeys.append(key)
|
allKeys.append(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let intKey = try self.decoder.unbox(type: Int.self, javaObject: object)
|
let intKey = try self.decoder.unbox(type: Int.self, javaObject: object, codingPath: codingPath + [indexKey])
|
||||||
if let key = K(intValue: intKey) {
|
if let key = K(intValue: intKey) {
|
||||||
javaKeys[intKey] = object
|
javaKeys[intKey] = object
|
||||||
allKeys.append(key)
|
allKeys.append(key)
|
||||||
|
@ -317,7 +423,7 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeNil(forKey key: K) throws -> Bool {
|
func decodeNil(forKey key: K) throws -> Bool {
|
||||||
throw JavaCodingError.notSupported("JavaHashMapContainer.decodeNil(forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nil not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
||||||
|
@ -331,20 +437,20 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
||||||
|
|
||||||
let javaKey = javaKeys[typeKey]
|
let javaKey = javaKeys[typeKey]
|
||||||
guard let object = JNI.CallObjectMethod(self.javaObject, methodID: HashMapGetMethod, args: [jvalue(l: javaKey)]) else {
|
guard let object = JNI.CallObjectMethod(self.javaObject, methodID: HashMapGetMethod, args: [jvalue(l: javaKey)]) else {
|
||||||
throw JavaCodingError.cantFindObject("HashMap[\(key.stringValue)]")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Can't find object")
|
||||||
}
|
}
|
||||||
defer {
|
defer {
|
||||||
JNI.DeleteLocalRef(object)
|
JNI.DeleteLocalRef(object)
|
||||||
}
|
}
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try self.decoder.unbox(type: type, javaObject: object, codingPath: codingPath + [key])
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||||
throw JavaCodingError.notSupported("JavaHashMapContainer.nestedContainer(keyedBy: \(type), forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested keyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
||||||
throw JavaCodingError.notSupported("JavaHashMapContainer.nestedUnkeyedContainer(forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested unkeyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder() throws -> Decoder {
|
func superDecoder() throws -> Decoder {
|
||||||
|
@ -352,13 +458,13 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder(forKey key: K) throws -> Decoder {
|
func superDecoder(forKey key: K) throws -> Decoder {
|
||||||
throw JavaCodingError.notSupported("JavaHashMapContainer.superDecoder(forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Super decoder not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
||||||
|
|
||||||
var codingPath = [CodingKey]()
|
var codingPath: [CodingKey]
|
||||||
|
|
||||||
var count: Int?
|
var count: Int?
|
||||||
|
|
||||||
|
@ -383,6 +489,7 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
self.javaObject = jniStorage.javaObject
|
self.javaObject = jniStorage.javaObject
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
self.count = Int(JNI.CallIntMethod(self.javaObject, methodID: HashMapSizeMethod)) * 2
|
self.count = Int(JNI.CallIntMethod(self.javaObject, methodID: HashMapSizeMethod)) * 2
|
||||||
|
|
||||||
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
||||||
|
@ -397,13 +504,14 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeNil() throws -> Bool {
|
func decodeNil() throws -> Bool {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.decodeNil")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nil not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||||
|
let codingKey = JavaKey(intValue: currentIndex)
|
||||||
if let javaCurrentKey = javaCurrentKey {
|
if let javaCurrentKey = javaCurrentKey {
|
||||||
guard let object = JNI.CallObjectMethod(self.javaObject, methodID: HashMapGetMethod, args: [jvalue(l: javaCurrentKey)]) else {
|
guard let object = JNI.CallObjectMethod(self.javaObject, methodID: HashMapGetMethod, args: [jvalue(l: javaCurrentKey)]) else {
|
||||||
throw JavaCodingError.cantFindObject("HashMap[]")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Can't find object")
|
||||||
}
|
}
|
||||||
currentIndex += 1
|
currentIndex += 1
|
||||||
defer {
|
defer {
|
||||||
|
@ -411,34 +519,34 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
||||||
JNI.DeleteLocalRef(self.javaCurrentKey)
|
JNI.DeleteLocalRef(self.javaCurrentKey)
|
||||||
self.javaCurrentKey = nil
|
self.javaCurrentKey = nil
|
||||||
}
|
}
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try self.decoder.unbox(type: type, javaObject: object, codingPath: codingPath + [codingKey])
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
guard let object = JNI.api.GetObjectArrayElement(JNI.env, javaKeys, jsize(self.currentIndex / 2)) else {
|
guard let object = JNI.api.GetObjectArrayElement(JNI.env, javaKeys, jsize(self.currentIndex / 2)) else {
|
||||||
throw JavaCodingError.wrongArrayLength
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Wrong array length")
|
||||||
}
|
}
|
||||||
self.javaCurrentKey = object
|
self.javaCurrentKey = object
|
||||||
currentIndex += 1
|
currentIndex += 1
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try self.decoder.unbox(type: type, javaObject: object, codingPath: codingPath + [codingKey])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedContainer(keyedBy: \(type))")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested keyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedUnkeyedContainer")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested unkeyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder() throws -> Decoder {
|
func superDecoder() throws -> Decoder {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.superDecoder")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Super decoder not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
||||||
|
|
||||||
var codingPath = [CodingKey]()
|
var codingPath: [CodingKey]
|
||||||
|
|
||||||
var count: Int?
|
var count: Int?
|
||||||
|
|
||||||
|
@ -459,6 +567,7 @@ fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
||||||
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
self.count = Int(JNI.CallIntMethod(jniStorage.javaObject, methodID: CollectionSizeMethod))
|
self.count = Int(JNI.CallIntMethod(jniStorage.javaObject, methodID: CollectionSizeMethod))
|
||||||
self.javaIterator = JNI.CallObjectMethod(jniStorage.javaObject, methodID: CollectionIteratorMethod)!
|
self.javaIterator = JNI.CallObjectMethod(jniStorage.javaObject, methodID: CollectionIteratorMethod)!
|
||||||
}
|
}
|
||||||
|
@ -468,35 +577,36 @@ fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeNil() throws -> Bool {
|
func decodeNil() throws -> Bool {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.decodeNil")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nil not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||||
|
let codingKey = JavaKey(intValue: currentIndex)
|
||||||
guard let object = JNI.CallObjectMethod(self.javaIterator, methodID: IteratorNextMethod) else {
|
guard let object = JNI.CallObjectMethod(self.javaIterator, methodID: IteratorNextMethod) else {
|
||||||
throw JavaCodingError.cantFindObject("Array out of range: \(self.currentIndex)")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Array out of range: \(self.currentIndex)")
|
||||||
}
|
}
|
||||||
defer {
|
defer {
|
||||||
JNI.DeleteLocalRef(object)
|
JNI.DeleteLocalRef(object)
|
||||||
}
|
}
|
||||||
currentIndex += 1
|
currentIndex += 1
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try self.decoder.unbox(type: type, javaObject: object, codingPath: codingPath + [codingKey])
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedContainer(keyedBy: \(type))")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested keyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedUnkeyedContainer")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested unkeyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder() throws -> Decoder {
|
func superDecoder() throws -> Decoder {
|
||||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.superDecoder")
|
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Super decoder not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
||||||
var codingPath: [CodingKey] = []
|
var codingPath: [CodingKey]
|
||||||
|
|
||||||
let decoder: JavaDecoder
|
let decoder: JavaDecoder
|
||||||
let jniStorage: JNIStorageObject
|
let jniStorage: JNIStorageObject
|
||||||
|
@ -507,6 +617,7 @@ fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
self.javaObject = jniStorage.javaObject
|
self.javaObject = jniStorage.javaObject
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
switch jniStorage.type {
|
switch jniStorage.type {
|
||||||
case let .object(className):
|
case let .object(className):
|
||||||
self.javaClass = className
|
self.javaClass = className
|
||||||
|
@ -519,23 +630,83 @@ fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
||||||
fatalError("Unsupported: JavaEnumDecodingContainer.decodeNil")
|
fatalError("Unsupported: JavaEnumDecodingContainer.decodeNil")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
func decode(_ type: Int.Type) throws -> Int {
|
||||||
let classname = self.decoder.getJavaClassname(forType: type)
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "I")
|
||||||
|
let value = JNI.api.GetIntField(JNI.env, javaObject, fieldID)
|
||||||
|
return Int(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: Int8.Type) throws -> Int8 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "B")
|
||||||
|
let value = JNI.api.GetByteField(JNI.env, javaObject, fieldID)
|
||||||
|
return Int8(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: Int16.Type) throws -> Int16 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "S")
|
||||||
|
let value = JNI.api.GetShortField(JNI.env, javaObject, fieldID)
|
||||||
|
return Int16(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: Int32.Type) throws -> Int32 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "I")
|
||||||
|
let value = JNI.api.GetIntField(JNI.env, javaObject, fieldID)
|
||||||
|
return Int32(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: Int64.Type) throws -> Int64 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "J")
|
||||||
|
let value = JNI.api.GetLongField(JNI.env, javaObject, fieldID)
|
||||||
|
return Int64(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: UInt.Type) throws -> UInt {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "I")
|
||||||
|
let value = JNI.api.GetIntField(JNI.env, javaObject, fieldID)
|
||||||
|
return UInt(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: UInt8.Type) throws -> UInt8 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "B")
|
||||||
|
let value = JNI.api.GetByteField(JNI.env, javaObject, fieldID)
|
||||||
|
return UInt8(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: UInt16.Type) throws -> UInt16 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "S")
|
||||||
|
let value = JNI.api.GetShortField(JNI.env, javaObject, fieldID)
|
||||||
|
return UInt16(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: UInt32.Type) throws -> UInt32 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "I")
|
||||||
|
let value = JNI.api.GetIntField(JNI.env, javaObject, fieldID)
|
||||||
|
return UInt32(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ type: UInt64.Type) throws -> UInt64 {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "J")
|
||||||
|
let value = JNI.api.GetLongField(JNI.env, javaObject, fieldID)
|
||||||
|
return UInt64(fromJavaPrimitive: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode<T>(_ valueType: T.Type) throws -> T where T : Decodable {
|
||||||
|
let classname = decoder.getJavaClassname(forType: valueType)
|
||||||
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "L\(classname);")
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: "rawValue", sig: "L\(classname);")
|
||||||
guard let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) else {
|
guard let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) else {
|
||||||
throw JavaCodingError.nilNotSupported("\(javaClass).rawValue")
|
throw JavaCodingError.notSupported(javaClass)
|
||||||
}
|
}
|
||||||
defer {
|
defer {
|
||||||
JNI.DeleteLocalRef(object)
|
JNI.DeleteLocalRef(object)
|
||||||
}
|
}
|
||||||
return try self.decoder.unbox(type: type, javaObject: object)
|
return try decoder.unbox(type: valueType, javaObject: object, codingPath: codingPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
||||||
typealias Key = K
|
typealias Key = K
|
||||||
|
|
||||||
var codingPath = [CodingKey]()
|
var codingPath: [CodingKey]
|
||||||
var allKeys = [K]()
|
var allKeys = [K]()
|
||||||
|
|
||||||
let decoder: JavaDecoder
|
let decoder: JavaDecoder
|
||||||
|
@ -545,6 +716,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
||||||
self.decoder = decoder
|
self.decoder = decoder
|
||||||
self.jniStorage = jniStorage
|
self.jniStorage = jniStorage
|
||||||
|
self.codingPath = jniStorage.codingPath
|
||||||
switch jniStorage.type {
|
switch jniStorage.type {
|
||||||
case let .anyCodable(codable):
|
case let .anyCodable(codable):
|
||||||
self.jniCodableType = codable
|
self.jniCodableType = codable
|
||||||
|
@ -558,7 +730,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeNil(forKey key: K) throws -> Bool {
|
func decodeNil(forKey key: K) throws -> Bool {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.decodeNil(forKey: \(key)")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nil not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
|
||||||
|
@ -584,7 +756,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if key.stringValue == "value" {
|
else if key.stringValue == "value" {
|
||||||
return try self.decoder.unbox(type: type, javaObject: self.jniStorage.javaObject)
|
return try self.decoder.unbox(type: type, javaObject: self.jniStorage.javaObject, codingPath: codingPath + [key])
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fatalError("Unknown key: \(key.stringValue)")
|
fatalError("Unknown key: \(key.stringValue)")
|
||||||
|
@ -592,7 +764,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||||
throw JavaCodingError.notSupported("JavaAnyCodableContainer.nestedContainer(keyedBy: \(type), forKey: \(key))")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested keyed container not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
|
||||||
|
@ -602,7 +774,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
case .dictionary:
|
case .dictionary:
|
||||||
return try JavaHashMapUnkeyedContainer(decoder: self.decoder, jniStorage: self.jniStorage)
|
return try JavaHashMapUnkeyedContainer(decoder: self.decoder, jniStorage: self.jniStorage)
|
||||||
default:
|
default:
|
||||||
fatalError("Unsupported type here")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested unkeyed container not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,16 +783,16 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
||||||
}
|
}
|
||||||
|
|
||||||
func superDecoder(forKey key: K) throws -> Decoder {
|
func superDecoder(forKey key: K) throws -> Decoder {
|
||||||
throw JavaCodingError.notSupported("JavaAnyCodableContainer.superDecoder(forKey: \(key)")
|
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Super decoder not supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension JavaDecoder {
|
extension JavaDecoder {
|
||||||
|
|
||||||
fileprivate func unbox<T: Decodable>(type: T.Type, javaObject: jobject) throws -> T {
|
fileprivate func unbox<T: Decodable>(type: T.Type, javaObject: jobject, codingPath: [CodingKey]) throws -> T {
|
||||||
let typeName = String(describing: type)
|
let typeName = String(describing: type)
|
||||||
if let decodableClosure = JavaCoderConfig.decodableClosures[typeName] {
|
if let decodableClosure = JavaCoderConfig.decodableClosures[typeName] {
|
||||||
return try decodableClosure(javaObject) as! T
|
return try decodableClosure(javaObject, codingPath) as! T
|
||||||
}
|
}
|
||||||
else if type == AnyCodable.self {
|
else if type == AnyCodable.self {
|
||||||
let cls = JNI.api.GetObjectClass(JNI.env, javaObject)
|
let cls = JNI.api.GetObjectClass(JNI.env, javaObject)
|
||||||
|
@ -642,7 +814,9 @@ extension JavaDecoder {
|
||||||
codableType = .object(className: className)
|
codableType = .object(className: className)
|
||||||
}
|
}
|
||||||
let obj = JNI.api.NewLocalRef(JNI.env, javaObject)!
|
let obj = JNI.api.NewLocalRef(JNI.env, javaObject)!
|
||||||
let storageObject = JNIStorageObject(type: .anyCodable(codable: codableType), javaObject: obj)
|
let storageObject = JNIStorageObject(type: .anyCodable(codable: codableType),
|
||||||
|
javaObject: obj,
|
||||||
|
codingPath: codingPath)
|
||||||
self.storage.append(storageObject)
|
self.storage.append(storageObject)
|
||||||
return try T.init(from: self)
|
return try T.init(from: self)
|
||||||
}
|
}
|
||||||
|
@ -652,13 +826,19 @@ extension JavaDecoder {
|
||||||
let obj = JNI.api.NewLocalRef(JNI.env, javaObject)!
|
let obj = JNI.api.NewLocalRef(JNI.env, javaObject)!
|
||||||
switch stringType {
|
switch stringType {
|
||||||
case _ where stringType.starts(with: "Array<"):
|
case _ where stringType.starts(with: "Array<"):
|
||||||
storageObject = JNIStorageObject(type: .array(className: ArrayListClassname), javaObject: obj)
|
storageObject = JNIStorageObject(type: .array(className: ArrayListClassname),
|
||||||
|
javaObject: obj,
|
||||||
|
codingPath: codingPath)
|
||||||
case _ where stringType.starts(with: "Set<"):
|
case _ where stringType.starts(with: "Set<"):
|
||||||
storageObject = JNIStorageObject(type: .array(className: HashSetClassname), javaObject: obj)
|
storageObject = JNIStorageObject(type: .array(className: HashSetClassname),
|
||||||
|
javaObject: obj,
|
||||||
|
codingPath: codingPath)
|
||||||
case _ where stringType.starts(with: "Dictionary<"):
|
case _ where stringType.starts(with: "Dictionary<"):
|
||||||
storageObject = JNIStorageObject(type: .dictionary, javaObject: obj)
|
storageObject = JNIStorageObject(type: .dictionary, javaObject: obj)
|
||||||
default:
|
default:
|
||||||
storageObject = JNIStorageObject(type: .object(className: "\(package)/\(type)"), javaObject: obj)
|
storageObject = JNIStorageObject(type: .object(className: "\(package)/\(type)"),
|
||||||
|
javaObject: obj,
|
||||||
|
codingPath: codingPath)
|
||||||
}
|
}
|
||||||
self.storage.append(storageObject)
|
self.storage.append(storageObject)
|
||||||
return try T.init(from: self)
|
return try T.init(from: self)
|
||||||
|
|
|
@ -15,12 +15,32 @@ public enum MissingFieldsStrategy: Error {
|
||||||
case ignore
|
case ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum JavaCodingError: Error {
|
internal struct JavaKey : CodingKey {
|
||||||
case notSupported(String)
|
|
||||||
case cantCreateObject(String)
|
public var stringValue: String
|
||||||
case cantFindObject(String)
|
public var intValue: Int?
|
||||||
case nilNotSupported(String)
|
|
||||||
case wrongArrayLength
|
public init(stringValue: String) {
|
||||||
|
self.stringValue = stringValue
|
||||||
|
self.intValue = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(intValue: Int) {
|
||||||
|
self.stringValue = "\(intValue)"
|
||||||
|
self.intValue = intValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(stringValue: String, intValue: Int?) {
|
||||||
|
self.stringValue = stringValue
|
||||||
|
self.intValue = intValue
|
||||||
|
}
|
||||||
|
|
||||||
|
init(index: Int) {
|
||||||
|
self.stringValue = "Index \(index)"
|
||||||
|
self.intValue = index
|
||||||
|
}
|
||||||
|
|
||||||
|
static let `super` = JavaKey(stringValue: "super")
|
||||||
}
|
}
|
||||||
|
|
||||||
indirect enum JNIStorageType {
|
indirect enum JNIStorageType {
|
||||||
|
@ -45,6 +65,7 @@ indirect enum JNIStorageType {
|
||||||
|
|
||||||
class JNIStorageObject {
|
class JNIStorageObject {
|
||||||
let type: JNIStorageType
|
let type: JNIStorageType
|
||||||
|
let codingPath: [CodingKey]
|
||||||
var javaObject: jobject! {
|
var javaObject: jobject! {
|
||||||
didSet {
|
didSet {
|
||||||
if let value = oldValue {
|
if let value = oldValue {
|
||||||
|
@ -53,13 +74,15 @@ class JNIStorageObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(type: JNIStorageType, javaObject: jobject) {
|
init(type: JNIStorageType, javaObject: jobject, codingPath: [CodingKey] = []) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.javaObject = javaObject
|
self.javaObject = javaObject
|
||||||
|
self.codingPath = codingPath
|
||||||
}
|
}
|
||||||
|
|
||||||
init(type: JNIStorageType) {
|
init(type: JNIStorageType, codingPath: [CodingKey] = []) {
|
||||||
self.type = type
|
self.type = type
|
||||||
|
self.codingPath = codingPath
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -88,8 +111,10 @@ open class JavaEncoder: Encoder {
|
||||||
|
|
||||||
// MARK: - Constructing a JSON Encoder
|
// MARK: - Constructing a JSON Encoder
|
||||||
/// Initializes `self` with default strategies.
|
/// Initializes `self` with default strategies.
|
||||||
public init(forPackage: String, missingFieldsStrategy: MissingFieldsStrategy = .throw) {
|
public init(forPackage: String,
|
||||||
self.codingPath = [CodingKey]()
|
missingFieldsStrategy: MissingFieldsStrategy = .throw,
|
||||||
|
codingPath: [CodingKey] = []) {
|
||||||
|
self.codingPath = codingPath
|
||||||
self.package = forPackage
|
self.package = forPackage
|
||||||
self.javaObjects = [JNIStorageObject]()
|
self.javaObjects = [JNIStorageObject]()
|
||||||
self.missingFieldsStrategy = missingFieldsStrategy
|
self.missingFieldsStrategy = missingFieldsStrategy
|
||||||
|
@ -104,7 +129,7 @@ open class JavaEncoder: Encoder {
|
||||||
/// - throws: An error if any value throws an error during encoding.
|
/// - throws: An error if any value throws an error during encoding.
|
||||||
open func encode<T : Encodable>(_ value: T) throws -> jobject {
|
open func encode<T : Encodable>(_ value: T) throws -> jobject {
|
||||||
do {
|
do {
|
||||||
let storage = try self.box(value)
|
let storage = try self.box(value, codingPath: codingPath)
|
||||||
assert(self.javaObjects.count == 0, "Missing encoding for \(self.javaObjects.count) objects")
|
assert(self.javaObjects.count == 0, "Missing encoding for \(self.javaObjects.count) objects")
|
||||||
return JNI.api.NewLocalRef(JNI.env, storage.javaObject)!
|
return JNI.api.NewLocalRef(JNI.env, storage.javaObject)!
|
||||||
}
|
}
|
||||||
|
@ -192,14 +217,190 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedEncodingContainerPro
|
||||||
return jniStorage.javaObject
|
return jniStorage.javaObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Encode JNI primitive fields
|
||||||
|
func encodeBoolean(_ value: jboolean, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "Z")
|
||||||
|
JNI.api.SetBooleanField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeByte(_ value: jbyte, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "B")
|
||||||
|
JNI.api.SetByteField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeShort(_ value: jshort, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "S")
|
||||||
|
JNI.api.SetShortField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInteger(_ value: jint, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "I")
|
||||||
|
JNI.api.SetIntField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeLong(_ value: jlong, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "J")
|
||||||
|
JNI.api.SetLongField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat(_ value: jfloat, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "F")
|
||||||
|
JNI.api.SetFloatField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeDouble(_ value: jdouble, key: String) throws {
|
||||||
|
let fieldID = try JNI.getJavaField(forClass: javaClass, field: key, sig: "D")
|
||||||
|
JNI.api.SetDoubleField(JNI.env, javaObject, fieldID, value)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||||
public func encodeNil(forKey key: Key) throws {
|
public func encodeNil(forKey key: Key) throws {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.encodeNil(forKey: \(key)")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Bool, forKey key: K) throws {
|
||||||
|
try encodeBoolean(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Double, forKey key: K) throws {
|
||||||
|
try encodeDouble(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Float, forKey key: K) throws {
|
||||||
|
try encodeFloat(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Int, forKey key: K) throws {
|
||||||
|
try encodeInteger(try value.javaPrimitive(codingPath: codingPath + [key]), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Int8, forKey key: K) throws {
|
||||||
|
try encodeByte(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Int16, forKey key: K) throws {
|
||||||
|
try encodeShort(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Int32, forKey key: K) throws {
|
||||||
|
try encodeInteger(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: Int64, forKey key: K) throws {
|
||||||
|
try encodeLong(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: UInt, forKey key: K) throws {
|
||||||
|
try encodeInteger(try value.javaPrimitive(codingPath: codingPath + [key]), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: UInt8, forKey key: K) throws {
|
||||||
|
try encodeByte(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: UInt16, forKey key: K) throws {
|
||||||
|
try encodeShort(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: UInt32, forKey key: K) throws {
|
||||||
|
try encodeInteger(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ value: UInt64, forKey key: K) throws {
|
||||||
|
try encodeLong(try value.javaPrimitive(), key: key.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Bool?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: String?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Double?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Float?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Int?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Int8?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Int16?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Int32?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: Int64?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: UInt?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: UInt8?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: UInt16?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: UInt32?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIfPresent(_ value: UInt64?, forKey key: K) throws {
|
||||||
|
if let value = value {
|
||||||
|
try encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||||
|
try self.encodeObject(value, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encodeObject<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||||
do {
|
do {
|
||||||
let object = try self.encoder.box(value)
|
let object = try self.encoder.box(value, codingPath: codingPath + [key])
|
||||||
let filed = try JNI.getJavaField(forClass: self.javaClass, field: key.stringValue, sig: object.type.sig)
|
let filed = try JNI.getJavaField(forClass: self.javaClass, field: key.stringValue, sig: object.type.sig)
|
||||||
JNI.api.SetObjectField(JNI.env, self.javaObject, filed, object.javaObject)
|
JNI.api.SetObjectField(JNI.env, self.javaObject, filed, object.javaObject)
|
||||||
}
|
}
|
||||||
|
@ -260,19 +461,19 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey> : KeyedEncodingContai
|
||||||
|
|
||||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||||
public func encodeNil(forKey key: Key) throws {
|
public func encodeNil(forKey key: Key) throws {
|
||||||
throw JavaCodingError.notSupported("JavaHashMapContainer.encodeNil(forKey: \(key))")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||||
let keyStorage: JNIStorageObject
|
let keyStorage: JNIStorageObject
|
||||||
if let intValue = key.intValue {
|
if let intValue = key.intValue {
|
||||||
keyStorage = try self.encoder.box(intValue)
|
keyStorage = try self.encoder.box(intValue, codingPath: codingPath + [key])
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
keyStorage = try self.encoder.box(key.stringValue)
|
keyStorage = try self.encoder.box(key.stringValue, codingPath: codingPath + [key])
|
||||||
}
|
}
|
||||||
|
|
||||||
let valueStorage = try self.encoder.box(value)
|
let valueStorage = try self.encoder.box(value, codingPath: codingPath + [key])
|
||||||
let result = JNI.CallObjectMethod(javaObject, methodID: HashMapPutMethod, args: [jvalue(l: keyStorage.javaObject), jvalue(l: valueStorage.javaObject)])
|
let result = JNI.CallObjectMethod(javaObject, methodID: HashMapPutMethod, args: [jvalue(l: keyStorage.javaObject), jvalue(l: valueStorage.javaObject)])
|
||||||
assert(result == nil, "Rewrite for key \(key.stringValue)")
|
assert(result == nil, "Rewrite for key \(key.stringValue)")
|
||||||
}
|
}
|
||||||
|
@ -323,11 +524,12 @@ fileprivate class JavaHashMapUnkeyedContainer : UnkeyedEncodingContainer {
|
||||||
|
|
||||||
// MARK: - UnkeyedEncodingContainer Methods
|
// MARK: - UnkeyedEncodingContainer Methods
|
||||||
public func encodeNil() throws {
|
public func encodeNil() throws {
|
||||||
throw JavaCodingError.notSupported("JavaArrayContainer.encodeNil")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T) throws {
|
public func encode<T : Encodable>(_ value: T) throws {
|
||||||
let javaValue = try self.encoder.box(value)
|
let indexKey = JavaKey(index: count)
|
||||||
|
let javaValue = try self.encoder.box(value, codingPath: codingPath + [indexKey])
|
||||||
if let javaKey = self.javaKey {
|
if let javaKey = self.javaKey {
|
||||||
let result = JNI.CallObjectMethod(javaObject, methodID: HashMapPutMethod, args: [jvalue(l: javaKey.javaObject), jvalue(l: javaValue.javaObject)])
|
let result = JNI.CallObjectMethod(javaObject, methodID: HashMapPutMethod, args: [jvalue(l: javaKey.javaObject), jvalue(l: javaValue.javaObject)])
|
||||||
assert(result == nil, "Rewrite for key")
|
assert(result == nil, "Rewrite for key")
|
||||||
|
@ -379,11 +581,12 @@ fileprivate class JavaArrayContainer : UnkeyedEncodingContainer {
|
||||||
|
|
||||||
// MARK: - UnkeyedEncodingContainer Methods
|
// MARK: - UnkeyedEncodingContainer Methods
|
||||||
public func encodeNil() throws {
|
public func encodeNil() throws {
|
||||||
throw JavaCodingError.notSupported("JavaArrayContainer.encodeNil")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T) throws {
|
public func encode<T : Encodable>(_ value: T) throws {
|
||||||
let storeObject = try self.encoder.box(value)
|
let indexKey = JavaKey(index: count)
|
||||||
|
let storeObject = try self.encoder.box(value, codingPath: codingPath + [indexKey])
|
||||||
let rewrite = JNI.CallBooleanMethod(self.javaObject, methodID: CollectionAddMethod, args: [jvalue(l: storeObject.javaObject)])
|
let rewrite = JNI.CallBooleanMethod(self.javaObject, methodID: CollectionAddMethod, args: [jvalue(l: storeObject.javaObject)])
|
||||||
assert(rewrite == JNI.TRUE, "ArrayList should always return true from add()")
|
assert(rewrite == JNI.TRUE, "ArrayList should always return true from add()")
|
||||||
count += 1
|
count += 1
|
||||||
|
@ -418,11 +621,76 @@ class JavaEnumValueEncodingContainer: SingleValueEncodingContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encodeNil() throws {
|
public func encodeNil() throws {
|
||||||
throw JavaCodingError.notSupported("JavaSingleValueEncodingContainer.encodeNil")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T) throws {
|
public func encode(_ value: Int8) throws {
|
||||||
let rawValue = try self.encoder.box(value)
|
try encode(jvalue(b: value.javaPrimitive()), sig: "B")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: Int16) throws {
|
||||||
|
try encode(jvalue(s: value.javaPrimitive()), sig: "S")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: Int32) throws {
|
||||||
|
try encode(jvalue(i: value.javaPrimitive()), sig: "I")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: Int64) throws {
|
||||||
|
try encode(jvalue(j: value.javaPrimitive()), sig: "J")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: Int) throws {
|
||||||
|
try encode(jvalue(i: value.javaPrimitive(codingPath: codingPath)), sig: "I")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: UInt8) throws {
|
||||||
|
try encode(jvalue(b: value.javaPrimitive()), sig: "B")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: UInt16) throws {
|
||||||
|
try encode(jvalue(s: value.javaPrimitive()), sig: "S")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: UInt32) throws {
|
||||||
|
try encode(jvalue(i: value.javaPrimitive()), sig: "I")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: UInt64) throws {
|
||||||
|
try encode(jvalue(j: value.javaPrimitive()), sig: "J")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: UInt) throws {
|
||||||
|
try encode(jvalue(i: value.javaPrimitive(codingPath: codingPath)), sig: "I")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ value: jvalue, sig: String) throws {
|
||||||
|
let clazz = try JNI.getJavaClass(javaClass)
|
||||||
|
// If jniStorage.javaObject == nil its enum, else optionSet
|
||||||
|
if jniStorage.javaObject == nil {
|
||||||
|
let valueOfMethodID = try JNI.getStaticJavaMethod(forClass: javaClass, method: "valueOf", sig: "(\(sig))L\(javaClass);")
|
||||||
|
guard let javaObject = JNI.CallStaticObjectMethod(clazz, methodID: valueOfMethodID, args: [value]) else {
|
||||||
|
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
|
||||||
|
debugDescription: "Nil not supported: \\(javaClass).valueOf()"))
|
||||||
|
}
|
||||||
|
jniStorage.javaObject = javaObject
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let filed = try JNI.getJavaField(forClass: self.javaClass, field: "rawValue", sig: sig)
|
||||||
|
let setterFunc = setterFuncMap[sig]
|
||||||
|
setterFunc?(self.jniStorage.javaObject, filed, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let setterFuncMap: [String: (jobject, jfieldID, jvalue) -> Void] = [
|
||||||
|
"B": { _ = JNI.api.SetByteField(JNI.env, $0, $1, $2.b) },
|
||||||
|
"S": { _ = JNI.api.SetShortField(JNI.env, $0, $1, $2.s) },
|
||||||
|
"I": { _ = JNI.api.SetIntField(JNI.env, $0, $1, $2.i) },
|
||||||
|
"J": { _ = JNI.api.SetLongField(JNI.env, $0, $1, $2.j) }
|
||||||
|
]
|
||||||
|
|
||||||
|
public func encode<T : Encodable>(_ valueType: T) throws {
|
||||||
|
let rawValue = try encoder.box(valueType, codingPath: codingPath)
|
||||||
let clazz = try JNI.getJavaClass(javaClass)
|
let clazz = try JNI.getJavaClass(javaClass)
|
||||||
// If jniStorage.javaObject == nil its enum, else optionSet
|
// If jniStorage.javaObject == nil its enum, else optionSet
|
||||||
if jniStorage.javaObject == nil {
|
if jniStorage.javaObject == nil {
|
||||||
|
@ -432,13 +700,13 @@ class JavaEnumValueEncodingContainer: SingleValueEncodingContainer {
|
||||||
JNI.RemoveFatalErrorMessage()
|
JNI.RemoveFatalErrorMessage()
|
||||||
}
|
}
|
||||||
guard let javaObject = JNI.CallStaticObjectMethod(clazz, methodID: valueOfMethodID, args: [jvalue(l: rawValue.javaObject)]) else {
|
guard let javaObject = JNI.CallStaticObjectMethod(clazz, methodID: valueOfMethodID, args: [jvalue(l: rawValue.javaObject)]) else {
|
||||||
throw JavaCodingError.nilNotSupported("\(javaClass).valueOf()")
|
throw JavaCodingError.cantCreateObject(javaClass)
|
||||||
}
|
}
|
||||||
jniStorage.javaObject = javaObject
|
jniStorage.javaObject = javaObject
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let filed = try JNI.getJavaField(forClass: self.javaClass, field: "rawValue", sig: rawValue.type.sig)
|
let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Unsupported: type \(type(of: valueType))")
|
||||||
JNI.api.SetObjectField(JNI.env, self.jniStorage.javaObject, filed, rawValue.javaObject)
|
throw EncodingError.invalidValue(valueType, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,7 +738,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
||||||
|
|
||||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||||
public func encodeNil(forKey key: Key) throws {
|
public func encodeNil(forKey key: Key) throws {
|
||||||
throw JavaCodingError.notSupported("JavaObjectContainer.encodeNil(forKey: \(key)")
|
throw EncodingError.invalidValue(NSNotFound, EncodingError.Context(codingPath: codingPath, debugDescription: "Nil not supported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
public func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||||
|
@ -479,7 +747,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let jniObject = try self.encoder.box(value)
|
let jniObject = try self.encoder.box(value, codingPath: codingPath + [key])
|
||||||
self.jniStorage.javaObject = JNI.api.NewLocalRef(JNI.env, jniObject.javaObject)
|
self.jniStorage.javaObject = JNI.api.NewLocalRef(JNI.env, jniObject.javaObject)
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
@ -523,18 +791,18 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
||||||
|
|
||||||
extension JavaEncoder {
|
extension JavaEncoder {
|
||||||
|
|
||||||
fileprivate func box<T: Encodable>(_ value: T) throws -> JNIStorageObject {
|
fileprivate func box<T: Encodable>(_ value: T, codingPath: [CodingKey]) throws -> JNIStorageObject {
|
||||||
let storage: JNIStorageObject
|
let storage: JNIStorageObject
|
||||||
let typeName = String(describing: type(of: value))
|
let typeName = String(describing: type(of: value))
|
||||||
if let encodableClosure = JavaCoderConfig.encodableClosures[typeName] {
|
if let encodableClosure = JavaCoderConfig.encodableClosures[typeName] {
|
||||||
let javaObject = try encodableClosure(value)
|
let javaObject = try encodableClosure(value, codingPath)
|
||||||
storage = JNIStorageObject(type: .object(className: JavaCoderConfig.codableClassNames[typeName]!), javaObject: javaObject)
|
storage = JNIStorageObject(type: .object(className: JavaCoderConfig.codableClassNames[typeName]!), javaObject: javaObject)
|
||||||
}
|
}
|
||||||
else if T.self == AnyCodable.self {
|
else if T.self == AnyCodable.self {
|
||||||
let anyCodableValue = value as! AnyCodable
|
let anyCodableValue = value as! AnyCodable
|
||||||
if let javaClassname = JavaCoderConfig.codableClassNames[anyCodableValue.typeName] {
|
if let javaClassname = JavaCoderConfig.codableClassNames[anyCodableValue.typeName] {
|
||||||
let encodableClosure = JavaCoderConfig.encodableClosures[anyCodableValue.typeName]!
|
let encodableClosure = JavaCoderConfig.encodableClosures[anyCodableValue.typeName]!
|
||||||
let javaObject = try encodableClosure(anyCodableValue.value)
|
let javaObject = try encodableClosure(anyCodableValue.value, codingPath)
|
||||||
storage = JNIStorageObject(type: .object(className: javaClassname), javaObject: javaObject)
|
storage = JNIStorageObject(type: .object(className: javaClassname), javaObject: javaObject)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
//
|
||||||
|
// Created by Andriy Druk on 24.01.2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import java_swift
|
||||||
|
import CJavaVM
|
||||||
|
|
||||||
|
extension Bool {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jboolean) {
|
||||||
|
self.init(javaPrimitive == JNI_TRUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jboolean {
|
||||||
|
return jboolean(self ? JNI_TRUE : JNI_FALSE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jint) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive(codingPath: [CodingKey] = []) throws -> jint {
|
||||||
|
if self < Int(Int32.min) || self > Int(Int32.max) {
|
||||||
|
let errorDescription = "Not enough bits to represent Int"
|
||||||
|
let context = EncodingError.Context(codingPath: codingPath, debugDescription: errorDescription)
|
||||||
|
throw EncodingError.invalidValue(self, context)
|
||||||
|
}
|
||||||
|
return jint(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int8 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jbyte) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jbyte {
|
||||||
|
return jbyte(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int16 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jshort) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jshort {
|
||||||
|
return jshort(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int32 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jint) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jint {
|
||||||
|
return jint(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int64 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jlong) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jlong {
|
||||||
|
return jlong(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UInt {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jint) {
|
||||||
|
#if arch(x86_64) || arch(arm64)
|
||||||
|
self.init(UInt32(bitPattern: javaPrimitive))
|
||||||
|
#else
|
||||||
|
self.init(bitPattern: javaPrimitive)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive(codingPath: [CodingKey] = []) throws -> jint {
|
||||||
|
if self < UInt(UInt32.min) || self > UInt(UInt32.max) {
|
||||||
|
let errorDescription = "Not enough bits to represent UInt"
|
||||||
|
let context = EncodingError.Context(codingPath: codingPath, debugDescription: errorDescription)
|
||||||
|
throw EncodingError.invalidValue(self, context)
|
||||||
|
}
|
||||||
|
#if arch(x86_64) || arch(arm64)
|
||||||
|
return jint(bitPattern: UInt32(self))
|
||||||
|
#else
|
||||||
|
return jint(bitPattern: self)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UInt8 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jbyte) {
|
||||||
|
self.init(bitPattern: javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jbyte {
|
||||||
|
return jbyte(bitPattern: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UInt16 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jshort) {
|
||||||
|
self.init(bitPattern: javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jshort {
|
||||||
|
return jshort(bitPattern: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UInt32 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jint) {
|
||||||
|
#if arch(x86_64) || arch(arm64)
|
||||||
|
self.init(bitPattern: javaPrimitive)
|
||||||
|
#else
|
||||||
|
self.init(UInt(bitPattern: javaPrimitive))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jint {
|
||||||
|
#if arch(x86_64) || arch(arm64)
|
||||||
|
return jint(bitPattern: self)
|
||||||
|
#else
|
||||||
|
return jint(bitPattern: UInt(self))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UInt64 {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jlong) {
|
||||||
|
self.init(bitPattern: javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jlong {
|
||||||
|
return jlong(bitPattern: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Float {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jfloat) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jfloat {
|
||||||
|
return jfloat(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Double {
|
||||||
|
|
||||||
|
public init(fromJavaPrimitive javaPrimitive: jdouble) {
|
||||||
|
self.init(javaPrimitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func javaPrimitive() throws -> jdouble {
|
||||||
|
return jdouble(self)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue