summaryrefslogtreecommitdiff
path: root/src/library/scala/xml/factory/NodeFactory.scala
blob: 9a00548c2c23b48abbbfd870f26908bf6c5a9eda (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
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |                                         **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id$


package scala.xml.factory;


trait NodeFactory[A <: Node] {

  val ignoreComments  = false;
  val ignoreProcInstr = false;

  /* default behaviour is to use hash-consing */
  val cache = new collection.mutable.HashMap[Int, List[A]]();

  protected def create(pre: String, name: String, attrs: MetaData, scope: NamespaceBinding, children:Seq[Node]): A;

  protected def construct(hash: Int, old:List[A], pre: String, name: String, attrSeq:MetaData, scope: NamespaceBinding, children:Seq[Node]): A = {
    val el = create(pre, name, attrSeq, scope, children);
    cache.update( hash, el::old );
    el
  }

  /** faster equality, because */
  def eqElements(ch1:Seq[Node], ch2:Seq[Node]): Boolean = {
    (ch1.length == ch2.length) && {
      val it1 = ch1.elements;
      val it2 = ch2.elements;
      var res = true;
      while(res && it1.hasNext) {
        res = it1.next.eq(it2.next);
      }
      res
    }
  }

  def nodeEquals(n: Node, pre: String, name: String, attrSeq:MetaData, scope: NamespaceBinding, children:Seq[Node]) = (
    (n.prefix == pre)
    &&(n.label == name)
    &&(n.attributes == attrSeq)
  // scope??
    &&(eqElements(n.child,children)));

  def makeNode(pre: String, name: String, attrSeq:MetaData, scpe: NamespaceBinding, children:Seq[Node]): A = {
    //Console.println("NodeFactory::makeNode("+pre+","+name+","+attrSeq+","+scpe+","+children+")");
    val hash    = Utility.hashCode( pre, name, attrSeq.hashCode(), scpe.hashCode(), children ) ;
    cache.get( hash ) match {
      case Some(list) => // find structurally equal
        val it     = list.elements;
        val lookup = it.find { x => nodeEquals(x, pre, name, attrSeq, scpe, children) };
        lookup match {
          case Some(x) =>
            //Console.println("[cache hit !]"+x);
            x; // return cached elem
          case _       => construct(hash, list, pre, name, attrSeq, scpe, children);
        }
      case _          => construct(hash, Nil, pre, name, attrSeq, scpe, children)
    }
  }

  def makeText(s: String) =
    Text( s );

  def makeComment(s: String): Seq[Comment] =
    if(ignoreComments) Nil else List(Comment( s ));

  def makeProcInstr(t: String, s: String): Seq[ProcInstr] =
    if(ignoreProcInstr) Nil else List(ProcInstr(t, s));

}