Swift4 отображает результат Parser в NSOutlineView - PullRequest
0 голосов
/ 18 мая 2018

Я написал анализатор TLV, который возвращает результаты, подобные следующему значению длины тега:

 <e1> 53 <9f1e0831 36303231 343337ef…> 
     <9f1e> 8 <31363032 31343337> 
     <ef> 18 <df0d084d 3030302d 4d5049df 7f04312d 3232> 
         <df0d> 8 <4d303030 2d4d5049> 
         <df7f> 4 <312d3232> 
     <ef> 20 <df0d0b4d 3030302d 54455354 4f53df7f 03362d35> 
         <df0d> 11 <4d303030 2d544553 544f53> 
         <df7f> 3 <362d35>

Я хочу отобразить это в OutlineView, но я не знаю, как должен выглядеть объект хранилища, икак его заполнить.Каким-то образом это должно быть примерно так:

class Node: NSObject {
    var isConstructed = false
    var tag = „Tag“
    var length = 0
    var value = „Value“
    var children = [Node]()
    weak var parent: Node?

    override init() {
       super.init()
    }


    init(tag: String) {
      self.tag = tag
    }
    init(length: Int) {
      self.length = length
    }
    init(value: String) {
      self.value = value
    }
    init(isConstructed: Bool) {
      self.isConstructed = isConstructed
    }

    func isLeaf() -> Bool {
      return children.isEmpty
    }
}

Демонстрационный анализатор TLV TLVparser

Должен выглядеть так: Результат анализа TLV в NSOutlineView

1 Ответ

0 голосов
/ 22 мая 2018

Наконец-то есть время поработать над этим вопросом сегодня.Поместить это в NSOutlineView - это просто.Сложная часть состоит в том, чтобы определить модель данных для ваших данных TLV и проанализировать Data в них.

TLVNode.swift

Этот класс хранит данные TLV.Ваш код выглядит как преобразованный C, и тогда он тоже не очень хорош.

import Foundation

// Since we use Cocoa Bindings, all properties need to be
// dynamically dispatch hence @objCMembers declaration
// Its ability to handle erroneous data is not tested. That
// is left for the OP as an exercise.
@objcMembers class TLVNode: NSObject {
    var tag: Data
    var length: Int
    var value: Data
    var isConstructed: Bool
    var children = [TLVNode]()

    // Convert `tag` from Data to a string of hex for display
    var displayTag: String {
        // Pad the hex value with 0 so it outputs `0d` instead of just `d`
        return tag.map { ("0" + String($0, radix: 16)).suffix(2) }.joined()
    }

    // Convert `value` from Data to string of hex
    var displayValue: String {
        let hexValues = value.map { ("0" + String($0, radix: 16)).suffix(2) }

        var str = ""
        for (index, hex) in hexValues.enumerated() {
            if index > 0 && index % 4 == 0 {
                str += " " + hex
            } else {
                str += hex
            }
        }
        return str
    }

    convenience init?(dataStream: Data) {
        var size = 0
        self.init(dataStream: dataStream, offset: 0, size: &size)
    }

    static func create(from dataStream: Data) -> [TLVNode] {
        var size = 0
        var offset = 0
        var nodes = [TLVNode]()

        while let node = TLVNode(dataStream: dataStream, offset: offset, size: &size) {
            nodes.append(node)
            offset += size
        }
        return nodes
    }

    /// Intialize a TLVNode object from a data stream
    ///
    /// - Parameters:
    ///   - dataStream: The serialized data stream, in TLV encoding
    ///   - offset: The location from which parsing of the data stream starts
    ///   - size: Upon return, the number of bytes that the node occupies
    private init?(dataStream: Data, offset: Int, size: inout Int) {
        // A node must have at least 3 bytes
        guard offset < dataStream.count - 3 else { return nil }

        // The number of bytes that `tag` occupies
        let m = dataStream[offset] & 0x1F == 0x1F ?
            2 + dataStream[(offset + 1)...].prefix(10).prefix(while: { $0 & 0x80 == 0x80 }).count : 1

        // The number of bytes that `length` occupies
        let n = dataStream[offset + m] & 0x80 == 0x80 ? Int(dataStream[offset + m] & 0x7f) : 1
        guard n <= 3 else { return nil }

        self.tag           = Data(dataStream[offset ..< (offset + m)])
        self.length        = dataStream[(offset + m) ..< (offset + m + n)].map { Int($0) }.reduce(0) { result, element in result * 0x100 + element }
        self.value         = Data(dataStream[(offset + m + n) ..< (offset + m + n + length)])
        self.isConstructed = dataStream[offset] & 0x20 == 0x20

        size = m + n + length
        if self.isConstructed {
            var childOffset = 0
            var childNodeSize = 0

            while let childNode = TLVNode(dataStream: self.value, offset: childOffset, size: &childNodeSize) {
                self.children.append(childNode)
                childOffset += childNodeSize
            }
        }
    }

