import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FileNotFoundException;
import java.io.BufferedReader;
import java.io.StringWriter;

import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;

import java.math.BigDecimal;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;


import org.xml.sax.InputSource;
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;  
import org.xml.sax.EntityResolver;  

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.DOMException;
import org.w3c.dom.Text;

import org.w3c.dom.ls.LSSerializer;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;

// $Id: verif.java,v 1.52 2008/03/18 18:49:19 ylafon Exp $

public class verif implements EntityResolver {

    private static String LOGNS = 
	    "http://www.w3.org/2002/ws/databinding/log/6/09/";
    private static String EXAMPLENS =
	    "http://www.w3.org/2002/ws/databinding/examples/6/09/";
    private static String CALLTAG = "call";
    private static String SOAP11NS = 
	    "http://schemas.xmlsoap.org/soap/envelope/";
    private static String SOAP12NS = 
	    "http://www.w3.org/2003/05/soap-envelope";
	
    private static String EXAMPLEURI = 
	    "http://www.w3.org/2002/ws/databinding/examples/6/09/";

    Document                         logdoc          = null;
    Document                         report          = null;
    NodeList                         calls           = null;
    int                              totalTestsCount = 0;
    String                           exampleuri      = null;
    Hashtable<String,String>         extable         = null;
    Hashtable<String,Vector<String>> fbasict         = null;
    Hashtable<String,Vector<String>> fadvancedt      = null;

    Hashtable<Node,String>           nodeschanged    = null;
    Hashtable<Node,Node[]>           nodesremoved    = null;
    Hashtable<Node,Node>             nodesreplaced   = null;
    Hashtable<Element,Attr>          attrsremoved    = null;
    File                             outputfile      = null;
    File                             templatef       = null;
    
    String                           toolkitname     = null;
    String                           toolkitversion  = null;
    String                           toolkitmapping  = null;     
    String                           toolkitlink     = null;
    String                           toolkitbinding  = null;

    boolean                          doctored        = false;

    public static final boolean debug = false;

    int passedbasic, passedadvanced, passedpending, failedbasic, failedadvanced, failedpending;
    int totalbasic, totaladvanced, totalpending;

    // hack to cache dtds
    public InputSource resolveEntity(String publicId, String systemId) {
	InputSource is = new InputSource();
	if (systemId.equals("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")) {
	    try {
		is.setSystemId((new File("dtdcache/xhtml1-transitional.dtd")).toURI().toString());
	    } catch (Exception ex) {
		ex.printStackTrace();
		return null;
	    }
	} else {
	    is.setSystemId(systemId);
	}
	return is;
    }

    /***
     *** check different datatypes values
     ***/

    void checkHexBinary(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	if (!reqval.equals(resval)) {
	    // a better check would be to do lower/uppercase check on [a-f] only, but
	    // as the input is strict.
	    if (reqval.equalsIgnoreCase(resval)) {
		System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
		nodeschanged.put(reqnode, reqval);
		// we modify only the request node
		reqnode.setNodeValue(resval);
	    }
	}
    }

    void checkBoolean(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	if (!reqval.equals(resval)) {
	    if (((reqval.equals("0") || reqval.equals("false")) && 
		 (resval.equals("false") || resval.equals("0"))) || 
		((reqval.equals("1") || reqval.equals("true")) && 
		 (resval.equals("true") || resval.equals("1")))) {
		System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
		nodeschanged.put(reqnode, reqval);
		// we modify only the request node
		reqnode.setNodeValue(resval);
	    }
	}	
    }

    void checkDecimal(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	String mreqval, mresval;
	// trimming + for java...
	mreqval = reqval.trim();
	if (reqval.charAt(0) == '+') {
	    mreqval = mreqval.substring(1);
	}
	
	mresval = resval.trim();
	// if empty string, fail (at worst string equality will work)
	if ("".equals(mresval)) 
	    return;
	if (resval.charAt(0) == '+') {
	    mresval = mresval.substring(1);
	}
	BigDecimal reqbi = new BigDecimal(mreqval);
	BigDecimal resbi = new BigDecimal(mresval);
	System.err.println("-- bigdecimal: "+reqbi+" / "+resbi);
	// equals on bigdecimal won't work
	if ((reqbi.compareTo(resbi) == 0) && !reqval.equals(resval)) {
	    System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
	    nodeschanged.put(reqnode, reqval);
	    // we modify only the request node
	    reqnode.setNodeValue(resval);
	}
    }

    void checkInteger(Node reqnode, Node resnode, String testinstance) {
	checkDecimal(reqnode, resnode, testinstance);
    }

    void checkFloat(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	float reqf=0;
	float resf=42;
	try {
	    reqf = Float.parseFloat(reqval);
	} catch (NumberFormatException nex) {
	    if ("NaN".equals(reqval)) {
		reqf = Float.NaN;
	    } else if ("INF".equals(reqval)) {
		reqf = Float.POSITIVE_INFINITY;
	    } else if ("-INF".equals(reqval)) {
		reqf = Float.NEGATIVE_INFINITY;
	    }
	}
	try {
	    resf = Float.parseFloat(resval);
	} catch (NumberFormatException nex) {
	    if ("NaN".equals(resval)) {
		resf = Float.NaN;
	    } else if ("INF".equals(resval)) {
		resf = Float.POSITIVE_INFINITY;
	    } else if ("-INF".equals(resval)) {
		resf = Float.NEGATIVE_INFINITY;
	    }
	}
	if ((reqf == resf) && !reqval.equals(resval)) {
	    System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
	    nodeschanged.put(reqnode, reqval);
	    // we modify only the request node
	    reqnode.setNodeValue(resval);
	}
    }

    void checkDouble(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	double reqd=0.42;
	double resd=42.0;
	try {
	    reqd = Double.parseDouble(reqval);
	} catch (NumberFormatException nex) {
	    if ("NaN".equals(reqval)) {
		reqd = Double.NaN;
	    } else if ("INF".equals(reqval)) {
		reqd = Double.POSITIVE_INFINITY;
	    } else if ("-INF".equals(reqval)) {
		reqd = Double.NEGATIVE_INFINITY;
	    }
	}
	try {
	    resd = Double.parseDouble(resval);
	} catch (NumberFormatException nex) {
	    if ("NaN".equals(resval)) {
		resd = Double.NaN;
	    } else if ("INF".equals(resval)) {
		resd = Double.POSITIVE_INFINITY;
	    } else if ("-INF".equals(resval)) {
		resd = Double.NEGATIVE_INFINITY;
	    }
	}
	if ((reqd == resd) && !reqval.equals(resval)) {
	    System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
	    nodeschanged.put(reqnode, reqval);
	    // we modify only the request node
	    reqnode.setNodeValue(resval);
	}
    }

    void checkQName(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	Node reqpnode, respnode;
	// get parent element
	if (reqnode.getParentNode().getNodeType() == Node.ATTRIBUTE_NODE) {
	    reqpnode = ((Attr) reqnode.getParentNode()).getOwnerElement();
	} else {
	    reqpnode = reqnode.getParentNode();
	}

	if (resnode.getParentNode().getNodeType() == Node.ATTRIBUTE_NODE) {
	    respnode = ((Attr) resnode.getParentNode()).getOwnerElement();
	} else {
	    respnode = resnode.getParentNode();
	}

	String reqns  = reqpnode.getNamespaceURI();
	String resns  = respnode.getNamespaceURI();
	String reqp   = reqpnode.getPrefix();
	String resp   = respnode.getPrefix();

	if (debug) {
	    System.err.println("QNAME: res : NS["+reqns+"] prefix["+reqp+"] value["+reqval);
	    System.err.println("       resp: NS["+resns+"] prefix["+resp+"] value["+resval);
	}

	if ((reqns == null && resns == null) || (reqns != null && reqns.equals(resns))) {
	    // two namespaces equals
	    if (!reqval.equals(resval)) {
		// two different values, check prefix
		String _res, _req;
		if (reqp != null && reqval.startsWith(reqp+':')) {
		    _req = reqval.substring(reqp.length()+1);
		} else {
		    _req = reqval;
		}
		if (resp != null && resval.startsWith(resp+':')) {
		    _res = resval.substring(resp.length()+1);
		} else {
		    _res = resval;
		}
		if (_res.equals(_req)) {
		    System.err.println("*** Modified value in "+testinstance+": "+reqval+
				       " -> "+resval);
		    nodeschanged.put(reqnode, reqval);
		    // we modify only the request node
		    reqnode.setNodeValue(resval);
		}
	    }
	}
    }

