1 // Copyright 2008, Google Inc. All rights reserved. 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are met: 5 // 6 // 1. Redistributions of source code must retain the above copyright notice, 7 // this list of conditions and the following disclaimer. 8 // 2. Redistributions in binary form must reproduce the above copyright notice, 9 // this list of conditions and the following disclaimer in the documentation 10 // and/or other materials provided with the distribution. 11 // 3. Neither the name of Google Inc. nor the names of its contributors may be 12 // used to endorse or promote products derived from this software without 13 // specific prior written permission. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 // This file contains the declaration of the internal XmlSerializer class. 27 // NOTE: This class is internal to libkml and is not intended for use in 28 // client code outside libkml. 29 30 #ifndef KML_DOM_XML_SERIALIZER_H__ 31 #define KML_DOM_XML_SERIALIZER_H__ 32 33 #include <ostream> 34 #include <stack> 35 #include <vector> 36 #include "kml/base/attributes.h" 37 #include "kml/base/vec3.h" 38 #include "kml/dom/serializer.h" 39 #include "kml/dom/xsd.h" 40 #include "kml/dom.h" 41 42 namespace kmldom { 43 44 // A poor man's std::ostringstream. Note that this uses a non-namespace 45 // qualified string. See base/util.h for more info about string vs std::string. 46 class StringAdapter { 47 public: StringAdapter(string * str)48 StringAdapter(string* str) 49 : str_(str) { 50 } 51 write(const char * s,size_t n)52 void write(const char* s, size_t n) { 53 str_->append(s, n); 54 } 55 put(char c)56 void put(char c) { 57 str_->append(1, c); 58 } 59 private: 60 string* str_; 61 }; 62 63 // T should match this signature: 64 // class T { 65 // public: 66 // void write(const char*, size_t); 67 // void put(char c); 68 // }; 69 // C++ std::ostream matches T 70 template<class T> 71 class XmlSerializer : public Serializer { 72 public: Serialize(const ElementPtr & root,const char * newline,const char * indent,T * output)73 static void Serialize(const ElementPtr& root, const char* newline, 74 const char* indent, T* output) { 75 if (!root || !newline || !indent || !output) { 76 return; 77 } 78 boost::scoped_ptr<XmlSerializer> xml_ostream_serializer( 79 new XmlSerializer(newline, indent, output)); 80 root->Serialize(*xml_ostream_serializer); 81 } 82 83 // Construct a serializer with the given strings for line breaks and 84 // indentation. The indent string is used once for each level of 85 // indentation. For no line break and/or indent whitespace use "". This is 86 // primarily for unit testing. Use SerializePrettyToBase whenever 87 // possible. Use kmldom::SerializeToBase() external client code. XmlSerializer(const char * newline,const char * indent,T * output)88 XmlSerializer(const char* newline, const char* indent, T* output) 89 : newline_(newline), 90 indent_(indent), 91 output_(output), 92 start_pending_(false) { 93 } 94 ~XmlSerializer()95 virtual ~XmlSerializer() {} 96 97 // Emit the start tag of the given element: <Placemark id="pm123">. BeginById(int type_id,const kmlbase::Attributes & attributes)98 virtual void BeginById(int type_id, const kmlbase::Attributes& attributes) { 99 // Here we just record the element we're starting and its attributes if 100 // it has any. The "<TAGNAME [name="VAL" ...]..." are not emmited until 101 // it is known if this is a nil element or not. 102 EmitStart(false); 103 Indent(); 104 tag_stack_.push(type_id); // So we know what tag to use in End(). 105 if (attributes.GetSize() > 0) { 106 // TODO: Attributes::SerializeToBase would be handy. 107 attributes.Serialize(&serialized_attributes_); 108 } 109 start_pending_ = true; 110 } 111 112 // Emit the end tag of the given element: </Placemark>. End()113 virtual void End() { 114 int type_id = tag_stack_.top(); 115 // TODO: make this less fiddly 116 if (EmitStart(true)) { 117 tag_stack_.pop(); 118 } else { 119 tag_stack_.pop(); 120 Indent(); 121 output_->write("</", 2); 122 const string& tag_name = xsd_.ElementName(type_id); 123 output_->write(tag_name.data(), tag_name.size()); 124 output_->put('>'); 125 Newline(); 126 } 127 } 128 129 // Emit the XML for the field of the given type with the given content 130 // as its character data. If value is empty a nil element is emitted. SaveStringFieldById(int type_id,string value)131 virtual void SaveStringFieldById(int type_id, string value) { 132 EmitStart(false); 133 Indent(); 134 const string& tag_name = xsd_.ElementName(type_id); 135 output_->put('<'); 136 output_->write(tag_name.data(), tag_name.size()); 137 if (value.empty()) { // Special case to emit <TAGNAME/> 138 output_->put('/'); 139 } else { // <TAGNAME>VALUE</TAGNAME> 140 output_->put('>'); 141 WriteQuoted(value); 142 output_->write("</", 2); 143 output_->write(tag_name.data(), tag_name.size()); 144 } 145 output_->put('>'); 146 Newline(); 147 } 148 149 // Save out character data. SaveContent(const string & content,bool maybe_quote)150 virtual void SaveContent(const string& content, bool maybe_quote) { 151 EmitStart(false); 152 if (maybe_quote) { 153 WriteQuoted(content); 154 } else { 155 output_->write(content.data(), content.size()); 156 } 157 } 158 159 // Save a lon,lat,alt tuple as appears within <coordinates>. SaveVec3(const kmlbase::Vec3 & vec3)160 virtual void SaveVec3(const kmlbase::Vec3& vec3) { 161 EmitStart(false); 162 Indent(); 163 string val = kmlbase::ToString(vec3.get_longitude()); 164 output_->write(val.data(), val.size()); 165 output_->put(','); 166 val = kmlbase::ToString(vec3.get_latitude()); 167 output_->write(val.data(), val.size()); 168 // Ideally, we'd only emit if vec3.has_altitude(), but lots of test cases 169 // expect lon,lat,0 170 output_->put(','); 171 val = kmlbase::ToString(vec3.get_altitude()); 172 output_->write(val.data(), val.size()); 173 // In libkml 1.2 a "\n" was baked into Serializer::SaveVec3. We emit an 174 // explicit "\n" for compatibility instead of calling Newline() because 175 // Newline() could be an empty string which would effectively concatenate 176 // coordinates items in SerializeRaw. 177 if (newline_.empty()) { 178 output_->write("\n", 1); 179 } else { 180 Newline(); 181 } 182 } 183 184 // Save a Color32 value as its AABBGGRR representation. SaveColor(int type_id,const kmlbase::Color32 & color)185 virtual void SaveColor(int type_id, const kmlbase::Color32& color) { 186 EmitStart(false); 187 SaveFieldById(type_id, color.to_string_abgr()); 188 } 189 190 // Emit one level of indentation. Indent()191 virtual void Indent() { 192 if (!indent_.empty()) { 193 size_t depth = tag_stack_.size(); 194 while (depth--) { 195 output_->write(indent_.data(), indent_.size()); 196 } 197 } 198 } 199 200 private: 201 // Emit a line break. Newline()202 void Newline() { 203 if (!newline_.empty()) { 204 output_->write(newline_.data(), newline_.size()); 205 } 206 } 207 208 // Emit quoted. See Serializer::MaybeQuoteString(). WriteQuoted(const string & value)209 void WriteQuoted(const string& value) { 210 string quoted = MaybeQuoteString(value); 211 output_->write(quoted.data(), quoted.size()); 212 } 213 EmitStart(bool is_nil)214 bool EmitStart(bool is_nil) { 215 if (!start_pending_) { 216 return false; 217 } 218 output_->put('<'); 219 const string& tag_name = xsd_.ElementName(tag_stack_.top()); 220 output_->write(tag_name.data(), tag_name.size()); 221 if (!serialized_attributes_.empty()) { 222 output_->write(serialized_attributes_.data(), 223 serialized_attributes_.size()); 224 serialized_attributes_.clear(); 225 } 226 if (is_nil) { 227 output_->write("/>", 2); 228 } else { 229 output_->put('>'); 230 } 231 Newline(); 232 start_pending_ = false; 233 return true; 234 } 235 236 const string newline_; 237 const string indent_; 238 T* output_; 239 std::stack<int> tag_stack_; 240 bool start_pending_; 241 string serialized_attributes_; 242 }; 243 244 } // end namespace kmldom 245 246 #endif // KML_DOM_XML_SERIALIZER_H__ 247