Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include <cassert>
23 : #include <climits>
24 : #include <cstddef>
25 :
26 : #include "com/sun/star/container/NoSuchElementException.hpp"
27 : #include "com/sun/star/uno/Reference.hxx"
28 : #include "com/sun/star/uno/RuntimeException.hpp"
29 : #include "com/sun/star/uno/XInterface.hpp"
30 : #include "osl/file.h"
31 : #include "rtl/string.h"
32 : #include "rtl/ustring.hxx"
33 : #include "sal/log.hxx"
34 : #include "sal/types.h"
35 : #include "xmlreader/pad.hxx"
36 : #include "xmlreader/span.hxx"
37 : #include "xmlreader/xmlreader.hxx"
38 :
39 : namespace xmlreader {
40 :
41 : namespace {
42 :
43 55017977 : bool isSpace(char c) {
44 55017977 : switch (c) {
45 : case '\x09':
46 : case '\x0A':
47 : case '\x0D':
48 : case ' ':
49 8472516 : return true;
50 : default:
51 46545461 : return false;
52 : }
53 : }
54 :
55 : }
56 :
57 21966 : XmlReader::XmlReader(OUString const & fileUrl)
58 : SAL_THROW((
59 : css::container::NoSuchElementException, css::uno::RuntimeException)):
60 22001 : fileUrl_(fileUrl)
61 : {
62 : oslFileError e = osl_openFile(
63 21966 : fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
64 21966 : switch (e)
65 : {
66 : case osl_File_E_None:
67 21933 : break;
68 : case osl_File_E_NOENT:
69 : throw css::container::NoSuchElementException(
70 33 : fileUrl_, css::uno::Reference< css::uno::XInterface >());
71 : default:
72 : throw css::uno::RuntimeException(
73 0 : "cannot open " + fileUrl_ + ": " + OUString::number(e),
74 0 : css::uno::Reference< css::uno::XInterface >());
75 : }
76 21933 : e = osl_getFileSize(fileHandle_, &fileSize_);
77 21933 : if (e == osl_File_E_None) {
78 : e = osl_mapFile(
79 : fileHandle_, &fileAddress_, fileSize_, 0,
80 21933 : osl_File_MapFlag_WillNeed);
81 : }
82 21933 : if (e != osl_File_E_None) {
83 2 : oslFileError e2 = osl_closeFile(fileHandle_);
84 2 : if (e2 != osl_File_E_None) {
85 : SAL_WARN(
86 : "xmlreader",
87 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e2);
88 : }
89 : throw css::uno::RuntimeException(
90 4 : "cannot mmap " + fileUrl_ + " (" + OUString::number(e) + ")",
91 6 : css::uno::Reference< css::uno::XInterface >());
92 : }
93 21931 : namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
94 21931 : namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
95 21931 : pos_ = static_cast< char * >(fileAddress_);
96 21931 : end_ = pos_ + fileSize_;
97 21931 : state_ = STATE_CONTENT;
98 21931 : firstAttribute_ = true;
99 21931 : }
100 :
101 43862 : XmlReader::~XmlReader() {
102 21931 : oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
103 21931 : if (e != osl_File_E_None) {
104 : SAL_WARN(
105 : "xmlreader",
106 : "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
107 : }
108 21931 : e = osl_closeFile(fileHandle_);
109 21931 : if (e != osl_File_E_None) {
110 : SAL_WARN(
111 : "xmlreader",
112 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
113 : }
114 21931 : }
115 :
116 60663 : int XmlReader::registerNamespaceIri(Span const & iri) {
117 60663 : int id = toNamespaceId(namespaceIris_.size());
118 60663 : namespaceIris_.push_back(iri);
119 60663 : if (iri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
120 : // Old user layer .xcu files used the xsi namespace prefix without
121 : // declaring a corresponding namespace binding, see issue 77174; reading
122 : // those files during migration would fail without this hack that can be
123 : // removed once migration is no longer relevant (see
124 : // configmgr::Components::parseModificationLayer):
125 19366 : namespaces_.push_back(NamespaceData(Span("xsi"), id));
126 : }
127 60663 : return id;
128 : }
129 :
130 19258659 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
131 : {
132 19258659 : switch (state_) {
133 : case STATE_CONTENT:
134 15965779 : switch (reportText) {
135 : case TEXT_NONE:
136 13321509 : return handleSkippedText(data, nsId);
137 : case TEXT_RAW:
138 1733580 : return handleRawText(data);
139 : case TEXT_NORMALIZED:
140 910690 : return handleNormalizedText(data);
141 : }
142 : case STATE_START_TAG:
143 4922 : return handleStartTag(nsId, data);
144 : case STATE_END_TAG:
145 2639348 : return handleEndTag();
146 : case STATE_EMPTY_ELEMENT_TAG:
147 626679 : handleElementEnd();
148 626679 : return RESULT_END;
149 : default: // STATE_DONE
150 21931 : return RESULT_DONE;
151 : }
152 : }
153 :
154 15803909 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
155 : assert(nsId != 0 && localName != 0);
156 15803909 : if (firstAttribute_) {
157 7484295 : currentAttribute_ = attributes_.begin();
158 7484295 : firstAttribute_ = false;
159 : } else {
160 8319614 : ++currentAttribute_;
161 : }
162 15803909 : if (currentAttribute_ == attributes_.end()) {
163 7484295 : return false;
164 : }
165 8319614 : if (currentAttribute_->nameColon == 0) {
166 359031 : *nsId = NAMESPACE_NONE;
167 : *localName = Span(
168 359031 : currentAttribute_->nameBegin,
169 718062 : currentAttribute_->nameEnd - currentAttribute_->nameBegin);
170 : } else {
171 : *nsId = getNamespaceId(
172 : Span(
173 7960583 : currentAttribute_->nameBegin,
174 15921166 : currentAttribute_->nameColon - currentAttribute_->nameBegin));
175 : *localName = Span(
176 7960583 : currentAttribute_->nameColon + 1,
177 15921166 : currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
178 : }
179 8319614 : return true;
180 : }
181 :
182 8303021 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
183 : return handleAttributeValue(
184 16606042 : currentAttribute_->valueBegin, currentAttribute_->valueEnd,
185 24909063 : fullyNormalize);
186 : }
187 :
188 9322496 : int XmlReader::getNamespaceId(Span const & prefix) const {
189 80654256 : for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
190 53769504 : i != namespaces_.rend(); ++i)
191 : {
192 26884752 : if (prefix.equals(i->prefix)) {
193 9322496 : return i->nsId;
194 : }
195 : }
196 0 : return NAMESPACE_UNKNOWN;
197 : }
198 :
199 151353 : OUString XmlReader::getUrl() const {
200 151353 : return fileUrl_;
201 : }
202 :
203 0 : void XmlReader::normalizeLineEnds(Span const & text) {
204 0 : char const * p = text.begin;
205 0 : sal_Int32 n = text.length;
206 : for (;;) {
207 0 : sal_Int32 i = rtl_str_indexOfChar_WithLength(p, n, '\x0D');
208 0 : if (i < 0) {
209 0 : break;
210 : }
211 0 : pad_.add(p, i);
212 0 : p += i + 1;
213 0 : n -= i + 1;
214 0 : if (n == 0 || *p != '\x0A') {
215 0 : pad_.add("\x0A");
216 : }
217 0 : }
218 0 : pad_.add(p, n);
219 0 : }
220 :
221 41341687 : void XmlReader::skipSpace() {
222 91155890 : while (isSpace(peek())) {
223 8472516 : ++pos_;
224 : }
225 41341687 : }
226 :
227 22048 : bool XmlReader::skipComment() {
228 22048 : if (rtl_str_shortenedCompare_WithLength(
229 22048 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
230 44096 : RTL_CONSTASCII_LENGTH("--")) !=
231 : 0)
232 : {
233 0 : return false;
234 : }
235 22048 : pos_ += RTL_CONSTASCII_LENGTH("--");
236 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
237 22048 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
238 22048 : if (i < 0) {
239 : throw css::uno::RuntimeException(
240 0 : "premature end (within comment) of " + fileUrl_,
241 0 : css::uno::Reference< css::uno::XInterface >());
242 : }
243 22048 : pos_ += i + RTL_CONSTASCII_LENGTH("--");
244 22048 : if (read() != '>') {
245 : throw css::uno::RuntimeException(
246 0 : "illegal \"--\" within comment in " + fileUrl_,
247 0 : css::uno::Reference< css::uno::XInterface >());
248 : }
249 22048 : return true;
250 : }
251 :
252 21931 : void XmlReader::skipProcessingInstruction() {
253 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
254 21931 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
255 21931 : if (i < 0) {
256 : throw css::uno::RuntimeException(
257 0 : "bad '<?' in " + fileUrl_,
258 0 : css::uno::Reference< css::uno::XInterface >());
259 : }
260 21931 : pos_ += i + RTL_CONSTASCII_LENGTH("?>");
261 21931 : }
262 :
263 0 : void XmlReader::skipDocumentTypeDeclaration() {
264 : // Neither is it checked that the doctypedecl is at the correct position in
265 : // the document, nor that it is well-formed:
266 : for (;;) {
267 0 : char c = read();
268 0 : switch (c) {
269 : case '\0': // i.e., EOF
270 : throw css::uno::RuntimeException(
271 0 : "premature end (within DTD) of " + fileUrl_,
272 0 : css::uno::Reference< css::uno::XInterface >());
273 : case '"':
274 : case '\'':
275 : {
276 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
277 0 : pos_, end_ - pos_, c);
278 0 : if (i < 0) {
279 : throw css::uno::RuntimeException(
280 0 : "premature end (within DTD) of " + fileUrl_,
281 0 : css::uno::Reference< css::uno::XInterface >());
282 : }
283 0 : pos_ += i + 1;
284 : }
285 0 : break;
286 : case '>':
287 0 : return;
288 : case '[':
289 : for (;;) {
290 0 : c = read();
291 0 : switch (c) {
292 : case '\0': // i.e., EOF
293 : throw css::uno::RuntimeException(
294 0 : "premature end (within DTD) of " + fileUrl_,
295 0 : css::uno::Reference< css::uno::XInterface >());
296 : case '"':
297 : case '\'':
298 : {
299 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
300 0 : pos_, end_ - pos_, c);
301 0 : if (i < 0) {
302 : throw css::uno::RuntimeException(
303 0 : "premature end (within DTD) of " + fileUrl_,
304 0 : css::uno::Reference< css::uno::XInterface >());
305 : }
306 0 : pos_ += i + 1;
307 : }
308 0 : break;
309 : case '<':
310 0 : switch (read()) {
311 : case '\0': // i.e., EOF
312 : throw css::uno::RuntimeException(
313 0 : "premature end (within DTD) of " + fileUrl_,
314 0 : css::uno::Reference< css::uno::XInterface >());
315 : case '!':
316 0 : skipComment();
317 0 : break;
318 : case '?':
319 0 : skipProcessingInstruction();
320 0 : break;
321 : default:
322 0 : break;
323 : }
324 0 : break;
325 : case ']':
326 0 : skipSpace();
327 0 : if (read() != '>') {
328 : throw css::uno::RuntimeException(
329 0 : "missing \">\" of DTD in " + fileUrl_,
330 0 : css::uno::Reference< css::uno::XInterface >());
331 : }
332 0 : return;
333 : default:
334 0 : break;
335 : }
336 0 : }
337 : default:
338 0 : break;
339 : }
340 0 : }
341 : }
342 :
343 0 : Span XmlReader::scanCdataSection() {
344 0 : if (rtl_str_shortenedCompare_WithLength(
345 0 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("[CDATA["),
346 0 : RTL_CONSTASCII_LENGTH("[CDATA[")) !=
347 : 0)
348 : {
349 0 : return Span();
350 : }
351 0 : pos_ += RTL_CONSTASCII_LENGTH("[CDATA[");
352 0 : char const * begin = pos_;
353 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
354 0 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("]]>"));
355 0 : if (i < 0) {
356 : throw css::uno::RuntimeException(
357 0 : "premature end (within CDATA section) of " + fileUrl_,
358 0 : css::uno::Reference< css::uno::XInterface >());
359 : }
360 0 : pos_ += i + RTL_CONSTASCII_LENGTH("]]>");
361 0 : return Span(begin, i);
362 : }
363 :
364 24424415 : bool XmlReader::scanName(char const ** nameColon) {
365 : assert(nameColon != 0 && *nameColon == 0);
366 165842859 : for (char const * begin = pos_;; ++pos_) {
367 165842859 : switch (peek()) {
368 : case '\0': // i.e., EOF
369 : case '\x09':
370 : case '\x0A':
371 : case '\x0D':
372 : case ' ':
373 : case '/':
374 : case '=':
375 : case '>':
376 48848830 : return pos_ != begin;
377 : case ':':
378 8180877 : *nameColon = pos_;
379 8180877 : break;
380 : default:
381 133237567 : break;
382 : }
383 141418444 : }
384 : }
385 :
386 76458 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
387 : assert(begin != 0 && begin <= end);
388 76458 : Span iri(handleAttributeValue(begin, end, false));
389 261687 : for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
390 242746 : if (namespaceIris_[i].equals(iri)) {
391 57517 : return toNamespaceId(i);
392 : }
393 : }
394 18941 : return XmlReader::NAMESPACE_UNKNOWN;
395 : }
396 :
397 29423 : char const * XmlReader::handleReference(char const * position, char const * end)
398 : {
399 : assert(position != 0 && *position == '&' && position < end);
400 29423 : ++position;
401 29423 : if (*position == '#') {
402 1020 : ++position;
403 1020 : sal_Int32 val = 0;
404 : char const * p;
405 1020 : if (*position == 'x') {
406 1020 : ++position;
407 1020 : p = position;
408 4080 : for (;; ++position) {
409 5100 : char c = *position;
410 5100 : if (c >= '0' && c <= '9') {
411 1870 : val = 16 * val + (c - '0');
412 3230 : } else if (c >= 'A' && c <= 'F') {
413 2210 : val = 16 * val + (c - 'A') + 10;
414 1020 : } else if (c >= 'a' && c <= 'f') {
415 0 : val = 16 * val + (c - 'a') + 10;
416 : } else {
417 : break;
418 : }
419 4080 : if (val > 0x10FFFF) { // avoid overflow
420 : throw css::uno::RuntimeException(
421 0 : "'&#x...' too large in " + fileUrl_,
422 0 : css::uno::Reference< css::uno::XInterface >());
423 : }
424 4080 : }
425 : } else {
426 0 : p = position;
427 0 : for (;; ++position) {
428 0 : char c = *position;
429 0 : if (c >= '0' && c <= '9') {
430 0 : val = 10 * val + (c - '0');
431 : } else {
432 : break;
433 : }
434 0 : if (val > 0x10FFFF) { // avoid overflow
435 : throw css::uno::RuntimeException(
436 0 : "'&#...' too large in " + fileUrl_,
437 0 : css::uno::Reference< css::uno::XInterface >());
438 : }
439 0 : }
440 : }
441 1020 : if (position == p || *position++ != ';') {
442 : throw css::uno::RuntimeException(
443 0 : "'&#...' missing ';' in " + fileUrl_,
444 0 : css::uno::Reference< css::uno::XInterface >());
445 : }
446 : assert(val >= 0 && val <= 0x10FFFF);
447 1020 : if ((val < 0x20 && val != 0x9 && val != 0xA && val != 0xD) ||
448 0 : (val >= 0xD800 && val <= 0xDFFF) || val == 0xFFFE || val == 0xFFFF)
449 : {
450 : throw css::uno::RuntimeException(
451 0 : "character reference denoting invalid character in " + fileUrl_,
452 0 : css::uno::Reference< css::uno::XInterface >());
453 : }
454 : char buf[4];
455 : sal_Int32 len;
456 1020 : if (val < 0x80) {
457 0 : buf[0] = static_cast< char >(val);
458 0 : len = 1;
459 1020 : } else if (val < 0x800) {
460 0 : buf[0] = static_cast< char >((val >> 6) | 0xC0);
461 0 : buf[1] = static_cast< char >((val & 0x3F) | 0x80);
462 0 : len = 2;
463 1020 : } else if (val < 0x10000) {
464 1020 : buf[0] = static_cast< char >((val >> 12) | 0xE0);
465 1020 : buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
466 1020 : buf[2] = static_cast< char >((val & 0x3F) | 0x80);
467 1020 : len = 3;
468 : } else {
469 0 : buf[0] = static_cast< char >((val >> 18) | 0xF0);
470 0 : buf[1] = static_cast< char >(((val >> 12) & 0x3F) | 0x80);
471 0 : buf[2] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
472 0 : buf[3] = static_cast< char >((val & 0x3F) | 0x80);
473 0 : len = 4;
474 : }
475 1020 : pad_.addEphemeral(buf, len);
476 1020 : return position;
477 : } else {
478 : struct EntityRef {
479 : char const * inBegin;
480 : sal_Int32 inLength;
481 : char const * outBegin;
482 : sal_Int32 outLength;
483 : };
484 : static EntityRef const refs[] = {
485 : { RTL_CONSTASCII_STRINGPARAM("amp;"),
486 : RTL_CONSTASCII_STRINGPARAM("&") },
487 : { RTL_CONSTASCII_STRINGPARAM("lt;"),
488 : RTL_CONSTASCII_STRINGPARAM("<") },
489 : { RTL_CONSTASCII_STRINGPARAM("gt;"),
490 : RTL_CONSTASCII_STRINGPARAM(">") },
491 : { RTL_CONSTASCII_STRINGPARAM("apos;"),
492 : RTL_CONSTASCII_STRINGPARAM("'") },
493 : { RTL_CONSTASCII_STRINGPARAM("quot;"),
494 : RTL_CONSTASCII_STRINGPARAM("\"") } };
495 54527 : for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
496 54527 : if (rtl_str_shortenedCompare_WithLength(
497 54527 : position, end - position, refs[i].inBegin, refs[i].inLength,
498 109054 : refs[i].inLength) ==
499 : 0)
500 : {
501 28403 : position += refs[i].inLength;
502 28403 : pad_.add(refs[i].outBegin, refs[i].outLength);
503 28403 : return position;
504 : }
505 : }
506 : throw css::uno::RuntimeException(
507 0 : "unknown entity reference in " + fileUrl_,
508 0 : css::uno::Reference< css::uno::XInterface >());
509 : }
510 : }
511 :
512 8379479 : Span XmlReader::handleAttributeValue(
513 : char const * begin, char const * end, bool fullyNormalize)
514 : {
515 8379479 : pad_.clear();
516 8379479 : if (fullyNormalize) {
517 5203774 : while (begin != end && isSpace(*begin)) {
518 0 : ++begin;
519 : }
520 5203774 : while (end != begin && isSpace(end[-1])) {
521 0 : --end;
522 : }
523 2601887 : char const * p = begin;
524 : enum Space { SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
525 : // a single true space character can go into the current span,
526 : // everything else breaks the span
527 2601887 : Space space = SPACE_NONE;
528 25275763 : while (p != end) {
529 20071989 : switch (*p) {
530 : case '\x09':
531 : case '\x0A':
532 : case '\x0D':
533 0 : switch (space) {
534 : case SPACE_NONE:
535 0 : pad_.add(begin, p - begin);
536 0 : pad_.add(" ");
537 0 : space = SPACE_BREAK;
538 0 : break;
539 : case SPACE_SPAN:
540 0 : pad_.add(begin, p - begin);
541 0 : space = SPACE_BREAK;
542 0 : break;
543 : case SPACE_BREAK:
544 0 : break;
545 : }
546 0 : begin = ++p;
547 0 : break;
548 : case ' ':
549 4344 : switch (space) {
550 : case SPACE_NONE:
551 4344 : ++p;
552 4344 : space = SPACE_SPAN;
553 4344 : break;
554 : case SPACE_SPAN:
555 0 : pad_.add(begin, p - begin);
556 0 : begin = ++p;
557 0 : space = SPACE_BREAK;
558 0 : break;
559 : case SPACE_BREAK:
560 0 : begin = ++p;
561 0 : break;
562 : }
563 4344 : break;
564 : case '&':
565 0 : pad_.add(begin, p - begin);
566 0 : p = handleReference(p, end);
567 0 : begin = p;
568 0 : space = SPACE_NONE;
569 0 : break;
570 : default:
571 20067645 : ++p;
572 20067645 : space = SPACE_NONE;
573 20067645 : break;
574 : }
575 : }
576 2601887 : pad_.add(begin, p - begin);
577 : } else {
578 5777592 : char const * p = begin;
579 81082424 : while (p != end) {
580 69527240 : switch (*p) {
581 : case '\x09':
582 : case '\x0A':
583 0 : pad_.add(begin, p - begin);
584 0 : begin = ++p;
585 0 : pad_.add(" ");
586 0 : break;
587 : case '\x0D':
588 0 : pad_.add(begin, p - begin);
589 0 : ++p;
590 0 : if (peek() == '\x0A') {
591 0 : ++p;
592 : }
593 0 : begin = p;
594 0 : pad_.add(" ");
595 0 : break;
596 : case '&':
597 1020 : pad_.add(begin, p - begin);
598 1020 : p = handleReference(p, end);
599 1020 : begin = p;
600 1020 : break;
601 : default:
602 69526220 : ++p;
603 69526220 : break;
604 : }
605 : }
606 5777592 : pad_.add(begin, p - begin);
607 : }
608 8379479 : return pad_.get();
609 : }
610 :
611 8296229 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
612 : assert(nsId != 0 && localName);
613 8296229 : char const * nameBegin = pos_;
614 8296229 : char const * nameColon = 0;
615 8296229 : if (!scanName(&nameColon)) {
616 : throw css::uno::RuntimeException(
617 0 : "bad tag name in " + fileUrl_,
618 0 : css::uno::Reference< css::uno::XInterface >());
619 : }
620 8296229 : char const * nameEnd = pos_;
621 8296229 : NamespaceList::size_type inheritedNamespaces = namespaces_.size();
622 8296229 : bool hasDefaultNs = false;
623 8296229 : int defaultNsId = NAMESPACE_NONE;
624 8296229 : attributes_.clear();
625 : for (;;) {
626 16754865 : char const * p = pos_;
627 16754865 : skipSpace();
628 16754865 : if (peek() == '/' || peek() == '>') {
629 8296229 : break;
630 : }
631 8458636 : if (pos_ == p) {
632 : throw css::uno::RuntimeException(
633 0 : "missing whitespace before attribute in " + fileUrl_,
634 0 : css::uno::Reference< css::uno::XInterface >());
635 : }
636 8458636 : char const * attrNameBegin = pos_;
637 8458636 : char const * attrNameColon = 0;
638 8458636 : if (!scanName(&attrNameColon)) {
639 : throw css::uno::RuntimeException(
640 0 : "bad attribute name in " + fileUrl_,
641 0 : css::uno::Reference< css::uno::XInterface >());
642 : }
643 8458636 : char const * attrNameEnd = pos_;
644 8458636 : skipSpace();
645 8458636 : if (read() != '=') {
646 : throw css::uno::RuntimeException(
647 0 : "missing '=' in " + fileUrl_,
648 0 : css::uno::Reference< css::uno::XInterface >());
649 : }
650 8458636 : skipSpace();
651 8458636 : char del = read();
652 8458636 : if (del != '\'' && del != '"') {
653 : throw css::uno::RuntimeException(
654 0 : "bad attribute value in " + fileUrl_,
655 0 : css::uno::Reference< css::uno::XInterface >());
656 : }
657 8458636 : char const * valueBegin = pos_;
658 8458636 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
659 8458636 : if (i < 0) {
660 : throw css::uno::RuntimeException(
661 0 : "unterminated attribute value in " + fileUrl_,
662 0 : css::uno::Reference< css::uno::XInterface >());
663 : }
664 8458636 : char const * valueEnd = pos_ + i;
665 8458636 : pos_ += i + 1;
666 25737504 : if (attrNameColon == 0 &&
667 9543424 : Span(attrNameBegin, attrNameEnd - attrNameBegin).equals("xmlns"))
668 : {
669 2565 : hasDefaultNs = true;
670 2565 : defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
671 33465253 : } else if (attrNameColon != 0 &&
672 8097040 : Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
673 40844231 : "xmlns"))
674 : {
675 : namespaces_.push_back(
676 : NamespaceData(
677 147786 : Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
678 221679 : scanNamespaceIri(valueBegin, valueEnd)));
679 : } else {
680 : attributes_.push_back(
681 : AttributeData(
682 : attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
683 8382178 : valueEnd));
684 : }
685 8458636 : }
686 16589893 : if (!hasDefaultNs && !elements_.empty()) {
687 8274298 : defaultNsId = elements_.top().defaultNamespaceId;
688 : }
689 8296229 : firstAttribute_ = true;
690 8296229 : if (peek() == '/') {
691 626679 : state_ = STATE_EMPTY_ELEMENT_TAG;
692 626679 : ++pos_;
693 : } else {
694 7669550 : state_ = STATE_CONTENT;
695 : }
696 8296229 : if (peek() != '>') {
697 : throw css::uno::RuntimeException(
698 0 : "missing '>' in " + fileUrl_,
699 0 : css::uno::Reference< css::uno::XInterface >());
700 : }
701 8296229 : ++pos_;
702 : elements_.push(
703 : ElementData(
704 8296229 : Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
705 16592458 : defaultNsId));
706 8296229 : if (nameColon == 0) {
707 8253770 : *nsId = defaultNsId;
708 8253770 : *localName = Span(nameBegin, nameEnd - nameBegin);
709 : } else {
710 42459 : *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
711 42459 : *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
712 : }
713 8296229 : return RESULT_BEGIN;
714 : }
715 :
716 7669550 : XmlReader::Result XmlReader::handleEndTag() {
717 7669550 : if (elements_.empty()) {
718 : throw css::uno::RuntimeException(
719 0 : "spurious end tag in " + fileUrl_,
720 0 : css::uno::Reference< css::uno::XInterface >());
721 : }
722 7669550 : char const * nameBegin = pos_;
723 7669550 : char const * nameColon = 0;
724 15339100 : if (!scanName(&nameColon) ||
725 7669550 : !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
726 : {
727 : throw css::uno::RuntimeException(
728 0 : "tag mismatch in " + fileUrl_,
729 0 : css::uno::Reference< css::uno::XInterface >());
730 : }
731 7669550 : handleElementEnd();
732 7669550 : skipSpace();
733 7669550 : if (peek() != '>') {
734 : throw css::uno::RuntimeException(
735 0 : "missing '>' in " + fileUrl_,
736 0 : css::uno::Reference< css::uno::XInterface >());
737 : }
738 7669550 : ++pos_;
739 7669550 : return RESULT_END;
740 : }
741 :
742 8296229 : void XmlReader::handleElementEnd() {
743 : assert(!elements_.empty());
744 8296229 : namespaces_.resize(elements_.top().inheritedNamespaces);
745 8296229 : elements_.pop();
746 8296229 : state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
747 8296229 : }
748 :
749 13365080 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
750 : for (;;) {
751 13365080 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
752 13365080 : if (i < 0) {
753 : throw css::uno::RuntimeException(
754 0 : "premature end of " + fileUrl_,
755 0 : css::uno::Reference< css::uno::XInterface >());
756 : }
757 13365080 : pos_ += i + 1;
758 13365080 : switch (peek()) {
759 : case '!':
760 21640 : ++pos_;
761 21640 : if (!skipComment() && !scanCdataSection().is()) {
762 0 : skipDocumentTypeDeclaration();
763 : }
764 21640 : break;
765 : case '/':
766 5030202 : ++pos_;
767 5030202 : return handleEndTag();
768 : case '?':
769 21931 : ++pos_;
770 21931 : skipProcessingInstruction();
771 21931 : break;
772 : default:
773 8291307 : return handleStartTag(nsId, data);
774 : }
775 43571 : }
776 : }
777 :
778 1733580 : XmlReader::Result XmlReader::handleRawText(Span * text) {
779 1733580 : pad_.clear();
780 1733580 : for (char const * begin = pos_;;) {
781 45554121 : switch (peek()) {
782 : case '\0': // i.e., EOF
783 : throw css::uno::RuntimeException(
784 0 : "premature end of " + fileUrl_,
785 0 : css::uno::Reference< css::uno::XInterface >());
786 : case '\x0D':
787 0 : pad_.add(begin, pos_ - begin);
788 0 : ++pos_;
789 0 : if (peek() != '\x0A') {
790 0 : pad_.add("\x0A");
791 : }
792 0 : begin = pos_;
793 0 : break;
794 : case '&':
795 28403 : pad_.add(begin, pos_ - begin);
796 28403 : pos_ = handleReference(pos_, end_);
797 28403 : begin = pos_;
798 28403 : break;
799 : case '<':
800 1733580 : pad_.add(begin, pos_ - begin);
801 1733580 : ++pos_;
802 1733580 : switch (peek()) {
803 : case '!':
804 0 : ++pos_;
805 0 : if (!skipComment()) {
806 0 : Span cdata(scanCdataSection());
807 0 : if (cdata.is()) {
808 0 : normalizeLineEnds(cdata);
809 : } else {
810 0 : skipDocumentTypeDeclaration();
811 : }
812 : }
813 0 : begin = pos_;
814 0 : break;
815 : case '/':
816 1728658 : *text = pad_.get();
817 1728658 : ++pos_;
818 1728658 : state_ = STATE_END_TAG;
819 1728658 : return RESULT_TEXT;
820 : case '?':
821 0 : ++pos_;
822 0 : skipProcessingInstruction();
823 0 : begin = pos_;
824 0 : break;
825 : default:
826 4922 : *text = pad_.get();
827 4922 : state_ = STATE_START_TAG;
828 4922 : return RESULT_TEXT;
829 : }
830 0 : break;
831 : default:
832 43792138 : ++pos_;
833 43792138 : break;
834 : }
835 43820541 : }
836 : }
837 :
838 910690 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
839 910690 : pad_.clear();
840 910690 : char const * flowBegin = pos_;
841 910690 : char const * flowEnd = pos_;
842 : enum Space { SPACE_START, SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
843 : // a single true space character can go into the current flow,
844 : // everything else breaks the flow
845 910690 : Space space = SPACE_START;
846 : for (;;) {
847 3430678 : switch (peek()) {
848 : case '\0': // i.e., EOF
849 : throw css::uno::RuntimeException(
850 0 : "premature end of " + fileUrl_,
851 0 : css::uno::Reference< css::uno::XInterface >());
852 : case '\x09':
853 : case '\x0A':
854 : case '\x0D':
855 1836 : switch (space) {
856 : case SPACE_START:
857 : case SPACE_BREAK:
858 918 : break;
859 : case SPACE_NONE:
860 : case SPACE_SPAN:
861 918 : space = SPACE_BREAK;
862 918 : break;
863 : }
864 1836 : ++pos_;
865 1836 : break;
866 : case ' ':
867 23103 : switch (space) {
868 : case SPACE_START:
869 : case SPACE_BREAK:
870 14382 : break;
871 : case SPACE_NONE:
872 8721 : space = SPACE_SPAN;
873 8721 : break;
874 : case SPACE_SPAN:
875 0 : space = SPACE_BREAK;
876 0 : break;
877 : }
878 23103 : ++pos_;
879 23103 : break;
880 : case '&':
881 0 : switch (space) {
882 : case SPACE_START:
883 0 : break;
884 : case SPACE_NONE:
885 : case SPACE_SPAN:
886 0 : pad_.add(flowBegin, pos_ - flowBegin);
887 0 : break;
888 : case SPACE_BREAK:
889 0 : pad_.add(flowBegin, flowEnd - flowBegin);
890 0 : pad_.add(" ");
891 0 : break;
892 : }
893 0 : pos_ = handleReference(pos_, end_);
894 0 : flowBegin = pos_;
895 0 : flowEnd = pos_;
896 0 : space = SPACE_NONE;
897 0 : break;
898 : case '<':
899 911098 : ++pos_;
900 911098 : switch (peek()) {
901 : case '!':
902 408 : ++pos_;
903 408 : if (skipComment()) {
904 408 : space = SPACE_BREAK;
905 : } else {
906 0 : Span cdata(scanCdataSection());
907 0 : if (cdata.is()) {
908 : // CDATA is not normalized (similar to character
909 : // references; it keeps the code simple), but it might
910 : // arguably be better to normalize it:
911 0 : switch (space) {
912 : case SPACE_START:
913 0 : break;
914 : case SPACE_NONE:
915 : case SPACE_SPAN:
916 0 : pad_.add(flowBegin, pos_ - flowBegin);
917 0 : break;
918 : case SPACE_BREAK:
919 0 : pad_.add(flowBegin, flowEnd - flowBegin);
920 0 : pad_.add(" ");
921 0 : break;
922 : }
923 0 : normalizeLineEnds(cdata);
924 0 : flowBegin = pos_;
925 0 : flowEnd = pos_;
926 0 : space = SPACE_NONE;
927 : } else {
928 0 : skipDocumentTypeDeclaration();
929 : }
930 : }
931 408 : break;
932 : case '/':
933 910690 : ++pos_;
934 910690 : pad_.add(flowBegin, flowEnd - flowBegin);
935 910690 : *text = pad_.get();
936 910690 : state_ = STATE_END_TAG;
937 910690 : return RESULT_TEXT;
938 : case '?':
939 0 : ++pos_;
940 0 : skipProcessingInstruction();
941 0 : space = SPACE_BREAK;
942 0 : break;
943 : default:
944 0 : pad_.add(flowBegin, flowEnd - flowBegin);
945 0 : *text = pad_.get();
946 0 : state_ = STATE_START_TAG;
947 0 : return RESULT_TEXT;
948 : }
949 408 : break;
950 : default:
951 2494641 : switch (space) {
952 : case SPACE_START:
953 910622 : flowBegin = pos_;
954 910622 : break;
955 : case SPACE_NONE:
956 : case SPACE_SPAN:
957 1583186 : break;
958 : case SPACE_BREAK:
959 833 : pad_.add(flowBegin, flowEnd - flowBegin);
960 833 : pad_.add(" ");
961 833 : flowBegin = pos_;
962 833 : break;
963 : }
964 2494641 : flowEnd = ++pos_;
965 2494641 : space = SPACE_NONE;
966 2494641 : break;
967 : }
968 2519988 : }
969 : }
970 :
971 118180 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
972 : assert(pos <= INT_MAX);
973 118180 : return static_cast< int >(pos);
974 : }
975 :
976 : }
977 :
978 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|