package examples;

import java.io.*;
import java.util.*;
import java.net.*;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.xml.sax.SAXException;

import oracle.xml.parser.v2.*;

public class BasicTagTLV extends TagLibraryValidator {
   public BasicTagTLV() {
        super();
    }

   public synchronized ValidationMessage[] validate(
            String prefix, String uri, PageData page) {
     Vector validationErrors = new Vector();
     XMLDocument xdoc = createXMLDocmentFromStream(page.getInputStream());
     NodeFilter nf = new TagElemFilter();
     NodeIterator nodei = 
          xdoc.createNodeIterator(((Node)xdoc.getDocumentElement()),
          NodeFilter.SHOW_ELEMENT, nf, true);
     try {
       XMLNode xn = (XMLNode)nodei.nextNode();
       while (xn != null) {
         if (xn.getLocalName().equals("parent")) { // check parent tag
           // parent tag can't have both attrs at same time
           if (!checkParentExclusiveAttrs(xn)) 
             validationErrors.addElement(vmFromString(xn,
                     "Supply either directory or file, not both"));
         }
         if (xn.getLocalName().equals("child")) { // check child tag
           // child tag must be nested in parent tag
           if (!xn.getParentNode().getLocalName().equals("parent")){
             validationErrors.addElement(vmFromString(xn,
                    "Child tag must be nested in parent tag"));
           }
           //child tag's limit value is numeric and in range
           if (!checkChildLimitInRange(xn)) {
             validationErrors.addElement(vmFromString(xn,
                 "Child tag's limit must be a valid number > 0 and < 100"));
           }
         }
         xn = (XMLNode)nodei.nextNode();
       }
     } catch (DOMException d) {
        System.err.println(d.getMessage());
     }
     return vmFromVector(validationErrors);
   }

   private XMLDocument createXMLDocmentFromStream(InputStream is){
     try {
       DOMParser parser = new DOMParser();
       parser.setErrorStream(System.out);
       parser.setValidationMode(DOMParser.NONVALIDATING);
       parser.showWarnings(true);
       parser.setPreserveWhitespace(true);
       parser.retainCDATASection(true);
       parser.parse(is);
       return (parser.getDocument());
     } catch (Exception e) {
       // should never get an invalid document from jsp engine
      System.err.println("Exception is " + e.getMessage());
     }
     return null; 
   }

   // constructs a ValidationMessage[] from a ValidationMessage Vector
   static ValidationMessage[] vmFromVector(Vector v) {
     ValidationMessage[] vm = new ValidationMessage[v.size()];
     for (int i = 0; i < vm.length; i++)
       vm[i] = (ValidationMessage) v.get(i);
     return vm;
   }

   // constructs a ValidationMessage from a node and error string
   static ValidationMessage vmFromString(XMLNode node, String message) {
     return new ValidationMessage(getElementId(node), message);
   }

   static String getElementId (XMLNode e) {
     NamedNodeMap nnm = e.getAttributes();
     if (nnm != null) {
       return nnm.getNamedItem("jsp:id").getNodeValue();
     }
     return null;
   }

   static boolean checkParentExclusiveAttrs(XMLNode node){
     NamedNodeMap nnm = node.getAttributes();
     if (nnm != null) {
       if ( nnm.getNamedItem("directory") != null &&
         nnm.getNamedItem("file") != null) {
         return false;
       }
     }
     return true;      
   }

   static boolean checkChildLimitInRange (XMLNode node) {
     NamedNodeMap nnm = node.getAttributes();
     if (nnm != null) {
       if ( nnm.getNamedItem("limit") != null) {
         try {
           Integer in = new Integer(nnm.getNamedItem("limit").getNodeValue());
           int i = in.intValue(); 
           if (i < 1 ||  i > 99) 
              return false;
         } catch (java.lang.NumberFormatException n ) {
           return false;
         }
       }
     }
     return true;     
   }

}  

// NodeFilter class returns elements to check
class TagElemFilter implements NodeFilter {
   public short acceptNode(Node node) {
     // specify the tags you want to check
     if ((node.getLocalName() != null) && 
        (node.getLocalName().equals("parent") || 
        (node.getLocalName().equals("child")))) {
       return NodeFilter.FILTER_ACCEPT;
     }
     return NodeFilter.FILTER_SKIP; 
   }
}


