Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-dom-1.cpp
#include "p6loader.h"
#include "p6domxml.h"
#include "cconsolestream.h"
#include "cfilestream.h"
using namespace P6R;
using namespace P6EXAMPLES;
P6DECLARE_CID( p6DOMXML );
P6DECLARE_CID( p6XpathExpression );
P6DECLARE_IID( IFileStreamInit );
namespace {
P6R::P6ERR getFileStream(const P6R::P6WCHAR *pszFilepath,P6R::p6IDataStream **ppIface)
{
P6ERR err;
if(P6SUCCEEDED(err = CFileStream::createInstance(NULL,VALIDATECOMPTR(IFileStreamInit,cpFileInit)))) {
if(P6SUCCEEDED(err = cpFileInit->initialize(pszFilepath))) {
err = cpFileInit.queryInterface(IID_p6IDataStream,ppIface);
}
}
return err;
}
P6R::P6ERR getDOMXML(P6R::p6IDataStream *pOutStream,P6R::p6IDOMXML **ppIface)
{
P6ERR err;
if(P6SUCCEEDED(err = p6CreateInstance(NULL,CID_p6DOMXML,VALIDATECOMPTR(p6IDOMXML,cpDOM)))) {
if(P6SUCCEEDED(err = cpDOM->initialize(P6DOMXML_NOFLAGS,pOutStream))) {
//
// Using detach() saves a reference counting operation
// and gives ownership to *ppIface.
//
cpDOM.detach(ppIface);
}
}
return err;
}
P6R::P6ERR getXPathEngine(P6R::p6IDataStream *pOutStream,P6R::p6IXpathExpression **ppIface)
{
P6ERR err;
if(P6SUCCEEDED(err = p6CreateInstance(NULL,CID_p6XpathExpression,VALIDATECOMPTR(p6IXpathExpression,cpXPath)))) {
if(P6SUCCEEDED(err = cpXPath->initialize(P6XPATH_NOFLAGS,pOutStream))) {
//
// Using detach() saves a reference counting operation
// and gives ownership to *ppIface.
//
cpXPath.detach(ppIface);
}
}
return err;
}
{
P6ERR err;
P6UINT32 length = 0;
P6ARG args[2];
// [C] Use XPath to find nodes in the DOM tree
// -> the result of an XPath expression is a node set enumerator
if (P6SUCCEEDED( err = pXPath->compileExpression( P6CTEXT("book/title"), 10, NULL )))
{
err = pXPath->eval( pDOM, NULL, NULL, &result );
if (P6SUCCEEDED( err ) && P6XPATH_TYPE_SET == result.type)
{
P6UINT32 number = 0;
//
// Normally, we would make this a loop since we
// would not know there was only one node of this type
//
err = result.pNodeSet->last( &number );
if (1 != number) {
P6AI_UINT32(&args[0],number);
pConsole->writeStdout("node set size returned by last() function is %1$\n",&args[0],1,NULL);
return eFail;
}
//
// Start at the beginning of the result set
// and retrieve the next() node.
//
err = result.pNodeSet->reset();
//
// Normall next() would be called in a while loop to iterate the
// entire result set. Notice that we are using p6ComPtr's
// addressofWithRelease() method on cpNode. This automatically releases
// the any previous interface before storing the new pointer value,
// so there is no need to call release() manually on cpNode for each
// iteration of next(). Less code to write, less chance of an interface leak.
//
if (P6SUCCEEDED( err = result.pNodeSet->next( cpNode.addressofWithRelease() )))
{
const P6WCHAR *pString = NULL;
P6INT32 retval = 0;
if (P6SUCCEEDED( err = cpNode->getName( &pString, &length )))
{
retval = -1;
err = pStr->wstrncmp( P6CTEXT("title"), pString, 5, &retval );
if (0 != retval) {
P6BWCSTR bStr;
bStr.pString = pString;
bStr.length = length;
P6AI_BWCSTR(&args[0],&bStr);
pConsole->writeStdout( "node name - expected [ title ] but got [ %1$ ]\n", &args[0],2,NULL );
return err;
}
}
// -> walk the DOM tree directly from any node in the tree
err = cpNode->resetChild();
//
// Again, we are using addressofWithRelease() to prevent
// interface leaks.
//
err = cpNode->nextChild( cpChild.addressofWithRelease() );
if (P6SUCCEEDED( err = cpChild->getValue( &pString, &length )))
{
retval = -1;
err = pStr->wstrncmp( P6CTEXT("P6R book of development"), pString, 23, &retval );
if (0 != retval) {
P6BWCSTR bStr;
bStr.pString = pString;
bStr.length = length;
P6AI_BWCSTR(&args[0],&bStr);
pConsole->writeStdout( "child node value - Expected [ P6R book of development ] but got [ %1$ ]\n", &args[0],1,NULL );
return eFail;
}
}
}
//
// You MUST always release P6XPATH_RESULT's
// p6IDOMNodeset interface when you are done.
//
if (NULL != result.pNodeSet)
{
result.pNodeSet->release();
result.pNodeSet = NULL;
}
}
}
return err;
}
P6R::P6ERR getNodeSequenceExample(P6R::p6IConsole *pConsole,P6R::p6ISafeString *pStr,P6R::p6IDOMXML *pDOM,P6R::p6IXpathExpression *pXPath)
{
P6ERR err;
P6UINT32 length = 0;
P6ARG args[2];
// Get a sequence of nodes, XPath 2.0 is very powerful and easy to use.
// These two XPath expressions will provide the same results in this
// example:
//
// err = pXPath->compileExpression( P6CTEXT("//para"), 6, NULL );
// err = pXPath->compileExpression( P6CTEXT("/book/chapter//para"), 19, NULL );
//
err = pXPath->compileExpression( P6CTEXT("/book/chapter//para"), 19, NULL );
if (P6SUCCEEDED( err = pXPath->eval( pDOM, NULL, NULL, &result )))
{
P6UINT32 number = 0;
P6UINT32 count = 0;
if (result.type != P6XPATH_TYPE_SET) {
P6AI_INT32(&args[1],(P6INT32)result.type);
pConsole->writeStdout( "Expected %1$ but got %2$\n", &args[0], 2, NULL );
return eFail;
}
number = 0;
err = result.pNodeSet->last( &number );
if (number != 5) {
P6AI_UINT32(&args[0],number);
pConsole->writeStdout("Expected 5 but got %1$\n", &args[0], 1, NULL );
return eFail;
}
count = 0;
while( P6SUCCEEDED( err ) && P6SUCCEEDED( err = result.pNodeSet->next( cpChild.addressofWithRelease() )))
{
P6WCHAR testStr[200] = {0};
P6INT32 retval = 0;
length = P6CHARCNT(testStr);
retval = -1;
if (P6SUCCEEDED( err = cpChild->toString( testStr, &length )))
{
switch( count ) {
case 0: err = pStr->wstrncmp( P6CTEXT("1 2 3\xE9\xE9 4 5 6"), testStr, 13, &retval ); break;
case 1: err = pStr->wstrncmp( P6CTEXT("7 8 9 10 11"), testStr, 11, &retval ); break;
case 2: err = pStr->wstrncmp( P6CTEXT("a b c d e f g h"), testStr, 15, &retval ); break;
case 3: err = pStr->wstrncmp( P6CTEXT("0 0 0 0 0 0 "), testStr, 12, &retval ); break;
case 4: err = pStr->wstrncmp( P6CTEXT("i j k l m n o p"), testStr, 15, &retval ); break;
}
if (retval != 0) {
P6AI_UINT32(&args[0],count);
pConsole->writeStdout("Case %1$ failed. Expected string does not match\n", &args[0], 1, NULL );
return eFail;
}
count++;
}
}
// -> we should hit the end of the nodeset enumerator
if (eEndOfRecord == err) err = eOk;
// NodeSets are feature rich and allow several operations such as sorting:
// -> now reverse order the elements in the set, sorting does an automatic reset()
// -> and validate that the order of the nodes has been reversed
{
P6WCHAR testStr[200] = {0};
err = cpSort->reverseOrder();
if (P6SUCCEEDED(err = result.pNodeSet->next( cpChild.addressofWithRelease() )))
{
length = P6CHARCNT(testStr);
if (P6SUCCEEDED( err = cpChild->toString( testStr, &length )))
{
P6INT32 retval = -1;
err = pStr->wstrncmp( P6CTEXT("i j k l m n o p"), testStr, 15, &retval );
if (retval != 0) {
P6BWSTR bStr;
bStr.pString = &testStr[0];
bStr.length = length;
P6AI_BWSTR(&args[0],&bStr);
pConsole->writeStdout("sorting - expected [ i j k l m n o p ] but got %1$\n", &args[0], 1, NULL );
return eFail;
}
}
}
}
if (NULL != result.pNodeSet)
{
result.pNodeSet->release();
result.pNodeSet = NULL;
}
}
return err;
}
// Notice how we use the XPath components directly. XPath is also used in our XSLT processor.
//
P6R::P6ERR runDOM( P6R::p6IConsole *pConsole, p6IDataStream *pStreamDebug )
{
P6ERR err = eOk;
p6ComPtr<p6ISafeString> cpStr; // loaders string interface
p6ComPtr<p6IDataStream> cpOutStream; // Filestream used for output by the DOM
p6ComPtr<p6IDataStream> cpStream; // This is used to hold the DOM's inpout stream
p6ComPtr<p6IDOMXML> cpDOM; // The DOM interface
p6ComPtr<p6IXpathExpression> cpXPath; // The XPath interface
P6UINT32 bufSize = 0;
P6ARG args[2];
const P6CHAR* pXML =
"<?xml version='1.0' encoding='UTF-8' ?>" \
"<book>" \
"<title>P6R book of development</title>" \
"<TOC>" \
" <chapter>One</chapter>" \
" <chapter>Two</chapter>" \
"</TOC>" \
"<chapter>" \
" <section>" \
" <footnote>footnote 1.1.0</footnote>" \
" <para>1 2 3&#233;&#xE9; 4 5 6</para>" \
" <para>7 8 9 10 11</para>" \
" <testcase> abc <![CDATA[ ab&#233;c &gt; 123 ]]> hey there </testcase>" \
" </section>" \
" <section>" \
" <para>a b c d e f g h</para>" \
" <footnote>footnote 1.2.0</footnote>" \
" <footnote id='ab'>footnote 1.2.1</footnote>" \
" </section>" \
"</chapter>" \
"<chapter>" \
" <para>0 0 0 0 0 0 </para>" \
" <section>" \
" <para>i j k l m n o p</para>" \
" <footnote>footnote 1.3.0</footnote>" \
" </section>" \
"</chapter>" \
"<index>" \
" <item>Index 1.0</item>" \
" <item>Index 2.0</item>" \
" <abc>" \
" <item>Index 2.1</item>" \
" <item>Index 2.2</item>" \
" </abc>" \
" <item>Index 3.0</item>" \
"</index>" \
"<appendixA>" \
" <item>appendix A 1.0</item>" \
" <item>appendix A 2.0</item>" \
"</appendixA>" \
"<appendixB>" \
" <item>appendix B 1.0</item>" \
" <item>appendix B 2.0</item>" \
" <item>appendix B 3.0</item>" \
"</appendixB>" \
"<appendixC>" \
" <item>appendix C 1.0</item>" \
" <abc>" \
" <item>appendix C 2.0</item>" \
" </abc>" \
"</appendixC>" \
"</book>";
//
// [A] Create & initialize the components we need
//
// - The loader string interface
// - An output file stream
// - The DOM interface
// - The XPath engine interface
//
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, cpStr )))) return err;
if (P6FAILED( err = getDOMXML(pStreamDebug,cpDOM.addressof()))) return err;
if (P6FAILED( err = getXPathEngine(NULL,cpXPath.addressof()))) return err;
//
// [B] Stream the XML document into the DOM parser
// (can be done with one or many calls to processStream(),
// see SAX2-2 example).
//
// 1) Retrieve the DOM's p6IDataStream interface
// 2) Tell the DOM we will be sending data via beginStream()
// 3) Pass the data to the DOM with processStream()
// 4) Tell the DOM we are done sending data with endStream()
//
err = cpDOM->parse( cpStream.addressof() );
if (P6SUCCEEDED( err )) err = cpStream->beginStream();
cpStr->strlen( pXML, 100000, &bufSize );
if (P6SUCCEEDED( err )) err = cpStream->processStream( pXML, bufSize );
if (P6SUCCEEDED( err ))err = cpStream->endStream();
//
// [C] Use XPath to find nodes in the DOM tree
//
err = findNodesExample(pConsole,cpStr,cpDOM,cpXPath);
//
// Get a sequence of nodes, XPath 2.0 is very powerful and easy to use
//
err = getNodeSequenceExample(pConsole,cpStr,cpDOM,cpXPath);
if (P6FAILED( err )) {
P6AI_ERR(&args[0],err);
pConsole->writeStdout("ERROR: Example failed with [ %1$ ]\n", &args[0], 1, NULL );
}
return err;
}
} // namespace
int main(int argc,char *argv[])
{
P6ERR err = eOk;
p6ComPtr<p6IDataStream> cpDebugStream;
P6ARG args[1];
if(P6SUCCEEDED(err = CConsoleStream::createInstance(NULL,VALIDATECOMPTR(p6IDataStream,cpDebugStream)))) {
if(P6SUCCEEDED(err = p6InitializeLoader(cpDebugStream,9,P6SCLF_NOFLAGS))) {
err = runDOM( cpConsole, cpDebugStream );
P6AI_ERR(&args[0],err);
cpConsole->writeStdout("runDOM result: [ %1$ ]\n",&args[0],1,NULL);
//
// Make sure to release the console interface
// before calling p6CleanupLoader() !! In this
// instance we don't let the smart pointer
// handle the cleanup.
//
cpConsole = NULL;
}
else printf("ERROR: Failed to retrieve console interface [ %x ]\n", err );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}