    private func generateDescription(indentation: Int) -> String {
        return "\(String(repeating: " ", count: indentation))\(tag as NSData) \(length) \(value as NSData)\n"
            + children.map { $0.generateDescription(indentation: indentation + 4) }.joined()
    }

    // Use this property when you need to quickly dump something to the debug console
    override var description: String {
        return self.generateDescription(indentation: 0)
    }

    // A more detailed view on the properties of the current instance
    // Does not include child nodes.
    override var debugDescription: String {
        return """
        TAG         = \(tag as NSData)
        LENGTH      = \(length)
        VALUE       = \(value as NSData)
        CONSTRUCTED = \(isConstructed)
        """
    }
}

View Controller

import Cocoa

class ViewController: NSViewController {
    @objc var tlvNodes: [TLVNode]!

    override func viewDidLoad() {
        super.viewDidLoad()

        let data = Data(bytes:
            [   0xe1,0x35,
                0x9f,0x1e,0x08,0x31,0x36,0x30,0x32,0x31,0x34,0x33,0x37,
                0xef,0x12,
                0xdf,0x0d,0x08,0x4d,0x30,0x30,0x30,0x2d,0x4d,0x50,0x49,
                0xdf,0x7f,0x04,0x31,0x2d,0x32,0x32,
                0xef,0x14,
                0xdf,0x0d,0x0b,0x4d,0x30,0x30,0x30,0x2d,0x54,0x45,0x53,0x54,0x4f,0x53,
                0xdf,0x7f,0x03,0x36,0x2d,0x35,
                // A repeat of the data above
                0xe1,0x35,
                0x9f,0x1e,0x08,0x31,0x36,0x30,0x32,0x31,0x34,0x33,0x37,
                0xef,0x12,
                0xdf,0x0d,0x08,0x4d,0x30,0x30,0x30,0x2d,0x4d,0x50,0x49,
                0xdf,0x7f,0x04,0x31,0x2d,0x32,0x32,
                0xef,0x14,
                0xdf,0x0d,0x0b,0x4d,0x30,0x30,0x30,0x2d,0x54,0x45,0x53,0x54,0x4f,0x53,
                0xdf,0x7f,0x03,0x36,0x2d,0x35
            ])

        // The Tree Controller won't know when we assign `tlvNode` to
        // an entirely new object. So we need to give it a notification
        let nodes = TLVNode.create(from: data)
        self.willChangeValue(forKey: "tlvNodes")
        self.tlvNodes = nodes
        self.didChangeValue(forKey: "tlvNodes")
    }
}

Настройка построителя интерфейса

Мы будем использовать привязки Cocoa, так какзаполнение Outline View вручную может быть довольно утомительным (см. мой другой ответ ), и ваш пример снимка экрана выглядит так, как будто вы уже движетесь в этом направлении.Предостережение: хотя привязка к какао очень удобна, ее следует рассматривать как сложную тему, так как ее довольно сложно устранить.На вашей раскадровке:

  1. Из библиотеки объектов справа добавьте Tree Controller к вашей сцене
  2. Выберите Tree Controller в инспекторе Атрибутов,set Children = children
  3. Перетащите контурный вид и настройте его с 3 столбцами.Мы назовем их Tag, Length и Value

Set the Children keypath for the Tree Controller

Откройте Инспектор привязок , для 5 выделенных объектов,установите их привязки следующим образом:

| IB Object       | Property          | Bind To         | Controller Key  | Model Key Path           |
|-----------------|-------------------|-----------------|-----------------|--------------------------|
| Tree Controller | Controller Array  | View Controller |                 | self.tlvNodes            |
| Outline View    | Content           | Tree Controller | arrangedObjects |                          |
| Table View Cell | Value             | Table Cell View |                 | objectValue.displayTag   |
| (Tag column)    |                   |                 |                 |                          |
| Table View Cell | Value             | Table Cell View |                 | objectValue.length       |
| (Length column) |                   |                 |                 |                          |
| Table View Cell | Value             | Table Cell View |                 | objectValue.displayValue |
| (Value column)  |                   |                 |                 |                          |

set Bindings for the highlighted objects in the scene hierarchy

Результат:

Showing multiple root nodes in an outline view

...