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 21 SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup) 22 { 23 } 24 25 SkXMLWriter::~SkXMLWriter() 26 { 27 SkASSERT(fElems.count() == 0); 28 } 29 30 void SkXMLWriter::flush() 31 { 32 while (fElems.count()) 33 this->endElement(); 34 } 35 36 void SkXMLWriter::addAttribute(const char name[], const char value[]) 37 { 38 this->addAttributeLen(name, value, strlen(value)); 39 } 40 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 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 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 62 void SkXMLWriter::doEnd(Elem* elem) 63 { 64 delete elem; 65 } 66 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 80 SkXMLWriter::Elem* SkXMLWriter::getEnd() 81 { 82 Elem* elem; 83 fElems.pop(&elem); 84 return elem; 85 } 86 87 const char* SkXMLWriter::getHeader() 88 { 89 static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; 90 return gHeader; 91 } 92 93 void SkXMLWriter::startElement(const char name[]) 94 { 95 this->startElementLen(name, strlen(name)); 96 } 97 98 static const char* escape_char(char c, char storage[2]) 99 { 100 static const char* gEscapeChars[] = { 101 "<<", 102 ">>", 103 //"\""", 104 //"''", 105 "&&" 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 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 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 163 void SkXMLWriter::startElementLen(const char elem[], size_t length) 164 { 165 this->onStartElementLen(elem, length); 166 } 167 168 //////////////////////////////////////////////////////////////////////////////////////// 169 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 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 200 void SkXMLWriter::writeHeader() 201 { 202 } 203 204 // SkXMLStreamWriter 205 206 static void tab(SkWStream& stream, int level) 207 { 208 for (int i = 0; i < level; i++) 209 stream.writeText("\t"); 210 } 211 212 SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream) 213 { 214 } 215 216 SkXMLStreamWriter::~SkXMLStreamWriter() 217 { 218 this->flush(); 219 } 220 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 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 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 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 273 SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser) 274 : SkXMLWriter(false), fParser(*parser) 275 { 276 } 277 278 SkXMLParserWriter::~SkXMLParserWriter() 279 { 280 this->flush(); 281 } 282 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 290 void SkXMLParserWriter::onEndElement() 291 { 292 Elem* elem = this->getEnd(); 293 fParser.endElement(elem->fName.c_str()); 294 this->doEnd(elem); 295 } 296 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 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