| /* |
| * 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()); |
| } |