Orb/Doxygen/src/xmlwriter.cpp
changeset 3 d8fccb2cd802
child 4 468f4c8d3d5b
equal deleted inserted replaced
2:932c358ece3e 3:d8fccb2cd802
       
     1 /******************************************************************************
       
     2  *
       
     3  * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4  *
       
     5  * Permission to use, copy, modify, and distribute this software and its
       
     6  * documentation under the terms of the GNU General Public License is hereby 
       
     7  * granted. No representations are made about the suitability of this software 
       
     8  * for any purpose. It is provided "as is" without express or implied warranty.
       
     9  * See the GNU General Public License for more details.
       
    10  *
       
    11  */
       
    12 
       
    13 
       
    14 
       
    15 #include "xmlwriter.h"
       
    16 #include "message.h"
       
    17 #include "xmldita.h"
       
    18 
       
    19 char ILLEGAL_UNICODE_REPLACEMENT = ' ';
       
    20 /******************** XmlStream ********************/
       
    21 XmlStream::XmlStream(const QString &fileName, const QString &aEncoding, const QString &aStandalone, const QString &doctypeStr) : mFile(fileName), \
       
    22 											mStreamP(0), \
       
    23 											mElemStack(), \
       
    24 											mInElement(false)
       
    25 {
       
    26 	mCanIndentList.setAutoDelete(true);
       
    27 	mIsOpen = mFile.open(IO_WriteOnly);
       
    28 	if (mIsOpen) {
       
    29 		mStreamP = new QTextStream(&mFile);
       
    30 		if (aEncoding == "UTF-8") {
       
    31 			mStreamP->setEncoding(QTextStream::UnicodeUTF8);
       
    32 		} else if(aEncoding == "Latin1") {
       
    33 			mStreamP->setEncoding(QTextStream::Latin1);
       
    34 		} else if(aEncoding == "ISO-8859-1") {
       
    35 			mStreamP->setEncoding(QTextStream::Latin1);
       
    36 		} else {
       
    37 			// No specific encoding set
       
    38 		}
       
    39 		*mStreamP << "<?xml version='1.0' encoding='" << aEncoding << "' standalone='" << aStandalone << "'?>";
       
    40 		if (doctypeStr.length() > 0) {
       
    41 			*mStreamP << "\n<!DOCTYPE " << doctypeStr << ">";
       
    42 		}
       
    43 	} else {
       
    44 		mStreamP = 0;
       
    45 		err("Cannot open file %s for writing!\n", fileName.data());
       
    46 	}
       
    47 	// Allow output
       
    48 	outputResume();
       
    49 	/// Build text -> unicode map
       
    50 	unicodeCharTable.insert("copy", "A9");
       
    51 	unicodeCharTable.insert("trade", "2122");
       
    52 	unicodeCharTable.insert("reg", "AE");
       
    53 	unicodeCharTable.insert("lsquo", "60");
       
    54 	unicodeCharTable.insert("rsquo", "B4");
       
    55 	unicodeCharTable.insert("ldquo", "201C");
       
    56 	unicodeCharTable.insert("rdquo", "201D");
       
    57 	unicodeCharTable.insert("ndash", "2013");
       
    58 	unicodeCharTable.insert("mdash", "2014");
       
    59 	unicodeCharTable.insert("Auml", "C4");
       
    60 	unicodeCharTable.insert("Euml", "CB");
       
    61 	unicodeCharTable.insert("Iuml", "CF");
       
    62 	unicodeCharTable.insert("Ouml", "F6");
       
    63 	unicodeCharTable.insert("Uuml", "FC");
       
    64 	unicodeCharTable.insert("Yuml", "178");
       
    65 	unicodeCharTable.insert("auml", "E4");
       
    66 	unicodeCharTable.insert("euml", "EB");
       
    67 	unicodeCharTable.insert("iuml", "EF");
       
    68 	unicodeCharTable.insert("ouml", "F6");
       
    69 	unicodeCharTable.insert("uuml", "FC");
       
    70 	unicodeCharTable.insert("yuml", "FF");
       
    71 	unicodeCharTable.insert("Aacute", "C1");
       
    72 	unicodeCharTable.insert("Eacute", "C9");
       
    73 	unicodeCharTable.insert("Iacute", "CD");
       
    74 	unicodeCharTable.insert("Oacute", "D3");
       
    75 	unicodeCharTable.insert("Uacute", "DA");
       
    76 	unicodeCharTable.insert("Yacute", "DD");
       
    77 	unicodeCharTable.insert("aacute", "E1");
       
    78 	unicodeCharTable.insert("eacute", "E9");
       
    79 	unicodeCharTable.insert("iacute", "ED");
       
    80 	unicodeCharTable.insert("oacute", "F3");
       
    81 	unicodeCharTable.insert("uacute", "FA");
       
    82 	unicodeCharTable.insert("yacute", "FD");
       
    83 	unicodeCharTable.insert("Agrave", "C0");
       
    84 	unicodeCharTable.insert("Egrave", "C8");
       
    85 	unicodeCharTable.insert("Igrave", "CC");
       
    86 	unicodeCharTable.insert("Ograve", "D2");
       
    87 	unicodeCharTable.insert("Ugrave", "F9");
       
    88 	unicodeCharTable.insert("agrave", "E0");
       
    89 	unicodeCharTable.insert("egrave", "E8");
       
    90 	unicodeCharTable.insert("igrave", "EC");
       
    91 	unicodeCharTable.insert("ograve", "F2");
       
    92 	unicodeCharTable.insert("ugrave", "F9");
       
    93 	//unicodeCharTable.insert("ygrave", ""); In doxygen documentation but code unknown or doesn't exist
       
    94 	unicodeCharTable.insert("Acirc", "C2");
       
    95 	unicodeCharTable.insert("Ecirc", "CA");
       
    96 	unicodeCharTable.insert("Icirc", "CE");
       
    97 	unicodeCharTable.insert("Ocirc", "D4");
       
    98 	unicodeCharTable.insert("Ucirc", "DB");
       
    99 	unicodeCharTable.insert("acirc", "E2");
       
   100 	unicodeCharTable.insert("ecirc", "EA");
       
   101 	unicodeCharTable.insert("icirc", "EE");
       
   102 	unicodeCharTable.insert("ocirc", "F4");
       
   103 	unicodeCharTable.insert("ucirc", "FB");
       
   104 	//unicodeCharTable.insert("ycirc", ""); In doxygen documentation but code unknown or doesn't exist
       
   105 	unicodeCharTable.insert("Atilde", "C3");
       
   106 	unicodeCharTable.insert("Ntilde", "D1");
       
   107 	unicodeCharTable.insert("Otilde", "D5");
       
   108 	unicodeCharTable.insert("atilde", "E3");
       
   109 	unicodeCharTable.insert("ntilde", "F1");
       
   110 	unicodeCharTable.insert("otilde", "F5");
       
   111 	unicodeCharTable.insert("szlig", "DF");
       
   112 	unicodeCharTable.insert("Ccedil", "C7");
       
   113 	unicodeCharTable.insert("ccedil", "E7");
       
   114 	unicodeCharTable.insert("Aring", "C5");
       
   115 	unicodeCharTable.insert("aring", "E5");
       
   116 	unicodeCharTable.insert("Oslash", "D8");
       
   117 	unicodeCharTable.insert("oslash", "F8");
       
   118 	unicodeCharTable.insert("nbsp", "A0");
       
   119 	unicodeCharTable.insert("AElig", "C6");
       
   120 	unicodeCharTable.insert("aelig", "E6");
       
   121 	
       
   122 }
       
   123 
       
   124 void XmlStream::startElement(const QString& aElemName, const AttributeMap& aAttrs)
       
   125 {
       
   126 	if (mStreamP && mIsOpen && mCanWrite) {
       
   127 		if (mInElement) {
       
   128 			// Close existing element
       
   129 			*mStreamP << ">";
       
   130 		} else {
       
   131 			mInElement = true;
       
   132 		}
       
   133 		indent();
       
   134 		// Write element name
       
   135 		*mStreamP << "<" << aElemName;
       
   136 		// Attributes in sorted order
       
   137 		AttributeMapIter it = aAttrs.begin();
       
   138 		while (it != aAttrs.end()){
       
   139 			QString attrVal = encodeText(it.data());
       
   140 #ifdef DITA_OT_BUG_ATTRIBUTE_VALUE_HACK
       
   141 			// DITA Open Toolkit error, it fails to re-encode files properly
       
   142 			// Replace "&lt;" with "&amp;lt;"
       
   143 			// Replace "&gt;" with "&amp;gt;"
       
   144 			int fIdx = 0;
       
   145 			QString toFind;
       
   146 			QString toReplace;
       
   147 			toFind = "&lt;";
       
   148 			toReplace = "&amp;lt;";
       
   149 			fIdx = attrVal.find(toFind, 0);
       
   150 			while (fIdx != -1) {
       
   151 				attrVal.replace(fIdx, toFind.length(), toReplace);
       
   152 				fIdx = attrVal.find(toFind, 0);
       
   153 			}
       
   154 			toFind = "&gt;";
       
   155 			toReplace = "&amp;gt;";
       
   156 			fIdx = attrVal.find(toFind, 0);
       
   157 			while (fIdx != -1) {
       
   158 				attrVal.replace(fIdx, toFind.length(), toReplace);
       
   159 				fIdx = attrVal.find(toFind, 0);
       
   160 			}
       
   161 #endif
       
   162 			*mStreamP << " " << it.key() << "=\"" << attrVal << "\"";
       
   163 			++it;
       
   164 		}
       
   165 		// Update internals
       
   166 		mInElement = true;
       
   167 		//mCanIndent = true;
       
   168 		mElemStack.push(&aElemName);
       
   169 		mCanIndentList.append(new bool(true));
       
   170 	}
       
   171 }
       
   172 
       
   173 void XmlStream::characters(const QString& aText)
       
   174 {
       
   175 	// If this test was not here then if passed an empty string the stream
       
   176 	// will end up writing <Element></Element> rather than <Element/>
       
   177 	if (aText.length() > 0) {
       
   178 		if (mStreamP && mIsOpen && mCanWrite) {
       
   179 			closeElementDeclIfOpen();
       
   180 			*mStreamP << encodeText(aText);
       
   181 		}
       
   182 		// Don't indent mixed content
       
   183 		//mCanIndent = false;
       
   184 		setLastIndent(false);
       
   185 	}
       
   186 #ifdef DITA_TRACE
       
   187 #ifdef DITA_TRACE_TO_XML
       
   188 	// Useful for assertion crashes where otherwise the buffer would be lost
       
   189 	flush(*mStreamP);
       
   190 #endif
       
   191 #endif
       
   192 }
       
   193 
       
   194 void XmlStream::characters(char c)
       
   195 {
       
   196 	if (mStreamP && mIsOpen && mCanWrite) {
       
   197 		closeElementDeclIfOpen();
       
   198 		if (isLegalXmlChar(c)) {
       
   199 			if (mustEncodeChar(c)) {
       
   200 				*mStreamP << encodeChar(c);
       
   201 			} else {
       
   202 				*mStreamP << c;
       
   203 			}
       
   204 		} else {
       
   205 			*mStreamP << ILLEGAL_UNICODE_REPLACEMENT;
       
   206 		}
       
   207 	}
       
   208 	// Don't indent mixed content
       
   209 	//mCanIndent = false;
       
   210 	setLastIndent(false);
       
   211 #ifdef DITA_TRACE
       
   212 #ifdef DITA_TRACE_TO_XML
       
   213 	// Useful for assertion crashes where otherwise the buffer would be lost
       
   214 	flush(*mStreamP);
       
   215 #endif
       
   216 #endif
       
   217 }
       
   218 
       
   219 XmlStream& XmlStream::operator<<(const QCString& s)
       
   220 {
       
   221     characters(s);
       
   222 	return *this;
       
   223 }
       
   224 
       
   225 XmlStream& XmlStream::operator<<(const char* s)
       
   226 {
       
   227     characters(s);
       
   228 	return *this;
       
   229 }
       
   230 
       
   231 XmlStream& XmlStream::operator<<(char c)
       
   232 {
       
   233     characters(c);
       
   234 	return *this;
       
   235 }
       
   236 
       
   237 XmlStream& XmlStream::writeUnicode(const QCString& s)
       
   238 {
       
   239 	if (mStreamP && mIsOpen && mCanWrite) {
       
   240 		closeElementDeclIfOpen();
       
   241 		if (unicodeCharTable.find(s)) {
       
   242 			*mStreamP << "&#x";
       
   243 			*mStreamP << unicodeCharTable[s];
       
   244 			*mStreamP << ";";
       
   245 		} else {
       
   246 			// Write a warning as a comment
       
   247 			QString cmtTxt = "Can not write Unicode for Doxygen interpreted symbol: \"";
       
   248 			cmtTxt += s;
       
   249 			cmtTxt += "\"";
       
   250 			comment(cmtTxt);
       
   251 		}
       
   252 	}
       
   253 	// Don't indent mixed content
       
   254 	//mCanIndent = false;
       
   255 	setLastIndent(false);
       
   256 	return *this;
       
   257 }
       
   258 
       
   259 void XmlStream::processingInstruction(const QString& aText)
       
   260 {
       
   261 	if (mStreamP && mIsOpen && mCanWrite) {
       
   262 		closeElementDeclIfOpen();
       
   263 		*mStreamP << "<?" << aText << "?>";
       
   264 	}
       
   265 	//mCanIndent = true;
       
   266 }
       
   267 
       
   268 void XmlStream::comment(const QString& aText)
       
   269 {
       
   270 	if (mStreamP && mIsOpen && mCanWrite) {
       
   271 		closeElementDeclIfOpen();
       
   272 		*mStreamP << "<!-- " << aText << " -->";
       
   273 	}
       
   274 	//mCanIndent = true;
       
   275 }
       
   276 
       
   277 void XmlStream::endElement(const QString& aElemName)
       
   278 {
       
   279 	if (mStreamP && mIsOpen && mCanWrite) {
       
   280 		if (mInElement) {
       
   281 			// Use minimal form
       
   282 			*mStreamP << "/>";
       
   283 			mInElement = false;
       
   284 			mElemStack.pop();
       
   285 		} else {
       
   286 			indent(1);
       
   287 			*mStreamP << "</" << *(mElemStack.pop()) << ">";
       
   288 		}
       
   289 	}
       
   290 	mCanIndentList.removeLast();
       
   291 }
       
   292 
       
   293 void XmlStream::closeElementDeclIfOpen()
       
   294 {
       
   295 	if (mStreamP && mIsOpen && mCanWrite) {
       
   296 		if (mInElement) {
       
   297 			*mStreamP << ">";
       
   298 			mInElement = false;
       
   299 		}
       
   300 	}
       
   301 }
       
   302 
       
   303 void XmlStream::indent(unsigned int aInitVal)
       
   304 {
       
   305 	if (mStreamP && mIsOpen && canIndent() && mCanWrite) {
       
   306 		*mStreamP << XML_OUTPUT_ENDL;
       
   307 		for (unsigned int i = aInitVal; i < mElemStack.count(); i++) {
       
   308 			*mStreamP << XML_INDENT;
       
   309 		}
       
   310 	}
       
   311 }
       
   312 
       
   313 /** Returns 1 if the character is in the legal unicode range
       
   314 See: http://www.w3.org/TR/REC-xml/#charsets
       
   315 */
       
   316 inline bool XmlStream::isLegalXmlChar(QChar c) const
       
   317 {
       
   318 	ushort u = c.unicode();
       
   319 	//printf("XmlStream::isLegalXmlChar() testing 0x%X\n", u);
       
   320 	// This is what the code should be:
       
   321 	/*
       
   322 	bool result = (u == 0x09 || u == 0x0A || u == 0x0D || \
       
   323 				((u >= 0x20) && (u <= 0xD7FF)) || \
       
   324 				((u >= 0xE000) && (u <= 0xFFFD)) \
       
   325 			);
       
   326 	*/
       
   327 	// An this is the kludge that prevents weird characters (e.g. 0xA0)
       
   328 	// that appear in the source code from getting into the XML.
       
   329 	bool result = (u == 0x09 || u == 0x0A || u == 0x0D || \
       
   330 				((u >= 0x20) && (u <= 0x7F)) \
       
   331 			);
       
   332 	if (!result) {
       
   333 		msg("XmlStream::isLegalXmlChar() rejecting 0x%X\n", u);
       
   334 	}
       
   335 	return result;
       
   336 }
       
   337 
       
   338 QString XmlStream::encodeText(const QString& aStr) const
       
   339 {
       
   340 	QCString result;
       
   341 	//printf("XmlStream::encodeText() encoding \"%s\"\n", aStr.data());
       
   342 	for (unsigned int i=0; i < aStr.length(); ++i) {
       
   343 		if (isLegalXmlChar(aStr[i])) {
       
   344 			char c = aStr[i];
       
   345 			if (mustEncodeChar(c)) {
       
   346 				result += encodeChar(c);
       
   347 			} else {
       
   348 				result += c;
       
   349 			}
       
   350 		} else {
       
   351 			result += ILLEGAL_UNICODE_REPLACEMENT;
       
   352 		}
       
   353   }
       
   354   return result;
       
   355 }
       
   356 
       
   357 /** Converts a char to a QString using XML entity transformaiton */
       
   358 QString XmlStream::encodeChar(char c) const
       
   359 {
       
   360 	switch (c) {
       
   361 		case '<': return QString("&lt;");	break;
       
   362 		case '>': return QString("&gt;"); break;
       
   363 		case '&': return QString("&amp;");  break;
       
   364 		case '\'': return QString("&apos;"); break; 
       
   365 		case '"': return QString("&quot;"); break;
       
   366 		default:
       
   367 			QString s;
       
   368 			s += c;
       
   369 			return s;
       
   370 			break;
       
   371 	}
       
   372 }
       
   373 
       
   374 /** Returns true if a char needs to be converted using XML entity transformaiton */
       
   375 bool XmlStream::mustEncodeChar(char c) const
       
   376 {
       
   377 	switch (c) {
       
   378 		// Note fall through
       
   379 		case '<':
       
   380 		case '>':
       
   381 		case '&':
       
   382 		case '\'':
       
   383 		case '"':
       
   384 			return true;
       
   385 			break;
       
   386 		default:
       
   387 			break;
       
   388 	}
       
   389 	return false;
       
   390 }
       
   391 
       
   392 bool XmlStream::canIndent()
       
   393 {
       
   394 	bool *bP;
       
   395 	for (bP = mCanIndentList.first(); bP != 0; bP = mCanIndentList.next()) {
       
   396 		if (!*bP) {
       
   397 			return false;
       
   398 		}
       
   399 	}
       
   400 	return true;
       
   401 }
       
   402 
       
   403 void XmlStream::setLastIndent(bool theB)
       
   404 {
       
   405 	mCanIndentList.removeLast();
       
   406 	mCanIndentList.append(new bool(theB));
       
   407 }
       
   408 
       
   409 /// Suspend output
       
   410 void XmlStream::outputSuspend()
       
   411 {
       
   412 	mCanWrite = false;
       
   413 }
       
   414 
       
   415 /// Resume output
       
   416 void XmlStream::outputResume()
       
   417 {
       
   418 	mCanWrite = true;
       
   419 }
       
   420 
       
   421 void XmlStream::close()
       
   422 {
       
   423 	if (mStreamP) {
       
   424 		// Ignore mCanWrite
       
   425 		outputResume();
       
   426 		closeElementDeclIfOpen();
       
   427 		while(mElemStack.count()){
       
   428 			endElement(mElemStack[mElemStack.count()-1]);
       
   429 		}
       
   430 		// Delete the stream and close the file
       
   431 		delete mStreamP;
       
   432 		mStreamP = 0;
       
   433 		mFile.close();
       
   434 		mIsOpen = false;
       
   435 	}
       
   436 }
       
   437 
       
   438 
       
   439 XmlStream::~XmlStream()
       
   440 {
       
   441 	try {
       
   442 		close();
       
   443 	}
       
   444 	catch(...) {}
       
   445 }
       
   446 /******************** END: XmlStream ********************/
       
   447 
       
   448 /******************** XmlElement ********************/
       
   449 XmlElement::XmlElement(XmlStream& aStream, const QString& aElemName) : mStream(aStream), mElemName(aElemName)
       
   450 {
       
   451 	AttributeMap attrs;
       
   452 	mStream.startElement(mElemName, attrs);
       
   453 }
       
   454 
       
   455 XmlElement::XmlElement(XmlStream& aStream, const QString& aElemName, const QString& aAttr, const QString& aAttrValue) : mStream(aStream), mElemName(aElemName)
       
   456 {
       
   457 	AttributeMap attrs;
       
   458 	attrs[aAttr] = aAttrValue;
       
   459 	mStream.startElement(mElemName, attrs);
       
   460 }
       
   461 
       
   462 XmlElement::XmlElement(XmlStream& aStream, const QString& aElemName, const QString& aAttr, char aAttrValue) : mStream(aStream), mElemName(aElemName)
       
   463 {
       
   464 	AttributeMap attrs;
       
   465 	attrs[aAttr] = QChar(aAttrValue);
       
   466 	mStream.startElement(mElemName, attrs);
       
   467 }
       
   468 
       
   469 XmlElement::XmlElement(XmlStream& aStream, const QString& aElemName, AttributeMap& aAttrs) : mStream(aStream), mElemName(aElemName)
       
   470 {
       
   471 	mStream.startElement(mElemName, aAttrs);
       
   472 }
       
   473 
       
   474 /*
       
   475 // Parse an attribute string of the form "attr_0=value_0 attr_1=value_1"
       
   476 XmlElement::XmlElement(XmlStream& aStream, const QString& aElemName, const QString& aAttrString) : mStream(aStream), mElemName(aElemName)
       
   477 {
       
   478 	AttributeMap attrMap;
       
   479 	QString myStr = aAttrString.simplifyWhiteSpace();
       
   480 	int s = 0; // Index of start of attr
       
   481 	int e = 0; // Index of '='
       
   482 	int v = 0; // Index of end of value
       
   483 	while (s < (int) myStr.length()) {
       
   484 		e = s;
       
   485 		v = s;
       
   486 		e = myStr.find('=', s);
       
   487 		if (e == -1) {
       
   488 			break;
       
   489 		}
       
   490 		v = myStr.find(' ', s);
       
   491 		if (v == -1) {
       
   492 			v = myStr.length();
       
   493 		}
       
   494 		attrMap[myStr.mid(s, e-s)] = myStr.mid(e+1, v-(e+1));
       
   495 		s = v+1;
       
   496 	}
       
   497 	mStream.startElement(mElemName, attrMap);
       
   498 }
       
   499 */
       
   500 
       
   501 XmlElement::~XmlElement()
       
   502 {
       
   503 	try {
       
   504 		mStream.endElement(mElemName);
       
   505 	}
       
   506 	catch(...) {}
       
   507 }
       
   508 /******************** END: XmlElement ********************/
       
   509 
       
   510 /******************** XmlElementStack ********************/
       
   511 XmlElementStack::XmlElementStack(XmlStream& aStream) : mStream(aStream)
       
   512 {
       
   513 }
       
   514 
       
   515 void XmlElementStack::push(const QString& aElementName)
       
   516 {
       
   517 	mElemStack.push(new XmlElement(mStream, aElementName));
       
   518 }
       
   519 
       
   520 void XmlElementStack::push(const QString& aElementName, const QString& aAttr, const QString& aAttrValue)
       
   521 {
       
   522 	mElemStack.push(new XmlElement(mStream, aElementName, aAttr, aAttrValue));
       
   523 }
       
   524 
       
   525 void XmlElementStack::push(const QString& aElementName, AttributeMap& aAttrs)
       
   526 {
       
   527 	mElemStack.push(new XmlElement(mStream, aElementName, aAttrs));
       
   528 }
       
   529 
       
   530 void XmlElementStack::pop(const QString &aElementName)
       
   531 {
       
   532 	XmlElement *pElem = mElemStack.pop();
       
   533 	if (pElem->getElemName() != aElementName) {
       
   534 		err(pElem->getElemName() + " is not equal to " + aElementName +"\n");
       
   535 	}
       
   536 	ASSERT(pElem->getElemName() == aElementName);
       
   537 	delete pElem;
       
   538 }
       
   539 
       
   540 void XmlElementStack::pop()
       
   541 {
       
   542 	XmlElement *pElem = mElemStack.pop();
       
   543 	delete pElem;
       
   544 }
       
   545 
       
   546 void XmlElementStack::pushpop(const QString &aElementName)
       
   547 {
       
   548 	mElemStack.push(new XmlElement(mStream, aElementName));
       
   549 	pop(aElementName);
       
   550 }
       
   551 
       
   552 void XmlElementStack::pushpop(const QString &aElementName, const QString& aText)
       
   553 {
       
   554 	mElemStack.push(new XmlElement(mStream, aElementName));
       
   555 	mStream.characters(aText);
       
   556 	pop(aElementName);
       
   557 }
       
   558 
       
   559 bool XmlElementStack::isEmpty() const
       
   560 {
       
   561 	return mElemStack.isEmpty();
       
   562 }
       
   563 
       
   564 const XmlElement& XmlElementStack::peek() const 
       
   565 {
       
   566 	return *mElemStack.top();
       
   567 }
       
   568 
       
   569 void XmlElementStack::addAttribute(const QString &name, const QString &value)
       
   570 {
       
   571 	XmlElement *pElem = mElemStack.pop();
       
   572 	QString elemenName = pElem->getElemName();
       
   573 	delete pElem;
       
   574 	XmlElement *elem = new XmlElement(mStream, elemenName, name, value);
       
   575 	mElemStack.push(elem);
       
   576 }
       
   577 
       
   578 void XmlElementStack::close()
       
   579 {
       
   580 	while(mElemStack.count()){
       
   581 		pop();
       
   582 	}
       
   583 }
       
   584 
       
   585 XmlElementStack::~XmlElementStack()
       
   586 {
       
   587 	try {
       
   588 		close();
       
   589 	}
       
   590 	catch(...) {}
       
   591 }
       
   592 /******************** END: XmlElementStack ********************/
       
   593