// // Copyright (c) 2021, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 09 Aug 2021 Matthew Giannini Creation // ** ** Base class for ASN.1 collection types. ** abstract const class AsnColl : AsnObj { ////////////////////////////////////////////////////////////////////////// // Construction ////////////////////////////////////////////////////////////////////////// protected new make(AsnTag[] tags, Obj val) : super(tags, toItems(val)) { } @NoDoc static AsnItem[] toItems(Obj val) { items := AsnItem[,] if (val is Map) { m := (Str:AsnObj)val m.each |v, k| { items.add(AsnItem(v, k)) } } else { arr := (List)val if (arr.size > 0 && arr[0].typeof.fits(AsnItem#)) items = val else arr.each { items.add(AsnItem(it)) } } return items } ** Get a [collection builder]`AsnCollBuilder` static AsnCollBuilder builder() { AsnCollBuilder() } ////////////////////////////////////////////////////////////////////////// // AsnColl ////////////////////////////////////////////////////////////////////////// ** Is this a 'SEQUENCE' Bool isSeq() { univTag == AsnTag.univSeq } ** Is this a 'SET' Bool isSet() { univTag == AsnTag.univSet } ** Get the raw `AsnObj` values in the collection AsnObj[] vals() { items.map { it.val } } @NoDoc AsnItem[] items() { val } ** Get the number of items in the collection Int size() { items.size } ** Is the collection empty Bool isEmpty() { items.isEmpty } ** Get an item value from the collection. ** - If key is a `sys::Str` then get the named item. ** - If key is an `sys::Int`, then get the item at that zero-based index. AsnObj? get(Obj key) { if (key is Int) return items.getSafe(key)?.val else if (key is Str) return items.find { it.name == name }?.val throw ArgErr("invalid key type: ${key.typeof}") } ////////////////////////////////////////////////////////////////////////// // AsnObj ////////////////////////////////////////////////////////////////////////// protected override Str valStr() { buf := StrBuf().add("{\n") indent := 2 items.each |AsnItem item| { buf.add("".padl(indent)) if (item.name != null) buf.add("${item.name}: ") if (item.val is AsnColl) { collStr := item.val.toStr collStr.splitLines.each |line, i| { if (i==0) buf.add(line) else buf.add("".padl(indent)).add(line) buf.add("\n") } } else buf.add(item.val.toStr).add("\n") } buf.add("}") return buf.toStr } } ************************************************************************** ** AsnItem ************************************************************************** ** An item in an ASN.1 collection. An item has a value, and an optional name ** associated with that value. When comparing items, only the values are ** compared; the name is ignored. final const class AsnItem { new make(AsnObj val, Str? name := null) { this.name = name this.val = val } const Str? name const AsnObj val override Int hash() { val.hash } override Bool equals(Obj? obj) { if (obj == null) return false that := obj as AsnItem if (that == null) return false // name is *not* considered for equality purposes return this.val == that.val } } ************************************************************************** ** AsnSeq ************************************************************************** ** ** Models an ASN.1 'SEQUENCE' ** const class AsnSeq : AsnColl { protected new makeUniv(Obj val) : this.make([AsnTag.univSeq], val) { } protected new make(AsnTag[] tags, Obj val) : super(tags, toItems(val)) { if (univTag != AsnTag.univSeq) throw ArgErr("Not a sequence: $tags") } } ************************************************************************** ** AsnSet ************************************************************************** ** ** Models an ASN.1 'SET' ** const class AsnSet : AsnColl { protected new makeUniv(Obj val) : this.make([AsnTag.univSet], val) { if (univTag != AsnTag.univSet) throw ArgErr("Not a set: $tags") } protected new make(AsnTag[] tags, Obj val) : super(tags, toItems(val)) { } } ************************************************************************** ** AsnCollBuilder ************************************************************************** ** ** `AsnColl` builder. ** class AsnCollBuilder { new make() { } private AsnItem[] items := [,] ** Convenience to add an `AsnItem` with the given value and name This add(AsnObj val, Str? name := null) { item(AsnItem(val, name)) } ** Add an `AsnItem` to the collection This item(AsnItem item) { items.add(item) return this } ** Build an ASN.1 sequence AsnColl toSeq(AsnTag? tag := null) { Asn.tag(tag).seq(items) } ** Build an ASN.1 set AsnColl toSet(AsnTag? tag := null) { Asn.tag(tag).set(items) } }