namespace xml { class element { public: std::string name; std::string text; std::map attributes; std::list children; element *parent; element() { } element(std::istream &s) { load(s); } void load(std::istream &s) { text.clear(); attributes.clear(); children.clear(); element *open_element = this; std::string tmp_tagname; std::string tmp_attrname; std::string tmp_attrvalue; std::map tmp_attributes; int open_tags = 0; // A counter for open tags to establish blocks. unsigned char cur = 0; // Bit field with any of the following flags... static const unsigned char cur_intag = 0x1; // Currently processing a tag. static const unsigned char cur_foundroot = 0x2; // We found at least the root element. static const unsigned char cur_ent = 0x4; // Currently processing a character entity. static const unsigned char cur_attr = 0x8; // Currently processing an attribute. static const unsigned char cur_str = 0x10; // Currently processing an attribute value within quotes. static const unsigned char cur_pastname = 0x20; // The tag name was already found. static const unsigned char cur_closing = 0x40; // We found a forward slash before the tag name. /// static const unsigned char cur_attr = 0x10; // Currently assigning an attribute. while(open_tags || !(cur & cur_foundroot)) // Continue until we have found and closed the root element. { char c = s.get(); if(!(cur & cur_str)) // If we are not in a string. { // Conclusion: Syntactic stuff is OK. if(c == '<') // Start tag { cur |= cur_intag; // Set in-tag flag. continue; } if(c == '>') // End tag { cur &= ~cur_intag; // Clear in-tag flag. if(cur & cur_closing) // If we found a slash before the tag name. { // Conclusion: This is a closing tag. open_element = open_element->parent; // Step back up to parent element. open_tags--; // One step closer to closing the root element. } else { // Conclusion: This is an opening tag for a new child element. open_element->name = tmp_tagname; open_element->children.resize(open_element->children.size()+1); // Create child element in open element element *tmp = open_element; // Save open element open_element = &*children.end(); // Set open element to new child open_element->parent = tmp; // Set child's parent open_tags++; // One step further from closing the root element. cur |= cur_foundroot; // Set found-root-element flag. } tmp_tagname.clear(); continue; } if(c == '/') // If this character is a forward slash. { if(cur & cur_intag) // If we're in a tag. { if(!(cur & cur_pastname)) // If we haven't passed the tag name yet. { // Conclusion: This is a closing tag. cur |= cur_closing; // Set currently-assigning-attribute flag. } } } if(c == '=') // If this character is an equal sign. { if(cur & cur_intag) // If inside a tag. { // Conclusion: This must be an attribute assignment operator. cur |= cur_attr; // Set currently-assigning-attribute flag. } } } if(c == '"') // If this character is a quote mark. { if(cur & cur_intag) // If inside a tag. { if(cur & cur_attr) // If currently assigning an attribute. { cur |= cur_str; // Set the currently-reading-string flag. } else if(cur & cur_str) // If already reading in a string. { // Conclusion: This is a closing quote mark for an attribute value. tmp_attributes[tmp_attrname] = tmp_attrvalue; tmp_attrname.clear(); tmp_attrvalue.clear(); cur &= ~cur_str; // Clear currently-reading-string flag. cur &= ~cur_attr; // Clear currently-assigning-attribute flag. } } } // What to expect inside a tag changes with whitespace. else if(std::isspace(c)) // If this character is whitespace. { if(cur & cur_intag) // If inside a tag { if(tmp_tagname.size()) // If tag name buffer is not empty. { // Conclusion: We are past the tag name and now reading attributes. cur |= cur_pastname; // Set currently-past-name flag. } } } else { // Conclusion: This character must be contents. if(cur & cur_intag) // If inside a tag. { if(cur & cur_pastname) // If past the tag name { // Conclusion: Reading in attributes. if(cur & cur_str) // If inside a string. { tmp_attrvalue += c; } else { // Append this character to the attribute name buffer. tmp_attrname += c; } } else { // Conclusion: We are still reading in the name. tmp_tagname += c; // Append character to temporary tag name buffer. } } else // Not inside a tag { // Conclusion: Basic text contents of element. open_element->text += c; // Append this character to the element's text buffer. } } } } }; }