All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ctc.wstx.sw.XmlWriter Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/* Woodstox XML processor
 *
 * Copyright (c) 2004- Tatu Saloranta, [email protected]
 *
 * Licensed under the License specified in file LICENSE, included with
 * the source code.
 * You may not use this file except in compliance with the License.
 *
 * 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.
 */

package com.ctc.wstx.sw;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.text.MessageFormat;

import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.io.EscapingWriterFactory;
import org.codehaus.stax2.ri.typed.AsciiValueEncoder;
import org.codehaus.stax2.validation.XMLValidator;

import com.ctc.wstx.api.InvalidCharHandler;
import com.ctc.wstx.api.WriterConfig;
import com.ctc.wstx.api.WstxOutputProperties;
import com.ctc.wstx.cfg.ErrorConsts;
import com.ctc.wstx.cfg.OutputConfigFlags;
import com.ctc.wstx.exc.WstxIOException;
import com.ctc.wstx.io.WstxInputData;

/**
 * This is the base class for actual physical xml outputters. These
 * instances will only handle actual writing (possibly including
 * encoding) of the serialized textual xml, and will in general
 * not verify content being output. The exception are the
 * character-by-character checks that are most efficiently done
 * at encoding level (such as character escaping, and checks for
 * illegal character combinations), which are handled at this
 * level.
 *

* Note that implementations can have different operating modes: * specifically, when dealing with illegal content (such as "--" * in a comment, "?>" in processing instruction, or "]]>" within * CDATA section), implementations can do one of 3 things: *

    *
  • Fix the problem, by splitting the section (which can be done * for CDATA sections, and to some degree, comments) *
  • *
  • Stop outputting, and return an index to the illegal piece * of data (if there is no easy way to fix the problem: for * example, for processing instruction) *
  • *
  • Just output content even though it will not result in * well-formed output. This should only be done if the calling * application has specifically requested verifications to be * disabled. *
  • *
*/ public abstract class XmlWriter { protected final static int SURR1_FIRST = 0xD800; protected final static int SURR1_LAST = 0xDBFF; protected final static int SURR2_FIRST = 0xDC00; protected final static int SURR2_LAST = 0xDFFF; protected final static char DEFAULT_QUOTE_CHAR = '"'; protected final WriterConfig mConfig; protected final String mEncoding; // // // Operating mode: base class needs to know whether // // // namespaces are support (for entity/PI target validation) protected final boolean mNsAware; protected final boolean mCheckStructure; protected final boolean mCheckContent; protected final boolean mCheckNames; protected final boolean mFixContent; /** * Whether to escape CR (\r) character. */ final boolean mEscapeCR; /** * Whether to add a space after empty element (before closing "/>") * or not. */ final boolean mAddSpaceAfterEmptyElem; /** * Whether to use double quotes in XML declaration or not. */ final boolean mUseDoubleQuotesInXmlDecl; /** * Flag that defines whether close() on this writer should call * close on the underlying output object (stream, writer) */ protected final boolean mAutoCloseOutput; /** * Optional escaping writer used for escaping characters like '<' * '&' and '>' in textual content. * Constructed if calling code has * installed a special escaping writer factory for text content. * Null if the default escaper is to be used. */ protected Writer mTextWriter; /** * Optional escaping writer used for escaping characters like '"' * '&' and '<' in attribute values. * Constructed if calling code has * installed a special escaping writer factory for text content. * Null if the default escaper is to be used. */ protected Writer mAttrValueWriter; /** * Indicates whether output is to be compliant; if false, is to be * xml 1.0 compliant, if true, xml 1.1 compliant. */ protected boolean mXml11 = false; /** * Lazy-constructed wrapper object, which will route all calls to * Writer API, to matching writeRaw methods of this * XmlWriter instance. */ protected XmlWriterWrapper mRawWrapper = null; /** * Lazy-constructed wrapper object, which will route all calls to * Writer API, to matching writeCharacters methods of this * XmlWriter instance. */ protected XmlWriterWrapper mTextWrapper = null; /* /////////////////////////////////////////////////////// // Output location info /////////////////////////////////////////////////////// */ /** * Number of characters output prior to currently buffered output */ protected int mLocPastChars = 0; protected int mLocRowNr = 1; /** * Offset of the first character on this line. May be negative, if * the offset was in a buffer that has been flushed out. */ protected int mLocRowStartOffset = 0; /* /////////////////////////////////////////////////////// // Life-cycle /////////////////////////////////////////////////////// */ protected XmlWriter(WriterConfig cfg, String encoding, boolean autoclose) throws IOException { mConfig = cfg; mEncoding = encoding; mAutoCloseOutput = autoclose; int flags = cfg.getConfigFlags(); mNsAware = (flags & OutputConfigFlags.CFG_ENABLE_NS) != 0; mCheckStructure = (flags & OutputConfigFlags.CFG_VALIDATE_STRUCTURE) != 0; mCheckContent = (flags & OutputConfigFlags.CFG_VALIDATE_CONTENT) != 0; mCheckNames = (flags & OutputConfigFlags.CFG_VALIDATE_NAMES) != 0; mFixContent = (flags & OutputConfigFlags.CFG_FIX_CONTENT) != 0; mEscapeCR = (flags & OutputConfigFlags.CFG_ESCAPE_CR) != 0; mAddSpaceAfterEmptyElem = (flags & OutputConfigFlags.CFG_ADD_SPACE_AFTER_EMPTY_ELEM) != 0; mUseDoubleQuotesInXmlDecl = (flags & OutputConfigFlags.CFG_USE_DOUBLE_QUOTES_IN_XML_DECL) != 0; // Has caller requested any custom text or attr value escaping? EscapingWriterFactory f = mConfig.getTextEscaperFactory(); if (f == null) { mTextWriter = null; } else { String enc = (mEncoding == null || mEncoding.length() == 0) ? WstxOutputProperties.DEFAULT_OUTPUT_ENCODING : mEncoding; mTextWriter = f.createEscapingWriterFor(wrapAsRawWriter(), enc); } f = mConfig.getAttrValueEscaperFactory(); if (f == null) { mAttrValueWriter = null; } else { String enc = (mEncoding == null || mEncoding.length() == 0) ? WstxOutputProperties.DEFAULT_OUTPUT_ENCODING : mEncoding; mAttrValueWriter = f.createEscapingWriterFor(wrapAsRawWriter(), enc); } } /* //////////////////////////////////////////////////// // Extra configuration //////////////////////////////////////////////////// */ public void enableXml11() { mXml11 = true; } /* //////////////////////////////////////////////////// // Access to underlying physical output destinations //////////////////////////////////////////////////// */ /** * @return Underlying OutputStream used for physical output, * if the writer was constructed using one */ protected abstract OutputStream getOutputStream(); /** * @return Underlying Writer used for physical output, * if the writer was constructed with one, or one was * created to be used with an OutputStream. */ protected abstract Writer getWriter(); /* //////////////////////////////////////////////////// // Basic methods for communicating with underlying // stream or writer //////////////////////////////////////////////////// */ /** * Method called to flush the buffer(s), and close the output * sink (stream or writer) if enabled (auto-closing) or * forced. */ public abstract void close(boolean forceRealClose) throws IOException; public abstract void flush() throws IOException; public abstract void writeRaw(String str, int offset, int len) throws IOException; public void writeRaw(String str) throws IOException { writeRaw(str, 0, str.length()); } public abstract void writeRaw(char[] cbuf, int offset, int len) throws IOException; /** * Like {@link #writeRaw}, but caller guarantees that the contents * additionally are known to be in 7-bit ascii range. */ public abstract void writeRawAscii(char[] cbuf, int offset, int len) throws IOException; /* //////////////////////////////////////////////////// // Raw, non-verifying write methods; used when // directly copying trusted content //////////////////////////////////////////////////// */ public abstract void writeCDataStart() throws IOException; public abstract void writeCDataEnd() throws IOException; public abstract void writeCommentStart() throws IOException; public abstract void writeCommentEnd() throws IOException; public abstract void writePIStart(String target, boolean addSpace) throws IOException; public abstract void writePIEnd() throws IOException; /* //////////////////////////////////////////////////// // Write methods, textual: //////////////////////////////////////////////////// */ /** * @param data Contents of the CDATA section to write out * @return offset of the (first) illegal content segment ("]]>") in * passed content and not in repairing mode; or -1 if none or is * repairing */ public abstract int writeCData(String data) throws IOException, XMLStreamException; public abstract int writeCData(char[] cbuf, int offset, int len) throws IOException, XMLStreamException; public abstract void writeCharacters(String data) throws IOException; public abstract void writeCharacters(char[] cbuf, int offset, int len) throws IOException; /* //////////////////////////////////////////////////// // Write methods, non-textual, non-elem/attr: //////////////////////////////////////////////////// */ /** * Method that will try to output the content as specified. If * the content passed in has embedded "--" in it, it will either * add an intervening space between consequtive hyphens (if content * fixing is enabled), or return the offset of the first hyphen in * multi-hyphen sequence. */ public abstract int writeComment(String data) throws IOException, XMLStreamException; /** * Older "legacy" output method for outputting DOCTYPE declaration. * Assumes that the passed-in String contains a complete DOCTYPE * declaration properly quoted. */ public abstract void writeDTD(String data) throws IOException, XMLStreamException; public abstract void writeDTD(String rootName, String systemId, String publicId, String internalSubset) throws IOException, XMLStreamException; public abstract void writeEntityReference(String name) throws IOException, XMLStreamException; public abstract int writePI(String target, String data) throws IOException, XMLStreamException; public abstract void writeXmlDeclaration(String version, String enc, String standalone) throws IOException; /* //////////////////////////////////////////////////// // Write methods, elements //////////////////////////////////////////////////// */ /** *

* Note: can throw XMLStreamException, if name checking is enabled, * and name is invalid (name check has to be in this writer, not * caller, since it depends not only on xml limitations, but also * on encoding limitations) */ public abstract void writeStartTagStart(String localName) throws IOException, XMLStreamException; /** *

* Note: can throw XMLStreamException, if name checking is enabled, * and name is invalid (name check has to be in this writer, not * caller, since it depends not only on xml limitations, but also * on encoding limitations) */ public abstract void writeStartTagStart(String prefix, String localName) throws IOException, XMLStreamException; public abstract void writeStartTagEnd() throws IOException; public abstract void writeStartTagEmptyEnd() throws IOException; public abstract void writeEndTag(String localName) throws IOException; public abstract void writeEndTag(String prefix, String localName) throws IOException; /* //////////////////////////////////////////////////// // Write methods, attributes/ns //////////////////////////////////////////////////// */ /** *

* Note: can throw XMLStreamException, if name checking is enabled, * and name is invalid (name check has to be in this writer, not * caller, since it depends not only on xml limitations, but also * on encoding limitations) */ public abstract void writeAttribute(String localName, String value) throws IOException, XMLStreamException; public abstract void writeAttribute(String localName, char[] value, int offset, int len) throws IOException, XMLStreamException; /** *

* Note: can throw XMLStreamException, if name checking is enabled, * and name is invalid (name check has to be in this writer, not * caller, since it depends not only on xml limitations, but also * on encoding limitations) */ public abstract void writeAttribute(String prefix, String localName, String value) throws IOException, XMLStreamException; public abstract void writeAttribute(String prefix, String localName, char[] value, int offset, int len) throws IOException, XMLStreamException; /* //////////////////////////////////////////////////// // Write methods, Typed Access API support //////////////////////////////////////////////////// */ /** * Like {@link #writeRaw}, but caller guarantees that the contents * additionally are known to be in 7-bit ascii range, and also * passes an encoder object that will encode values only when * being handed a buffer to append to. * * @param enc Encoder that will produce content */ public abstract void writeTypedElement(AsciiValueEncoder enc) throws IOException; /** * Like {@link #writeRaw}, but caller guarantees that the contents * additionally are known to be in 7-bit ascii range, and also * passes an encoder object that will encode values only when * being handed a buffer to append to. * * @param enc Encoder that will produce content * @param validator Validator to use for validating serialized textual * content (can not be null) * @param copyBuffer Temporary buffer that writer can use for temporary * copies as necessary */ public abstract void writeTypedElement(AsciiValueEncoder enc, XMLValidator validator, char[] copyBuffer) throws IOException, XMLStreamException; /** * Method similar to {@link #writeAttribute(String,String,char[],int,int)} * but where is known not to require escaping. * No validation needs to be performed. */ public abstract void writeTypedAttribute(String localName, AsciiValueEncoder enc) throws IOException, XMLStreamException; /** * Method similar to {@link #writeAttribute(String,String,char[],int,int)} * but where is known not to require escaping. * No validation needs to be performed. */ public abstract void writeTypedAttribute(String prefix, String localName, AsciiValueEncoder enc) throws IOException, XMLStreamException; /** * Method similar to {@link #writeAttribute(String,String,char[],int,int)} * but where is known not to require escaping. * Validation of the attribute value must be done by calling given * validator appropriately. */ public abstract void writeTypedAttribute(String prefix, String localName, String nsURI, AsciiValueEncoder enc, XMLValidator validator, char[] copyBuffer) throws IOException, XMLStreamException; /* //////////////////////////////////////////////////// // Location information //////////////////////////////////////////////////// */ protected abstract int getOutputPtr(); public int getRow() { return mLocRowNr; } public int getColumn() { return (getOutputPtr() - mLocRowStartOffset) + 1; } public int getAbsOffset() { return mLocPastChars +getOutputPtr(); } /* //////////////////////////////////////////////////// // Wrapper methods, semi-public //////////////////////////////////////////////////// */ /** * Method that can be called to get a wrapper instance that * can be used to essentially call the writeRaw * method. */ public final Writer wrapAsRawWriter() { if (mRawWrapper == null) { mRawWrapper = XmlWriterWrapper.wrapWriteRaw(this); } return mRawWrapper; } public final Writer wrapAsTextWriter() { if (mTextWrapper == null) { mTextWrapper = XmlWriterWrapper.wrapWriteCharacters(this); } return mTextWrapper; } /* //////////////////////////////////////////////////// // Helper methods for sub-classes //////////////////////////////////////////////////// */ /** * Method called to verify that the name is a legal XML name. */ public final void verifyNameValidity(String name, boolean checkNs) throws XMLStreamException { /* No empty names... caller must have dealt with optional arguments * prior to calling this method */ if (name == null || name.length() == 0) { reportNwfName(ErrorConsts.WERR_NAME_EMPTY); } int illegalIx = WstxInputData.findIllegalNameChar(name, checkNs, mXml11); if (illegalIx >= 0) { if (illegalIx == 0) { reportNwfName(ErrorConsts.WERR_NAME_ILLEGAL_FIRST_CHAR, WstxInputData.getCharDesc(name.charAt(0))); } reportNwfName(ErrorConsts.WERR_NAME_ILLEGAL_CHAR, WstxInputData.getCharDesc(name.charAt(illegalIx))); } } /** * This is the method called when an output method call violates * name well-formedness checks * and {@link WstxOutputProperties#P_OUTPUT_VALIDATE_NAMES} is * is enabled. */ protected void reportNwfName(String msg) throws XMLStreamException { throwOutputError(msg); } protected void reportNwfName(String msg, Object arg) throws XMLStreamException { throwOutputError(msg, arg); } protected void reportNwfContent(String msg) throws XMLStreamException { throwOutputError(msg); } protected void throwOutputError(String msg) throws XMLStreamException { // First, let's flush any output we may have, to make debugging easier try { flush(); } catch (IOException ioe) { throw new WstxIOException(ioe); } throw new XMLStreamException(msg); } protected void throwOutputError(String format, Object arg) throws XMLStreamException { String msg = MessageFormat.format(format, new Object[] { arg }); throwOutputError(msg); } /** * Method called to handle invalid character in textual content requested * to be output. Content may be part of textual events (CHARACTER, CDATA), * attribute value, COMMENT content or PROCESSING_INSTRUCTION data. * The default behavior is to just throw an exception, but this can * be configured via property {@link WstxOutputProperties#P_OUTPUT_INVALID_CHAR_HANDLER}. */ protected char handleInvalidChar(int c) throws IOException { // First, let's flush any output we may have, to make debugging easier flush(); InvalidCharHandler h = mConfig.getInvalidCharHandler(); if (h == null) { h = InvalidCharHandler.FailingHandler.getInstance(); } return h.convertInvalidChar(c); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy