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