package com.openMap1.mapper.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.xmi.util.XMLProcessor;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.core.NamespaceSet;
import com.openMap1.mapper.core.SchemaMismatch;
import com.openMap1.mapper.core.XMLException;
import com.openMap1.mapper.core.namespace;
/**
* A collection of XML utilities, all static.
*
* @author robert
*
*/
public class XMLUtil {
/**
* W3C defined URIs
*/
public static String SCHEMAURI = "http://www.w3.org/2001/XMLSchema";
public static String SCHEMAINSTANCEPREFIX = "xsi";
public static String SCHEMAINSTANCEURI = "http://www.w3.org/2001/XMLSchema-instance";
public static String XSLURI = "http://www.w3.org/1999/XSL/Transform";
public static String XMIURI = "http://www.omg.org/XMI";
public static String XMISchemaLocation = "unknown";
/**
* get the name of an XML element, with the namespace prefix stripped off
* @param el
* @return
*/
public static String getLocalName(Node el)
{
String locName = "";
StringTokenizer st = new StringTokenizer(el.getNodeName(),":");
while (st.hasMoreTokens()) locName = st.nextToken();
return locName;
}
/**
* Vector of child elements of an element
*/
public static Vector<Element> childElements(Element el) {
Vector<Element> res = new Vector<Element>();
NodeList nodes = el.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node nd = nodes.item(i);
if (nd instanceof Element) {
Element eg = (Element) nd;
res.addElement(eg);
}
}
return res;
}
/**
* return the Vector of child Elements with given local name
* @param el
* @param name
* @return
*/
public static Vector<Element> namedChildElements(Element el, String lName)
{
Vector<Element> nc = new Vector<Element>();
for (Iterator<Element> it = childElements(el).iterator(); it.hasNext();)
{
Element en = it.next();
if (getLocalName(en).equals(lName)) nc.addElement(en);
}
return nc;
}
/**
* return the first child of the element with given name, or null if there are none
* @param el
* @param lName
* @return
*/
public static Element firstNamedChild(Element el, String lName)
{
if (el == null) return null;
Element fc = null;
if (namedChildElements(el,lName).size() > 0) fc = namedChildElements(el,lName).elementAt(0);
return fc;
}
/**
* get the text string in an element (eg interspersed between child elements),
* or "" if there is none or if the Element is null.
* Tries to ignore white space text; but does not succeed.
*/
public static String getText(Element el) {
String res = "";
if (el != null) try
{
el.normalize(); // does not help recognise white space
NodeList nodes = el.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++)
if (nodes.item(i) instanceof Text)
{
Text text = (Text)nodes.item(i);
// this filter seems to make no difference
if (!text.isElementContentWhitespace())
{
String tData = text.getData();
// this seems to be an effective way to catch pure white space
StringTokenizer nonWhiteSpace = new StringTokenizer(tData,"\n \t");
if (nonWhiteSpace.countTokens() > 0 )res = res + tData;
}
}
}
catch (Exception e) {System.out.println("Text failure: " + e.getMessage());}
return res;
}
/**
* return the text value of a node, which may be an Element or attribute
*/
public static String textValue(Node n) {
String res = null;
if (n == null) {
GenUtil.message("Text-holding element or attribute node is null.");
} else {
if (n instanceof Element) {
res = getText((Element) n);
} else if (n instanceof Attr) {
res = ((Attr) n).getValue();
} else {
GenUtil.message("Node is neither an element or an attribute. ");
GenUtil.message("Class of node: " + n.getClass().getName());
}
}
return res;
}
/**
*
* @param uri
* @return the root Element of an XML file with URI
* @throws MapperException
*/
public static Element getRootElement(URI uri) throws MapperException
{
String location = FileUtil.editURIConverter().normalize(uri).toFileString();
return getRootElement(location);
}
/**
* return the root Element of an XML file with given location
* @param location
* @return Element the root element, or null if there is any problem
*/
public static Element getRootElement(String location) throws MapperException
{
Element root = null;
try {
FileInputStream fi = new FileInputStream(location);
DocumentBuilderFactory builderFac = DocumentBuilderFactory.newInstance();
builderFac.setNamespaceAware(true);
root = builderFac.newDocumentBuilder().parse(fi).getDocumentElement();
}
catch (SAXException ex) {notify(location,ex);}
catch (FileNotFoundException ex) {notify(location,ex);}
catch (IOException ex) {notify(location,ex);}
catch (ParserConfigurationException ex) {notify(location,ex);}
return root;
}
/**
* return the root Element of an XML file in a given inputStream
* @param location
* @return Element the root element, or null if there is any problem
*/
public static Element getRootElement(InputStream fi) throws MapperException
{
return getDocument(fi).getDocumentElement();
}
/**
* return the Document of an XML file in a given inputStream
* @param location
* @return Element the root element, or null if there is any problem
*/
public static Document getDocument(InputStream fi) throws MapperException
{
Document doc = null;
try {
DocumentBuilderFactory builderFac = DocumentBuilderFactory.newInstance();
builderFac.setNamespaceAware(true);
doc = builderFac.newDocumentBuilder().parse(fi);
}
catch (SAXException ex) {notify("Input Stream",ex);}
catch (FileNotFoundException ex) {notify("Input Stream",ex);}
catch (IOException ex) {notify("Input Stream",ex);}
catch (ParserConfigurationException ex) {notify("Input Stream",ex);}
return doc;
}
/**
* return the Document of an XML file in a given inputStream
* @param location
* @return Element the root element, or null if there is any problem
*/
public static Document getDocument(String location) throws MapperException
{
try{
FileInputStream fi = new FileInputStream(location);
return getDocument(fi);
}
catch (IOException ex) {notify("Input Stream",ex);}
return null;
}
private static void notify(String location, Exception ex) throws MapperException
{
throw new MapperException ("Exception getting XML root element from "
+ location + "; " + ex.getMessage());
}
//--------------------------------------------------------------------------------------
// Output File handling
//--------------------------------------------------------------------------------------
// make an output XML document
public static Document makeOutDoc() throws XMLException
{
Document outDoc = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
outDoc = db.newDocument();
}
catch (ParserConfigurationException pce) {
throw new XMLException("Parser Config exception: " + pce.getMessage());
}
return outDoc;
}
//--------------------------------------------------------------------------------------
// Writing HTML
//--------------------------------------------------------------------------------------
public static Document makeHTMLOutDoc() throws XMLException
{
Document outDoc = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
DOMImplementation domImpl = db.getDOMImplementation();
DocumentType docType = domImpl.createDocumentType("html",
"-//W3C//DTD XHTML 1.0 Transitional//EN", // public identifier
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"); // system identifier
outDoc = domImpl.createDocument("namespaceuri", "html", docType);
}
catch (Exception ex) {
ex.printStackTrace();
throw new XMLException("Parser Config exception: " + ex.getMessage());
}
return outDoc;
}
//--------------------------------------------------------------------------------------
// Writing XML
//--------------------------------------------------------------------------------------
/**
* write the XML output to the named file
* @param fileName the filename to write to
* @param outDoc the XML document to be written
* @param isFormatted if true, put in line breaks and indenting
* @throws XMLException if anything goes wrong
*/
public static void writeOutput(Document outDoc, String fileName, boolean isFormatted)
throws XMLException
{
if (isFormatted) {
addFormatting(outDoc);
}
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
Source src = new DOMSource(outDoc);
Result dest = new StreamResult(new File(fileName));
t.transform(src, dest);
}
catch (TransformerConfigurationException tce) {
throw new XMLException("Transform Config exception: " + tce.getMessage());
}
catch (TransformerException te) {
throw new XMLException("Transform exception: " + te.getMessage());
}
}
/**
* write the XML output to the output stream
* @param Document Doc the XML document to be written out
* @param OutputStream stream the output stream to write it to
* @param boolean formatted : true if you want sensible line breaks and indents
*/
public static void writeToStream(Document Doc, OutputStream stream, boolean formatted)
throws XMLException
{
if (formatted) {
addFormatting(Doc,Doc.getDocumentElement(),"");
}
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
Source src = new DOMSource(Doc);
Result dest = new StreamResult(stream);
t.transform(src, dest);
}
catch (TransformerConfigurationException tce) {
throw new XMLException("Transform Config exception: " + tce.getMessage());
}
catch (TransformerException te) {
throw new XMLException("Transform exception: " + te.getMessage());
}
}
/**
* write the XML output to the named file
* @param fileName the filename to write to
* @param xslFilename the file to use to transform
*/
public static void writeFormattedOutput(Document outDoc, String fileName, String xslFilename )
throws XMLException
{
try {
StreamSource xsltSource = new StreamSource(new FileInputStream(xslFilename));
Result dest = new StreamResult(new File(fileName));
writeFormattedOutputFromXSLSource( outDoc, dest, xsltSource);
} catch (FileNotFoundException e) {
throw new XMLException("Transform file not found exception: " + e.getMessage());
}
}
/**
* write the XML output to the named file
* @param fileName the filename to write to
* @param xslStream input stream of the xsl to use to transform
*/
public static void writeTransformedOutput(Document outDoc, String fileName, InputStream xslStream )
throws XMLException
{
StreamSource xsltSource = new StreamSource(xslStream);
Result dest = new StreamResult(new File(fileName));
writeFormattedOutputFromXSLSource( outDoc, dest, xsltSource);
}
/**
* create the XSLT transformed output
* @param xslNode the top node of the XSL DOM file used to transform
* @return the top node of the transformed DOM
*/
public static Node makeTransformedOutput(Document outDoc, Node xslNode ) throws XMLException
{
DOMSource xsltSource = new DOMSource(xslNode);
DOMResult dest = new DOMResult();
writeFormattedOutputFromXSLSource(outDoc,dest, xsltSource);
return dest.getNode();
}
/**
* write the XML output to the named file
* @param dest the Result to write to
* @param xsltSource the xslt to use to transform
*/
public static void writeFormattedOutputFromXSLSource(Document outDoc, Result dest, Source xsltSource )
throws XMLException
{
try {
TransformerFactory tf = TransformerFactory.newInstance();
Source src = new DOMSource(outDoc);
Templates xslt = tf.newTemplates(xsltSource);
// Apply the xsl file to the source file and write the result to the output file
Transformer trans = xslt.newTransformer();
// Transform the XML
trans.transform(src, dest);
}
catch (TransformerConfigurationException tce) {
throw new XMLException("Transform Config exception: " + tce.getMessage());
}
catch (TransformerException te) {
throw new XMLException("Transform exception: " + te.getMessage());
}
}
//---------------------------------------------------------------------------------------------------
// formatting code
//---------------------------------------------------------------------------------------------------
/** This method goes through and adds formatting as necessary so that the XML is human readable
* TODO work out why we can't put space in front of the first element */
protected static void addFormatting(Document outDoc) {
// First we want a space before the first element
addFormatting(outDoc,outDoc.getDocumentElement(), "");
}
/** Goes through and adds newlines and indent to the current node and all its children
* @param current the current node
* @param indent the current level of indent this is increased recursively*/
private static void addFormatting(Document doc, Node current, String indent) {
// go through each of the children adding space as required
Node child = current.getFirstChild();
String childIndent = indent+"\t";
while (child != null) {
Node nextChild = child.getNextSibling();
if (child.getNodeType() != Node.TEXT_NODE) {
// Only if we aren't a text node do we add the space
current.insertBefore(doc.createTextNode("\n"+childIndent), child);
if (child.hasChildNodes()) {
addFormatting(doc, child, childIndent);
}
if (nextChild == null) {
// Because this is the last child, we need to add some space after it
current.appendChild(doc.createTextNode("\n"+indent));
}
}
child = nextChild;
}
}
//--------------------------------------------------------------------------------------
// XML Utilities - for writing files
//--------------------------------------------------------------------------------------
/**
* create an empty element in a namespace
*
* @param prefix String: namespace prefix
* @param localName String: local name (after prefix and ':')
* @param URI String: namespace URI
* @return Element: the Element
*/
public static Element NSElement(Document outDoc, String prefix, String localName, String URI)
throws XMLException {
String qualName = prefix + ":" + localName;
if (prefix.equals("")) qualName = localName;
Element en = null;
try {
en = (Element) outDoc.createElementNS(URI, qualName);
}
catch (Exception e) {
throw new XMLException ("Exception creating namespace element '" + qualName
+ "'. " + e.getMessage());
}
return en;
}
/**
* create an element in a namespace, with text content
*
* @param prefix String: namespace prefix
* @param localName String: local name (after prefix and ':')
* @param URI String: namespace URI
* @param text String: text content of the element
* @return Element: the Element
*/
public static Element textNSElement(Document outDoc, String prefix, String localName, String URI, String text)
throws XMLException {
Text t;
Element en = null;
try {
en = NSElement(outDoc, prefix, localName, URI);
t = outDoc.createTextNode(text);
en.appendChild(t);
}
catch (Exception e) {
throw new XMLException ("Exception creating text-filled namespace element '"
+ prefix + ":" + localName
+ "'. " + e.getMessage());
}
return en;
}
/**
* @param el
* @return the first text child of an element, or null if it has none
*/
public static Text firstTextChild(Element el)
{
Text text = null;
for (int i = 0; i < el.getChildNodes().getLength(); i++)
{
Node n = el.getChildNodes().item(i);
if (n instanceof Text) text = (Text)n;
}
return text;
}
/**
* create a new element in no namespace with no text content.
* write an error message and return null if any exception
*
* @param name String: name of the element
* @return Element: the element
*/
public static Element newElement(Document outDoc, String name)
throws XMLException {
Element ei = null;
try {
ei = (Element) outDoc.createElement(name);
}
catch (Exception e) {
throw new XMLException ("Exception creating element '" + name
+ "'. " + e.getMessage());
}
return ei;
}
/**
* create a new element in no namespace with no text content.
* write an error message and return null if any exception
*
* @param name String: name of the element
* @param text String: the text content
* @return Element: the element
*/
public static Element textElement(Document outDoc, String name, String text)
throws XMLException {
Text t;
Element en = null;
try {
en = newElement(outDoc,name);
t = outDoc.createTextNode(text);
en.appendChild(t);
}
catch (Exception e) {
throw new XMLException ("Exception creating text-filled element '" + name
+ "'. " + e.getMessage());
}
return en;
}
/**
* add a comment below element el
*/
public static void addComment(Document outDoc, Element el, String cText) {
Comment com = outDoc.createComment(cText);
el.appendChild(com);
}
/**
* remove from a name any characters not allowed in XML tags
* (what are they?)
* @param name
* @return
*/
public static String legalTagName(String name)
{
String tagName = "";
StringTokenizer st = new StringTokenizer(name," ,/;'\\");
while (st.hasMoreTokens()) tagName = tagName + st.nextToken();
return tagName;
}
/**
* @param location String: location of XML file
* @return Element: root Element of XML file
* @throws XMLFileOpeningException: if file cannot be found
*/
public static Element readXMLFile(String location) throws XMLException {
Element root = null;
try {
FileInputStream fi = new FileInputStream(location);
root = readXMLFile(fi, location);
}
catch (FileNotFoundException ex) {
throw new XMLException("Cannot find XML file " + location);
}
return root;
}
/**
* Open an XML file and set its root element 'root' (an instance variable)
* or leave root = null if some problem.
*/
public static Element readXMLFile(InputStream is, String location) throws XMLException {
Element root = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);
root = doc.getDocumentElement();
if (root == null) {
throw new XMLException("No root element in file " + location);
} else {
}
}
catch (java.io.IOException e) {
throw new XMLException("IO Exception opening XML file " + location);
}
catch (org.xml.sax.SAXException e) {
throw new XMLException("File at "
+ location + " is not a valid XML file. " + e.getMessage());
}
catch (Exception e) {
throw new XMLException("Unidentified exception opening XML file "
+ location + ": " + e.getMessage());
}
return root;
}
//----------------------------------------------------------------------------------------------------
// namespaces
//----------------------------------------------------------------------------------------------------
/**
* find all namespaces (prefix and URI) in an XML document,
* starting from the root and descending.
* @param rootEl the root element of the document
* @return the NamespaceSet containing all namespaces
* @exception MapperException if any namespace URI is declared twice with different prefixes
*/
public static NamespaceSet getNameSpaceSet(Element rootEl) throws MapperException
{
NamespaceSet nsSet = new NamespaceSet();
addNamespaceSet(rootEl,nsSet);
return nsSet;
}
/**
* recursive descent of an XML document, picking up all namespace declarations
* @param el the current element
* @param nsSet the namespace set being built up
* @exception MapperException if any namespace URI is declared twice with different prefixes
*/
private static void addNamespaceSet(Element el, NamespaceSet nsSet) throws MapperException
{
NamedNodeMap attList = el.getAttributes();
for (int i = 0; i < attList.getLength(); i++)
{
// find all namespace attributes on this node
Node n = attList.item(i);
if (n instanceof Attr)
{
Attr att = (Attr)n;
String name = att.getName();
if ((name.equals("xmlns"))|(name.startsWith("xmlns:")))
{
String prefix = "";
if (name.length() > 6) prefix = name.substring(6);
String uri = att.getValue();
// if the namespace prefix has been declared already, check it was declared with the same URI
namespace ns = nsSet.getByPrefix(prefix);
if ((ns != null) && (!(uri.equals(ns.URI()))))
throw new MapperException("XML Instance declares namespace prefix '" + prefix
+ "' with uris '" + uri + "' and '" + ns.URI() + "'");
else if (ns == null) nsSet.addNamespace(new namespace(prefix,uri));
}
}
}
// recurse over child elements
for (int i = 0; i < el.getChildNodes().getLength();i++)
{
Node n = el.getChildNodes().item(i);
if (n instanceof Element) addNamespaceSet((Element)n, nsSet);
}
}
/**
* @param el an Element
* @return the ordinal position 1..N of this element amongst its sibling
* elements (of any name), as a String;
* or '0' if the element has no parent
*/
public static String ordinalPosition(Element el)
{
String position = "0";
Node parent = el.getParentNode();
if (parent instanceof Element)
{
int pos = 0;
NodeList nl = ((Element)parent).getChildNodes();
for (int i = 0; i < nl.getLength();i++)
{
Node n = nl.item(i);
if (n instanceof Element) pos++;
if (n.equals(el)) position = new Integer(pos).toString();
}
}
return position;
}
/**
* copy all attributes form one element to another
* @param fromEl
* @param toEl
*/
public static void copyAttributes(Element fromEl,Element toEl)
{
for (int a = 0; a < fromEl.getAttributes().getLength();a++)
{
Attr at = (Attr)fromEl.getAttributes().item(a);
toEl.setAttribute(at.getName(), at.getValue());
}
}
/**
* copy text from one element to another
* @param fromEl
* @param toEl
* @param outDoc
*/
public static void copyText(Element fromEl,Element toEl, Document outDoc)
{
String text = XMLUtil.getText(fromEl);
if ((text != null) && (text.length() > 0)) toEl.appendChild(outDoc.createTextNode(text));
}
/**
*
* @param el an element
* @param attNames names of allowed attributes
* @throws MapperException if the element has any attribute with another name
*/
public static void checkAttributes(Element el, String[] attNames) throws MapperException
{
NamedNodeMap map = el.getAttributes();
for (int i = 0; i < map.getLength(); i++)
{
String attName = map.item(i).getLocalName();
if (!GenUtil.inArray(attName, attNames))
throw new MapperException("Element '" + el.getLocalName() + "' has no attribute '" + attName + "'");
}
}
/**
*
* @param el an element
* @param elNames allowed names for its child elements
* @throws MapperException
*/
public static void checkChildElements(Element el, String[] elNames) throws MapperException
{
Vector<Element> children = childElements(el);
for (Iterator<Element> it = children.iterator();it.hasNext();)
{
String childName = it.next().getLocalName();
if (!GenUtil.inArray(childName, elNames))
throw new MapperException("Element '" + el.getLocalName() + "' has no child element '" + childName + "'");
}
}
/**
* append an XML element child to a parent Element parent, first checking that they are from
* the same document.
* @param parent
* @param child
* @throws MapperException
*/
public static void appendChildWithDocumentCheck(Element parent,Element child) throws MapperException
{
Document doc1 = parent.getOwnerDocument();
Document doc2 = child.getOwnerDocument();
if (!doc1.equals(doc2))
{
String doc1Name = doc1.getDocumentURI();
String doc2Name = doc1.getDocumentURI();
String message = "Attempt to add a child node from Document '" + doc1Name
+ "' to a parent from Document '" + doc2Name + "'";
throw new MapperException(message);
}
parent.appendChild(child);
}
//---------------------------------------------------------------------------------------------
// Schema Validation
//---------------------------------------------------------------------------------------------
/**
* validate an XML instance against the schema
* if there is such a schema. If there is no schema, return an empty list.
* (Appears not to work, so it has been temporarily bypassed - see below)
* @param the root element of the XML instance
* @return a Vector of SchemaMismatch objects, which wrap org.eclipse.emf.ecore.util.Diagnostic
*/
public static Vector<SchemaMismatch> schemaValidate(Element instanceRoot, URI schemaURI)
{
boolean bypassed = true;
Vector<SchemaMismatch> mismatches = new Vector<SchemaMismatch>();
if (!bypassed) try
{
XMLProcessor processor = new XMLProcessor(schemaURI);
// can throw exceptions at the next line if the instance has features not in the schema
Resource resource = processor.load(instanceRoot, null);
EObject document = (EObject)resource.getContents().get(0);
// Validate the feed and convert the resulting diagnostic to a set of SchemaMismatches
Diagnostic diagnostic = Diagnostician.INSTANCE.validate(document);
captureDiagnosticTree(diagnostic,0,mismatches);
}
catch (Exception ex)
{
String message = "Exception during schema validation: " + ex.getMessage();
System.out.println(message);
ex.printStackTrace();
Diagnostic diagnostic = new BasicDiagnostic(1,"schema",1,message,null);
mismatches.add(new SchemaMismatch(diagnostic,SchemaMismatch.SCHEMA_VALIDATION_ERROR));
}
return mismatches;
}
/**
* build up a set of SchemaMismatch objects from a diagnostic tree
* @param diagnostic
* @param level current depth in the diagnostic tree
* @param mismatches the Vector of schemaMismatches being built up
*/
private static void captureDiagnosticTree(Diagnostic diagnostic, int level,Vector<SchemaMismatch> mismatches)
{
switch (diagnostic.getSeverity())
{
case Diagnostic.OK:
// record nothing for an OK diagnostic
break;
case Diagnostic.ERROR:
{
// do not record the overall diagnostic message
if (!(diagnostic.getMessage().startsWith("Diagnosis of")))
mismatches.add(new SchemaMismatch(diagnostic,SchemaMismatch.SCHEMA_VALIDATION_ERROR));
}
break;
case Diagnostic.INFO:
mismatches.add(new SchemaMismatch(diagnostic,SchemaMismatch.SCHEMA_VALIDATION_INFO));
break;
}
// recurse down the tree of diagnostics
for (Iterator<Diagnostic> i = diagnostic.getChildren().iterator(); i.hasNext();)
captureDiagnosticTree(i.next(), level + 1, mismatches);
}
//---------------------------------------------------------------------------------------------------------------
// Utilities for xhtml
//---------------------------------------------------------------------------------------------------------------
/**
*
* @param doc the html document
* @param header array of column names
* @param bodyId id for the body element (for javascript to pick up), or null
* @return an html table element with a head, a header row and a body with an id, but no body rows
*/
public static Element htmlTable(Document doc, String[] header,String bodyId) throws MapperException
{
Element table = newElement(doc, "table");
table.setAttribute("border", "1");
Element thead = newElement(doc, "thead");
table.appendChild(thead);
Element headerRow = newElement(doc, "tr");
thead.appendChild(headerRow);
for (int i = 0; i < header.length; i++)
{
Element headerCell = XMLUtil.textElement(doc, "th", header[i]);
headerRow.appendChild(headerCell);
}
Element tbody = newElement(doc, "tbody");
if (bodyId != null) tbody.setAttribute("id", bodyId);
table.appendChild(tbody);
return table;
}
/**
* add a row to the end of an html table
* @param table
* @param row array of cell contents
* @return the new number of rows in the body
* @throws MapperException if the table has no header, or the header size does not match the row
*/
public static int addhtmlTableRow(Element table, String[] row, String[] links) throws MapperException
{
Document doc = table.getOwnerDocument();
Element head = firstNamedChild(table, "thead");
if (head == null) throw new MapperException("Table has no head");
Element headerRow = firstNamedChild(head, "tr");
if (headerRow == null) throw new MapperException("Table has no header row");
int size = XMLUtil.namedChildElements(headerRow, "th").size();
if (size != row.length) throw new MapperException("Cannot add a row of length " + row.length + " to a table with " + size + " columns.");
Element body = firstNamedChild(table, "tbody");
if (body == null) throw new MapperException("Table has no body");
Element newRow = newElement(doc, "tr");
body.appendChild(newRow);
for (int i = 0; i < row.length; i++)
{
// the contents of this cell are a link to somewhere
if ((links != null) && (links[i] != null) && (!links[i].equals("")))
{
Element rowCell = XMLUtil.newElement(doc, "td");
newRow.appendChild(rowCell);
Element aCell = XMLUtil.textElement(doc, "a", row[i]);
aCell.setAttribute("href", links[i]);
rowCell.appendChild(aCell);
}
// the contents of this cell are not a link
else
{
Element rowCell = XMLUtil.textElement(doc, "td", row[i]);
newRow.appendChild(rowCell);
}
}
// return the new number of rows in the body
return namedChildElements(body, "tr").size();
}
//---------------------------------------------------------------------------------------------------------------
// counts of nodes
//---------------------------------------------------------------------------------------------------------------
/**
* @param el
* @return the number of element and attribute nodes in an XML tree
*/
public static int nodeCount(Element el)
{
int count = 1 + el.getAttributes().getLength(); // this node and its attributes
// contributions from child elements
Vector<Element> childElements = childElements(el);
for (int i = 0; i < childElements.size(); i++)
count = count + nodeCount(childElements.get(i));
return count;
}
/**
* @param el
* @return the number of element and attribute nodes in an XML tree
*/
public static int nodeValueCount(Element el)
{
int count = el.getAttributes().getLength(); // attributes of this node
// text value of this node
String text = getText(el);
if ((text !=null) && (!text.equals(""))) count++;
// contributions from child elements
Vector<Element> childElements = childElements(el);
for (int i = 0; i < childElements.size(); i++)
count = count + nodeValueCount(childElements.get(i));
return count;
}
/**
*
* @param el
* @return depth of an XML node tree
*/
public static int getDepth(Element el)
{
int depth = 1;
if (el.getAttributes().getLength() > 0) depth = 2;
int max = 0;
Vector<Element> childElements = childElements(el);
for (int i = 0; i < childElements.size(); i++)
{
int childDepth = getDepth(childElements.get(i));
if (childDepth > max) max = childDepth;
}
return depth + max;
}
/**
* print out all distinct paths in an XML subtree
* @param el
*/
public static void writeAllPaths(Element el)
{
Hashtable<String,String> allPaths = new Hashtable<String,String>();
String path = "";
collectPaths(path, allPaths,el);
for (Enumeration<String> en = allPaths.keys();en.hasMoreElements();)
System.out.println("path: " + en.nextElement());
}
private static void collectPaths(String path, Hashtable<String,String> allPaths,Element el)
{
String newPath = path + el.getLocalName() + "/";
allPaths.put(newPath, "1");
for (Iterator<Element> it = childElements(el).iterator(); it.hasNext();)
collectPaths(newPath, allPaths, it.next());
}
}