diff options
Diffstat (limited to 'src/library/scala/xml/dtd/ElementValidator.scala')
-rw-r--r-- | src/library/scala/xml/dtd/ElementValidator.scala | 166 |
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 + } +} |