blob: 28270b790e74a5aead6437b4278da400162bdc4f [file] [log] [blame]
#ifndef IMAGE_IO_XML_XML_WRITER_H_ // NOLINT
#define IMAGE_IO_XML_XML_WRITER_H_ // NOLINT
#include <sstream>
#include <string>
#include <vector>
namespace photos_editing_formats {
namespace image_io {
/// A very simple writer forXML that frees client code from worries about XML
/// formatting and bracket issues.
///
/// The intended sequence of operations this writer supports is as follows:
/// 1. Start writing an element.
/// 2. Write any and all attribute names and values to that element.
/// 3. Write any content, or add a child element by starting to write another
/// element (i.e., go to step 1). The "context" of the current element you
/// are writing is saved on a stack. Once you start writing content or
/// child elements you cannot add attribute names and values and expect to
/// see them as such in the resulting XML.
/// 4. When you are done with the element, finish writing it. The element
/// context stack is popped and you continue where you left off.
///
/// When writing element content and attribute values no XML escaping of any
/// kind is done. If you need to do that, do it yourself.
class XmlWriter {
public:
/// @param os The stream to which the XML is written.
explicit XmlWriter(std::ostream& os);
/// @return The number of elements that have been written.
size_t GetElementCount() const { return element_count_; }
/// @return The depth of the element stack.
size_t GetElementDepth() const { return element_data_.size(); }
/// @return The quote mark used when writing attribute values. The default
/// value set up by the constructor is the double quote (").
char GetQuoteMark() const { return quote_mark_; }
/// @param quote_park The new quote mark to use when writing attribute values.
void SetQuoteMark(char quote_mark) { quote_mark_ = quote_mark; }
/// @return The leading indent written before the current element.
const std::string& GetIndent() const { return indent_; }
/// Once you are done writing your elements, you can call this function to
/// finish writing of all open elements. After this call, the string contained
/// in the ostream you passed to the constructor is fully formed XML.
void FinishWriting() { FinishWritingElementsToDepth(0); }
/// @return Whether the writing of XML can be considered done.
bool IsDone() const { return indent_.empty(); }
/// Writes an xmlns attribute to the currently open element.
/// @param prefix The prefix you intend to use for elements/attributes.
/// @param uri The uri of the namespace.
void WriteXmlns(const std::string& prefix, const std::string& uri);
/// Starts writing a new child element of the current element. Immediately
/// after this function you can add attributes to the element using one of the
/// AddAttributeNameAndValue() functions.
/// @param element_name The name of the element to write.
/// @return The number of open elements on the stack at the start of this
/// function. You can use this value with the FinishWritingElementToDepth()
/// function to finish writing this element and any open descendents.
size_t StartWritingElement(const std::string& element_name);
/// Finishes writing the element and returns the "context" to the previously
/// open element so that you can continue adding child elements (via a call to
/// StartWritingElement()) or content (via a call to WriteContent()).
void FinishWritingElement();
/// Finishes writing any elements that exist in the stack of open elements
/// above the depth value parameter.
/// @param depth The depth above which to finish writing open elements.
void FinishWritingElementsToDepth(size_t depth);
/// Starts writing the elements in the vector, leaving the last open for you
/// to add attributes or other elements to.
/// @param element_names The array of element names to start writing.
/// @return The number of open elements on the stack at the start of this
/// function. You can use this value with the FinishWritingElementToDepth()
/// function to finish writing this element and any open descendents.
size_t StartWritingElements(const std::vector<std::string>& element_names);
/// A template method function that allows you to start an element, add the
/// value as its content and then finish writing the element. This is useful
/// if you are writing property values as elements.
/// @param element_name The name of the element to write.
/// @param value The value that is converted to a string and written as the
/// element's content.
template <class T>
void WriteElementAndContent(const std::string& element_name, const T& value) {
std::stringstream ss;
ss << value;
WriteElementAndContent(element_name, ss.str());
}
/// Starts writing an element with the given name, adds the string value as
/// its content and then finishes writing the element. This is useful
/// if you are writing property values as elements.
/// @param element_name The name of the element to write.
/// @param value The value to use as the element's content.
void WriteElementAndContent(const std::string& element_name,
const std::string& content);
/// Writes the string as the currently open element's content. Note that if
/// you add child elements to the open element, the content you will see when
/// you read your element will have the whitespace due to the indent string.
/// @param content The content to write to the currently open element.
void WriteContent(const std::string& content);
/// A template method function that allows you to add an attribute name and
/// value to a just-opened element. Attributes must be added to an element
/// before adding content or child elements.
/// @param name The name of the attribute to add.
/// @param value The value of the attribute. This value is converted to a
/// string and enclosed in the quote marks from the GetQuoteMark() function.
template <class T>
void WriteAttributeNameAndValue(const std::string& name, const T& value) {
std::stringstream ss;
ss << GetQuoteMark() << value << GetQuoteMark();
WriteAttributeNameAndValue(name, ss.str(), false);
}
/// Adds an attribute name and value to a just-opened element. Attributes must
/// be added to an element before adding content or child elements.
/// @param name The name of the attribute to add.
/// @param value The value of the attribute.
/// @param add_quote_marks Whether quote marks should be added before and
/// after the value. If this value is false, it is assumed that the client
/// code has added them before calling this function.
void WriteAttributeNameAndValue(const std::string& name,
const std::string& value,
bool add_quote_marks = true);
/// Adds an attribute name and equal sign to the just-opened element.
/// Attributes must be added to an element before adding content or child
/// elements. Clients that use this function must call WriteAttributeValue()
/// with appropriate values to define a legally quoted value. This function
/// is useful for writing attribute with extremely long values that might not
/// be efficient to store as a single string value.
/// @param name The name of the attribute to add.
void WriteAttributeName(const std::string& name);
/// Writes the attribute value with optional quote marks on either side. This
/// function may be repeatedly called with appropriate valeus for the leading
/// and trailing quote mark flags to write extremely long attribute values.
/// @param add_leading_quote_mark Whether to add a leading quote mark.
/// @param value The (probably partial) value to write.
/// @param add_trailing_quote_mark Whether to add a trailing quote mark.
void WriteAttributeValue(bool add_leading_quote_mark,
const std::string& value,
bool add_trailing_quote_mark);
/// Writes a comment to the xml stream. Note that writing a comment is like
/// adding a child node/element to the current element. If the current element
/// is still open for names/values, it will be closed before writing it - i.e.
/// you can't add attributes to an element after calling this function.
/// @param comment The text of the comment to write.
void WriteComment(const std::string& comment);
private:
/// The data that is known about each element on the stack.
struct ElementData {
ElementData(const std::string& name_)
: name(name_),
has_attributes(false),
has_content(false),
has_children(false) {}
std::string name;
bool has_attributes;
bool has_content;
bool has_children;
};
/// Determines if the start element syntax of the current element needs to
/// be closed with a bracket so that content or child elements or comments
/// can be added to the element.
/// @param with_trailing_newline Whether a newline is added after the bracket.
/// @return Whether the element's start syntax was closed with a bracket.
bool MaybeWriteCloseBracket(bool with_trailing_newline);
/// The stream to which everything is written.
std::ostream& os_;
/// The indent to write before elements and attribute names/values.
std::string indent_;
/// The currently open elements being written.
std::vector<ElementData> element_data_;
/// The number of elements that have been written.
size_t element_count_;
/// The quote mark to use around attribute values by default.
char quote_mark_;
};
} // namespace image_io
} // namespace photos_editing_formats
#endif // IMAGE_IO_XML_XML_WRITER_H_ // NOLINT