xmlNodePtr XMLFile::FindNode( const string& path, bool createIfMissing ) { xmlNodePtr cur,parent; string tokens = "/"; vector<string> tokenized; vector<string>::iterator iter; string partialPath; // Check previously memoized values map<string,xmlNodePtr>::iterator val = values.find( path ); if( val != values.end() ){ // If we found it // Check that we don't return memoized NULL values when instructed to createIfMissing. if( !((val->second==NULL) && createIfMissing) ) { return val->second; } } if( xmlPtr == NULL ) { return( (xmlNodePtr)NULL ); } // Initialize the xml navigation cursor cur = xmlDocGetRootElement( xmlPtr ); if( cur == NULL ) { LogMsg(WARN, "XML file (%s) appears to be empty.", filename.c_str() ); return( (xmlNodePtr)NULL ); } tokenized = TokenizedString(path, tokens); iter = tokenized.begin(); // The root is optional since it isn't a Child. if( !xmlStrcmp(cur->name, (const xmlChar *)(tokenized.front().c_str()) ) ) { ++iter; } // Walk the tokenized path // If FirstChildNamed() doesn't find the path, it will return NULL for(; iter != tokenized.end() && cur != NULL; ++iter) { partialPath = *iter; if( partialPath.find_first_of(tokens) != string::npos) { continue; } parent = cur; cur = FirstChildNamed(parent, partialPath.c_str()); if( (createIfMissing) && (cur==NULL) ) { cur = xmlNewChild(parent, NULL, BAD_CAST partialPath.c_str(), NULL ); } } // Memoize this result for later values[path] = cur; return( cur ); }
/**\brief Search this Container for a Widget * * \see Container::Search */ Widget *Container::Search( string full_query ) { int section = 0; char token; string subquery; string tokens = "/[]\"'(,)"; vector<string> tokenized; vector<string>::iterator iter; list<Widget *>::iterator i; Container *current = this; // Temporary query values typedef struct { union { int flags; struct { int FOUND_COORD :1; int FOUND_TYPE :1; int FOUND_NAME :1; int FOUND_INDEX :1; }; }; int x,y; string type; string name; int index; } Query; Query query = {{0},0,0,"","",0}; // Tokenize the String tokenized = TokenizedString( full_query, tokens ); // Check the Start of the Query iter = tokenized.begin(); if( (tokenized.size() >= 2) && (tokenized[0] == "") && (tokenized[1] == "/") ) { ++iter; ++iter; ++section; } else { LogMsg(ERR, "Query '%s' does not start with '/'.", full_query.c_str() ); return NULL; } // Search all the tokens LogMsg(INFO, "QUERY: '%s'", full_query.c_str() ); for(; iter != tokenized.end(); ++iter ) { subquery = (*iter); // LogMsg(INFO, "token: '%s'", (*iter).c_str() ); token = subquery[0]; if( subquery == "" ) { continue; } // If we're checking a Token, we need to be in a Container if( !( (current->GetMask()) & WIDGET_CONTAINER ) ) { LogMsg(INFO, "The query '%s' reached a non-container Widget and aborted at section %d.", full_query.c_str(), section ); return NULL; } // If this is not a token then it is Widget Type if( subquery.find_first_of(tokens) != string::npos) { assert( subquery.size() == 1 ); switch( token ) { // Boundary: Search the Children case '/': { int ind = 0; assert( (current->GetMask()) & WIDGET_CONTAINER ); for( i = current->children.begin(); i != current->children.end(); ++i ) { // LogMsg(DEBUG1, "Checking %s %s (%d,%d) 0x%08X\n", (*i)->GetName().c_str(), (*i)->GetType().c_str(), (*i)->GetX(), (*i)->GetY(), (*i)->GetMask() ); if( query.FOUND_NAME && (query.name != (*i)->GetName()) ) { continue; } if( query.FOUND_TYPE && (query.type != (*i)->GetType()) ) { continue; } if( query.FOUND_COORD && ((*i)->Contains(query.x, query.y) == false) ) { continue; } if( query.FOUND_INDEX && (query.index != ind) ) { ind++; continue; } // Found a match! current = (Container*)(*i); // Forget about the current query query.flags = 0; break; } if( i == current->children.end() ) { LogMsg(INFO, "The query '%s' failed to find a widget at section %d", full_query.c_str(), section ); return NULL; } ++section; break; } // Bracketed number: Container Index case '[': { query.FOUND_INDEX = 1; query.index = convertTo<int>( *(++iter) ); assert( *(++iter) == "]" ); break; } // Quoted String: Widget Name case '\'': case '"': { query.FOUND_NAME = 1; query.name = *(++iter); ++iter; assert( (*(iter) == "\"") || (*(iter) == "'") ); break; } // Paren Tuple: Widget's Relative coordinate case '(': { query.FOUND_COORD = 1; query.x = convertTo<int>( *(++iter) ); assert( *(++iter) == "," ); query.y = convertTo<int>( *(++iter) ); assert( *(++iter) == ")" ); break; } default: LogMsg(ERR, "Unexpected token '%c' in query '%s'", token, full_query.c_str() ); assert(0); return NULL; } } // Plain String: Widget Type else if( subquery.size() > 0 ) { query.FOUND_TYPE = 1; query.type = subquery; } // Empty String, skip it. else { } } if( query.flags != 0 ) { LogMsg(WARN, "Query '%s' did not end with a '/'", full_query.c_str() ); } LogMsg(DEBUG1, "Found %s %s (%d,%d) 0x%08X\n", (*i)->GetName().c_str(), (*i)->GetType().c_str(), (*i)->GetX(), (*i)->GetY(), (*i)->GetMask() ); return current; }