|
||
This section explains how to write applications which respond to the output of the Parser framework by implementing the content handler functionality of the parser framework.
You are writing a complex application with numerous settings which the
user customises from a tab window. This means you need the application to save
these settings so that it has them available when it is restarted after being
shut down. You want a generic solution which will work on a wide range of
platforms, for a wide range of applications and in a wide range of
environments. A good way to do this while ensuring cross-platform compatibility
is to save the settings in an XML file which the application parses on
start-up. You provide the functionality with which your application responds to
the XML statements by implementing the MContentHandler
interface. As the parser detects tags and their content, it calls the
associated content handler functions to respond with the required behaviour.
The callback functions which an implementation of
MContentHandler
must provide are listed below - these
correspond to functions defined in the ContentHandler interface of the
SAX specification. The last
parameter of each function is an error code. If no error has taken place then
the null error code KErrNone
is returned by the framework:
other error codes are defined in the file XMLFrameworkErrors.h
.
MContentHandler callback | SAX specification function |
---|---|
|
startDocument() endDocument() startElement() endElement() characters() startPrefixMapping() endPrefixMapping() ignorableWhitespace() skippedEntity() processingInstruction()
|
You need to know the following classes which are parameters of the functions you have to implement.
A class containing the character set the document uses.
A class consisting of an array of RAttribute
objects: these hold the name (as an RTagInfo
object),
value and type of each attribute of the element.
A class containing information about an XML tag: its namespace URI, its namespace prefix and its local name.
class CMyContentHandler : public CBase, public MContentHandler
{
public:
// A callback to indicate the start of the document.
void CXmlExample::OnStartDocumentL(const RDocumentParameters&, TInt)
{
iConsole->Printf(KOnStartDoc);
iConsole->Printf(KPressAKey);
iConsole->Getch();
iNumElements = 0;
iNumSkippedEntities = 0;
iNumPrefixMappings = 0;
iNumPrefixUnmappings = 0;
}
// A callback to indicate an element has been parsed.
void CXmlExample::OnStartElementL(const RTagInfo&, const RAttributeArray&, TInt)
{
iConsole->Printf(KOnStartEle);
if(iLeaveOnStartElement)
if(iNumElements++ == 0)
{
iConsole->Printf(KOnStartErr, KExpectedLeaveCode);
User::After(1);
User::Leave(KExpectedLeaveCode);
}
iNumElements++;
}
// implementations of the other callbacks
// ...
}
To have your content handler object called from the XML framework, you instantiate it in the client application code and pass it to the constructor method of a parser object.
...
CMyContentHandler* ch = CMyContentHandler::NewL();
CParser* parser = CParser::NewLC(KXmlMimeType,ch);
parser->ParseL(myXMLdata); // this will result in callbacks to ch
...
The XML framework contains several different parsers, called parser
plugins. If you are writing an application using the framework you do not need
to specify a particular parser as the framework will select one for you using
data which you supply. There are two ways of doing this: in both cases you need
to know the mime type of the file you want to parse. If that is all you know
about your files, you call the constructor method of a
CParser
object with the mime type as a parameter. However,
you may want to specify a particular parser variant (usually identified by the
name of its supplier). This involves a two step process. First you construct a
CMatchData
object and pass it the data about the files and
parser variant: then you pass the CMatchData
object to the
constructor method of a CParser
object as shown in the
following code.
// 1. Create CMatchData object
CMatchData *matchData = CMatchData::NewLC();
// 2. Set Type
matchData->SetMimeTypeL(_LIT(“text/xml”));
// 3. Set variant string
matchData->SetVariantL(_LIT(“LicenseeX”));
// 4. Call creation method (assumption that content handler was created previously)
CParser* parser = CParser::NewLC(*matchData, *contentHandler);
// 5. Use the parser
// ….
// 6. Destroy the parser and CMatchData object
CleanupStack::PopAndDestroy(2, matchData);
To parse a document you must write code which somewhere includes calls to the parse methods of a CParser object. It is often simplest to call them indirectly from the global parse methods provided with the framework.
The global parse methods are Xml::ParseL(Xml::CParser& aParser, RFs& aFs, const TDesC& aFilename)
and
Xml::ParseL(Xml::CParser& aParser, RFile& aFile)
A: Xml::ParseL(Xml::CParser& aParser, const TDesC8& aContent)
B: Xml::ParseL(Xml::CParser& aParser, RFs& aFs, const TDesC& aFilename)
C: Xml::ParseL(Xml::CParser& aParser, RFile& aFile)
The global parse methods call the CParser object parse methods which are
ParseBeginL()
ParseBeginL(const TDesC8& aDocumentMimeType)
ParseL()
ParseEndL()
Which methods you call depends on the nature of the input to the parser. Input may consist of one file or several files and it may be received in one piece or asynchronously in chunks. The files may be of the same or different types and asynchronous input may or may not be buffered before parsing.
Global parse method A makes a single call to each of the CParser parse methods. This is the simplest approach but it only works when you are parsing a single file which has previously been buffered.
Global parse methods B and C have the same functionality: the only difference is how they identify the input file (by name or from an RFile object). They call CParser::ParseL() in a loop and then call CParser::ParseEnd(). The use of a loop means that input does not have to be buffered, but only one file can be parsed by this technique.
This is because of the functionality of the CParser parse methods.
ParseBeginL()
resets the CParser object to the
file type it was created to parse.
ParseBeginL(const TDesC8& aDocumentMimeType)
resets the CParser object to the passed in file type.
ParseL(const TDesC8& aFragment)
does the
actual parsing. The argument may refer to a fragment, not a complete document,
so that a call to this function may return before the entire document has been
parsed. If a document is expected to arrive in asynchronous chunks, the
programmer must either buffer the input before a single call to this function
or else call this function within a loop until all input has been received.
ParseEndL()
performs cleanup tasks after a
document has been parsed.
To parse several unbuffered documents of the same type, you need multiple calls to global parse method B or C. To parse several buffered documents possibly of different types, you need multiple calls to global parse method A. Other eventualities require individual calls to the parse methods of CParser.
Sometimes you want to do more with a document than just parse it: for instance you might want to validate the parsed text against a specification, and then autocorrect spelling mistakes in the validated text.
In such a case you write three applications, a parser, a validator and an
autocorrector. They should implement the MContentProcessor
interface. Writing a content processor is almost exactly the same as writing a
content handler as explained inHow To Parse an XML Document: MContentProcessor
is a small
extension of MContentHandler
and you write each
application by implementing the callback functions of
MContentHandler
. The only difference is that
MContentProcessor
has a mechanism for directing output, so
that the output of your parser is the input to your validator and the output of
your validator is the input of your autocorrector.
You direct output of a content processor by implementing its
SetContentSink()
function so that your parser outputs to
your validator and your validator outputs to your autocorrector. A sequence of
several applications linked in this way is called a plugin chain.
To perform the actual parsing, you use a CParser object as explained in
Choosing a Parser Plugin. To
ensure that parsing is followed by validation and autocorrection, you associate
the CParser object with the plugin chain. You do this by calling the
SetProcessorChainL()
function of the CParser object with a
list of the items in the plugin chain as a parameter.