| /* | |
| * 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 "src/xml/SkXMLWriter.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/private/base/SkTo.h" | |
| SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup) | |
| {} | |
| SkXMLWriter::~SkXMLWriter() { | |
| SkASSERT(fElems.empty()); | |
| } | |
| void SkXMLWriter::flush() { | |
| while (!fElems.empty()) { | |
| 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::addText(const char text[], size_t length) { | |
| if (fElems.empty()) { | |
| return; | |
| } | |
| this->onAddText(text, length); | |
| fElems.back()->fHasText = true; | |
| } | |
| void SkXMLWriter::doEnd(Elem* elem) { | |
| delete elem; | |
| } | |
| bool SkXMLWriter::doStart(const char name[], size_t length) { | |
| int level = fElems.size(); | |
| bool firstChild = level > 0 && !fElems[level-1]->fHasChildren; | |
| if (firstChild) { | |
| fElems[level-1]->fHasChildren = true; | |
| } | |
| Elem** elem = fElems.append(); | |
| *elem = new Elem(name, length); | |
| return firstChild; | |
| } | |
| SkXMLWriter::Elem* SkXMLWriter::getEnd() { | |
| Elem* elem = fElems.back(); | |
| fElems.pop_back(); | |
| 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 < std::size(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(nullptr, value, length); | |
| if (extra) { | |
| valueStr.resize(length + extra); | |
| (void)escape_markup(valueStr.data(), 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) { | |
| const char* elem = dom.getName(node); | |
| if (dom.getType(node) == SkDOM::kText_Type) { | |
| SkASSERT(dom.countChildren(node) == 0); | |
| w->addText(elem, strlen(elem)); | |
| return; | |
| } | |
| w->startElement(elem); | |
| SkDOM::AttrIter iter(dom, node); | |
| const char* name; | |
| const char* value; | |
| while ((name = iter.next(&value)) != nullptr) { | |
| w->addAttribute(name, value); | |
| } | |
| } | |
| node = dom.getFirstChild(node, nullptr); | |
| while (node) { | |
| write_dom(dom, node, w, false); | |
| node = dom.getNextSibling(node, nullptr); | |
| } | |
| 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 | |
| SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream, uint32_t flags) | |
| : fStream(*stream) | |
| , fFlags(flags) {} | |
| SkXMLStreamWriter::~SkXMLStreamWriter() { | |
| this->flush(); | |
| } | |
| void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length) { | |
| SkASSERT(!fElems.back()->fHasChildren && !fElems.back()->fHasText); | |
| fStream.writeText(" "); | |
| fStream.writeText(name); | |
| fStream.writeText("=\""); | |
| fStream.write(value, length); | |
| fStream.writeText("\""); | |
| } | |
| void SkXMLStreamWriter::onAddText(const char text[], size_t length) { | |
| Elem* elem = fElems.back(); | |
| if (!elem->fHasChildren && !elem->fHasText) { | |
| fStream.writeText(">"); | |
| this->newline(); | |
| } | |
| this->tab(fElems.size() + 1); | |
| fStream.write(text, length); | |
| this->newline(); | |
| } | |
| void SkXMLStreamWriter::onEndElement() { | |
| Elem* elem = getEnd(); | |
| if (elem->fHasChildren || elem->fHasText) { | |
| this->tab(fElems.size()); | |
| fStream.writeText("</"); | |
| fStream.writeText(elem->fName.c_str()); | |
| fStream.writeText(">"); | |
| } else { | |
| fStream.writeText("/>"); | |
| } | |
| this->newline(); | |
| doEnd(elem); | |
| } | |
| void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length) { | |
| int level = fElems.size(); | |
| if (this->doStart(name, length)) { | |
| // the first child, need to close with > | |
| fStream.writeText(">"); | |
| this->newline(); | |
| } | |
| this->tab(level); | |
| fStream.writeText("<"); | |
| fStream.write(name, length); | |
| } | |
| void SkXMLStreamWriter::writeHeader() { | |
| const char* header = getHeader(); | |
| fStream.write(header, strlen(header)); | |
| this->newline(); | |
| } | |
| void SkXMLStreamWriter::newline() { | |
| if (!(fFlags & kNoPretty_Flag)) { | |
| fStream.newline(); | |
| } | |
| } | |
| void SkXMLStreamWriter::tab(int level) { | |
| if (!(fFlags & kNoPretty_Flag)) { | |
| for (int i = 0; i < level; i++) { | |
| fStream.writeText("\t"); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////////////////////////////////////////////////////////////// | |
| #include "src/xml/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.empty() || (!fElems.back()->fHasChildren && !fElems.back()->fHasText)); | |
| SkString str(value, length); | |
| fParser.addAttribute(name, str.c_str()); | |
| } | |
| void SkXMLParserWriter::onAddText(const char text[], size_t length) { | |
| fParser.text(text, SkToInt(length)); | |
| } | |
| 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()); | |
| } |