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
36namespace buzz {
37
38XmlnsStack::XmlnsStack() :
39  pxmlnsStack_(new std::vector<std::string>),
40  pxmlnsDepthStack_(new std::vector<size_t>) {
41}
42
43XmlnsStack::~XmlnsStack() {}
44
45void
46XmlnsStack::PushFrame() {
47  pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
48}
49
50void
51XmlnsStack::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}
59const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false);
60const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true);
61const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true);
62
63const std::string *
64XmlnsStack::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
89bool
90XmlnsStack::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
97std::pair<std::string, bool>
98XmlnsStack::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
117std::string
118XmlnsStack::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
126void
127XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
128  pxmlnsStack_->push_back(prefix);
129  pxmlnsStack_->push_back(ns);
130}
131
132void
133XmlnsStack::RemoveXmlns() {
134  pxmlnsStack_->pop_back();
135  pxmlnsStack_->pop_back();
136}
137
138static bool IsAsciiLetter(char ch) {
139  return ((ch >= 'a' && ch <= 'z') ||
140          (ch >= 'A' && ch <= 'Z'));
141}
142
143static 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
153static 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
181std::pair<std::string, bool>
182XmlnsStack::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
199void XmlnsStack::Reset() {
200  pxmlnsStack_->clear();
201  pxmlnsDepthStack_->clear();
202}
203
204}
205