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 "talk/xmllite/xmlnsstack.h"
     29 
     30 #include <sstream>
     31 #include <string>
     32 #include <vector>
     33 
     34 #include "talk/xmllite/xmlelement.h"
     35 #include "talk/xmllite/xmlconstants.h"
     36 
     37 namespace buzz {
     38 
     39 XmlnsStack::XmlnsStack() :
     40   pxmlnsStack_(new std::vector<std::string>),
     41   pxmlnsDepthStack_(new std::vector<size_t>) {
     42 }
     43 
     44 XmlnsStack::~XmlnsStack() {}
     45 
     46 void XmlnsStack::PushFrame() {
     47   pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
     48 }
     49 
     50 void XmlnsStack::PopFrame() {
     51   size_t prev_size = pxmlnsDepthStack_->back();
     52   pxmlnsDepthStack_->pop_back();
     53   if (prev_size < pxmlnsStack_->size()) {
     54     pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
     55                         pxmlnsStack_->end());
     56   }
     57 }
     58 
     59 std::pair<std::string, bool> XmlnsStack::NsForPrefix(
     60     const std::string& prefix) {
     61   if (prefix.length() >= 3 &&
     62       (prefix[0] == 'x' || prefix[0] == 'X') &&
     63       (prefix[1] == 'm' || prefix[1] == 'M') &&
     64       (prefix[2] == 'l' || prefix[2] == 'L')) {
     65     if (prefix == "xml")
     66       return std::make_pair(NS_XML, true);
     67     if (prefix == "xmlns")
     68       return std::make_pair(NS_XMLNS, true);
     69     // Other names with xml prefix are illegal.
     70     return std::make_pair(STR_EMPTY, false);
     71   }
     72 
     73   std::vector<std::string>::iterator pos;
     74   for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
     75     pos -= 2;
     76     if (*pos == prefix)
     77       return std::make_pair(*(pos + 1), true);
     78   }
     79 
     80   if (prefix == STR_EMPTY)
     81     return std::make_pair(STR_EMPTY, true);  // default namespace
     82 
     83   return std::make_pair(STR_EMPTY, false);  // none found
     84 }
     85 
     86 bool XmlnsStack::PrefixMatchesNs(const std::string& prefix,
     87                                  const std::string& ns) {
     88   const std::pair<std::string, bool> match = NsForPrefix(prefix);
     89   return match.second && (match.first == ns);
     90 }
     91 
     92 std::pair<std::string, bool> XmlnsStack::PrefixForNs(const std::string& ns,
     93                                                      bool isattr) {
     94   if (ns == NS_XML)
     95     return std::make_pair(std::string("xml"), true);
     96   if (ns == NS_XMLNS)
     97     return std::make_pair(std::string("xmlns"), true);
     98   if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
     99     return std::make_pair(STR_EMPTY, true);
    100 
    101   std::vector<std::string>::iterator pos;
    102   for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
    103     pos -= 2;
    104     if (*(pos + 1) == ns &&
    105         (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
    106       return std::make_pair(*pos, true);
    107   }
    108 
    109   return std::make_pair(STR_EMPTY, false); // none found
    110 }
    111 
    112 std::string XmlnsStack::FormatQName(const QName& name, bool isAttr) {
    113   std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
    114   if (prefix == STR_EMPTY)
    115     return name.LocalPart();
    116   else
    117     return prefix + ':' + name.LocalPart();
    118 }
    119 
    120 void XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
    121   pxmlnsStack_->push_back(prefix);
    122   pxmlnsStack_->push_back(ns);
    123 }
    124 
    125 void XmlnsStack::RemoveXmlns() {
    126   pxmlnsStack_->pop_back();
    127   pxmlnsStack_->pop_back();
    128 }
    129 
    130 static bool IsAsciiLetter(char ch) {
    131   return ((ch >= 'a' && ch <= 'z') ||
    132           (ch >= 'A' && ch <= 'Z'));
    133 }
    134 
    135 static std::string AsciiLower(const std::string & s) {
    136   std::string result(s);
    137   size_t i;
    138   for (i = 0; i < result.length(); i++) {
    139     if (result[i] >= 'A' && result[i] <= 'Z')
    140       result[i] += 'a' - 'A';
    141   }
    142   return result;
    143 }
    144 
    145 static std::string SuggestPrefix(const std::string & ns) {
    146   size_t len = ns.length();
    147   size_t i = ns.find_last_of('.');
    148   if (i != std::string::npos && len - i <= 4 + 1)
    149     len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
    150   size_t last = len;
    151   while (last > 0) {
    152     last -= 1;
    153     if (IsAsciiLetter(ns[last])) {
    154       size_t first = last;
    155       last += 1;
    156       while (first > 0) {
    157         if (!IsAsciiLetter(ns[first - 1]))
    158           break;
    159         first -= 1;
    160       }
    161       if (last - first > 4)
    162         last = first + 3;
    163       std::string candidate(AsciiLower(ns.substr(first, last - first)));
    164       if (candidate.find("xml") != 0)
    165         return candidate;
    166       break;
    167     }
    168   }
    169   return "ns";
    170 }
    171 
    172 std::pair<std::string, bool> XmlnsStack::AddNewPrefix(const std::string& ns,
    173                                                       bool isAttr) {
    174   if (PrefixForNs(ns, isAttr).second)
    175     return std::make_pair(STR_EMPTY, false);
    176 
    177   std::string base(SuggestPrefix(ns));
    178   std::string result(base);
    179   int i = 2;
    180   while (NsForPrefix(result).second) {
    181     std::stringstream ss;
    182     ss << base;
    183     ss << (i++);
    184     ss >> result;
    185   }
    186   AddXmlns(result, ns);
    187   return std::make_pair(result, true);
    188 }
    189 
    190 void XmlnsStack::Reset() {
    191   pxmlnsStack_->clear();
    192   pxmlnsDepthStack_->clear();
    193 }
    194 
    195 }
    196