Home | History | Annotate | Download | only in xml
      1 /* libs/graphics/xml/SkXMLWriter.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkXMLWriter.h"
     19 #include "SkStream.h"
     20 
     21 SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
     22 {
     23 }
     24 
     25 SkXMLWriter::~SkXMLWriter()
     26 {
     27     SkASSERT(fElems.count() == 0);
     28 }
     29 
     30 void SkXMLWriter::flush()
     31 {
     32     while (fElems.count())
     33         this->endElement();
     34 }
     35 
     36 void SkXMLWriter::addAttribute(const char name[], const char value[])
     37 {
     38     this->addAttributeLen(name, value, strlen(value));
     39 }
     40 
     41 void SkXMLWriter::addS32Attribute(const char name[], int32_t value)
     42 {
     43     SkString    tmp;
     44     tmp.appendS32(value);
     45     this->addAttribute(name, tmp.c_str());
     46 }
     47 
     48 void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits)
     49 {
     50     SkString    tmp("0x");
     51     tmp.appendHex(value, minDigits);
     52     this->addAttribute(name, tmp.c_str());
     53 }
     54 
     55 void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
     56 {
     57     SkString    tmp;
     58     tmp.appendScalar(value);
     59     this->addAttribute(name, tmp.c_str());
     60 }
     61 
     62 void SkXMLWriter::doEnd(Elem* elem)
     63 {
     64     delete elem;
     65 }
     66 
     67 bool SkXMLWriter::doStart(const char name[], size_t length)
     68 {
     69     int level = fElems.count();
     70     bool firstChild = level > 0 && !fElems[level-1]->fHasChildren;
     71     if (firstChild)
     72         fElems[level-1]->fHasChildren = true;
     73     Elem** elem = fElems.push();
     74     *elem = new Elem;
     75     (*elem)->fName.set(name, length);
     76     (*elem)->fHasChildren = 0;
     77     return firstChild;
     78 }
     79 
     80 SkXMLWriter::Elem* SkXMLWriter::getEnd()
     81 {
     82     Elem* elem;
     83     fElems.pop(&elem);
     84     return elem;
     85 }
     86 
     87 const char* SkXMLWriter::getHeader()
     88 {
     89     static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
     90     return gHeader;
     91 }
     92 
     93 void SkXMLWriter::startElement(const char name[])
     94 {
     95     this->startElementLen(name, strlen(name));
     96 }
     97 
     98 static const char* escape_char(char c, char storage[2])
     99 {
    100     static const char* gEscapeChars[] = {
    101         "<&lt;",
    102         ">&gt;",
    103         //"\"&quot;",
    104         //"'&apos;",
    105         "&&amp;"
    106     };
    107 
    108     const char** array = gEscapeChars;
    109     for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++)
    110     {
    111         if (array[i][0] == c)
    112             return &array[i][1];
    113     }
    114     storage[0] = c;
    115     storage[1] = 0;
    116     return storage;
    117 }
    118 
    119 static size_t escape_markup(char dst[], const char src[], size_t length)
    120 {
    121     size_t      extra = 0;
    122     const char* stop = src + length;
    123 
    124     while (src < stop)
    125     {
    126         char        orig[2];
    127         const char* seq = escape_char(*src, orig);
    128         size_t      seqSize = strlen(seq);
    129 
    130         if (dst)
    131         {
    132             memcpy(dst, seq, seqSize);
    133             dst += seqSize;
    134         }
    135 
    136         // now record the extra size needed
    137         extra += seqSize - 1;   // minus one to subtract the original char
    138 
    139         // bump to the next src char
    140         src += 1;
    141     }
    142     return extra;
    143 }
    144 
    145 void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length)
    146 {
    147     SkString valueStr;
    148 
    149     if (fDoEscapeMarkup)
    150     {
    151         size_t   extra = escape_markup(NULL, value, length);
    152         if (extra)
    153         {
    154             valueStr.resize(length + extra);
    155             (void)escape_markup(valueStr.writable_str(), value, length);
    156             value = valueStr.c_str();
    157             length += extra;
    158         }
    159     }
    160     this->onAddAttributeLen(name, value, length);
    161 }
    162 
    163 void SkXMLWriter::startElementLen(const char elem[], size_t length)
    164 {
    165     this->onStartElementLen(elem, length);
    166 }
    167 
    168 ////////////////////////////////////////////////////////////////////////////////////////
    169 
    170 static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot)
    171 {
    172     if (!skipRoot)
    173     {
    174         w->startElement(dom.getName(node));
    175 
    176         SkDOM::AttrIter iter(dom, node);
    177         const char* name;
    178         const char* value;
    179         while ((name = iter.next(&value)) != NULL)
    180             w->addAttribute(name, value);
    181     }
    182 
    183     node = dom.getFirstChild(node, NULL);
    184     while (node)
    185     {
    186         write_dom(dom, node, w, false);
    187         node = dom.getNextSibling(node, NULL);
    188     }
    189 
    190     if (!skipRoot)
    191         w->endElement();
    192 }
    193 
    194 void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
    195 {
    196     if (node)
    197         write_dom(dom, node, this, skipRoot);
    198 }
    199 
    200 void SkXMLWriter::writeHeader()
    201 {
    202 }
    203 
    204 // SkXMLStreamWriter
    205 
    206 static void tab(SkWStream& stream, int level)
    207 {
    208     for (int i = 0; i < level; i++)
    209         stream.writeText("\t");
    210 }
    211 
    212 SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
    213 {
    214 }
    215 
    216 SkXMLStreamWriter::~SkXMLStreamWriter()
    217 {
    218     this->flush();
    219 }
    220 
    221 void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
    222 {
    223     SkASSERT(!fElems.top()->fHasChildren);
    224     fStream.writeText(" ");
    225     fStream.writeText(name);
    226     fStream.writeText("=\"");
    227     fStream.write(value, length);
    228     fStream.writeText("\"");
    229 }
    230 
    231 void SkXMLStreamWriter::onEndElement()
    232 {
    233     Elem* elem = getEnd();
    234     if (elem->fHasChildren)
    235     {
    236         tab(fStream, fElems.count());
    237         fStream.writeText("</");
    238         fStream.writeText(elem->fName.c_str());
    239         fStream.writeText(">");
    240     }
    241     else
    242         fStream.writeText("/>");
    243     fStream.newline();
    244     doEnd(elem);
    245 }
    246 
    247 void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length)
    248 {
    249     int level = fElems.count();
    250     if (this->doStart(name, length))
    251     {
    252         // the first child, need to close with >
    253         fStream.writeText(">");
    254         fStream.newline();
    255     }
    256 
    257     tab(fStream, level);
    258     fStream.writeText("<");
    259     fStream.write(name, length);
    260 }
    261 
    262 void SkXMLStreamWriter::writeHeader()
    263 {
    264     const char* header = getHeader();
    265     fStream.write(header, strlen(header));
    266     fStream.newline();
    267 }
    268 
    269 ////////////////////////////////////////////////////////////////////////////////////////////////
    270 
    271 #include "SkXMLParser.h"
    272 
    273 SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
    274     : SkXMLWriter(false), fParser(*parser)
    275 {
    276 }
    277 
    278 SkXMLParserWriter::~SkXMLParserWriter()
    279 {
    280     this->flush();
    281 }
    282 
    283 void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
    284 {
    285     SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren);
    286     SkString str(value, length);
    287     fParser.addAttribute(name, str.c_str());
    288 }
    289 
    290 void SkXMLParserWriter::onEndElement()
    291 {
    292     Elem* elem = this->getEnd();
    293     fParser.endElement(elem->fName.c_str());
    294     this->doEnd(elem);
    295 }
    296 
    297 void SkXMLParserWriter::onStartElementLen(const char name[], size_t length)
    298 {
    299     (void)this->doStart(name, length);
    300     SkString str(name, length);
    301     fParser.startElement(str.c_str());
    302 }
    303 
    304 
    305 ////////////////////////////////////////////////////////////////////////////////////////
    306 ////////////////////////////////////////////////////////////////////////////////////////
    307 
    308 #ifdef SK_DEBUG
    309 
    310 void SkXMLStreamWriter::UnitTest()
    311 {
    312 #ifdef SK_SUPPORT_UNITTEST
    313     SkDebugWStream  s;
    314     SkXMLStreamWriter       w(&s);
    315 
    316     w.startElement("elem0");
    317     w.addAttribute("hello", "world");
    318     w.addS32Attribute("dec", 42);
    319     w.addHexAttribute("hex", 0x42, 3);
    320 #ifdef SK_SCALAR_IS_FLOAT
    321     w.addScalarAttribute("scalar", -4.2f);
    322 #endif
    323     w.startElement("elem1");
    324         w.endElement();
    325         w.startElement("elem1");
    326         w.addAttribute("name", "value");
    327         w.endElement();
    328         w.startElement("elem1");
    329             w.startElement("elem2");
    330                 w.startElement("elem3");
    331                 w.addAttribute("name", "value");
    332                 w.endElement();
    333             w.endElement();
    334             w.startElement("elem2");
    335             w.endElement();
    336         w.endElement();
    337     w.endElement();
    338 #endif
    339 }
    340 
    341 #endif
    342 
    343