LCOV - code coverage report
Current view: top level - xmlreader/source - xmlreader.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 314 557 56.4 %
Date: 2014-04-14 Functions: 23 27 85.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10