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 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 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 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 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 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 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 188 Span XmlReader::getAttributeValue(bool fullyNormalize) { 189 return handleAttributeValue( 190 currentAttribute_->valueBegin, currentAttribute_->valueEnd, 191 fullyNormalize); 192 } 193 194 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 205 rtl::OUString XmlReader::getUrl() const { 206 return fileUrl_; 207 } 208 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 227 void XmlReader::skipSpace() { 228 while (isSpace(peek())) { 229 ++pos_; 230 } 231 } 232 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1042 int XmlReader::toNamespaceId(NamespaceIris::size_type pos) { 1043 OSL_ASSERT(pos <= INT_MAX); 1044 return static_cast< int >(pos); 1045 } 1046 1047 } 1048