1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/libjingle/xmllite/xmlnsstack.h"
12
13#include <sstream>
14#include <string>
15#include <vector>
16
17#include "webrtc/libjingle/xmllite/xmlconstants.h"
18#include "webrtc/libjingle/xmllite/xmlelement.h"
19
20namespace buzz {
21
22XmlnsStack::XmlnsStack() :
23  pxmlnsStack_(new std::vector<std::string>),
24  pxmlnsDepthStack_(new std::vector<size_t>) {
25}
26
27XmlnsStack::~XmlnsStack() {}
28
29void XmlnsStack::PushFrame() {
30  pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
31}
32
33void XmlnsStack::PopFrame() {
34  size_t prev_size = pxmlnsDepthStack_->back();
35  pxmlnsDepthStack_->pop_back();
36  if (prev_size < pxmlnsStack_->size()) {
37    pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
38                        pxmlnsStack_->end());
39  }
40}
41
42std::pair<std::string, bool> XmlnsStack::NsForPrefix(
43    const std::string& prefix) {
44  if (prefix.length() >= 3 &&
45      (prefix[0] == 'x' || prefix[0] == 'X') &&
46      (prefix[1] == 'm' || prefix[1] == 'M') &&
47      (prefix[2] == 'l' || prefix[2] == 'L')) {
48    if (prefix == "xml")
49      return std::make_pair(NS_XML, true);
50    if (prefix == "xmlns")
51      return std::make_pair(NS_XMLNS, true);
52    // Other names with xml prefix are illegal.
53    return std::make_pair(STR_EMPTY, false);
54  }
55
56  std::vector<std::string>::iterator pos;
57  for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
58    pos -= 2;
59    if (*pos == prefix)
60      return std::make_pair(*(pos + 1), true);
61  }
62
63  if (prefix == STR_EMPTY)
64    return std::make_pair(STR_EMPTY, true);  // default namespace
65
66  return std::make_pair(STR_EMPTY, false);  // none found
67}
68
69bool XmlnsStack::PrefixMatchesNs(const std::string& prefix,
70                                 const std::string& ns) {
71  const std::pair<std::string, bool> match = NsForPrefix(prefix);
72  return match.second && (match.first == ns);
73}
74
75std::pair<std::string, bool> XmlnsStack::PrefixForNs(const std::string& ns,
76                                                     bool isattr) {
77  if (ns == NS_XML)
78    return std::make_pair(std::string("xml"), true);
79  if (ns == NS_XMLNS)
80    return std::make_pair(std::string("xmlns"), true);
81  if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
82    return std::make_pair(STR_EMPTY, true);
83
84  std::vector<std::string>::iterator pos;
85  for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
86    pos -= 2;
87    if (*(pos + 1) == ns &&
88        (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
89      return std::make_pair(*pos, true);
90  }
91
92  return std::make_pair(STR_EMPTY, false); // none found
93}
94
95std::string XmlnsStack::FormatQName(const QName& name, bool isAttr) {
96  std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
97  if (prefix == STR_EMPTY)
98    return name.LocalPart();
99  else
100    return prefix + ':' + name.LocalPart();
101}
102
103void XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
104  pxmlnsStack_->push_back(prefix);
105  pxmlnsStack_->push_back(ns);
106}
107
108void XmlnsStack::RemoveXmlns() {
109  pxmlnsStack_->pop_back();
110  pxmlnsStack_->pop_back();
111}
112
113static bool IsAsciiLetter(char ch) {
114  return ((ch >= 'a' && ch <= 'z') ||
115          (ch >= 'A' && ch <= 'Z'));
116}
117
118static std::string AsciiLower(const std::string & s) {
119  std::string result(s);
120  size_t i;
121  for (i = 0; i < result.length(); i++) {
122    if (result[i] >= 'A' && result[i] <= 'Z')
123      result[i] += 'a' - 'A';
124  }
125  return result;
126}
127
128static std::string SuggestPrefix(const std::string & ns) {
129  size_t len = ns.length();
130  size_t i = ns.find_last_of('.');
131  if (i != std::string::npos && len - i <= 4 + 1)
132    len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
133  size_t last = len;
134  while (last > 0) {
135    last -= 1;
136    if (IsAsciiLetter(ns[last])) {
137      size_t first = last;
138      last += 1;
139      while (first > 0) {
140        if (!IsAsciiLetter(ns[first - 1]))
141          break;
142        first -= 1;
143      }
144      if (last - first > 4)
145        last = first + 3;
146      std::string candidate(AsciiLower(ns.substr(first, last - first)));
147      if (candidate.find("xml") != 0)
148        return candidate;
149      break;
150    }
151  }
152  return "ns";
153}
154
155std::pair<std::string, bool> XmlnsStack::AddNewPrefix(const std::string& ns,
156                                                      bool isAttr) {
157  if (PrefixForNs(ns, isAttr).second)
158    return std::make_pair(STR_EMPTY, false);
159
160  std::string base(SuggestPrefix(ns));
161  std::string result(base);
162  int i = 2;
163  while (NsForPrefix(result).second) {
164    std::stringstream ss;
165    ss << base;
166    ss << (i++);
167    ss >> result;
168  }
169  AddXmlns(result, ns);
170  return std::make_pair(result, true);
171}
172
173void XmlnsStack::Reset() {
174  pxmlnsStack_->clear();
175  pxmlnsDepthStack_->clear();
176}
177
178}
179