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)
|
||||
}
|
||||
|
||||
enum JNIError: Error {
|
||||
enum JNIError: Error, LocalizedError {
|
||||
|
||||
case classNotFoundException(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
|
||||
|
@ -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? {
|
||||
return checkArgument(args: args, { argsPtr in
|
||||
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...) {
|
||||
checkArgumentAndWrap(args: args, { argsPtr in
|
||||
api.CallVoidMethodA(env, object, methodID, argsPtr)
|
||||
|
|
|
@ -6,8 +6,8 @@ import Foundation
|
|||
import java_swift
|
||||
import CJavaVM
|
||||
|
||||
public typealias JavaEncodableClosure = (Any) throws -> jobject
|
||||
public typealias JavaDecodableClosure = (jobject) throws -> Decodable
|
||||
public typealias JavaEncodableClosure = (Any, [CodingKey]) throws -> jobject
|
||||
public typealias JavaDecodableClosure = (jobject, [CodingKey]) throws -> Decodable
|
||||
|
||||
public struct JavaCoderConfig {
|
||||
|
||||
|
@ -43,153 +43,170 @@ public struct JavaCoderConfig {
|
|||
|
||||
public static func RegisterBasicJavaTypes() {
|
||||
|
||||
RegisterType(type: Int.self, javaClassname: IntegerClassname, encodableClosure: {
|
||||
// jint for macOS and Android different, that's why we make cast to jint() here
|
||||
let args = [jvalue(i: jint($0 as! Int))]
|
||||
RegisterType(type: Int.self, javaClassname: IntegerClassname, encodableClosure: { any, codingPath in
|
||||
let value = any as! Int
|
||||
let primitive = try value.javaPrimitive(codingPath: codingPath)
|
||||
let args = [jvalue(i: primitive)]
|
||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return Int(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
||||
}, decodableClosure: { value, _ in
|
||||
Int(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Int8.self, javaClassname: ByteClassname, encodableClosure: {
|
||||
let args = [jvalue(b: $0 as! Int8)]
|
||||
RegisterType(type: Int8.self, javaClassname: ByteClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Int8
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(b: primitive)]
|
||||
return JNI.NewObject(ByteClass, methodID: ByteConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return JNI.CallByteMethod($0, methodID: NumberByteValueMethod)
|
||||
}, decodableClosure: { value, _ in
|
||||
Int8(fromJavaPrimitive: JNI.CallByteMethod(value, methodID: NumberByteValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Int16.self, javaClassname: ShortClassname, encodableClosure: {
|
||||
let args = [jvalue(s: $0 as! Int16)]
|
||||
RegisterType(type: Int16.self, javaClassname: ShortClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Int16
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(s: primitive)]
|
||||
return JNI.NewObject(ShortClass, methodID: ShortConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return JNI.CallShortMethod($0, methodID: NumberShortValueMethod)
|
||||
}, decodableClosure: { value, _ in
|
||||
Int16(fromJavaPrimitive: JNI.CallShortMethod(value, methodID: NumberShortValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Int32.self, javaClassname: IntegerClassname, encodableClosure: {
|
||||
let args = [jvalue(i: jint($0 as! Int32))]
|
||||
RegisterType(type: Int32.self, javaClassname: IntegerClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Int32
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(i: primitive)]
|
||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return Int32(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
||||
}, decodableClosure: { value, _ in
|
||||
Int32(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Int64.self, javaClassname: LongClassname, encodableClosure: {
|
||||
let args = [jvalue(j: $0 as! Int64)]
|
||||
RegisterType(type: Int64.self, javaClassname: LongClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Int64
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(j: primitive)]
|
||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return JNI.CallLongMethod($0, methodID: NumberLongValueMethod)
|
||||
}, decodableClosure: { value, _ in
|
||||
Int64(fromJavaPrimitive: JNI.CallLongMethod(value, methodID: NumberLongValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: UInt.self, javaClassname: LongClassname, encodableClosure: {
|
||||
let args = [jvalue(j: Int64($0 as! UInt))]
|
||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return UInt(JNI.CallLongMethod($0, methodID: NumberLongValueMethod))
|
||||
RegisterType(type: UInt.self, javaClassname: IntegerClassname, encodableClosure: { any, codingPath in
|
||||
let value = any as! UInt
|
||||
let primitive = try value.javaPrimitive(codingPath: codingPath)
|
||||
let args = [jvalue(i: primitive)]
|
||||
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: {
|
||||
let args = [jvalue(s: Int16($0 as! UInt8))]
|
||||
RegisterType(type: UInt8.self, javaClassname: ByteClassname, encodableClosure: { any, _ in
|
||||
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)!
|
||||
}, decodableClosure: {
|
||||
return UInt8(JNI.CallShortMethod($0, methodID: NumberShortValueMethod))
|
||||
}, decodableClosure: { value, _ in
|
||||
UInt16(fromJavaPrimitive: JNI.CallShortMethod(value, methodID: NumberShortValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: UInt16.self, javaClassname: IntegerClassname, encodableClosure: {
|
||||
let args = [jvalue(i: jint($0 as! UInt16))]
|
||||
RegisterType(type: UInt32.self, javaClassname: IntegerClassname, encodableClosure: { any, _ in
|
||||
let value = any as! UInt32
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(i: primitive)]
|
||||
return JNI.NewObject(IntegerClass, methodID: IntegerConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return UInt16(JNI.CallIntMethod($0, methodID: NumberIntValueMethod))
|
||||
}, decodableClosure: { value, _ in
|
||||
UInt32(fromJavaPrimitive: JNI.CallIntMethod(value, methodID: NumberIntValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: UInt32.self, javaClassname: LongClassname, encodableClosure: {
|
||||
let args = [jvalue(j: Int64($0 as! UInt32))]
|
||||
RegisterType(type: UInt64.self, javaClassname: LongClassname, encodableClosure: { any, _ in
|
||||
let value = any as! UInt64
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(j: primitive)]
|
||||
return JNI.NewObject(LongClass, methodID: LongConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return UInt32(JNI.CallLongMethod($0, methodID: NumberLongValueMethod))
|
||||
}, decodableClosure: { value, _ in
|
||||
UInt64(fromJavaPrimitive: JNI.CallLongMethod(value, methodID: NumberLongValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: UInt64.self, javaClassname: BigIntegerClassname, encodableClosure: {
|
||||
var locals = [jobject]()
|
||||
let args = [jvalue(l: String($0 as! UInt64).localJavaObject(&locals))]
|
||||
return JNI.check(JNI.NewObject(BigIntegerClass, methodID: BigIntegerConstructor, args: args)!, &locals)
|
||||
}, 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)]
|
||||
RegisterType(type: Float.self, javaClassname: FloatClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Float
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(f: primitive)]
|
||||
return JNI.NewObject(FloatClass, methodID: FloatConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return JNI.api.CallFloatMethodA(JNI.env, $0, NumberFloatValueMethod, nil)
|
||||
}, decodableClosure: { value, _ in
|
||||
Float(fromJavaPrimitive: JNI.CallFloatMethod(value, methodID: NumberFloatValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Double.self, javaClassname: DoubleClassname, encodableClosure: {
|
||||
let args = [jvalue(d: $0 as! Double)]
|
||||
RegisterType(type: Double.self, javaClassname: DoubleClassname, encodableClosure: { any, _ in
|
||||
let value = any as! Double
|
||||
let primitive = try value.javaPrimitive()
|
||||
let args = [jvalue(d: primitive)]
|
||||
return JNI.NewObject(DoubleClass, methodID: DoubleConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return JNI.api.CallDoubleMethodA(JNI.env, $0, NumberDoubleValueMethod, nil)
|
||||
}, decodableClosure: { value, _ in
|
||||
Double(fromJavaPrimitive: JNI.CallDoubleMethod(value, methodID: NumberDoubleValueMethod))
|
||||
})
|
||||
|
||||
RegisterType(type: Bool.self, javaClassname: BooleanClassname, encodableClosure: {
|
||||
let args = [jvalue(z: $0 as! Bool ? JNI.TRUE : JNI.FALSE)]
|
||||
RegisterType(type: Bool.self, javaClassname: BooleanClassname, encodableClosure: { value, _ in
|
||||
let args = [jvalue(z: value as! Bool ? JNI.TRUE : JNI.FALSE)]
|
||||
return JNI.NewObject(BooleanClass, methodID: BooleanConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
return (JNI.CallBooleanMethod($0, methodID: NumberBooleanValueMethod) == JNI.TRUE)
|
||||
}, decodableClosure: { value, _ in
|
||||
JNI.CallBooleanMethod(value, methodID: NumberBooleanValueMethod) == JNI.TRUE
|
||||
})
|
||||
|
||||
RegisterType(type: String.self, javaClassname: StringClassname, encodableClosure: {
|
||||
let valueString = $0 as! String
|
||||
RegisterType(type: String.self, javaClassname: StringClassname, encodableClosure: { value, _ in
|
||||
let valueString = value as! String
|
||||
var locals = [jobject]()
|
||||
// Locals ignored because JNIStorageObject take ownership of LocalReference
|
||||
return valueString.localJavaObject(&locals)!
|
||||
}, decodableClosure: {
|
||||
return String(javaObject: $0)
|
||||
}, decodableClosure: { value, _ in
|
||||
String(javaObject: value)
|
||||
})
|
||||
|
||||
RegisterType(type: Date.self, javaClassname: DateClassname, encodableClosure: {
|
||||
let valueDate = $0 as! Date
|
||||
RegisterType(type: Date.self, javaClassname: DateClassname, encodableClosure: { value, _ in
|
||||
let valueDate = value as! Date
|
||||
let args = [jvalue(j: jlong(valueDate.timeIntervalSince1970 * 1000))]
|
||||
return JNI.NewObject(DateClass, methodID: DateConstructor, args: args)!
|
||||
}, decodableClosure: {
|
||||
let timeInterval = JNI.api.CallLongMethodA(JNI.env, $0, DateGetTimeMethod, nil)
|
||||
}, decodableClosure: { value, _ in
|
||||
let timeInterval = JNI.api.CallLongMethodA(JNI.env, value, DateGetTimeMethod, nil)
|
||||
// Java save TimeInterval in UInt64 milliseconds
|
||||
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]()
|
||||
let javaString = ($0 as! URL).absoluteString.localJavaObject(&locals)
|
||||
let javaString = (value as! URL).absoluteString.localJavaObject(&locals)
|
||||
let args = [jvalue(l: javaString)]
|
||||
JNI.SaveFatalErrorMessage("UriConstructor")
|
||||
defer {
|
||||
JNI.RemoveFatalErrorMessage()
|
||||
}
|
||||
return JNI.check(JNI.CallStaticObjectMethod(UriClass, methodID: UriConstructor!, args: args)!, &locals)
|
||||
}, decodableClosure: {
|
||||
let pathString = JNI.api.CallObjectMethodA(JNI.env, $0, ObjectToStringMethod, nil)
|
||||
}, decodableClosure: { value, _ in
|
||||
let pathString = JNI.api.CallObjectMethodA(JNI.env, value, ObjectToStringMethod, nil)
|
||||
return URL(string: String(javaObject: pathString))
|
||||
})
|
||||
|
||||
RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: {
|
||||
let valueData = $0 as! Data
|
||||
RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: { data, codingPath in
|
||||
let valueData = data as! Data
|
||||
let byteArray = JNI.api.NewByteArray(JNI.env, jint(valueData.count))
|
||||
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)"))
|
||||
}
|
||||
try valueData.withUnsafeBytes({ pointer in
|
||||
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)"))
|
||||
}
|
||||
JNI.api.SetByteArrayRegion(JNI.env, byteArray, 0, jint(valueData.count), bytes)
|
||||
})
|
||||
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)"))
|
||||
}
|
||||
JNI.SaveFatalErrorMessage("java/nio/ByteBuffer wrap")
|
||||
|
@ -197,8 +214,8 @@ public struct JavaCoderConfig {
|
|||
JNI.RemoveFatalErrorMessage()
|
||||
}
|
||||
return JNI.CallStaticObjectMethod(ByteBufferClass, methodID: ByteBufferWrap, args: [jvalue(l: byteArray)])!
|
||||
}, decodableClosure: {
|
||||
let byteArray = JNI.CallObjectMethod($0, methodID: ByteBufferArray)
|
||||
}, decodableClosure: { value, _ in
|
||||
let byteArray = JNI.CallObjectMethod(value, methodID: ByteBufferArray)
|
||||
defer {
|
||||
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 var codingPath = [CodingKey]()
|
||||
public var codingPath: [CodingKey]
|
||||
|
||||
public var userInfo = [CodingUserInfoKey : Any]()
|
||||
|
||||
|
@ -19,14 +19,17 @@ public class JavaDecoder: Decoder {
|
|||
fileprivate let package: String
|
||||
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.missingFieldsStrategy = missingFieldsStrategy
|
||||
self.codingPath = codingPath
|
||||
}
|
||||
|
||||
public func decode<T : Decodable>(_ type: T.Type, from javaObject: jobject) throws -> T {
|
||||
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")
|
||||
return value
|
||||
}
|
||||
|
@ -86,7 +89,7 @@ public class JavaDecoder: Decoder {
|
|||
fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
|
||||
typealias Key = K
|
||||
|
||||
var codingPath = [CodingKey]()
|
||||
var codingPath: [CodingKey]
|
||||
var allKeys = [K]()
|
||||
|
||||
let decoder: JavaDecoder
|
||||
|
@ -98,6 +101,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.javaObject = jniStorage.javaObject
|
||||
self.codingPath = jniStorage.codingPath
|
||||
switch jniStorage.type {
|
||||
case let .object(className):
|
||||
self.javaClass = className
|
||||
|
@ -111,7 +115,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -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 {
|
||||
// TODO: WTF? Delete decodeWithMissingStrategy with default false -> CRASH
|
||||
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:)
|
||||
public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
|
||||
return try self.decodeJava(type, forKey: key)
|
||||
|
@ -176,6 +270,14 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
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? {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
@ -206,7 +308,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
JNI.ExceptionReset()
|
||||
let errorMessage = "\(javaClass).\(key.stringValue): JavaDecoder uses reflection for AnyCodable, " +
|
||||
"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 javaClassName = JNI.api.CallObjectMethodA(JNI.env, fieldClass, ClassGetNameMethod, nil)!
|
||||
|
@ -228,16 +330,16 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
defer {
|
||||
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 {
|
||||
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 {
|
||||
throw JavaCodingError.notSupported("JavaObjectContainer.nestedUnkeyedContainer(forKey: \(key))")
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested unkeyed container not supported")
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
|
@ -246,14 +348,14 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
|
|||
}
|
||||
|
||||
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 {
|
||||
typealias Key = K
|
||||
|
||||
var codingPath = [CodingKey]()
|
||||
var codingPath: [CodingKey]
|
||||
var allKeys = [K]()
|
||||
|
||||
private let decoder: JavaDecoder
|
||||
|
@ -266,6 +368,7 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
|||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.javaObject = jniStorage.javaObject
|
||||
self.codingPath = jniStorage.codingPath
|
||||
|
||||
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
||||
let keyArray = JNI.api.CallObjectMethodA(JNI.env, keySet, SetToArrayMethod, nil)
|
||||
|
@ -283,21 +386,24 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
|||
var keySig: String?
|
||||
|
||||
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 {
|
||||
throw JavaCodingError.wrongArrayLength
|
||||
throw DecodingError.dataCorruptedError(forKey: K(stringValue: "init()")!, in: self, debugDescription: "Wrong array length")
|
||||
}
|
||||
if keySig == nil {
|
||||
keySig = self.decoder.getJavaClassname(from: object).sig
|
||||
}
|
||||
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) {
|
||||
javaKeys[stringKey] = object
|
||||
allKeys.append(key)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
javaKeys[intKey] = object
|
||||
allKeys.append(key)
|
||||
|
@ -317,7 +423,7 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -331,20 +437,20 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
|||
|
||||
let javaKey = javaKeys[typeKey]
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
throw JavaCodingError.notSupported("JavaHashMapContainer.nestedUnkeyedContainer(forKey: \(key))")
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Nested unkeyed container not supported")
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
|
@ -352,13 +458,13 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey>: KeyedDecodingContain
|
|||
}
|
||||
|
||||
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 {
|
||||
|
||||
var codingPath = [CodingKey]()
|
||||
var codingPath: [CodingKey]
|
||||
|
||||
var count: Int?
|
||||
|
||||
|
@ -383,6 +489,7 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
|||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.javaObject = jniStorage.javaObject
|
||||
self.codingPath = jniStorage.codingPath
|
||||
self.count = Int(JNI.CallIntMethod(self.javaObject, methodID: HashMapSizeMethod)) * 2
|
||||
|
||||
let keySet = JNI.api.CallObjectMethodA(JNI.env, javaObject, HashMapKeySetMethod, nil)
|
||||
|
@ -397,13 +504,14 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
|||
}
|
||||
|
||||
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 {
|
||||
let codingKey = JavaKey(intValue: currentIndex)
|
||||
if let javaCurrentKey = javaCurrentKey {
|
||||
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
|
||||
defer {
|
||||
|
@ -411,34 +519,34 @@ fileprivate class JavaHashMapUnkeyedContainer: UnkeyedDecodingContainer {
|
|||
JNI.DeleteLocalRef(self.javaCurrentKey)
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedContainer(keyedBy: \(type))")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested keyed container not supported")
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedUnkeyedContainer")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested unkeyed container not supported")
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.superDecoder")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Super decoder not supported")
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
||||
|
||||
var codingPath = [CodingKey]()
|
||||
var codingPath: [CodingKey]
|
||||
|
||||
var count: Int?
|
||||
|
||||
|
@ -459,6 +567,7 @@ fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
|||
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
||||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.codingPath = jniStorage.codingPath
|
||||
self.count = Int(JNI.CallIntMethod(jniStorage.javaObject, methodID: CollectionSizeMethod))
|
||||
self.javaIterator = JNI.CallObjectMethod(jniStorage.javaObject, methodID: CollectionIteratorMethod)!
|
||||
}
|
||||
|
@ -468,35 +577,36 @@ fileprivate class JavaArrayContainer: UnkeyedDecodingContainer {
|
|||
}
|
||||
|
||||
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 {
|
||||
let codingKey = JavaKey(intValue: currentIndex)
|
||||
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 {
|
||||
JNI.DeleteLocalRef(object)
|
||||
}
|
||||
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 {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedContainer(keyedBy: \(type))")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested keyed container not supported")
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.nestedUnkeyedContainer")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Nested unkeyed container not supported")
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
throw JavaCodingError.notSupported("JavaUnkeyedDecodingContainer.superDecoder")
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Super decoder not supported")
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
||||
var codingPath: [CodingKey] = []
|
||||
var codingPath: [CodingKey]
|
||||
|
||||
let decoder: JavaDecoder
|
||||
let jniStorage: JNIStorageObject
|
||||
|
@ -507,6 +617,7 @@ fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
|||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.javaObject = jniStorage.javaObject
|
||||
self.codingPath = jniStorage.codingPath
|
||||
switch jniStorage.type {
|
||||
case let .object(className):
|
||||
self.javaClass = className
|
||||
|
@ -519,23 +630,83 @@ fileprivate class JavaEnumContainer: SingleValueDecodingContainer {
|
|||
fatalError("Unsupported: JavaEnumDecodingContainer.decodeNil")
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||
let classname = self.decoder.getJavaClassname(forType: type)
|
||||
func decode(_ type: Int.Type) throws -> Int {
|
||||
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);")
|
||||
guard let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) else {
|
||||
throw JavaCodingError.nilNotSupported("\(javaClass).rawValue")
|
||||
throw JavaCodingError.notSupported(javaClass)
|
||||
}
|
||||
defer {
|
||||
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 {
|
||||
typealias Key = K
|
||||
|
||||
var codingPath = [CodingKey]()
|
||||
var codingPath: [CodingKey]
|
||||
var allKeys = [K]()
|
||||
|
||||
let decoder: JavaDecoder
|
||||
|
@ -545,6 +716,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
|||
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
|
||||
self.decoder = decoder
|
||||
self.jniStorage = jniStorage
|
||||
self.codingPath = jniStorage.codingPath
|
||||
switch jniStorage.type {
|
||||
case let .anyCodable(codable):
|
||||
self.jniCodableType = codable
|
||||
|
@ -558,7 +730,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -584,7 +756,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
|||
}
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -602,7 +774,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedDecodingContaine
|
|||
case .dictionary:
|
||||
return try JavaHashMapUnkeyedContainer(decoder: self.decoder, jniStorage: self.jniStorage)
|
||||
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 {
|
||||
throw JavaCodingError.notSupported("JavaAnyCodableContainer.superDecoder(forKey: \(key)")
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Super decoder not supported")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if let decodableClosure = JavaCoderConfig.decodableClosures[typeName] {
|
||||
return try decodableClosure(javaObject) as! T
|
||||
return try decodableClosure(javaObject, codingPath) as! T
|
||||
}
|
||||
else if type == AnyCodable.self {
|
||||
let cls = JNI.api.GetObjectClass(JNI.env, javaObject)
|
||||
|
@ -642,7 +814,9 @@ extension JavaDecoder {
|
|||
codableType = .object(className: className)
|
||||
}
|
||||
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)
|
||||
return try T.init(from: self)
|
||||
}
|
||||
|
@ -652,13 +826,19 @@ extension JavaDecoder {
|
|||
let obj = JNI.api.NewLocalRef(JNI.env, javaObject)!
|
||||
switch stringType {
|
||||
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<"):
|
||||
storageObject = JNIStorageObject(type: .array(className: HashSetClassname), javaObject: obj)
|
||||
storageObject = JNIStorageObject(type: .array(className: HashSetClassname),
|
||||
javaObject: obj,
|
||||
codingPath: codingPath)
|
||||
case _ where stringType.starts(with: "Dictionary<"):
|
||||
storageObject = JNIStorageObject(type: .dictionary, javaObject: obj)
|
||||
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)
|
||||
return try T.init(from: self)
|
||||
|
|
|
@ -15,12 +15,32 @@ public enum MissingFieldsStrategy: Error {
|
|||
case ignore
|
||||
}
|
||||
|
||||
public enum JavaCodingError: Error {
|
||||
case notSupported(String)
|
||||
case cantCreateObject(String)
|
||||
case cantFindObject(String)
|
||||
case nilNotSupported(String)
|
||||
case wrongArrayLength
|
||||
internal struct JavaKey : CodingKey {
|
||||
|
||||
public var stringValue: String
|
||||
public var intValue: Int?
|
||||
|
||||
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 {
|
||||
|
@ -45,6 +65,7 @@ indirect enum JNIStorageType {
|
|||
|
||||
class JNIStorageObject {
|
||||
let type: JNIStorageType
|
||||
let codingPath: [CodingKey]
|
||||
var javaObject: jobject! {
|
||||
didSet {
|
||||
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.javaObject = javaObject
|
||||
self.codingPath = codingPath
|
||||
}
|
||||
|
||||
init(type: JNIStorageType) {
|
||||
init(type: JNIStorageType, codingPath: [CodingKey] = []) {
|
||||
self.type = type
|
||||
self.codingPath = codingPath
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -88,8 +111,10 @@ open class JavaEncoder: Encoder {
|
|||
|
||||
// MARK: - Constructing a JSON Encoder
|
||||
/// Initializes `self` with default strategies.
|
||||
public init(forPackage: String, missingFieldsStrategy: MissingFieldsStrategy = .throw) {
|
||||
self.codingPath = [CodingKey]()
|
||||
public init(forPackage: String,
|
||||
missingFieldsStrategy: MissingFieldsStrategy = .throw,
|
||||
codingPath: [CodingKey] = []) {
|
||||
self.codingPath = codingPath
|
||||
self.package = forPackage
|
||||
self.javaObjects = [JNIStorageObject]()
|
||||
self.missingFieldsStrategy = missingFieldsStrategy
|
||||
|
@ -104,7 +129,7 @@ open class JavaEncoder: Encoder {
|
|||
/// - throws: An error if any value throws an error during encoding.
|
||||
open func encode<T : Encodable>(_ value: T) throws -> jobject {
|
||||
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")
|
||||
return JNI.api.NewLocalRef(JNI.env, storage.javaObject)!
|
||||
}
|
||||
|
@ -192,14 +217,190 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedEncodingContainerPro
|
|||
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
|
||||
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 {
|
||||
try self.encodeObject(value, forKey: key)
|
||||
}
|
||||
|
||||
private func encodeObject<T : Encodable>(_ value: T, forKey key: Key) throws {
|
||||
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)
|
||||
JNI.api.SetObjectField(JNI.env, self.javaObject, filed, object.javaObject)
|
||||
}
|
||||
|
@ -260,19 +461,19 @@ fileprivate class JavaHashMapKeyedContainer<K : CodingKey> : KeyedEncodingContai
|
|||
|
||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||
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 {
|
||||
let keyStorage: JNIStorageObject
|
||||
if let intValue = key.intValue {
|
||||
keyStorage = try self.encoder.box(intValue)
|
||||
keyStorage = try self.encoder.box(intValue, codingPath: codingPath + [key])
|
||||
}
|
||||
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)])
|
||||
assert(result == nil, "Rewrite for key \(key.stringValue)")
|
||||
}
|
||||
|
@ -323,11 +524,12 @@ fileprivate class JavaHashMapUnkeyedContainer : UnkeyedEncodingContainer {
|
|||
|
||||
// MARK: - UnkeyedEncodingContainer Methods
|
||||
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 {
|
||||
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 {
|
||||
let result = JNI.CallObjectMethod(javaObject, methodID: HashMapPutMethod, args: [jvalue(l: javaKey.javaObject), jvalue(l: javaValue.javaObject)])
|
||||
assert(result == nil, "Rewrite for key")
|
||||
|
@ -379,11 +581,12 @@ fileprivate class JavaArrayContainer : UnkeyedEncodingContainer {
|
|||
|
||||
// MARK: - UnkeyedEncodingContainer Methods
|
||||
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 {
|
||||
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)])
|
||||
assert(rewrite == JNI.TRUE, "ArrayList should always return true from add()")
|
||||
count += 1
|
||||
|
@ -418,11 +621,76 @@ class JavaEnumValueEncodingContainer: SingleValueEncodingContainer {
|
|||
}
|
||||
|
||||
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 {
|
||||
let rawValue = try self.encoder.box(value)
|
||||
public func encode(_ value: Int8) throws {
|
||||
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)
|
||||
// If jniStorage.javaObject == nil its enum, else optionSet
|
||||
if jniStorage.javaObject == nil {
|
||||
|
@ -432,13 +700,13 @@ class JavaEnumValueEncodingContainer: SingleValueEncodingContainer {
|
|||
JNI.RemoveFatalErrorMessage()
|
||||
}
|
||||
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
|
||||
}
|
||||
else {
|
||||
let filed = try JNI.getJavaField(forClass: self.javaClass, field: "rawValue", sig: rawValue.type.sig)
|
||||
JNI.api.SetObjectField(JNI.env, self.jniStorage.javaObject, filed, rawValue.javaObject)
|
||||
let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Unsupported: type \(type(of: valueType))")
|
||||
throw EncodingError.invalidValue(valueType, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +738,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
|||
|
||||
// MARK: - KeyedEncodingContainerProtocol Methods
|
||||
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 {
|
||||
|
@ -479,7 +747,7 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
|||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
catch {
|
||||
|
@ -523,18 +791,18 @@ fileprivate class JavaAnyCodableContainer<K : CodingKey> : KeyedEncodingContaine
|
|||
|
||||
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 typeName = String(describing: type(of: value))
|
||||
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)
|
||||
}
|
||||
else if T.self == AnyCodable.self {
|
||||
let anyCodableValue = value as! AnyCodable
|
||||
if let javaClassname = JavaCoderConfig.codableClassNames[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)
|
||||
}
|
||||
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