aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/util/Attachment.scala
blob: 20facfd975446f87c3c5661abc8f7ad5d6af203d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package dotty.tools.dotc.util

/** A class inheriting from Attachment.Container supports
 *  adding, removing and lookup of attachments. Attachments are typed key/value pairs.
 */
object Attachment {
  import Property.Key

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

    /** 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)
    }

    /** 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[_])
      extends LinkSource

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

    final def pushAttachment[V](key: Key[V], value: V): Unit = {
      assert(!getAttachment(key).isDefined, s"duplicate attachment for key $key")
      next = new Link(key, value, next)
    }
  }
}