Back to index

lightning-sunbird  0.9+nobinonly
txXMLOutput.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Van der Beken <peterv@propagandism.org>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "txXMLOutput.h"
00040 
00041 const int txXMLOutput::DEFAULT_INDENT = 2;
00042 
00043 txOutAttr::txOutAttr(PRInt32 aNsID, nsIAtom* aLocalName,
00044                          const nsAString& aValue) :
00045     mName(aNsID, aLocalName),
00046     mValue(aValue),
00047     mShorthand(MB_FALSE)
00048 {
00049 }
00050 
00051 txXMLOutput::txXMLOutput(txOutputFormat* aFormat, ostream* aOut)
00052     : mOut(aOut),
00053       mUseEmptyElementShorthand(MB_TRUE),
00054       mHaveDocumentElement(MB_FALSE),
00055       mStartTagOpen(MB_FALSE),
00056       mAfterEndTag(MB_FALSE),
00057       mInCDATASection(MB_FALSE),
00058       mIndentLevel(0)
00059 {
00060     mOutputFormat.merge(*aFormat);
00061     mOutputFormat.setFromDefaults();
00062 }
00063 
00064 txXMLOutput::~txXMLOutput()
00065 {
00066 }
00067 
00068 void txXMLOutput::attribute(const nsAString& aName,
00069                             const PRInt32 aNsID,
00070                             const nsAString& aValue)
00071 {
00072     if (!mStartTagOpen)
00073         // XXX Signal this? (can't add attributes after element closed)
00074         return;
00075 
00076     txListIterator iter(&mAttributes);
00077     nsCOMPtr<nsIAtom> localName = do_GetAtom(XMLUtils::getLocalPart(aName));
00078     txExpandedName att(aNsID, localName);
00079 
00080     txOutAttr* setAtt = 0;
00081     while ((setAtt = (txOutAttr*)iter.next())) {
00082          if (setAtt->mName == att) {
00083              setAtt->mValue = aValue;
00084              break;
00085          }
00086     }
00087     if (!setAtt) {
00088         setAtt = new txOutAttr(aNsID, localName, aValue);
00089         mAttributes.add(setAtt);
00090     }
00091 }
00092 
00093 void txXMLOutput::characters(const nsAString& aData, PRBool aDOE)
00094 {
00095     closeStartTag(MB_FALSE);
00096 
00097     if (aDOE) {
00098         printUTF8Chars(aData);
00099 
00100         return;
00101     }
00102 
00103     if (mInCDATASection) {
00104         PRUint32 length = aData.Length();
00105 
00106         *mOut << CDATA_START;
00107 
00108         if (length <= 3) {
00109             printUTF8Chars(aData);
00110         }
00111         else {
00112             PRUint32 j = 0;
00113             nsAString::const_iterator iter;
00114             aData.BeginReading(iter);
00115             mBuffer[j++] = *(iter++);
00116             mBuffer[j++] = *(iter++);
00117             mBuffer[j++] = *(iter++);
00118 
00119             nsAString::const_iterator end;
00120             aData.EndReading(end);
00121             while (iter != end) {
00122                 mBuffer[j++] = *(iter++);
00123                 if (mBuffer[(j - 1) % 4] == ']' &&
00124                     mBuffer[j % 4] == ']' &&
00125                     mBuffer[(j + 1) % 4] == '>') {
00126                     *mOut << CDATA_END;
00127                     *mOut << CDATA_START;
00128                 }
00129                 j = j % 4;
00130                 printUTF8Char(mBuffer[j]);
00131             }
00132 
00133             j = ++j % 4;
00134             printUTF8Char(mBuffer[j]);
00135             j = ++j % 4;
00136             printUTF8Char(mBuffer[j]);
00137             j = ++j % 4;
00138             printUTF8Char(mBuffer[j]);
00139         }
00140 
00141         *mOut << CDATA_END;
00142     }
00143     else {
00144         printWithXMLEntities(aData);
00145     }
00146 }
00147 
00148 void txXMLOutput::comment(const nsAString& aData)
00149 {
00150     closeStartTag(MB_FALSE);
00151 
00152     if (mOutputFormat.mIndent == eTrue) {
00153         for (PRUint32 i = 0; i < mIndentLevel; i++)
00154             *mOut << ' ';
00155     }
00156     *mOut << COMMENT_START;
00157     printUTF8Chars(aData);
00158     *mOut << COMMENT_END;
00159     if (mOutputFormat.mIndent == eTrue)
00160         *mOut << endl;
00161 }
00162 
00163 void txXMLOutput::endDocument(nsresult aResult)
00164 {
00165 }
00166 
00167 void txXMLOutput::endElement(const nsAString& aName,
00168                              const PRInt32 aNsID)
00169 {
00170     MBool newLine = (mOutputFormat.mIndent == eTrue) && mAfterEndTag;
00171     MBool writeEndTag = !(mStartTagOpen && mUseEmptyElementShorthand);
00172     closeStartTag(mUseEmptyElementShorthand);
00173     if (newLine)
00174         *mOut << endl;
00175     if (mOutputFormat.mIndent == eTrue)
00176         mIndentLevel -= DEFAULT_INDENT;
00177     if (writeEndTag) {
00178         if (newLine) {
00179             for (PRUint32 i = 0; i < mIndentLevel; i++)
00180                 *mOut << ' ';
00181         }
00182         *mOut << L_ANGLE_BRACKET << FORWARD_SLASH;
00183         printUTF8Chars(aName);
00184         *mOut << R_ANGLE_BRACKET;
00185     }
00186     if (mOutputFormat.mIndent == eTrue)
00187         *mOut << endl;
00188     mAfterEndTag = MB_TRUE;
00189     mInCDATASection = (MBool)mCDATASections.pop();
00190 }
00191 
00192 void txXMLOutput::processingInstruction(const nsAString& aTarget,
00193                                         const nsAString& aData)
00194 {
00195     closeStartTag(MB_FALSE);
00196     if (mOutputFormat.mIndent == eTrue) {
00197         for (PRUint32 i = 0; i < mIndentLevel; i++)
00198             *mOut << ' ';
00199     }
00200     *mOut << PI_START;
00201     printUTF8Chars(aTarget);
00202     *mOut << SPACE;
00203     printUTF8Chars(aData);
00204     *mOut << PI_END;
00205     if (mOutputFormat.mIndent == eTrue)
00206         *mOut << endl;
00207 }
00208 
00209 void txXMLOutput::startDocument()
00210 {
00211     if (mOutputFormat.mMethod == eMethodNotSet) {
00212         // XXX We should "cache" content until we have a 
00213         //     document element
00214     }
00215     *mOut << PI_START << XML_DECL << DOUBLE_QUOTE;
00216     *mOut << XML_VERSION;
00217     *mOut << DOUBLE_QUOTE << " encoding=\"UTF-8\"" << PI_END << endl;
00218 }
00219 
00220 void txXMLOutput::startElement(const nsAString& aName,
00221                                const PRInt32 aNsID)
00222 {
00223     if (!mHaveDocumentElement) {
00224         // XXX Output doc type and "cached" content
00225         mHaveDocumentElement = MB_TRUE;
00226     }
00227 
00228     MBool newLine = mStartTagOpen || mAfterEndTag;
00229     closeStartTag(MB_FALSE);
00230 
00231     if (mOutputFormat.mIndent == eTrue) {
00232         if (newLine) {
00233             *mOut << endl;
00234             for (PRUint32 i = 0; i < mIndentLevel; i++)
00235                 *mOut << ' ';
00236         }
00237     }
00238     *mOut << L_ANGLE_BRACKET;
00239     printUTF8Chars(aName);
00240     mStartTagOpen = MB_TRUE;
00241     if (mOutputFormat.mIndent == eTrue)
00242         mIndentLevel += DEFAULT_INDENT;
00243 
00244     mCDATASections.push((void*)mInCDATASection);
00245     mInCDATASection = MB_FALSE;
00246 
00247     nsCOMPtr<nsIAtom> localName = do_GetAtom(aName);
00248     txExpandedName currentElement(aNsID, localName);
00249     txListIterator iter(&(mOutputFormat.mCDATASectionElements));
00250     while (iter.hasNext()) {
00251         if (currentElement == *(txExpandedName*)iter.next()) {
00252             mInCDATASection = MB_TRUE;
00253             break;
00254         }
00255     }
00256 }
00257 
00258 void txXMLOutput::closeStartTag(MBool aUseEmptyElementShorthand)
00259 {
00260     mAfterEndTag = aUseEmptyElementShorthand;
00261     if (mStartTagOpen) {
00262         txListIterator iter(&mAttributes);
00263         txOutAttr* att;
00264         while ((att = (txOutAttr*)iter.next())) {
00265             *mOut << SPACE;
00266             const char* attrVal;
00267             att->mName.mLocalName->GetUTF8String(&attrVal);
00268             *mOut << attrVal;
00269             if (!att->mShorthand) {
00270                 *mOut << EQUALS << DOUBLE_QUOTE;
00271                 printWithXMLEntities(att->mValue, MB_TRUE);
00272                 *mOut << DOUBLE_QUOTE;
00273             }
00274             delete (txOutAttr*)iter.remove();
00275         }
00276 
00277         if (aUseEmptyElementShorthand)
00278             *mOut << FORWARD_SLASH;
00279         *mOut << R_ANGLE_BRACKET;
00280         mStartTagOpen = MB_FALSE;
00281     }
00282 }
00283 
00284 void txXMLOutput::printUTF8Char(PRUnichar& ch)
00285 {
00286     // PRUnichar is 16-bits so we only need to cover up to 0xFFFF
00287 
00288     // 0x0000-0x007F
00289     if (ch < 128) {
00290         *mOut << (char)ch;
00291     }
00292     // 0x0080-0x07FF
00293     else if (ch < 2048) {
00294         *mOut << (char) (192+(ch/64));        // 0xC0 + x/64
00295         *mOut << (char) (128+(ch%64));        // 0x80 + x%64
00296     }
00297     // 0x800-0xFFFF
00298     else {
00299         *mOut << (char) (224+(ch/4096));      // 0xE0 + x/64^2
00300         *mOut << (char) (128+((ch/64)%64));   // 0x80 + (x/64)%64
00301         *mOut << (char) (128+(ch%64));        // 0x80 + x%64
00302     }
00303 }
00304 
00305 void txXMLOutput::printUTF8Chars(const nsAString& aData)
00306 {
00307     *mOut << NS_ConvertUCS2toUTF8(aData).get();
00308 }
00309 
00310 void txXMLOutput::printWithXMLEntities(const nsAString& aData,
00311                                        MBool aAttribute)
00312 {
00313     nsAString::const_iterator iter, end;
00314     aData.EndReading(end);
00315 
00316     for (aData.BeginReading(iter); iter != end; ++iter) {
00317         PRUnichar currChar = *iter;
00318         switch (currChar) {
00319             case AMPERSAND:
00320                 *mOut << AMP_ENTITY;
00321                 break;
00322             case APOSTROPHE:
00323                 if (aAttribute)
00324                     *mOut << APOS_ENTITY;
00325                 else
00326                     printUTF8Char(currChar);
00327                 break;
00328             case GT:
00329                 *mOut << GT_ENTITY;
00330                 break;
00331             case LT:
00332                 *mOut << LT_ENTITY;
00333                 break;
00334             case QUOTE:
00335                 if (aAttribute)
00336                     *mOut << QUOT_ENTITY;
00337                 else
00338                     printUTF8Char(currChar);
00339                 break;
00340             default:
00341                 printUTF8Char(currChar);
00342                 break;
00343         }
00344     }
00345     *mOut << flush;
00346 }