sourcecamAxonPlugin::Dict.fan

//
// Copyright (c) 2009, SkyFoundry LLC
// All Rights Reserved
//
// History:
//   22 Dec 09  Brian Frank  Creation
//

**
** Dict is a map of name/value pairs which is consistently models Rows,
** grid meta-data, and name/value object literals.  Dict is characterized by:
**   - names must match `Etc.isTagName` rules
**   - values must be one of the built-in scalar types (see `Kind`)
**   - get '[]' access returns null if name not found
**   - trap '->' access throws exception if name not found
**
** Also see `Etc.emptyDict`, `Etc.makeDict`.
**
@Js
const mixin Dict
{
  **
  ** Return if the there are no name/value pairs
  **
  abstract Bool isEmpty()

  **
  ** Get the value for the given name or 'def' is not mapped
  **
  @Operator
  abstract Obj? get(Str name, Obj? def := null)

  **
  ** Return true if the given name is mapped to a non-null value.
  **
  abstract Bool has(Str name)

  **
  ** Return true if the given name is not mapped to a non-null value.
  **
  abstract Bool missing(Str name)

  **
  ** Iterate through the name/value pairs
  **
  abstract Void each(|Obj? val, Str name| f)

  **
  ** Get the value mapped by the given name.  If it is not
  ** mapped to a non-null value, then throw an UnknownNameErr.
  **
  override abstract Obj? trap(Str name, Obj?[]? args := null)

  **
  ** Get the 'id' tag as a Ref or raise CastErr/UnknownNameErr
  **
  Ref id()
  {
    id := get("id", null)
    if (id == null) throw UnknownNameErr("id")
    return id
  }

  **
  ** Deprecated - use `dis`
  **
  @Deprecated Str toDis(Str def := "???") { dis(null, def) }

  **
  ** Get display string for dict or the given tag.  If 'name'
  ** is null, then return display text for the entire dict
  ** using `Etc.dictToDis`.  If 'name' is non-null then format
  ** the tag value using its appropiate 'toLocale' method.  If
  ** 'name' is not defined by this dict, then return 'def'.
  **
  virtual Str? dis(Str? name := null, Str? def := "")
  {
    // if name is null
    if (name == null) return Etc.dictToDis(this, def)

    // get the value, if null return the def
    val := get(name)
    if (val == null) return def

    // fallback to Kind to get a suitable default display value
    return Kind.fromType(val.typeof).valToDis(val)
  }
}

@Js
internal const class EmptyDict : Dict
{
  static const EmptyDict val := EmptyDict()
  override Bool isEmpty() { true }
  override Obj? get(Str key, Obj? def := null) { def }
  override Bool has(Str name) { false }
  override Bool missing(Str name) { true }
  override Void each(|Obj?, Str| f) {}
  override Str toStr() { "{}" }
  override Obj? trap(Str n, Obj?[]? a := null) { throw UnknownNameErr(n) }
}

@NoDoc
@Js
const class MapDict : Dict
{
  new make(Str:Obj? map) { this.map = map.ro }
  const Str:Obj? map
  override Bool isEmpty() { map.isEmpty }
  override Obj? get(Str n, Obj? def := null) { map.get(n, def) }
  override Bool has(Str n) { map[n] != null }
  override Bool missing(Str n) { map[n] == null }
  override Void each(|Obj?, Str| f) { map.each(f) }
  override Str toStr() { ZincWriter.tagsToStr(this) }
  override Obj? trap(Str n, Obj?[]? a := null)
  {
    v := map[n]
    if (v != null) return v
    throw UnknownNameErr(n)
  }
}