From bd0f3345a938b35ce6a12f6150373b0955b8dd12 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sun, 10 Jul 2011 15:24:15 -0500 Subject: Add Qt3 development HEAD version --- doc/html/xml-sax-features-walkthrough.html | 379 +++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 doc/html/xml-sax-features-walkthrough.html (limited to 'doc/html/xml-sax-features-walkthrough.html') diff --git a/doc/html/xml-sax-features-walkthrough.html b/doc/html/xml-sax-features-walkthrough.html new file mode 100644 index 0000000..90d1603 --- /dev/null +++ b/doc/html/xml-sax-features-walkthrough.html @@ -0,0 +1,379 @@ + + + + + +Walkthrough: Using SAX2 features with the Qt XML classes + + + + + + + +
+ +Home + | +All Classes + | +Main Classes + | +Annotated + | +Grouped Classes + | +Functions +

Walkthrough: Using SAX2 features with the Qt XML classes

+ + +

+

This document assumes that you are familiar with namespaces in XML and the concept of a SAX2 +parser. +If features of SAX2 readers are new to you please read +the feature section of the SAX2 document. +

As a novice to the Qt XML classes it is advisable to have a look at the +tiny SAX2 parser walkthrough before +reading on. +

This walkthrough covers two topics: First of all it shows how to +set SAX2 features and secondly how to integrate the Qt XML functionality +into a Qt GUI application. +

The resulting application allows you to compare the output of the reader +depending on how the two features +http://xml.org/sax/features/namespace-prefixes +and http://xml.org/sax/features/namespaces are set. +To do this it shows tree views of the read XML file +listing the qualified names of elements and attributes and the respective +namespace URIs. +

Setting features

+

+ +

Let's begin with the main program of the application. First the boring +part: we include all the classes we need: +

    #include "structureparser.h"
+    #include <qapplication.h>
+    #include <qfile.h>
+    #include <qxml.h>
+    #include <qlistview.h>
+    #include <qgrid.h>
+    #include <qmainwindow.h>
+    #include <qlabel.h>
+
+

structureparser.h contains the API of +the XML parser that we implement in structureparser.cpp. +

    int main( int argc, char **argv )
