summaryrefslogtreecommitdiffstats
path: root/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala
blob: 6c58d857c8679505a26413452862b133cb80fa04 (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.di.componentgraph.core

import com.yahoo.component.ComponentId
import com.yahoo.container.di.ConfigKeyT
import com.yahoo.container.di.componentgraph.Provider
import com.google.inject.Key
import Node._



/**
 * @author tonytv
 * @author gjoranv
 */
abstract class Node(val componentId: ComponentId) {

  def instanceKey: Key[AnyRef]

  var instance : Option[AnyRef] = None

  var componentsToInject = List[Node]()

  /**
   * The components actually used by this node.
   * Consist of a subset of the injected nodes + subset of the global nodes.
   */
  def usedComponents: List[Node]

  protected def newInstance() : AnyRef

  def newOrCachedInstance() : AnyRef = {
    component(
      instance.getOrElse {
        instance = Some(newInstance())
        instance.get
      })
  }

  private def component(instance: AnyRef) = instance match {
    case provider: Provider[_] => provider.get().asInstanceOf[AnyRef]
    case other                 => other
  }

  def configKeys: Set[ConfigKeyT]

  def inject(component: Node) {
    componentsToInject ::= component
  }

  def injectAll(componentNodes: Traversable[ComponentNode]) {
    componentNodes.foreach(inject(_))
  }

  def instanceType: Class[_ <: AnyRef]
  def componentType: Class[_ <: AnyRef]

  override def equals(other: Any) = {
    other match {
      case that: Node =>
        getClass == that.getClass &&
          componentId == that.componentId &&
          instanceType == that.instanceType &&
          equalEdges(usedComponents, that.usedComponents)
      case _ => false
    }
  }

  def label: String

  def idAndType = {
    val className = instanceType.getName

    if (className == componentId.getName) s"'$componentId'"
    else s"'$componentId' of type '$className'"
  }

}

object Node {

  def equalEdges(edges1: List[AnyRef], edges2: List[AnyRef]): Boolean = {
    def compare(objects: (AnyRef, AnyRef)): Boolean = {
      objects match {
        case (edge1: Node, edge2: Node) => equalEdge(edge1, edge2)
        case (o1, o2) => o1 == o2
      }
    }

    def equalEdge(e1: Node, e2: Node) =  e1.componentId == e2.componentId

    (edges1 zip edges2).forall(compare)
  }

  /**
   * @param identityObject  The identifying object that makes the Node unique
   */
  private[componentgraph]
  def syntheticComponentId(className: String, identityObject: AnyRef, namespace: ComponentId) = {
    val name = className + "_" + System.identityHashCode(identityObject)
    ComponentId.fromString(name).nestInNamespace(namespace)
  }


  def packageName(componentClass: Class[_]) = {
    def nullIfNotFound(index : Int) = if (index == -1) 0 else index

    val fullClassName = componentClass.getName
    fullClassName.substring(0, nullIfNotFound(fullClassName.lastIndexOf(".")))
  }
}