--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Orb/Doxygen/src/xmlwriter.h Thu Jan 21 17:29:01 2010 +0000
@@ -0,0 +1,223 @@
+
+#ifndef _XMLWRITER_H
+#define _XMLWRITER_H
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qmap.h>
+#include <qdict.h>
+#include <qstack.h>
+
+typedef QMap<QString, QString> AttributeMap;
+typedef QMapConstIterator<QString, QString> AttributeMapIter;
+
+static QString XML_INDENT("\t");
+static QString XML_OUTPUT_ENDL("\n");
+
+// TODO: If the output file fails to open then write to an internal string that can be
+// accessed by the caller after they call close(). This might be useful for internal
+// XML snippets.
+
+/**
+Class that handles writing to an XML file. It guarentees well-formedness,
+handles the encoding of PCDATA and indents the output.
+
+Individual elements are created as objects, they are written to the file on
+creation and closed when the element is destroyed.
+*/
+class XmlStream
+{
+ friend class XmlElement;
+public:
+ /// Create an XML stream that writes to a file with the given encoding
+ XmlStream(const QString &fileName,
+ const QString &aEncoding="UTF-8",
+ const QString &aStandalone="no",
+ const QString &doctypeStr="");
+ /// Returns true of the file tha tthis stream is writing to is currently open.
+ bool isOpen() const { return mIsOpen; }
+ /// Write PCDATA to the output file, entities will be translated
+ void characters(const QString &aText);
+ void characters(char c);
+ /// Write a processing instruction to the output file.
+ void processingInstruction(const QString &aText);
+ /// Write a comment to the output file.
+ void comment(const QString &aText);
+ /// Output operator overloading, these equate to calling characters()
+ XmlStream& operator<<(const QCString &s);
+ XmlStream& operator<<(const char *s);
+ XmlStream& operator<<(char s);
+ /// Write a unicode character to the stream
+ XmlStream& writeUnicode(const QCString &s);
+ /// Close the XML file (if open), this will flush any unclosed elements
+ void close();
+ /// Get the file name
+ QString getFileName() const { return mFile.name(); }
+ /// Suspend and resume output
+ void outputSuspend();
+ void outputResume();
+ /// Destructor, this will call close()
+ ~XmlStream();
+private:
+ QDict<char> unicodeCharTable;
+ /// Write out the start of element and register that element name on the stack
+ void startElement(const QString &aElemName, const AttributeMap &aAttrs);
+ /// Write out the end of element and remove that element name on the stack
+ void endElement(const QString &aElemName);
+ void closeElementDeclIfOpen();
+ void indent(unsigned int aInitVal=0);
+ inline bool isLegalXmlChar(QChar c) const;
+ QString encodeText(const QString &aStr) const;
+ QString encodeChar(char c) const;
+ bool mustEncodeChar(char c) const;
+private:
+ QFile mFile;
+ QTextStream *mStreamP;
+ QStack<QString> mElemStack;
+ bool mInElement;
+ bool mCanIndent;
+ bool mIsOpen;
+ bool mCanWrite;
+private:
+ // Private cctor and op=
+ XmlStream (const XmlStream &rhs);
+ XmlStream& operator=(const XmlStream &rhs);
+};
+
+/**
+XmlElement
+
+Usage example:
+@code
+// Start an XML file
+XmlStream t(fileName);
+if (!t.isOpen()) {
+ err("Cannot open file %s for writing!\n", fileName.data());
+ return;
+}
+// Create and persist a root element with no attributes
+XmlElement elemRoot(t, QString("root"), AttributeMap());
+// Note the use of {} that causes elem to go out of scope an thus
+// its destructor will be called so closing the element
+{
+ // Create and persist a root element with attributes
+ // First create the attributes
+ AttributeMap attrs;
+ attrs["attr_1"] = "value_1";
+ attrs["attr_2"] = "value_2";
+ // Create the element
+ XmlElement elem(t, "Element", attrs);
+ // Write some text
+ t.characters("Some stuff");
+ // Now elem is going out of scope
+}
+// Open and close and element
+XmlElement(t, "OpenClose");
+// Open and close and element with one attribute
+XmlElement(t, "WhatsForDinner", "spam", "eggs");
+// t goes out of scope and will close the file
+@endcode
+
+The result is:
+@verbatim
+<?xml version='1.0' encoding='UTF-8' standalone='no'?>
+<root>
+ <Element attr_1="value_1" attr_2="value_2">Some stuff</Element>
+ <OpenClose/>
+ <WhatsForDinner spam="eggs"/>
+</root>
+@endverbatim
+
+*/
+class XmlElement
+{
+public:
+ /// Constructor with no attributes
+ XmlElement(XmlStream &aStream, const QString &aElemName);
+ /// Constructor with one attribute
+ XmlElement(XmlStream &aStream, const QString &aElemName, const QString &aAttr, const QString &aAttrValue);
+ /// Constructor with one attribute the values of which is a char
+ XmlElement(XmlStream &aStream, const QString &aElemName, const QString &aAttr, char aAttrValue);
+ /// Constructor with any number of attributes
+ XmlElement(XmlStream &aStream, const QString &aElemName, AttributeMap &aAttrs);
+ const QString& getElemName() const { return mElemName; }
+ /// Destructor, this closes the element on the stream
+ ~XmlElement();
+private:
+ XmlStream& mStream;
+ QString mElemName;
+private:
+ // Private cctor and op=
+ XmlElement (const XmlElement &rhs);
+ XmlElement& operator=(const XmlElement &rhs);
+};
+
+/*
+This can be used in the case that elements need to persist over scope.
+
+So given this kind of code:
+void XmlDocVisitor::visitPre(DocHtmlDescTitle *)
+{
+ if (m_hide) return;
+ m_t << "<varlistentry><term>";
+}
+
+void XmlDocVisitor::visitPost(DocHtmlDescTitle *)
+{
+ if (m_hide) return;
+ m_t << "</term></varlistentry>\n";
+}
+
+It can be replaced by this (assuming XmlDocVisitor has a member XmlElementStack m_es;):
+void XmlDocVisitor::visitPre(DocHtmlDescTitle *)
+{
+ if (m_hide) return;
+ m_es.push("varlistentry", AttributeMap());
+ m_es.push("term", AttributeMap());
+}
+
+void XmlDocVisitor::visitPost(DocHtmlDescTitle *)
+{
+ if (m_hide) return;
+ m_es.pop();
+ m_es.pop();
+}
+
+*/
+class XmlElementStack
+{
+public:
+ XmlElementStack(XmlStream &aStream);
+ /// Push element with no attributes
+ void push(const QString &aElemName);
+ /// Constructor with one attribute
+ void push(const QString &aElemName, const QString &aAttr, const QString &aAttrValue);
+ /// Push element with any number of attributes
+ void push(const QString &aElementName, AttributeMap &aAttrs);
+ /// Method that will check the element name matches
+ void pop(const QString &aElementName);
+ /// Push and pop an element with no attributes
+ void pushpop(const QString &aElemName);
+ /// Push and pop an element with no attributes but with some content
+ void pushpop(const QString &aElemName, const QString &aText);
+ /// Add attribute to item currently on top of the stack
+ void addAttribute(const QString &name, const QString &value);
+ /// Return a reference to the top of the stack. The stack is not modified.
+ const XmlElement& peek() const;
+ /// Return true if the stack is empty
+ bool isEmpty() const;
+ void close();
+ ~XmlElementStack();
+private:
+ XmlStream& mStream;
+ QStack<XmlElement> mElemStack;
+private:
+ // Private cctor and op=
+ XmlElementStack (const XmlElementStack &rhs);
+ XmlElementStack& operator=(const XmlElementStack &rhs);
+ // Used by destructor
+ void pop();
+};
+
+#endif // _XMLWRITER_H