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