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