From 4d38bfb42c33f8585f9f94108956f70593ff04af Mon Sep 17 00:00:00 2001 From: Hwee-Boon Yar Date: Mon, 12 Nov 2018 02:18:15 +0800 Subject: [PATCH] Update XML parsing to handle latest asset definition schema --- .../AssetDefinition/AssetAttribute.swift | 49 ++++++++++++------- AlphaWallet/AssetDefinition/XMLHandler.swift | 2 +- .../web3swift-pod/CallForAssetAttribute.swift | 16 +++++- .../CallForAssetAttributeCoordinator.swift | 2 +- .../UI/ViewModels/TokenCardRowViewModel.swift | 11 +++++ AlphaWallet/UI/Views/TokenCardRowView.swift | 5 ++ 6 files changed, 63 insertions(+), 22 deletions(-) diff --git a/AlphaWallet/AssetDefinition/AssetAttribute.swift b/AlphaWallet/AssetDefinition/AssetAttribute.swift index c70174d9d8..fa52b2d4e9 100644 --- a/AlphaWallet/AssetDefinition/AssetAttribute.swift +++ b/AlphaWallet/AssetDefinition/AssetAttribute.swift @@ -83,27 +83,16 @@ enum AssetAttribute { let attributeAccessor = XML.Accessor(attribute) let functionElement = attributeAccessor["\(rootNamespacePrefix)origin"]["\(rootNamespacePrefix)function"] - if let attributeName = attributeAccessor.attributes["id"], case .singleElement(let origin) = attributeAccessor["\(rootNamespacePrefix)origin"], let rawSyntax = attributeAccessor.attributes["syntax"], let syntax = AssetAttributeSyntax(rawValue: rawSyntax), let functionName = functionElement.text?.dropParenthesis, !functionName.isEmpty { + if let attributeName = attributeAccessor.attributes["id"], case .singleElement(let origin) = attributeAccessor["\(rootNamespacePrefix)origin"], let rawSyntax = attributeAccessor.attributes["syntax"], let syntax = AssetAttributeSyntax(rawValue: rawSyntax), let functionName = functionElement.attributes["name"], !functionName.isEmpty { let inputs: [CallForAssetAttribute.Argument] let returnType = syntax.solidityReturnType let output = CallForAssetAttribute.ReturnType(type: returnType) - - switch functionElement["\(rootNamespacePrefix)value"] { - case .singleElement(let inputElement): - if let inputTypeString = inputElement.text, !inputTypeString.isEmpty, let inputName = inputElement.attributes["ref"], !inputName.isEmpty, let inputType = CallForAssetAttribute.SolidityType(rawValue: inputTypeString) { - inputs = [.init(name: inputName, type: inputType)] - } else { - inputs = [] - } - case .sequence(let inputElements): - inputs = inputElements.compactMap { - if let inputTypeString = $0.text, !inputTypeString.isEmpty, let inputName = $0.attributes["ref"], !inputName.isEmpty, let inputType = CallForAssetAttribute.SolidityType(rawValue: inputTypeString) { - return .init(name: inputName, type: inputType) - } else { - return nil - } - } + switch functionElement["\(rootNamespacePrefix)inputs"] { + case .singleElement(let inputsElement): + inputs = AssetAttribute.extractInputs(fromInputsElements: [inputsElement]) + case .sequence(let inputsElements): + inputs = AssetAttribute.extractInputs(fromInputsElements: inputsElements) case .failure: inputs = [] } @@ -116,7 +105,18 @@ enum AssetAttribute { }() } - func extract(from tokenValue: BigUInt, ofContract contract: String, config: Config, callForAssetAttributeCoordinator: CallForAssetAttributeCoordinator?) -> AssetAttributeValue { + private static func extractInputs(fromInputsElements inputsElements: [XML.Element]) -> [CallForAssetAttribute.Argument]{ + return inputsElements.flatMap { $0.childElements }.compactMap { + let inputTypeString = $0.name.withoutXMLNamespacePrefix + if !inputTypeString.isEmpty, let inputName = $0.attributes["ref"], !inputName.isEmpty, let inputType = CallForAssetAttribute.SolidityType(rawValue: inputTypeString) { + return .init(name: inputName, type: inputType) + } else { + return nil + } + } + } + + func extract(from tokenValue: BigUInt, ofContract contract: String, config: Config, callForAssetAttributeCoordinator: CallForAssetAttributeCoordinator?) -> AssetAttributeValue { switch self { case .mapping(_, _, let syntax, _, _, _), .direct(_, _, let syntax, _, _): switch syntax { @@ -155,7 +155,7 @@ enum AssetAttribute { switch self { case .mapping(let attribute, let rootNamespacePrefix, let syntax, let lang, _, _): guard let key = parseValue(tokenValue: tokenValue) else { return nil } - guard let value = attribute["\(rootNamespacePrefix)origin"]["\(rootNamespacePrefix)option"].getElementWithKeyAttribute(equals: String(key))?["\(rootNamespacePrefix)value"].getElementWithLangAttribute(equals: lang)?.text else { return nil } + guard let value = attribute["\(rootNamespacePrefix)origin"]["\(rootNamespacePrefix)mapping"]["\(rootNamespacePrefix)option"].getElementWithKeyAttribute(equals: String(key))?["\(rootNamespacePrefix)value"].getElementWithLangAttribute(equals: lang)?.text else { return nil } return syntax.extract(from: value, isMapping: true) as? T case .direct(_, _, let syntax, _, _): guard let value = parseValue(tokenValue: tokenValue) else { return nil } @@ -222,3 +222,14 @@ enum AssetAttribute { return callForAssetAttributeCoordinator.getValue(forAttributeName: attributeName, tokenId: tokenId, functionCall: functionCall) } } + +extension String { + fileprivate var withoutXMLNamespacePrefix: String { + let components = split(separator: ":") + if components.count > 1 { + return String(components[1]) + } else { + return String(components[0]) + } + } +} diff --git a/AlphaWallet/AssetDefinition/XMLHandler.swift b/AlphaWallet/AssetDefinition/XMLHandler.swift index d2d42ffeae..c5a02417c7 100644 --- a/AlphaWallet/AssetDefinition/XMLHandler.swift +++ b/AlphaWallet/AssetDefinition/XMLHandler.swift @@ -89,7 +89,7 @@ private class PrivateXMLHandler { let lang = getLang() var fields = [String: AssetAttribute]() for e in xml["\(rootNamespacePrefix)token"]["\(rootNamespacePrefix)attribute-types"]["\(rootNamespacePrefix)attribute-type"] { - if let id = e.attributes["id"], case let .singleElement(element) = e, XML.Accessor(element)["\(rootNamespacePrefix)origin"].attributes["as"] != nil { + if let id = e.attributes["id"], case let .singleElement(element) = e, XML.Accessor(element)["\(rootNamespacePrefix)origin"].attributes["bitmask"] != nil { fields[id] = AssetAttribute(attribute: element, rootNamespacePrefix: rootNamespacePrefix, lang: lang) } else if let id = e.attributes["id"], case let .singleElement(element) = e, XML.Accessor(element)["\(rootNamespacePrefix)origin"].attributes["contract"] == "holding-contract" { fields[id] = AssetAttribute(attribute: element, rootNamespacePrefix: rootNamespacePrefix) diff --git a/AlphaWallet/RPC/Commands/web3swift-pod/CallForAssetAttribute.swift b/AlphaWallet/RPC/Commands/web3swift-pod/CallForAssetAttribute.swift index daadfd0493..a987246e76 100644 --- a/AlphaWallet/RPC/Commands/web3swift-pod/CallForAssetAttribute.swift +++ b/AlphaWallet/RPC/Commands/web3swift-pod/CallForAssetAttribute.swift @@ -4,10 +4,24 @@ import Foundation struct CallForAssetAttribute { enum SolidityType: String { + //TODO do we need to support the "odd" ones like uint24 in all steps of 8? + //TODO support address, enums, etc? case bool case int - case string + case int8 + case int16 + case int32 + case int64 + case int128 + case int256 + case uint + case uint8 + case uint16 + case uint32 + case uint64 + case uint128 case uint256 + case string } struct Argument: Equatable { diff --git a/AlphaWallet/Tokens/Coordinators/CallForAssetAttributeCoordinator.swift b/AlphaWallet/Tokens/Coordinators/CallForAssetAttributeCoordinator.swift index 1393062725..effd7650f3 100644 --- a/AlphaWallet/Tokens/Coordinators/CallForAssetAttributeCoordinator.swift +++ b/AlphaWallet/Tokens/Coordinators/CallForAssetAttributeCoordinator.swift @@ -101,7 +101,7 @@ class CallForAssetAttributeCoordinator { let result = value as? String ?? "" seal.fulfill(result) self.updateDataStore(forContract: functionCall.contract, tokenId: tokenId, attributeName: attributeName, value: result) - case .int, .uint256: + case .int, .int8, .int16, .int32, .int64, .int128, .int256, .uint, .uint8, .uint16, .uint32, .uint64, .uint128, .uint256: let result = value as? Int ?? 0 seal.fulfill(result) self.updateDataStore(forContract: functionCall.contract, tokenId: tokenId, attributeName: attributeName, value: result) diff --git a/AlphaWallet/UI/ViewModels/TokenCardRowViewModel.swift b/AlphaWallet/UI/ViewModels/TokenCardRowViewModel.swift index f1c8fca1f1..ea2e30a068 100644 --- a/AlphaWallet/UI/ViewModels/TokenCardRowViewModel.swift +++ b/AlphaWallet/UI/ViewModels/TokenCardRowViewModel.swift @@ -75,6 +75,17 @@ struct TokenCardRowViewModel: TokenCardRowViewModelProtocol { } } + func subscribeLocality(withBlock block: @escaping (String) -> ()) { + guard isMeetupContract else { return } + if let subscribableAssetAttributeValue = tokenHolder.values["locality"] as? SubscribableAssetAttributeValue { + subscribableAssetAttributeValue.subscribable.subscribe { value in + if let value = value as? String { + block(value) + } + } + } + } + func subscribeBuilding(withBlock block: @escaping (String) -> ()) { if let subscribableAssetAttributeValue = tokenHolder.values["building"] as? SubscribableAssetAttributeValue { subscribableAssetAttributeValue.subscribable.subscribe { value in diff --git a/AlphaWallet/UI/Views/TokenCardRowView.swift b/AlphaWallet/UI/Views/TokenCardRowView.swift index 36ba4da88e..2fa1220293 100644 --- a/AlphaWallet/UI/Views/TokenCardRowView.swift +++ b/AlphaWallet/UI/Views/TokenCardRowView.swift @@ -190,6 +190,11 @@ class TokenCardRowView: UIView { strongSelf.categoryLabel.text = building } + vm.subscribeLocality { [weak self] locality in + guard let strongSelf = self else { return } + strongSelf.cityLabel.text = ", \(locality)" + } + vm.subscribeExpired { [weak self] expired in guard let strongSelf = self else { return } strongSelf.teamsLabel.text = expired