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