package org.spaceroots.rkcheck;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;  
import org.xml.sax.ErrorHandler;
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory;  
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.OutputKeys;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;

/** This class is the base class for application-specific XML files
 * reading/writing.

 * 

In order to read/write application-specific XML files, a * concrete class extending this abstract base class must be created * and the four {@link #analyze analyze}, {@link #build build}, {@link * #getPublicId getPublicId} and {@link #getSystemId getSystemId} * methods must be implemented.

* @version $Id: XMLFile.java,v 1.2 2004/05/23 20:14:04 luc Exp $ * @author Luc Maisonobe */ public abstract class XMLFile implements ErrorHandler { /** Simple constructor. * @exception RKCheckException if no parser can be built */ public XMLFile() throws RKCheckException { try { factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); resolver = new EmbeddedCatalogResolver("/org/spaceroots/resources/catalog", factory.newDocumentBuilder()); } catch (ParserConfigurationException pce) { throw new RKCheckException(pce); } } /** Parse an XML document containing an application-specific configuration. * @param uri URI of the project document to read * @exception RKCheckException if an error occurs during parsing (mainly * because there is an error in the document) * @exception IOException if there is low level read error * @exception FileNotFoundException if there is low level file error */ public void read(String uri) throws RKCheckException, IOException, FileNotFoundException { try { // build the DOM tree from the file DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(resolver); builder.setErrorHandler(this); Document document = builder.parse(uri); // analyze the DOM tree analyze(document, uri); } catch (NumberFormatException nfe) { throw new RKCheckException(nfe); } catch (ParserConfigurationException pce) { throw new RKCheckException(pce); } catch (SAXException se) { if ((se.getException() != null) && (se.getException() instanceof RKCheckException)) { throw (RKCheckException) se.getException(); } else { throw new RKCheckException(se); } } } /** Write an XML document. * @param writer writer to use for producing the document * @param uri URI of the document * @exception RKCheckException if an error occurs during write * @exception IOException if there is low level read error */ public void write(Writer writer, String uri) throws RKCheckException, IOException { try { // build the DOM tree Document document = factory.newDocumentBuilder().newDocument(); build(document, uri); indent(document, document.getDocumentElement(), "", " "); document.getDocumentElement().normalize(); // write down the tree into the file TransformerFactory tf = TransformerFactory.newInstance(); Transformer t = tf.newTransformer(); t.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, getPublicId()); t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, getSystemId()); t.transform(new DOMSource(document.getDocumentElement()), new StreamResult(writer)); writer.write(System.getProperty("line.separator")); } catch (ParserConfigurationException pce) { throw new RKCheckException(pce); } catch (TransformerException te) { throw new RKCheckException(te); } } /** Indent an element sub-elements. *

If an element doesn't have any sub-elements, its content is unchanged.

* @param document complete DOM tree beeing built * @param element element to indent * @param base base indentation (it has already been inserted before the node start) * @param increment increment indentation added at each level */ private void indent(Document document, Element element, String base, String increment) { String newBase = base + increment; boolean indented = false; for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) { if (node.getNodeType() == Node.ELEMENT_NODE) { indented = true; element.insertBefore(document.createTextNode("\n" + newBase), node); indent(document, (Element) node, newBase, increment); } } if (indented) { element.appendChild(document.createTextNode("\n" + base)); } } /** Get a specific child element node. * @param parent parent node * @param name name of the element * @param missingOK if true no exception is generated if the element * is not found * @return element node with the given name (null if not found and * missingOK is true) * @exception RKCheckException if the element cannot be found and * missingOK is false */ protected Element getElement(Node parent, String name, boolean missingOK) throws RKCheckException { for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) { if ((node.getNodeType() == Node.ELEMENT_NODE) && node.getNodeName().equals(name)) { return (Element) node; } } if (missingOK) { return null; } else { throw new RKCheckException("element {0} cannot be found in file {1}", new String[] { name, uri }); } } /** Get an attribute value. * @param element element containing the attribute * @param name attribute name * @param missingOK if true no exception is generated if the attribute * is not found * @return attribute value (null if not found and * missingOK is true) * @exception RKCheckException if the attribute cannot be found and * missingOK is false */ protected String getAttribute(Node element, String name, boolean missingOK) throws RKCheckException { Attr attribute = ((Element) element).getAttributeNode(name); if ((attribute == null) && ! missingOK) { throw new RKCheckException("missing {0} attribute in {1}", new String[] { name, uri }); } return (attribute == null) ? null : attribute.getValue(); } /** Get the content of a node. * @param node node to analyze * @return node content * @exception RKCheckException if the node contains unexpected elements */ protected String getContent(Node node) throws RKCheckException { StringBuffer buffer = new StringBuffer(); for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { switch(child.getNodeType()) { case Node.TEXT_NODE : buffer.append(child.getNodeValue()); break; case Node.ENTITY_REFERENCE_NODE : buffer.append(getContent(child)); break; case Node.CDATA_SECTION_NODE : buffer.append(child.getNodeValue()); break; default: throw new RKCheckException("unexpected content in element {0} of file {1}", new String[] { node.getNodeName(), uri }); } } return buffer.toString(); } /** Receive notification of a recoverable error. * @param exception the recoverable error * @exception SAXException the error is directly thrown as an exception */ public void error(SAXParseException exception) throws SAXParseException { throw exception; } /** Receive notification of a non-recoverable error. * @param exception the non-recoverable error * @exception SAXException the error is directly thrown as an exception */ public void fatalError(SAXParseException exception) throws SAXParseException { throw exception; } /** Receive notification of a warning. * @param exception the warning * @exception SAXException the error is directly thrown as an exception */ public void warning(SAXParseException exception) throws SAXParseException { throw exception; } /** Analyze the DOM tree. *

This method is called by the {@link #read read} method after * having build the DOM tree. Its purpose is to analyze the tree and * store the contained information in application-specific ways.

* @param document DOM tree to analyze * @param uri URI from which the project document was read (useful * mainly for error messages) * @exception RKCheckException if some data cannot be found in the tree * @exception NumberFormatException if some number cannot be parsed */ public abstract void analyze(Document document, String uri) throws RKCheckException, NumberFormatException; /** Build the DOM tree. *

This method is called by the {@link #write write} method * before writing the DOM tree. Its purpose is to build the DOM tree * in application-specific ways.

* @param document empty DOM tree to build * @param uri URI where the project document will be written (useful * mainly for error messages) * @exception RKCheckException if some problem occurs while building * the tree */ public abstract void build(Document document, String uri) throws RKCheckException; /** Get the public ID of the DTD. * @return public ID of the DTD */ protected abstract String getPublicId(); /** Get the system ID of the DTD. * @return system ID of the DTD */ protected abstract String getSystemId(); /** URI of the configuration file. */ protected String uri; /** Documents builder. */ private DocumentBuilderFactory factory; /** Entity resolver and public/system id mapper. */ private EmbeddedCatalogResolver resolver; }

Ads help maintain this website.