Rapidxml next_sibling not return the next sibling - c++

I want to select the "actual" xml node by attribute.
When I try to iterate trough the nodes, it only return the first one,
but strangely, when I ask the last node, it will return it without any problem. Also, if I search it with name like "actualroot->first_node("roodnode1")" it will found it.
edit:
I checked it just in case and the "actualroot" have parent
Rapidxml version: 1.13
(I tried different version too(1.1), and it had the same result)
The saved xml:
<rootnode Game="gamename">
<rootnode0 table="a" cluster="b" item="c"/>
<rootnode1 table="atest" cluster="btest" item="ctest"/>
<rootnode2 table="1" cluster="2" item="3"/>
</rootnode>
The simplified verion of the code I use:
#include "rapidxml.hpp"
#include "rapidxml_print.hpp"
using namespace std;
using namespace rapidxml;
bool listloaded;
bool actualcontainerloaded;
int rootcounter;
xml_document<> *actuallist;
xml_node<>* actualroot;
xml_node<>* actualcontainer;
std::unordered_map<std::string, xml_document<>*> xmllistmap;
main()
{
listloaded = false;
actualcontainerloaded = false;
rootcounter = 0;
create("tastx")
addcontainer("a","b","c")
addcontainer("atest","btest","ctest")
addcontainer("1","2","3")
setactualcontainer("atest","btest","ctest")
}
bool create(string name)
{
if (isalreadyexist(name)) return false;
//RPXdata oneRPX;
xml_document<> *newlist = new xml_document<>();
xml_node<>* root = newlist->allocate_node(node_element, newlist->allocate_string("rootnode"));
root->append_attribute(newlist->allocate_attribute("Game", newlist->allocate_string("gamename")));
newlist->append_node(root);
newlist->name(name.c_str());
xmllistmap[name] = newlist;
actuallist = newlist;
actualroot = newlist->first_node();
listloaded = true;
return true;
}
bool addcontainer(string table, string cluster, string item)
{
if (listloaded)
{
for (xml_node<> *child = actualroot->first_node(); child; child = actualroot->next_sibling())
{
if (child->first_attribute("table") == NULL) continue;
string tableattr = child->first_attribute("table")->value();
if (tableattr != table) continue;
if (child->first_attribute("cluster") == NULL) continue;
string clusterattr = child->first_attribute("cluster")->value();
if (clusterattr != cluster) continue;
if (child->first_attribute("item") == NULL) continue;
string itemattr = child->first_attribute("item")->value();
if (itemattr == item) continue;
return false;
}
string rootname = "rootnode"+functions.converttostring(rootcounter);
xml_node<>* root = actuallist->allocate_node(node_element, actuallist->allocate_string(rootname.c_str()));
root->append_attribute(actuallist->allocate_attribute("table", actuallist->allocate_string(table.c_str())));
root->append_attribute(actuallist->allocate_attribute("cluster", actuallist->allocate_string(cluster.c_str())));
root->append_attribute(actuallist->allocate_attribute("item", actuallist->allocate_string(item.c_str())));
actualroot->append_node(root);
functions.log("container added:" + table);
rootcounter++;
}
return false;
}
bool setactualcontainer(string table, string cluster, string item)
{
if (listloaded)
{
for (xml_node<> *child = actualroot->first_node(); child; child = actualroot->next_sibling())
{
xml_node<> *lastchild = actualroot->last_node();
string lcname = lastchild->name();
functions.log("last name: " + lcname);
string cname = child->name();
functions.log("cname: " + cname);
functions.log("for step called");
if (child->first_attribute("table") == NULL) continue;
string tableattr = child->first_attribute("table")->value();
string clusterattr = child->first_attribute("cluster")->value();
string itemattr = child->first_attribute("item")->value();
functions.log("actual tableattr:" + tableattr);
if (tableattr == table && clusterattr == cluster && itemattr == item)
{
functions.log("actual continer settd:"+ table);
actualcontainer = child;
actualcontainerloaded = true;
return true;
}
}
}
return false;
}
The result:
23:11:17: container added:a
23:11:17: container added:atest
23:11:17: container added:1
23:11:17: last name: rootnode2
23:11:17: cname: rootnode0
23:11:17: for step called
23:11:17: actual tableattr:a

Just a simple inattention:
The for loop
for (xml_node<> *child = actualroot->first_node(); child; child = actualroot->next_sibling())
actually should looks like this:
for (xml_node<> *child = actualroot->first_node(); child; child = child->next_sibling())

Related

How to read in a .cfg.txt file

