1 | /* |
2 | * This file is part of the syndication library |
3 | * |
4 | * Copyright (C) 2006 Frank Osterfeld <osterfeld@kde.org> |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | #include "elementwrapper.h" |
23 | #include "constants.h" |
24 | |
25 | #include <kurl.h> |
26 | |
27 | #include <QtXml/QDomDocument> |
28 | #include <QtXml/QDomElement> |
29 | #include <QtCore/QString> |
30 | #include <QtCore/QStringList> |
31 | #include <QtCore/QTextStream> |
32 | |
33 | namespace Syndication { |
34 | |
35 | class ElementWrapper::ElementWrapperPrivate |
36 | { |
37 | public: |
38 | |
39 | QDomElement element; |
40 | QDomDocument ownerDoc; |
41 | mutable QString xmlBase; |
42 | mutable bool xmlBaseParsed; |
43 | mutable QString xmlLang; |
44 | mutable bool xmlLangParsed; |
45 | }; |
46 | |
47 | ElementWrapper::ElementWrapper() : d(new ElementWrapperPrivate) |
48 | { |
49 | d->xmlBaseParsed = true; |
50 | d->xmlLangParsed = true; |
51 | } |
52 | |
53 | ElementWrapper::ElementWrapper(const ElementWrapper& other) |
54 | { |
55 | *this = other; |
56 | } |
57 | |
58 | ElementWrapper::ElementWrapper(const QDomElement& element) : d(new ElementWrapperPrivate) |
59 | { |
60 | d->element = element; |
61 | d->ownerDoc = element.ownerDocument(); //keep a copy of the (shared, thus cheap) document around to ensure the element isn't deleted too early (Bug 190068) |
62 | d->xmlBaseParsed = false; |
63 | d->xmlLangParsed = false; |
64 | } |
65 | |
66 | ElementWrapper::~ElementWrapper() |
67 | { |
68 | } |
69 | |
70 | ElementWrapper& ElementWrapper::operator=(const ElementWrapper& other) |
71 | { |
72 | d = other.d; |
73 | return *this; |
74 | } |
75 | |
76 | bool ElementWrapper::operator==(const ElementWrapper& other) const |
77 | { |
78 | return d->element == other.d->element; |
79 | } |
80 | |
81 | bool ElementWrapper::isNull() const |
82 | { |
83 | return d->element.isNull(); |
84 | } |
85 | |
86 | const QDomElement& ElementWrapper::element() const |
87 | { |
88 | return d->element; |
89 | } |
90 | |
91 | QString ElementWrapper::xmlBase() const |
92 | { |
93 | if (!d->xmlBaseParsed) // xmlBase not computed yet |
94 | { |
95 | QDomElement current = d->element; |
96 | |
97 | /* |
98 | An atom feed can contain nested xml:base elements, like this: |
99 | |
100 | <feed xml:base="http://example.com/foo.atom"> |
101 | <entry xml:base="subdir/"> |
102 | <link href="foo.html"/> |
103 | </entry> |
104 | </feed> |
105 | |
106 | To compute xml:base we explore the tree all the way up to the top. |
107 | `bases` stores all the xml:base values from the deepest element up to |
108 | the root element. |
109 | */ |
110 | QStringList bases; |
111 | while (!current.isNull()) |
112 | { |
113 | if (current.hasAttributeNS(xmlNamespace(), QLatin1String("base" ))) |
114 | { |
115 | bases << current.attributeNS(xmlNamespace(), QLatin1String("base" )); |
116 | } |
117 | |
118 | QDomNode parent = current.parentNode(); |
119 | |
120 | if (!parent.isNull() && parent.isElement()) |
121 | current = parent.toElement(); |
122 | else |
123 | current = QDomElement(); |
124 | } |
125 | while (!bases.isEmpty()) |
126 | { |
127 | KUrl u(d->xmlBase, bases.takeLast()); |
128 | d->xmlBase = u.url(); |
129 | } |
130 | |
131 | d->xmlBaseParsed = true; |
132 | } |
133 | |
134 | return d->xmlBase; |
135 | } |
136 | |
137 | QString ElementWrapper::completeURI(const QString& uri) const |
138 | { |
139 | KUrl u(xmlBase(), uri); |
140 | |
141 | if (u.isValid()) |
142 | return u.url(); |
143 | |
144 | return uri; |
145 | } |
146 | |
147 | QString ElementWrapper::xmlLang() const |
148 | { |
149 | if (!d->xmlLangParsed) // xmlLang not computed yet |
150 | { |
151 | QDomElement current = d->element; |
152 | |
153 | while (!current.isNull()) |
154 | { |
155 | if (current.hasAttributeNS(xmlNamespace(), QLatin1String("lang" ))) |
156 | { |
157 | d->xmlLang = current.attributeNS(xmlNamespace(), QLatin1String("lang" )); |
158 | return d->xmlLang; |
159 | } |
160 | |
161 | QDomNode parent = current.parentNode(); |
162 | |
163 | if (!parent.isNull() && parent.isElement()) |
164 | current = parent.toElement(); |
165 | else |
166 | current = QDomElement(); |
167 | } |
168 | d->xmlLangParsed = true; |
169 | } |
170 | return d->xmlLang; |
171 | } |
172 | |
173 | QString ElementWrapper::(const QString& tagName) const |
174 | { |
175 | QDomElement el = d->element.namedItem(tagName).toElement(); |
176 | return el.isNull() ? QString() : el.text().trimmed(); |
177 | } |
178 | |
179 | QString ElementWrapper::(const QString& namespaceURI, const QString& localName) const |
180 | { |
181 | QDomElement el = firstElementByTagNameNS(namespaceURI, localName); |
182 | return el.isNull() ? QString() : el.text().trimmed(); |
183 | } |
184 | |
185 | QString ElementWrapper::childNodesAsXML(const QDomElement& parent) |
186 | { |
187 | ElementWrapper wrapper(parent); |
188 | |
189 | if (parent.isNull()) |
190 | return QString(); |
191 | |
192 | QDomNodeList list = parent.childNodes(); |
193 | |
194 | QString str; |
195 | QTextStream ts( &str, QIODevice::WriteOnly ); |
196 | |
197 | // if there is a xml:base in our scope, first set it for |
198 | // each child element so the xml:base shows up in the |
199 | // serialization |
200 | QString base = wrapper.xmlBase(); |
201 | |
202 | |
203 | for (int i = 0; i < list.count(); ++i) |
204 | { |
205 | QDomNode it = list.item(i); |
206 | if (!base.isEmpty() && it.isElement() |
207 | && !it.toElement().hasAttributeNS(xmlNamespace(), QLatin1String("base" ))) |
208 | { |
209 | it.toElement().setAttributeNS(xmlNamespace(), QLatin1String("base" ), base); |
210 | } |
211 | |
212 | ts << it; |
213 | } |
214 | return str.trimmed(); |
215 | } |
216 | |
217 | QString ElementWrapper::childNodesAsXML() const |
218 | { |
219 | return childNodesAsXML(d->element); |
220 | } |
221 | |
222 | QList<QDomElement> ElementWrapper::elementsByTagName(const QString& tagName) const |
223 | { |
224 | QList<QDomElement> elements; |
225 | for (QDomNode n = d->element.firstChild(); !n.isNull(); n = n.nextSibling()) |
226 | { |
227 | if (n.isElement()) |
228 | { |
229 | QDomElement e = n.toElement(); |
230 | if (e.tagName() == tagName) |
231 | elements.append(e); |
232 | } |
233 | } |
234 | return elements; |
235 | } |
236 | |
237 | QDomElement ElementWrapper::firstElementByTagNameNS(const QString& nsURI, const QString& localName) const |
238 | { |
239 | if (isNull()) |
240 | return QDomElement(); |
241 | |
242 | for (QDomNode n = d->element.firstChild(); !n.isNull(); n = n.nextSibling()) |
243 | { |
244 | if (n.isElement()) |
245 | { |
246 | QDomElement e = n.toElement(); |
247 | if (e.localName() == localName && e.namespaceURI() == nsURI) |
248 | return e; |
249 | } |
250 | } |
251 | |
252 | return QDomElement(); |
253 | } |
254 | |
255 | |
256 | QList<QDomElement> ElementWrapper::elementsByTagNameNS(const QString& nsURI, const QString& localName) const |
257 | { |
258 | if (isNull()) |
259 | return QList<QDomElement>(); |
260 | |
261 | QList<QDomElement> elements; |
262 | for (QDomNode n = d->element.firstChild(); !n.isNull(); n = n.nextSibling()) |
263 | { |
264 | if (n.isElement()) |
265 | { |
266 | QDomElement e = n.toElement(); |
267 | if (e.localName() == localName && e.namespaceURI() == nsURI) |
268 | elements.append(e); |
269 | } |
270 | } |
271 | return elements; |
272 | } |
273 | |
274 | QString ElementWrapper::text() const |
275 | { |
276 | return d->element.text(); |
277 | } |
278 | |
279 | QString ElementWrapper::attribute(const QString& name, const QString& defValue) const |
280 | { |
281 | return d->element.attribute(name, defValue); |
282 | } |
283 | |
284 | QString ElementWrapper::attributeNS(const QString& nsURI, const QString& localName, const QString& defValue) const |
285 | { |
286 | return d->element.attributeNS(nsURI, localName, defValue); |
287 | } |
288 | |
289 | bool ElementWrapper::hasAttribute(const QString& name) const |
290 | { |
291 | return d->element.hasAttribute(name); |
292 | } |
293 | |
294 | bool ElementWrapper::hasAttributeNS(const QString& nsURI, const QString& localName) const |
295 | { |
296 | return d->element.hasAttributeNS(nsURI, localName); |
297 | } |
298 | |
299 | } // namespace Syndication |
300 | |