Dev: add MissingFieldsStrategy and enum support for decoding

This commit is contained in:
Andrew Druk 2017-10-31 18:16:43 +02:00
parent 8a307a9644
commit 9ba26b6593
2 changed files with 92 additions and 25 deletions

View File

@ -16,9 +16,16 @@ public class JavaDecoder: Decoder {
fileprivate var storage = [JNIStorageObject]() fileprivate var storage = [JNIStorageObject]()
fileprivate let package: String fileprivate let package: String
fileprivate let missingFieldsStrategy: MissingFieldsStrategy
public init(forPackage package: String) { public init(forPackage package: String) {
self.package = package self.package = package
self.missingFieldsStrategy = .throw
}
public init(forPackage package: String, missingFieldsStrategy: MissingFieldsStrategy) {
self.package = package
self.missingFieldsStrategy = missingFieldsStrategy
} }
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 {
@ -64,7 +71,13 @@ public class JavaDecoder: Decoder {
} }
public func singleValueContainer() throws -> SingleValueDecodingContainer { public func singleValueContainer() throws -> SingleValueDecodingContainer {
return JavaSingleValueDecodingContainer() let storageObject = self.popInstance()
switch storageObject.type {
case .object:
return JavaEnumDecodingContainer(decoder: self, jniStorage: storageObject)
default:
fatalError("Only object supported here")
}
} }
} }
@ -102,35 +115,61 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
throw JavaCodingError.notSupported throw JavaCodingError.notSupported
} }
private func decodeWithMissingStrategy<T>(defaultValue: T, block: () throws -> T) throws -> T {
do {
return try block()
}
catch {
if self.decoder.missingFieldsStrategy == .ignore {
return defaultValue
}
else {
throw error
}
}
}
func decode(_ type: Bool.Type, forKey key: K) throws -> Bool { func decode(_ type: Bool.Type, forKey key: K) throws -> Bool {
return try decodeWithMissingStrategy(defaultValue: false) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "Z") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "Z")
return JNI.api.GetBooleanField(JNI.env, javaObject, fieldID) == JNI_TRUE return JNI.api.GetBooleanField(JNI.env, javaObject, fieldID) == JNI_TRUE
} }
}
func decode(_ type: Int.Type, forKey key: K) throws -> Int { func decode(_ type: Int.Type, forKey key: K) throws -> Int {
return try decodeWithMissingStrategy(defaultValue: 0) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "J") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "J")
return Int(JNI.api.GetLongField(JNI.env, javaObject, fieldID)) return Int(JNI.api.GetLongField(JNI.env, javaObject, fieldID))
} }
}
func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 { func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 {
return try decodeWithMissingStrategy(defaultValue: 0) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "B") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "B")
return JNI.api.GetByteField(JNI.env, javaObject, fieldID) return JNI.api.GetByteField(JNI.env, javaObject, fieldID)
} }
}
func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 { func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 {
return try decodeWithMissingStrategy(defaultValue: 0) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "S") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "S")
return JNI.api.GetShortField(JNI.env, javaObject, fieldID) return JNI.api.GetShortField(JNI.env, javaObject, fieldID)
} }
}
func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 { func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 {
return try decodeWithMissingStrategy(defaultValue: 0) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "I") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "I")
return Int32(JNI.api.GetIntField(JNI.env, javaObject, fieldID)) return Int32(JNI.api.GetIntField(JNI.env, javaObject, fieldID))
} }
}
func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 { func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 {
return try decodeWithMissingStrategy(defaultValue: 0) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "J") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "J")
return JNI.api.GetLongField(JNI.env, javaObject, fieldID) return JNI.api.GetLongField(JNI.env, javaObject, fieldID)
} }
}
func decode(_ type: String.Type, forKey key: K) throws -> String { func decode(_ type: String.Type, forKey key: K) throws -> String {
guard let result = try decodeIfPresent(type, forKey: key) else { guard let result = try decodeIfPresent(type, forKey: key) else {
@ -147,14 +186,17 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
} }
public func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? { public func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? {
return try decodeWithMissingStrategy(defaultValue: nil) {
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "L\(JavaStringClassname);") let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: "L\(JavaStringClassname);")
let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID)
let str = String(javaObject: object) let str = String(javaObject: object)
JNI.api.DeleteLocalRef(JNI.env, object) JNI.api.DeleteLocalRef(JNI.env, object)
return str return str
} }
}
public func decodeIfPresent<T>(_ type: T.Type, forKey key: K) throws -> T? where T : Decodable { public func decodeIfPresent<T>(_ type: T.Type, forKey key: K) throws -> T? where T : Decodable {
return try decodeWithMissingStrategy(defaultValue: nil) {
let sig = self.decoder.getSig(forType: type) let sig = self.decoder.getSig(forType: type)
let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: sig) let fieldID = try getJavaField(forClass: javaClass, field: key.stringValue, sig: sig)
guard let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) else { guard let object = JNI.api.GetObjectField(JNI.env, javaObject, fieldID) else {
@ -163,6 +205,7 @@ fileprivate class JavaObjectContainer<K : CodingKey> : KeyedDecodingContainerPro
self.decoder.pushObject(object, forType: type) self.decoder.pushObject(object, forType: type)
return try T(from: self.decoder) return try T(from: self.decoder)
} }
}
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 throw JavaCodingError.notSupported
@ -363,14 +406,33 @@ fileprivate class JavaUnkeyedDecodingContainer: UnkeyedDecodingContainer {
} }
} }
fileprivate class JavaSingleValueDecodingContainer: SingleValueDecodingContainer { fileprivate class JavaEnumDecodingContainer: SingleValueDecodingContainer {
var codingPath: [CodingKey] = [] var codingPath: [CodingKey] = []
let decoder: JavaDecoder
let javaObject: jobject
let javaClass: String
fileprivate init(decoder: JavaDecoder, jniStorage: JNIStorageObject) {
self.decoder = decoder
self.javaObject = jniStorage.javaObject
switch jniStorage.type {
case let .object(className):
self.javaClass = className
default:
fatalError("Wrong container type")
}
}
func decodeNil() -> Bool { func decodeNil() -> Bool {
fatalError("Unsupported") fatalError("Unsupported")
} }
func decode<T>(_ type: T.Type) throws -> T where T : Decodable { func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
if type == Int.self {
let fieldID = try getJavaField(forClass: javaClass, field: "rawValue", sig: "J")
return Int(JNI.api.GetLongField(JNI.env, javaObject, fieldID)) as! T
}
throw JavaCodingError.notSupported throw JavaCodingError.notSupported
} }
} }

View File

@ -9,6 +9,11 @@ import Foundation
import CoreFoundation import CoreFoundation
import java_swift import java_swift
public enum MissingFieldsStrategy: Error {
case `throw`
case ignore
}
public enum JavaCodingError: Error { public enum JavaCodingError: Error {
case notSupported case notSupported
case cantCreateObject(String) case cantCreateObject(String)