LCOV - code coverage report
Current view: top level - xmlreader/source - xmlreader.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 319 557 57.3 %
Date: 2014-04-11 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    60420947 : bool isSpace(char c) {
      44    60420947 :     switch (c) {
      45             :     case '\x09':
      46             :     case '\x0A':
      47             :     case '\x0D':
      48             :     case ' ':
      49     9529681 :         return true;
      50             :     default:
      51    50891266 :         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        6786 : XmlReader::XmlReader(OUString const & fileUrl)
      72             :     SAL_THROW((
      73             :         css::container::NoSuchElementException, css::uno::RuntimeException)):
      74        6821 :     fileUrl_(fileUrl)
      75             : {
      76             :     oslFileError e = osl_openFile(
      77        6786 :         fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
      78        6786 :     switch (e)
      79             :     {
      80             :     case osl_File_E_None:
      81        6753 :         break;
      82             :     case osl_File_E_NOENT:
      83             :         throw css::container::NoSuchElementException(
      84          33 :             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        6753 :     e = osl_getFileSize(fileHandle_, &fileSize_);
      91        6753 :     if (e == osl_File_E_None) {
      92             :         e = osl_mapFile(
      93             :             fileHandle_, &fileAddress_, fileSize_, 0,
      94        6753 :             osl_File_MapFlag_WillNeed);
      95             :     }
      96        6753 :     if (e != osl_File_E_None) {
      97           2 :         oslFileError e2 = osl_closeFile(fileHandle_);
      98           2 :         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           4 :             "cannot mmap " + fileUrl_ + " (" + OUString::number(e) + ")",
     105           6 :             css::uno::Reference< css::uno::XInterface >());
     106             :     }
     107        6751 :     namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
     108        6751 :     namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
     109        6751 :     pos_ = static_cast< char * >(fileAddress_);
     110        6751 :     end_ = pos_ + fileSize_;
     111        6751 :     state_ = STATE_CONTENT;
     112        6751 :     firstAttribute_ = true;
     113        6751 : }
     114             : 
     115       13502 : XmlReader::~XmlReader() {
     116        6751 :     if (!fileHandle_)
     117           0 :         return;
     118        6751 :     oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
     119        6751 :     if (e != osl_File_E_None) {
     120             :         SAL_WARN(
     121             :             "xmlreader",
     122             :             "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
     123             :     }
     124        6751 :     e = osl_closeFile(fileHandle_);
     125        6751 :     if (e != osl_File_E_None) {
     126             :         SAL_WARN(
     127             :             "xmlreader",
     128             :             "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
     129             :     }
     130        6751 : }
     131             : 
     132       14642 : int XmlReader::registerNamespaceIri(Span const & iri) {
     133       14642 :     int id = toNamespaceId(namespaceIris_.size());
     134       14642 :     namespaceIris_.push_back(iri);
     135       14642 :     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        3946 :         namespaces_.push_back(NamespaceData(Span("xsi"), id));
     142             :     }
     143       14642 :     return id;
     144             : }
     145             : 
     146    20336500 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
     147             : {
     148    20336500 :     switch (state_) {
     149             :     case STATE_CONTENT:
     150    16472213 :         switch (reportText) {
     151             :         case TEXT_NONE:
     152    13433820 :             return handleSkippedText(data, nsId);
     153             :         case TEXT_RAW:
     154     2009265 :             return handleRawText(data);
     155             :         case TEXT_NORMALIZED:
     156     1029128 :             return handleNormalizedText(data);
     157             :         }
     158             :     case STATE_START_TAG:
     159        5232 :         return handleStartTag(nsId, data);
     160             :     case STATE_END_TAG:
     161     3033161 :         return handleEndTag();
     162             :     case STATE_EMPTY_ELEMENT_TAG:
     163      819143 :         handleElementEnd();
     164      819143 :         return RESULT_END;
     165             :     default: // STATE_DONE
     166        6751 :         return RESULT_DONE;
     167             :     }
     168             : }
     169             : 
     170    18065767 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
     171             :     assert(nsId != 0 && localName != 0);
     172    18065767 :     if (firstAttribute_) {
     173     8578570 :         currentAttribute_ = attributes_.begin();
     174     8578570 :         firstAttribute_ = false;
     175             :     } else {
     176     9487197 :         ++currentAttribute_;
     177             :     }
     178    18065767 :     if (currentAttribute_ == attributes_.end()) {
     179     8578570 :         return false;
     180             :     }
     181     9487197 :     if (currentAttribute_->nameColon == 0) {
     182      435803 :         *nsId = NAMESPACE_NONE;
     183             :         *localName = Span(
     184      435803 :             currentAttribute_->nameBegin,
     185      871606 :             currentAttribute_->nameEnd - currentAttribute_->nameBegin);
     186             :     } else {
     187             :         *nsId = getNamespaceId(
     188             :             Span(
     189     9051394 :                 currentAttribute_->nameBegin,
     190    18102788 :                 currentAttribute_->nameColon - currentAttribute_->nameBegin));
     191             :         *localName = Span(
     192     9051394 :             currentAttribute_->nameColon + 1,
     193    18102788 :             currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
     194             :     }
     195     9487197 :     return true;
     196             : }
     197             : 
     198     9467037 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
     199             :     return handleAttributeValue(
     200    18934074 :         currentAttribute_->valueBegin, currentAttribute_->valueEnd,
     201    28401111 :         fullyNormalize);
     202             : }
     203             : 
     204    10580209 : int XmlReader::getNamespaceId(Span const & prefix) const {
     205    75451017 :     for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
     206    50300678 :          i != namespaces_.rend(); ++i)
     207             :     {
     208    25150339 :         if (prefix.equals(i->prefix)) {
     209    10580209 :             return i->nsId;
     210             :         }
     211             :     }
     212           0 :     return NAMESPACE_UNKNOWN;
     213             : }
     214             : 
     215      160500 : OUString XmlReader::getUrl() const {
     216      160500 :     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    45057812 : void XmlReader::skipSpace() {
     238    99645305 :     while (isSpace(peek())) {
     239     9529681 :         ++pos_;
     240             :     }
     241    45057812 : }
     242             : 
     243         121 : bool XmlReader::skipComment() {
     244         121 :     if (rtl_str_shortenedCompare_WithLength(
     245         121 :             pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
     246         242 :             RTL_CONSTASCII_LENGTH("--")) !=
     247             :         0)
     248             :     {
     249           0 :         return false;
     250             :     }
     251         121 :     pos_ += RTL_CONSTASCII_LENGTH("--");
     252             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     253         121 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
     254         121 :     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         121 :     pos_ += i + RTL_CONSTASCII_LENGTH("--");
     260         121 :     if (read() != '>') {
     261             :         throw css::uno::RuntimeException(
     262           0 :             "illegal \"--\" within comment in " + fileUrl_,
     263           0 :             css::uno::Reference< css::uno::XInterface >());
     264             :     }
     265         121 :     return true;
     266             : }
     267             : 
     268        6751 : void XmlReader::skipProcessingInstruction() {
     269             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     270        6751 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
     271        6751 :     if (i < 0) {
     272             :         throw css::uno::RuntimeException(
     273           0 :             "bad '<?' in " + fileUrl_,
     274           0 :             css::uno::Reference< css::uno::XInterface >());
     275             :     }
     276        6751 :     pos_ += i + RTL_CONSTASCII_LENGTH("?>");
     277        6751 : }
     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    26000746 : bool XmlReader::scanName(char const ** nameColon) {
     381             :     assert(nameColon != 0 && *nameColon == 0);
     382   178113231 :     for (char const * begin = pos_;; ++pos_) {
     383   178113231 :         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    52001492 :             return pos_ != begin;
     393             :         case ':':
     394     9195782 :             *nameColon = pos_;
     395     9195782 :             break;
     396             :         default:
     397   142916703 :             break;
     398             :         }
     399   152112485 :     }
     400             : }
     401             : 
     402       41336 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
     403             :     assert(begin != 0 && begin <= end);
     404       41336 :     Span iri(handleAttributeValue(begin, end, false));
     405      174703 :     for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
     406      147935 :         if (namespaceIris_[i].equals(iri)) {
     407       14568 :             return toNamespaceId(i);
     408             :         }
     409             :     }
     410       26768 :     return XmlReader::NAMESPACE_UNKNOWN;
     411             : }
     412             : 
     413       34272 : char const * XmlReader::handleReference(char const * position, char const * end)
     414             : {
     415             :     assert(position != 0 && *position == '&' && position < end);
     416       34272 :     ++position;
     417       34272 :     if (*position == '#') {
     418        2016 :         ++position;
     419        2016 :         sal_Int32 val = 0;
     420             :         char const * p;
     421        2016 :         if (*position == 'x') {
     422        2016 :             ++position;
     423        2016 :             p = position;
     424        8064 :             for (;; ++position) {
     425       10080 :                 char c = *position;
     426       10080 :                 if (c >= '0' && c <= '9') {
     427        3696 :                     val = 16 * val + (c - '0');
     428        6384 :                 } else if (c >= 'A' && c <= 'F') {
     429        4368 :                     val = 16 * val + (c - 'A') + 10;
     430        2016 :                 } else if (c >= 'a' && c <= 'f') {
     431           0 :                     val = 16 * val + (c - 'a') + 10;
     432             :                 } else {
     433             :                     break;
     434             :                 }
     435        8064 :                 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        8064 :             }
     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        2016 :         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        2016 :         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        2016 :         if (val < 0x80) {
     473           0 :             buf[0] = static_cast< char >(val);
     474           0 :             len = 1;
     475        2016 :         } 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        2016 :         } else if (val < 0x10000) {
     480        2016 :             buf[0] = static_cast< char >((val >> 12) | 0xE0);
     481        2016 :             buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
     482        2016 :             buf[2] = static_cast< char >((val & 0x3F) | 0x80);
     483        2016 :             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        2016 :         pad_.addEphemeral(buf, len);
     492        2016 :         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       61824 :         for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
     512       61824 :             if (rtl_str_shortenedCompare_WithLength(
     513       61824 :                     position, end - position, refs[i].inBegin, refs[i].inLength,
     514      123648 :                     refs[i].inLength) ==
     515             :                 0)
     516             :             {
     517       32256 :                 position += refs[i].inLength;
     518       32256 :                 pad_.add(refs[i].outBegin, refs[i].outLength);
     519       32256 :                 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     9508373 : Span XmlReader::handleAttributeValue(
     529             :     char const * begin, char const * end, bool fullyNormalize)
     530             : {
     531     9508373 :     pad_.clear();
     532     9508373 :     if (fullyNormalize) {
     533     5833454 :         while (begin != end && isSpace(*begin)) {
     534           0 :             ++begin;
     535             :         }
     536     5833454 :         while (end != begin && isSpace(end[-1])) {
     537           0 :             --end;
     538             :         }
     539     2916727 :         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     2916727 :         Space space = SPACE_NONE;
     544    28230650 :         while (p != end) {
     545    22397196 :             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        3192 :                 switch (space) {
     566             :                 case SPACE_NONE:
     567        3192 :                     ++p;
     568        3192 :                     space = SPACE_SPAN;
     569        3192 :                     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        3192 :                 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    22394004 :                 ++p;
     588    22394004 :                 space = SPACE_NONE;
     589    22394004 :                 break;
     590             :             }
     591             :         }
     592     2916727 :         pad_.add(begin, p - begin);
     593             :     } else {
     594     6591646 :         char const * p = begin;
     595    92232665 :         while (p != end) {
     596    79049373 :             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        2016 :                 pad_.add(begin, p - begin);
     614        2016 :                 p = handleReference(p, end);
     615        2016 :                 begin = p;
     616        2016 :                 break;
     617             :             default:
     618    79047357 :                 ++p;
     619    79047357 :                 break;
     620             :             }
     621             :         }
     622     6591646 :         pad_.add(begin, p - begin);
     623             :     }
     624     9508373 :     return pad_.get();
     625             : }
     626             : 
     627     8645678 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
     628             :     assert(nsId != 0 && localName);
     629     8645678 :     char const * nameBegin = pos_;
     630     8645678 :     char const * nameColon = 0;
     631     8645678 :     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     8645678 :     char const * nameEnd = pos_;
     637     8645678 :     NamespaceList::size_type inheritedNamespaces = namespaces_.size();
     638     8645678 :     bool hasDefaultNs = false;
     639     8645678 :     int defaultNsId = NAMESPACE_NONE;
     640     8645678 :     attributes_.clear();
     641             :     for (;;) {
     642    18174211 :         char const * p = pos_;
     643    18174211 :         skipSpace();
     644    18174211 :         if (peek() == '/' || peek() == '>') {
     645     8645678 :             break;
     646             :         }
     647     9528533 :         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     9528533 :         char const * attrNameBegin = pos_;
     653     9528533 :         char const * attrNameColon = 0;
     654     9528533 :         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     9528533 :         char const * attrNameEnd = pos_;
     660     9528533 :         skipSpace();
     661     9528533 :         if (read() != '=') {
     662             :             throw css::uno::RuntimeException(
     663           0 :                 "missing '=' in " + fileUrl_,
     664           0 :                 css::uno::Reference< css::uno::XInterface >());
     665             :         }
     666     9528533 :         skipSpace();
     667     9528533 :         char del = read();
     668     9528533 :         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     9528533 :         char const * valueBegin = pos_;
     674     9528533 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
     675     9528533 :         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     9528533 :         char const * valueEnd = pos_ + i;
     681     9528533 :         pos_ += i + 1;
     682    29024206 :         if (attrNameColon == 0 &&
     683    10844354 :             Span(attrNameBegin, attrNameEnd - attrNameBegin).equals("xmlns"))
     684             :         {
     685        2804 :             hasDefaultNs = true;
     686        2804 :             defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
     687    37667113 :         } else if (attrNameColon != 0 &&
     688     9089926 :                    Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
     689    45885433 :                        "xmlns"))
     690             :         {
     691             :             namespaces_.push_back(
     692             :                 NamespaceData(
     693       77064 :                     Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
     694      115596 :                     scanNamespaceIri(valueBegin, valueEnd)));
     695             :         } else {
     696             :             attributes_.push_back(
     697             :                 AttributeData(
     698             :                     attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
     699     9487197 :                     valueEnd));
     700             :         }
     701     9528533 :     }
     702    17288552 :     if (!hasDefaultNs && !elements_.empty()) {
     703     8638927 :         defaultNsId = elements_.top().defaultNamespaceId;
     704             :     }
     705     8645678 :     firstAttribute_ = true;
     706     8645678 :     if (peek() == '/') {
     707      819143 :         state_ = STATE_EMPTY_ELEMENT_TAG;
     708      819143 :         ++pos_;
     709             :     } else {
     710     7826535 :         state_ = STATE_CONTENT;
     711             :     }
     712     8645678 :     if (peek() != '>') {
     713             :         throw css::uno::RuntimeException(
     714           0 :             "missing '>' in " + fileUrl_,
     715           0 :             css::uno::Reference< css::uno::XInterface >());
     716             :     }
     717     8645678 :     ++pos_;
     718             :     elements_.push(
     719             :         ElementData(
     720     8645678 :             Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
     721    17291356 :             defaultNsId));
     722     8645678 :     if (nameColon == 0) {
     723     8589825 :         *nsId = defaultNsId;
     724     8589825 :         *localName = Span(nameBegin, nameEnd - nameBegin);
     725             :     } else {
     726       55853 :         *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
     727       55853 :         *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
     728             :     }
     729     8645678 :     return RESULT_BEGIN;
     730             : }
     731             : 
     732     7826535 : XmlReader::Result XmlReader::handleEndTag() {
     733     7826535 :     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     7826535 :     char const * nameBegin = pos_;
     739     7826535 :     char const * nameColon = 0;
     740    15653070 :     if (!scanName(&nameColon) ||
     741     7826535 :         !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     7826535 :     handleElementEnd();
     748     7826535 :     skipSpace();
     749     7826535 :     if (peek() != '>') {
     750             :         throw css::uno::RuntimeException(
     751           0 :             "missing '>' in " + fileUrl_,
     752           0 :             css::uno::Reference< css::uno::XInterface >());
     753             :     }
     754     7826535 :     ++pos_;
     755     7826535 :     return RESULT_END;
     756             : }
     757             : 
     758     8645678 : void XmlReader::handleElementEnd() {
     759             :     assert(!elements_.empty());
     760     8645678 :     namespaces_.resize(elements_.top().inheritedNamespaces);
     761     8645678 :     elements_.pop();
     762     8645678 :     state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
     763     8645678 : }
     764             : 
     765    13440692 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
     766             :     for (;;) {
     767    13440692 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
     768    13440692 :         if (i < 0) {
     769             :             throw css::uno::RuntimeException(
     770           0 :                 "premature end of " + fileUrl_,
     771           0 :                 css::uno::Reference< css::uno::XInterface >());
     772             :         }
     773    13440692 :         pos_ += i + 1;
     774    13440692 :         switch (peek()) {
     775             :         case '!':
     776         121 :             ++pos_;
     777         121 :             if (!skipComment() && !scanCdataSection().is()) {
     778           0 :                 skipDocumentTypeDeclaration();
     779             :             }
     780         121 :             break;
     781             :         case '/':
     782     4793374 :             ++pos_;
     783     4793374 :             return handleEndTag();
     784             :         case '?':
     785        6751 :             ++pos_;
     786        6751 :             skipProcessingInstruction();
     787        6751 :             break;
     788             :         default:
     789     8640446 :             return handleStartTag(nsId, data);
     790             :         }
     791        6872 :     }
     792             : }
     793             : 
     794     2009265 : XmlReader::Result XmlReader::handleRawText(Span * text) {
     795     2009265 :     pad_.clear();
     796     2009265 :     for (char const * begin = pos_;;) {
     797    52977145 :         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       32256 :             pad_.add(begin, pos_ - begin);
     812       32256 :             pos_ = handleReference(pos_, end_);
     813       32256 :             begin = pos_;
     814       32256 :             break;
     815             :         case '<':
     816     2009265 :             pad_.add(begin, pos_ - begin);
     817     2009265 :             ++pos_;
     818     2009265 :             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     2004033 :                 *text = pad_.get();
     833     2004033 :                 ++pos_;
     834     2004033 :                 state_ = STATE_END_TAG;
     835     2004033 :                 return RESULT_TEXT;
     836             :             case '?':
     837           0 :                 ++pos_;
     838           0 :                 skipProcessingInstruction();
     839           0 :                 begin = pos_;
     840           0 :                 break;
     841             :             default:
     842        5232 :                 *text = pad_.get();
     843        5232 :                 state_ = STATE_START_TAG;
     844        5232 :                 return RESULT_TEXT;
     845             :             }
     846           0 :             break;
     847             :         default:
     848    50935624 :             ++pos_;
     849    50935624 :             break;
     850             :         }
     851    50967880 :     }
     852             : }
     853             : 
     854     1029128 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
     855     1029128 :     pad_.clear();
     856     1029128 :     char const * flowBegin = pos_;
     857     1029128 :     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     1029128 :     Space space = SPACE_START;
     862             :     for (;;) {
     863     3879926 :         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        2016 :             switch (space) {
     872             :             case SPACE_START:
     873             :             case SPACE_BREAK:
     874        1008 :                 break;
     875             :             case SPACE_NONE:
     876             :             case SPACE_SPAN:
     877        1008 :                 space = SPACE_BREAK;
     878        1008 :                 break;
     879             :             }
     880        2016 :             ++pos_;
     881        2016 :             break;
     882             :         case ' ':
     883       25368 :             switch (space) {
     884             :             case SPACE_START:
     885             :             case SPACE_BREAK:
     886       15792 :                 break;
     887             :             case SPACE_NONE:
     888        9576 :                 space = SPACE_SPAN;
     889        9576 :                 break;
     890             :             case SPACE_SPAN:
     891           0 :                 space = SPACE_BREAK;
     892           0 :                 break;
     893             :             }
     894       25368 :             ++pos_;
     895       25368 :             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     1029128 :             ++pos_;
     916     1029128 :             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     1029128 :                 ++pos_;
     950     1029128 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     951     1029128 :                 *text = pad_.get();
     952     1029128 :                 state_ = STATE_END_TAG;
     953     1029128 :                 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     2823414 :             switch (space) {
     968             :             case SPACE_START:
     969     1029128 :                 flowBegin = pos_;
     970     1029128 :                 break;
     971             :             case SPACE_NONE:
     972             :             case SPACE_SPAN:
     973     1793446 :                 break;
     974             :             case SPACE_BREAK:
     975         840 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     976         840 :                 pad_.add(" ");
     977         840 :                 flowBegin = pos_;
     978         840 :                 break;
     979             :             }
     980     2823414 :             flowEnd = ++pos_;
     981     2823414 :             space = SPACE_NONE;
     982     2823414 :             break;
     983             :         }
     984     2850798 :     }
     985             : }
     986             : 
     987       29210 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
     988             :     assert(pos <= INT_MAX);
     989       29210 :     return static_cast< int >(pos);
     990             : }
     991             : 
     992             : }
     993             : 
     994             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10