Home | History | Annotate | Download | only in xmllite
      1 /*
      2  * libjingle
      3  * Copyright 2004--2005, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <string>
     29 #include <iostream>
     30 #include <vector>
     31 #include <sstream>
     32 #include "talk/xmllite/xmlelement.h"
     33 #include "talk/xmllite/xmlnsstack.h"
     34 #include "talk/xmllite/xmlconstants.h"
     35 
     36 namespace buzz {
     37 
     38 XmlnsStack::XmlnsStack() :
     39   pxmlnsStack_(new std::vector<std::string>),
     40   pxmlnsDepthStack_(new std::vector<size_t>) {
     41 }
     42 
     43 XmlnsStack::~XmlnsStack() {}
     44 
     45 void
     46 XmlnsStack::PushFrame() {
     47   pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
     48 }
     49 
     50 void
     51 XmlnsStack::PopFrame() {
     52   size_t prev_size = pxmlnsDepthStack_->back();
     53   pxmlnsDepthStack_->pop_back();
     54   if (prev_size < pxmlnsStack_->size()) {
     55     pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
     56                         pxmlnsStack_->end());
     57   }
     58 }
     59 const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false);
     60 const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true);
     61 const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true);
     62 
     63 const std::string *
     64 XmlnsStack::NsForPrefix(const std::string & prefix) {
     65   if (prefix.length() >= 3 &&
     66       (prefix[0] == 'x' || prefix[0] == 'X') &&
     67       (prefix[1] == 'm' || prefix[1] == 'M') &&
     68       (prefix[2] == 'l' || prefix[2] == 'L')) {
     69     if (prefix == "xml")
     70       return &(NS_XML);
     71     if (prefix == "xmlns")
     72       return &(NS_XMLNS);
     73     return NULL;
     74   }
     75 
     76   std::vector<std::string>::iterator pos;
     77   for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
     78     pos -= 2;
     79     if (*pos == prefix)
     80       return &(*(pos + 1));
     81   }
     82 
     83   if (prefix == STR_EMPTY)
     84     return &(STR_EMPTY); // default namespace
     85 
     86   return NULL; // none found
     87 }
     88 
     89 bool
     90 XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) {
     91   const std::string * match = NsForPrefix(prefix);
     92   if (match == NULL)
     93     return false;
     94   return (*match == ns);
     95 }
     96 
     97 std::pair<std::string, bool>
     98 XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) {
     99   if (ns == NS_XML)
    100     return std::make_pair(std::string("xml"), true);
    101   if (ns == NS_XMLNS)
    102     return std::make_pair(std::string("xmlns"), true);
    103   if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
    104     return std::make_pair(STR_EMPTY, true);
    105 
    106   std::vector<std::string>::iterator pos;
    107   for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
    108     pos -= 2;
    109     if (*(pos + 1) == ns &&
    110         (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
    111       return std::make_pair(*pos, true);
    112   }
    113 
    114   return std::make_pair(STR_EMPTY, false); // none found
    115 }
    116 
    117 std::string
    118 XmlnsStack::FormatQName(const QName & name, bool isAttr) {
    119   std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
    120   if (prefix == STR_EMPTY)
    121     return name.LocalPart();
    122   else
    123     return prefix + ':' + name.LocalPart();
    124 }
    125 
    126 void
    127 XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
    128   pxmlnsStack_->push_back(prefix);
    129   pxmlnsStack_->push_back(ns);
    130 }
    131 
    132 void
    133 XmlnsStack::RemoveXmlns() {
    134   pxmlnsStack_->pop_back();
    135   pxmlnsStack_->pop_back();
    136 }
    137 
    138 static bool IsAsciiLetter(char ch) {
    139   return ((ch >= 'a' && ch <= 'z') ||
    140           (ch >= 'A' && ch <= 'Z'));
    141 }
    142 
    143 static std::string AsciiLower(const std::string & s) {
    144   std::string result(s);
    145   size_t i;
    146   for (i = 0; i < result.length(); i++) {
    147     if (result[i] >= 'A' && result[i] <= 'Z')
    148       result[i] += 'a' - 'A';
    149   }
    150   return result;
    151 }
    152 
    153 static std::string SuggestPrefix(const std::string & ns) {
    154   size_t len = ns.length();
    155   size_t i = ns.find_last_of('.');
    156   if (i != std::string::npos && len - i <= 4 + 1)
    157     len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
    158   size_t last = len;
    159   while (last > 0) {
    160     last -= 1;
    161     if (IsAsciiLetter(ns[last])) {
    162       size_t first = last;
    163       last += 1;
    164       while (first > 0) {
    165         if (!IsAsciiLetter(ns[first - 1]))
    166           break;
    167         first -= 1;
    168       }
    169       if (last - first > 4)
    170         last = first + 3;
    171       std::string candidate(AsciiLower(ns.substr(first, last - first)));
    172       if (candidate.find("xml") != 0)
    173         return candidate;
    174       break;
    175     }
    176   }
    177   return "ns";
    178 }
    179 
    180 
    181 std::pair<std::string, bool>
    182 XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) {
    183   if (PrefixForNs(ns, isAttr).second)
    184     return std::make_pair(STR_EMPTY, false);
    185 
    186   std::string base(SuggestPrefix(ns));
    187   std::string result(base);
    188   int i = 2;
    189   while (NsForPrefix(result) != NULL) {
    190     std::stringstream ss;
    191     ss << base;
    192     ss << (i++);
    193     ss >> result;
    194   }
    195   AddXmlns(result, ns);
    196   return std::make_pair(result, true);
    197 }
    198 
    199 void XmlnsStack::Reset() {
    200   pxmlnsStack_->clear();
    201   pxmlnsDepthStack_->clear();
    202 }
    203 
    204 }
    205