| /* | |
| * 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); | |
| w.addScalarAttribute("scalar", -4.2f); | |
| 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 |