Merge pull request #893 from AlphaWallet/update-xml-parsing-for-latest-asset-definition-schema

Update XML parsing to handle latest asset definition schema
pull/894/head
James Sangalli 6 years ago committed by GitHub
commit 896ff006cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 49
      AlphaWallet/AssetDefinition/AssetAttribute.swift
  2. 2
      AlphaWallet/AssetDefinition/XMLHandler.swift
  3. 16
      AlphaWallet/RPC/Commands/web3swift-pod/CallForAssetAttribute.swift
  4. 2
      AlphaWallet/Tokens/Coordinators/CallForAssetAttributeCoordinator.swift
  5. 11
      AlphaWallet/UI/ViewModels/TokenCardRowViewModel.swift
  6. 5
      AlphaWallet/UI/Views/TokenCardRowView.swift

@ -83,27 +83,16 @@ enum AssetAttribute {
let attributeAccessor = XML.Accessor(attribute) let attributeAccessor = XML.Accessor(attribute)
let functionElement = attributeAccessor["\(rootNamespacePrefix)origin"]["\(rootNamespacePrefix)function"] 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 inputs: [CallForAssetAttribute.Argument]
let returnType = syntax.solidityReturnType let returnType = syntax.solidityReturnType
let output = CallForAssetAttribute.ReturnType(type: returnType) let output = CallForAssetAttribute.ReturnType(type: returnType)
switch functionElement["\(rootNamespacePrefix)inputs"] {
switch functionElement["\(rootNamespacePrefix)value"] { case .singleElement(let inputsElement):
case .singleElement(let inputElement): inputs = AssetAttribute.extractInputs(fromInputsElements: [inputsElement])
if let inputTypeString = inputElement.text, !inputTypeString.isEmpty, let inputName = inputElement.attributes["ref"], !inputName.isEmpty, let inputType = CallForAssetAttribute.SolidityType(rawValue: inputTypeString) { case .sequence(let inputsElements):
inputs = [.init(name: inputName, type: inputType)] inputs = AssetAttribute.extractInputs(fromInputsElements: inputsElements)
} 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
}
}
case .failure: case .failure:
inputs = [] 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 { switch self {
case .mapping(_, _, let syntax, _, _, _), .direct(_, _, let syntax, _, _): case .mapping(_, _, let syntax, _, _, _), .direct(_, _, let syntax, _, _):
switch syntax { switch syntax {
@ -155,7 +155,7 @@ enum AssetAttribute {
switch self { switch self {
case .mapping(let attribute, let rootNamespacePrefix, let syntax, let lang, _, _): case .mapping(let attribute, let rootNamespacePrefix, let syntax, let lang, _, _):
guard let key = parseValue(tokenValue: tokenValue) else { return nil } 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 return syntax.extract(from: value, isMapping: true) as? T
case .direct(_, _, let syntax, _, _): case .direct(_, _, let syntax, _, _):
guard let value = parseValue(tokenValue: tokenValue) else { return nil } guard let value = parseValue(tokenValue: tokenValue) else { return nil }
@ -222,3 +222,14 @@ enum AssetAttribute {
return callForAssetAttributeCoordinator.getValue(forAttributeName: attributeName, tokenId: tokenId, functionCall: functionCall) 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])
}
}
}

@ -89,7 +89,7 @@ private class PrivateXMLHandler {
let lang = getLang() let lang = getLang()
var fields = [String: AssetAttribute]() var fields = [String: AssetAttribute]()
for e in xml["\(rootNamespacePrefix)token"]["\(rootNamespacePrefix)attribute-types"]["\(rootNamespacePrefix)attribute-type"] { 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) 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" { } 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) fields[id] = AssetAttribute(attribute: element, rootNamespacePrefix: rootNamespacePrefix)

@ -4,10 +4,24 @@ import Foundation
struct CallForAssetAttribute { struct CallForAssetAttribute {
enum SolidityType: String { 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 bool
case int 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 uint256
case string
} }
struct Argument: Equatable { struct Argument: Equatable {

@ -101,7 +101,7 @@ class CallForAssetAttributeCoordinator {
let result = value as? String ?? "" let result = value as? String ?? ""
seal.fulfill(result) seal.fulfill(result)
self.updateDataStore(forContract: functionCall.contract, tokenId: tokenId, attributeName: attributeName, value: 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 let result = value as? Int ?? 0
seal.fulfill(result) seal.fulfill(result)
self.updateDataStore(forContract: functionCall.contract, tokenId: tokenId, attributeName: attributeName, value: result) self.updateDataStore(forContract: functionCall.contract, tokenId: tokenId, attributeName: attributeName, value: result)

@ -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) -> ()) { func subscribeBuilding(withBlock block: @escaping (String) -> ()) {
if let subscribableAssetAttributeValue = tokenHolder.values["building"] as? SubscribableAssetAttributeValue { if let subscribableAssetAttributeValue = tokenHolder.values["building"] as? SubscribableAssetAttributeValue {
subscribableAssetAttributeValue.subscribable.subscribe { value in subscribableAssetAttributeValue.subscribable.subscribe { value in

@ -190,6 +190,11 @@ class TokenCardRowView: UIView {
strongSelf.categoryLabel.text = building 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 vm.subscribeExpired { [weak self] expired in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
strongSelf.teamsLabel.text = expired strongSelf.teamsLabel.text = expired

Loading…
Cancel
Save