summaryrefslogtreecommitdiff
path: root/src/library/scala/xml/dtd/ElementValidator.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/scala/xml/dtd/ElementValidator.scala')
-rw-r--r--src/library/scala/xml/dtd/ElementValidator.scala166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/library/scala/xml/dtd/ElementValidator.scala b/src/library/scala/xml/dtd/ElementValidator.scala
new file mode 100644
index 0000000000..52ba82cb54
--- /dev/null
+++ b/src/library/scala/xml/dtd/ElementValidator.scala
@@ -0,0 +1,166 @@
+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[String] if (a.data.asInstanceOf[String].trim().length() == 0 ) =>
+ false; // always skip all-whitespace nodes
+
+ case _ =>
+ !skipPCDATA
+
+ }
+ case _ =>
+ x.namespace == 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.set(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 );
+ 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 {
+ 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
+ }
+}