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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2006, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
// $Id$
package scala.xml.dtd;
import ContentModel.ElemName ;
import scala.util.automata._ ;
/** validate children and/or attributes of an element
* exceptions are created but not thrown.
*/
class ElementValidator() extends Function1[Node,Boolean] {
var exc: List[ValidationException] = Nil;
protected var contentModel: ContentModel = _;
protected var dfa: DetWordAutom[ElemName] = _;
protected var adecls: List[AttrDecl] = _;
/** set content model, enabling element validation */
def setContentModel(cm:ContentModel) = {
contentModel = cm; cm match {
case ELEMENTS( r ) =>
val nfa = ContentModel.Translator.automatonFrom(r, 1);
dfa = new SubsetConstruction(nfa).determinize;
case _ =>
dfa = null;
}
}
def getContentModel = contentModel;
/** set meta data, enabling attribute validation */
def setMetaData(adecls: List[AttrDecl]) =
this.adecls = adecls;
def getIterator(nodes: Seq[Node], skipPCDATA: Boolean): Iterator[ElemName] =
nodes.toList
.filter { x => x match {
case y:SpecialNode => y match {
case a:Atom[_] if (a.data.isInstanceOf[String] &&
a.data.asInstanceOf[String].trim.length == 0 ) =>
false; // always skip all-whitespace nodes
case _ =>
!skipPCDATA
}
case _ =>
x.namespace eq null
}}
. map { x => ElemName(x.label) }
. elements;
/** check attributes, return true if md corresponds to attribute declarations in adecls.
*/
def check(md: MetaData): Boolean = {
//Console.println("checking md = "+md);
//Console.println("adecls = "+adecls);
//@todo other exceptions
import MakeValidationException._;
val len: Int = exc.length;
var j = 0;
var ok = new scala.collection.mutable.BitSet(adecls.length);
def find(Key:String): AttrDecl = {
var attr: AttrDecl = null;
val jt = adecls.elements; while(j < adecls.length) {
jt.next match {
case a @ AttrDecl(Key, _, _) => attr = a; ok += j; j = adecls.length;
case _ => j = j + 1;
}
}
attr
}
val it = md.elements; while(it.hasNext) {
val attr = it.next;
//Console.println("attr:"+attr);
j = 0;
find(attr.key) match {
case null =>
//Console.println("exc");
exc = fromUndefinedAttribute( attr.key ) :: exc;
case AttrDecl(_, tpe, DEFAULT(true, fixedValue)) if(attr.value != fixedValue) =>
exc = fromFixedAttribute( attr.key, fixedValue, attr.value.toString) :: exc;
case s =>
//Console.println("s: "+s);
}
}
//Console.println("so far:"+(exc.length == len));
//val missing = ok.toSet( false ); FIXME: it doesn't seem to be used anywhere
j = 0; var kt = adecls.elements; while(kt.hasNext) {
kt.next match {
case AttrDecl(key, tpe, REQUIRED) if !ok(j) =>
exc = fromMissingAttribute( key, tpe ) :: exc;
j = j + 1;
case _ =>
j = j + 1;
}
}
//Console.println("finish:"+(exc.length == len));
(exc.length == len) //- true if no new exception
}
/** check children, return true if conform to content model
* @pre contentModel != null
*/
def check(nodes: Seq[Node]): Boolean = contentModel match {
case ANY => true ;
case EMPTY => !getIterator(nodes, false).hasNext
case PCDATA => !getIterator(nodes, true).hasNext;
case MIXED(ContentModel.Alt(branches @ _*)) => //@todo
val j = exc.length;
def find(Key: String): Boolean = {
var res = false;
val jt = branches.elements;
while(jt.hasNext && !res)
jt.next match { // !!! check for match translation problem
case ContentModel.Letter(ElemName(Key)) => res = true;
case _ =>
}
res
}
var it = getIterator(nodes, true); while(it.hasNext) {
var label = it.next.name;
if(!find(label)) {
exc = MakeValidationException.fromUndefinedElement(label) :: exc;
}
}
(exc.length == j) //- true if no new exception
case _:ELEMENTS =>
var q = 0;
val it = getIterator(nodes, false);
//Console.println("it empty from the start? "+(!it.hasNext));
while( it.hasNext ) {
val e = it.next;
dfa.delta(q).get(e) match {
case Some(p) => q = p;
case _ => throw ValidationException("element "+e+" not allowed here")
}
//Console.println("q now " + q);
}
dfa.isFinal(q) //- true if arrived in final state
}
/** applies various validations - accumulates error messages in exc
* @todo: fail on first error, ignore other errors (rearranging conditions)
*/
def apply(n: Node): Boolean = {
//- ? check children
var res = (null == contentModel) || check( n.child );
//- ? check attributes
res = ((null == adecls) || check( n.attributes )) && res;
res
}
}
|