• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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 
SkXMLWriter(bool doEscapeMarkup)21 SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
22 {
23 }
24 
~SkXMLWriter()25 SkXMLWriter::~SkXMLWriter()
26 {
27     SkASSERT(fElems.count() == 0);
28 }
29 
flush()30 void SkXMLWriter::flush()
31 {
32     while (fElems.count())
33         this->endElement();
34 }
35 
addAttribute(const char name[],const char value[])36 void SkXMLWriter::addAttribute(const char name[], const char value[])
37 {
38     this->addAttributeLen(name, value, strlen(value));
39 }
40 
addS32Attribute(const char name[],int32_t value)41 void 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 
addHexAttribute(const char name[],uint32_t value,int minDigits)48 void 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 
addScalarAttribute(const char name[],SkScalar value)55 void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
56 {
57     SkString    tmp;
58     tmp.appendScalar(value);
59     this->addAttribute(name, tmp.c_str());
60 }
61 
doEnd(Elem * elem)62 void SkXMLWriter::doEnd(Elem* elem)
63 {
64     delete elem;
65 }
66 
doStart(const char name[],size_t length)67 bool 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 
getEnd()80 SkXMLWriter::Elem* SkXMLWriter::getEnd()
81 {
82     Elem* elem;
83     fElems.pop(&elem);
84     return elem;
85 }
86 
getHeader()87 const char* SkXMLWriter::getHeader()
88 {
89     static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
90     return gHeader;
91 }
92 
startElement(const char name[])93 void SkXMLWriter::startElement(const char name[])
94 {
95     this->startElementLen(name, strlen(name));
96 }
97 
escape_char(char c,char storage[2])98 static 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 
escape_markup(char dst[],const char src[],size_t length)119 static 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 
addAttributeLen(const char name[],const char value[],size_t length)145 void 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 
startElementLen(const char elem[],size_t length)163 void SkXMLWriter::startElementLen(const char elem[], size_t length)
164 {
165     this->onStartElementLen(elem, length);
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////////////
169 
write_dom(const SkDOM & dom,const SkDOM::Node * node,SkXMLWriter * w,bool skipRoot)170 static 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 
writeDOM(const SkDOM & dom,const SkDOM::Node * node,bool skipRoot)194 void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
195 {
196     if (node)
197         write_dom(dom, node, this, skipRoot);
198 }
199 
writeHeader()200 void SkXMLWriter::writeHeader()
201 {
202 }
203 
204 // SkXMLStreamWriter
205 
tab(SkWStream & stream,int level)206 static void tab(SkWStream& stream, int level)
207 {
208     for (int i = 0; i < level; i++)
209         stream.writeText("\t");
210 }
211 
SkXMLStreamWriter(SkWStream * stream)212 SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
213 {
214 }
215 
~SkXMLStreamWriter()216 SkXMLStreamWriter::~SkXMLStreamWriter()
217 {
218     this->flush();
219 }
220 
onAddAttributeLen(const char name[],const char value[],size_t length)221 void 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 
onEndElement()231 void 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 
onStartElementLen(const char name[],size_t length)247 void 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 
writeHeader()262 void 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 
SkXMLParserWriter(SkXMLParser * parser)273 SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
274     : SkXMLWriter(false), fParser(*parser)
275 {
276 }
277 
~SkXMLParserWriter()278 SkXMLParserWriter::~SkXMLParserWriter()
279 {
280     this->flush();
281 }
282 
onAddAttributeLen(const char name[],const char value[],size_t length)283 void 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 
onEndElement()290 void SkXMLParserWriter::onEndElement()
291 {
292     Elem* elem = this->getEnd();
293     fParser.endElement(elem->fName.c_str());
294     this->doEnd(elem);
295 }
296 
onStartElementLen(const char name[],size_t length)297 void 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 
UnitTest()310 void 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 
343