| /* |
| * 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. |
| */ |
| #if !defined(FORMATTERTOXML_UNICODE_HEADER_GUARD_1357924680) |
| #define FORMATTERTOXML_UNICODE_HEADER_GUARD_1357924680 |
| |
| |
| // Base include file. Must be first. |
| #include "xalanc/XMLSupport/XMLSupportDefinitions.hpp" |
| |
| |
| |
| #include "xalanc/XMLSupport/XalanXMLSerializerBase.hpp" |
| |
| |
| |
| #include "xercesc/sax/AttributeList.hpp" |
| |
| |
| |
| #include "xalanc/PlatformSupport/DoubleSupport.hpp" |
| #include "xalanc/PlatformSupport/XalanOutputStream.hpp" |
| #include "xalanc/PlatformSupport/XalanUnicode.hpp" |
| |
| |
| |
| #include <xalanc/DOMSupport/DOMServices.hpp> |
| |
| |
| |
| XALAN_CPP_NAMESPACE_BEGIN |
| |
| |
| /** |
| * FormatterToXMLUnicode formats SAX-style events into XML. |
| */ |
| template< |
| class UnicodeWriter, |
| class ConstantsType, |
| class CharPredicate, |
| class IndentHandler, |
| FormatterListener::eXMLVersion XMLVersion> |
| class XALAN_XMLSUPPORT_EXPORT FormatterToXMLUnicode : public XalanXMLSerializerBase |
| { |
| public: |
| |
| typedef typename UnicodeWriter::value_type value_type; |
| |
| enum |
| { |
| eDefaultIndentAmount = 0 |
| }; |
| |
| /** |
| * Constructor |
| * |
| * @param theManager The MemoryManager instance to use for all memory allocations |
| * @param writer the writer. |
| * @param version the string to write for the XML version number. |
| * @param doctypeSystem system identifier to be used in the document |
| * type declaration |
| * @param doctypePublic public identifier to be used in the document |
| * type declaration |
| * @param xmlDecl true if the XSLT processor should output an XML |
| * declaration |
| * @param standalone The string the XSLT processor should output for |
| * the standalone document declaration |
| * |
| */ |
| FormatterToXMLUnicode( |
| MemoryManager& theManager, |
| Writer& writer, |
| const XalanDOMString& encoding, |
| const XalanDOMString& doctypeSystem = s_emptyString, |
| const XalanDOMString& doctypePublic = s_emptyString, |
| bool xmlDecl = true, |
| const XalanDOMString& standalone = s_emptyString, |
| size_type indent = eDefaultIndentAmount) : |
| XalanXMLSerializerBase( |
| theManager, |
| XMLVersion, |
| encoding, |
| doctypeSystem, |
| doctypePublic, |
| xmlDecl, |
| standalone), |
| m_stringBuffer(theManager), |
| m_writer(writer, theManager), |
| m_constants(), |
| m_charPredicate(), |
| m_indentHandler(m_writer , indent) |
| { |
| } |
| |
| static FormatterToXMLUnicode* |
| create( |
| MemoryManager& theManager, |
| Writer& writer, |
| const XalanDOMString& encoding, |
| const XalanDOMString& doctypeSystem = s_emptyString, |
| const XalanDOMString& doctypePublic = s_emptyString, |
| bool xmlDecl = true, |
| const XalanDOMString& standalone = s_emptyString, |
| size_type indent = eDefaultIndentAmount) |
| { |
| |
| typedef FormatterToXMLUnicode ThisType; |
| |
| XalanAllocationGuard theGuard(theManager, theManager.allocate(sizeof(ThisType))); |
| |
| ThisType* const theResult = |
| new (theGuard.get()) ThisType( |
| theManager, |
| writer, |
| encoding, |
| doctypeSystem, |
| doctypePublic, |
| xmlDecl, |
| standalone, |
| indent); |
| |
| theGuard.release(); |
| |
| return theResult; |
| } |
| |
| virtual |
| ~FormatterToXMLUnicode() |
| { |
| } |
| |
| Writer* |
| getWriter() const |
| { |
| return m_writer.getWriter(); |
| } |
| |
| // These are inherited from XalanXMLSerializerBase... |
| |
| virtual void |
| endDocument() |
| { |
| m_indentHandler.setStartNewLine(true); |
| |
| m_indentHandler.indent(); |
| |
| flushBuffer(); |
| |
| flushWriter(); |
| } |
| |
| virtual void |
| startElement( |
| const XMLCh* const name, |
| AttributeList& attrs) |
| { |
| generateDoctypeDecl(name); |
| |
| writeParentTagEnd(); |
| |
| m_indentHandler.setPreserve(false); |
| |
| m_indentHandler.indent(); |
| |
| m_indentHandler.setStartNewLine(true); |
| |
| m_writer.write(value_type(XalanUnicode::charLessThanSign)); |
| |
| writeName(name); |
| |
| const XalanSize_t nAttrs = attrs.getLength(); |
| |
| for (XalanSize_t i = 0; i < nAttrs ; i++) |
| { |
| processAttribute(attrs.getName(i), attrs.getValue(i)); |
| } |
| |
| // Flag the current element as not yet having any children. |
| openElementForChildren(); |
| |
| m_indentHandler.increaseIndent(); |
| |
| m_indentHandler.setPrevText(false); |
| } |
| |
| virtual void |
| endElement(const XMLCh* const name) |
| { |
| m_indentHandler.decreaseIndent(); |
| |
| const bool hasChildNodes = childNodesWereAdded(); |
| |
| if (hasChildNodes == true) |
| { |
| m_indentHandler.indent(); |
| |
| m_writer.write(value_type(XalanUnicode::charLessThanSign)); |
| m_writer.write(value_type(XalanUnicode::charSolidus)); |
| |
| writeName(name); |
| } |
| else |
| { |
| if(m_spaceBeforeClose == true) |
| { |
| m_writer.write(value_type(XalanUnicode::charSpace)); |
| } |
| |
| m_writer.write(value_type(XalanUnicode::charSolidus)); |
| } |
| |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| |
| if (hasChildNodes == true) |
| { |
| m_indentHandler.pop_preserve(); |
| } |
| |
| m_indentHandler.setPrevText(false); |
| } |
| |
| virtual void |
| charactersRaw( |
| const XMLCh* const chars, |
| const size_type length) |
| { |
| writeParentTagEnd(); |
| |
| m_indentHandler.setPreserve(true); |
| |
| m_writer.write(chars, length); |
| } |
| |
| |
| virtual void |
| entityReference(const XMLCh* const name) |
| { |
| writeParentTagEnd(); |
| |
| m_indentHandler.indent(); |
| |
| m_writer.write(value_type(XalanUnicode::charAmpersand)); |
| |
| writeName(name); |
| |
| m_writer.write(value_type(XalanUnicode::charSemicolon)); |
| } |
| |
| virtual void |
| comment(const XMLCh* const data) |
| { |
| writeParentTagEnd(); |
| |
| m_indentHandler.indent(); |
| |
| m_writer.write(value_type(XalanUnicode::charLessThanSign)); |
| m_writer.write(value_type(XalanUnicode::charExclamationMark)); |
| m_writer.write(value_type(XalanUnicode::charHyphenMinus)); |
| m_writer.write(value_type(XalanUnicode::charHyphenMinus)); |
| |
| writeNormalizedData(data, XalanDOMString::length(data)); |
| |
| m_writer.write(value_type(XalanUnicode::charHyphenMinus)); |
| m_writer.write(value_type(XalanUnicode::charHyphenMinus)); |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| |
| m_indentHandler.setStartNewLine(true); |
| } |
| |
| protected: |
| |
| virtual void |
| flushBuffer() |
| { |
| m_writer.flushBuffer(); |
| } |
| |
| virtual void |
| flushWriter() |
| { |
| m_writer.flushWriter(); |
| } |
| |
| virtual void |
| writeXMLHeader() |
| { |
| // "<?xml version=\"" |
| m_writer.write( |
| m_constants.s_xmlHeaderStartString, |
| m_constants.s_xmlHeaderStartStringLength); |
| |
| if (m_version.empty() == false) |
| { |
| m_writer.write(m_version); |
| } |
| else |
| { |
| m_writer.write( |
| m_constants.s_defaultVersionString, |
| m_constants.s_defaultVersionStringLength); |
| } |
| |
| // "\" encoding=\"" |
| m_writer.write( |
| m_constants.s_xmlHeaderEncodingString, |
| m_constants.s_xmlHeaderEncodingStringLength); |
| |
| m_writer.write(m_encoding); |
| |
| if (m_standalone.empty() == false) |
| { |
| m_writer.write( |
| m_constants.s_xmlHeaderStandaloneString, |
| m_constants.s_xmlHeaderStandaloneStringLength); |
| |
| m_writer.write(m_standalone); |
| } |
| |
| m_writer.write( |
| m_constants.s_xmlHeaderEndString, |
| m_constants.s_xmlHeaderEndStringLength); |
| |
| if (getNeedToOutputDoctypeDecl() == false) |
| { |
| m_indentHandler.outputLineSep(); |
| } |
| } |
| |
| |
| void |
| writeDoctypeDecl(const XalanDOMChar* name) |
| { |
| // "<!DOCTYPE " |
| m_writer.write( |
| m_constants.s_doctypeHeaderStartString, |
| m_constants.s_doctypeHeaderStartStringLength); |
| |
| m_writer.write(name); |
| |
| if (m_doctypePublic.empty() == false) |
| { |
| // " PUBLIC \"" |
| m_writer.write( |
| m_constants.s_doctypeHeaderPublicString, |
| m_constants.s_doctypeHeaderPublicStringLength); |
| |
| writeName(m_doctypePublic.c_str()); |
| |
| m_writer.write(value_type(XalanUnicode::charQuoteMark)); |
| m_writer.write(value_type(XalanUnicode::charSpace)); |
| m_writer.write(value_type(XalanUnicode::charQuoteMark)); |
| } |
| else |
| { |
| // " SYSTEM \"" |
| m_writer.write( |
| m_constants.s_doctypeHeaderSystemString, |
| m_constants.s_doctypeHeaderSystemStringLength); |
| } |
| |
| writeName(m_doctypeSystem.c_str()); |
| |
| m_writer.write(value_type(XalanUnicode::charQuoteMark)); |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| |
| outputNewline(); |
| } |
| |
| |
| void |
| writeProcessingInstruction( |
| const XMLCh* target, |
| const XMLCh* data) |
| { |
| writeParentTagEnd(); |
| |
| m_indentHandler.indent(); |
| |
| m_writer.write(value_type(XalanUnicode::charLessThanSign)); |
| m_writer.write(value_type(XalanUnicode::charQuestionMark)); |
| writeName(target); |
| |
| const size_type len = length(data); |
| |
| // We need to make sure there is a least one whitespace character |
| // between the target and the data. |
| if (len > 0 && !isXMLWhitespace(data[0])) |
| { |
| m_writer.write(value_type(XalanUnicode::charSpace)); |
| } |
| |
| writeNormalizedData(data, len); |
| |
| m_writer.write(value_type(XalanUnicode::charQuestionMark)); |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| } |
| |
| void |
| writeCharacters( |
| const XMLCh* chars, |
| size_type length) |
| { |
| assert(length != 0); |
| |
| writeParentTagEnd(); |
| |
| m_indentHandler.setPreserve(true); |
| |
| size_type i = 0; |
| size_type firstIndex = 0; |
| |
| while(i < length) |
| { |
| const XalanDOMChar ch = chars[i]; |
| |
| if(m_charPredicate.range(ch) == true) |
| { |
| safeWriteContent(chars + firstIndex, i - firstIndex); |
| |
| i = writeNormalizedCharBig(chars, i, length); |
| |
| ++i; |
| |
| firstIndex = i; |
| } |
| else if(m_charPredicate.content(ch) == false) |
| { |
| ++i; |
| } |
| else |
| { |
| safeWriteContent(chars + firstIndex, i - firstIndex); |
| |
| writeDefaultEscape(ch); |
| |
| ++i; |
| |
| firstIndex = i; |
| } |
| } |
| |
| safeWriteContent(chars + firstIndex, i - firstIndex); |
| |
| m_indentHandler.setPrevText(true); |
| } |
| |
| |
| void |
| writeCDATA( |
| const XMLCh* chars, |
| size_type length) |
| { |
| assert(length != 0); |
| |
| writeParentTagEnd(); |
| |
| m_indentHandler.setPreserve(true); |
| |
| m_indentHandler.indent(); |
| |
| m_writer.write( |
| m_constants.s_cdataOpenString, |
| m_constants.s_cdataOpenStringLength); |
| |
| bool outsideCDATA = false; |
| |
| writeCDATAChars(chars, length, outsideCDATA); |
| |
| if (outsideCDATA == false) |
| { |
| m_writer.write( |
| m_constants.s_cdataCloseString, |
| m_constants.s_cdataCloseStringLength); |
| } |
| } |
| |
| /** |
| * Output a line break. |
| */ |
| void |
| outputNewline() |
| { |
| m_writer.outputNewline(); |
| } |
| |
| /** |
| * Escape and write a character. |
| */ |
| void |
| writeDefaultEscape(XalanDOMChar ch) |
| { |
| assert(m_charPredicate.content(ch) == true); |
| |
| if(!writeDefaultEntity(ch)) |
| { |
| if (XalanUnicode::charLF == ch) |
| { |
| outputNewline(); |
| } |
| else |
| { |
| if(m_charPredicate.isForbidden(ch) == true) |
| { |
| throwInvalidXMLCharacterException( |
| ch, |
| m_version, |
| getMemoryManager()); |
| } |
| else |
| { |
| writeNumericCharacterReference(ch); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Escape and write a character in an attribute. |
| */ |
| void |
| writeDefaultAttributeEscape(XalanDOMChar ch) |
| { |
| assert(m_charPredicate.attribute(ch) == true); |
| |
| if(writeDefaultAttributeEntity(ch) == false) |
| { |
| if(m_charPredicate.isForbidden(ch) == true) |
| { |
| throwInvalidXMLCharacterException( |
| ch, |
| m_version, |
| getMemoryManager()); |
| } |
| else |
| { |
| writeNumericCharacterReference(ch); |
| } |
| |
| } |
| } |
| |
| /** |
| * Handle one of the default entities, return false if it |
| * is not a default entity. |
| */ |
| bool |
| writeDefaultEntity(XalanDOMChar ch) |
| { |
| if (XalanUnicode::charLessThanSign == ch) |
| { |
| m_writer.write( |
| m_constants.s_lessThanEntityString, |
| m_constants.s_lessThanEntityStringLength); |
| } |
| else if (XalanUnicode::charGreaterThanSign == ch) |
| { |
| m_writer.write( |
| m_constants.s_greaterThanEntityString, |
| m_constants.s_greaterThanEntityStringLength); |
| } |
| else if (XalanUnicode::charAmpersand == ch) |
| { |
| m_writer.write( |
| m_constants.s_ampersandEntityString, |
| m_constants.s_ampersandEntityStringLength); |
| } |
| else |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Handle one of the default entities, return false if it |
| * is not a default entity. |
| */ |
| bool |
| writeDefaultAttributeEntity(XalanDOMChar ch) |
| { |
| if (writeDefaultEntity(ch) == true) |
| { |
| return true; |
| } |
| else if (XalanUnicode::charQuoteMark == ch) |
| { |
| m_writer.write( |
| m_constants.s_quoteEntityString, |
| m_constants.s_quoteEntityStringLength); |
| } |
| else |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Check to see if a parent's ">" has been written, and, if |
| * it has not, write it. |
| */ |
| void |
| writeParentTagEnd() |
| { |
| if(markParentForChildren() == true) |
| { |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| |
| m_indentHandler.setPrevText(false); |
| |
| m_indentHandler.push_preserve(); |
| } |
| } |
| |
| /** |
| * Write a normalized character to the stream. |
| * @param ch the string to write. |
| * @param start the start offset into the string. |
| * @param length the length of the string. |
| */ |
| size_type |
| writeNormalizedChar( |
| XalanDOMChar ch, |
| const XalanDOMChar chars[], |
| size_type start, |
| size_type length) |
| { |
| if (XalanUnicode::charLF == ch) |
| { |
| outputNewline(); |
| } |
| else |
| { |
| if(m_charPredicate.isCharRefForbidden(ch)) |
| { |
| throwInvalidXMLCharacterException( |
| ch, |
| m_version, |
| getMemoryManager()); |
| } |
| else |
| { |
| start = m_writer.write( chars, start, length); |
| } |
| } |
| |
| return start; |
| } |
| |
| void |
| writeNumericCharacterReference(unsigned long theNumber) |
| { |
| m_writer.write(value_type(XalanUnicode::charAmpersand)); |
| m_writer.write(value_type(XalanUnicode::charNumberSign)); |
| |
| m_writer.write(NumberToDOMString(theNumber, m_stringBuffer)); |
| m_stringBuffer.clear(); |
| |
| m_writer.write(value_type(XalanUnicode::charSemicolon)); |
| } |
| |
| size_type |
| writeNormalizedCharBig( |
| const XalanDOMChar chars[], |
| size_type start, |
| size_type length) |
| { |
| assert(start < length); |
| |
| const XalanDOMChar ch = chars[start]; |
| |
| assert(m_charPredicate.range(ch) == true); |
| |
| if (XMLVersion == XML_VERSION_1_1 && |
| XalanUnicode::charLSEP == ch) |
| { |
| writeNumericCharacterReference(ch); |
| } |
| else |
| { |
| start = m_writer.write(chars, start, length); |
| } |
| |
| return start; |
| } |
| |
| /** |
| * Write characters for a CDATA section |
| * |
| * @param ch the string to write. |
| * @param length the length of the string. |
| */ |
| void |
| writeCDATAChars( |
| const XalanDOMChar chars[], |
| size_type length, |
| bool& outsideCDATA) |
| { |
| size_type i = 0; |
| |
| while(i < length) |
| { |
| // If "]]>", which would close the CDATA appears in |
| // the content, we have to put the first two characters |
| // in the CDATA section, close the CDATA section, then |
| // open a new one and add the last character. |
| |
| const XalanDOMChar theChar = chars[i]; |
| |
| if (theChar == XalanUnicode::charRightSquareBracket && |
| i - length > 2 && |
| XalanUnicode::charRightSquareBracket == chars[i + 1] && |
| XalanUnicode::charGreaterThanSign == chars[i + 2]) |
| { |
| if (outsideCDATA == true) |
| { |
| m_writer.write( |
| m_constants.s_cdataCloseString, |
| m_constants.s_cdataCloseStringLength); |
| } |
| |
| m_writer.write(value_type(XalanUnicode::charRightSquareBracket)); |
| m_writer.write(value_type(XalanUnicode::charRightSquareBracket)); |
| |
| m_writer.write( |
| m_constants.s_cdataCloseString, |
| m_constants.s_cdataCloseStringLength); |
| |
| m_writer.write( |
| m_constants.s_cdataOpenString, |
| m_constants.s_cdataOpenStringLength); |
| |
| m_writer.write(value_type(XalanUnicode::charGreaterThanSign)); |
| |
| outsideCDATA = false; |
| |
| i += 2; |
| } |
| else |
| { |
| if (XalanUnicode::charLF == theChar) |
| { |
| outputNewline(); |
| } |
| else if(m_charPredicate.isCharRefForbidden(theChar)) |
| { |
| throwInvalidXMLCharacterException( |
| theChar, |
| m_version, |
| getMemoryManager()); |
| } |
| else |
| { |
| i = m_writer.writeCDATAChar(chars, i, length, outsideCDATA); |
| } |
| } |
| |
| ++i; |
| } |
| |
| if(outsideCDATA == true) |
| { |
| m_writer.write( |
| m_constants.s_cdataOpenString, |
| m_constants.s_cdataOpenStringLength); |
| } |
| } |
| |
| |
| /** |
| * Write an attribute string. |
| * |
| * @param theString The string to write. |
| * @param theStringLength The length of the string. |
| */ |
| void |
| writeAttrString( |
| const XalanDOMChar* theString, |
| size_type theStringLength) |
| { |
| assert(theString != 0); |
| |
| size_type i = 0; |
| size_type firstIndex = 0; |
| |
| while(i < theStringLength) |
| { |
| const XalanDOMChar ch = theString[i]; |
| |
| if(m_charPredicate.range(ch) == true) |
| { |
| safeWriteContent(theString + firstIndex, i - firstIndex); |
| |
| i = writeNormalizedCharBig(theString, i, theStringLength); |
| |
| ++i; |
| |
| firstIndex = i; |
| } |
| else if (m_charPredicate.attribute(ch) == false) |
| { |
| ++i; |
| } |
| else |
| { |
| safeWriteContent(theString + firstIndex, i - firstIndex); |
| |
| writeDefaultAttributeEscape(ch); |
| |
| ++i; |
| |
| firstIndex = i; |
| } |
| } |
| |
| safeWriteContent(theString + firstIndex, i - firstIndex); |
| } |
| |
| private: |
| |
| /** |
| * Process an attribute. |
| * @param name The name of the attribute. |
| * @param value The value of the attribute. |
| */ |
| void |
| processAttribute( |
| const XalanDOMChar* name, |
| const XalanDOMChar* value) |
| { |
| m_writer.write(value_type(XalanUnicode::charSpace)); |
| writeName(name); |
| m_writer.write(value_type(XalanUnicode::charEqualsSign)); |
| m_writer.write(value_type(XalanUnicode::charQuoteMark)); |
| writeAttrString(value, length(value)); |
| m_writer.write(value_type(XalanUnicode::charQuoteMark)); |
| } |
| |
| /** |
| * Write normalized data. |
| * @param theData the data to write. |
| * @param theLength the data to write. |
| */ |
| void |
| writeNormalizedData( |
| const XalanDOMChar* theData, |
| size_type theLength) |
| { |
| for (size_type i = 0; i < theLength; ++i) |
| { |
| const XalanDOMChar theChar = theData[i]; |
| |
| i = writeNormalizedChar(theChar, theData, i, theLength); |
| } |
| } |
| |
| void |
| safeWriteContent( |
| const XalanDOMChar* theChars, |
| size_type theLength) |
| { |
| for(size_type i = 0; i < theLength; ++i) |
| { |
| m_writer.write(value_type(theChars[i])); |
| } |
| } |
| |
| void |
| writeName(const XalanDOMChar* theChars) |
| { |
| assert( theChars != 0); |
| |
| m_writer.writeNameChar(theChars, length(theChars)); |
| } |
| |
| private: |
| |
| // These are not implemented. |
| FormatterToXMLUnicode(const FormatterToXMLUnicode&); |
| |
| FormatterToXMLUnicode& |
| operator=(const FormatterToXMLUnicode&); |
| |
| bool |
| operator==(const FormatterToXMLUnicode&) const; |
| |
| |
| // Data members... |
| XalanDOMString m_stringBuffer; |
| |
| UnicodeWriter m_writer; |
| |
| ConstantsType m_constants; |
| |
| CharPredicate m_charPredicate; |
| |
| IndentHandler m_indentHandler; |
| }; |
| |
| |
| |
| XALAN_CPP_NAMESPACE_END |
| |
| |
| |
| #endif // FORMATTERTOXML_UNICODE_HEADER_GUARD_1357924680 |