    void checkAll(Node reqnode, Node resnode, String testinstance) {
	NodeList reqnl = reqnode.getChildNodes();
	NodeList resnl = resnode.getChildNodes();
	Node reqchild = null;
	Node reschild;
	String reqelname;
	String reselname;
	boolean ok = true;
	int i,j, reqsize, ressize;
	Vector<Node> na;
	reqsize = reqnl.getLength();
	ressize = resnl.getLength();
	j = 0;
	for (i=0; i<reqsize; i++) {
	    if (reqnl.item(i).getNodeType() == Node.ELEMENT_NODE) {
		j++;
	    }
	}
	for (i=0; i<ressize; i++) {
	    if (resnl.item(i).getNodeType() == Node.ELEMENT_NODE) {
		j--;
	    }
	}
		
	if (j!=0) {
	    System.err.println("-- size mismatch: "+j);
	    return;
	}
	na = new Vector<Node>();
	for (i=0; i < ressize; i++) {
	    ok = false;
	    reschild = resnl.item(i);
	    if (reschild.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }
	    reselname = reschild.getNodeName();
	    for (j=0; j < reqsize; j++) {
		reqchild = reqnl.item(j);
		if (reqchild.getNodeType() != Node.ELEMENT_NODE) {
		    continue;
		}
		reqelname = reqchild.getNodeName();
		if (checkName(reqchild, reschild)) {
		    ok = true;
		    break;
		}
	    }
	    if (!ok) {
		break;
	    }
	    na.add(reqchild.cloneNode(true));
	}
	if (ok) {
	    Node parent = reqnode.getParentNode();
	    Node clonednode = reqnode.cloneNode(false);
	    Node narray[] = new Node[2];
	    narray[0] = reqnode;
	    narray[1] = null;
	    nodesreplaced.put(clonednode, reqnode);
	    Enumeration<Node> e = na.elements();
	    while (e.hasMoreElements()) {
		clonednode.appendChild(e.nextElement());
	    }
	    parent.replaceChild(clonednode, reqnode);
	    System.err.println("*** Modified node in "+testinstance);
	}
    }


    void checkDateTime(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	// if equals, no need to check more!
	if ((reqval != null) && (reqval.equals(resval))) {
	    return;
	}
	XMLGregorianCalendar reqcal, rescal;
	try {
	    DatatypeFactory df = DatatypeFactory.newInstance();
	    reqcal = df.newXMLGregorianCalendar(reqval);
	    rescal = df.newXMLGregorianCalendar(resval);
	    if (reqcal.equals(rescal)) {
		System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
		nodeschanged.put(reqnode, reqval);
		// we modify only the request node
		reqnode.setNodeValue(resval);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	    // if something fails here, the error will be caught later...
	}
    }

    void checkDuration(Node reqnode, Node resnode, String testinstance) {
	String reqval = reqnode.getNodeValue();
	String resval = resnode.getNodeValue();
	// if equals, no need to check more!
	if ((reqval != null) && (reqval.equals(resval))) {
	    return;
	}
	Duration reqdur, resdur;
	try {
	    DatatypeFactory df = DatatypeFactory.newInstance();
	    reqdur = df.newDuration(reqval);
	    resdur = df.newDuration(resval);
	    if (reqdur.equals(resdur)) {
		System.err.println("*** Modified value in "+testinstance+": "+reqval+" -> "+resval);
		nodeschanged.put(reqnode, reqval);
		// we modify only the request node
		reqnode.setNodeValue(resval);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	    // if something fails here, the error will be caught later...
	}
    }

    Node[] extractElementNode(Element requestnode, Element responsenode, 
			      String ns, String element) {
	NodeList reqnl, resnl;
	reqnl = requestnode.getElementsByTagNameNS(ns, element);
	resnl = responsenode.getElementsByTagNameNS(ns, element);
	if ((reqnl == null) || (resnl == null) || (reqnl.getLength() != resnl.getLength())) {
	    System.err.println("******* "+element+" children numbers invalid");
	    return null;
	}
	Node reqv = reqnl.item(0);
	Node resv = resnl.item(0);
	if ((reqv == null) || (resv == null)) {
	    System.err.println("******* "+element+" children list does not match");
	    return null;
	}
	Node[] n = new Node[2];
	n[0]     = reqv;
	n[1]     = resv;
	return n;
    }

    Node[] extractTextNodesForElement(Element requestnode, Element responsenode, 
				      String ns, String element) {
	NodeList reqnl, resnl;
	reqnl = requestnode.getElementsByTagNameNS(ns, element);
	resnl = responsenode.getElementsByTagNameNS(ns, element);
	if ((reqnl == null) || (resnl == null) || (reqnl.getLength() != resnl.getLength())) {
	    System.err.println("******* "+element+" children numbers invalid");
	    return null;
	}
	Node reqv = reqnl.item(0).getFirstChild();
	Node resv = resnl.item(0).getFirstChild();
	if ((reqv == null) || (resv == null) || (reqv.getNodeType() != Node.TEXT_NODE) ||
	    (resv.getNodeType() != Node.TEXT_NODE)) {
	    System.err.println("******* "+element+" children not TEXT_NODES");
	    return null;
	}
	Node[] n = new Node[2];
	n[0]     = reqv;
	n[1]     = resv;
	return n;
    }

    Node[] extractTextNodesForAttribute(Element requestnode, Element responsenode, 
					String ns, String element, String attribute) {
	NodeList reqnl, resnl;
	reqnl = requestnode.getElementsByTagNameNS(ns, element);
	resnl = responsenode.getElementsByTagNameNS(ns, element);
	if ((reqnl == null) || (resnl == null) || (reqnl.getLength() != resnl.getLength())) {
	    System.err.println("******* "+element+" children numbers invalid");
	    return null;
	}

	Node reqv;
	Node resv;
        try {
	    reqv = ((Element)reqnl.item(0)).getAttributeNode(attribute).getFirstChild();
	    resv = ((Element)resnl.item(0)).getAttributeNode(attribute).getFirstChild();
	} catch(Exception ex) {
	    System.err.println("******* "+element+"/"+attribute+" null pointer");
	    return null;
        }
	if ((reqv == null) || (resv == null) || (reqv.getNodeType() != Node.TEXT_NODE) ||
	    (resv.getNodeType() != Node.TEXT_NODE)) {
	    System.err.println("******* "+element+"/"+attribute+" children not TEXT_NODES");
	    return null;
	}
	Node[] n = new Node[2];
	n[0]     = reqv;
	n[1]     = resv;
	return n;
    }

    void checkAttributeDefault01(Element reqnode, Element resnode, String testinstance) {
	// Here we know that we have a defaulted attribute value not present in the request
	// and it may appear in the reply.
	// so if the right value is in the response, just remove it.
	NodeList nl = resnode.getElementsByTagNameNS(EXAMPLENS, "attributeDefault");
	if (nl==null || nl.getLength() != 1) {
	    System.err.println("******* no attributeDefault element found");
	    return;
	}
	Element e = (Element)nl.item(0);
	Attr resv = e.getAttributeNode("defaultedValue");
	if (resv == null || resv.getFirstChild().getNodeType() != Node.TEXT_NODE) {
	    System.err.println("******* no defaultedValue attribute found");
	    return;
	}
	Node txt = resv.getFirstChild();
	if ("theDefaultValue".equals(((Text) txt).getData().trim())) {
	    attrsremoved.put(e, resv);
	    e.removeAttributeNode(resv);
	}
    }

    void checkGlobalElementDefault(Element reqnode, Element resnode, String testinstance) {

	NodeList reqnl, resnl;
	reqnl = reqnode.getElementsByTagNameNS(EXAMPLENS, "globalElementDefault");
	resnl = resnode.getElementsByTagNameNS(EXAMPLENS, "globalElementDefault");
	if (reqnl==null || resnl==null || reqnl.getLength() != 1 || resnl.getLength() != 1 ) {
	    System.err.println("******* no element of wrong number of elements in globalElementDefault found");
	    return;
	}
	Node reqv = reqnl.item(0);
	Node resv = resnl.item(0);
	Node reqvt = reqv.getFirstChild();
	Node resvt = resv.getFirstChild();

	if (reqvt == null) {
	    if (resvt == null) {
		return;
	    }
	    if (resvt.getNodeType() == Node.TEXT_NODE ) {
		if ("theDefaultValue".equals(((Text) resvt).getData().trim())) {
		    Node n[] = new Node[2];
		    n[0] = resvt;
		    n[1] = null;
		    nodesremoved.put(resv, n); 
		}
	    }
	} else {
	    if (resvt == null) {
		if (reqvt.getNodeType() == Node.TEXT_NODE ) {
		    if ("theDefaultValue".equals(((Text) reqvt).getData().trim())) {
			Node n[] = new Node[2];
			n[0] = reqvt;
			n[1] = null;
			nodesremoved.put(reqv, n); 
		    }
		}
	    }
	}
    }

    void checkLocalElementDefault(Element reqnode, Element resnode, String testinstance) {

	NodeList reqnl, resnl, treqnl, tresnl;
	reqnl = reqnode.getElementsByTagNameNS(EXAMPLENS, "localElementDefault");
	resnl = resnode.getElementsByTagNameNS(EXAMPLENS, "localElementDefault");
	if (reqnl==null || resnl==null || reqnl.getLength() != 1 || resnl.getLength() != 1 ) {
	    System.err.println("******* no element of wrong number of elements in localElementDefault found");
	    return;
	}
	treqnl = reqnode.getElementsByTagNameNS(EXAMPLENS, "text");
	tresnl = resnode.getElementsByTagNameNS(EXAMPLENS, "text");
	Node reqv  = null;
	Node resv  = null;
	Node reqvt = null;
	Node resvt = null;
	
	if (treqnl != null ) {
	    reqv = treqnl.item(0);
	    if (reqv != null) {
		reqvt = reqv.getFirstChild();
	    }
	}
	if (tresnl != null) {
	    resv = tresnl.item(0);
	    if (resv != null) {
		resvt = resv.getFirstChild();	    
	    }
	}
	
	// two <ex:text> missing
	if ( (treqnl == null) && (tresnl == null) ) {
	    return;
	}
	// two <ex:text> values missing
	if ( (reqvt == null) && ( resvt == null) ) {
	    return;
	}
	// now the weird case... no <ex:text> element, or empty one, or ok with a "theDefaultValue" value.
	// trim as necessary both.

	// <ex:text /> in req
	if (reqv != null) {
	    if (reqvt == null) {
		// removing <ex:text /> from the request
		System.err.println("******* LocalElementDefault -> removing <ex:text/> form request");
		Node n[] = new Node[2];
		Node parent = reqnl.item(0);
		n[0] = reqv;
		n[1] = null;
		nodesremoved.put(parent, n); 
		parent.removeChild(reqv);
	    } else {
		// <ex:text> with default value in req
		if (reqvt.getNodeType() == Node.TEXT_NODE ) {
		    if ("theDefaultValue".equals(((Text) reqvt).getData().trim())) {
			System.err.println("******* LocalElementDefault -> removing <ex:text>theDefaultValue</ex:text> form request");
			Node n[] = new Node[2];
			Node parent = reqnl.item(0);
			n[0] = reqv;
			n[1] = null;
			nodesremoved.put(parent, n); 
			parent.removeChild(reqv);
		    }
		}
	    }
	}

	// <ex:text /> in res
	if (resv != null) {
	    if (resvt == null) {
		// removing <ex:text /> from the response
		System.err.println("******* LocalElementDefault -> removing <ex:text/> form response");
		Node n[] = new Node[2];
		Node parent = resnl.item(0);
		n[0] = resv;
		n[1] = null;
		nodesremoved.put(parent, n); 
		parent.removeChild(resv);
	    } else {
		// <ex:text> with default value in res
		if (resvt.getNodeType() == Node.TEXT_NODE ) {
		    if ("theDefaultValue".equals(((Text) resvt).getData().trim())) {
			System.err.println("******* LocalElementDefault -> removing <ex:text>theDefaultValue</ex:text> form response");
			Node n[] = new Node[2];
			Node parent = resnl.item(0);
			n[0] = resv;
			n[1] = null;
			nodesremoved.put(parent, n); 
			parent.removeChild(resv);
		    }
		}
	    }
	}

    }

    /**
     * As if everything else was not already ugly enough... this will be used to align
     * some tests results in some weird cases
     */
    void preProcess(String testname, String testinstance, Element requestnode, Element responsenode) {
	// reset the list of node changed and their original value...
	nodeschanged  = new Hashtable<Node,String> ();
	nodesremoved  = new Hashtable<Node,Node[]> ();
	nodesreplaced = new Hashtable<Node,Node> ();
	attrsremoved  = new Hashtable<Element,Attr> ();
	Node[] tn;

	// done on a case-by-case basis, so random order! (and need to do that in a better way)
	// so far...
	// BooleanElement
	// BooleanAttribute
	// HexBinaryElement
	// HexBinaryAttribute
	// IntegerElement
	// IntegerAttribute
	// IntElement
	// IntAttribute
	// LongElement
	// LongAttribute
	// NegativeIntegerElement
	// NegativeIntegerAttribute
	// NonNegativeIntegerElement
	// NonNegativeIntegerAttribute
	// NonPositiveIntegerElement
	// NonPositiveIntegerAttribute
	// PositiveIntegerElement
	// PositiveIntegerAttribute
	// ShortElement
	// ShortAttribute
	// UnsignedByteElement
	// UnsignedByteAttribute
	// UnsignedIntElement
	// UnsignedIntAttribute
	// UnsignedLongElement
	// UnsignedLongAttribute
	// UnsignedShortElement
	// UnsignedShortAttribute
	// DecimalElement
	// DecimalAttribute
	// FloatElement
	// FloatAttribute
	// DoubleElement
	// DoubleAttribute
	// DateTimeElement
	// DateTimeAttribute
	// TimeElement
	// TimeAttribute
	// * AttributeDefault01
	// DurationElement
	// DurationAttribute
	// QNameElement
	// QNameAttribute
	// DoubleEnumeration
	// FloatEnumeration
	// ComplexTypeAll
	// GlobalElementAll
	if (testname.equals("BooleanElement")) {
	    // should work for BooleanElement01, BooleanElement02, BooleanElement03, BooleanElement04
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "booleanElement");
	    if (tn == null) {
		return;
	    }
	    checkBoolean(tn[0], tn[1], testinstance);
	} else if (testname.equals("BooleanAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS,
					      "booleanAttribute","boolean");
	    if (tn == null) {
		return;
	    }
	    checkBoolean(tn[0], tn[1], testinstance);
	} else if (testname.equals("HexBinaryElement")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "hexBinaryElement");
	    if (tn == null) {
		return;
	    }
	    checkHexBinary(tn[0], tn[1], testinstance);
	} else if (testname.equals("HexBinaryAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "hexBinaryAttribute", "hexBinary");
	    if (tn == null) {
		return;
	    }
	    checkHexBinary(tn[0], tn[1], testinstance);
	} else if (testname.equals("IntegerElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "integerElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("IntegerAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "integerAttribute", "integer");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("IntElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "intElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("IntAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "intAttribute", "int");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("LongElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "longElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("LongAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "longAttribute", "long");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NegativeIntegerElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "negativeIntegerElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NegativeIntegerAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "negativeIntegerAttribute", "negativeInteger");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NonNegativeIntegerElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "nonNegativeIntegerElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NonNegativeIntegerAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "nonNegativeIntegerAttribute", "nonNegativeInteger");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NonPositiveIntegerElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "nonPositiveIntegerElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("NonPositiveIntegerAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "nonPositiveIntegerAttribute", "nonPositiveInteger");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("PositiveIntegerElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "positiveIntegerElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("PositiveIntegerAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "positiveIntegerAttribute", "positiveInteger");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("ShortElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "shortElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("ShortAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "shortAttribute", "short");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedByteElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "unsignedByteElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedByteAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "unsignedByteAttribute", "unsignedByte");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedIntElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "unsignedIntElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedIntAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "unsignedIntAttribute", "unsignedInt");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedLongElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "unsignedLongElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedLongAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "unsignedLongAttribute", "unsignedLong");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedShortElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "unsignedShortElement");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);
	} else if (testname.equals("UnsignedShortAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "unsignedShortAttribute", "unsignedShort");
	    if (tn == null) {
		return;
	    }
	    checkInteger(tn[0], tn[1], testinstance);

	} else if (testname.equals("DecimalElement")) {
	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "decimalElement");
	    if (tn == null) {
		return;
	    }
	    checkDecimal(tn[0], tn[1], testinstance);
	} else if (testname.equals("DecimalAttribute")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "decimalAttribute", "decimal");
	    if (tn == null) {
		return;
	    }
	    checkDecimal(tn[0], tn[1], testinstance);
	} else if (testname.equals("FloatElement")) {
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "floatElement");
	    if (tn == null) {
		return;
	    }
//	    checkFloat(tn[0], tn[1], testinstance);
	    checkDouble(tn[0], tn[1], testinstance);
	} else if (testname.equals("FloatAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "floatAttribute", "float");
	    if (tn == null) {
		return;
	    }
//	    checkFloat(tn[0], tn[1], testinstance);
	    checkDouble(tn[0], tn[1], testinstance);
	} else if (testname.equals("FloatEnumerationType")) {
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "floatEnumerationType");
	    if (tn == null) {
		return;
	    }
	    checkDouble(tn[0], tn[1], testinstance);
//	    checkFloat(tn[0], tn[1], testinstance);
	} else if (testname.equals("DoubleElement")) {    
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "doubleElement");
	    if (tn == null) {
		return;
	    }
	    checkDouble(tn[0], tn[1], testinstance);
	} else if (testname.equals("DoubleAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "doubleAttribute", "double");
	    if (tn == null) {
		return;
	    }
	    checkDouble(tn[0], tn[1], testinstance);
	} else if (testname.equals("DoubleEnumerationType")) {    
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "doubleEnumerationType");
	    if (tn == null) {
		return;
	    }
	    checkDouble(tn[0], tn[1], testinstance);    
	} else if (testname.equals("DateTimeElement")) {    
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "dateTimeElement");
	    if (tn == null) {
		return;
	    }
	    checkDateTime(tn[0], tn[1], testinstance);
	} else if (testname.equals("DateTimeAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "dateTimeAttribute", "dateTime");
	    if (tn == null) {
		return;
	    }
	    checkDateTime(tn[0], tn[1], testinstance);
	} else if (testname.equals("TimeElement")) {    
	// works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "timeElement");
	    if (tn == null) {
		return;
	    }
	    checkDateTime(tn[0], tn[1], testinstance);
	} else if (testname.equals("TimeAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "timeAttribute", "time");
	    if (tn == null) {
		return;
	    }
	    checkDateTime(tn[0], tn[1], testinstance);
	} else if (testinstance.equals("AttributeDefault01")) {
	    // Specific check for default attribute test
	    System.err.println("*** preprocessing "+testinstance);
	    checkAttributeDefault01(requestnode, responsenode, testinstance);
	} else if (testname.equals("GlobalElementDefault")) {
	    // Specific check for default attribute test
	    System.err.println("*** preprocessing "+testinstance);
	    checkGlobalElementDefault(requestnode, responsenode, testinstance);	    
	} else if (testname.equals("LocalElementDefault")) {
	    // Specific check for default attribute test
	    System.err.println("*** preprocessing "+testinstance);
	    checkLocalElementDefault(requestnode, responsenode, testinstance);	    
	} else if (testname.equals("DurationElement")) {    
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "durationElement");
	    if (tn == null) {
		return;
	    }
	    checkDuration(tn[0], tn[1], testinstance);
	} else if (testname.equals("DurationAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "durationAttribute", "duration");
	    if (tn == null) {
		return;
	    }
	    checkDuration(tn[0], tn[1], testinstance);
	} else if (testname.equals("QNameElement")) {    
 	    // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForElement(requestnode, responsenode, EXAMPLENS, "QNameElement");
	    if (tn == null) {
		return;
	    }
 	    checkQName(tn[0], tn[1], testinstance);
	} else if (testname.equals("QNameAttribute")) {
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS, 
					      "QNameAttribute", "QName");
	    if (tn == null) {
		return;
	    }
	    checkQName(tn[0], tn[1], testinstance);
	} else if (testname.equals("ComplexTypeAll")) {
            // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractElementNode(requestnode, responsenode, EXAMPLENS, "complexTypeAll");
	    if (tn == null) {
		return;
	    }
 	    checkAll(tn[0], tn[1], testinstance);
	} else if (testname.equals("GlobalElementAll")) {
            // works for all? check...
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractElementNode(requestnode, responsenode, EXAMPLENS, "globalElementAll");
	    if (tn == null) {
		return;
	    }
 	    checkAll(tn[0], tn[1], testinstance);
	} else if (testname.equals("AttributeRequired")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS,
					      "discount","seasonal");
	    if (tn == null) {
		return;
	    }
	    checkBoolean(tn[0], tn[1], testinstance);
	} else if (testname.equals("AttributeElementNameClash")) {
	    // check applicability
	    System.err.println("*** preprocessing "+testinstance);
	    tn = extractTextNodesForAttribute(requestnode, responsenode, EXAMPLENS,
					      "clientDetails","title");
	    if (tn == null) {
		return;
	    }
	    checkBoolean(tn[0], tn[1], testinstance);
	} else {
	    System.err.println("*** No preprocessing defined for "+testinstance); 
	}
    }
    
    void postProcess() {
	if (nodeschanged != null && !nodeschanged.isEmpty()) {
	    System.err.println("*** reverting equivalence changes done in preProcessing");
	    Enumeration<Node> e = nodeschanged.keys();
	    while (e.hasMoreElements()) {
		Node   n       = e.nextElement();
		String origval = nodeschanged.get(n);
		System.err.println("*** reverting to "+origval);
		n.setNodeValue(origval);
	    }
	}
	if (nodesremoved != null && !nodesremoved.isEmpty()) {
	    System.err.println("*** re-adding nodes removed in preProcessing");
	    Enumeration<Node> e = nodesremoved.keys();
	    while (e.hasMoreElements()) {
		Node n          = e.nextElement();
		Node children[] = nodesremoved.get(n);
		if (children[1] == null) {
		    n.appendChild(children[0]);
		} else {
		    n.insertBefore(children[0], children[1]);
		}
	    }
	}
	if (nodesreplaced != null && !nodesreplaced.isEmpty()) {
	    System.err.println("*** re-adding nodes replaced in preProcessing");
	    Enumeration<Node> e = nodesreplaced.keys();
	    while (e.hasMoreElements()) {
		Node n          = e.nextElement();
		Node original   = nodesreplaced.get(n);
		Node parent     = n.getParentNode();
		parent.replaceChild(original, n);
	    }
	}
	if (attrsremoved != null && !attrsremoved.isEmpty()) {
	    System.err.println("*** re-adding attributes removed in preProcessing");
	    Enumeration<Element> e = attrsremoved.keys();
	    while (e.hasMoreElements()) {
		Element n = e.nextElement();
		Attr    a = attrsremoved.get(n);
		n.setAttributeNode(a);
	    }
	}
    }

    /***
     *** End of processing specific to datatypes in our list of tests
     ***/

    void initTests() {
	InputStream is = null;
	URL tests = null;
	URL test = null;
	String examplefile,testfile,id,classification;
	int i;
	if (exampleuri == null) {
	    examplefile=EXAMPLEURI+"examples.xml";
	} else {
	    examplefile=exampleuri+"examples.xml";
	}
	System.err.println("*** fetching examples.txt to extract the number of tests");
	try {
	    tests = new URL(examplefile);
	} catch (MalformedURLException muex) {
	    muex.printStackTrace();
	    System.exit(1);
	}
	try {
	    is = tests.openStream();
	} catch (IOException ioex) {
	    ioex.printStackTrace();
	    System.exit(1);
	}
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	dbf.setValidating(false);
	dbf.setNamespaceAware(true);
	DocumentBuilder parser = null;
	try { 
	    parser = dbf.newDocumentBuilder(); 
	} catch (Exception ex) {
	    System.err.println("*** FATAL can't build XML parser");
	    System.exit(1);
	}
	BufferedReader bf = new BufferedReader(new InputStreamReader(is));
	InputSource ins = new InputSource(bf);
	Document exdoc = null;
	try {
	    exdoc = parser.parse(ins);
	} catch (org.xml.sax.SAXException saxex) {
	    saxex.printStackTrace();
	    System.exit(1);
	} catch (IOException ioex) {
	    ioex.printStackTrace();
	    System.exit(1);
	}
	NodeList inl = exdoc.getElementsByTagNameNS(EXAMPLENS, "instance");
	totalTestsCount = inl.getLength();

	System.err.println("*** got "+totalTestsCount+" tests total");
	NodeList nl = exdoc.getElementsByTagNameNS(EXAMPLENS, "example");
	extable = new Hashtable<String,String>();
	Document testdoc = null;
	for (i=0; i<nl.getLength(); i++) {
	    String exname = ((Element)nl.item(i)).getAttribute("xml:id");
	    // now go to exampleuri/exname/exname-patterns.xml
	    // and fetch <detected... status>
	    if (exampleuri == null) {
		testfile=EXAMPLEURI+exname+"/"+exname+"-patterns.xml";
	    } else {
	        testfile=exampleuri+exname+"/"+exname+"-patterns.xml";
	    }
	    try {
	        test = new URL(testfile);
	    } catch (MalformedURLException muex) {
		muex.printStackTrace();
		System.exit(1);
	    }
	    try {
		parser = dbf.newDocumentBuilder();
		bf = new BufferedReader(new 
					InputStreamReader(test.openStream()));
		ins = new InputSource(bf);
		testdoc = parser.parse(ins);
	    } catch (org.xml.sax.SAXException saxex) {
		saxex.printStackTrace();
		System.exit(1);
	    } catch (Exception ioex) {
		ioex.printStackTrace();
		System.exit(1);
	    }
	    NodeList tnl = testdoc.getElementsByTagName("detected");
	    if (tnl.getLength() == 0) {
		System.err.println("Error, no info on pattern "+exname);
		System.exit(3);
	    }
	    classification = ((Element)tnl.item(0)).getAttribute("status");
	    extable.put(exname, classification);
	    System.err.println("***** "+exname+" ["+classification+"]");
	}
	// now check the breakdown of all instances
	totalbasic    = 0;
	totaladvanced = 0;
	totalpending  = 0;
	for (i=0; i<inl.getLength(); i++) {
	    Node parent = inl.item(i).getParentNode();
	    // should be an example...
	    id      = ((Element)parent).getAttribute("xml:id");
	    classification = extable.get(id); 
	    if (classification.equals("basic")) {
		totalbasic++;
	    } else if (classification.equals("advanced")) {
		totaladvanced++;
	    } else if (classification.equals("pending")) {
		totalpending++;
	    } else {
		System.err.println("Wrong classification for "+id+": "+classification);
		totalpending++;
	    }
	}
    }

    void readLog(String logfile) {
	File log = new File(logfile);
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	dbf.setValidating(false);
	dbf.setNamespaceAware(true);
	DocumentBuilder parser = null;
	BufferedReader inr = null;
	try {
	    inr = new BufferedReader(new FileReader(log));
	} catch (FileNotFoundException fex) {
	    fex.printStackTrace();
	    System.exit(1);
	}
	InputSource ins = new InputSource(inr);
	try {
	    parser = dbf.newDocumentBuilder();
	    logdoc = parser.parse(ins);
	} catch (org.xml.sax.SAXException saxex) {
	    saxex.printStackTrace();
	    System.exit(1);
	} catch (Exception ioex) {
	    ioex.printStackTrace();
	    System.exit(1);
	}
    }

    void readToolkitInformation(String logfile) {
	File log = new File(logfile);
	File parent = log.getParentFile();
	File toolkitdefs = new File(parent, "toolkit.xml");
	
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	dbf.setValidating(false);
	dbf.setNamespaceAware(false);
	DocumentBuilder parser = null;
	BufferedReader inr = null;
	try {
	    inr = new BufferedReader(new FileReader(toolkitdefs));
	} catch (FileNotFoundException fex) {
	    fex.printStackTrace();
	    System.exit(1);
	}
	InputSource ins = new InputSource(inr);
	Document tinfo = null;
	try {
	    parser = dbf.newDocumentBuilder();
	    tinfo = parser.parse(ins);
	} catch (org.xml.sax.SAXException saxex) {
	    saxex.printStackTrace();
	    System.exit(1);
	} catch (Exception ioex) {
	    ioex.printStackTrace();
	    System.exit(1);
	}
	Element toolkit = tinfo.getDocumentElement();
	toolkitname     = toolkit.getAttribute("name");
	toolkitversion  = toolkit.getAttribute("version");
	toolkitmapping  = toolkit.getAttribute("mapping");
	toolkitlink     = toolkit.getAttribute("link");
	toolkitbinding  = toolkit.getAttribute("binding");

    }
    
    void initReport() {
	report = parseTemplate();
	Element meta = report.createElement("meta");
	meta.setAttribute("name", "version");
	meta.setAttribute("content","$Id: verif.java,v 1.52 2008/03/18 18:49:19 ylafon Exp $");
	
	NodeList n = report.getDocumentElement().getElementsByTagName("style");
	Element e = (Element) n.item(0);
	e.getParentNode().insertBefore(meta,e);
    }

    void reportAddToolkitInfos() {
	// add a title
	Element title = report.createElement("title");
	if (doctored) {
	    title.setAttribute("id", "doctored");
	}
	Text t = report.createTextNode("XML Schema Patterns for Databinding"+
				       " Test Result Page: "+toolkitname+ 
				       " ("+toolkitversion+")");
	title.appendChild(t);
	
	NodeList n = report.getDocumentElement().getElementsByTagName("meta");
	Element meta = (Element) n.item(0);
	meta.getParentNode().insertBefore(title,meta);
	// populate the h1 with the same title
	Element h1 = report.getElementById("firsth1");
	h1.appendChild(t.cloneNode(false));
	// populate the toolkit metadate
	Element e = report.getElementById("toolkitname");
	t = report.createTextNode(toolkitname);
	e.appendChild(t);
	
	e = report.getElementById("toolkitversion");
	t = report.createTextNode(toolkitversion);
	e.appendChild(t);
	
	e = report.getElementById("toolkitmapping");
	t = report.createTextNode(toolkitmapping);
	e.appendChild(t);

	e = report.getElementById("toolkitbinding");
	if (toolkitbinding.equals("")) {
	    t = report.createTextNode("n.a.");
	} else {
	    t = report.createTextNode(toolkitbinding);
	}
	e.appendChild(t);
	
	e = report.getElementById("toolkitlink");
	if (toolkitlink != null) {
	    Element a = report.createElement("a");
	    a.setAttribute("href", toolkitlink);
	    t = report.createTextNode(toolkitlink);
	    a.appendChild(t);
	    e.appendChild(a);
	} else {
	    t = report.createTextNode("n.a.");
	    e.appendChild(t);
	}
	
	
    }

    /***
     *** start of the methods used to check equivalence of XML fragments
     ***/
    
    boolean checkName(Node requested, Node echoed) {
	String       reqname;
	String       resname;
	String       reqnsuri;
	String       resnsuri;
	
	reqname  = requested.getLocalName();
	reqnsuri = requested.getNamespaceURI();
	
	resname  = echoed.getLocalName();
	resnsuri = echoed.getNamespaceURI();
	
	// are local names equals?
	if (!reqname.equals(resname)) {
	    System.err.println("[Difference] <nodename> ("+reqname+") != ("+resname+")");
	    return false;
	}
	// is one namespace null and not the other?
	if (((reqnsuri == null) && (resnsuri != null)) || ((reqnsuri != null) && (resnsuri == null))) {
	    System.err.println("[Difference] <nodensuri> "+resname+" ("+reqnsuri+") != ("+resnsuri+")");
	    return false; 
	}
	// are the two namespace URI different?
	if ((reqnsuri != null) && (resnsuri != null) && (!reqnsuri.equals(resnsuri))) {
	    System.err.println("[Difference] <nodensuri> "+resname+" ("+reqnsuri+") != ("+resnsuri+")");
	    return false; 
	}
	return true;
    }

    boolean checkAttributes(Element requested, Element echoed) {
	NamedNodeMap             reqattrs, respattrs;
	Hashtable<String,String> reqfiltattrs, respfiltattrs;

	reqattrs  = requested.getAttributes();
	respattrs = echoed.getAttributes();
	
	reqfiltattrs = new Hashtable<String,String>();
	respfiltattrs = new Hashtable<String,String>();
	// gather (and filter) attributes
	for(int i=0; i<reqattrs.getLength(); i++) {
	    Attr a = (Attr) reqattrs.item(i);
	    String nsuri = a.getNamespaceURI();
	    String nsname;
	    if (nsuri!= null) {
		// filter out NS declaration
		if (nsuri.equals("http://www.w3.org/2000/xmlns/")) {
		    continue;
		}
		// fitler out also xsi:* declaration
		if (nsuri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
		    continue;
		}
		nsname = nsuri+":"+a.getLocalName();
	    } else {
		nsname = a.getLocalName();
	    }
	    if (debug) {
		System.err.println("--ATTRS: "+nsname);
	    }
	    reqfiltattrs.put(nsname, a.getValue());
	}
	// same for response (but it can be done directly, saving the interim hashtable)
	for(int i=0; i<respattrs.getLength(); i++) {
	    Attr a = (Attr) respattrs.item(i);	    	    
	    String nsuri = a.getNamespaceURI();
	    String nsname;
	    if (nsuri!= null) {
		// filter out NS declaration
		if (nsuri.equals("http://www.w3.org/2000/xmlns/")) {
		    continue;
		}
		// fitler out also xsi:* declaration
		if (nsuri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
		    continue;
		}
		nsname = nsuri+":"+a.getLocalName();
	    } else {
		nsname = a.getLocalName();
	    }
	    if (debug) {
		System.err.println("++ATTRS: "+nsname);
	    }
	    respfiltattrs.put(nsname, a.getValue());
	}  
	// and cross-check... 
	if (reqfiltattrs.size() != respfiltattrs.size()) {
	    System.err.println("[Difference] number of attribute different: "
			       +reqfiltattrs.size()+" / "+ respfiltattrs.size());
	    return false;
	}
	Enumeration<String> e;
	e = reqfiltattrs.keys();
	while(e.hasMoreElements()) {
	    String attname, reqval, respval;
	    attname = e.nextElement();
	    if (!respfiltattrs.containsKey(attname)) {
		System.err.println("[Difference] missing attribute name: "+attname);
		return false;
	    }
	    reqval  = reqfiltattrs.get(attname);
	    respval = respfiltattrs.get(attname);
	    if (debug) {
		System.err.println("--- ATTVAL: attribute value for "+attname
				   +": ("+reqval+") / ("+respval+")");
	    }
	    if (!reqval.equals(respval)) {
		System.err.println("[Difference] different attribute value for "+attname
				   +": ("+reqval+") / ("+respval+")");
		return false;
	    }
	    respfiltattrs.remove(attname);
	}
	// if number is equal, and attributes all have the same name, it should be empty...
	if (respfiltattrs.size() != 0) {
	    System.err.println("[Difference] Attribute check FAILED!");
	    return false;
	}
	return true;
    }

    boolean checkTextNodes(Element requested, Element echoed) {
	NodeList     reqnl;
	NodeList     resnl;
	NamedNodeMap reqattrs;
	NamedNodeMap resattrs;	
	boolean ok;
	int i;

	if (debug) {
	    System.err.println("*** Check Text ["+requested.getLocalName()+"]");
	}

	reqnl = requested.getChildNodes();
	resnl = echoed.getChildNodes();
	Vector<Text> reqvect, resvect;
	reqvect = new Vector<Text>();
	for (i=0; i<reqnl.getLength(); i++) {
	    if (reqnl.item(i).getNodeType() == Node.TEXT_NODE) {
		Text t = (Text)reqnl.item(i);
		// remove "blank" text nodes
		if (t.getLength() == 0 || "".equals(t.getData().trim())) {
		    if (debug) {
			System.err.println("+++ TEXT: skipping blank node ("+t.getLength()+")");
		    }
		    continue;
		}
		if (debug) {
		    System.err.println("+++ TEXT: ("+t.getLength()+") ["+t.getData().trim()+"]");
		}
		reqvect.add(t);
	    }
	}
	resvect = new Vector<Text>();
	for (i=0; i<resnl.getLength(); i++) {
	    if (resnl.item(i).getNodeType() == Node.TEXT_NODE) {
		Text t = (Text)resnl.item(i);
		// remove "blank" text nodes
		if (t.getLength() == 0 || "".equals(t.getData().trim())) {
		    if (debug) {
			System.err.println("--- TEXT: skipping blank node ("+t.getLength()+")");
		    }
		    continue;
		}
		if (debug) {
		    System.err.println("--- TEXT: ("+t.getLength()+") ["+t.getData().trim()+"]");
		}
		resvect.add(t);
	    }
	}
	if (reqvect.size() != resvect.size()) {
	    System.err.println("[Difference] different number of text nodes: "
			       +reqvect.size()+" / "+resvect.size());
	    return false;
	}
	for (i=0; i<reqvect.size(); i++) {
	    String reqval,resval;
	    reqval = reqvect.elementAt(i).getData().trim();
	    resval = resvect.elementAt(i).getData().trim();
	    if (!reqval.equals(resval)) {
		System.err.println("[Difference] in text node: ("+reqval+") / ("+resval+")");
		return false;
	    }
	}
	return true;
    }

    boolean checkNodesNG(Element requested, Element echoed) {
	NodeList     reqnl;
	NodeList     resnl;
	NamedNodeMap reqattrs;
	NamedNodeMap resattrs;	
	boolean ok;
	int i;

	if (!checkName(requested, echoed)) {
	    return false;
	}
	if (debug) {
	    System.err.println("*** Checking ["+requested.getNamespaceURI()+":"+requested.getLocalName()+"]");
	}
	// ok now check the attribute sets
	if (!checkAttributes(requested, echoed)) {
	    System.err.println("[Difference] Attribute Check failed");
	    return false;
	}
	// then text nodes
	if (!checkTextNodes(requested, echoed)) {
	    System.err.println("[Difference] Text Check failed");
	    return false;
	}
	// then recurse each element
	reqnl = requested.getChildNodes();
	resnl = echoed.getChildNodes();
	Vector<Element> reqvect, resvect;
	reqvect = new Vector<Element>();
	for (i=0; i<reqnl.getLength(); i++) {
	    if (reqnl.item(i).getNodeType() == Node.ELEMENT_NODE) {
		reqvect.add((Element)reqnl.item(i));
	    }
	}
	resvect = new Vector<Element>();
	for (i=0; i<resnl.getLength(); i++) {
	    if (resnl.item(i).getNodeType() == Node.ELEMENT_NODE) {
		resvect.add((Element)resnl.item(i));
	    }
	}
	if (resvect.size() != reqvect.size()) {
	    System.err.println("[Difference] wrong number of child elements "+
			       resvect.size()+" / "+reqvect.size());
	    return false;
	}
	for (i=0, ok = true; ok && i<reqvect.size(); i++) {
	    ok = checkNodesNG(resvect.elementAt(i), reqvect.elementAt(i));
	    if (!ok) {
		System.err.println("[Difference] node check failed");
		return false;
	    }
	}
	return true;
    }

    boolean checkNodesSOAPBody(Element requested, Element echoed) {
	NodeList req, resp;
	req  = requested.getElementsByTagNameNS(SOAP11NS, "Body");
	resp = echoed.getElementsByTagNameNS(SOAP11NS, "Body");
	if ((req == null) || (req.getLength() == 0)) {
	    req = requested.getElementsByTagNameNS(SOAP12NS, "Body");
	}
	if ((resp == null) || (resp.getLength() == 0)) {
	    resp = requested.getElementsByTagNameNS(SOAP12NS, "Body");
	}
	return checkNodesNG((Element)req.item(0),(Element)resp.item(0));
    }
    

    /***
     *** End of the new XML fragment checker
     ***/

    boolean checkCall(Element insertHere, Document doc, Node callnode) {
	NodeList nl = null;
	Element req, resp = null;
	boolean ok;
	String testinfo, testinstance = null;
	Vector<String> instancelist;
	
	nl = ((Element)callnode).getElementsByTagNameNS(SOAP11NS, "Envelope");
	if (nl == null || (nl.getLength() == 0)) {
	    nl = ((Element)callnode).getElementsByTagNameNS(SOAP12NS, "Envelope");
	}
	if (nl == null || (nl.getLength() == 0)) {
	    System.err.println("******* Unable to get SOAP body element");
	    return false;
	}
	testinfo = ((Element)callnode).getAttribute("example");
	testinstance = ((Element)callnode).getAttribute("instance");
	if (nl.getLength() == 2) {	
	    req  = (Element) nl.item(0);
	    resp = (Element) nl.item(1);
	    
	    System.err.println("*** Checking "+testinstance+" ( Pattern: "+testinfo+" )");

	    preProcess(testinfo, testinstance, req, resp);

	    ok = checkNodesSOAPBody(req, resp);
	    System.err.println(ok ? "*** success":"*** failure");
	    postProcess();

	    String classification = extable.get(testinfo);
	    if (classification == null) {
		    System.err.println("ERROR for "+testinfo);
			classification = "unclassified";
	    }
	    if (classification.equals("basic")) {
		if (ok) {
		    passedbasic++;
		} else {
		    instancelist = fbasict.get(testinfo);
		    if (instancelist == null) {
			instancelist = new Vector<String>();
		    }
		    instancelist.add(testinstance);
		    fbasict.put(testinfo, instancelist);
		    failedbasic++;
		}
	    } else if (classification.equals("advanced")) {
		if (ok) {
		    passedadvanced++;
		} else {
		    instancelist = fadvancedt.get(testinfo);
		    if (instancelist == null) {
			instancelist = new Vector<String>();
		    }
		    instancelist.add(testinstance);
		    fadvancedt.put(testinfo, instancelist);
		    failedadvanced++;
		}
	    } else {
		    System.err.println("******* "+testinfo+" classified as "+classification);
		if (ok) {
		    passedpending++;
		} else {
		    failedpending++;
		}		    
	    }
	    // tr
	    Element tr = report.createElement("tr");
	    insertHere.appendChild(tr);
	    tr.setAttribute("id", testinstance);
	    tr.setAttribute("class", (ok ? "passed" : "failed"));
	    // first td 
	    Element td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("rowspan","2");
	    td.setAttribute("class", "testname");
	    Element a = report.createElement("a");
	    td.appendChild(a);
	    a.setAttribute("href", EXAMPLENS + testinfo);
	    Text t = report.createTextNode(testinfo);
	    a.appendChild(t);
	    // second td
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("rowspan", "2");
	    td.setAttribute("class", "testname");
	    a = report.createElement("a");
	    td.appendChild(a);
	    a.setAttribute("href", EXAMPLENS + testinfo + '/' + testinfo
			   + '-' + testinstance + ".xml");
	    t = report.createTextNode(testinstance);
	    a.appendChild(t);
	    // third td
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("rowspan","2");
	    td.setAttribute("class", (ok ? "passed" : "failed"));
	    t = report.createTextNode((ok ? "passed" : "failed"));
	    td.appendChild(t);
	    // fourth th
	    td = report.createElement("td");
	    tr.appendChild(td);
	    appendNode(td, req);
	    td.setAttribute("class", "request");
	    // next tr
	    tr = report.createElement("tr");
	    insertHere.appendChild(tr);
	    tr.setAttribute("class", (ok ? "passed" : "failed"));
	    // fourth td
	    td = report.createElement("td");
	    td.setAttribute("class", "response");
	    tr.appendChild(td);
	    appendNode(td, resp);
	    return ok;
	} else {
	    System.err.println("Wrong node length: " + nl.getLength());
	    return false;
	}
    }

    void appendNode(Element insertHere, Element nd) {
	DOMImplementationRegistry domReg;
	DOMImplementationLS domImpl;
	LSSerializer serializer = null;
	LSOutput lso = null;
	StringWriter sw = new StringWriter();
	
	try {
	    domReg  = DOMImplementationRegistry.newInstance();
	    domImpl = (DOMImplementationLS) domReg.getDOMImplementation("LS");
	    serializer = domImpl.createLSSerializer();
	    DOMConfiguration domc = serializer.getDomConfig();
	    domc.setParameter("format-pretty-print", true);
	    lso = domImpl.createLSOutput();
	    lso.setEncoding("UTF-8");
	} catch (Exception ex) {
	}
	lso.setCharacterStream(sw);
	serializer.write(nd, lso);
	
	String out = sw.toString();
	Element pre = report.createElement("pre");
	Text t = report.createTextNode(out);
	pre.appendChild(t);
	insertHere.appendChild(pre);
    }

    void printReport() {
	DOMImplementationRegistry domReg;
	DOMImplementationLS domImpl;
	LSSerializer serializer = null;
	LSOutput lso = null;

	try {
	    domReg  = DOMImplementationRegistry.newInstance();
	    domImpl = (DOMImplementationLS) domReg.getDOMImplementation("LS");
	    serializer = domImpl.createLSSerializer();
	    DOMConfiguration domc = serializer.getDomConfig();
	    domc.setParameter("format-pretty-print", true);
	    lso = domImpl.createLSOutput();
	    lso.setEncoding("UTF-8");
	    if (outputfile != null) {
		lso.setCharacterStream(new FileWriter(outputfile));
	    } else {
	    	lso.setByteStream(System.out);
	    }
	} catch (Exception ex) {
	}
	serializer.write(report, lso);
    }

    void appendTableHeader(Element table) {
	// really ugly
	Element tr = report.createElement("tr");
	table.appendChild(tr);
	// 1st th
	Element th = report.createElement("th");
	tr.appendChild(th);
	th.setAttribute("rowspan", "2");
	th.setAttribute("class", "tstname");
	Text t = report.createTextNode("Test Name");
	th.appendChild(t);
	// 2nd th
	th = report.createElement("th");
	tr.appendChild(th);
	th.setAttribute("rowspan", "2");
	th.setAttribute("class", "tstinstance");
	t = report.createTextNode("Test Instance");
	th.appendChild(t);
	// 3rd th
	th = report.createElement("th");
	tr.appendChild(th);
	th.setAttribute("rowspan", "2");
	th.setAttribute("class", "result");
	t = report.createTextNode("Test Result");
	th.appendChild(t);
	// 4th th
	th = report.createElement("th");
	tr.appendChild(th);
	th.setAttribute("class", "reqh");
	t = report.createTextNode("Request");
	th.appendChild(t);
	// new tr
	tr = report.createElement("tr");
	table.appendChild(tr);
	// 4th th (bis)
	th = report.createElement("th");
	tr.appendChild(th);
	th.setAttribute("class", "reph");
	t = report.createTextNode("Response");
	th.appendChild(t);
    }

    void createResultGraphics(Element insertHere, int goodcalls, int totalcalls) {
	int pcent = goodcalls*100 / totalcalls;

	Element p = report.createElement("p");
	insertHere.appendChild(p);
	p.setAttribute("id","passed");
	Text t = report.createTextNode("Number of passed tests: "+goodcalls);
	p.appendChild(t);

	p = report.createElement("p");
	insertHere.appendChild(p);
	p.setAttribute("id","numtests");
	t = report.createTextNode("Total number of tests: "+totalcalls);
	p.appendChild(t);

	p = report.createElement("p");
	insertHere.appendChild(p);
	p.setAttribute("id","alltests");
	t = report.createTextNode("All tests: "+totalTestsCount);
	p.appendChild(t);
      

	Element div = report.getElementById("progress");
	
	Element table = report.createElement("table");
	div.getParentNode().replaceChild(table, div);
	table.setAttribute("id", "progress");
	Element tr = report.createElement("tr");
	table.appendChild(tr);
	Element td;

	if (pcent != 0 ) {
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("class", (doctored) ? "dpassed" : "cpassed");
	    td.setAttribute("width", pcent+"%");
	    t = report.createTextNode(pcent+"%");
	    td.appendChild(t);
	}
	if (pcent != 100) {
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("class", "cfailed");
	    td.setAttribute("width", (100-pcent)+"%");
	    t = report.createTextNode((100-pcent)+"%");
	    td.appendChild(t);
	}
    }

    Element createOverallResultGraphics(String id, 
					int goodcalls, int totalcalls, int grandtotal) {
	int pcentpassed   = goodcalls*100/grandtotal;
	int pcentfailed   = (totalcalls-goodcalls)*100 / grandtotal;
	int pcentexcluded = 100 - pcentpassed - pcentfailed;

	System.err.println("*** checkind id: "+id);
	Element original = report.getElementById(id);
	
	Element table = report.createElement("table");

	table.setAttribute("class", "progress");
	table.setAttribute("id", id);
	original.getParentNode().replaceChild(table, original);
	Element tr = report.createElement("tr");
	table.appendChild(tr);
	Element td;
	Text t;
	if (pcentpassed > 0) {
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("width", pcentpassed+"%");
	    td.setAttribute("class", (doctored) ? "dpassed" : "cpassed");
	    t = report.createTextNode(pcentpassed+"%");
	    td.appendChild(t);
	}
	if (pcentfailed > 0) {
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("width", pcentfailed+"%");
	    td.setAttribute("class", "cfailed");
	    t = report.createTextNode(pcentfailed+"%");
	    td.appendChild(t);
	}
	if (pcentexcluded > 0) {
	    td = report.createElement("td");
	    tr.appendChild(td);
	    td.setAttribute("width", pcentexcluded+"%");
	    td.setAttribute("class", "cexcl");
	    t = report.createTextNode(pcentexcluded+"%");
	    td.appendChild(t);
	}

	return table;
    }

    void generateFailedTable(Element insertHere, Hashtable<String,Vector<String>> table) {
	if (fbasict.size()==0) {
	    return;
	}
	Vector<String> instanceslist;
	Enumeration<String> tests, instances;
	String testname;
	String testinstance;

	tests = table.keys();
	Element ol = report.createElement("ol");
	insertHere.getParentNode().insertBefore(ol, insertHere.getNextSibling());
	Element a, li, ul;
	Text t;
	while (tests.hasMoreElements()) {
	    testname      = tests.nextElement();
	    instanceslist = table.get(testname);
	    li = report.createElement("li");
	    ol.appendChild(li);
	    t = report.createTextNode(testname);
	    li.appendChild(t);
	    ul = report.createElement("ul");
	    li.appendChild(ul);
	    instances = instanceslist.elements();
	    while (instances.hasMoreElements()) {
		testinstance = instances.nextElement();
		li = report.createElement("li");
		ul.appendChild(li);
		a = report.createElement("a");
		li.appendChild(a);
		a.setAttribute("href", "#"+testinstance);
		t = report.createTextNode(testinstance);
		a.appendChild(t);
	    }
	}
    }

    Document parseTemplate() {
	DocumentBuilder parser = null;
	try {
	    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	    dbf.setValidating(false);
	    dbf.setNamespaceAware(true);
	    parser = dbf.newDocumentBuilder();
	    parser.setEntityResolver(this);
	    if (templatef == null) {
		templatef = new File("reportTemplate.html");
	    }
	    BufferedReader bf;
	    InputSource is = null;
	    bf = new BufferedReader(new FileReader(templatef));
	    is = new InputSource(bf);
	    return parser.parse(is);
	} catch (Exception e) {
	    e.printStackTrace();
	}
	return null;
    }

    void doStuff(String[] args) {
	fbasict    = new Hashtable<String,Vector<String>>();
	fadvancedt = new Hashtable<String,Vector<String>>();
	String logfile = null;
        for (int i = 0 ; i < args.length ; i++) {
	    if ( args[i].equals ("-root") ) {
		exampleuri = args[++i];
		continue;
	    }  
	    if (args[i].equals ("-o") ) {
		String filename = args[++i];
		outputfile = new File(filename);
		if (!outputfile.exists()) {
		    try {
			outputfile.createNewFile();
		    } catch (IOException ieox) {
			System.err.println("*** FATAL, can't create: "+filename);
			System.exit(1);
		    }
		}
		if (!outputfile.canWrite()) {
		    System.err.println("*** FATAL, can't write on: "+filename);
		    System.exit(1);
		}
	    } else {
	        logfile = args[i];
	    }
	}
	if (logfile == null) {
	    System.err.println("Usage: verif [-root <dir>] <outputlog>");
	    System.exit(1);
	}
        int goodcalls, totalcalls, pcent;
	NodeList nl = null;
	String toolkit = "default";
	float successfactor;
	
	initTests();
	initReport();
	readLog(logfile);
	readToolkitInformation(logfile);

	calls = logdoc.getElementsByTagNameNS(LOGNS, CALLTAG);
	nl = logdoc.getElementsByTagNameNS(LOGNS, "log");
	doctored = logdoc.getDocumentElement().hasAttribute("doctored");
	
	reportAddToolkitInfos();
	System.err.println("Number of calls: " + calls.getLength());
	totalcalls = calls.getLength();
	goodcalls = 0;
	passedbasic = 0;
	passedadvanced = 0;
	passedpending = 0;
	failedbasic = 0;
	failedadvanced = 0;
	failedpending = 0;

	Element table = report.getElementById("detailedlog");
	appendTableHeader(table);

	for (int i=0; i<totalcalls; i++) {
	    System.err.println("**************************************************");
	    if (checkCall(table, logdoc, calls.item(i))) {
		goodcalls++;
	    }
	}
	
	Element div =  report.getElementById("testbreakdown");
	createResultGraphics(div, goodcalls, totalcalls);
	Element here;
	Text t;

	here = report.getElementById("passedtotal");
	t = report.createTextNode(Integer.toString(goodcalls));
	here.appendChild(t);
	here = report.getElementById("failedtotal");
	t = report.createTextNode(Integer.toString(totalcalls-goodcalls));
	here.appendChild(t);
	here = report.getElementById("skippedtotal");
	t = report.createTextNode(Integer.toString(totalTestsCount-totalcalls));
	here.appendChild(t);
	here = report.getElementById("teststotal");
	t = report.createTextNode(Integer.toString(totalTestsCount));
	here.appendChild(t);

	createOverallResultGraphics("total", goodcalls, totalcalls, totalTestsCount);

	here = report.getElementById("passedbasic");
	t = report.createTextNode(Integer.toString(passedbasic));
	here.appendChild(t);
	here = report.getElementById("failedbasic");
	t = report.createTextNode(Integer.toString(failedbasic));
	here.appendChild(t);
	here = report.getElementById("skippedbasic");
	t = report.createTextNode(Integer.toString(totalbasic-(passedbasic+failedbasic)));
	here.appendChild(t);
	here = report.getElementById("totalbasic");
	t = report.createTextNode(Integer.toString(totalbasic));
	here.appendChild(t);

	createOverallResultGraphics("basic", passedbasic, 
				    (passedbasic+failedbasic), totalbasic);

	here = report.getElementById("passedadvanced");
	t = report.createTextNode(Integer.toString(passedadvanced));
	here.appendChild(t);
	here = report.getElementById("failedadvanced");
	t = report.createTextNode(Integer.toString(failedadvanced));
	here.appendChild(t);
	here = report.getElementById("skippedadvanced");
	t = report.createTextNode(Integer.toString(totaladvanced-(passedadvanced+failedadvanced)));
	here.appendChild(t);
	here = report.getElementById("totaladvanced");
	t = report.createTextNode(Integer.toString(totaladvanced));
	here.appendChild(t);

	createOverallResultGraphics("advanced", passedadvanced, 
				    (passedadvanced+failedadvanced), totaladvanced);

	here = report.getElementById("passedpending");
	t = report.createTextNode(Integer.toString(passedpending));
	here.appendChild(t);
	here = report.getElementById("failedpending");
	t = report.createTextNode(Integer.toString(failedpending));
	here.appendChild(t);
	here = report.getElementById("skippedpending");
	t = report.createTextNode(Integer.toString(totalpending-(passedpending+failedpending)));
	here.appendChild(t);
	here = report.getElementById("totalpending");
	t = report.createTextNode(Integer.toString(totalpending));
	here.appendChild(t);

	if (totalpending > 0 ) {
	    createOverallResultGraphics("pending", passedpending, 
				   (passedpending+failedpending), totalpending);
	}

	here = report.getElementById("failedbasictests");
	generateFailedTable(here, fbasict);

	here = report.getElementById("failedadvancedtests");
	generateFailedTable(here, fadvancedt);

	System.err.println("*** "+goodcalls+" positive out of "+totalcalls);

	printReport();
    }
	
    public static void main(String[] argv) {
	new verif().doStuff(argv);
    }
}
