/*
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
* Copyright (C) 2006, 2007, 2008 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 07. March 2004 by Joe Walnes
*/
package modmanager.utility.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.StreamException;
import com.thoughtworks.xstream.io.xml.AbstractXmlDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer;
import java.util.Stack;
import org.w3c.dom.Element;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.ext.DefaultHandler2;
import org.xml.sax.helpers.DefaultHandler;
/**
* This driver is used by XStream to load the ShirkitReader. Through that Reader I've solved the problem of getting blank spaces of Strings, solving one of the hardest problems I had to face. Without that Reader, it's not possible the Manager do it's job.
* @author Shirkit
*/
public class ShirkitDriver extends AbstractXmlDriver {
private final String encoding;
private final DocumentBuilderFactory documentBuilderFactory;
/**
* Construct a ShirkitDriver.
*/
public ShirkitDriver() {
this(null);
}
/**
* Construct a ShirkitDriver with a specified encoding. The created DomReader will ignore any
* encoding attribute of the XML header though.
*/
public ShirkitDriver(String encoding) {
this(encoding, new XmlFriendlyReplacer());
}
/**
* @since 1.2
*/
public ShirkitDriver(String encoding, XmlFriendlyReplacer replacer) {
super(replacer);
documentBuilderFactory = DocumentBuilderFactory.newInstance();
this.encoding = encoding;
}
public HierarchicalStreamReader createReader(Reader xml) {
return createReader(new InputSource(xml));
}
public HierarchicalStreamReader createReader(InputStream xml) {
return createReader(new InputSource(xml));
}
private HierarchicalStreamReader createReader(InputSource source) {
try {
//DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
if (encoding != null) {
source.setEncoding(encoding);
}
//Document document = documentBuilder.parse(source);
if (source.getByteStream() != null) {
Document document = readXML(source.getByteStream(), "lineStart", "lineEnd");
return new ShirkitReader(document, xmlFriendlyReplacer());
} else {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(source);
return new ShirkitReader(document, xmlFriendlyReplacer());
}
} catch (FactoryConfigurationError e) {
throw new StreamException(e);
} catch (ParserConfigurationException e) {
throw new StreamException(e);
} catch (SAXException e) {
throw new StreamException(e);
} catch (IOException e) {
throw new StreamException(e);
}
}
public static Document readXML(InputStream is, final String lineStartNumAttribName, final String lineEndNumAttribName) throws IOException, SAXException {
final Document doc;
SAXParser parser;
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
parser = factory.newSAXParser();
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
doc = docBuilder.newDocument();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Can't create SAX parser / DOM builder.", e);
}
final Stack<Element> elementStack = new Stack<Element>();
final StringBuilder textBuffer = new StringBuilder();
DefaultHandler handler = new DefaultHandler2() {
//DefaultHandler handler2 = new DefaultHandler() {
private Locator locator;
boolean insideCdata = false;
@Override
public void endCDATA() throws SAXException {
super.endCDATA();
addTextIfNeeded();
}
@Override
public void startCDATA() throws SAXException {
super.startCDATA();
addTextIfNeeded();
}
@Override
public void setDocumentLocator(Locator locator) {
this.locator = locator; //Save the locator, so that it can be used later for <b style="color:black;background-color:#99ff99">line</b> tracking when traversing nodes.
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
addTextIfNeeded();
Element el = doc.createElement(qName);
for (int i = 0; i < attributes.getLength(); i++) {
el.setAttribute(attributes.getQName(i), attributes.getValue(i));
}
el.setAttribute(lineStartNumAttribName, String.valueOf(locator.getLineNumber()));
elementStack.push(el);
}
@Override
public void endElement(String uri, String localName, String qName) {
addTextIfNeeded();
Element closedEl = elementStack.pop();
closedEl.setAttribute(lineEndNumAttribName, String.valueOf(locator.getLineNumber()));
if (elementStack.isEmpty()) { // Is this the root element?
doc.appendChild(closedEl);
} else {
Element parentEl = elementStack.peek();
parentEl.appendChild(closedEl);
}
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
textBuffer.append(ch, start, length);
}
// Outputs text accumulated under the current node
private void addTextIfNeeded() {
if (textBuffer.toString().trim().length() > 0) {
Element el = elementStack.peek();
Node textNode = doc.createTextNode(textBuffer.toString());
el.appendChild(textNode);
}
textBuffer.delete(0, textBuffer.length());
}
};
parser.getXMLReader().setContentHandler(handler);
parser.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
parser.parse(is, handler);
return doc;
}
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out, xmlFriendlyReplacer());
}
public HierarchicalStreamWriter createWriter(OutputStream out) {
try {
return createWriter(encoding != null
? new OutputStreamWriter(out, encoding)
: new OutputStreamWriter(out));
} catch (UnsupportedEncodingException e) {
throw new StreamException(e);
}
}
}