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

com.ossuminc.riddl.passes.resolve.ReferenceMap.scala Maven / Gradle / Ivy

/*
 * Copyright 2019 Ossum, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package com.ossuminc.riddl.passes.resolve

import com.ossuminc.riddl.language.AST.*
import com.ossuminc.riddl.language.Messages
import com.ossuminc.riddl.utils.StringHelpers

import scala.collection.mutable
import scala.reflect.{ClassTag, classTag}

/** The primary output of the [[ResolutionPass]]. It provides a mapping from a reference to the referenced definition.
  * This is useful for fast resolution during validation and other Passes because the resolution logic doesn't need
  * to be exercised again.
  * @param messages
  * A message accumulator for collecting messages when member functions are invoked
  */
case class ReferenceMap(messages: Messages.Accumulator) {

  private case class Key(path: String, in: NamedValue) {
    override def toString: String = s"(k=$path,v=${in.identify})"
  }

  private val map: mutable.HashMap[Key, NamedValue] = mutable.HashMap.empty

  override def toString: String = {
    StringHelpers.toPrettyString(this)
  }

  def size: Int = map.size

  def add[T <: NamedValue: ClassTag](ref: Reference[T], parent: Parent, definition: T): Unit = {
    add(ref.pathId.format, parent, definition)
  }

  def add[T <: NamedValue: ClassTag](pathId: PathIdentifier, parent: Parent, definition: T): Unit = {
    add(pathId.format, parent, definition)
  }

  private def add[T <: NamedValue: ClassTag](pathId: String, parent: Parent, definition: T): Unit = {
    val key = Key(pathId, parent)
    val expected = classTag[T].runtimeClass
    val actual = definition.getClass
    require(
      expected.isAssignableFrom(actual),
      s"referenced ${actual.getSimpleName} is not assignable to expected ${expected.getSimpleName}"
    )
    map.update(key, definition)
  }

  def definitionOf[T <: NamedValue: ClassTag](pathId: String): Option[T] = {
    val potentials = map.find(key => key._1.path == pathId)
    potentials match
      case None => Option.empty[T]
      case Some((key, definition)) =>
        val klass = classTag[T].runtimeClass
        if definition.getClass == klass then Some(definition.asInstanceOf[T]) else Option.empty[T]
  }

  def definitionOf[T <: NamedValue: ClassTag](pid: PathIdentifier, parent: Parent): Option[T] = {
    val key = Key(pid.format, parent)
    val value = map.get(key)
    value match
      case None =>
        None
      case Some(x: T) =>
        Some(x)
      case Some(x) =>
        val className = classTag[T].runtimeClass.getSimpleName
        messages.addError(pid.loc, s"Path Id '${pid.format} found ${x.identify} but a $className was expected")
        None
  }

  def definitionOf[T <: Definition: ClassTag](ref: Reference[T], parent: Parent): Option[T] = {
    definitionOf[T](ref.pathId, parent)
  }
}

object ReferenceMap {
  val empty: ReferenceMap = ReferenceMap(Messages.Accumulator.empty)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy