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

dotty.tools.dotc.util.Attachment.scala Maven / Gradle / Ivy

There is a newer version: 3.6.4-RC1-bin-20241220-0bfa1af-NIGHTLY
Show newest version
package dotty.tools.dotc
package util

import core.Contexts.Context

/** A class inheriting from Attachment.Container supports
 *  adding, removing and lookup of attachments. Attachments are typed key/value pairs.
 *
 *  Attachments whose key is an instance of `StickyKey` will be kept when the attachments
 *  are copied using `withAttachmentsFrom`.
 */
object Attachment {
  import Property.{Key, StickyKey}

  /** An implementation trait for attachments.
   *  Clients should inherit from Container instead.
   */
  trait LinkSource {
    private[Attachment] var next: Link[?] | Null

    /** Optionally get attachment corresponding to `key` */
    final def getAttachment[V](key: Key[V]): Option[V] = {
      val nx = next
      if (nx == null) None
      else if (nx.key eq key) Some(nx.value.asInstanceOf[V])
      else nx.getAttachment[V](key)
    }

    /** Does an attachment corresponding to `key` exist? */
    final def hasAttachment[V](key: Key[V]): Boolean = {
      val nx = next
      if (nx == null) false
      else if (nx.key eq key) true
      else nx.hasAttachment[V](key)
    }

    /** The attachment corresponding to `key`.
     *  @throws NoSuchElementException  if no attachment with key exists
     */
    final def attachment[V](key: Key[V]): V = {
      val nx = next
      if (nx == null) throw new NoSuchElementException
      else if (nx.key eq key) nx.value.asInstanceOf[V]
      else nx.attachment(key)
    }

    /** The attachment corresponding to `key`, or `default`
     *  if no attachment with `key` exists.
     */
    final def attachmentOrElse[V](key: Key[V], default: V): V = {
      val nx = next
      if (nx == null) default
      else if (nx.key eq key) nx.value.asInstanceOf[V]
      else nx.attachmentOrElse(key, default)
    }

    /** Add attachment with given `key` and `value`.
     *  @return  Optionally, the old attachment with given `key` if one existed before.
     *  The new attachment is added at the position of the old one, or at the end
     *  if no attachment with same `key` existed.
     */
    final def putAttachment[V](key: Key[V], value: V): Option[V] = {
      val nx = next
      if (nx == null) {
        next = new Link(key, value, null)
        None
      }
      else if (nx.key eq key) {
        next = new Link(key, value, nx.next)
        Some(nx.value.asInstanceOf[V])
      }
      else nx.putAttachment(key, value)
    }

    /** Remove attachment with given `key`, if it exists.
     *  @return  Optionally, the removed attachment with given `key` if one existed before.
     */
    final def removeAttachment[V](key: Key[V]): Option[V] = {
      val nx = next
      if (nx == null)
        None
      else if (nx.key eq key) {
        next = nx.next
        Some(nx.value.asInstanceOf[V])
      }
      else nx.removeAttachment(key)
    }

    /** The list of all keys and values attached to this container. */
    final def allAttachments: List[(Key[?], Any)] = {
      val nx = next
      if (nx == null) Nil else (nx.key, nx.value) :: nx.allAttachments
    }
  }

  /** A private, concrete implementation class linking attachments.
   */
  private[Attachment] class Link[+V](val key: Key[V], val value: V, var next: Link[?] | Null)
      extends LinkSource

  /** A trait for objects that can contain attachments */
  trait Container extends LinkSource {
    private[Attachment] var next: Link[?] | Null = null

    /** Copy the sticky attachments from `container` to this container. */
    final def withAttachmentsFrom(container: Container): this.type = {
      var current: Link[?] | Null = container.next
      while (current != null) {
        if (current.key.isInstanceOf[StickyKey[?]]) putAttachment(current.key, current.value)
        current = current.next
      }
      this
    }

    def withAttachment[V](key: Key[V], value: V): this.type = {
      putAttachment(key, value)
      this
    }

    final def pushAttachment[V](key: Key[V], value: V)(using ctx: Context): Unit = {
      assert(!hasAttachment(key) || ctx.base.errorsToBeReported, s"duplicate attachment for key $key")
      next = new Link(key, value, next)
    }

    final def removeAllAttachments(): Unit =
      next = null
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy