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