|  | 
 | /* | 
 |  * Copyright 2006 The Android Open Source Project | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 |  | 
 | #include "SkXMLWriter.h" | 
 | #include "SkStream.h" | 
 |  | 
 | SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup) | 
 | { | 
 | } | 
 |  | 
 | SkXMLWriter::~SkXMLWriter() | 
 | { | 
 |     SkASSERT(fElems.count() == 0); | 
 | } | 
 |  | 
 | void SkXMLWriter::flush() | 
 | { | 
 |     while (fElems.count()) | 
 |         this->endElement(); | 
 | } | 
 |  | 
 | void SkXMLWriter::addAttribute(const char name[], const char value[]) | 
 | { | 
 |     this->addAttributeLen(name, value, strlen(value)); | 
 | } | 
 |  | 
 | void SkXMLWriter::addS32Attribute(const char name[], int32_t value) | 
 | { | 
 |     SkString    tmp; | 
 |     tmp.appendS32(value); | 
 |     this->addAttribute(name, tmp.c_str()); | 
 | } | 
 |  | 
 | void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits) | 
 | { | 
 |     SkString    tmp("0x"); | 
 |     tmp.appendHex(value, minDigits); | 
 |     this->addAttribute(name, tmp.c_str()); | 
 | } | 
 |  | 
 | void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value) | 
 | { | 
 |     SkString    tmp; | 
 |     tmp.appendScalar(value); | 
 |     this->addAttribute(name, tmp.c_str()); | 
 | } | 
 |  | 
 | void SkXMLWriter::doEnd(Elem* elem) | 
 | { | 
 |     delete elem; | 
 | } | 
 |  | 
 | bool SkXMLWriter::doStart(const char name[], size_t length) | 
 | { | 
 |     int level = fElems.count(); | 
 |     bool firstChild = level > 0 && !fElems[level-1]->fHasChildren; | 
 |     if (firstChild) | 
 |         fElems[level-1]->fHasChildren = true; | 
 |     Elem** elem = fElems.push(); | 
 |     *elem = new Elem; | 
 |     (*elem)->fName.set(name, length); | 
 |     (*elem)->fHasChildren = 0; | 
 |     return firstChild; | 
 | } | 
 |  | 
 | SkXMLWriter::Elem* SkXMLWriter::getEnd()  | 
 | { | 
 |     Elem* elem; | 
 |     fElems.pop(&elem); | 
 |     return elem; | 
 | } | 
 |  | 
 | const char* SkXMLWriter::getHeader() | 
 | { | 
 |     static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; | 
 |     return gHeader; | 
 | } | 
 |  | 
 | void SkXMLWriter::startElement(const char name[]) | 
 | { | 
 |     this->startElementLen(name, strlen(name)); | 
 | } | 
 |  | 
 | static const char* escape_char(char c, char storage[2]) | 
 | { | 
 |     static const char* gEscapeChars[] = { | 
 |         "<<", | 
 |         ">>", | 
 |         //"\""", | 
 |         //"''", | 
 |         "&&" | 
 |     }; | 
 |  | 
 |     const char** array = gEscapeChars; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++) | 
 |     { | 
 |         if (array[i][0] == c) | 
 |             return &array[i][1]; | 
 |     } | 
 |     storage[0] = c; | 
 |     storage[1] = 0; | 
 |     return storage; | 
 | } | 
 |  | 
 | static size_t escape_markup(char dst[], const char src[], size_t length) | 
 | { | 
 |     size_t      extra = 0; | 
 |     const char* stop = src + length; | 
 |  | 
 |     while (src < stop) | 
 |     { | 
 |         char        orig[2]; | 
 |         const char* seq = escape_char(*src, orig); | 
 |         size_t      seqSize = strlen(seq); | 
 |  | 
 |         if (dst) | 
 |         { | 
 |             memcpy(dst, seq, seqSize); | 
 |             dst += seqSize; | 
 |         } | 
 |  | 
 |         // now record the extra size needed | 
 |         extra += seqSize - 1;   // minus one to subtract the original char | 
 |  | 
 |         // bump to the next src char | 
 |         src += 1; | 
 |     } | 
 |     return extra; | 
 | } | 
 |  | 
 | void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length) | 
 | { | 
 |     SkString valueStr; | 
 |  | 
 |     if (fDoEscapeMarkup) | 
 |     { | 
 |         size_t   extra = escape_markup(NULL, value, length); | 
 |         if (extra) | 
 |         { | 
 |             valueStr.resize(length + extra); | 
 |             (void)escape_markup(valueStr.writable_str(), value, length); | 
 |             value = valueStr.c_str(); | 
 |             length += extra; | 
 |         } | 
 |     } | 
 |     this->onAddAttributeLen(name, value, length); | 
 | } | 
 |  | 
 | void SkXMLWriter::startElementLen(const char elem[], size_t length) | 
 | { | 
 |     this->onStartElementLen(elem, length); | 
 | } | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot) | 
 | { | 
 |     if (!skipRoot) | 
 |     { | 
 |         w->startElement(dom.getName(node)); | 
 |  | 
 |         SkDOM::AttrIter iter(dom, node); | 
 |         const char* name; | 
 |         const char* value; | 
 |         while ((name = iter.next(&value)) != NULL) | 
 |             w->addAttribute(name, value); | 
 |     } | 
 |  | 
 |     node = dom.getFirstChild(node, NULL); | 
 |     while (node) | 
 |     { | 
 |         write_dom(dom, node, w, false); | 
 |         node = dom.getNextSibling(node, NULL); | 
 |     } | 
 |  | 
 |     if (!skipRoot) | 
 |         w->endElement(); | 
 | } | 
 |  | 
 | void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot) | 
 | { | 
 |     if (node) | 
 |         write_dom(dom, node, this, skipRoot); | 
 | } | 
 |  | 
 | void SkXMLWriter::writeHeader() | 
 | { | 
 | } | 
 |  | 
 | // SkXMLStreamWriter | 
 |  | 
 | static void tab(SkWStream& stream, int level) | 
 | { | 
 |     for (int i = 0; i < level; i++) | 
 |         stream.writeText("\t"); | 
 | } | 
 |  | 
 | SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream) | 
 | { | 
 | } | 
 |  | 
 | SkXMLStreamWriter::~SkXMLStreamWriter() | 
 | { | 
 |     this->flush(); | 
 | } | 
 |  | 
 | void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length) | 
 | { | 
 |     SkASSERT(!fElems.top()->fHasChildren); | 
 |     fStream.writeText(" "); | 
 |     fStream.writeText(name); | 
 |     fStream.writeText("=\""); | 
 |     fStream.write(value, length); | 
 |     fStream.writeText("\""); | 
 | } | 
 |  | 
 | void SkXMLStreamWriter::onEndElement() | 
 | { | 
 |     Elem* elem = getEnd(); | 
 |     if (elem->fHasChildren) | 
 |     { | 
 |         tab(fStream, fElems.count()); | 
 |         fStream.writeText("</"); | 
 |         fStream.writeText(elem->fName.c_str()); | 
 |         fStream.writeText(">"); | 
 |     } | 
 |     else | 
 |         fStream.writeText("/>"); | 
 |     fStream.newline(); | 
 |     doEnd(elem); | 
 | } | 
 |  | 
 | void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length) | 
 | { | 
 |     int level = fElems.count(); | 
 |     if (this->doStart(name, length)) | 
 |     { | 
 |         // the first child, need to close with > | 
 |         fStream.writeText(">"); | 
 |         fStream.newline(); | 
 |     } | 
 |  | 
 |     tab(fStream, level); | 
 |     fStream.writeText("<"); | 
 |     fStream.write(name, length); | 
 | } | 
 |  | 
 | void SkXMLStreamWriter::writeHeader() | 
 | { | 
 |     const char* header = getHeader(); | 
 |     fStream.write(header, strlen(header)); | 
 |     fStream.newline(); | 
 | } | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #include "SkXMLParser.h" | 
 |  | 
 | SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser) | 
 |     : SkXMLWriter(false), fParser(*parser) | 
 | { | 
 | } | 
 |  | 
 | SkXMLParserWriter::~SkXMLParserWriter() | 
 | { | 
 |     this->flush(); | 
 | } | 
 |  | 
 | void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length) | 
 | { | 
 |     SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren); | 
 |     SkString str(value, length); | 
 |     fParser.addAttribute(name, str.c_str()); | 
 | } | 
 |  | 
 | void SkXMLParserWriter::onEndElement() | 
 | { | 
 |     Elem* elem = this->getEnd(); | 
 |     fParser.endElement(elem->fName.c_str()); | 
 |     this->doEnd(elem); | 
 | } | 
 |  | 
 | void SkXMLParserWriter::onStartElementLen(const char name[], size_t length) | 
 | { | 
 |     (void)this->doStart(name, length); | 
 |     SkString str(name, length); | 
 |     fParser.startElement(str.c_str()); | 
 | } | 
 |  | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////////////// | 
 | //////////////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #ifdef SK_DEBUG | 
 |  | 
 | void SkXMLStreamWriter::UnitTest() | 
 | { | 
 | #ifdef SK_SUPPORT_UNITTEST | 
 |     SkDebugWStream  s; | 
 |     SkXMLStreamWriter       w(&s); | 
 |  | 
 |     w.startElement("elem0"); | 
 |     w.addAttribute("hello", "world"); | 
 |     w.addS32Attribute("dec", 42); | 
 |     w.addHexAttribute("hex", 0x42, 3); | 
 | #ifdef SK_SCALAR_IS_FLOAT | 
 |     w.addScalarAttribute("scalar", -4.2f); | 
 | #endif | 
 |     w.startElement("elem1"); | 
 |         w.endElement(); | 
 |         w.startElement("elem1"); | 
 |         w.addAttribute("name", "value"); | 
 |         w.endElement(); | 
 |         w.startElement("elem1"); | 
 |             w.startElement("elem2"); | 
 |                 w.startElement("elem3"); | 
 |                 w.addAttribute("name", "value"); | 
 |                 w.endElement(); | 
 |             w.endElement(); | 
 |             w.startElement("elem2"); | 
 |             w.endElement(); | 
 |         w.endElement(); | 
 |     w.endElement(); | 
 | #endif | 
 | } | 
 |  | 
 | #endif | 
 |  |