diff options
Diffstat (limited to 'infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java')
-rw-r--r-- | infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java b/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java new file mode 100644 index 0000000..d8ad495 --- /dev/null +++ b/infrastructure/rhino1_7R1/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java @@ -0,0 +1,445 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino DOM-only E4X implementation. + * + * The Initial Developer of the Original Code is + * David P. Caldwell. + * Portions created by David P. Caldwell are Copyright (C) + * 2007 David P. Caldwell. All Rights Reserved. + * + * + * Contributor(s): + * David P. Caldwell <inonit@inonit.com> + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript.xmlimpl; + +import org.w3c.dom.*; + +import javax.xml.parsers.DocumentBuilder; + +import org.mozilla.javascript.*; + +// Disambiguate from org.mozilla.javascript.Node +import org.w3c.dom.Node; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXParseException; + +class XmlProcessor { + private boolean ignoreComments; + private boolean ignoreProcessingInstructions; + private boolean ignoreWhitespace; + private boolean prettyPrint; + private int prettyIndent; + + private javax.xml.parsers.DocumentBuilderFactory dom; + private javax.xml.transform.TransformerFactory xform; + private DocumentBuilder documentBuilder; + private RhinoSAXErrorHandler errorHandler = new RhinoSAXErrorHandler(); + + + private static class RhinoSAXErrorHandler implements ErrorHandler { + + private void throwError(SAXParseException e) { + throw ScriptRuntime.constructError("TypeError", e.getMessage(), + e.getLineNumber() - 1); + } + + public void error(SAXParseException e) { + throwError(e); + } + + public void fatalError(SAXParseException e) { + throwError(e); + } + + public void warning(SAXParseException e) { + Context.reportWarning(e.getMessage()); + } + } + + XmlProcessor() { + setDefault(); + this.dom = javax.xml.parsers.DocumentBuilderFactory.newInstance(); + this.dom.setNamespaceAware(true); + this.dom.setIgnoringComments(false); + this.xform = javax.xml.transform.TransformerFactory.newInstance(); + } + + final void setDefault() { + this.setIgnoreComments(true); + this.setIgnoreProcessingInstructions(true); + this.setIgnoreWhitespace(true); + this.setPrettyPrinting(true); + this.setPrettyIndent(2); + } + + final void setIgnoreComments(boolean b) { + this.ignoreComments = b; + } + + final void setIgnoreWhitespace(boolean b) { + this.ignoreWhitespace = b; + } + + final void setIgnoreProcessingInstructions(boolean b) { + this.ignoreProcessingInstructions = b; + } + + final void setPrettyPrinting(boolean b) { + this.prettyPrint = b; + } + + final void setPrettyIndent(int i) { + this.prettyIndent = i; + } + + final boolean isIgnoreComments() { + return ignoreComments; + } + + final boolean isIgnoreProcessingInstructions() { + return ignoreProcessingInstructions; + } + + final boolean isIgnoreWhitespace() { + return ignoreWhitespace; + } + + final boolean isPrettyPrinting() { + return prettyPrint; + } + + final int getPrettyIndent() { + return prettyIndent; + } + + private String toXmlNewlines(String rv) { + StringBuffer nl = new StringBuffer(); + for (int i=0; i<rv.length(); i++) { + if (rv.charAt(i) == '\r') { + if (rv.charAt(i+1) == '\n') { + // DOS, do nothing and skip the \r + } else { + // Macintosh, substitute \n + nl.append('\n'); + } + } else { + nl.append(rv.charAt(i)); + } + } + return nl.toString(); + } + + private javax.xml.parsers.DocumentBuilderFactory getDomFactory() { + return dom; + } + + private synchronized DocumentBuilder getDocumentBuilderFromPool() + throws javax.xml.parsers.ParserConfigurationException + { + DocumentBuilder result; + if (documentBuilder == null) { + javax.xml.parsers.DocumentBuilderFactory factory = getDomFactory(); + result = factory.newDocumentBuilder(); + } else { + result = documentBuilder; + documentBuilder = null; + } + result.setErrorHandler(errorHandler); + return result; + } + + private synchronized void returnDocumentBuilderToPool(DocumentBuilder db) { + if (documentBuilder == null) { + documentBuilder = db; + documentBuilder.reset(); + } + } + + private void addProcessingInstructionsTo(java.util.Vector v, Node node) { + if (node instanceof ProcessingInstruction) { + v.add(node); + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addProcessingInstructionsTo(v, node.getChildNodes().item(i)); + } + } + } + + private void addCommentsTo(java.util.Vector v, Node node) { + if (node instanceof Comment) { + v.add(node); + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addProcessingInstructionsTo(v, node.getChildNodes().item(i)); + } + } + } + + private void addTextNodesToRemoveAndTrim(java.util.Vector toRemove, Node node) { + if (node instanceof Text) { + Text text = (Text)node; + boolean BUG_369394_IS_VALID = false; + if (!BUG_369394_IS_VALID) { + text.setData(text.getData().trim()); + } else { + if (text.getData().trim().length() == 0) { + text.setData(""); + } + } + if (text.getData().length() == 0) { + toRemove.add(node); + } + } + if (node.getChildNodes() != null) { + for (int i=0; i<node.getChildNodes().getLength(); i++) { + addTextNodesToRemoveAndTrim(toRemove, node.getChildNodes().item(i)); + } + } + } + + final Node toXml(String defaultNamespaceUri, String xml) throws org.xml.sax.SAXException { + // See ECMA357 10.3.1 + DocumentBuilder builder = null; + try { + String syntheticXml = "<parent xmlns=\"" + defaultNamespaceUri + + "\">" + xml + "</parent>"; + builder = getDocumentBuilderFromPool(); + Document document = builder.parse( new org.xml.sax.InputSource(new java.io.StringReader(syntheticXml)) ); + if (ignoreProcessingInstructions) { + java.util.Vector v = new java.util.Vector(); + addProcessingInstructionsTo(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + if (ignoreComments) { + java.util.Vector v = new java.util.Vector(); + addCommentsTo(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + if (ignoreWhitespace) { + // Apparently JAXP setIgnoringElementContentWhitespace() has a different meaning, it appears from the Javadoc + // Refers to element-only content models, which means we would need to have a validating parser and DTD or schema + // so that it would know which whitespace to ignore. + + // Instead we will try to delete it ourselves. + java.util.Vector v = new java.util.Vector(); + addTextNodesToRemoveAndTrim(v, document); + for (int i=0; i<v.size(); i++) { + Node node = (Node)v.elementAt(i); + node.getParentNode().removeChild(node); + } + } + NodeList rv = document.getDocumentElement().getChildNodes(); + if (rv.getLength() > 1) { + throw ScriptRuntime.constructError("SyntaxError", "XML objects may contain at most one node."); + } else if (rv.getLength() == 0) { + Node node = document.createTextNode(""); + return node; + } else { + Node node = rv.item(0); + document.getDocumentElement().removeChild(node); + return node; + } + } catch (java.io.IOException e) { + throw new RuntimeException("Unreachable."); + } catch (javax.xml.parsers.ParserConfigurationException e) { + throw new RuntimeException(e); + } finally { + if (builder != null) + returnDocumentBuilderToPool(builder); + } + } + + Document newDocument() { + DocumentBuilder builder = null; + try { + // TODO Should this use XML settings? + builder = getDocumentBuilderFromPool(); + return builder.newDocument(); + } catch (javax.xml.parsers.ParserConfigurationException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } finally { + if (builder != null) + returnDocumentBuilderToPool(builder); + } + } + + // TODO Cannot remember what this is for, so whether it should use settings or not + private String toString(Node node) { + javax.xml.transform.dom.DOMSource source = new javax.xml.transform.dom.DOMSource(node); + java.io.StringWriter writer = new java.io.StringWriter(); + javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(writer); + try { + javax.xml.transform.Transformer transformer = xform.newTransformer(); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "no"); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml"); + transformer.transform(source, result); + } catch (javax.xml.transform.TransformerConfigurationException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } catch (javax.xml.transform.TransformerException ex) { + // TODO How to handle these runtime errors? + throw new RuntimeException(ex); + } + return toXmlNewlines(writer.toString()); + } + + String escapeAttributeValue(Object value) { + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return ""; + + Document dom = newDocument(); + Element e = dom.createElement("a"); + e.setAttribute("b", text); + String elementText = toString(e); + int begin = elementText.indexOf('"'); + int end = elementText.lastIndexOf('"'); + return elementText.substring(begin+1,end); + } + + String escapeTextValue(Object value) { + if (value instanceof XMLObjectImpl) { + return ((XMLObjectImpl)value).toXMLString(); + } + + String text = ScriptRuntime.toString(value); + + if (text.length() == 0) return text; + + Document dom = newDocument(); + Element e = dom.createElement("a"); + e.setTextContent(text); + String elementText = toString(e); + + int begin = elementText.indexOf('>') + 1; + int end = elementText.lastIndexOf('<'); + return (begin < end) ? elementText.substring(begin, end) : ""; + } + + private String escapeElementValue(String s) { + // TODO Check this + return escapeTextValue(s); + } + + private String elementToXmlString(Element element) { + // TODO My goodness ECMA is complicated (see 10.2.1). We'll try this first. + Element copy = (Element)element.cloneNode(true); + if (prettyPrint) { + beautifyElement(copy, 0); + } + return toString(copy); + } + + final String ecmaToXmlString(Node node) { + // See ECMA 357 Section 10.2.1 + StringBuffer s = new StringBuffer(); + int indentLevel = 0; + if (prettyPrint) { + for (int i=0; i<indentLevel; i++) { + s.append(' '); + } + } + if (node instanceof Text) { + String data = ((Text)node).getData(); + // TODO Does Java trim() work same as XMLWhitespace? + String v = (prettyPrint) ? data.trim() : data; + s.append(escapeElementValue(v)); + return s.toString(); + } + if (node instanceof Attr) { + String value = ((Attr)node).getValue(); + s.append(escapeAttributeValue(value)); + return s.toString(); + } + if (node instanceof Comment) { + s.append("<!--" + ((Comment)node).getNodeValue() + "-->"); + return s.toString(); + } + if (node instanceof ProcessingInstruction) { + ProcessingInstruction pi = (ProcessingInstruction)node; + s.append("<?" + pi.getTarget() + " " + pi.getData() + "?>"); + return s.toString(); + } + s.append(elementToXmlString((Element)node)); + return s.toString(); + } + + private void beautifyElement(Element e, int indent) { + StringBuffer s = new StringBuffer(); + s.append('\n'); + for (int i=0; i<indent; i++) { + s.append(' '); + } + String afterContent = s.toString(); + for (int i=0; i<prettyIndent; i++) { + s.append(' '); + } + String beforeContent = s.toString(); + + // We "mark" all the nodes first; if we tried to do this loop otherwise, it would behave unexpectedly (the inserted nodes + // would contribute to the length and it might never terminate). + java.util.Vector toIndent = new java.util.Vector(); + boolean indentChildren = false; + for (int i=0; i<e.getChildNodes().getLength(); i++) { + if (i == 1) indentChildren = true; + if (e.getChildNodes().item(i) instanceof Text) { + toIndent.add(e.getChildNodes().item(i)); + } else { + indentChildren = true; + toIndent.add(e.getChildNodes().item(i)); + } + } + if (indentChildren) { + for (int i=0; i<toIndent.size(); i++) { + e.insertBefore( e.getOwnerDocument().createTextNode(beforeContent), (Node)toIndent.elementAt(i) ); + } + } + NodeList nodes = e.getChildNodes(); + java.util.Vector v = new java.util.Vector(); + for (int i=0; i<nodes.getLength(); i++) { + if (nodes.item(i) instanceof Element) { + v.add( nodes.item(i) ); + } + } + for (int i=0; i<v.size(); i++) { + beautifyElement( (Element)v.elementAt(i), indent + prettyIndent ); + } + if (indentChildren) { + e.appendChild( e.getOwnerDocument().createTextNode(afterContent) ); + } + } +} |