blob: 4685d81674a750af8fff4551b789050d3eadbf4d [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/xml/SkXMLWriter.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkXMLWriter.h"
19#include "SkStream.h"
20
21SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
22{
23}
24
25SkXMLWriter::~SkXMLWriter()
26{
27 SkASSERT(fElems.count() == 0);
28}
29
30void SkXMLWriter::flush()
31{
32 while (fElems.count())
33 this->endElement();
34}
35
36void SkXMLWriter::addAttribute(const char name[], const char value[])
37{
38 this->addAttributeLen(name, value, strlen(value));
39}
40
41void SkXMLWriter::addS32Attribute(const char name[], int32_t value)
42{
43 SkString tmp;
44 tmp.appendS32(value);
45 this->addAttribute(name, tmp.c_str());
46}
47
48void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits)
49{
50 SkString tmp("0x");
51 tmp.appendHex(value, minDigits);
52 this->addAttribute(name, tmp.c_str());
53}
54
55void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
56{
57 SkString tmp;
58 tmp.appendScalar(value);
59 this->addAttribute(name, tmp.c_str());
60}
61
62void SkXMLWriter::doEnd(Elem* elem)
63{
64 delete elem;
65}
66
67bool SkXMLWriter::doStart(const char name[], size_t length)
68{
69 int level = fElems.count();
70 bool firstChild = level > 0 && !fElems[level-1]->fHasChildren;
71 if (firstChild)
72 fElems[level-1]->fHasChildren = true;
73 Elem** elem = fElems.push();
74 *elem = new Elem;
75 (*elem)->fName.set(name, length);
76 (*elem)->fHasChildren = 0;
77 return firstChild;
78}
79
80SkXMLWriter::Elem* SkXMLWriter::getEnd()
81{
82 Elem* elem;
83 fElems.pop(&elem);
84 return elem;
85}
86
87const char* SkXMLWriter::getHeader()
88{
89 static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
90 return gHeader;
91}
92
93void SkXMLWriter::startElement(const char name[])
94{
95 this->startElementLen(name, strlen(name));
96}
97
98static const char* escape_char(char c, char storage[2])
99{
100 static const char* gEscapeChars[] = {
101 "<&lt;",
102 ">&gt;",
103 //"\"&quot;",
104 //"'&apos;",
105 "&&amp;"
106 };
107
108 const char** array = gEscapeChars;
109 for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++)
110 {
111 if (array[i][0] == c)
112 return &array[i][1];
113 }
114 storage[0] = c;
115 storage[1] = 0;
116 return storage;
117}
118
119static size_t escape_markup(char dst[], const char src[], size_t length)
120{
121 size_t extra = 0;
122 const char* stop = src + length;
123
124 while (src < stop)
125 {
126 char orig[2];
127 const char* seq = escape_char(*src, orig);
128 size_t seqSize = strlen(seq);
129
130 if (dst)
131 {
132 memcpy(dst, seq, seqSize);
133 dst += seqSize;
134 }
135
136 // now record the extra size needed
137 extra += seqSize - 1; // minus one to subtract the original char
138
139 // bump to the next src char
140 src += 1;
141 }
142 return extra;
143}
144
145void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length)
146{
147 SkString valueStr;
148
149 if (fDoEscapeMarkup)
150 {
151 size_t extra = escape_markup(NULL, value, length);
152 if (extra)
153 {
154 valueStr.resize(length + extra);
155 (void)escape_markup(valueStr.writable_str(), value, length);
156 value = valueStr.c_str();
157 length += extra;
158 }
159 }
160 this->onAddAttributeLen(name, value, length);
161}
162
163void SkXMLWriter::startElementLen(const char elem[], size_t length)
164{
165 this->onStartElementLen(elem, length);
166}
167
168////////////////////////////////////////////////////////////////////////////////////////
169
170static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot)
171{
172 if (!skipRoot)
173 {
174 w->startElement(dom.getName(node));
175
176 SkDOM::AttrIter iter(dom, node);
177 const char* name;
178 const char* value;
179 while ((name = iter.next(&value)) != NULL)
180 w->addAttribute(name, value);
181 }
182
183 node = dom.getFirstChild(node, NULL);
184 while (node)
185 {
186 write_dom(dom, node, w, false);
187 node = dom.getNextSibling(node, NULL);
188 }
189
190 if (!skipRoot)
191 w->endElement();
192}
193
194void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
195{
196 if (node)
197 write_dom(dom, node, this, skipRoot);
198}
199
200void SkXMLWriter::writeHeader()
201{
202}
203
204// SkXMLStreamWriter
205
206static void tab(SkWStream& stream, int level)
207{
208 for (int i = 0; i < level; i++)
209 stream.writeText("\t");
210}
211
212SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
213{
214}
215
216SkXMLStreamWriter::~SkXMLStreamWriter()
217{
218 this->flush();
219}
220
221void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
222{
223 SkASSERT(!fElems.top()->fHasChildren);
224 fStream.writeText(" ");
225 fStream.writeText(name);
226 fStream.writeText("=\"");
227 fStream.write(value, length);
228 fStream.writeText("\"");
229}
230
231void SkXMLStreamWriter::onEndElement()
232{
233 Elem* elem = getEnd();
234 if (elem->fHasChildren)
235 {
236 tab(fStream, fElems.count());
237 fStream.writeText("</");
238 fStream.writeText(elem->fName.c_str());
239 fStream.writeText(">");
240 }
241 else
242 fStream.writeText("/>");
243 fStream.newline();
244 doEnd(elem);
245}
246
247void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length)
248{
249 int level = fElems.count();
250 if (this->doStart(name, length))
251 {
252 // the first child, need to close with >
253 fStream.writeText(">");
254 fStream.newline();
255 }
256
257 tab(fStream, level);
258 fStream.writeText("<");
259 fStream.write(name, length);
260}
261
262void SkXMLStreamWriter::writeHeader()
263{
264 const char* header = getHeader();
265 fStream.write(header, strlen(header));
266 fStream.newline();
267}
268
269////////////////////////////////////////////////////////////////////////////////////////////////
270
271#include "SkXMLParser.h"
272
273SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
274 : SkXMLWriter(false), fParser(*parser)
275{
276}
277
278SkXMLParserWriter::~SkXMLParserWriter()
279{
280 this->flush();
281}
282
283void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
284{
285 SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren);
286 SkString str(value, length);
287 fParser.addAttribute(name, str.c_str());
288}
289
290void SkXMLParserWriter::onEndElement()
291{
292 Elem* elem = this->getEnd();
293 fParser.endElement(elem->fName.c_str());
294 this->doEnd(elem);
295}
296
297void SkXMLParserWriter::onStartElementLen(const char name[], size_t length)
298{
299 (void)this->doStart(name, length);
300 SkString str(name, length);
301 fParser.startElement(str.c_str());
302}
303
304
305////////////////////////////////////////////////////////////////////////////////////////
306////////////////////////////////////////////////////////////////////////////////////////
307
308#ifdef SK_DEBUG
309
310void SkXMLStreamWriter::UnitTest()
311{
312#ifdef SK_SUPPORT_UNITTEST
313 SkDebugWStream s;
314 SkXMLStreamWriter w(&s);
315
316 w.startElement("elem0");
317 w.addAttribute("hello", "world");
318 w.addS32Attribute("dec", 42);
319 w.addHexAttribute("hex", 0x42, 3);
320#ifdef SK_SCALAR_IS_FLOAT
321 w.addScalarAttribute("scalar", -4.2f);
322#endif
323 w.startElement("elem1");
324 w.endElement();
325 w.startElement("elem1");
326 w.addAttribute("name", "value");
327 w.endElement();
328 w.startElement("elem1");
329 w.startElement("elem2");
330 w.startElement("elem3");
331 w.addAttribute("name", "value");
332 w.endElement();
333 w.endElement();
334 w.startElement("elem2");
335 w.endElement();
336 w.endElement();
337 w.endElement();
338#endif
339}
340
341#endif
342