| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Sep 14, 2000: |
| // Fixed comments to preserve whitespaces and add a line break |
| // when indenting. Reported by Gervase Markham <gerv@gerv.net> |
| // Sep 14, 2000: |
| // Fixed serializer to report IO exception directly, instead at |
| // the end of document processing. |
| // Reported by Patrick Higgins <phiggins@transzap.com> |
| // Sep 13, 2000: |
| // CR in character data will print as �D; |
| // Aug 25, 2000: |
| // Fixed processing instruction printing inside element content |
| // to not escape content. Reported by Mikael Staldal |
| // <d96-mst@d.kth.se> |
| // Aug 25, 2000: |
| // Added ability to omit comments. |
| // Contributed by Anupam Bagchi <abagchi@jtcsv.com> |
| // Aug 26, 2000: |
| // Fixed bug in newline handling when preserving spaces. |
| // Contributed by Mike Dusseault <mdusseault@home.com> |
| // Aug 29, 2000: |
| // Fixed state.unescaped not being set to false when |
| // entering element state. |
| // Reported by Lowell Vaughn <lvaughn@agillion.com> |
| |
| |
| package org.apache.jackrabbit.vault.util.xml.serialize; |
| |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.Writer; |
| import java.util.Map; |
| import java.util.TreeMap; |
| import java.util.Vector; |
| |
| import org.apache.jackrabbit.vault.util.xml.xerces.dom.DOMErrorImpl; |
| import org.apache.jackrabbit.vault.util.xml.xerces.dom.DOMLocatorImpl; |
| import org.apache.jackrabbit.vault.util.xml.xerces.dom.DOMMessageFormatter; |
| import org.apache.jackrabbit.vault.util.xml.xerces.util.XMLChar; |
| import org.w3c.dom.DOMError; |
| import org.w3c.dom.DOMErrorHandler; |
| import org.w3c.dom.DOMImplementation; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentFragment; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Entity; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Notation; |
| import org.w3c.dom.ls.LSSerializer; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.DTDHandler; |
| import org.xml.sax.DocumentHandler; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.ext.DeclHandler; |
| import org.xml.sax.ext.LexicalHandler; |
| |
| /** |
| * Base class for a serializer supporting both DOM and SAX pretty |
| * serializing of XML/HTML/XHTML documents. Derives classes perform |
| * the method-specific serializing, this class provides the common |
| * serializing mechanisms. |
| * <p> |
| * The serializer must be initialized with the proper writer and |
| * output format before it can be used by calling {@link #setOutputCharStream} |
| * or {@link #setOutputByteStream} for the writer and {@link #setOutputFormat} |
| * for the output format. |
| * <p> |
| * The serializer can be reused any number of times, but cannot |
| * be used concurrently by two threads. |
| * <p> |
| * If an output stream is used, the encoding is taken from the |
| * output format (defaults to <tt>UTF-8</tt>). If a writer is |
| * used, make sure the writer uses the same encoding (if applies) |
| * as specified in the output format. |
| * <p> |
| * The serializer supports both DOM and SAX. DOM serializing is done |
| * by calling {@link #serialize(Document)} and SAX serializing is done by firing |
| * SAX events and using the serializer as a document handler. |
| * This also applies to derived class. |
| * <p> |
| * If an I/O exception occurs while serializing, the serializer |
| * will not throw an exception directly, but only throw it |
| * at the end of serializing (either DOM or SAX's {@link |
| * org.xml.sax.DocumentHandler#endDocument}. |
| * <p> |
| * For elements that are not specified as whitespace preserving, |
| * the serializer will potentially break long text lines at space |
| * boundaries, indent lines, and serialize elements on separate |
| * lines. Line terminators will be regarded as spaces, and |
| * spaces at beginning of line will be stripped. |
| * <p> |
| * When indenting, the serializer is capable of detecting seemingly |
| * element content, and serializing these elements indented on separate |
| * lines. An element is serialized indented when it is the first or |
| * last child of an element, or immediate following or preceding |
| * another element. |
| * |
| * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a> |
| * @author <a href="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a> |
| * @author Elena Litani, IBM |
| * @see Serializer |
| * @see LSSerializer |
| */ |
| public abstract class BaseMarkupSerializer |
| implements ContentHandler, DocumentHandler, LexicalHandler, |
| DTDHandler, DeclHandler, DOMSerializer, Serializer { |
| |
| // DOM L3 implementation |
| protected short features = 0xFFFFFFFF; |
| protected DOMErrorHandler fDOMErrorHandler; |
| protected final DOMErrorImpl fDOMError = new DOMErrorImpl(); |
| //protected LSSerializerFilter fDOMFilter; |
| |
| protected EncodingInfo _encodingInfo; |
| |
| |
| /** |
| * Holds array of all element states that have been entered. |
| * The array is automatically resized. When leaving an element, |
| * it's state is not removed but reused when later returning |
| * to the same nesting level. |
| */ |
| private ElementState[] _elementStates; |
| |
| |
| /** |
| * The index of the next state to place in the array, |
| * or one plus the index of the current state. When zero, |
| * we are in no state. |
| */ |
| private int _elementStateCount; |
| |
| |
| /** |
| * Vector holding comments and PIs that come before the root |
| * element (even after it), see {@link #serializePreRoot}. |
| */ |
| private Vector _preRoot; |
| |
| |
| /** |
| * If the document has been started (header serialized), this |
| * flag is set to true so it's not started twice. |
| */ |
| protected boolean _started; |
| |
| |
| /** |
| * True if the serializer has been prepared. This flag is set |
| * to false when the serializer is reset prior to using it, |
| * and to true after it has been prepared for usage. |
| */ |
| private boolean _prepared; |
| |
| |
| /** |
| * Association between namespace URIs (keys) and prefixes (values). |
| * Accumulated here prior to starting an element and placing this |
| * list in the element state. |
| */ |
| protected Map<String, String> _prefixes; |
| |
| |
| /** |
| * The system identifier of the document type, if known. |
| */ |
| protected String _docTypePublicId; |
| |
| |
| /** |
| * The system identifier of the document type, if known. |
| */ |
| protected String _docTypeSystemId; |
| |
| |
| /** |
| * The output format associated with this serializer. This will never |
| * be a null reference. If no format was passed to the constructor, |
| * the default one for this document type will be used. The format |
| * object is never changed by the serializer. |
| */ |
| protected OutputFormat _format; |
| |
| |
| /** |
| * The printer used for printing text parts. |
| */ |
| protected Printer _printer; |
| |
| |
| /** |
| * True if indenting printer. |
| */ |
| protected boolean _indenting; |
| |
| /** |
| * Temporary buffer to store character data |
| */ |
| protected final StringBuffer fStrBuffer = new StringBuffer(40); |
| |
| /** |
| * The underlying writer. |
| */ |
| private Writer _writer; |
| |
| |
| /** |
| * The output stream. |
| */ |
| private OutputStream _output; |
| |
| /** |
| * Current node that is being processed |
| */ |
| protected Node fCurrentNode = null; |
| |
| //--------------------------------// |
| // Constructor and initialization // |
| //--------------------------------// |
| |
| |
| /** |
| * Protected constructor can only be used by derived class. |
| * Must initialize the serializer before serializing any document, |
| * by calling {@link #setOutputCharStream} or {@link #setOutputByteStream} |
| * first |
| */ |
| protected BaseMarkupSerializer(OutputFormat format) { |
| int i; |
| |
| _elementStates = new ElementState[10]; |
| for (i = 0; i < _elementStates.length; ++i) |
| _elementStates[i] = new ElementState(); |
| _format = format; |
| } |
| |
| |
| public DocumentHandler asDocumentHandler() |
| throws IOException { |
| prepare(); |
| return this; |
| } |
| |
| |
| public ContentHandler asContentHandler() |
| throws IOException { |
| prepare(); |
| return this; |
| } |
| |
| |
| public DOMSerializer asDOMSerializer() |
| throws IOException { |
| prepare(); |
| return this; |
| } |
| |
| /** |
| * <p>The specified stream will not be closed by this class. |
| */ |
| public void setOutputByteStream(OutputStream output) { |
| if (output == null) { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "ArgumentIsNull", new Object[]{"output"}); |
| throw new NullPointerException(msg); |
| } |
| _output = output; |
| _writer = null; |
| reset(); |
| } |
| |
| |
| /** |
| * <p>The specified writer will not be closed by this class. |
| */ |
| public void setOutputCharStream(Writer writer) { |
| if (writer == null) { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "ArgumentIsNull", new Object[]{"writer"}); |
| throw new NullPointerException(msg); |
| } |
| _writer = writer; |
| _output = null; |
| reset(); |
| } |
| |
| |
| public void setOutputFormat(OutputFormat format) { |
| if (format == null) { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "ArgumentIsNull", new Object[]{"format"}); |
| throw new NullPointerException(msg); |
| } |
| _format = format; |
| reset(); |
| } |
| |
| |
| public boolean reset() { |
| if (_elementStateCount > 1) { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "ResetInMiddle", null); |
| throw new IllegalStateException(msg); |
| } |
| _prepared = false; |
| fCurrentNode = null; |
| fStrBuffer.setLength(0); |
| return true; |
| } |
| |
| |
| protected void prepare() |
| throws IOException { |
| if (_prepared) |
| return; |
| |
| if (_writer == null && _output == null) { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "NoWriterSupplied", null); |
| throw new IOException(msg); |
| } |
| // If the output stream has been set, use it to construct |
| // the writer. It is possible that the serializer has been |
| // reused with the same output stream and different encoding. |
| |
| _encodingInfo = _format.getEncodingInfo(); |
| |
| if (_output != null) { |
| _writer = _encodingInfo.getWriter(_output); |
| } |
| |
| if (_format.getIndenting()) { |
| _indenting = true; |
| _printer = new IndentPrinter(_writer, _format); |
| } else { |
| _indenting = false; |
| _printer = new Printer(_writer, _format); |
| } |
| |
| ElementState state; |
| |
| _elementStateCount = 0; |
| state = _elementStates[0]; |
| state.namespaceURI = null; |
| state.localName = null; |
| state.rawName = null; |
| state.preserveSpace = _format.getPreserveSpace(); |
| state.empty = true; |
| state.afterElement = false; |
| state.afterComment = false; |
| state.doCData = state.inCData = false; |
| state.prefixes = null; |
| |
| _docTypePublicId = _format.getDoctypePublic(); |
| _docTypeSystemId = _format.getDoctypeSystem(); |
| _started = false; |
| _prepared = true; |
| } |
| |
| //----------------------------------// |
| // DOM document serializing methods // |
| //----------------------------------// |
| |
| |
| /** |
| * Serializes the DOM element using the previously specified |
| * writer and output format. Throws an exception only if |
| * an I/O exception occured while serializing. |
| * |
| * @param elem The element to serialize |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| public void serialize(Element elem) |
| throws IOException { |
| reset(); |
| prepare(); |
| serializeNode(elem); |
| _printer.flush(); |
| if (_printer.getException() != null) |
| throw _printer.getException(); |
| } |
| |
| |
| /** |
| * Serializes the DOM document fragmnt using the previously specified |
| * writer and output format. Throws an exception only if |
| * an I/O exception occured while serializing. |
| * |
| * @param frag The element to serialize |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| public void serialize(DocumentFragment frag) |
| throws IOException { |
| reset(); |
| prepare(); |
| serializeNode(frag); |
| _printer.flush(); |
| if (_printer.getException() != null) |
| throw _printer.getException(); |
| } |
| |
| |
| /** |
| * Serializes the DOM document using the previously specified |
| * writer and output format. Throws an exception only if |
| * an I/O exception occured while serializing. |
| * |
| * @param doc The document to serialize |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| public void serialize(Document doc) |
| throws IOException { |
| reset(); |
| prepare(); |
| serializeNode(doc); |
| serializePreRoot(); |
| _printer.flush(); |
| if (_printer.getException() != null) |
| throw _printer.getException(); |
| } |
| |
| //------------------------------------------// |
| // SAX document handler serializing methods // |
| //------------------------------------------// |
| |
| |
| public void startDocument() |
| throws SAXException { |
| try { |
| prepare(); |
| } catch (IOException except) { |
| throw new SAXException(except.toString()); |
| } |
| // Nothing to do here. All the magic happens in startDocument(String) |
| } |
| |
| |
| public void characters(char[] chars, int start, int length) |
| throws SAXException { |
| ElementState state; |
| |
| try { |
| state = content(); |
| |
| // Check if text should be print as CDATA section or unescaped |
| // based on elements listed in the output format (the element |
| // state) or whether we are inside a CDATA section or entity. |
| |
| if (state.inCData || state.doCData) { |
| int saveIndent; |
| |
| // Print a CDATA section. The text is not escaped, but ']]>' |
| // appearing in the code must be identified and dealt with. |
| // The contents of a text node is considered space preserving. |
| if (!state.inCData) { |
| _printer.printText("<![CDATA["); |
| state.inCData = true; |
| } |
| saveIndent = _printer.getNextIndent(); |
| _printer.setNextIndent(0); |
| char ch; |
| for (int index = start; index < length; ++index) { |
| ch = chars[index]; |
| if (ch == ']' && index + 2 < length && |
| chars[index + 1] == ']' && chars[index + 2] == '>') { |
| _printer.printText("]]]]><![CDATA[>"); |
| index += 2; |
| continue; |
| } |
| if (!XMLChar.isValid(ch)) { |
| // check if it is surrogate |
| if (++index < length) { |
| surrogates(ch, chars[index]); |
| } else { |
| fatalError("The character '" + (char) ch + "' is an invalid XML character"); |
| } |
| continue; |
| } else { |
| if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch) && ch != 0xF7) || |
| ch == '\n' || ch == '\r' || ch == '\t') { |
| _printer.printText((char) ch); |
| } else { |
| // The character is not printable -- split CDATA section |
| _printer.printText("]]>&#x"); |
| _printer.printText(Integer.toHexString(ch)); |
| _printer.printText(";<![CDATA["); |
| } |
| } |
| } |
| _printer.setNextIndent(saveIndent); |
| |
| } else { |
| |
| int saveIndent; |
| |
| if (state.preserveSpace) { |
| // If preserving space then hold of indentation so no |
| // excessive spaces are printed at line breaks, escape |
| // the text content without replacing spaces and print |
| // the text breaking only at line breaks. |
| saveIndent = _printer.getNextIndent(); |
| _printer.setNextIndent(0); |
| printText(chars, start, length, true, state.unescaped); |
| _printer.setNextIndent(saveIndent); |
| } else { |
| printText(chars, start, length, false, state.unescaped); |
| } |
| } |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void ignorableWhitespace(char[] chars, int start, int length) |
| throws SAXException { |
| int i; |
| |
| try { |
| content(); |
| |
| // Print ignorable whitespaces only when indenting, after |
| // all they are indentation. Cancel the indentation to |
| // not indent twice. |
| if (_indenting) { |
| _printer.setThisIndent(0); |
| for (i = start; length-- > 0; ++i) |
| _printer.printText(chars[i]); |
| } |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public final void processingInstruction(String target, String code) |
| throws SAXException { |
| try { |
| processingInstructionIO(target, code); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| public void processingInstructionIO(String target, String code) |
| throws IOException { |
| int index; |
| ElementState state; |
| |
| state = content(); |
| |
| // Create the processing instruction textual representation. |
| // Make sure we don't have '?>' inside either target or code. |
| index = target.indexOf("?>"); |
| if (index >= 0) |
| fStrBuffer.append("<?").append(target.substring(0, index)); |
| else |
| fStrBuffer.append("<?").append(target); |
| if (code != null) { |
| fStrBuffer.append(' '); |
| index = code.indexOf("?>"); |
| if (index >= 0) |
| fStrBuffer.append(code.substring(0, index)); |
| else |
| fStrBuffer.append(code); |
| } |
| fStrBuffer.append("?>"); |
| |
| // If before the root element (or after it), do not print |
| // the PI directly but place it in the pre-root vector. |
| if (isDocumentState()) { |
| if (_preRoot == null) |
| _preRoot = new Vector(); |
| _preRoot.addElement(fStrBuffer.toString()); |
| } else { |
| _printer.indent(); |
| printText(fStrBuffer.toString(), true, true); |
| _printer.unindent(); |
| if (_indenting) |
| state.afterElement = true; |
| } |
| |
| fStrBuffer.setLength(0); |
| } |
| |
| |
| public void comment(char[] chars, int start, int length) |
| throws SAXException { |
| try { |
| comment(new String(chars, start, length)); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void comment(String text) |
| throws IOException { |
| int index; |
| ElementState state; |
| |
| if (_format.getOmitComments()) |
| return; |
| |
| state = content(); |
| // Create the processing comment textual representation. |
| // Make sure we don't have '-->' inside the comment. |
| index = text.indexOf("-->"); |
| if (index >= 0) |
| fStrBuffer.append("<!--").append(text.substring(0, index)).append("-->"); |
| else |
| fStrBuffer.append("<!--").append(text).append("-->"); |
| |
| // If before the root element (or after it), do not print |
| // the comment directly but place it in the pre-root vector. |
| if (isDocumentState()) { |
| if (_preRoot == null) |
| _preRoot = new Vector(); |
| _preRoot.addElement(fStrBuffer.toString()); |
| } else { |
| // Indent this element on a new line if the first |
| // content of the parent element or immediately |
| // following an element. |
| if (_indenting && !state.preserveSpace) |
| _printer.breakLine(); |
| _printer.indent(); |
| printText(fStrBuffer.toString(), true, true); |
| _printer.unindent(); |
| if (_indenting) |
| state.afterElement = true; |
| } |
| |
| fStrBuffer.setLength(0); |
| state.afterComment = true; |
| state.afterElement = false; |
| } |
| |
| |
| public void startCDATA() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.doCData = true; |
| } |
| |
| |
| public void endCDATA() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.doCData = false; |
| } |
| |
| |
| public void startNonEscaping() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.unescaped = true; |
| } |
| |
| |
| public void endNonEscaping() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.unescaped = false; |
| } |
| |
| |
| public void startPreserving() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.preserveSpace = true; |
| } |
| |
| |
| public void endPreserving() { |
| ElementState state; |
| |
| state = getElementState(); |
| state.preserveSpace = false; |
| } |
| |
| |
| /** |
| * Called at the end of the document to wrap it up. |
| * Will flush the output stream and throw an exception |
| * if any I/O error occured while serializing. |
| * |
| * @throws SAXException An I/O exception occured during |
| * serializing |
| */ |
| public void endDocument() |
| throws SAXException { |
| try { |
| // Print all the elements accumulated outside of |
| // the root element. |
| serializePreRoot(); |
| // Flush the output, this is necessary for fStrBuffered output. |
| _printer.flush(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void startEntity(String name) { |
| // ??? |
| } |
| |
| |
| public void endEntity(String name) { |
| // ??? |
| } |
| |
| |
| public void setDocumentLocator(Locator locator) { |
| // Nothing to do |
| } |
| |
| //-----------------------------------------// |
| // SAX content handler serializing methods // |
| //-----------------------------------------// |
| |
| |
| public void skippedEntity(String name) |
| throws SAXException { |
| try { |
| endCDATA(); |
| content(); |
| _printer.printText('&'); |
| _printer.printText(name); |
| _printer.printText(';'); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void startPrefixMapping(String prefix, String uri) |
| throws SAXException { |
| if (_prefixes == null) |
| _prefixes = new TreeMap<String, String>(); |
| _prefixes.put(uri, prefix == null ? "" : prefix); |
| } |
| |
| |
| public void endPrefixMapping(String prefix) |
| throws SAXException { |
| } |
| |
| //------------------------------------------// |
| // SAX DTD/Decl handler serializing methods // |
| //------------------------------------------// |
| |
| |
| public final void startDTD(String name, String publicId, String systemId) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| _docTypePublicId = publicId; |
| _docTypeSystemId = systemId; |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void endDTD() { |
| // Nothing to do here, all the magic occurs in startDocument(String). |
| } |
| |
| |
| public void elementDecl(String name, String model) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| _printer.printText("<!ELEMENT "); |
| _printer.printText(name); |
| _printer.printText(' '); |
| _printer.printText(model); |
| _printer.printText('>'); |
| if (_indenting) |
| _printer.breakLine(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void attributeDecl(String eName, String aName, String type, |
| String valueDefault, String value) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| _printer.printText("<!ATTLIST "); |
| _printer.printText(eName); |
| _printer.printText(' '); |
| _printer.printText(aName); |
| _printer.printText(' '); |
| _printer.printText(type); |
| if (valueDefault != null) { |
| _printer.printText(' '); |
| _printer.printText(valueDefault); |
| } |
| if (value != null) { |
| _printer.printText(" \""); |
| printEscaped(value); |
| _printer.printText('"'); |
| } |
| _printer.printText('>'); |
| if (_indenting) |
| _printer.breakLine(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void internalEntityDecl(String name, String value) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| _printer.printText("<!ENTITY "); |
| _printer.printText(name); |
| _printer.printText(" \""); |
| printEscaped(value); |
| _printer.printText("\">"); |
| if (_indenting) |
| _printer.breakLine(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void externalEntityDecl(String name, String publicId, String systemId) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| unparsedEntityDecl(name, publicId, systemId, null); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void unparsedEntityDecl(String name, String publicId, |
| String systemId, String notationName) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| if (publicId == null) { |
| _printer.printText("<!ENTITY "); |
| _printer.printText(name); |
| _printer.printText(" SYSTEM "); |
| printDoctypeURL(systemId); |
| } else { |
| _printer.printText("<!ENTITY "); |
| _printer.printText(name); |
| _printer.printText(" PUBLIC "); |
| printDoctypeURL(publicId); |
| _printer.printText(' '); |
| printDoctypeURL(systemId); |
| } |
| if (notationName != null) { |
| _printer.printText(" NDATA "); |
| _printer.printText(notationName); |
| } |
| _printer.printText('>'); |
| if (_indenting) |
| _printer.breakLine(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| |
| public void notationDecl(String name, String publicId, String systemId) |
| throws SAXException { |
| try { |
| _printer.enterDTD(); |
| if (publicId != null) { |
| _printer.printText("<!NOTATION "); |
| _printer.printText(name); |
| _printer.printText(" PUBLIC "); |
| printDoctypeURL(publicId); |
| if (systemId != null) { |
| _printer.printText(' '); |
| printDoctypeURL(systemId); |
| } |
| } else { |
| _printer.printText("<!NOTATION "); |
| _printer.printText(name); |
| _printer.printText(" SYSTEM "); |
| printDoctypeURL(systemId); |
| } |
| _printer.printText('>'); |
| if (_indenting) |
| _printer.breakLine(); |
| } catch (IOException except) { |
| throw new SAXException(except); |
| } |
| } |
| |
| //------------------------------------------// |
| // Generic node serializing methods methods // |
| //------------------------------------------// |
| |
| |
| /** |
| * Serialize the DOM node. This method is shared across XML, HTML and XHTML |
| * serializers and the differences are masked out in a separate {@link |
| * #serializeElement}. |
| * |
| * @param node The node to serialize |
| * @throws IOException An I/O exception occured while |
| * serializing |
| * @see #serializeElement |
| */ |
| protected void serializeNode(Node node) |
| throws IOException { |
| fCurrentNode = node; |
| |
| // Based on the node type call the suitable SAX handler. |
| // Only comments entities and documents which are not |
| // handled by SAX are serialized directly. |
| switch (node.getNodeType()) { |
| case Node.TEXT_NODE: { |
| String text; |
| |
| text = node.getNodeValue(); |
| if (text != null) { |
| /* |
| if (fDOMFilter != null && |
| (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_TEXT) != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: |
| case NodeFilter.FILTER_SKIP: { |
| break; |
| } |
| default: { |
| characters(text); |
| } |
| } |
| } else */ |
| if (!_indenting || getElementState().preserveSpace |
| || (text.replace('\n', ' ').trim().length() != 0)) |
| characters(text); |
| |
| } |
| break; |
| } |
| |
| case Node.CDATA_SECTION_NODE: { |
| String text = node.getNodeValue(); |
| if ((features & (0x1 << 3) /*DOMSerializerImpl.CDATA*/) != 0) { |
| if (text != null) { |
| /* |
| if (fDOMFilter != null |
| && (fDOMFilter.getWhatToShow() |
| & NodeFilter.SHOW_CDATA_SECTION) |
| != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: |
| case NodeFilter.FILTER_SKIP: { |
| // skip the CDATA node |
| return; |
| } |
| default: { |
| //fall through.. |
| } |
| } |
| } |
| */ |
| startCDATA(); |
| characters(text); |
| endCDATA(); |
| } |
| } else { |
| // transform into a text node |
| characters(text); |
| } |
| break; |
| } |
| case Node.COMMENT_NODE: { |
| String text; |
| |
| if (!_format.getOmitComments()) { |
| text = node.getNodeValue(); |
| if (text != null) { |
| /* |
| if (fDOMFilter != null && |
| (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_COMMENT) != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: |
| case NodeFilter.FILTER_SKIP: { |
| // skip the comment node |
| return; |
| } |
| default: { |
| // fall through |
| } |
| } |
| } |
| */ |
| comment(text); |
| } |
| } |
| break; |
| } |
| |
| case Node.ENTITY_REFERENCE_NODE: { |
| Node child; |
| |
| endCDATA(); |
| content(); |
| |
| if (((features & (0x1 << 2)/*DOMSerializerImpl.ENTITIES*/) != 0) |
| || (node.getFirstChild() == null)) { |
| /* |
| if (fDOMFilter != null && |
| (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ENTITY_REFERENCE) != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: { |
| return; // remove the node |
| } |
| case NodeFilter.FILTER_SKIP: { |
| child = node.getFirstChild(); |
| while (child != null) { |
| serializeNode(child); |
| child = child.getNextSibling(); |
| } |
| return; |
| } |
| |
| default: { |
| // fall through |
| } |
| } |
| } |
| */ |
| checkUnboundNamespacePrefixedNode(node); |
| |
| _printer.printText("&"); |
| _printer.printText(node.getNodeName()); |
| _printer.printText(";"); |
| } else { |
| child = node.getFirstChild(); |
| while (child != null) { |
| serializeNode(child); |
| child = child.getNextSibling(); |
| } |
| } |
| |
| break; |
| } |
| |
| case Node.PROCESSING_INSTRUCTION_NODE: { |
| /* |
| if (fDOMFilter != null && |
| (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_PROCESSING_INSTRUCTION) != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: |
| case NodeFilter.FILTER_SKIP: { |
| return; // skip this node |
| } |
| default: { // fall through |
| } |
| } |
| } |
| */ |
| processingInstructionIO(node.getNodeName(), node.getNodeValue()); |
| break; |
| } |
| case Node.ELEMENT_NODE: { |
| /* |
| if (fDOMFilter != null && |
| (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ELEMENT) != 0) { |
| short code = fDOMFilter.acceptNode(node); |
| switch (code) { |
| case NodeFilter.FILTER_REJECT: { |
| return; |
| } |
| case NodeFilter.FILTER_SKIP: { |
| Node child = node.getFirstChild(); |
| while (child != null) { |
| serializeNode(child); |
| child = child.getNextSibling(); |
| } |
| return; // skip this node |
| } |
| |
| default: { // fall through |
| } |
| } |
| } |
| */ |
| serializeElement((Element) node); |
| break; |
| } |
| case Node.DOCUMENT_NODE: { |
| DocumentType docType; |
| DOMImplementation domImpl; |
| NamedNodeMap map; |
| Entity entity; |
| Notation notation; |
| int i; |
| |
| // If there is a document type, use the SAX events to |
| // serialize it. |
| docType = ((Document) node).getDoctype(); |
| if (docType != null) { |
| // DOM Level 2 (or higher) |
| domImpl = ((Document) node).getImplementation(); |
| try { |
| String internal; |
| |
| _printer.enterDTD(); |
| _docTypePublicId = docType.getPublicId(); |
| _docTypeSystemId = docType.getSystemId(); |
| internal = docType.getInternalSubset(); |
| if (internal != null && internal.length() > 0) |
| _printer.printText(internal); |
| endDTD(); |
| } |
| // DOM Level 1 -- does implementation have methods? |
| catch (NoSuchMethodError nsme) { |
| Class docTypeClass = docType.getClass(); |
| |
| String docTypePublicId = null; |
| String docTypeSystemId = null; |
| try { |
| java.lang.reflect.Method getPublicId = docTypeClass.getMethod("getPublicId"); |
| if (getPublicId.getReturnType().equals(String.class)) { |
| docTypePublicId = (String) getPublicId.invoke(docType); |
| } |
| } |
| catch (Exception e) { |
| // ignore |
| } |
| try { |
| java.lang.reflect.Method getSystemId = docTypeClass.getMethod("getSystemId"); |
| if (getSystemId.getReturnType().equals(String.class)) { |
| docTypeSystemId = (String) getSystemId.invoke(docType); |
| } |
| } |
| catch (Exception e) { |
| // ignore |
| } |
| _printer.enterDTD(); |
| _docTypePublicId = docTypePublicId; |
| _docTypeSystemId = docTypeSystemId; |
| endDTD(); |
| } |
| } |
| // !! Fall through |
| } |
| case Node.DOCUMENT_FRAGMENT_NODE: { |
| Node child; |
| |
| // By definition this will happen if the node is a document, |
| // document fragment, etc. Just serialize its contents. It will |
| // work well for other nodes that we do not know how to serialize. |
| child = node.getFirstChild(); |
| while (child != null) { |
| serializeNode(child); |
| child = child.getNextSibling(); |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| |
| /** |
| * Must be called by a method about to print any type of content. |
| * If the element was just opened, the opening tag is closed and |
| * will be matched to a closing tag. Returns the current element |
| * state with <tt>empty</tt> and <tt>afterElement</tt> set to false. |
| * |
| * @return The current element state |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| protected ElementState content() |
| throws IOException { |
| ElementState state; |
| |
| state = getElementState(); |
| if (!isDocumentState()) { |
| // Need to close CData section first |
| if (state.inCData && !state.doCData) { |
| _printer.printText("]]>"); |
| state.inCData = false; |
| } |
| // If this is the first content in the element, |
| // change the state to not-empty and close the |
| // opening element tag. |
| if (state.empty) { |
| _printer.printText('>'); |
| state.empty = false; |
| } |
| // Except for one content type, all of them |
| // are not last element. That one content |
| // type will take care of itself. |
| state.afterElement = false; |
| // Except for one content type, all of them |
| // are not last comment. That one content |
| // type will take care of itself. |
| state.afterComment = false; |
| } |
| return state; |
| } |
| |
| |
| /** |
| * Called to print the text contents in the prevailing element format. |
| * Since this method is capable of printing text as CDATA, it is used |
| * for that purpose as well. White space handling is determined by the |
| * current element state. In addition, the output format can dictate |
| * whether the text is printed as CDATA or unescaped. |
| * |
| * @param text The text to print |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| protected void characters(String text) |
| throws IOException { |
| ElementState state; |
| |
| state = content(); |
| // Check if text should be print as CDATA section or unescaped |
| // based on elements listed in the output format (the element |
| // state) or whether we are inside a CDATA section or entity. |
| |
| if (state.inCData || state.doCData) { |
| int saveIndent; |
| |
| // Print a CDATA section. The text is not escaped, but ']]>' |
| // appearing in the code must be identified and dealt with. |
| // The contents of a text node is considered space preserving. |
| if (!state.inCData) { |
| _printer.printText("<![CDATA["); |
| state.inCData = true; |
| } |
| saveIndent = _printer.getNextIndent(); |
| _printer.setNextIndent(0); |
| printCDATAText(text); |
| _printer.setNextIndent(saveIndent); |
| |
| } else { |
| |
| int saveIndent; |
| |
| if (state.preserveSpace) { |
| // If preserving space then hold of indentation so no |
| // excessive spaces are printed at line breaks, escape |
| // the text content without replacing spaces and print |
| // the text breaking only at line breaks. |
| saveIndent = _printer.getNextIndent(); |
| _printer.setNextIndent(0); |
| printText(text, true, state.unescaped); |
| _printer.setNextIndent(saveIndent); |
| } else { |
| printText(text, false, state.unescaped); |
| } |
| } |
| } |
| |
| |
| /** |
| * Returns the suitable entity reference for this character value, |
| * or null if no such entity exists. Calling this method with <tt>'&'</tt> |
| * will return <tt>"&amp;"</tt>. |
| * |
| * @param ch Character value |
| * @return Character entity name, or null |
| */ |
| protected abstract String getEntityRef(int ch); |
| |
| |
| /** |
| * Called to serializee the DOM element. The element is serialized based on |
| * the serializer's method (XML, HTML, XHTML). |
| * |
| * @param elem The element to serialize |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| protected abstract void serializeElement(Element elem) |
| throws IOException; |
| |
| |
| /** |
| * Comments and PIs cannot be serialized before the root element, |
| * because the root element serializes the document type, which |
| * generally comes first. Instead such PIs and comments are |
| * accumulated inside a vector and serialized by calling this |
| * method. Will be called when the root element is serialized |
| * and when the document finished serializing. |
| * |
| * @throws IOException An I/O exception occured while |
| * serializing |
| */ |
| protected void serializePreRoot() |
| throws IOException { |
| int i; |
| |
| if (_preRoot != null) { |
| for (i = 0; i < _preRoot.size(); ++i) { |
| printText((String) _preRoot.elementAt(i), true, true); |
| if (_indenting) |
| _printer.breakLine(); |
| } |
| _preRoot.removeAllElements(); |
| } |
| } |
| |
| //---------------------------------------------// |
| // Text pretty printing and formatting methods // |
| //---------------------------------------------// |
| |
| protected void printCDATAText(String text) throws IOException { |
| int length = text.length(); |
| char ch; |
| |
| for (int index = 0; index < length; ++index) { |
| ch = text.charAt(index); |
| if (ch == ']' |
| && index + 2 < length |
| && text.charAt(index + 1) == ']' |
| && text.charAt(index + 2) == '>') { // check for ']]>' |
| if (fDOMErrorHandler != null) { |
| // REVISIT: this means that if DOM Error handler is not registered we don't report any |
| // fatal errors and might serialize not wellformed document |
| /* |
| if ((features & DOMSerializerImpl.SPLITCDATA) == 0 |
| && (features & DOMSerializerImpl.WELLFORMED) == 0) { |
| // issue fatal error |
| String msg = |
| DOMMessageFormatter.formatMessage( |
| DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "EndingCDATA", |
| null); |
| modifyDOMError( |
| msg, |
| DOMError.SEVERITY_FATAL_ERROR, |
| fCurrentNode); |
| boolean continueProcess = |
| fDOMErrorHandler.handleError(fDOMError); |
| if (!continueProcess) { |
| throw new IOException(); |
| } |
| } else*/ { |
| // issue warning |
| String msg = |
| DOMMessageFormatter.formatMessage( |
| DOMMessageFormatter.SERIALIZER_DOMAIN, |
| "SplittingCDATA", |
| null); |
| modifyDOMError( |
| msg, |
| DOMError.SEVERITY_WARNING, |
| fCurrentNode); |
| fDOMErrorHandler.handleError(fDOMError); |
| } |
| } |
| // split CDATA section |
| _printer.printText("]]]]><![CDATA[>"); |
| index += 2; |
| continue; |
| } |
| |
| if (!XMLChar.isValid(ch)) { |
| // check if it is surrogate |
| if (++index < length) { |
| surrogates(ch, text.charAt(index)); |
| } else { |
| fatalError("The character '" + (char) ch + "' is an invalid XML character"); |
| } |
| continue; |
| } else { |
| if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch) && ch != 0xF7) || |
| ch == '\n' || ch == '\r' || ch == '\t') { |
| _printer.printText((char) ch); |
| } else { |
| |
| // The character is not printable -- split CDATA section |
| _printer.printText("]]>&#x"); |
| _printer.printText(Integer.toHexString(ch)); |
| _printer.printText(";<![CDATA["); |
| } |
| } |
| } |
| } |
| |
| |
| protected void surrogates(int high, int low) throws IOException { |
| if (XMLChar.isHighSurrogate(high)) { |
| if (!XMLChar.isLowSurrogate(low)) { |
| //Invalid XML |
| fatalError("The character '" + (char) low + "' is an invalid XML character"); |
| } else { |
| int supplemental = XMLChar.supplemental((char) high, (char) low); |
| if (!XMLChar.isValid(supplemental)) { |
| //Invalid XML |
| fatalError("The character '" + (char) supplemental + "' is an invalid XML character"); |
| } else { |
| if (content().inCData) { |
| _printer.printText("]]>&#x"); |
| _printer.printText(Integer.toHexString(supplemental)); |
| _printer.printText(";<![CDATA["); |
| } else { |
| printHex(supplemental); |
| } |
| } |
| } |
| } else { |
| fatalError("The character '" + (char) high + "' is an invalid XML character"); |
| } |
| |
| } |
| |
| /** |
| * Called to print additional text with whitespace handling. |
| * If spaces are preserved, the text is printed as if by calling |
| * {@link #printText(String,boolean,boolean)} with a call to {@link Printer#breakLine} |
| * for each new line. If spaces are not preserved, the text is |
| * broken at space boundaries if longer than the line width; |
| * Multiple spaces are printed as such, but spaces at beginning |
| * of line are removed. |
| * |
| * @param chars The text to print |
| * @param preserveSpace Space preserving flag |
| * @param unescaped Print unescaped |
| */ |
| protected void printText(char[] chars, int start, int length, |
| boolean preserveSpace, boolean unescaped) |
| throws IOException { |
| int index; |
| char ch; |
| |
| if (preserveSpace) { |
| // Preserving spaces: the text must print exactly as it is, |
| // without breaking when spaces appear in the text and without |
| // consolidating spaces. If a line terminator is used, a line |
| // break will occur. |
| while (length-- > 0) { |
| ch = chars[start]; |
| ++start; |
| if (ch == '\n' || ch == '\r' || unescaped) |
| _printer.printText(ch); |
| else |
| printEscaped(ch); |
| } |
| } else { |
| // Not preserving spaces: print one part at a time, and |
| // use spaces between parts to break them into different |
| // lines. Spaces at beginning of line will be stripped |
| // by printing mechanism. Line terminator is treated |
| // no different than other text part. |
| while (length-- > 0) { |
| ch = chars[start]; |
| ++start; |
| if (ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r') |
| _printer.printSpace(); |
| else if (unescaped) |
| _printer.printText(ch); |
| else |
| printEscaped(ch); |
| } |
| } |
| } |
| |
| |
| protected void printText(String text, boolean preserveSpace, boolean unescaped) |
| throws IOException { |
| int index; |
| char ch; |
| |
| if (preserveSpace) { |
| // Preserving spaces: the text must print exactly as it is, |
| // without breaking when spaces appear in the text and without |
| // consolidating spaces. If a line terminator is used, a line |
| // break will occur. |
| for (index = 0; index < text.length(); ++index) { |
| ch = text.charAt(index); |
| if (ch == '\n' || ch == '\r' || unescaped) |
| _printer.printText(ch); |
| else |
| printEscaped(ch); |
| } |
| } else { |
| // Not preserving spaces: print one part at a time, and |
| // use spaces between parts to break them into different |
| // lines. Spaces at beginning of line will be stripped |
| // by printing mechanism. Line terminator is treated |
| // no different than other text part. |
| for (index = 0; index < text.length(); ++index) { |
| ch = text.charAt(index); |
| if (ch == ' ' || ch == '\f' || ch == '\t' || ch == '\n' || ch == '\r') |
| _printer.printSpace(); |
| else if (unescaped) |
| _printer.printText(ch); |
| else |
| printEscaped(ch); |
| } |
| } |
| } |
| |
| |
| /** |
| * Print a document type public or system identifier URL. |
| * Encapsulates the URL in double quotes, escapes non-printing |
| * characters and print it equivalent to {@link #printText}. |
| * |
| * @param url The document type url to print |
| */ |
| protected void printDoctypeURL(String url) |
| throws IOException { |
| int i; |
| |
| _printer.printText('"'); |
| for (i = 0; i < url.length(); ++i) { |
| if (url.charAt(i) == '"' || url.charAt(i) < 0x20 || url.charAt(i) > 0x7F) { |
| _printer.printText('%'); |
| _printer.printText(Integer.toHexString(url.charAt(i))); |
| } else |
| _printer.printText(url.charAt(i)); |
| } |
| _printer.printText('"'); |
| } |
| |
| |
| protected void printEscaped(int ch) |
| throws IOException { |
| String charRef; |
| // If there is a suitable entity reference for this |
| // character, print it. The list of available entity |
| // references is almost but not identical between |
| // XML and HTML. |
| charRef = getEntityRef(ch); |
| if (charRef != null) { |
| _printer.printText('&'); |
| _printer.printText(charRef); |
| _printer.printText(';'); |
| } else if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch) && ch != 0xF7) || |
| ch == '\n' || ch == '\r' || ch == '\t') { |
| // Non printables are below ASCII space but not tab or line |
| // terminator, ASCII delete, or above a certain Unicode threshold. |
| if (ch < 0x10000) { |
| _printer.printText((char) ch); |
| } else { |
| _printer.printText((char) (((ch - 0x10000) >> 10) + 0xd800)); |
| _printer.printText((char) (((ch - 0x10000) & 0x3ff) + 0xdc00)); |
| } |
| } else { |
| printHex(ch); |
| } |
| } |
| |
| /** |
| * Escapes chars |
| */ |
| final void printHex(int ch) throws IOException { |
| _printer.printText("&#x"); |
| _printer.printText(Integer.toHexString(ch)); |
| _printer.printText(';'); |
| |
| } |
| |
| |
| /** |
| * Escapes a string so it may be printed as text content or attribute |
| * value. Non printable characters are escaped using character references. |
| * Where the format specifies a deault entity reference, that reference |
| * is used (e.g. <tt>&lt;</tt>). |
| * |
| * @param source The string to escape |
| */ |
| protected void printEscaped(String source) |
| throws IOException { |
| for (int i = 0; i < source.length(); ++i) { |
| int ch = source.charAt(i); |
| if ((ch & 0xfc00) == 0xd800 && i + 1 < source.length()) { |
| int lowch = source.charAt(i + 1); |
| if ((lowch & 0xfc00) == 0xdc00) { |
| ch = 0x10000 + ((ch - 0xd800) << 10) + lowch - 0xdc00; |
| i++; |
| } |
| } |
| printEscaped(ch); |
| } |
| } |
| |
| //--------------------------------// |
| // Element state handling methods // |
| //--------------------------------// |
| |
| |
| /** |
| * Return the state of the current element. |
| * |
| * @return Current element state |
| */ |
| protected ElementState getElementState() { |
| return _elementStates[_elementStateCount]; |
| } |
| |
| |
| /** |
| * Enter a new element state for the specified element. |
| * Tag name and space preserving is specified, element |
| * state is initially empty. |
| * |
| * @return Current element state, or null |
| */ |
| protected ElementState enterElementState(String namespaceURI, String localName, |
| String rawName, boolean preserveSpace) { |
| ElementState state; |
| |
| if (_elementStateCount + 1 == _elementStates.length) { |
| ElementState[] newStates; |
| |
| // Need to create a larger array of states. This does not happen |
| // often, unless the document is really deep. |
| newStates = new ElementState[_elementStates.length + 10]; |
| for (int i = 0; i < _elementStates.length; ++i) |
| newStates[i] = _elementStates[i]; |
| for (int i = _elementStates.length; i < newStates.length; ++i) |
| newStates[i] = new ElementState(); |
| _elementStates = newStates; |
| } |
| |
| ++_elementStateCount; |
| state = _elementStates[_elementStateCount]; |
| state.namespaceURI = namespaceURI; |
| state.localName = localName; |
| state.rawName = rawName; |
| state.preserveSpace = preserveSpace; |
| state.empty = true; |
| state.afterElement = false; |
| state.afterComment = false; |
| state.doCData = state.inCData = false; |
| state.unescaped = false; |
| state.prefixes = _prefixes; |
| |
| _prefixes = null; |
| return state; |
| } |
| |
| |
| /** |
| * Leave the current element state and return to the |
| * state of the parent element. If this was the root |
| * element, return to the state of the document. |
| * |
| * @return Previous element state |
| */ |
| protected ElementState leaveElementState() { |
| if (_elementStateCount > 0) { |
| /*Corrected by David Blondeau (blondeau@intalio.com)*/ |
| _prefixes = null; |
| //_prefixes = _elementStates[ _elementStateCount ].prefixes; |
| --_elementStateCount; |
| return _elementStates[_elementStateCount]; |
| } else { |
| String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "Internal", null); |
| throw new IllegalStateException(msg); |
| } |
| } |
| |
| |
| /** |
| * Returns true if in the state of the document. |
| * Returns true before entering any element and after |
| * leaving the root element. |
| * |
| * @return True if in the state of the document |
| */ |
| protected boolean isDocumentState() { |
| return _elementStateCount == 0; |
| } |
| |
| |
| /** |
| * Returns the namespace prefix for the specified URI. |
| * If the URI has been mapped to a prefix, returns the |
| * prefix, otherwise returns null. |
| * |
| * @param namespaceURI The namespace URI |
| * @return The namespace prefix if known, or null |
| */ |
| protected String getPrefix(String namespaceURI) { |
| String prefix; |
| |
| if (_prefixes != null) { |
| prefix = (String) _prefixes.get(namespaceURI); |
| if (prefix != null) |
| return prefix; |
| } |
| if (_elementStateCount == 0) |
| return null; |
| else { |
| for (int i = _elementStateCount; i > 0; --i) { |
| if (_elementStates[i].prefixes != null) { |
| prefix = (String) _elementStates[i].prefixes.get(namespaceURI); |
| if (prefix != null) |
| return prefix; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * The method modifies global DOM error object |
| * |
| * @param message |
| * @param severity |
| * @return a DOMError |
| */ |
| protected DOMError modifyDOMError(String message, short severity, Node node) { |
| fDOMError.reset(); |
| fDOMError.fMessage = message; |
| fDOMError.fSeverity = severity; |
| fDOMError.fLocator = new DOMLocatorImpl(-1, -1, -1, node, null); |
| return fDOMError; |
| |
| } |
| |
| |
| protected void fatalError(String message) throws IOException { |
| if (fDOMErrorHandler != null) { |
| modifyDOMError(message, DOMError.SEVERITY_FATAL_ERROR, fCurrentNode); |
| fDOMErrorHandler.handleError(fDOMError); |
| } else { |
| throw new IOException(message); |
| } |
| } |
| |
| /** |
| * DOM level 3: |
| * Check a node to determine if it contains unbound namespace prefixes. |
| * |
| * @param node The node to check for unbound namespace prefices |
| */ |
| protected void checkUnboundNamespacePrefixedNode(Node node) throws IOException { |
| |
| } |
| } |