+    {
+        QApplication app( argc, argv );
+
+

As usual we then create a Qt application object and hand command line arguments +over to it. +

        QFile xmlFile( argc == 2 ? argv[1] : "fnord.xml" );
+
+

If the user runs the program with one filename as +an argument we process this file, otherwise we use the fnord.xml file from +the example directory for demonstration purposes. +

        QXmlInputSource source( &xmlFile );
+
+

We use xmlFile as the XML Input Source... +

        QXmlSimpleReader reader;
+
+

... and instantiate a reader object. Later we will manipulate its features +and thus influence how the XML data are read. +

        QGrid * container = new QGrid( 3 );
+
+

Now let's think about presenting the output: As described in the +Qt SAX2 documentation +there are three valid combinations of http://xml.org/sax/features/namespace-prefixes +and http://xml.org/sax/features/namespaces: TRUE/TRUE, TRUE/FALSE and +FALSE/TRUE. To show the relevant output side by side of each other +and mark them with three labels makes up for a grid layout consisting +of three columns (and thus two lines). +

        QListView * nameSpace = new QListView( container, "table_namespace" );
+
+

The most natural way of presenting XML elements is in a tree. +Thus we use a listview. Its name nameSpace indicates that this +one will be used to present the combination of http://xml.org/sax/features/namespaces being TRUE and +http://xml.org/sax/features/namespace-prefixes +being FALSE -- the default configuration of a QXmlSimpleReader. +

Being the first grid entry the nameSpace listview will +appear in the upper left corner of the virtual grid. +

        StructureParser * handler = new StructureParser( nameSpace );
+
+

Then we create a handler that deals with the XML data read by the reader. +As the provided handler class QXmlDefaultHandler simply does nothing +with the data from the reader, +we can't use it right away. Instead we have to subclass our +own StructureParser from it. +

        reader.setContentHandler( handler );
+
+

The handler serves as content handler for the reader. Note that +for simplicity reasons we don't register e.g. an error handler. Thus +our program will not complain about for example missing closing tags +in the parsed XML document. +

        reader.parse( source );
+
+

Finally we parse the document with the reader's default feature settings. +

        QListView * namespacePrefix = new QListView( container,
+                                                     "table_namespace_prefix" );
+
+

Now we prepare for the parsing of the same XML input source with +different reader settings. The output will be presented in +a second QListView, namespacePrefix. As it is the second +member of the container grid it will appear in the middle of +the upper grid row. +

        handler->setListView( namespacePrefix );
+
+

Then we ask the handler to present the data in the namespacePrefix +listview. +

        reader.setFeature( "http://xml.org/sax/features/namespace-prefixes",
+                           TRUE );
+
+

Now we modify the behaviour of the reader and change +http://xml.org/sax/features/namespace-prefixes from the default FALSE +to TRUE. The http://xml.org/sax/features/namespaces feature has +still its default setting TRUE. +

        source.reset();
+
+

We have to reset the input source to make the new parsing start from the +beginning of the document again. +

        reader.parse( source );
+
+

Finally we parse the XML file a second time with the changed reader +settings (TRUE/TRUE). +

        QListView * prefix = new QListView( container, "table_prefix");
+        handler->setListView( prefix );
+        reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
+        source.reset();
+        reader.parse( source );
+
+

Next we prepare and use the upper right listview to show the reader results +with the feature setting http://xml.org/sax/features/namespaces +FALSE and http://xml.org/sax/features/namespace-prefixes TRUE. +

        // namespace label
+        (void) new QLabel(
+                 "Default:\n"
+                 "http://xml.org/sax/features/namespaces: TRUE\n"
+                 "http://xml.org/sax/features/namespace-prefixes: FALSE\n",
+                 container );
+
+        // namespace prefix label
+        (void) new QLabel(
+                 "\n"
+                 "http://xml.org/sax/features/namespaces: TRUE\n"
+                 "http://xml.org/sax/features/namespace-prefixes: TRUE\n",
+                 container );
+
+        // prefix label
+        (void) new QLabel(
+                 "\n"
+                 "http://xml.org/sax/features/namespaces: FALSE\n"
+                 "http://xml.org/sax/features/namespace-prefixes: TRUE\n",
+                 container );
+
+

The second row of the container grid is filled with three labels +denoting the reader settings that belong to the above listview. +

        app.setMainWidget( container );
+        container->show();
+        return app.exec();
+    }
+
+

Same procedure as with every Qt GUI program: the grid serves as the +main widget of our application and is shown. After that we enter +the GUI's event loop. +

The handler API

+

Let's have a brief look at the API of our handler class +StructureParser: +

+ +

    #include <qxml.h>
+    #include <qptrstack.h>
+
+    class QListView;
+    class QListViewItem;
+    class QString;
+
+

    class StructureParser: public QXmlDefaultHandler
+    {
+
+

We derive it from the QXmlDefaultHandler class that +implements a handler that simply does nothing. +

    public:
+        StructureParser( QListView * );
+
+

This makes it easy for us to implement only the functionality +we in fact need. In our case this is the constructor that +takes a QListView as an argument, +

        bool startElement( const QString&, const QString&, const QString& ,
+                           const QXmlAttributes& );
+
+

the function to execute at the occurrence of element start tags +(inherited from QXmlContentHandler), and +

        bool endElement( const QString&, const QString&, const QString& );
+
+

the code to run when an end tag occurs. +

All we have to implement so far is content handling. +

        void setListView( QListView * );
+
+

In addition we have a function that selects a listview +for the output. +

    private:
+        QPtrStack<QListViewItem> stack;
+
+

Keep in mind that we write a SAX2 parser that doesn't +have an object model to keep all elements and attributes +in memory. To display the elements and attributes in a tree like +structure we must however keep track of all elements +that haven't been closed yet. +

To do this we use a LIFO stack +of QListItems. An element will be added to the stack when +its start tag appears and removed +as soon as its end tag is parsed. +

        QListView * table;
+    };
+
+

Apart from this we define a member variable that contains +the currently used listview. +

The handler itself

+

Now that we defined the API we have to implement the +relevant functions. +

