namespace xml
{
class element
{
public:
std::string name;
std::string text;
std::map<std::string, std::string> attributes;
std::list<element> 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<std::string, std::string> 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.
}
}
}
}
};
}