package org.benow.xml.sax;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Creates DOM fragments from the SAX events. Fragments are created
* for each subtree which initially matches the given element name. For example,
* for the xml document:
* <pre>
* <items>
* <item>
* <name>boo</name>
* <item>...</item>
* </item>
* <item>
* <name>two</item>
* </item>
* <pre>
* with a DOMConstructor("item"), then element subtrees will be created for
* the first item (and contained item) and the second item.
*
*
* @author andy
*
*/
public abstract class DOMProducer extends DefaultHandler {
public static class FinishedException extends SAXException {
/**
*
*/
private static final long serialVersionUID = 1L;
}
private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
private Element currElem;
private boolean trimEmpty = false;
protected final String matchElementName;
private int maxObjects = -1;
private int currObjNum;
private String currText;
protected boolean finished = false;;
public DOMProducer(String matchElementName) {
this.matchElementName = matchElementName;
}
/**
* If set to true, elements which have no contained attributes or nodes (elements, text)
* will be trimmed.
*
* @param trimEmpty
*/
public void setTrimEmpty(
boolean trimEmpty) {
this.trimEmpty = trimEmpty;
}
@Override
public void startElement(
String uri,
String localName,
String qName,
Attributes attributes) throws SAXException {
if (qName.equals(matchElementName)) {
if (currElem == null) {
currElem = newDoc().createElement(qName);
int len = attributes.getLength();
for (int i = 0; i < len; i++) {
String name = attributes.getQName(i);
String val = attributes.getValue(i);
currElem.setAttribute(name, val);
}
return;
}
}
if (currElem != null) {
Element newElem = currElem.getOwnerDocument().createElement(qName);
currElem.appendChild(newElem);
currElem = newElem;
int len = attributes.getLength();
for (int i = 0; i < len; i++) {
String name = attributes.getQName(i);
String val = attributes.getValue(i);
currElem.setAttribute(name, val);
}
}
}
public void preCalculate(
InputStream in) throws ParserConfigurationException, SAXException, IOException {
System.out.print("Precalculating...");
System.out.flush();
maxObjects = ElementCounter.getCount(in, matchElementName);
System.out.println(" " + maxObjects + " objects will be produced.");
in.close();
}
/**
* Gets the number of objects which have been produced.
* @return
*/
public int getCurrentObjectNumber() {
return currObjNum;
}
/**
* Gets the maximum number of objects which will be produced. Available
* by default, unless precalculation has been disabled with disablePreCalc().
*
* @return
*/
public int getMaxNumberOfObjects() {
return maxObjects;
}
/**
* Get the percentage of iteration through the results. Available by default
* unless precalculation has been disabled with disablePreCalc()
*
* @return
*/
public double getPercentComplete() {
return (((double) getCurrentObjectNumber()) / ((double) getMaxNumberOfObjects())) * 100.0;
}
public String getPercentCompleteString() {
return (((int) (getPercentComplete() * 10.0)) / 10.0) + "%";
}
public void parse(
InputStream in) throws ParserConfigurationException, SAXException, IOException {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
try {
try {
parser.parse(in, this);
} catch (FinishedException e) {
// done
}
} finally {
in.close();
}
}
@Override
public void endElement(
String uri,
String localName,
String qName) throws SAXException {
if (currElem != null) {
if (currText != null) {
currElem.appendChild(currElem.getOwnerDocument().createCDATASection(currText.trim()));
currText = null;
}
if (currElem.getParentNode() == null) {
currObjNum++;
try {
onConstruct(currElem);
} catch (Throwable t) {
t.printStackTrace(System.err);
System.exit(-1);
}
if (finished)
throw new FinishedException();
currElem = (Element) currElem.getParentNode();
} else if (trimEmpty && currElem.getAttributes().getLength() == 0 && currElem.getChildNodes().getLength() == 0) {
// trim empty
Element p = (Element) currElem.getParentNode();
p.removeChild(currElem);
currElem = p;
} else
currElem = (Element) currElem.getParentNode();
}
}
protected abstract void onConstruct(
Element constructed);
@Override
public void characters(
char[] ch,
int start,
int length) throws SAXException {
String txt = new String(ch,
start,
length);
if (currElem != null) {
if (txt.length() > 0) {
if (currText == null)
currText = txt;
else
currText += txt;
}
}
}
private Document newDoc() {
try {
return factory.newDocumentBuilder().newDocument();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Unexcpected exception",
e);
}
}
}