summaryrefslogtreecommitdiff
path: root/sources/scala/xml/parsing/ValidatingMarkupHandler.scala
blob: 4ae195511ca88b75de7de36aa52ff7088f9ad599 (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
package scala.xml.parsing ;

import scala.xml.dtd._ ;
import scala.util.logging.Logged ;

abstract class ValidatingMarkupHandler extends MarkupHandler with Logged {

  var rootLabel:String = _;
  var qStack: List[Int] = Nil;
  var qCurrent: Int = -1;

  var declStack: List[ElemDecl] = Nil;
  var declCurrent: ElemDecl = null;

  final override val isValidating = true;

  override def log(msg:String) = {};

  /*
  override def checkChildren(pos:int, pre: String, label:String,ns:NodeSeq): Unit = {
    Console.println("checkChildren()");
    val decl = lookupElemDecl(label);
    // @todo: nice error message
    val res = decl.contentModel.validate(ns);
    Console.println("res = "+res);
    if(!res)
      error("invalid!");
  }
  */

  override def endDTD(n:String) = {
    rootLabel = n;
  }
  override def elemStart(pos: int, pre: String, label: String, attrs: MetaData, scope:NamespaceBinding): Unit = {

    def advanceDFA(dm:DFAContentModel) = {
      val trans = dm.dfa.delta(qCurrent);
      log("advanceDFA(dm): "+dm);
      log("advanceDFA(trans): "+trans);
      trans.get(ContentModel.ElemName(label)) match {
          case Some(qNew) => qCurrent = qNew
          case _          => reportValidationError(pos, "DTD says, wrong element, expected one of "+trans.keys.toString);
        }
    }
    // advance in current automaton
    log("[qCurrent = "+qCurrent+" visiting "+label+"]");

    if(qCurrent == -1) { // root
      log("  checking root");
      if(label != rootLabel)
        reportValidationError(pos, "this element should be "+rootLabel);
    } else {
      log("  checking node");
      declCurrent.contentModel match {
        case ANY         =>

          case EMPTY       =>
            reportValidationError(pos, "DTD says, no elems, no text allowed here");
        case PCDATA      =>
          reportValidationError(pos, "DTD says, no elements allowed here");

        case m@MIXED(r)    => advanceDFA(m);
        case e@ELEMENTS(r) => advanceDFA(e);
      }
    }
    // push state, decl
    qStack    =    qCurrent :: qStack;
    declStack = declCurrent :: declStack;

    declCurrent = lookupElemDecl(label);
    qCurrent = 0;
    log("  done  now");
  }

  override def elemEnd(pos: int, pre: String, label: String): Unit = {
    log("  elemEnd");
    qCurrent = qStack.head;
    qStack   = qStack.tail;
    declCurrent = declStack.head;
    declStack   = declStack.tail;
    log("    qCurrent now"+qCurrent);
    log("    declCurrent now"+declCurrent);
  }

  final override def elemDecl(name: String, cmstr: String): Unit =
    decls = ElemDecl( name,  ContentModel.parse(cmstr)) :: decls;

  final override def attListDecl(name: String, attList: List[AttrDecl]): Unit =
    decls = AttListDecl( name, attList) :: decls;

  final override def unparsedEntityDecl(name: String, extID: ExternalID, notat: String): Unit = {
    decls =  UnparsedEntityDecl( name, extID, notat) :: decls;
  }

  final override def notationDecl(notat: String, extID: ExternalID): Unit =
    decls = NotationDecl( notat, extID) :: decls;

  final override def peReference(name: String): Unit =
    decls = PEReference( name ) :: decls;

  /** report a syntax error */
  def reportValidationError(pos: Int, str: String): Unit;

}