+ +

    #include "structureparser.h"
+
+    #include <qstring.h>
+    #include <qlistview.h>
+
+

    StructureParser::StructureParser( QListView * t )
+                    : QXmlDefaultHandler()
+    {
+
+

First we have the constructor that takes a listview pointer as +its argument. +

        setListView( t );
+    }
+
+

All we have to do here is to prepare the argument QListView +before usage. This we do with the setListView() function. +

+

    void StructureParser::setListView( QListView * t )
+    {
+        table = t;
+
+

First we store the argument away. +

        table->setSorting( -1 );
+
+

We want the elements to be listed as they appear in the +document -- and not for example sorted alphabetically. That's +why we switch off sorting at all. +

        table->addColumn( "Qualified name" );
+        table->addColumn( "Namespace" );
+    }
+
+

The listview now consists of two columns: one for the +element's or attribute's qualified names and one for +their namespace URIs. Columns are added from left to right +and with the title as an argument. +

Now let's deal with XML content handling. +

    bool StructureParser::startElement( const QString& namespaceURI,
+                                        const QString& ,
+                                        const QString& qName,
+                                        const QXmlAttributes& attributes)
+    {
+
+

When we come across the start tag of an element the handler does +the real work. Although startElement is called with four +arguments we keep track of only three: the namespace URI +of the element, its qualified name and its attributes. +If an element has no namespace assigned or if the feature +settings of the reader don't provide the handler with +namespace URIs at all namespaceURI contains an empty +string. +

Note that we don't assign a variable to the second argument -- +we're simply not interested in the local name of the element. +

        QListViewItem * element;
+
+

Whenever an element occurs we want to show it in the listview. +Therefore we define a QListViewItem variable. +

        if ( ! stack.isEmpty() ){
+            QListViewItem *lastChild = stack.top()->firstChild();
+
+

As long as the element stack isn't empty the current element +is a child of the topmost (last unclosed) element on the stack. Thus we +create a new QListViewItem as a child of QPtrStack::stack.top() with +the new element's qualified name in the first column and the according +namespace URI (or nothing) in the second one. +

The QListViewItem is usally inserted as the first child. This means that we +would get the elements in reverse order. So we first search for the last +child of the QPtrStack::stack.top() element and insert it after this +element. +

In a valid XML document this applies to all elements except +the document root. +

            if ( lastChild ) {
+                while ( lastChild->nextSibling() )
+                    lastChild = lastChild->nextSibling();
+            }
+            element = new QListViewItem( stack.top(), lastChild, qName, namespaceURI );
+        } else {
+            element = new QListViewItem( table, qName, namespaceURI );
+        }
+
+

The root element we have to handle separately because it is +the first element to go onto the QListViewItem stack. +Its listview item is therefore a direct child of the +table listview itself. +

        stack.push( element );
+
+

Now we put the element's listview item on top of the stack. +

        element->setOpen( TRUE );
+
+

By default a QListView presents all of its nodes closed. +The user may then click on the + icon to see the child +entries. +

We however want to see the entire element tree +at once when we run the program. +Therefore we open each listview item manually. +

        if ( attributes.length() > 0 ) {
+
+

What do we do if an element has attributes? +

            for ( int i = 0 ; i < attributes.length(); i++ ) {
+                new QListViewItem( element, attributes.qName(i), attributes.uri(i) );
+            }
+        }
+
+

For each of them we create a new listview item to present the attribute's +qualified name and the relevant namespace URI (or nothing). +Obviously attribute is a child of +the current element. +

        return TRUE;
+    }
+
+

To prevent the reader from throwing an error we have to +return TRUE when we successfully dealt with an +element's start tag. +

    bool StructureParser::endElement( const QString&, const QString&,
+                                      const QString& )
+    {
+        stack.pop();
+
+

Whenever we come across an element's closing tag we +have to remove its listview item from the stack as +it can't have children any longer. +

        return TRUE;
+    }
+
+

And so we're done. +

See also Step-by-step Examples. + + +


+ +
Copyright © 2007 +TrolltechTrademarks +
Qt 3.3.8
+
+ -- cgit v1.2.3