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

Generated by: LCOV version 1.11