summaryrefslogblamecommitdiff
path: root/sources/scala/xml/dtd/ElementValidator.scala
blob: 1304ff4123305d7b897283f9db4d564031648ed2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                              


                                                     

                                                          




                                                         
 








                                                              

   








                                                                              
                                                                                         






















                                                                                          
                                




                                                                                 
       
          
     































                                                                                        

   






































                                                                           
                                   





                                                                                     

   











                                                                              
 
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;
    }
  }

  /** 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 match { case t => t.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) :: 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
  }
}