All pastes #172500 Raw Edit

Stuff

public text v1 · immutable
·published 2006-09-16 04:13 UTC
rendered paste body
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.
					}
				}
			}

		}
	};

}