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
37namespace buzz {
38
39XmlnsStack::XmlnsStack() :
40  pxmlnsStack_(new std::vector<std::string>),
41  pxmlnsDepthStack_(new std::vector<size_t>) {
42}
43
44XmlnsStack::~XmlnsStack() {}
45
46void XmlnsStack::PushFrame() {
47  pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
48}
49
50void 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
59std::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
86bool 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
92std::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
112std::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
120void XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
121  pxmlnsStack_->push_back(prefix);
122  pxmlnsStack_->push_back(ns);
123}
124
125void XmlnsStack::RemoveXmlns() {
126  pxmlnsStack_->pop_back();
127  pxmlnsStack_->pop_back();
128}
129
130static bool IsAsciiLetter(char ch) {
131  return ((ch >= 'a' && ch <= 'z') ||
132          (ch >= 'A' && ch <= 'Z'));
133}
134
135static 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
145static 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
172std::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
190void XmlnsStack::Reset() {
191  pxmlnsStack_->clear();
192  pxmlnsDepthStack_->clear();
193}
194
195}
196