I've been trying to read this file for some time now and tried about everything I could think of. I placed the file in my Products folder and In my resource folder and included (ResourcePath + "File.cfg.txt") and neither worked. I'd appreciate it if someone could tell me what I'm missing and where to put this file to read it in. Again I'm using Xcode and the SFML library with it.
Keys.cfg.txt
Window_close 0:0
Fullscreen_toggle 5:89
Move 9:0 24:38
/////////////////////////////////////////
.CPP
#include "EventManager.h"
using namespace std;
EventManager::EventManager(): m_hasFocus(true){ LoadBindings(); }
EventManager::~EventManager(){
for (auto &itr : m_bindings){
delete itr.second;
itr.second = nullptr;
}
}
bool EventManager::AddBinding(Binding *l_binding){
if (m_bindings.find(l_binding->m_name) != m_bindings.end())
return false;
return m_bindings.emplace(l_binding->m_name, l_binding).second;
}
bool EventManager::RemoveBinding(std::string l_name){
auto itr = m_bindings.find(l_name);
if (itr == m_bindings.end()){ return false; }
delete itr->second;
m_bindings.erase(itr);
return true;
}
void EventManager::SetFocus(const bool& l_focus){ m_hasFocus = l_focus; }
void EventManager::HandleEvent(sf::Event& l_event){
// Handling SFML events.
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
EventType sfmlEvent = (EventType)l_event.type;
if (e_itr.first != sfmlEvent){ continue; }
if (sfmlEvent == EventType::KeyDown || sfmlEvent == EventType::KeyUp){
if (e_itr.second.m_code == l_event.key.code){
// Matching event/keystroke.
// Increase count.
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else if (sfmlEvent == EventType::MButtonDown || sfmlEvent == EventType::MButtonUp){
if (e_itr.second.m_code == l_event.mouseButton.button){
// Matching event/keystroke.
// Increase count.
bind->m_details.m_mouse.x = l_event.mouseButton.x;
bind->m_details.m_mouse.y = l_event.mouseButton.y;
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else {
// No need for additional checking.
if (sfmlEvent == EventType::MouseWheel){
bind->m_details.m_mouseWheelDelta = l_event.mouseWheel.delta;
} else if (sfmlEvent == EventType::WindowResized){
bind->m_details.m_size.x = l_event.size.width;
bind->m_details.m_size.y = l_event.size.height;
} else if (sfmlEvent == EventType::TextEntered){
bind->m_details.m_textEntered = l_event.text.unicode;
}
++(bind->c);
}
}
}
}
void EventManager::Update(){
if (!m_hasFocus){ return; }
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
switch (e_itr.first){
case(EventType::Keyboard) :
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Mouse) :
if (sf::Mouse::isButtonPressed(sf::Mouse::Button(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Joystick) :
// Up for expansion.
break;
default:
break;
}
}
if (bind->m_events.size() == bind->c){
auto callItr = m_callbacks.find(bind->m_name);
if(callItr != m_callbacks.end()){
callItr->second(&bind->m_details);
}
}
bind->c = 0;
bind->m_details.Clear();
}
}
void EventManager::LoadBindings(){
std::string delimiter = ":";
std::ifstream bindings;
bindings.open("keys.cfg");
if (!bindings.is_open()){ std::cout << "! Failed loading keys.cfg." << std::endl; return; }
std::string line;
while (std::getline(bindings, line)){
std::stringstream keystream(line);
std::string callbackName;
keystream >> callbackName;
Binding* bind = new Binding(callbackName);
while (!keystream.eof()){
std::string keyval;
keystream >> keyval;
int start = 0;
int end = keyval.find(delimiter);
if (end == std::string::npos){ delete bind; bind = nullptr; break; }
EventType type = EventType(stoi(keyval.substr(start, end - start)));
int code = stoi(keyval.substr(end + delimiter.length(),
keyval.find(delimiter, end + delimiter.length())));
EventInfo eventInfo;
eventInfo.m_code = code;
bind->BindEvent(type, eventInfo);
}
if (!AddBinding(bind)){ delete bind; }
bind = nullptr;
}
bindings.close();
}
The problem is that you have to copy the respective files in the bundle, and that only objective-c can provide you the full name of the file on the destination device.
To overcome this, make a .mm-file and place a c++ trampoline function in there, which gives you the full path (see code below).
One pitfall can be that you have to make sure that the config- and text files like "keys.cfg" are actually copied into the bundle. Select the respective file in the project and open the property inspector; make sure that - the respective target in "Target Membership" is checked.
// File: myFileNameProvider.mm
#import <Foundation/Foundation.h>
#include <iostream>
std::string GetTextureFilename(const char *name)
{
NSString *nameAsNSString = [NSString stringWithUTF8String:name];
NSString *fullName = [[NSBundle mainBundle]
pathForResource:nameAsNSString ofType: nil];
if (fullName)
return std::string([fullName UTF8String]);
else
return "";
}
Then, in your CPP-code, declare the signature of std::string GetTextureFilename(const char *name), and before opening the file get the full path by calling it:
// MyCPPFile.cpp
#include <iostream>
#include <fstream>
// declaration:
std::string GetTextureFilename(const char *name);
void myC_Func {
std::string fullPath = GetTextureFilename("keys.cfg");
std::ifstream bindings;
bindings.open(fullPath.c_str());
if (!bindings.is_open()) {
std::cout << "! Failed loading keys.cfg." << std::endl;
}
...
}

Logic flaw in trie search

I'm currently working on a trie implementation for practice and have run into a mental roadbloack.
The issue is with my searching function. I am attempting to have my trie tree be able to retrieve a list of strings from a supplied prefix after they are loaded into the programs memory.
I also understand I could be using a queue/shouldnt use C functions in C++ ect.. This is just a 'rough draft' so to speak.
This is what I have so far:
bool SearchForStrings(vector<string> &output, string data)
{
Node *iter = GetLastNode("an");
Node *hold = iter;
stack<char> str;
while (hold->visited == false)
{
int index = GetNextChild(iter);
if (index > -1)
{
str.push(char('a' + index));
//current.push(iter);
iter = iter->next[index];
}
//We've hit a leaf so we want to unwind the stack and print the string
else if (index < 0 && IsLeaf(iter))
{
iter->visited = true;
string temp("");
stringstream ss;
while (str.size() > 0)
{
temp += str.top();
str.pop();
}
int i = 0;
for (std::string::reverse_iterator it = temp.rbegin(); it != temp.rend(); it++)
ss << *it;
//Store the string we have
output.push_back(data + ss.str());
//Move our iterator back to the root node
iter = hold;
}
//We know this isnt a leaf so we dont want to print out the stack
else
{
iter->visited = true;
iter = hold;
}
}
return (output.size() > 0);
}
int GetNextChild(Node *s)
{
for (int i = 0; i < 26; i++)
{
if (s->next[i] != nullptr && s->next[i]->visited == false)
return i;
}
return -1;
}
bool IsLeaf(Node *s)
{
for (int i = 0; i < 26; i++)
{
if (s->next[i] != nullptr)
return false;
}
return true;
}
struct Node{
int value;
Node *next[26];
bool visited;
};
The code is too long or i'd post it all, GetLastNode() retrieves the node at the end of the data passed in, so if the prefix was 'su' and the string was 'substring' the node would be pointing to the 'u' to use as an artificial root node
(might be completely wrong... just typed it here, no testing)
something like:
First of all, we need a way of indicating that a node represents an entry.
So let's have:
struct Node{
int value;
Node *next[26];
bool entry;
};
I've removed your visited flag because I don't have a use for it.
You should modify your insert/update/delete functions to support this flag. If the flag is true it means there's an actual entry up to that node.
Now we can modify the
bool isLeaf(Node *s) {
return s->entry;
}
Meaning that we consider a leaf when there's an entry... perhaps the name is wrong now, as the leaf might have childs ("y" node with "any" and "anywhere" is a leaf, but it has childs)
Now for the search:
First a public function that can be called.
bool searchForStrings(std::vector<string> &output, const std::string &key) {
// start the recursion
// theTrieRoot is the root node for the whole structure
return searchForString(theTrieRoot,output,key);
}
Then the internal function that will use for recursion.
bool searchForStrings(Node *node, std::vector<string> &output, const std::string &key) {
if(isLeaf(node->next[i])) {
// leaf node - add an empty string.
output.push_back(std::string());
}
if(key.empty()) {
// Key is empty, collect all child nodes.
for (int i = 0; i < 26; i++)
{
if (node->next[i] != nullptr) {
std::vector<std::string> partial;
searchForStrings(node->next[i],partial,key);
// so we got a list of the childs,
// add the key of this node to them.
for(auto s:partial) {
output.push_back(std::string('a'+i)+s)
}
}
} // end for
} // end if key.empty
else {
// key is not empty, try to get the node for the
// first character of the key.
int c=key[0]-'a';
if((c<0 || (c>26)) {
// first character was not a letter.
return false;
}
if(node->next[c]==nullptr) {
// no match (no node where we expect it)
return false;
}
// recurse into the node matching the key
std::vector<std::string> partial;
searchForStrings(node->next[c],partial,key.substr(1));
// add the key of this node to the result
for(auto s:partial) {
output.push_back(std::string(key[0])+s)
}
}
// provide a meaningful return value
if(output.empty()) {
return false;
} else {
return true;
}
}
And the execution for "an" search is.
Call searchForStrings(root,[],"an")
root is not leaf, key is not empty. Matched next node keyed by "a"
Call searchForStrings(node(a),[],"n")
node(a) is not leaf, key is not empty. Matched next node keyed by "n"
Call searchForStrings(node(n),[],"")
node(n) is not leaf, key is empty. Need to recurse on all not null childs:
Call searchForStrings(node(s),[],"")
node(s) is not leaf, key is empty, Need to recurse on all not null childs:
... eventually we will reach Node(r) which is a leaf node, so it will return an [""], going back it will get added ["r"] -> ["er"] -> ["wer"] -> ["swer"]
Call searchForStings(node(y),[],"")
node(y) is leaf (add "" to the output), key is empty,
recurse, we will get ["time"]
we will return ["","time"]
At this point we will add the "y" to get ["y","ytime"]
And here we will add the "n" to get ["nswer","ny","nytime"]
Adding the "a" to get ["answer","any","anytime"]
we're done

Dangling memory/ unallocated memory issue

I've this piece of code, which is called by a timer Update mechanism.
However, I notice, that the memory size of the application, while running, continuously increases by 4, indicating that there might be a rogue pointer, or some other issue.
void RtdbConnection::getItemList()
{
std::vector<CString> tagList = mItemList->getItems();
//CString str(_T("STD-DOL1"));
PwItemList* pil = mPwSrv->GetItemList();
CPwItem pw ;
for(auto it = tagList.begin(); it != tagList.end(); ++it)
{
pw = mPwSrv->GetItem(*it);
pil->AddItem(&(PwItem)pw);
}
pil->AddInfo(DB_DESC); //Description
pil->AddInfo(DB_QALRM); // Alarm Status
pil->AddInfo(DB_QUNAK); //UNACK status
pil->AddInfo(DB_AL_PRI); // Priority of the alarm tag
pil->ExecuteQuery();
int i = 0;
for (auto it = tagList.begin(); i < pil->GetInfoRetrievedCount() && it != tagList.end(); i+=4, it++)
{
//item = {0};
CString str(*it);
PwInfo info = pil->GetInfo(i);
CString p(info.szValue().c_str());
bool isAlarm = pil->GetInfo(i+1).bValue();
bool isAck = pil->GetInfo(i+2).bValue();
int priority = pil->GetInfo(i+3).iValue();
item = ItemInfo(str, p, isAlarm, isAck, priority);
//int r = sizeof(item);
mItemList->setInfo(str, item); // Set the details for the item of the List
}
delete pil;
pil = NULL;
}
I cannot seem to find a memory block requiring de-allocation here. Nor is there any allocation of memory when I step inside the following function :
mItemList->setInfo(str, item);
which is defined as :
void ItemList::setInfo(CString tagname, ItemInfo info)
{
int flag = 0;
COLORREF tempColour;
std::map<CString, ItemInfo>::iterator tempIterator;
if ( (tempIterator = mAlarmListMap.find(tagname)) !=mAlarmListMap.end() )
{
//remove the current iteminfo and insert new one
if(mAlarmListMap[tagname].getPriority() != info.getPriority() && (mAlarmListMap[tagname].getPriority()!=0))
{
mAlarmListMap[tagname].updatePriority(info.getPriority());
mAlarmListMap[tagname].mPrioChanged = TRUE;
}
else
{
mAlarmListMap[tagname].mPrioChanged = FALSE;
((mAlarmListMap[tagname].getPrevPriority() != 0)?(mAlarmListMap[tagname].ResetPrevPriority()):TRUE);
mAlarmListMap[tagname].setPriority(info.getPriority());
}
mAlarmListMap[tagname].setDescription(info.getDescription());
mAlarmListMap[tagname].setAlarm(info.getAlarmStat());
mAlarmListMap[tagname].setAlarmAck(info.getAckStat());
tempColour = mColourLogic->setUpdatedColour(mAlarmListMap[tagname].getAlarmStat(), mAlarmListMap[tagname].getAckStat(), flag);
mAlarmListMap[tagname].setColour(tempColour);
if(!(info.getAlarmStat() || info.getAckStat()))
{
flag = 1;
mAlarmListMap[tagname].mIsRTN = true;
mAlarmListMap[tagname].setDisplayCondition(false);
}
else
{
mAlarmListMap[tagname].setDisplayCondition(true);
}
//((mAlarmListMap[tagname].mIsRTN == true)?
}
else
{
tempIterator = mAlarmListMap.begin();
tempColour = mColourLogic->fillColourFirst(info.getAlarmStat(), info.getAckStat());
info.setColour(tempColour);
mAlarmListMap.insert(tempIterator, std::pair<CString,ItemInfo>(tagname,info));
}
}
I tried juggling with the allocations, but the increase is always a constant 4.
Could anyone kindly look and highlight where the issue could be?
Thanks a lot.

list node pointer 'not declared in this scope'?

I've searched high and low trying to debug this, but so far I can't figure it out.
I've got a class declaration in a file named DataList.h, the methods in a file named DataList.cc file, and the main program in a separate .cc file.
DataList.cc has an #include header to include DataList.h.
whenever I run
g++ DataList.cc
in Ubuntu terminal, it spits out
DataList.cc: In function ‘bool TransactOnce(int&, TRANS&, std::string&)’:
DataList.cc:395:2: error: ‘ListNodeT’ was not declared in this scope
DataList.cc:395:13: error: ‘nodeTPtr’ was not declared in this scope
DataList.cc:396:13: error: ‘headTrans’ was not declared in this scope
What's weird is that I declare variables of these type ALL OVER THE PLACE in my other member functions in DataList.cc, but the compiler doesn't give me any other errors besides those above. I'm trying to figure out what it is that g++ doesn't like about those particular declarations in that one function.
'ListNodeT' is a struct within my main class, and the struct includes a pointer to another object that struct type.
'nodeTPtr' is simply a pointer to a 'ListNodeT' object, and
'headTrans' is a pointer (contained privately in the class) that points to an object of type ListNodeT, particularly, the very first node in the linked list.
The class "List" is intended for linked lists.
Here's the "DataList.h" file:
#ifndef DATALIST_H
#define DATALIST_H
#include <iostream>
#include <fstream>
#include <ostream>
#include <iomanip>
using namespace std;
const int MAXDISCIPLINES = 4;
const int MAXBREEDS = 4;
struct HORSE {
int ID;
string name; // maximum 25 alphanumeric characters, spaces included
double height;// in inches?
int age;
char genderCode;//'m', 'f', 'M', or 'F'
int breed;
int disciplineCount;
int discipline[MAXDISCIPLINES];
double lowBid; // minimum selling price
};
struct TRANS {
char transCode; //'A' for add, 'D' for delete or updating disciplines, or 'P' for updating lowBid price
HORSE transHorse;
};
class List {
private:
struct ListNodeT {
TRANS trans;
struct ListNodeT *nextLNT;
};
ListNodeT *headTrans;
struct ListNodeH {
HORSE horse;
struct ListNodeH *nextLNH;
};
ListNodeH *headHorse;
public:
//ListNodeH *headHorse;
//ListNodeT *headTrans;
List();
List(const List &obj);
~List();
bool InsertHorse(HORSE newHorse);
bool InsertTrans(TRANS newTrans);
bool DeleteHorse(int target);
bool DeleteTrans(int target);
bool ViewHorse(int target, HORSE &viewHorse);
bool ViewTrans(int target, TRANS &viewTrans);
int getNumHorses();
void getNumTrans(int &numA, int &numDel, int &numDisc, int &numP);
bool TransactOnce(int &skip, TRANS &thisTrans, string &failString);
bool UpdateDisc(TRANS thisTrans);
bool UpdatePrice(TRANS thisTrans);
void ReportOneTransaction(fstream &strm, TRANS thisTrans);
}; //end List class definition
//methods are in file DataList.cc and compiled separately.
#endif
And below is my "DataList.cc" file. The problematic "TransactOnce()" is towards the bottom.
#include "DataList.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <ostream>
#include <iomanip>
using namespace std;
//constructor
List::List(){
headHorse = NULL;
headTrans = NULL;
}
//copy constructor
List::List(const List &obj){
//copy the linked list of horses
ListNodeH *nodeHPtr;
ListNodeH **newListHPtr;
headHorse = NULL;
newListHPtr = &headHorse; //means &(this->headHorse), not &(obj.headHorse)
nodeHPtr = obj.headHorse;
while (nodeHPtr != NULL) {
*newListHPtr = new ListNodeH;
(*newListHPtr)->horse = nodeHPtr->horse;
(*newListHPtr)->nextLNH = NULL;
newListHPtr = &((*newListHPtr)->nextLNH);
nodeHPtr = nodeHPtr->nextLNH;
}
//copy the linked list of transactions
ListNodeT *nodeTPtr;
ListNodeT **newListTPtr;
headTrans = NULL;
newListTPtr = &headTrans;
nodeTPtr = obj.headTrans;
while (nodeTPtr != NULL) {
*newListTPtr = new ListNodeT;
(*newListTPtr)->trans = nodeTPtr->trans;
(*newListTPtr)->nextLNT = NULL;
newListTPtr = &((*newListTPtr)->nextLNT);
nodeTPtr = nodeTPtr->nextLNT;
}
}//end copy constructor
//destructor
List::~List(){
//destroy the list of horses
ListNodeH *nodeHPtr;
ListNodeH *nextHNode;
nodeHPtr = headHorse;
while(nodeHPtr != NULL){
nextHNode = nodeHPtr->nextLNH;
delete nodeHPtr;
nodeHPtr = nextHNode;
}//end walkthrough
//destroy the list of transactions
ListNodeT *nodeTPtr;
ListNodeT *nextTNode;
nodeTPtr = headTrans;
while(nodeTPtr != NULL){
nextTNode = nodeTPtr->nextLNT;
delete nodeTPtr;
nodeTPtr = nextTNode;
}//end walkthrough
}//end List::~List()
//********************************************************************
// FUNCTION NAME: InsertHorse()
// FUNCTION PURPOSE: inserts a new HORSE struct into our list
// INPUT PARAMETERS:
// 1. HORSE newHorse - the object of type DATA to be inserted into the List.
//RETURN VALUE – bool
//********************************************************************
bool List::InsertHorse(HORSE newHorse){
bool success = false;
ListNodeH *newNodeHPtr;
ListNodeH *nodeHPtr;
ListNodeH *prevNodeHPtr = NULL;
newNodeHPtr = new ListNodeH;
newNodeHPtr->horse = newHorse;
if(!headHorse){ //if head==NULL, our list is empty
headHorse = newNodeHPtr;
newNodeHPtr->nextLNH = NULL;
success = true;
}
else { //perhaps add? Our list is currently not empty.
nodeHPtr = headHorse;
//prevNodePtr; [sic] in my notes. I think it's supposed to be prevNodePtr = NULL;, but this should already be true
while(nodeHPtr != NULL && nodeHPtr->horse.ID < newHorse.ID){ //searches for approriate place
prevNodeHPtr = nodeHPtr;
nodeHPtr = nodeHPtr->nextLNH;
}
if(nodeHPtr != NULL && nodeHPtr->horse.ID == newHorse.ID){ //checks for duplicates
delete newNodeHPtr;
success = false; //note: this is the only way that Insert could return false
}
else { //insert the node
success = true;
newNodeHPtr->nextLNH = nodeHPtr;
if(prevNodeHPtr == NULL) //we're at the beginning of the list
headHorse = newNodeHPtr;
else //not at the beginning of the list
prevNodeHPtr->nextLNH = newNodeHPtr;
}//successful insertion
}
// delete newNodeHPtr;
return success;
}
//end List::InsertHorse()
//********************************************************************
// FUNCTION NAME: InsertTrans()
// FUNCTION PURPOSE: inserts a new TRANS struct into our list.
// if the transaction is trying to delete or update a horse that does not exist,
// returns false, and transaction is not inserted.
// INPUT PARAMETERS:
// 1. TRANS newTrans - the object of type TRANS to be inserted into the List.
//RETURN VALUE – bool
//********************************************************************
bool List::InsertTrans(TRANS newTrans){
bool success = false;
ListNodeT *newNodeTPtr;
ListNodeT *nodeTPtr;
ListNodeT *prevNodeTPtr = NULL;
newNodeTPtr = new ListNodeT;
newNodeTPtr->trans = newTrans;
HORSE dummyHorse; //dummy variable to be passed by reference to ViewHorse()
//we need to ensure a few things
//no duplicate IDs in transactions
//no Del, Disc, or P transactions for which the horse dosn't already exist in inventory
if(!headTrans){ //if head==NULL, our list is empty
headTrans = newNodeTPtr;
newNodeTPtr->nextLNT = NULL;
success = true;
}
else { //perhaps add? Our list is currently not empty.
nodeTPtr = headTrans;
//prevNodePtr; [sic] in my notes. I think it's supposed to be prevNodePtr = NULL;, but this should already be true
while(nodeTPtr != NULL && nodeTPtr->trans.transHorse.ID < newTrans.transHorse.ID){ //searches for approriate place
prevNodeTPtr = nodeTPtr;
nodeTPtr = nodeTPtr->nextLNT;
}
//check for duplicates
if(nodeTPtr != NULL && nodeTPtr->trans.transHorse.ID == newTrans.transHorse.ID){
delete newNodeTPtr;
success = false;
}
//in the case of Delete or Update transactions for which the horse does not already exist in inventory
else if((newTrans.transCode == 'D' || newTrans.transCode == 'P') && !List::ViewHorse(newTrans.transHorse.ID, dummyHorse)){
delete newNodeTPtr;
success = false;
}
else { //insert the transaction node
success = true;
newNodeTPtr->nextLNT = nodeTPtr;
if(prevNodeTPtr == NULL) //we're at the beginning of the list
headTrans = newNodeTPtr;
else //not at the beginning of the list
prevNodeTPtr->nextLNT = newNodeTPtr;
}//successful insertion
}
// delete newNodeTPtr;
return success;
}//end List::InsertTrans()
//********************************************************************
// FUNCTION NAME: DeleteHorse()
// FUNCTION PURPOSE: deletes a HORSE struct from our object of class List.
// INPUT PARAMETERS:
// 1. int target - the ID of the object of type HORSE to be deleted from List
//RETURN VALUE – bool
//********************************************************************
bool List::DeleteHorse(int target){
bool success = false;
ListNodeH *nodeHPtr;
ListNodeH *prevHPtr;
if (!headHorse) //empty list
success = false;
else if (headHorse->horse.ID == target) { //found target at the beginning of list, delete it
nodeHPtr = headHorse->nextLNH;
delete headHorse;
headHorse = nodeHPtr;
success=true;
}
else { // maybe delete?
nodeHPtr = headHorse;
while(nodeHPtr!=NULL && nodeHPtr->horse.ID < target){ //searching...
prevHPtr = nodeHPtr;
nodeHPtr = nodeHPtr->nextLNH;
}
if(nodeHPtr == NULL && nodeHPtr->horse.ID != target) //we're at the end of the list, and if target's not here...
success=false; //deletion failed. This is the only case where Delete() returns false.
else { //we're between the beginning and end, and we found the target
prevHPtr->nextLNH = nodeHPtr->nextLNH;
delete nodeHPtr; //delete target.
success = true;
}
}
return success;
}
//********************************************************************
// FUNCTION NAME: DeleteTrans()
// FUNCTION PURPOSE: deletes a TRANS struct from our object of class List.
// INPUT PARAMETERS:
// 1. int target - the ID of the object of type TRANS to be deleted from List
//RETURN VALUE – bool
//********************************************************************
bool List::DeleteTrans(int target){
bool success = false;
ListNodeT *nodeTPtr;
ListNodeT *prevTPtr;
if (!headTrans) //empty list
success = false;
else if (headTrans->trans.transHorse.ID == target) { //found target at the beginning of list, delete it
nodeTPtr = headTrans->nextLNT;
delete headTrans;
headTrans = nodeTPtr;
success=true;
}
else { // maybe delete?
nodeTPtr = headTrans;
while(nodeTPtr!=NULL && nodeTPtr->trans.transHorse.ID < target){ //searching...
prevTPtr = nodeTPtr;
nodeTPtr = nodeTPtr->nextLNT;
}
if(nodeTPtr == NULL && nodeTPtr->trans.transHorse.ID != target) //we're at the end of the list, and if target's not here...
success=false; //deletion failed. This is the only case where Delete() returns false.
else { //we're between the beginning and end, and we found the target
prevTPtr->nextLNT = nodeTPtr->nextLNT;
delete nodeTPtr; //delete target.
success = true;
}
}
return success;
}
//********************************************************************
// FUNCTION NAME: ViewHorse()
// FUNCTION PURPOSE: finds the HORSE struct whose ID is target, then returns the HORSE by reference.
// INPUT PARAMETERS:
// 1. int target - the ID to be searched for
// 2. HORSE &viewHorse - the object of type HORSE to be found and returned (by reference)
//RETURN VALUE – bool
//********************************************************************
//returns by reference the HORSE in the list with ID == target
//if View() returns false, then the target was not found and &viewHorse was not changed by ViewHorse().
bool List::ViewHorse(int target, HORSE &viewHorse){
bool success = false;
ListNodeH *nodeHPtr;
nodeHPtr = headHorse; //start at the beginning of the list
while(nodeHPtr != NULL && nodeHPtr->horse.ID < target) //search...
nodeHPtr = nodeHPtr->nextLNH;
//which test failed?
if(nodeHPtr == NULL || nodeHPtr->horse.ID != target) //we're at the end of the list, or didn't find the target.
success = false; //Note: this is the ONLY way that ViewHorse() returns false.
else { //nodeHPtr != NULL && nodeHPtr->horse.ID == target, we found the target
success = true;
viewHorse = nodeHPtr->horse; //return by reference the HORSE in the list with ID == target
}
return success;
}//end List::ViewHorse()
//********************************************************************
// FUNCTION NAME: ViewTrans()
// FUNCTION PURPOSE: finds the TRANS struct whose ID is target, then returns the TRANS by reference.
// INPUT PARAMETERS:
// 1. int target - the ID to be searched for
// 2. TRANS &viewTrans - the object of type TRANS to be found and returned (by reference)
//RETURN VALUE – bool
//********************************************************************
//returns by reference the TRANS in the list with ID == target
//if ViewTrans() returns false, then the target was not found and &viewTrans was not changed by ViewTrans().
bool List::ViewTrans(int target, TRANS &viewTrans){
bool success = false;
ListNodeT *nodeTPtr;
nodeTPtr = headTrans; //start at the beginning of the list
while(nodeTPtr != NULL && nodeTPtr->trans.transHorse.ID < target) //search...
nodeTPtr = nodeTPtr->nextLNT;
//which test failed?
if(nodeTPtr == NULL || nodeTPtr->trans.transHorse.ID != target) //we're at the end of the list, or didn't find the target.
success = false; //Note: this is the ONLY way that ViewTrans() returns false.
else { //nodeTPtr != NULL && nodeTPtr->horse.ID == target, we found the target
success = true;
viewTrans = nodeTPtr->trans; //return by reference the TRANS in the list with ID == target
}
return success;
}//end List::ViewTrans
//********************************************************************
// FUNCTION NAME: getNumHorses()
// FUNCTION PURPOSE: returns the number of nodes in our list of horses
// INPUT PARAMETERS: [none]
//RETURN VALUE – int
//********************************************************************
int List::getNumHorses(){
int horseCounter = 0;
ListNodeH *nodeHPtr;
nodeHPtr = headHorse;
while(nodeHPtr != NULL){
nodeHPtr = nodeHPtr->nextLNH;
horseCounter++;
}
return horseCounter;
}//end List::getNumHorses()
//********************************************************************
// FUNCTION NAME: getNumTrans()
// FUNCTION PURPOSE: returns (by reference) the numbers of various types of transactions in our list of transactions
// INPUT PARAMETERS:
// 1.int &numA - number of "Add" transactions
// 2.int &numDel - number of "Delete" transactions
// 3.int &numDisc - number of "update discipline" transactions
// 4.int &numP - number of "update lowBid" transactions
//RETURN VALUE – void
//********************************************************************
void List::getNumTrans(int &numA, int &numDel, int &numDisc, int &numP){
numA = 0;
numDel = 0;
numDisc = 0;
numP = 0;
ListNodeT *nodeTPtr;
nodeTPtr = headTrans;
while(nodeTPtr != NULL){
if(nodeTPtr->trans.transCode == 'A')
numA++;
else if(nodeTPtr->trans.transCode == 'D'){
if (nodeTPtr->trans.transHorse.disciplineCount == 0)
numDel++;
else
numDisc++;
}
else if(nodeTPtr->trans.transCode == 'P'){
numP++;
}
nodeTPtr = nodeTPtr->nextLNT;
}
}//end List::getNumTrans()
//********************************************************************
// FUNCTION NAME: TransactOnce()
// FUNCTION PURPOSE: Performs a single transaction.
// [skip] number of transactions at the beginning of the linked list of trans are skipped.
// The transaction immediately after the skipped transactions is attempted.
// If the transaction fails, skip is incremented and the function returns false.
// In any case, thisTrans is returned by reference as the transaction that was just attempted.
// INPUT PARAMETERS:
// 1. int &skip - the number of transactions to skip
// 2. TRANS &thisTrans - the returned transaction that was just attempted
// 3. string &failString - the ERROR, if any.
//RETURN VALUE – bool
//********************************************************************
bool TransactOnce(int &skip, TRANS &thisTrans, string &failString){
bool success = true;
stringstream failStream;
ListNodeT *nodeTPtr;
nodeTPtr = headTrans;
HORSE dummyHorse; //for ViewHorse()
//if 'A', the ID must not already exist in our list of horses
//if 'D' or 'P', the horse must already exist in our list of horses.
//if 'D' and disciplineCount != 0, then
//the existing horse must already have disciplineCount <4
//transaction's transHorse.discipline[] must not have duplicates in the array
//each of transaction's transHorse.discipline[]s must be valid (1<= discipline <=4)
//find the transaction in question
for (int i=0; i < skip; i++){
nodeTPtr = nodeTPtr->nextLNT;
}
//nodeTPtr is now pointing to the appropriate ListNodeT.
if (nodeTPtr->trans.transCode == 'A' && !List::InsertHorse(nodeTPtr->trans.transHorse)){
failStream << "ERROR for horse transaction" << nodeTPtr->trans.transHorse.ID << " via InsertHorse().";
success = false;
}
else if (nodeTPtr->trans.transCode == 'D'){ //delete or update disciplines
if(nodeTPtr->trans.transHorse.disciplineCount == 0 && !List::DeleteHorse(nodeTPtr->trans.transHorse.ID)){ //for deletion
failStream << "ERROR for horse transaction" << nodeTPtr->trans.transHorse.ID << " via DeleteHorse().";
success = false;
}
else if(nodeTPtr->trans.transHorse.disciplineCount != 0 && !List::UpdateDisc(nodeTPtr->trans)){ //update disciplines
failStream << "ERROR for horse transaction" << nodeTPtr->trans.transHorse.ID << " via UpdateDisc().";
success = false;
}
}
else if (nodeTPtr->trans.transCode == 'P' && !List::UpdatePrice(nodeTPtr->trans)){
failStream << "ERROR for horse transaction" << nodeTPtr->trans.transHorse.ID << " via UpdatePrice().";
success = false;
}
else {
failStream << "ERROR for horse transaction" << nodeTPtr->trans.transHorse.ID << ", not a valid transCode.";
success = false;
}
if (success){ //attempt to delete the transaction
if (!List::DeleteTrans(nodeTPtr->trans.transHorse.ID)){
failStream << "ERROR, could not delete transaction " << nodeTPtr->trans.transHorse.ID << ".";
success = false;
}
else //return true and error free
failStream << "No error to report.";
}
if (!success)
skip++;
failStream << "\n";
failString = failStream.str();
return success;
}//end TransactOnce()
//********************************************************************
// FUNCTION NAME: UpdateDisc()
// FUNCTION PURPOSE: for an "update discipline" transaction, update the disciplines of that horse in inventory.
// INPUT PARAMETERS:
// 1. TRANS thisTrans - the transaction to be executed
//RETURN VALUE – bool
//********************************************************************
bool List::UpdateDisc(TRANS thisTrans){
bool success = true;
ListNodeH *nodeHPtr = headHorse; //no need for a ListNodeT
//obtain a pointer to the horse in question
while (nodeHPtr != NULL && nodeHPtr->horse.ID < thisTrans.transHorse.ID)
nodeHPtr = nodeHPtr->nextLNH;
if(nodeHPtr == NULL) //failed to find the horse
success = false;
//horse found; nodeHPtr->horse.ID == thisTrans.transHorse.ID
else if(thisTrans.transCode != 'D') //this isn't even a "D" transaction...
success = false;
else if(thisTrans.transHorse.disciplineCount == 0) //nothing to do, this is for deletion
success = false;
else if(nodeHPtr->horse.disciplineCount + thisTrans.transHorse.disciplineCount > 4)
success = false; //too many disciplines
else {
//update the disciplines
while(thisTrans.transHorse.disciplineCount !=0){
nodeHPtr->horse.discipline[nodeHPtr->horse.disciplineCount] = thisTrans.transHorse.discipline[thisTrans.transHorse.disciplineCount-1];
nodeHPtr->horse.disciplineCount++;
thisTrans.transHorse.disciplineCount--;
}//disciplines are now updated but perhaps not sorted
//bubble-sort the horse's array of disciplines.
int i, j;
bool swaped = true;
int tempInt;
for(i = 1; (i <= nodeHPtr->horse.disciplineCount)&&swaped; i++){
swaped = false;
for (j=0; j< nodeHPtr->horse.disciplineCount-1; j++){
if(nodeHPtr->horse.discipline[j] > nodeHPtr->horse.discipline[j+1]){
tempInt = nodeHPtr->horse.discipline[j];
nodeHPtr->horse.discipline[j] = nodeHPtr->horse.discipline[j+1];
nodeHPtr->horse.discipline[j+1] = tempInt;
swaped = true;
}
}
}
}//end update and sort horse's disciplines
return success;
}//end List::UpdateDisc()
//********************************************************************
// FUNCTION NAME: UpdatePrice()
// FUNCTION PURPOSE: for an "update lowBid" transaction, update the lowBid of that horse in inventory.
// INPUT PARAMETERS:
// 1. TRANS thisTrans - the transaction to be executed
//RETURN VALUE – bool
//********************************************************************
bool List::UpdatePrice(TRANS thisTrans){
bool success = true;
ListNodeH *nodeHPtr;
nodeHPtr = headHorse;
//obtain a pointer to the horse in question
while (nodeHPtr != NULL && nodeHPtr->horse.ID < thisTrans.transHorse.ID)
nodeHPtr = nodeHPtr->nextLNH;
if(nodeHPtr == NULL) //failed to find the horse
success = false;
//else, horse found; nodeHPtr->horse.ID == thisTrans.transHorse.ID
else if(thisTrans.transCode != 'P') //this isn't even a "P" transaction...
success = false;
else { //update lowBid
nodeHPtr->horse.lowBid = thisTrans.transHorse.lowBid;
}
return success;
}//end List::UpdatePrice()
//********************************************************************
// FUNCTION NAME: ReportOneTransaction()
// FUNCTION PURPOSE: prints to Report.out a single transaction. called by Transact().
// INPUT PARAMETERS:
// 1. fstream &strm - the stream to print to.
// 2. TRANS oneTrans - the transaction being reported.
//RETURN VALUE – void
//********************************************************************
void List::ReportOneTransaction(fstream &strm, TRANS oneTrans){
//some "Ragged Arrays"
string breedDescription[MAXBREEDS+1] = {"Dummy", "Pleasure Saddle Horse", "American Fox Trotter", "Virginia Highlander", "Arabian"};//21
string disciplineDescription[MAXDISCIPLINES+1] = {"Dummy", "Dressage", "Jumper", "Hunter", "Western Pleasure"};//16
if (oneTrans.transCode == 'A')
strm << "Horse ADDED to inventory named '" ;
else
strm << "Horse REMOVED from inventory named '" ;
strm << oneTrans.transHorse.name << "'"<< endl;
strm << "______________________________________________________________________\n";//70
strm <<"ID Height Age M/F LowBid "<< oneTrans.transHorse.disciplineCount << " Discipline(s): Breed\n";
strm << left << setw(3) <<oneTrans.transHorse.ID <<" ";//ID
strm << left << setw(5) <<setprecision(2) <<oneTrans.transHorse.height <<" ";//height (double)
strm << left << setw(3) <<oneTrans.transHorse.age <<" ";//age
strm << left << setw(1) <<oneTrans.transHorse.genderCode; //genderCode (char)
strm << " "<<left<<'$';
strm << right << fixed << setw(8) << setprecision(2) << oneTrans.transHorse.lowBid ;//lowBid (double)
strm << left << " " << setw(16) << disciplineDescription[oneTrans.transHorse.discipline[0]] ;
strm << " " << breedDescription[oneTrans.transHorse.breed] << endl;
for (int j=1; j < oneTrans.transHorse.disciplineCount; j++){
strm << " ";
strm << disciplineDescription[oneTrans.transHorse.discipline[j]] << endl;
}
strm << endl << endl;
}//end ReportOneTransaction()
You forgot List:: on TransactOnce so it's trying to parse it as a stand-alone function. I did this myself just a couple days ago...
The TransactOnce function should be implemented as it was declared, as a member function of List:
Change this:
bool TransactOnce(int &skip, TRANS &thisTrans, string &failString){
to this:
bool List::TransactOnce(int &skip, TRANS &thisTrans, string &failString){
OP here. I figured it out.
when defining TransactOnce()
bool TransactOnce(.....)
should have been
bool List::TransactOnce(....)

Find a specific node in a XML document with TinyXML

I want to parse some data from an xml file with TinyXML.
Here's my text.xml file content:
<?xml version="1.0" encoding="iso-8859-1"?>
<toto>
<tutu>
<tata>
<user name="toto" pass="13" indice="1"/>
<user name="tata" pass="142" indice="2"/>
<user name="titi" pass="azerty" indice="1"/>
</tata>
</tutu>
</toto>
I want to access to the first element 'user'. The way to do this is the following :
TiXmlDocument doc("test.xml");
if (doc.LoadFile())
{
TiXmlNode *elem = doc.FirstChildElement()->FirstChildElement()->FirstChildElement()->FirstChildElement();
std::cout << elem->Value() << std::endl;
}
In output : user.
But the code is pretty ugly and not generic. I tried the code below to simulate the same behaviour than the code above but it doesn't work and an error occured.
TiXmlElement *getElementByName(TiXmlDocument &doc, std::string const &elemt_value)
{
TiXmlElement *elem = doc.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return (elem);
elem = elem->NextSiblingElement();
}
return (NULL);
}
Maybe I missed a special function in the library which can do this work (a getElementByName function). I just want to get a pointer to the element where the value is the one I'm looking for. Does anyone can help me? Thanks in advance for your help.
Try this
TiXmlElement * getElementByName(TiXmlDocument & doc, std::string const & elemt_value) {
TiXmlElement * elem = doc.RootElement(); //Tree root
while (elem) {
if (!std::string(elem - > Value()).compare(elemt_value)) return (elem);
/*elem = elem->NextSiblingElement();*/
if (elem - > FirstChildElement()) {
elem = elem - > FirstChildElement();
} else if (elem - > NextSiblingElement()) {
elem = elem - > NextSiblingElement();
} else {
while (!elem - > Parent() - > NextSiblingElement()) {
if (elem - > Parent() - > ToElement() == doc.RootElement()) {
return NULL;
}
elem = elem - > Parent() - > NextSiblingElement();
}
}
}
return (NULL);
}
Adi's answer didn't work when i just copy pasted it into my code,
but i modified it and now it works fine for me.
since i made quite a lot of changes i thought i should post my final code here.
void parseXML(tinyxml2::XMLDocument& xXmlDocument, std::string sSearchString, std::function<void(tinyxml2::XMLNode*)> fFoundSomeElement)
{
if ( xXmlDocument.ErrorID() != tinyxml2::XML_SUCCESS )
{
// XML file is not ok ... we throw some exception
throw DataReceiverException( "XML file parsing failed" );
} // if
//ispired by http://stackoverflow.com/questions/11921463/find-a-specific-node-in-a-xml-document-with-tinyxml
tinyxml2::XMLNode * xElem = xXmlDocument.FirstChild();
while(xElem)
{
if (xElem->Value() && !std::string(xElem->Value()).compare(sSearchString))
{
fFoundSomeElement(xElem);
}
/*
* We move through the XML tree following these rules (basically in-order tree walk):
*
* (1) if there is one or more child element(s) visit the first one
* else
* (2) if there is one or more next sibling element(s) visit the first one
* else
* (3) move to the parent until there is one or more next sibling elements
* (4) if we reach the end break the loop
*/
if (xElem->FirstChildElement()) //(1)
xElem = xElem->FirstChildElement();
else if (xElem->NextSiblingElement()) //(2)
xElem = xElem->NextSiblingElement();
else
{
while(xElem->Parent() && !xElem->Parent()->NextSiblingElement()) //(3)
xElem = xElem->Parent();
if(xElem->Parent() && xElem->Parent()->NextSiblingElement())
xElem = xElem->Parent()->NextSiblingElement();
else //(4)
break;
}//else
}//while
}
(for completeness) example how to call the function:
tinyxml2::XMLDocument xXmlDocument;
xXmlDocument.Parse(sXmlDocument.c_str());
parseXML(xXmlDocument, "user",[](tinyxml2::XMLNode* xElem)
{
int iPass;
xElem->QueryIntAttribute( "pass", &iPass );
std::cout << iPass << "\n";
});
You can also iterate through your XML elements one-by-one by using recursive function combined with lamda-function as a handler.
//
// This function will iterate through your XML tree and call the 'parseElement' function for each found element.
//
void RecursiveXMLParse(TiXmlElement* element, std::function<void(TiXmlElement*)>& parseElement)
{
if (element != nullptr)
{
parseElement(element);
auto child = element->FirstChildElement();
if (child != nullptr)
{
RecursiveXMLParse(child, parseElement);
}
for (auto sibling = element->NextSiblingElement(); sibling != nullptr; sibling = sibling->NextSiblingElement())
{
RecursiveXMLParse(sibling, parseElement);
}
}
}
Usage: Just pass the XML root element, and your data handler lambda function to the recursive Parser-function.
int main()
{
//
// Define your data handler labmda
//
std::function<void(TiXmlElement*)>parseElement = [&](TiXmlElement* e) -> void
{
if (std::string(elem->Value()).compare("user"))
{
// Parse your user data
}
};
// Pass the root element along with the above defined lambda to the recursive function
RecursiveXMLParse(doc.RootElement(), parseElement);
return 0;
}
XMLElement *getElementByName(XMLDocument &ele, std::string const &elemt_value)
{
XMLElement *elem = ele.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return elem;
if (elem->FirstChildElement())
{
elem = elem->FirstChildElement();
}
else if (elem->NextSiblingElement())
{
elem = elem->NextSiblingElement();
}
else
{
if (elem->Parent()->ToElement()->NextSiblingElement())
{
elem = elem->Parent()->ToElement()->NextSiblingElement();
}
else if (elem->Parent()->ToElement()->FirstChildElement()
&& strcmp(elem->Name(), elem->Parent()->ToElement()->FirstChildElement()->Name()))
{
elem = elem->Parent()->ToElement()->FirstChildElement();
}
else {
break;
}
}
}
return NULL;
}
// A small tweak in the above given solution