package dom;

import util.Arguments;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import org.apache.xerces.dom.TextImpl;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMCount {

  private static final String
  DEFAULT_PARSER_NAME = "dom.wrappers.DOMParser";
  private static boolean setValidation    = false; //defaults
  private static boolean setNameSpaces    = true;
  private static boolean setSchemaSupport = true;
  private static boolean setDeferredDOM   = true;
   
  private long elements; /** Elements. */
  private long attributes; /** Attributes. */
  private long characters; /** Characters. */
  private long ignorableWhitespace; /** Ignorable whitespace. */

  /** Counts the resulting document tree. */
  public static void count(String uri) {
   try {
    DOMParserWrapper parser = new dom.wrappers.DOMParser();
    DOMCount counter = new DOMCount();
    long before = System.currentTimeMillis();
    parser.setFeature( "http://apache.org/xml/features/dom/defer-node-expansion", setDeferredDOM );
    parser.setFeature( "http://xml.org/sax/features/validation", setValidation);
    parser.setFeature( "http://xml.org/sax/features/namespaces", setNameSpaces);
    parser.setFeature( "http://apache.org/xml/features/validation/schema",setSchemaSupport);

    Document document = parser.parse(uri);
    counter.traverse(document);
    long after = System.currentTimeMillis();
    counter.printResults(uri, after - before);
   } catch (org.xml.sax.SAXParseException spe) {
   } catch (org.xml.sax.SAXNotRecognizedException ex ){
   } catch (org.xml.sax.SAXNotSupportedException ex ){
   } catch (org.xml.sax.SAXException se) {
    if (se.getException() != null)
             se.getException().printStackTrace(System.err);
    else se.printStackTrace(System.err);
   } catch (Exception e) {
    e.printStackTrace(System.err);
   }
  }

  /** Traverses the specified node, recursively. */
  public void traverse(Node node) {
    if (node == null) return; // is there anything to do?

    switch (node.getNodeType()) { 
     case Node.DOCUMENT_NODE: // print document
      elements            = 0;
      attributes          = 0;
      characters          = 0;
      ignorableWhitespace = 0;
      traverse(((Document)node).getDocumentElement());
      break;

     case Node.ELEMENT_NODE: {// print element with attributes
      elements++;
      NamedNodeMap attrs = node.getAttributes();
      if (attrs != null) attributes += attrs.getLength();
      NodeList children = node.getChildNodes();
      if (children != null) {
        int len = children.getLength();
        for (int i = 0; i < len; i++) traverse(children.item(i));
      }
     } break;

     case Node.ENTITY_REFERENCE_NODE: {
      NodeList children = node.getChildNodes();
      if (children != null) {
        int len = children.getLength();
        for (int i = 0; i < len; i++) traverse(children.item(i));
      }
     } break;

      case Node.CDATA_SECTION_NODE: // print text
       characters += node.getNodeValue().length();
       break;

      case Node.TEXT_NODE:
       if (node instanceof TextImpl) {
         if (((TextImpl)node).isIgnorableWhitespace())
           ignorableWhitespace += node.getNodeValue().length();
         else characters += node.getNodeValue().length();
       } else characters += node.getNodeValue().length();
       break;
    }
  }

  public void printResults(String uri, long time) {
  // filename.xml: 631 ms (4 elems, 0 attrs, 78 spaces, 0 chars)
    System.out.println (uri+":"+time+"time ms ("
     +elements+" elems, "+attributes+" attrs, "
     +ignorableWhitespace+" spaces, "+characters+" chars)");
  }

  public static void main(String argv[]) {
    count(argv[0] ); //count uri
  }
}
