All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.kaitai.struct.format.ParseUtils.scala Maven / Gradle / Ivy

package io.kaitai.struct.format

import io.kaitai.struct.Utils

object ParseUtils {
  def ensureLegalKeys(src: Map[String, Any], legalKeys: Set[String], path: List[String], where: Option[String] = None) = {
    src.keys.foreach((key) =>
      if (!key.startsWith("-") && !legalKeys.contains(key)) {
        val msg = where match {
          case Some(ctx) => s"invalid key found in $ctx, allowed"
          case None => "unknown key found, expected"
        }
        throw new YAMLParseException(s"$msg: ${legalKeys.toList.sorted.mkString(", ")}", path ++ List(key))
      }
    )
  }

  // Unfortunately, these can't be parameterized with [T] - for some reason, `case Some(value: T)`
  // doesn't seem to work properly, checking that value is indeed of type T. Besides, there are some
  // variations when we need to do implicit type conversions.

  def getValueStr(src: Map[String, Any], field: String, path: List[String]): String = {
    src.get(field) match {
      case Some(value) =>
        asStr(value, path ++ List(field))
      case None =>
        throw YAMLParseException.noKey(path ++ List(field))
    }
  }

  def getValueMapStrStr(src: Map[String, Any], field: String, path: List[String]): Map[String, String] = {
    src.get(field) match {
      case Some(value) =>
        asMapStrStr(value, path ++ List(field))
      case None =>
        throw YAMLParseException.noKey(path ++ List(field))
    }
  }

  def getOptValueStr(src: Map[String, Any], field: String, path: List[String]): Option[String] = {
    src.get(field) match {
      case None =>
        None
      case Some(value) =>
        Some(asStr(value, path ++ List(field)))
      case unknown =>
        throw YAMLParseException.badType("string", unknown, path ++ List(field))
    }
  }

  def getOptValueBool(src: Map[String, Any], field: String, path: List[String]): Option[Boolean] = {
    src.get(field) match {
      case None =>
        None
      case Some(value: Boolean) =>
        Some(value)
      case unknown =>
        throw YAMLParseException.badType("boolean", unknown, path ++ List(field))
    }
  }

  def getOptValueInt(src: Map[String, Any], field: String, path: List[String]): Option[Int] = {
    src.get(field) match {
      case None =>
        None
      case Some(value: Int) =>
        Some(value)
      case unknown =>
        throw YAMLParseException.badType("int", unknown, path ++ List(field))
    }
  }

  def getValueIdentifier(src: Map[String, Any], idx: Int, entityName: String, path: List[String]): Identifier = {
    getOptValueStr(src, "id", path) match {
      case Some(idStr) =>
        try {
          NamedIdentifier(idStr)
        } catch {
          case _: InvalidIdentifier =>
            throw YAMLParseException.invalidId(idStr, entityName, path ++ List("id"))
        }
      case None => NumberedIdentifier(idx)
    }
  }

  /**
    * Gets a list of T-typed values from a given YAML map's key "field",
    * reporting errors accurately and ensuring type safety.
    *
    * Ensures that this is indeed a valid list, and each list's element
    * is converted using `convertFunc` function. Lack of "field" key
    * results in an empty list.
    *
    * @param src YAML map to get list from
    * @param field key name in YAML map, value expected to be a list
    * @param convertFunc function that gets element of Any type, expected
    *                    to check its type and do the conversion
    * @param path path used to report YAML errors
    * @tparam T type of list's elements
    * @return
    */
  def getList[T](
                  src: Map[String, Any],
                  field: String,
                  convertFunc: ((Any, List[String]) => (T)),
                  path: List[String]
  ): List[T] = {
    val pathField = path ++ List(field)
    src.get(field) match {
      case Some(srcList: List[Any]) =>
        srcList.zipWithIndex.map { case (element, idx) =>
          convertFunc(element, pathField ++ List(idx.toString))
        }
      case None =>
        List()
      case unknown =>
        throw YAMLParseException.badType("array", unknown, pathField)
    }
  }

  /**
    * Gets a list of strings from a given YAML map's key "field",
    * reporting errors accurately and ensuring type safety.
    * @param src YAML map to get list from
    * @param field key name in YAML map, value expected to be a list
    * @param path path used to report YAML errors
    * @return list of strings from YAML map
    */
  def getListStr(src: Map[String, Any], field: String, path: List[String]): List[String] =
    getList[String](src, field, asStr, path)

  def asStr(src: Any, path: List[String]): String = {
    src match {
      case str: String =>
        str
      case n: Int =>
        n.toString
      case n: Long =>
        n.toString
      case n: Double =>
        n.toString
      case n: Boolean =>
        n.toString
      case unknown =>
        throw YAMLParseException.badType("string", unknown, path)
    }
  }

  def asLong(src: Any, path: List[String]): Long = {
    src match {
      case n: Long =>
        n
      case n: Int =>
        n
      case str: String =>
        // Generally should not happen, but when the data comes from JavaScript,
        // all object keys are forced to be strings.
        try {
          Utils.strToLong(str)
        } catch {
          case ex: MatchError =>
            throw new YAMLParseException(s"unable to parse `$str` as int", path)
        }
      case unknown =>
        throw YAMLParseException.badType("int", unknown, path)
    }
  }

  def asMap(src: Any, path: List[String]): Map[Any, Any] = {
    src match {
      case srcMap: Map[Any, Any] =>
        srcMap
      case unknown =>
        throw YAMLParseException.badType("map", unknown, path)
    }
  }

  def asMapStr(src: Any, path: List[String]): Map[String, Any] =
    anyMapToStrMap(asMap(src, path), path)

  def asMapStrStr(src: Any, path: List[String]): Map[String, String] =
    anyMapToStrStrMap(asMap(src, path), path)

  def anyMapToStrMap(anyMap: Map[Any, Any], path: List[String]): Map[String, Any] = {
    anyMap.map { case (key, value) =>
      val keyStr = asStr(key, path)
      keyStr -> value
    }
  }

  def anyMapToStrStrMap(anyMap: Map[Any, Any], path: List[String]): Map[String, String] = {
    anyMap.map { case (key, value) =>
      val keyStr = asStr(key, path)
      val valueStr = asStr(value, path ++ List(keyStr))
      keyStr -> valueStr
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy