grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..4685d81
--- /dev/null
+++ b/src/xml/SkXMLWriter.cpp
@@ -0,0 +1,342 @@
+/* libs/graphics/xml/SkXMLWriter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#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[] = {
+        "<&lt;",
+        ">&gt;",
+        //"\"&quot;",
+        //"'&apos;",
+        "&&amp;"
+    };
+
+    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
+