1 #ifndef IMAGE_IO_XML_XML_WRITER_H_ // NOLINT 2 #define IMAGE_IO_XML_XML_WRITER_H_ // NOLINT 3 4 #include <sstream> 5 #include <string> 6 #include <vector> 7 8 namespace photos_editing_formats { 9 namespace image_io { 10 11 /// A very simple writer forXML that frees client code from worries about XML 12 /// formatting and bracket issues. 13 /// 14 /// The intended sequence of operations this writer supports is as follows: 15 /// 1. Start writing an element. 16 /// 2. Write any and all attribute names and values to that element. 17 /// 3. Write any content, or add a child element by starting to write another 18 /// element (i.e., go to step 1). The "context" of the current element you 19 /// are writing is saved on a stack. Once you start writing content or 20 /// child elements you cannot add attribute names and values and expect to 21 /// see them as such in the resulting XML. 22 /// 4. When you are done with the element, finish writing it. The element 23 /// context stack is popped and you continue where you left off. 24 /// 25 /// When writing element content and attribute values no XML escaping of any 26 /// kind is done. If you need to do that, do it yourself. 27 class XmlWriter { 28 public: 29 /// @param os The stream to which the XML is written. 30 explicit XmlWriter(std::ostream& os); 31 32 /// @return The number of elements that have been written. GetElementCount()33 size_t GetElementCount() const { return element_count_; } 34 35 /// @return The depth of the element stack. GetElementDepth()36 size_t GetElementDepth() const { return element_data_.size(); } 37 38 /// @return The quote mark used when writing attribute values. The default 39 /// value set up by the constructor is the double quote ("). GetQuoteMark()40 char GetQuoteMark() const { return quote_mark_; } 41 42 /// @param quote_park The new quote mark to use when writing attribute values. SetQuoteMark(char quote_mark)43 void SetQuoteMark(char quote_mark) { quote_mark_ = quote_mark; } 44 45 /// @return The leading indent written before the current element. GetIndent()46 const std::string& GetIndent() const { return indent_; } 47 48 /// Once you are done writing your elements, you can call this function to 49 /// finish writing of all open elements. After this call, the string contained 50 /// in the ostream you passed to the constructor is fully formed XML. FinishWriting()51 void FinishWriting() { FinishWritingElementsToDepth(0); } 52 53 /// @return Whether the writing of XML can be considered done. IsDone()54 bool IsDone() const { return indent_.empty(); } 55 56 /// Writes an xmlns attribute to the currently open element. 57 /// @param prefix The prefix you intend to use for elements/attributes. 58 /// @param uri The uri of the namespace. 59 void WriteXmlns(const std::string& prefix, const std::string& uri); 60 61 /// Starts writing a new child element of the current element. Immediately 62 /// after this function you can add attributes to the element using one of the 63 /// AddAttributeNameAndValue() functions. 64 /// @param element_name The name of the element to write. 65 /// @return The number of open elements on the stack at the start of this 66 /// function. You can use this value with the FinishWritingElementToDepth() 67 /// function to finish writing this element and any open descendents. 68 size_t StartWritingElement(const std::string& element_name); 69 70 /// Finishes writing the element and returns the "context" to the previously 71 /// open element so that you can continue adding child elements (via a call to 72 /// StartWritingElement()) or content (via a call to WriteContent()). 73 void FinishWritingElement(); 74 75 /// Finishes writing any elements that exist in the stack of open elements 76 /// above the depth value parameter. 77 /// @param depth The depth above which to finish writing open elements. 78 void FinishWritingElementsToDepth(size_t depth); 79 80 /// Starts writing the elements in the vector, leaving the last open for you 81 /// to add attributes or other elements to. 82 /// @param element_names The array of element names to start writing. 83 /// @return The number of open elements on the stack at the start of this 84 /// function. You can use this value with the FinishWritingElementToDepth() 85 /// function to finish writing this element and any open descendents. 86 size_t StartWritingElements(const std::vector<std::string>& element_names); 87 88 /// A template method function that allows you to start an element, add the 89 /// value as its content and then finish writing the element. This is useful 90 /// if you are writing property values as elements. 91 /// @param element_name The name of the element to write. 92 /// @param value The value that is converted to a string and written as the 93 /// element's content. 94 template <class T> WriteElementAndContent(const std::string & element_name,const T & value)95 void WriteElementAndContent(const std::string& element_name, const T& value) { 96 std::stringstream ss; 97 ss << value; 98 WriteElementAndContent(element_name, ss.str()); 99 } 100 101 /// Starts writing an element with the given name, adds the string value as 102 /// its content and then finishes writing the element. This is useful 103 /// if you are writing property values as elements. 104 /// @param element_name The name of the element to write. 105 /// @param value The value to use as the element's content. 106 void WriteElementAndContent(const std::string& element_name, 107 const std::string& content); 108 109 /// Writes the string as the currently open element's content. Note that if 110 /// you add child elements to the open element, the content you will see when 111 /// you read your element will have the whitespace due to the indent string. 112 /// @param content The content to write to the currently open element. 113 void WriteContent(const std::string& content); 114 115 /// A template method function that allows you to add an attribute name and 116 /// value to a just-opened element. Attributes must be added to an element 117 /// before adding content or child elements. 118 /// @param name The name of the attribute to add. 119 /// @param value The value of the attribute. This value is converted to a 120 /// string and enclosed in the quote marks from the GetQuoteMark() function. 121 template <class T> WriteAttributeNameAndValue(const std::string & name,const T & value)122 void WriteAttributeNameAndValue(const std::string& name, const T& value) { 123 std::stringstream ss; 124 ss << GetQuoteMark() << value << GetQuoteMark(); 125 WriteAttributeNameAndValue(name, ss.str(), false); 126 } 127 128 /// Adds an attribute name and value to a just-opened element. Attributes must 129 /// be added to an element before adding content or child elements. 130 /// @param name The name of the attribute to add. 131 /// @param value The value of the attribute. 132 /// @param add_quote_marks Whether quote marks should be added before and 133 /// after the value. If this value is false, it is assumed that the client 134 /// code has added them before calling this function. 135 void WriteAttributeNameAndValue(const std::string& name, 136 const std::string& value, 137 bool add_quote_marks = true); 138 139 /// Adds an attribute name and equal sign to the just-opened element. 140 /// Attributes must be added to an element before adding content or child 141 /// elements. Clients that use this function must call WriteAttributeValue() 142 /// with appropriate values to define a legally quoted value. This function 143 /// is useful for writing attribute with extremely long values that might not 144 /// be efficient to store as a single string value. 145 /// @param name The name of the attribute to add. 146 void WriteAttributeName(const std::string& name); 147 148 /// Writes the attribute value with optional quote marks on either side. This 149 /// function may be repeatedly called with appropriate valeus for the leading 150 /// and trailing quote mark flags to write extremely long attribute values. 151 /// @param add_leading_quote_mark Whether to add a leading quote mark. 152 /// @param value The (probably partial) value to write. 153 /// @param add_trailing_quote_mark Whether to add a trailing quote mark. 154 void WriteAttributeValue(bool add_leading_quote_mark, 155 const std::string& value, 156 bool add_trailing_quote_mark); 157 158 /// Writes a comment to the xml stream. Note that writing a comment is like 159 /// adding a child node/element to the current element. If the current element 160 /// is still open for names/values, it will be closed before writing it - i.e. 161 /// you can't add attributes to an element after calling this function. 162 /// @param comment The text of the comment to write. 163 void WriteComment(const std::string& comment); 164 165 private: 166 /// The data that is known about each element on the stack. 167 struct ElementData { ElementDataElementData168 ElementData(const std::string& name_) 169 : name(name_), 170 has_attributes(false), 171 has_content(false), 172 has_children(false) {} 173 std::string name; 174 bool has_attributes; 175 bool has_content; 176 bool has_children; 177 }; 178 179 /// Determines if the start element syntax of the current element needs to 180 /// be closed with a bracket so that content or child elements or comments 181 /// can be added to the element. 182 /// @param with_trailing_newline Whether a newline is added after the bracket. 183 /// @return Whether the element's start syntax was closed with a bracket. 184 bool MaybeWriteCloseBracket(bool with_trailing_newline); 185 186 /// The stream to which everything is written. 187 std::ostream& os_; 188 189 /// The indent to write before elements and attribute names/values. 190 std::string indent_; 191 192 /// The currently open elements being written. 193 std::vector<ElementData> element_data_; 194 195 /// The number of elements that have been written. 196 size_t element_count_; 197 198 /// The quote mark to use around attribute values by default. 199 char quote_mark_; 200 }; 201 202 } // namespace image_io 203 } // namespace photos_editing_formats 204 205 #endif // IMAGE_IO_XML_XML_WRITER_H_ // NOLINT 206