| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtXmlPatterns module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include <QStringList> | 
| 41 |  | 
| 42 | #include "qbuiltintypes_p.h" | 
| 43 | #include "qcommonnamespaces_p.h" | 
| 44 | #include "qparsercontext_p.h" | 
| 45 | #include "qquerytransformparser_p.h" | 
| 46 | #include "qxquerytokenizer_p.h" | 
| 47 | #include "qpatternistlocale_p.h" | 
| 48 |  | 
| 49 | #include "qxslttokenizer_p.h" | 
| 50 |  | 
| 51 | QT_BEGIN_NAMESPACE | 
| 52 |  | 
| 53 | using namespace QPatternist; | 
| 54 |  | 
| 55 | Tokenizer::Token SingleTokenContainer::nextToken(XPATHLTYPE *const location) | 
| 56 | { | 
| 57 |     if(m_hasDelivered) | 
| 58 |         return Tokenizer::Token(T_END_OF_FILE); | 
| 59 |     else | 
| 60 |     { | 
| 61 |         *location = m_location; | 
| 62 |         m_hasDelivered = true; | 
| 63 |         return m_token; | 
| 64 |     } | 
| 65 | } | 
| 66 |  | 
| 67 | XSLTTokenizer::XSLTTokenizer(QIODevice *const queryDevice, | 
| 68 |                              const QUrl &location, | 
| 69 |                              const ReportContext::Ptr &context, | 
| 70 |                              const NamePool::Ptr &np) : Tokenizer(location) | 
| 71 |                                                       , MaintainingReader<XSLTTokenLookup>(createElementDescriptions(), createStandardAttributes(), context, queryDevice) | 
| 72 |                                                       , m_location(location) | 
| 73 |                                                       , m_namePool(np) | 
| 74 |                                                       /* We initialize after all name constants. */ | 
| 75 |                                                       , m_validationAlternatives(createValidationAlternatives()) | 
| 76 |                                                       , m_parseInfo(0) | 
| 77 | { | 
| 78 |     Q_ASSERT(m_namePool); | 
| 79 |  | 
| 80 |     pushState(nextState: OutsideDocumentElement); | 
| 81 | } | 
| 82 |  | 
| 83 | bool XSLTTokenizer::isAnyAttributeAllowed() const | 
| 84 | { | 
| 85 |     return m_processingMode.top() == ForwardCompatible; | 
| 86 | } | 
| 87 |  | 
| 88 | void XSLTTokenizer::setParserContext(const ParserContext::Ptr &parseInfo) | 
| 89 | { | 
| 90 |     m_parseInfo = parseInfo; | 
| 91 | } | 
| 92 |  | 
| 93 | void XSLTTokenizer::validateElement() const | 
| 94 | { | 
| 95 |     MaintainingReader<XSLTTokenLookup>::validateElement(elementName: currentElementName()); | 
| 96 | } | 
| 97 |  | 
| 98 | QSet<XSLTTokenizer::NodeName> XSLTTokenizer::createStandardAttributes() | 
| 99 | { | 
| 100 |     QSet<NodeName> retval; | 
| 101 |     enum | 
| 102 |     { | 
| 103 |         ReservedForAttributes = 6 | 
| 104 |     }; | 
| 105 |  | 
| 106 |     retval.reserve(asize: 6); | 
| 107 |  | 
| 108 |     retval.insert(value: DefaultCollation); | 
| 109 |     retval.insert(value: ExcludeResultPrefixes); | 
| 110 |     retval.insert(value: ExtensionElementPrefixes); | 
| 111 |     retval.insert(value: UseWhen); | 
| 112 |     retval.insert(value: Version); | 
| 113 |     retval.insert(value: XpathDefaultNamespace); | 
| 114 |  | 
| 115 |     Q_ASSERT(retval.count() == ReservedForAttributes); | 
| 116 |  | 
| 117 |     return retval; | 
| 118 | } | 
| 119 |  | 
| 120 | ElementDescription<XSLTTokenLookup>::Hash XSLTTokenizer::createElementDescriptions() | 
| 121 | { | 
| 122 |     ElementDescription<XSLTTokenLookup>::Hash result; | 
| 123 |     enum | 
| 124 |     { | 
| 125 |         ReservedForElements = 40 | 
| 126 |     }; | 
| 127 |     result.reserve(asize: ReservedForElements); | 
| 128 |  | 
| 129 |     /* xsl:apply-templates */ | 
| 130 |     { | 
| 131 |         ElementDescription<XSLTTokenLookup> &e = result[ApplyTemplates]; | 
| 132 |         e.optionalAttributes.insert(value: Select); | 
| 133 |         e.optionalAttributes.insert(value: Mode); | 
| 134 |     } | 
| 135 |  | 
| 136 |     /* xsl:template */ | 
| 137 |     { | 
| 138 |         ElementDescription<XSLTTokenLookup> &e = result[Template]; | 
| 139 |         e.optionalAttributes.insert(value: Match); | 
| 140 |         e.optionalAttributes.insert(value: Name); | 
| 141 |         e.optionalAttributes.insert(value: Mode); | 
| 142 |         e.optionalAttributes.insert(value: Priority); | 
| 143 |         e.optionalAttributes.insert(value: As); | 
| 144 |     } | 
| 145 |  | 
| 146 |     /* xsl:text, xsl:choose and xsl:otherwise */ | 
| 147 |     { | 
| 148 |         ElementDescription<XSLTTokenLookup> &e = result[Text]; | 
| 149 |         result.insert(akey: Choose, avalue: e); | 
| 150 |         result.insert(akey: Otherwise, avalue: e); | 
| 151 |     } | 
| 152 |  | 
| 153 |     /* xsl:stylesheet */ | 
| 154 |     { | 
| 155 |         ElementDescription<XSLTTokenLookup> &e = result[Stylesheet]; | 
| 156 |  | 
| 157 |         e.requiredAttributes.insert(value: Version); | 
| 158 |  | 
| 159 |         e.optionalAttributes.insert(value: Id); | 
| 160 |         e.optionalAttributes.insert(value: ExtensionElementPrefixes); | 
| 161 |         e.optionalAttributes.insert(value: ExcludeResultPrefixes); | 
| 162 |         e.optionalAttributes.insert(value: XpathDefaultNamespace); | 
| 163 |         e.optionalAttributes.insert(value: DefaultValidation); | 
| 164 |         e.optionalAttributes.insert(value: DefaultCollation); | 
| 165 |         e.optionalAttributes.insert(value: InputTypeAnnotations); | 
| 166 |     } | 
| 167 |  | 
| 168 |     /* xsl:transform */ | 
| 169 |     { | 
| 170 |         result[Transform] = result[Stylesheet]; | 
| 171 |     } | 
| 172 |  | 
| 173 |     /* xsl:value-of */ | 
| 174 |     { | 
| 175 |         ElementDescription<XSLTTokenLookup> &e = result[ValueOf]; | 
| 176 |         e.optionalAttributes.insert(value: Separator); | 
| 177 |         e.optionalAttributes.insert(value: Select); | 
| 178 |     } | 
| 179 |  | 
| 180 |     /* xsl:variable */ | 
| 181 |     { | 
| 182 |         ElementDescription<XSLTTokenLookup> &e = result[Variable]; | 
| 183 |  | 
| 184 |         e.requiredAttributes.insert(value: Name); | 
| 185 |  | 
| 186 |         e.optionalAttributes.insert(value: Select); | 
| 187 |         e.optionalAttributes.insert(value: As); | 
| 188 |     } | 
| 189 |  | 
| 190 |     /* xsl:when & xsl:if */ | 
| 191 |     { | 
| 192 |         ElementDescription<XSLTTokenLookup> &e = result[When]; | 
| 193 |  | 
| 194 |         e.requiredAttributes.insert(value: Test); | 
| 195 |  | 
| 196 |         result.insert(akey: If, avalue: e); | 
| 197 |     } | 
| 198 |  | 
| 199 |     /* xsl:sequence, xsl:for-each */ | 
| 200 |     { | 
| 201 |         ElementDescription<XSLTTokenLookup> &e = result[Sequence]; | 
| 202 |  | 
| 203 |         e.requiredAttributes.insert(value: Select); | 
| 204 |  | 
| 205 |         result.insert(akey: ForEach, avalue: e); | 
| 206 |     } | 
| 207 |  | 
| 208 |     /* xsl:comment */ | 
| 209 |     { | 
| 210 |         ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::Comment]; | 
| 211 |  | 
| 212 |         e.optionalAttributes.insert(value: Select); | 
| 213 |     } | 
| 214 |  | 
| 215 |     /* xsl:processing-instruction */ | 
| 216 |     { | 
| 217 |         ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::ProcessingInstruction]; | 
| 218 |  | 
| 219 |         e.requiredAttributes.insert(value: Name); | 
| 220 |         e.optionalAttributes.insert(value: Select); | 
| 221 |     } | 
| 222 |  | 
| 223 |     /* xsl:document */ | 
| 224 |     { | 
| 225 |         ElementDescription<XSLTTokenLookup> &e = result[Document]; | 
| 226 |  | 
| 227 |         e.optionalAttributes.insert(value: Validation); | 
| 228 |         e.optionalAttributes.insert(value: Type); | 
| 229 |     } | 
| 230 |  | 
| 231 |     /* xsl:element */ | 
| 232 |     { | 
| 233 |         ElementDescription<XSLTTokenLookup> &e = result[Element]; | 
| 234 |  | 
| 235 |         e.requiredAttributes.insert(value: Name); | 
| 236 |  | 
| 237 |         e.optionalAttributes.insert(value: Namespace); | 
| 238 |         e.optionalAttributes.insert(value: InheritNamespaces); | 
| 239 |         e.optionalAttributes.insert(value: UseAttributeSets); | 
| 240 |         e.optionalAttributes.insert(value: Validation); | 
| 241 |         e.optionalAttributes.insert(value: Type); | 
| 242 |     } | 
| 243 |  | 
| 244 |     /* xsl:attribute */ | 
| 245 |     { | 
| 246 |         ElementDescription<XSLTTokenLookup> &e = result[Attribute]; | 
| 247 |  | 
| 248 |         e.requiredAttributes.insert(value: Name); | 
| 249 |  | 
| 250 |         e.optionalAttributes.insert(value: Namespace); | 
| 251 |         e.optionalAttributes.insert(value: Select); | 
| 252 |         e.optionalAttributes.insert(value: Separator); | 
| 253 |         e.optionalAttributes.insert(value: Validation); | 
| 254 |         e.optionalAttributes.insert(value: Type); | 
| 255 |     } | 
| 256 |  | 
| 257 |     /* xsl:function */ | 
| 258 |     { | 
| 259 |         ElementDescription<XSLTTokenLookup> &e = result[Function]; | 
| 260 |  | 
| 261 |         e.requiredAttributes.insert(value: Name); | 
| 262 |  | 
| 263 |         e.optionalAttributes.insert(value: As); | 
| 264 |         e.optionalAttributes.insert(value: Override); | 
| 265 |     } | 
| 266 |  | 
| 267 |     /* xsl:param */ | 
| 268 |     { | 
| 269 |         ElementDescription<XSLTTokenLookup> &e = result[Param]; | 
| 270 |  | 
| 271 |         e.requiredAttributes.insert(value: Name); | 
| 272 |  | 
| 273 |         e.optionalAttributes.insert(value: Select); | 
| 274 |         e.optionalAttributes.insert(value: As); | 
| 275 |         e.optionalAttributes.insert(value: Required); | 
| 276 |         e.optionalAttributes.insert(value: Tunnel); | 
| 277 |     } | 
| 278 |  | 
| 279 |     /* xsl:namespace */ | 
| 280 |     { | 
| 281 |         ElementDescription<XSLTTokenLookup> &e = result[Namespace]; | 
| 282 |  | 
| 283 |         e.requiredAttributes.insert(value: Name); | 
| 284 |         e.optionalAttributes.insert(value: Select); | 
| 285 |     } | 
| 286 |  | 
| 287 |     /* xsl:call-template */ | 
| 288 |     { | 
| 289 |         ElementDescription<XSLTTokenLookup> &e = result[CallTemplate]; | 
| 290 |         e.requiredAttributes.insert(value: Name); | 
| 291 |     } | 
| 292 |  | 
| 293 |     /* xsl:perform-sort */ | 
| 294 |     { | 
| 295 |         ElementDescription<XSLTTokenLookup> &e = result[PerformSort]; | 
| 296 |         e.requiredAttributes.insert(value: Select); | 
| 297 |     } | 
| 298 |  | 
| 299 |     /* xsl:sort */ | 
| 300 |     { | 
| 301 |         ElementDescription<XSLTTokenLookup> &e = result[Sort]; | 
| 302 |  | 
| 303 |         e.optionalAttributes.reserve(asize: 7); | 
| 304 |         e.optionalAttributes.insert(value: Select); | 
| 305 |         e.optionalAttributes.insert(value: Lang); | 
| 306 |         e.optionalAttributes.insert(value: Order); | 
| 307 |         e.optionalAttributes.insert(value: Collation); | 
| 308 |         e.optionalAttributes.insert(value: Stable); | 
| 309 |         e.optionalAttributes.insert(value: CaseOrder); | 
| 310 |         e.optionalAttributes.insert(value: DataType); | 
| 311 |     } | 
| 312 |  | 
| 313 |     /* xsl:import-schema */ | 
| 314 |     { | 
| 315 |         ElementDescription<XSLTTokenLookup> &e = result[ImportSchema]; | 
| 316 |  | 
| 317 |         e.optionalAttributes.reserve(asize: 2); | 
| 318 |         e.optionalAttributes.insert(value: Namespace); | 
| 319 |         e.optionalAttributes.insert(value: SchemaLocation); | 
| 320 |     } | 
| 321 |  | 
| 322 |     /* xsl:message */ | 
| 323 |     { | 
| 324 |         ElementDescription<XSLTTokenLookup> &e = result[Message]; | 
| 325 |  | 
| 326 |         e.optionalAttributes.reserve(asize: 2); | 
| 327 |         e.optionalAttributes.insert(value: Select); | 
| 328 |         e.optionalAttributes.insert(value: Terminate); | 
| 329 |     } | 
| 330 |  | 
| 331 |     /* xsl:copy-of */ | 
| 332 |     { | 
| 333 |         ElementDescription<XSLTTokenLookup> &e = result[CopyOf]; | 
| 334 |  | 
| 335 |         e.requiredAttributes.insert(value: Select); | 
| 336 |  | 
| 337 |         e.optionalAttributes.reserve(asize: 2); | 
| 338 |         e.optionalAttributes.insert(value: CopyNamespaces); | 
| 339 |         e.optionalAttributes.insert(value: Type); | 
| 340 |         e.optionalAttributes.insert(value: Validation); | 
| 341 |     } | 
| 342 |  | 
| 343 |     /* xsl:copy */ | 
| 344 |     { | 
| 345 |         ElementDescription<XSLTTokenLookup> &e = result[Copy]; | 
| 346 |  | 
| 347 |         e.optionalAttributes.reserve(asize: 5); | 
| 348 |         e.optionalAttributes.insert(value: CopyNamespaces); | 
| 349 |         e.optionalAttributes.insert(value: InheritNamespaces); | 
| 350 |         e.optionalAttributes.insert(value: UseAttributeSets); | 
| 351 |         e.optionalAttributes.insert(value: Type); | 
| 352 |         e.optionalAttributes.insert(value: Validation); | 
| 353 |     } | 
| 354 |  | 
| 355 |     /* xsl:output */ | 
| 356 |     { | 
| 357 |         ElementDescription<XSLTTokenLookup> &e = result[Output]; | 
| 358 |  | 
| 359 |         e.optionalAttributes.reserve(asize: 17); | 
| 360 |         e.optionalAttributes.insert(value: Name); | 
| 361 |         e.optionalAttributes.insert(value: Method); | 
| 362 |         e.optionalAttributes.insert(value: ByteOrderMark); | 
| 363 |         e.optionalAttributes.insert(value: CdataSectionElements); | 
| 364 |         e.optionalAttributes.insert(value: DoctypePublic); | 
| 365 |         e.optionalAttributes.insert(value: DoctypeSystem); | 
| 366 |         e.optionalAttributes.insert(value: Encoding); | 
| 367 |         e.optionalAttributes.insert(value: EscapeUriAttributes); | 
| 368 |         e.optionalAttributes.insert(value: IncludeContentType); | 
| 369 |         e.optionalAttributes.insert(value: Indent); | 
| 370 |         e.optionalAttributes.insert(value: MediaType); | 
| 371 |         e.optionalAttributes.insert(value: NormalizationForm); | 
| 372 |         e.optionalAttributes.insert(value: OmitXmlDeclaration); | 
| 373 |         e.optionalAttributes.insert(value: Standalone); | 
| 374 |         e.optionalAttributes.insert(value: UndeclarePrefixes); | 
| 375 |         e.optionalAttributes.insert(value: UseCharacterMaps); | 
| 376 |         e.optionalAttributes.insert(value: Version); | 
| 377 |     } | 
| 378 |  | 
| 379 |     /* xsl:attribute-set */ | 
| 380 |     { | 
| 381 |         ElementDescription<XSLTTokenLookup> &e = result[AttributeSet]; | 
| 382 |  | 
| 383 |         e.requiredAttributes.insert(value: Name); | 
| 384 |         e.optionalAttributes.insert(value: UseAttributeSets); | 
| 385 |     } | 
| 386 |  | 
| 387 |     /* xsl:include and xsl:import. */ | 
| 388 |     { | 
| 389 |         ElementDescription<XSLTTokenLookup> &e = result[Include]; | 
| 390 |         e.requiredAttributes.insert(value: Href); | 
| 391 |         result[Import] = e; | 
| 392 |     } | 
| 393 |  | 
| 394 |     /* xsl:with-param */ | 
| 395 |     { | 
| 396 |         ElementDescription<XSLTTokenLookup> &e = result[WithParam]; | 
| 397 |         e.requiredAttributes.insert(value: Name); | 
| 398 |  | 
| 399 |         e.optionalAttributes.insert(value: Select); | 
| 400 |         e.optionalAttributes.insert(value: As); | 
| 401 |         e.optionalAttributes.insert(value: Tunnel); | 
| 402 |     } | 
| 403 |  | 
| 404 |     /* xsl:strip-space */ | 
| 405 |     { | 
| 406 |         ElementDescription<XSLTTokenLookup> &e = result[StripSpace]; | 
| 407 |         e.requiredAttributes.insert(value: Elements); | 
| 408 |  | 
| 409 |         result.insert(akey: PreserveSpace, avalue: e); | 
| 410 |     } | 
| 411 |  | 
| 412 |     /* xsl:result-document */ | 
| 413 |     { | 
| 414 |         ElementDescription<XSLTTokenLookup> &e = result[ResultDocument]; | 
| 415 |  | 
| 416 |         e.optionalAttributes.insert(value: ByteOrderMark); | 
| 417 |         e.optionalAttributes.insert(value: CdataSectionElements); | 
| 418 |         e.optionalAttributes.insert(value: DoctypePublic); | 
| 419 |         e.optionalAttributes.insert(value: DoctypeSystem); | 
| 420 |         e.optionalAttributes.insert(value: Encoding); | 
| 421 |         e.optionalAttributes.insert(value: EscapeUriAttributes); | 
| 422 |         e.optionalAttributes.insert(value: Format); | 
| 423 |         e.optionalAttributes.insert(value: Href); | 
| 424 |         e.optionalAttributes.insert(value: IncludeContentType); | 
| 425 |         e.optionalAttributes.insert(value: Indent); | 
| 426 |         e.optionalAttributes.insert(value: MediaType); | 
| 427 |         e.optionalAttributes.insert(value: Method); | 
| 428 |         e.optionalAttributes.insert(value: NormalizationForm); | 
| 429 |         e.optionalAttributes.insert(value: OmitXmlDeclaration); | 
| 430 |         e.optionalAttributes.insert(value: OutputVersion); | 
| 431 |         e.optionalAttributes.insert(value: Standalone); | 
| 432 |         e.optionalAttributes.insert(value: Type); | 
| 433 |         e.optionalAttributes.insert(value: UndeclarePrefixes); | 
| 434 |         e.optionalAttributes.insert(value: UseCharacterMaps); | 
| 435 |         e.optionalAttributes.insert(value: Validation); | 
| 436 |     } | 
| 437 |  | 
| 438 |     /* xsl:key */ | 
| 439 |     { | 
| 440 |         ElementDescription<XSLTTokenLookup> &e = result[Key]; | 
| 441 |  | 
| 442 |         e.requiredAttributes.insert(value: Name); | 
| 443 |         e.requiredAttributes.insert(value: Match); | 
| 444 |  | 
| 445 |         e.optionalAttributes.insert(value: Use); | 
| 446 |         e.optionalAttributes.insert(value: Collation); | 
| 447 |     } | 
| 448 |  | 
| 449 |     /* xsl:analyze-string */ | 
| 450 |     { | 
| 451 |         ElementDescription<XSLTTokenLookup> &e = result[AnalyzeString]; | 
| 452 |  | 
| 453 |         e.requiredAttributes.insert(value: Select); | 
| 454 |         e.requiredAttributes.insert(value: Regex); | 
| 455 |  | 
| 456 |         e.optionalAttributes.insert(value: Flags); | 
| 457 |     } | 
| 458 |  | 
| 459 |     /* xsl:matching-substring */ | 
| 460 |     { | 
| 461 |         /* We insert a default constructed value. */ | 
| 462 |         result[MatchingSubstring]; | 
| 463 |     } | 
| 464 |  | 
| 465 |     /* xsl:non-matching-substring */ | 
| 466 |     { | 
| 467 |         /* We insert a default constructed value. */ | 
| 468 |         result[NonMatchingSubstring]; | 
| 469 |     } | 
| 470 |  | 
| 471 |     Q_ASSERT(result.count() == ReservedForElements); | 
| 472 |  | 
| 473 |     return result; | 
| 474 | } | 
| 475 |  | 
| 476 | QHash<QString, int> XSLTTokenizer::createValidationAlternatives() | 
| 477 | { | 
| 478 |     QHash<QString, int> retval; | 
| 479 |  | 
| 480 |     retval.insert(akey: QLatin1String("preserve" ), avalue: 0); | 
| 481 |     retval.insert(akey: QLatin1String("strip" ), avalue: 1); | 
| 482 |     retval.insert(akey: QLatin1String("strict" ), avalue: 2); | 
| 483 |     retval.insert(akey: QLatin1String("lax" ), avalue: 3); | 
| 484 |  | 
| 485 |     return retval; | 
| 486 | } | 
| 487 |  | 
| 488 | bool XSLTTokenizer::whitespaceToSkip() const | 
| 489 | { | 
| 490 |     return m_stripWhitespace.top() && isWhitespace(); | 
| 491 | } | 
| 492 |  | 
| 493 | void XSLTTokenizer::unexpectedContent(const ReportContext::ErrorCode code) const | 
| 494 | { | 
| 495 |     QString message; | 
| 496 |  | 
| 497 |     ReportContext::ErrorCode effectiveCode = code; | 
| 498 |  | 
| 499 |     switch(tokenType()) | 
| 500 |     { | 
| 501 |         case QXmlStreamReader::StartElement: | 
| 502 |         { | 
| 503 |             if(isXSLT()) | 
| 504 |             { | 
| 505 |                 switch(currentElementName()) | 
| 506 |                 { | 
| 507 |                     case Include: | 
| 508 |                         effectiveCode = ReportContext::XTSE0170; | 
| 509 |                         break; | 
| 510 |                     case Import: | 
| 511 |                         effectiveCode = ReportContext::XTSE0190; | 
| 512 |                         break; | 
| 513 |                     default: | 
| 514 |                         ; | 
| 515 |                 } | 
| 516 |             } | 
| 517 |  | 
| 518 |             message = QtXmlPatterns::tr(sourceText: "Element %1 is not allowed at this location." ) | 
| 519 |                                        .arg(a: formatKeyword(keyword: name())); | 
| 520 |             break; | 
| 521 |         } | 
| 522 |         case QXmlStreamReader::Characters: | 
| 523 |         { | 
| 524 |             if(whitespaceToSkip()) | 
| 525 |                 return; | 
| 526 |  | 
| 527 |             message = QtXmlPatterns::tr(sourceText: "Text nodes are not allowed at this location." ); | 
| 528 |             break; | 
| 529 |         } | 
| 530 |         case QXmlStreamReader::Invalid: | 
| 531 |         { | 
| 532 |             /* It's an issue with well-formedness. */ | 
| 533 |             message = escape(input: errorString()); | 
| 534 |             break; | 
| 535 |         } | 
| 536 |         default: | 
| 537 |             Q_ASSERT(false); | 
| 538 |     } | 
| 539 |  | 
| 540 |     error(message, code: effectiveCode); | 
| 541 | } | 
| 542 |  | 
| 543 | void XSLTTokenizer::checkForParseError() const | 
| 544 | { | 
| 545 |     if(hasError()) | 
| 546 |     { | 
| 547 |         error(message: QtXmlPatterns::tr(sourceText: "Parse error: %1" ).arg(a: escape(input: errorString())), code: ReportContext::XTSE0010); | 
| 548 |     } | 
| 549 | } | 
| 550 |  | 
| 551 | QString XSLTTokenizer::readElementText() | 
| 552 | { | 
| 553 |     QString result; | 
| 554 |  | 
| 555 |     while(!atEnd()) | 
| 556 |     { | 
| 557 |         switch(readNext()) | 
| 558 |         { | 
| 559 |             case QXmlStreamReader::Characters: | 
| 560 |             { | 
| 561 |                 result += text().toString(); | 
| 562 |                 continue; | 
| 563 |             } | 
| 564 |             case QXmlStreamReader::Comment: | 
| 565 |             case QXmlStreamReader::ProcessingInstruction: | 
| 566 |                 continue; | 
| 567 |             case QXmlStreamReader::EndElement: | 
| 568 |                 return result; | 
| 569 |             default: | 
| 570 |                 unexpectedContent(); | 
| 571 |         } | 
| 572 |     } | 
| 573 |  | 
| 574 |     checkForParseError(); | 
| 575 |     return result; | 
| 576 | } | 
| 577 |  | 
| 578 | int XSLTTokenizer::commenceScanOnly() | 
| 579 | { | 
| 580 |     /* Do nothing, return a dummy value. */ | 
| 581 |     return 0; | 
| 582 | } | 
| 583 |  | 
| 584 | void XSLTTokenizer::resumeTokenizationFrom(const int position) | 
| 585 | { | 
| 586 |     /* Do nothing. */ | 
| 587 |     Q_UNUSED(position); | 
| 588 | } | 
| 589 |  | 
| 590 | void XSLTTokenizer::handleXSLTVersion(TokenSource::Queue *const to, | 
| 591 |                                       QStack<Token> *const queueOnExit, | 
| 592 |                                       const bool isXSLTElement, | 
| 593 |                                       const QXmlStreamAttributes *atts, | 
| 594 |                                       const bool generateCode, | 
| 595 |                                       const bool setGlobalVersion) | 
| 596 | { | 
| 597 |     const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT); | 
| 598 |     const QXmlStreamAttributes effectiveAtts(atts ? *atts : attributes()); | 
| 599 |  | 
| 600 |     if(!effectiveAtts.hasAttribute(namespaceUri: ns, name: QLatin1String("version" ))) | 
| 601 |         return; | 
| 602 |  | 
| 603 |     const QString attribute(effectiveAtts.value(namespaceUri: ns, name: QLatin1String("version" )).toString()); | 
| 604 |     const AtomicValue::Ptr number(Decimal::fromLexical(strNumeric: attribute)); | 
| 605 |  | 
| 606 |     if(number->hasError()) | 
| 607 |     { | 
| 608 |         error(message: QtXmlPatterns::tr(sourceText: "The value of the XSL-T version attribute "  | 
| 609 |                                            "must be a value of type %1, which %2 isn't." ).arg(args: formatType(np: m_namePool, type: BuiltinTypes::xsDecimal), | 
| 610 |                                                                                               args: formatData(data: attribute)), | 
| 611 |               code: ReportContext::XTSE0110); | 
| 612 |     } | 
| 613 |     else | 
| 614 |     { | 
| 615 |  | 
| 616 |         if(generateCode) | 
| 617 |         { | 
| 618 |             queueToken(token: Token(T_XSLT_VERSION, attribute), ts: to); | 
| 619 |             queueToken(token: T_CURLY_LBRACE, ts: to); | 
| 620 |         } | 
| 621 |  | 
| 622 |         const xsDecimal version = number->as<Numeric>()->toDecimal(); | 
| 623 |         if(version == 2.0) | 
| 624 |             m_processingMode.push(t: NormalProcessing); | 
| 625 |         else if(version == 1.0) | 
| 626 |         { | 
| 627 |             /* See section 3.6 Stylesheet Element discussing this. */ | 
| 628 |             warning(message: QtXmlPatterns::tr(sourceText: "Running an XSL-T 1.0 stylesheet with a 2.0 processor." )); | 
| 629 |             m_processingMode.push(t: BackwardsCompatible); | 
| 630 |  | 
| 631 |             if(setGlobalVersion) | 
| 632 |             { | 
| 633 |                 m_parseInfo->staticContext->setCompatModeEnabled(true); | 
| 634 |                 m_parseInfo->isBackwardsCompat.push(t: true); | 
| 635 |             } | 
| 636 |         } | 
| 637 |         else if(version > 2.0) | 
| 638 |             m_processingMode.push(t: ForwardCompatible); | 
| 639 |         else if(version < 2.0) | 
| 640 |             m_processingMode.push(t: BackwardsCompatible); | 
| 641 |     } | 
| 642 |  | 
| 643 |     if(generateCode) | 
| 644 |         queueOnExit->push(t: T_CURLY_RBRACE); | 
| 645 | } | 
| 646 |  | 
| 647 | void XSLTTokenizer::handleXMLBase(TokenSource::Queue *const to, | 
| 648 |                                   QStack<Token> *const queueOnExit, | 
| 649 |                                   const bool isInstruction, | 
| 650 |                                   const QXmlStreamAttributes *atts) | 
| 651 | { | 
| 652 |     const QXmlStreamAttributes effectiveAtts(atts ? *atts : m_currentAttributes); | 
| 653 |  | 
| 654 |     if(effectiveAtts.hasAttribute(qualifiedName: QLatin1String("xml:base" ))) | 
| 655 |     { | 
| 656 |         const QStringRef val(effectiveAtts.value(qualifiedName: QLatin1String("xml:base" ))); | 
| 657 |  | 
| 658 |         if(!val.isEmpty()) | 
| 659 |         { | 
| 660 |             if(isInstruction) | 
| 661 |             { | 
| 662 |                 queueToken(token: T_BASEURI, ts: to); | 
| 663 |                 queueToken(token: Token(T_STRING_LITERAL, val.toString()), ts: to); | 
| 664 |                 queueToken(token: T_CURLY_LBRACE, ts: to); | 
| 665 |                 queueOnExit->push(t: T_CURLY_RBRACE); | 
| 666 |             } | 
| 667 |             else | 
| 668 |             { | 
| 669 |                 queueToken(token: T_DECLARE, ts: to); | 
| 670 |                 queueToken(token: T_BASEURI, ts: to); | 
| 671 |                 queueToken(token: T_INTERNAL, ts: to); | 
| 672 |                 queueToken(token: Token(T_STRING_LITERAL, val.toString()), ts: to); | 
| 673 |                 queueToken(token: T_SEMI_COLON, ts: to); | 
| 674 |             } | 
| 675 |         } | 
| 676 |     } | 
| 677 | } | 
| 678 |  | 
| 679 | void XSLTTokenizer::handleStandardAttributes(const bool isXSLTElement) | 
| 680 | { | 
| 681 |     /* We're not necessarily StartElement, that's why we have atts passed in. */ | 
| 682 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 683 |  | 
| 684 |     if(m_hasHandledStandardAttributes) | 
| 685 |         return; | 
| 686 |  | 
| 687 |     m_hasHandledStandardAttributes = true; | 
| 688 |  | 
| 689 |     const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT); | 
| 690 |     const int len = m_currentAttributes.count(); | 
| 691 |  | 
| 692 |     for(int i = 0; i < len; ++i) | 
| 693 |     { | 
| 694 |         const QXmlStreamAttribute &att = m_currentAttributes.at(i); | 
| 695 |  | 
| 696 |         if(att.qualifiedName() == QLatin1String("xml:space" )) | 
| 697 |         { | 
| 698 |             /* We raise an error if the value is not recognized. | 
| 699 |              * | 
| 700 |              * Extensible Markup Language (XML) 1.0 (Fourth Edition), 2.10 | 
| 701 |              * White Space Handling: | 
| 702 |              * | 
| 703 |              * 'This specification does not give meaning to any value of | 
| 704 |              * xml:space other than "default" and "preserve". It is an error | 
| 705 |              * for other values to be specified; the XML processor may report | 
| 706 |              * the error or may recover by ignoring the attribute specification | 
| 707 |              * or by reporting the (erroneous) value to the application.' */ | 
| 708 |             m_stripWhitespace.push(t: readToggleAttribute(attributeName: QLatin1String("xml:space" ), | 
| 709 |                                                        isTrue: QLatin1String("default" ), | 
| 710 |                                                        isFalse: QLatin1String("preserve" ), | 
| 711 |                                                        atts: &m_currentAttributes)); | 
| 712 |         } | 
| 713 |  | 
| 714 |         if(att.namespaceUri() != ns) | 
| 715 |             continue; | 
| 716 |  | 
| 717 |         switch(toToken(value: att.name())) | 
| 718 |         { | 
| 719 |             case Type: | 
| 720 |             case Validation: | 
| 721 |             case UseAttributeSets: | 
| 722 |             case Version: | 
| 723 |                 /* These are handled by other function such as | 
| 724 |                  * handleValidationAttributes() and handleXSLTVersion(). */ | 
| 725 |                 continue; | 
| 726 |             default: | 
| 727 |             { | 
| 728 |                 if(!isXSLTElement) /* validateElement() will take care of it, and we | 
| 729 |                                     * don't want to flag non-standard XSL-T attributes. */ | 
| 730 |                 { | 
| 731 |                     error(message: QtXmlPatterns::tr(sourceText: "Unknown XSL-T attribute %1." ) | 
| 732 |                                                       .arg(a: formatKeyword(keyword: att.name())), | 
| 733 |                           code: ReportContext::XTSE0805); | 
| 734 |                 } | 
| 735 |             } | 
| 736 |         } | 
| 737 |     } | 
| 738 | } | 
| 739 |  | 
| 740 | void XSLTTokenizer::handleValidationAttributes(const bool isLRE) const | 
| 741 | { | 
| 742 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 743 |  | 
| 744 |     const QString ns(isLRE ? QString() : CommonNamespaces::XSLT); | 
| 745 |  | 
| 746 |     const bool hasValidation = hasAttribute(namespaceURI: ns, localName: QLatin1String("validation" )); | 
| 747 |     const bool hasType = hasAttribute(namespaceURI: ns, localName: QLatin1String("type" )); | 
| 748 |  | 
| 749 |     if(!hasType && !hasValidation) | 
| 750 |         return; | 
| 751 |  | 
| 752 |     if(hasType && hasValidation) | 
| 753 |     { | 
| 754 |         error(message: QtXmlPatterns::tr(sourceText: "Attribute %1 and %2 are mutually exclusive." ) | 
| 755 |                                           .arg(args: formatKeyword(keyword: QLatin1String("validation" )), | 
| 756 |                                                args: formatKeyword(keyword: QLatin1String("type" ))), | 
| 757 |               code: ReportContext::XTSE1505); | 
| 758 |     } | 
| 759 |  | 
| 760 |     /* QXmlStreamReader surely doesn't make this easy. */ | 
| 761 |     QXmlStreamAttribute validationAttribute; | 
| 762 |     int len = m_currentAttributes.count(); | 
| 763 |  | 
| 764 |     for(int i = 0; i < len; ++i) | 
| 765 |     { | 
| 766 |         const QXmlStreamAttribute &at = m_currentAttributes.at(i); | 
| 767 |         if(at.name() == QLatin1String("validation" ) && at.namespaceUri() == ns) | 
| 768 |             validationAttribute = at; | 
| 769 |     } | 
| 770 |  | 
| 771 |     Q_ASSERT_X(!validationAttribute.name().isNull(), Q_FUNC_INFO, | 
| 772 |                "We should always find the attribute." ); | 
| 773 |  | 
| 774 |     /* We don't care about the return value, we just want to check it's a valid | 
| 775 |      * one. */ | 
| 776 |     readAlternativeAttribute(alternatives: m_validationAlternatives, | 
| 777 |                              attr: validationAttribute); | 
| 778 | } | 
| 779 |  | 
| 780 | Tokenizer::Token XSLTTokenizer::nextToken(XPATHLTYPE *const sourceLocator) | 
| 781 | { | 
| 782 |     Q_UNUSED(sourceLocator); | 
| 783 |  | 
| 784 |     if(m_tokenSource.isEmpty()) | 
| 785 |     { | 
| 786 |         switch(m_state.top()) | 
| 787 |         { | 
| 788 |             case OutsideDocumentElement: | 
| 789 |                 outsideDocumentElement(); | 
| 790 |                 break; | 
| 791 |             case InsideStylesheetModule: | 
| 792 |                 insideStylesheetModule(); | 
| 793 |                 break; | 
| 794 |             case InsideSequenceConstructor: | 
| 795 |                 insideSequenceConstructor(to: &m_tokenSource); | 
| 796 |                 break; | 
| 797 |         } | 
| 798 |  | 
| 799 |         if(m_tokenSource.isEmpty()) | 
| 800 |         { | 
| 801 |             *sourceLocator = currentSourceLocator(); | 
| 802 |             return Token(T_END_OF_FILE); | 
| 803 |         } | 
| 804 |         else | 
| 805 |             return m_tokenSource.head()->nextToken(sourceLocator); | 
| 806 |     } | 
| 807 |     else | 
| 808 |     { | 
| 809 |         do | 
| 810 |         { | 
| 811 |             const Token candidate(m_tokenSource.head()->nextToken(sourceLocator)); | 
| 812 |             if (candidate.type == T_END_OF_FILE) | 
| 813 |                 m_tokenSource.dequeue(); | 
| 814 |             else | 
| 815 |                 return candidate; | 
| 816 |         } | 
| 817 |         while(!m_tokenSource.isEmpty()); | 
| 818 |  | 
| 819 |         /* Now we will resume parsing inside the regular XSL-T(XML) file. */ | 
| 820 |         return nextToken(sourceLocator); | 
| 821 |     } | 
| 822 | } | 
| 823 |  | 
| 824 | bool XSLTTokenizer::isElement(const XSLTTokenLookup::NodeName &name) const | 
| 825 | { | 
| 826 |     Q_ASSERT(isXSLT()); | 
| 827 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || | 
| 828 |              tokenType() == QXmlStreamReader::EndElement); | 
| 829 |  | 
| 830 |     return currentElementName() == name; | 
| 831 | } | 
| 832 |  | 
| 833 | inline bool XSLTTokenizer::isXSLT() const | 
| 834 | { | 
| 835 |     Q_ASSERT_X(tokenType() == QXmlStreamReader::StartElement || | 
| 836 |                tokenType() == QXmlStreamReader::EndElement, | 
| 837 |                Q_FUNC_INFO, "The current token state must be StartElement or EndElement." ); | 
| 838 |     /* Possible optimization: let MaintainingReader set an m_isXSLT which we | 
| 839 |      * read. */ | 
| 840 |     return namespaceUri() == CommonNamespaces::XSLT; | 
| 841 | } | 
| 842 |  | 
| 843 | void XSLTTokenizer::queueOnExit(QStack<Token> &source, | 
| 844 |                                 TokenSource::Queue *const destination) | 
| 845 | { | 
| 846 |     while(!source.isEmpty()) | 
| 847 |         queueToken(token: source.pop(), ts: destination); | 
| 848 | } | 
| 849 |  | 
| 850 | void XSLTTokenizer::outsideDocumentElement() | 
| 851 | { | 
| 852 |     while(!atEnd()) | 
| 853 |     { | 
| 854 |         switch(readNext()) | 
| 855 |         { | 
| 856 |             case QXmlStreamReader::StartElement: | 
| 857 |             { | 
| 858 |                 /* First, we synthesize one of the built-in templates, | 
| 859 |                  * see section 6.6 Built-in Template Rules. | 
| 860 |                  * | 
| 861 |                  * Note that insideStylesheetModule() can be called multiple | 
| 862 |                  * times so we can't do it there.  */ | 
| 863 |                 { | 
| 864 |                     /* Start with the one for text nodes and attributes. | 
| 865 |                      * declare template matches (text() | @*) mode #all | 
| 866 |                      * { | 
| 867 |                      *      text{.} | 
| 868 |                      * }; | 
| 869 |                      */ | 
| 870 |  | 
| 871 |                     /* declare template matches (text() | @*) */ | 
| 872 |                     queueToken(token: T_DECLARE, ts: &m_tokenSource); | 
| 873 |                     queueToken(token: T_TEMPLATE, ts: &m_tokenSource); | 
| 874 |                     queueToken(token: T_MATCHES, ts: &m_tokenSource); | 
| 875 |                     queueToken(token: T_LPAREN, ts: &m_tokenSource); | 
| 876 |                     queueToken(token: T_TEXT, ts: &m_tokenSource); | 
| 877 |                     queueToken(token: T_LPAREN, ts: &m_tokenSource); | 
| 878 |                     queueToken(token: T_RPAREN, ts: &m_tokenSource); | 
| 879 |                     queueToken(token: T_BAR, ts: &m_tokenSource); | 
| 880 |                     queueToken(token: T_AT_SIGN, ts: &m_tokenSource); | 
| 881 |                     queueToken(token: T_STAR, ts: &m_tokenSource); | 
| 882 |                     queueToken(token: T_RPAREN, ts: &m_tokenSource); | 
| 883 |  | 
| 884 |                     /* mode #all */ | 
| 885 |                     queueToken(token: T_MODE, ts: &m_tokenSource); | 
| 886 |                     queueToken(token: Token(T_NCNAME, QLatin1String("#all" )), ts: &m_tokenSource); | 
| 887 |                     queueToken(token: T_CURLY_LBRACE, ts: &m_tokenSource); | 
| 888 |  | 
| 889 |                     /* text{.} { */ | 
| 890 |                     queueToken(token: T_TEXT, ts: &m_tokenSource); | 
| 891 |                     queueToken(token: T_CURLY_LBRACE, ts: &m_tokenSource); | 
| 892 |                     queueToken(token: T_DOT, ts: &m_tokenSource); | 
| 893 |                     queueToken(token: T_CURLY_RBRACE, ts: &m_tokenSource); | 
| 894 |  | 
| 895 |                     /* }; */ | 
| 896 |                     queueToken(token: T_CURLY_RBRACE, ts: &m_tokenSource); | 
| 897 |                     queueToken(token: T_SEMI_COLON, ts: &m_tokenSource); | 
| 898 |                 } | 
| 899 |  | 
| 900 |                 if(isXSLT() && isStylesheetElement()) | 
| 901 |                 { | 
| 902 |                     handleStandardAttributes(isXSLTElement: true); | 
| 903 |                     QStack<Token> onExitTokens; | 
| 904 |                     handleXMLBase(to: &m_tokenSource, queueOnExit: &onExitTokens, isInstruction: false); | 
| 905 |                     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: true, atts: 0, generateCode: false, setGlobalVersion: true); | 
| 906 |                     validateElement(); | 
| 907 |                     queueNamespaceDeclarations(ts: &m_tokenSource, target: 0, isDeclaration: true); | 
| 908 |  | 
| 909 |                     /* We're a regular stylesheet. */ | 
| 910 |  | 
| 911 |                     pushState(nextState: InsideStylesheetModule); | 
| 912 |                     insideStylesheetModule(); | 
| 913 |                 } | 
| 914 |                 else | 
| 915 |                 { | 
| 916 |                     /* We're a simplified stylesheet. */ | 
| 917 |  | 
| 918 |                     if(!hasAttribute(namespaceURI: CommonNamespaces::XSLT, localName: QLatin1String("version" ))) | 
| 919 |                     { | 
| 920 |                         error(message: QtXmlPatterns::tr(sourceText: "In a simplified stylesheet module, attribute %1 must be present." ) | 
| 921 |                                                           .arg(a: formatKeyword(keyword: QLatin1String("version" ))), | 
| 922 |                               code: ReportContext::XTSE0010); | 
| 923 |                     } | 
| 924 |  | 
| 925 |                     QStack<Token> onExitTokens; | 
| 926 |  | 
| 927 |                     /* We synthesize this as exemplified in | 
| 928 |                      * 3.7 Simplified Stylesheet Modules. */ | 
| 929 |                     queueToken(token: T_DECLARE, ts: &m_tokenSource); | 
| 930 |                     queueToken(token: T_TEMPLATE, ts: &m_tokenSource); | 
| 931 |                     queueToken(token: T_MATCHES, ts: &m_tokenSource); | 
| 932 |                     queueToken(token: T_LPAREN, ts: &m_tokenSource); | 
| 933 |                     queueToken(token: T_SLASH, ts: &m_tokenSource); | 
| 934 |                     queueToken(token: T_RPAREN, ts: &m_tokenSource); | 
| 935 |                     queueToken(token: T_CURLY_LBRACE, ts: &m_tokenSource); | 
| 936 |                     pushState(nextState: InsideSequenceConstructor); | 
| 937 |  | 
| 938 |                     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: false, atts: 0, generateCode: true); | 
| 939 |                     handleStandardAttributes(isXSLTElement: false); | 
| 940 |  | 
| 941 |                     insideSequenceConstructor(to: &m_tokenSource, initialAdvance: false); | 
| 942 |  | 
| 943 |                     queueOnExit(source&: onExitTokens, destination: &m_tokenSource); | 
| 944 |                     queueToken(token: T_CURLY_RBRACE, ts: &m_tokenSource); | 
| 945 |                     queueToken(token: T_CURLY_RBRACE, ts: &m_tokenSource); | 
| 946 |                     queueToken(token: T_SEMI_COLON, ts: &m_tokenSource); | 
| 947 |                 } | 
| 948 |  | 
| 949 |                 queueToken(token: T_APPLY_TEMPLATE, ts: &m_tokenSource); | 
| 950 |                 queueToken(token: T_LPAREN, ts: &m_tokenSource); | 
| 951 |                 queueToken(token: T_RPAREN, ts: &m_tokenSource); | 
| 952 |  | 
| 953 |                 break; | 
| 954 |             } | 
| 955 |             default: | 
| 956 |                 /* Do nothing. */; | 
| 957 |         } | 
| 958 |     } | 
| 959 |     checkForParseError(); | 
| 960 | } | 
| 961 |  | 
| 962 | void XSLTTokenizer::queueToken(const Token &token, | 
| 963 |                                TokenSource::Queue *const to) | 
| 964 | { | 
| 965 |     TokenSource::Queue *const effective = to ? to : &m_tokenSource; | 
| 966 |  | 
| 967 |     effective->enqueue(t: TokenSource::Ptr(new SingleTokenContainer(token, currentSourceLocator()))); | 
| 968 | } | 
| 969 |  | 
| 970 | void XSLTTokenizer::pushState(const State nextState) | 
| 971 | { | 
| 972 |     m_state.push(t: nextState); | 
| 973 | } | 
| 974 |  | 
| 975 | void XSLTTokenizer::leaveState() | 
| 976 | { | 
| 977 |     m_state.pop(); | 
| 978 | } | 
| 979 |  | 
| 980 | void XSLTTokenizer::insideTemplate() | 
| 981 | { | 
| 982 |     const bool hasPriority  = hasAttribute(localName: QLatin1String("priority" )); | 
| 983 |     const bool hasMatch     = hasAttribute(localName: QLatin1String("match" )); | 
| 984 |     const bool hasName      = hasAttribute(localName: QLatin1String("name" )); | 
| 985 |     const bool hasMode      = hasAttribute(localName: QLatin1String("mode" )); | 
| 986 |     const bool hasAs        = hasAttribute(localName: QLatin1String("as" )); | 
| 987 |  | 
| 988 |     if(!hasMatch && | 
| 989 |        (hasMode || | 
| 990 |         hasPriority)) | 
| 991 |     { | 
| 992 |         error(message: QtXmlPatterns::tr(sourceText: "If element %1 has no attribute %2, it cannot have attribute %3 or %4." ) | 
| 993 |                          .arg(args: formatKeyword(keyword: QLatin1String("template" )), | 
| 994 |                               args: formatKeyword(keyword: QLatin1String("match" )), | 
| 995 |                               args: formatKeyword(keyword: QLatin1String("mode" )), | 
| 996 |                               args: formatKeyword(keyword: QLatin1String("priority" ))), | 
| 997 |               code: ReportContext::XTSE0500); | 
| 998 |     } | 
| 999 |     else if(!hasMatch && !hasName) | 
| 1000 |     { | 
| 1001 |         error(message: QtXmlPatterns::tr(sourceText: "Element %1 must have at least one of the attributes %2 or %3." ) | 
| 1002 |                          .arg(args: formatKeyword(keyword: QLatin1String("template" )), | 
| 1003 |                               args: formatKeyword(keyword: QLatin1String("name" )), | 
| 1004 |                               args: formatKeyword(keyword: QLatin1String("match" ))), | 
| 1005 |               code: ReportContext::XTSE0500); | 
| 1006 |     } | 
| 1007 |  | 
| 1008 |     queueToken(token: T_DECLARE, to: &m_tokenSource); | 
| 1009 |     queueToken(token: T_TEMPLATE, to: &m_tokenSource); | 
| 1010 |  | 
| 1011 |     if(hasName) | 
| 1012 |     { | 
| 1013 |         queueToken(token: T_NAME, to: &m_tokenSource); | 
| 1014 |         queueToken(token: Token(T_QNAME, readAttribute(localName: QLatin1String("name" ))), to: &m_tokenSource); | 
| 1015 |     } | 
| 1016 |  | 
| 1017 |     if(hasMatch) | 
| 1018 |     { | 
| 1019 |         queueToken(token: T_MATCHES, to: &m_tokenSource); | 
| 1020 |         queueExpression(expr: readAttribute(localName: QLatin1String("match" )), to: &m_tokenSource); | 
| 1021 |     } | 
| 1022 |  | 
| 1023 |     if(hasMode) | 
| 1024 |     { | 
| 1025 |         const QString modeString(readAttribute(localName: QLatin1String("mode" )).simplified()); | 
| 1026 |  | 
| 1027 |         if(modeString.isEmpty()) | 
| 1028 |         { | 
| 1029 |             error(message: QtXmlPatterns::tr(sourceText: "At least one mode must be specified in the %1-attribute on element %2." ) | 
| 1030 |                              .arg(args: formatKeyword(keyword: QLatin1String("mode" )), | 
| 1031 |                                   args: formatKeyword(keyword: QLatin1String("template" ))), | 
| 1032 |                   code: ReportContext::XTSE0500); | 
| 1033 |         } | 
| 1034 |  | 
| 1035 |         queueToken(token: T_MODE, to: &m_tokenSource); | 
| 1036 |  | 
| 1037 |         const QStringList modeList(modeString.split(sep: QLatin1Char(' '))); | 
| 1038 |  | 
| 1039 |         for(int i = 0; i < modeList.count(); ++i) | 
| 1040 |         { | 
| 1041 |             const QString &mode = modeList.at(i); | 
| 1042 |  | 
| 1043 |             queueToken(token: Token(mode.contains(c: QLatin1Char(':')) ? T_QNAME : T_NCNAME, mode), to: &m_tokenSource); | 
| 1044 |  | 
| 1045 |             if(i < modeList.count() - 1) | 
| 1046 |                 queueToken(token: T_COMMA, to: &m_tokenSource); | 
| 1047 |         } | 
| 1048 |     } | 
| 1049 |  | 
| 1050 |     if(hasPriority) | 
| 1051 |     { | 
| 1052 |         queueToken(token: T_PRIORITY, to: &m_tokenSource); | 
| 1053 |         queueToken(token: Token(T_STRING_LITERAL, readAttribute(localName: QLatin1String("priority" ))), to: &m_tokenSource); | 
| 1054 |     } | 
| 1055 |  | 
| 1056 |     QStack<Token> onExitTokens; | 
| 1057 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 1058 |  | 
| 1059 |     /* queueParams moves the reader so we need to freeze the attributes. */ | 
| 1060 |     const QXmlStreamAttributes atts(m_currentAttributes); | 
| 1061 |     handleStandardAttributes(isXSLTElement: true); | 
| 1062 |     queueToken(token: T_LPAREN, to: &m_tokenSource); | 
| 1063 |     queueParams(parentName: Template, to: &m_tokenSource); | 
| 1064 |     queueToken(token: T_RPAREN, to: &m_tokenSource); | 
| 1065 |  | 
| 1066 |     if(hasAs) | 
| 1067 |     { | 
| 1068 |         queueToken(token: T_AS, to: &m_tokenSource); | 
| 1069 |         queueSequenceType(expr: atts.value(qualifiedName: QLatin1String("as" )).toString()); | 
| 1070 |     } | 
| 1071 |  | 
| 1072 |     queueToken(token: T_CURLY_LBRACE, to: &m_tokenSource); | 
| 1073 |  | 
| 1074 |     handleXMLBase(to: &m_tokenSource, queueOnExit: &onExitTokens, isInstruction: true, atts: &atts); | 
| 1075 |     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: true, atts: &atts); | 
| 1076 |     pushState(nextState: InsideSequenceConstructor); | 
| 1077 |     startStorageOfCurrent(to: &m_tokenSource); | 
| 1078 |     insideSequenceConstructor(to: &m_tokenSource, queueOnExit&: onExitTokens, initialAdvance: false); | 
| 1079 |     queueOnExit(source&: onExitTokens, destination: &m_tokenSource); | 
| 1080 | } | 
| 1081 |  | 
| 1082 | void XSLTTokenizer::queueExpression(const QString &expr, | 
| 1083 |                                     TokenSource::Queue *const to, | 
| 1084 |                                     const bool wrapWithParantheses) | 
| 1085 | { | 
| 1086 |     TokenSource::Queue *const effectiveTo = to ? to : &m_tokenSource; | 
| 1087 |  | 
| 1088 |     if(wrapWithParantheses) | 
| 1089 |         queueToken(token: T_LPAREN, to: effectiveTo); | 
| 1090 |  | 
| 1091 |     effectiveTo->enqueue(t: TokenSource::Ptr(new XQueryTokenizer(expr, queryURI()))); | 
| 1092 |  | 
| 1093 |     if(wrapWithParantheses) | 
| 1094 |         queueToken(token: T_RPAREN, to: effectiveTo); | 
| 1095 | } | 
| 1096 |  | 
| 1097 | void XSLTTokenizer::queueAVT(const QString &expr, | 
| 1098 |                              TokenSource::Queue *const to) | 
| 1099 | { | 
| 1100 |     queueToken(token: T_AVT, to); | 
| 1101 |     queueToken(token: T_LPAREN, to); | 
| 1102 |     to->enqueue(t: TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(), | 
| 1103 |                                                                XQueryTokenizer::QuotAttributeContent))); | 
| 1104 |     queueToken(token: T_RPAREN, to); | 
| 1105 | } | 
| 1106 |  | 
| 1107 | void XSLTTokenizer::queueSequenceType(const QString &expr) | 
| 1108 | { | 
| 1109 |     m_tokenSource.enqueue(t: TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(), | 
| 1110 |                                                                          XQueryTokenizer::ItemType))); | 
| 1111 | } | 
| 1112 |  | 
| 1113 | void XSLTTokenizer::commencingExpression(bool &hasWrittenExpression, | 
| 1114 |                                          TokenSource::Queue *const to) | 
| 1115 | { | 
| 1116 |     if(hasWrittenExpression) | 
| 1117 |         queueToken(token: T_COMMA, to); | 
| 1118 |     else | 
| 1119 |         hasWrittenExpression = true; | 
| 1120 | } | 
| 1121 |  | 
| 1122 | void XSLTTokenizer::queueEmptySequence(TokenSource::Queue *const to) | 
| 1123 | { | 
| 1124 |     queueToken(token: T_LPAREN, to); | 
| 1125 |     queueToken(token: T_RPAREN, to); | 
| 1126 | } | 
| 1127 |  | 
| 1128 | void XSLTTokenizer::insideChoose(TokenSource::Queue *const to) | 
| 1129 | { | 
| 1130 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 1131 |     bool hasHandledOtherwise = false; | 
| 1132 |     bool hasEncounteredAtLeastOneWhen = false; | 
| 1133 |  | 
| 1134 |     while(!atEnd()) | 
| 1135 |     { | 
| 1136 |         switch(readNext()) | 
| 1137 |         { | 
| 1138 |             case QXmlStreamReader::StartElement: | 
| 1139 |             { | 
| 1140 |                 if(isXSLT()) | 
| 1141 |                 { | 
| 1142 |                     QStack<Token> onExitTokens; | 
| 1143 |                     handleStandardAttributes(isXSLTElement: true); | 
| 1144 |                     validateElement(); | 
| 1145 |  | 
| 1146 |                     switch(currentElementName()) | 
| 1147 |                     { | 
| 1148 |                         case When: | 
| 1149 |                         { | 
| 1150 |                             if(hasHandledOtherwise) | 
| 1151 |                             { | 
| 1152 |                                 error(message: QtXmlPatterns::tr(sourceText: "Element %1 must come last." ) | 
| 1153 |                                                                   .arg(a: formatKeyword(keyword: QLatin1String("otherwise" ))), | 
| 1154 |                                       code: ReportContext::XTSE0010); | 
| 1155 |                             } | 
| 1156 |  | 
| 1157 |                             queueToken(token: T_IF, to); | 
| 1158 |                             queueToken(token: T_LPAREN, to); | 
| 1159 |                             queueExpression(expr: readAttribute(localName: QLatin1String("test" )), to); | 
| 1160 |                             queueToken(token: T_RPAREN, to); | 
| 1161 |                             queueToken(token: T_THEN, to); | 
| 1162 |                             queueToken(token: T_LPAREN, to); | 
| 1163 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1164 |                             insideSequenceConstructor(to); | 
| 1165 |                             queueToken(token: T_RPAREN, to); | 
| 1166 |                             Q_ASSERT(tokenType() == QXmlStreamReader::EndElement); | 
| 1167 |                             queueToken(token: T_ELSE, to); | 
| 1168 |                             hasEncounteredAtLeastOneWhen = true; | 
| 1169 |                             queueOnExit(source&: onExitTokens, destination: to); | 
| 1170 |                             break; | 
| 1171 |                         } | 
| 1172 |                         case Otherwise: | 
| 1173 |                         { | 
| 1174 |                             if(!hasEncounteredAtLeastOneWhen) | 
| 1175 |                             { | 
| 1176 |                                 error(message: QtXmlPatterns::tr(sourceText: "At least one %1-element must occur before %2." ) | 
| 1177 |                                                                   .arg(args: formatKeyword(keyword: QLatin1String("when" )), | 
| 1178 |                                                                        args: formatKeyword(keyword: QLatin1String("otherwise" ))), | 
| 1179 |                                       code: ReportContext::XTSE0010); | 
| 1180 |                             } | 
| 1181 |                             else if(hasHandledOtherwise) | 
| 1182 |                             { | 
| 1183 |                                 error(message: QtXmlPatterns::tr(sourceText: "Only one %1-element can appear." ) | 
| 1184 |                                                                   .arg(a: formatKeyword(keyword: QLatin1String("otherwise" ))), | 
| 1185 |                                       code: ReportContext::XTSE0010); | 
| 1186 |                             } | 
| 1187 |  | 
| 1188 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1189 |                             queueToken(token: T_LPAREN, to); | 
| 1190 |                             insideSequenceConstructor(to, initialAdvance: to); | 
| 1191 |                             queueToken(token: T_RPAREN, to); | 
| 1192 |                             hasHandledOtherwise = true; | 
| 1193 |                             queueOnExit(source&: onExitTokens, destination: to); | 
| 1194 |                             break; | 
| 1195 |                         } | 
| 1196 |                         default: | 
| 1197 |                             unexpectedContent(); | 
| 1198 |                     } | 
| 1199 |                 } | 
| 1200 |                 else | 
| 1201 |                     unexpectedContent(); | 
| 1202 |                 break; | 
| 1203 |             } | 
| 1204 |             case QXmlStreamReader::EndElement: | 
| 1205 |             { | 
| 1206 |                 if(isXSLT()) | 
| 1207 |                 { | 
| 1208 |                     switch(currentElementName()) | 
| 1209 |                     { | 
| 1210 |                         case Choose: | 
| 1211 |                         { | 
| 1212 |                             if(!hasEncounteredAtLeastOneWhen) | 
| 1213 |                             { | 
| 1214 |                                 error(message: QtXmlPatterns::tr(sourceText: "At least one %1-element must occur inside %2." ) | 
| 1215 |                                                                   .arg(args: formatKeyword(keyword: QLatin1String("when" )), | 
| 1216 |                                                                        args: formatKeyword(keyword: QLatin1String("choose" ))), | 
| 1217 |                                       code: ReportContext::XTSE0010); | 
| 1218 |                             } | 
| 1219 |  | 
| 1220 |                             if(!hasHandledOtherwise) | 
| 1221 |                                 queueEmptySequence(to); | 
| 1222 |                             return; | 
| 1223 |                         } | 
| 1224 |                         case Otherwise: | 
| 1225 |                             continue; | 
| 1226 |                         default: | 
| 1227 |                             unexpectedContent(); | 
| 1228 |                     } | 
| 1229 |                 } | 
| 1230 |                 else | 
| 1231 |                     unexpectedContent(); | 
| 1232 |                 break; | 
| 1233 |             } | 
| 1234 |             case QXmlStreamReader::Comment: | 
| 1235 |             case QXmlStreamReader::ProcessingInstruction: | 
| 1236 |                 continue; | 
| 1237 |             case QXmlStreamReader::Characters: | 
| 1238 |             { | 
| 1239 |                 /* We ignore regardless of what xml:space says, see step 4 in | 
| 1240 |                  * 4.2 Stripping Whitespace from the Stylesheet. */ | 
| 1241 |                 if(isWhitespace()) | 
| 1242 |                     continue; | 
| 1243 |                 Q_FALLTHROUGH(); | 
| 1244 |             } | 
| 1245 |             default: | 
| 1246 |                 unexpectedContent(); | 
| 1247 |                 break; | 
| 1248 |         } | 
| 1249 |     } | 
| 1250 |     checkForParseError(); | 
| 1251 | } | 
| 1252 |  | 
| 1253 | bool XSLTTokenizer::queueSelectOrSequenceConstructor(const ReportContext::ErrorCode code, | 
| 1254 |                                                      const bool emptynessAllowed, | 
| 1255 |                                                      TokenSource::Queue *const to, | 
| 1256 |                                                      const QXmlStreamAttributes *const attsP, | 
| 1257 |                                                      const bool queueEmptyOnEmpty) | 
| 1258 | { | 
| 1259 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || attsP); | 
| 1260 |     const NodeName elementName(currentElementName()); | 
| 1261 |     const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes); | 
| 1262 |  | 
| 1263 |     if(atts.hasAttribute(qualifiedName: QLatin1String("select" ))) | 
| 1264 |     { | 
| 1265 |         queueExpression(expr: atts.value(qualifiedName: QLatin1String("select" )).toString(), to); | 
| 1266 |  | 
| 1267 |         /* First, verify that we don't have a body. */ | 
| 1268 |         if(skipSubTree(exitOnContent: true)) | 
| 1269 |         { | 
| 1270 |             error(message: QtXmlPatterns::tr(sourceText: "When attribute %1 is present on %2, a sequence "  | 
| 1271 |                                                "constructor cannot be used." ).arg(args: formatKeyword(keyword: QLatin1String("select" )), | 
| 1272 |                                                                                   args: formatKeyword(keyword: toString(token: elementName))), | 
| 1273 |                   code); | 
| 1274 |         } | 
| 1275 |  | 
| 1276 |         return true; | 
| 1277 |     } | 
| 1278 |     else | 
| 1279 |     { | 
| 1280 |         pushState(nextState: InsideSequenceConstructor); | 
| 1281 |         if(!insideSequenceConstructor(to, initialAdvance: true, queueEmptyOnEmpty) && !emptynessAllowed) | 
| 1282 |         { | 
| 1283 |             error(message: QtXmlPatterns::tr(sourceText: "Element %1 must have either a %2-attribute "  | 
| 1284 |                                                "or a sequence constructor." ).arg(args: formatKeyword(keyword: toString(token: elementName)), | 
| 1285 |                                                                                  args: formatKeyword(keyword: QLatin1String("select" ))), | 
| 1286 |                   code); | 
| 1287 |  | 
| 1288 |         } | 
| 1289 |  | 
| 1290 |         return false; | 
| 1291 |     } | 
| 1292 | } | 
| 1293 |  | 
| 1294 | void XSLTTokenizer::queueSimpleContentConstructor(const ReportContext::ErrorCode code, | 
| 1295 |                                                   const bool emptynessAllowed, | 
| 1296 |                                                   TokenSource::Queue *const to, | 
| 1297 |                                                   const bool selectOnlyFirst) | 
| 1298 | { | 
| 1299 |     queueToken(token: T_INTERNAL_NAME, to); | 
| 1300 |     queueToken(token: Token(T_NCNAME, QLatin1String("generic-string-join" )), to); | 
| 1301 |     queueToken(token: T_LPAREN, to); | 
| 1302 |  | 
| 1303 |     /* We have to read the attribute before calling | 
| 1304 |      * queueSelectOrSequenceConstructor(), since it advances the reader. */ | 
| 1305 |     const bool hasSeparator = m_currentAttributes.hasAttribute(qualifiedName: QLatin1String("separator" )); | 
| 1306 |     const QString separatorAVT(m_currentAttributes.value(qualifiedName: QLatin1String("separator" )).toString()); | 
| 1307 |  | 
| 1308 |     queueToken(token: T_LPAREN, to); | 
| 1309 |     const bool viaSelectAttribute = queueSelectOrSequenceConstructor(code, emptynessAllowed, to); | 
| 1310 |     queueToken(token: T_RPAREN, to); | 
| 1311 |  | 
| 1312 |     if(selectOnlyFirst) | 
| 1313 |     { | 
| 1314 |         queueToken(token: T_LBRACKET, to); | 
| 1315 |         queueToken(token: Token(T_NUMBER, QChar::fromLatin1(c: '1')), to); | 
| 1316 |         queueToken(token: T_RBRACKET, to); | 
| 1317 |     } | 
| 1318 |  | 
| 1319 |     queueToken(token: T_COMMA, to); | 
| 1320 |  | 
| 1321 |     if(hasSeparator) | 
| 1322 |         queueAVT(expr: separatorAVT, to); | 
| 1323 |     else | 
| 1324 |     { | 
| 1325 |         /* The default value depends on whether the value is from @select, or from | 
| 1326 |          * the sequence constructor. */ | 
| 1327 |         queueToken(token: Token(T_STRING_LITERAL, viaSelectAttribute ? QString(QLatin1Char(' ')) | 
| 1328 |                                                             : QString()), | 
| 1329 |                    to); | 
| 1330 |     } | 
| 1331 |  | 
| 1332 |     queueToken(token: T_RPAREN, to); | 
| 1333 | } | 
| 1334 |  | 
| 1335 | void XSLTTokenizer::queueTextConstructor(QString &chars, | 
| 1336 |                                          bool &hasWrittenExpression, | 
| 1337 |                                          TokenSource::Queue *const to) | 
| 1338 | { | 
| 1339 |     if(!chars.isEmpty()) | 
| 1340 |     { | 
| 1341 |         commencingExpression(hasWrittenExpression, to); | 
| 1342 |         queueToken(token: T_TEXT, to); | 
| 1343 |         queueToken(token: T_CURLY_LBRACE, to); | 
| 1344 |         queueToken(token: Token(T_STRING_LITERAL, chars), to); | 
| 1345 |         queueToken(token: T_CURLY_RBRACE, to); | 
| 1346 |         chars.clear(); | 
| 1347 |     } | 
| 1348 | } | 
| 1349 |  | 
| 1350 | void XSLTTokenizer::queueVariableDeclaration(const VariableType variableType, | 
| 1351 |                                              TokenSource::Queue *const to) | 
| 1352 | { | 
| 1353 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 1354 |  | 
| 1355 |     if(variableType == VariableInstruction) | 
| 1356 |     { | 
| 1357 |         queueToken(token: T_LET, to); | 
| 1358 |         queueToken(token: T_INTERNAL, to); | 
| 1359 |     } | 
| 1360 |     else if(variableType == VariableDeclaration || variableType == GlobalParameter) | 
| 1361 |     { | 
| 1362 |         queueToken(token: T_DECLARE, to); | 
| 1363 |         queueToken(token: T_VARIABLE, to); | 
| 1364 |         queueToken(token: T_INTERNAL, to); | 
| 1365 |     } | 
| 1366 |  | 
| 1367 |     queueToken(token: T_DOLLAR, to); | 
| 1368 |  | 
| 1369 |     queueExpression(expr: readAttribute(localName: QLatin1String("name" )), to, wrapWithParantheses: false); | 
| 1370 |  | 
| 1371 |     const bool hasAs = m_currentAttributes.hasAttribute(qualifiedName: QLatin1String("as" )); | 
| 1372 |     if(hasAs) | 
| 1373 |     { | 
| 1374 |         queueToken(token: T_AS, to); | 
| 1375 |         queueSequenceType(expr: m_currentAttributes.value(qualifiedName: QLatin1String("as" )).toString()); | 
| 1376 |     } | 
| 1377 |  | 
| 1378 |     if(variableType == FunctionParameter) | 
| 1379 |     { | 
| 1380 |         skipBodyOfParam(code: ReportContext::XTSE0760); | 
| 1381 |         return; | 
| 1382 |     } | 
| 1383 |  | 
| 1384 |     /* We must do this here, because queueSelectOrSequenceConstructor() | 
| 1385 |      * advances the reader. */ | 
| 1386 |     const bool hasSelect = hasAttribute(localName: QLatin1String("select" )); | 
| 1387 |     const bool isRequired = hasAttribute(localName: QLatin1String("required" )) ? attributeYesNo(localName: QLatin1String("required" )) : false; | 
| 1388 |  | 
| 1389 |     TokenSource::Queue storage; | 
| 1390 |     queueSelectOrSequenceConstructor(code: ReportContext::XTSE0620, emptynessAllowed: true, to: &storage, attsP: 0, queueEmptyOnEmpty: false); | 
| 1391 |  | 
| 1392 |     /* XSL-T has some wicked rules, see | 
| 1393 |      * 9.3 Values of Variables and Parameters. */ | 
| 1394 |  | 
| 1395 |     const bool hasQueuedContent = !storage.isEmpty(); | 
| 1396 |  | 
| 1397 |     /* The syntax for global parameters is: | 
| 1398 |      * | 
| 1399 |      * declare variable $var external := 'defaultValue'; | 
| 1400 |      */ | 
| 1401 |     if(variableType == GlobalParameter) | 
| 1402 |         queueToken(token: T_EXTERNAL, to); | 
| 1403 |  | 
| 1404 |     if(isRequired) | 
| 1405 |     { | 
| 1406 |         if(hasQueuedContent) | 
| 1407 |         { | 
| 1408 |             error(message: QtXmlPatterns::tr(sourceText: "When a parameter is required, a default value "  | 
| 1409 |                                                "cannot be supplied through a %1-attribute or "  | 
| 1410 |                                                "a sequence constructor." ).arg(a: formatKeyword(keyword: QLatin1String("select" ))), | 
| 1411 |                   code: ReportContext::XTSE0010); | 
| 1412 |         } | 
| 1413 |     } | 
| 1414 |     else | 
| 1415 |     { | 
| 1416 |         if(hasQueuedContent) | 
| 1417 |         { | 
| 1418 |             queueToken(token: T_ASSIGN, to); | 
| 1419 |  | 
| 1420 |             if(!hasSelect && !hasAs && !hasQueuedContent) | 
| 1421 |                 queueToken(token: Token(T_STRING_LITERAL, QString()), to); | 
| 1422 |             else if(hasAs || hasSelect) | 
| 1423 |                 queueToken(token: T_LPAREN, to); | 
| 1424 |             else | 
| 1425 |             { | 
| 1426 |                 queueToken(token: T_DOCUMENT, to); | 
| 1427 |                 queueToken(token: T_INTERNAL, to); | 
| 1428 |                 queueToken(token: T_CURLY_LBRACE, to); | 
| 1429 |             } | 
| 1430 |         } | 
| 1431 |         else | 
| 1432 |         { | 
| 1433 |             if(!hasAs) | 
| 1434 |             { | 
| 1435 |                 queueToken(token: T_ASSIGN, to); | 
| 1436 |                 queueToken(token: Token(T_STRING_LITERAL, QString()), to); | 
| 1437 |             } | 
| 1438 |             else if(variableType == VariableDeclaration || variableType == VariableInstruction) | 
| 1439 |             { | 
| 1440 |                 queueToken(token: T_ASSIGN, to); | 
| 1441 |                 queueEmptySequence(to); | 
| 1442 |             } | 
| 1443 |         } | 
| 1444 |  | 
| 1445 |         /* storage has tokens if hasSelect or hasQueuedContent is true. */ | 
| 1446 |         if(hasSelect | hasQueuedContent) | 
| 1447 |             *to += storage; | 
| 1448 |  | 
| 1449 |         if(hasQueuedContent) | 
| 1450 |         { | 
| 1451 |             if(!hasSelect && !hasAs && !hasQueuedContent) | 
| 1452 |                 queueToken(token: Token(T_STRING_LITERAL, QString()), to); | 
| 1453 |             else if(hasAs || hasSelect) | 
| 1454 |                 queueToken(token: T_RPAREN, to); | 
| 1455 |             else | 
| 1456 |                 queueToken(token: T_CURLY_RBRACE, to); | 
| 1457 |         } | 
| 1458 |     } | 
| 1459 |  | 
| 1460 |     if(variableType == VariableInstruction) | 
| 1461 |         queueToken(token: T_RETURN, to); | 
| 1462 |     else if(variableType == VariableDeclaration || variableType == GlobalParameter) | 
| 1463 |         queueToken(token: T_SEMI_COLON, to); | 
| 1464 | } | 
| 1465 |  | 
| 1466 | void XSLTTokenizer::startStorageOfCurrent(TokenSource::Queue *const to) | 
| 1467 | { | 
| 1468 |     queueToken(token: T_CURRENT, to); | 
| 1469 |     queueToken(token: T_CURLY_LBRACE, to); | 
| 1470 | } | 
| 1471 |  | 
| 1472 | void XSLTTokenizer::endStorageOfCurrent(TokenSource::Queue *const to) | 
| 1473 | { | 
| 1474 |     queueToken(token: T_CURLY_RBRACE, to); | 
| 1475 | } | 
| 1476 |  | 
| 1477 | void XSLTTokenizer::queueNamespaceDeclarations(TokenSource::Queue *const to, | 
| 1478 |                                                QStack<Token> *const queueOnExit, | 
| 1479 |                                                const bool isDeclaration) | 
| 1480 | { | 
| 1481 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 1482 |     Q_ASSERT_X(isDeclaration || queueOnExit, | 
| 1483 |                Q_FUNC_INFO, | 
| 1484 |                "If isDeclaration is false, queueOnExit must be passed." ); | 
| 1485 |  | 
| 1486 |     const QXmlStreamNamespaceDeclarations nss(namespaceDeclarations()); | 
| 1487 |  | 
| 1488 |     for(int i = 0; i < nss.count(); ++i) | 
| 1489 |     { | 
| 1490 |         const QXmlStreamNamespaceDeclaration &at = nss.at(i); | 
| 1491 |         queueToken(token: T_DECLARE, to); | 
| 1492 |         queueToken(token: T_NAMESPACE, to); | 
| 1493 |         queueToken(token: Token(T_NCNAME, at.prefix().toString()), to); | 
| 1494 |         queueToken(token: T_G_EQ, to); | 
| 1495 |         queueToken(token: Token(T_STRING_LITERAL, at.namespaceUri().toString()), to); | 
| 1496 |  | 
| 1497 |         if(isDeclaration) | 
| 1498 |         { | 
| 1499 |             queueToken(token: T_INTERNAL, to); | 
| 1500 |             queueToken(token: T_SEMI_COLON, to); | 
| 1501 |         } | 
| 1502 |         else | 
| 1503 |         { | 
| 1504 |             queueToken(token: T_CURLY_LBRACE, to); | 
| 1505 |             queueOnExit->push(t: T_CURLY_RBRACE); | 
| 1506 |         } | 
| 1507 |     } | 
| 1508 | } | 
| 1509 |  | 
| 1510 | bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to, | 
| 1511 |                                               const bool initialAdvance, | 
| 1512 |                                               const bool queueEmptyOnEmpty) | 
| 1513 | { | 
| 1514 |     QStack<Token> onExitTokens; | 
| 1515 |     return insideSequenceConstructor(to, queueOnExit&: onExitTokens, initialAdvance, queueEmptyOnEmpty); | 
| 1516 | } | 
| 1517 |  | 
| 1518 | bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to, | 
| 1519 |                                               QStack<Token> &onExitTokens, | 
| 1520 |                                               const bool initialAdvance, | 
| 1521 |                                               const bool queueEmptyOnEmpty) | 
| 1522 | { | 
| 1523 |     bool effectiveInitialAdvance = initialAdvance; | 
| 1524 |     bool hasWrittenExpression = false; | 
| 1525 |  | 
| 1526 |     /* Buffer which all text nodes, that might be split up by comments, | 
| 1527 |      * processing instructions and CDATA sections, are appended to. */ | 
| 1528 |     QString characters; | 
| 1529 |  | 
| 1530 |     while(!atEnd()) | 
| 1531 |     { | 
| 1532 |         if(effectiveInitialAdvance) | 
| 1533 |             readNext(); | 
| 1534 |         else | 
| 1535 |             effectiveInitialAdvance = true; | 
| 1536 |  | 
| 1537 |         switch(tokenType()) | 
| 1538 |         { | 
| 1539 |             case QXmlStreamReader::StartElement: | 
| 1540 |             { | 
| 1541 |                 queueTextConstructor(chars&: characters, hasWrittenExpression, to); | 
| 1542 |                 handleXMLBase(to, queueOnExit: &onExitTokens); | 
| 1543 |  | 
| 1544 |                 pushState(nextState: InsideSequenceConstructor); | 
| 1545 |  | 
| 1546 |                 commencingExpression(hasWrittenExpression, to); | 
| 1547 |  | 
| 1548 |                 if(isXSLT()) | 
| 1549 |                 { | 
| 1550 |                     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: true); | 
| 1551 |                     handleStandardAttributes(isXSLTElement: true); | 
| 1552 |                     validateElement(); | 
| 1553 |  | 
| 1554 |                     queueNamespaceDeclarations(to, queueOnExit: &onExitTokens); | 
| 1555 |  | 
| 1556 |                     switch(currentElementName()) | 
| 1557 |                     { | 
| 1558 |                         case If: | 
| 1559 |                         { | 
| 1560 |                             queueToken(token: T_IF, to); | 
| 1561 |                             queueToken(token: T_LPAREN, to); | 
| 1562 |  | 
| 1563 |                             queueExpression(expr: readAttribute(localName: QLatin1String("test" )), to); | 
| 1564 |                             queueToken(token: T_RPAREN, to); | 
| 1565 |                             queueToken(token: T_THEN, to); | 
| 1566 |  | 
| 1567 |                             queueToken(token: T_LPAREN, to); | 
| 1568 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1569 |                             insideSequenceConstructor(to); | 
| 1570 |  | 
| 1571 |                             break; | 
| 1572 |                         } | 
| 1573 |                         case Choose: | 
| 1574 |                         { | 
| 1575 |                             insideChoose(to); | 
| 1576 |                             break; | 
| 1577 |                         } | 
| 1578 |                         case ValueOf: | 
| 1579 |                         { | 
| 1580 |                             /* We generate a computed text node constructor. */ | 
| 1581 |                             queueToken(token: T_TEXT, to); | 
| 1582 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1583 |  | 
| 1584 |                             queueSimpleContentConstructor(code: ReportContext::XTSE0870, emptynessAllowed: true, to, | 
| 1585 |                                                           selectOnlyFirst: !hasAttribute(localName: QLatin1String("separator" )) && m_processingMode.top() == BackwardsCompatible); | 
| 1586 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1587 |                             break; | 
| 1588 |                         } | 
| 1589 |                         case Sequence: | 
| 1590 |                         { | 
| 1591 |                             queueExpression(expr: readAttribute(localName: QLatin1String("select" )), to); | 
| 1592 |                             parseFallbacksOnly(); | 
| 1593 |                             break; | 
| 1594 |                         } | 
| 1595 |                         case Text: | 
| 1596 |                         { | 
| 1597 |                             queueToken(token: T_TEXT, to); | 
| 1598 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1599 |  | 
| 1600 |                             queueToken(token: Token(T_STRING_LITERAL, readElementText()), to); | 
| 1601 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1602 |                             break; | 
| 1603 |                         } | 
| 1604 |                         case Variable: | 
| 1605 |                         { | 
| 1606 |                             queueVariableDeclaration(variableType: VariableInstruction, to); | 
| 1607 |  | 
| 1608 |                             /* We wrap the children in parantheses since we may | 
| 1609 |                              * queue several expressions using the comma operator, | 
| 1610 |                              * and in that case the let-binding is only in-scope | 
| 1611 |                              * for the first expression. */ | 
| 1612 |                             queueToken(token: T_LPAREN, to); | 
| 1613 |  | 
| 1614 |                             /* We don't want a comma outputted, we're expecting an | 
| 1615 |                              * expression now. */ | 
| 1616 |                             hasWrittenExpression = false; | 
| 1617 |  | 
| 1618 |                             onExitTokens.push(t: T_RPAREN); | 
| 1619 |  | 
| 1620 |                             break; | 
| 1621 |                         } | 
| 1622 |                         case CallTemplate: | 
| 1623 |                         { | 
| 1624 |                             queueToken(token: T_CALL_TEMPLATE, to); | 
| 1625 |                             queueToken(token: Token(T_QNAME, readAttribute(localName: QLatin1String("name" ))), to); | 
| 1626 |                             queueToken(token: T_LPAREN, to); | 
| 1627 |                             queueWithParams(parentName: CallTemplate, to); | 
| 1628 |                             queueToken(token: T_RPAREN, to); | 
| 1629 |                             break; | 
| 1630 |                         } | 
| 1631 |                         case ForEach: | 
| 1632 |                         { | 
| 1633 |                             queueExpression(expr: readAttribute(localName: QLatin1String("select" )), to); | 
| 1634 |                             queueToken(token: T_MAP, to); | 
| 1635 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1636 |  | 
| 1637 |                             TokenSource::Queue sorts; | 
| 1638 |                             queueSorting(oneSortRequired: false, to: &sorts); | 
| 1639 |  | 
| 1640 |  | 
| 1641 |                             if(sorts.isEmpty()) | 
| 1642 |                             { | 
| 1643 |                                 startStorageOfCurrent(to); | 
| 1644 |                                 insideSequenceConstructor(to, initialAdvance: false); | 
| 1645 |                                 endStorageOfCurrent(to); | 
| 1646 |                             } | 
| 1647 |                             else | 
| 1648 |                             { | 
| 1649 |                                 queueToken(token: T_SORT, to); | 
| 1650 |                                 *to += sorts; | 
| 1651 |                                 queueToken(token: T_RETURN, to); | 
| 1652 |                                 startStorageOfCurrent(to); | 
| 1653 |                                 insideSequenceConstructor(to, initialAdvance: false); | 
| 1654 |                                 endStorageOfCurrent(to); | 
| 1655 |                                 queueToken(token: T_END_SORT, to); | 
| 1656 |                             } | 
| 1657 |  | 
| 1658 |                             break; | 
| 1659 |                         } | 
| 1660 |                         case XSLTTokenLookup::Comment: | 
| 1661 |                         { | 
| 1662 |                             queueToken(token: T_COMMENT, to); | 
| 1663 |                             queueToken(token: T_INTERNAL, to); | 
| 1664 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1665 |                             queueSelectOrSequenceConstructor(code: ReportContext::XTSE0940, emptynessAllowed: true, to); | 
| 1666 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1667 |                             break; | 
| 1668 |                         } | 
| 1669 |                         case CopyOf: | 
| 1670 |                         { | 
| 1671 |                             queueExpression(expr: readAttribute(localName: QLatin1String("select" )), to); | 
| 1672 |                             // TODO | 
| 1673 |  | 
| 1674 |                             if(readNext() == QXmlStreamReader::EndElement) | 
| 1675 |                                 break; | 
| 1676 |                             else | 
| 1677 |                             { | 
| 1678 |                                 error(message: QtXmlPatterns::tr(sourceText: "Element %1 cannot have children." ).arg(a: formatKeyword(keyword: QLatin1String("copy-of" ))), | 
| 1679 |                                       code: ReportContext::XTSE0010); | 
| 1680 |                             } | 
| 1681 |                             break; | 
| 1682 |                         } | 
| 1683 |                         case AnalyzeString: | 
| 1684 |                         { | 
| 1685 |                             // TODO | 
| 1686 |                             skipSubTree(); | 
| 1687 |                             break; | 
| 1688 |                         } | 
| 1689 |                         case ResultDocument: | 
| 1690 |                         { | 
| 1691 |                             // TODO | 
| 1692 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1693 |                             insideSequenceConstructor(to); | 
| 1694 |                             break; | 
| 1695 |                         } | 
| 1696 |                         case Copy: | 
| 1697 |                         { | 
| 1698 |                             /* We translate: | 
| 1699 |                              *      <xsl:copy>expr</xsl:copy> | 
| 1700 |                              * into: | 
| 1701 |                              * | 
| 1702 |                              *  let $body := expr | 
| 1703 |                              *  return | 
| 1704 |                              *      if(self::element()) then | 
| 1705 |                              *          element internal {node-name()} {$body} | 
| 1706 |                              *      else if(self::document-node()) then | 
| 1707 |                              *          document internal {$body} | 
| 1708 |                              *      else (: This includes comments, processing-instructions, | 
| 1709 |                              *              attributes, and comments. :) | 
| 1710 |                              *          . | 
| 1711 |                              * | 
| 1712 |                              * TODO node identity is the same as the old node. | 
| 1713 |                              * TODO namespace bindings are lost when elements are constructed | 
| 1714 |                              */ | 
| 1715 |  | 
| 1716 |                             /* let $body := expr */ | 
| 1717 |                             queueToken(token: T_LET, to); | 
| 1718 |                             queueToken(token: T_INTERNAL, to); | 
| 1719 |                             queueToken(token: T_DOLLAR, to); | 
| 1720 |                             queueToken(token: Token(T_NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name | 
| 1721 |                             queueToken(token: T_ASSIGN, to); | 
| 1722 |                             queueToken(token: T_LPAREN, to); | 
| 1723 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1724 |                             /* Don't queue an empty sequence, we want the dot. */ | 
| 1725 |                             insideSequenceConstructor(to); | 
| 1726 |                             queueToken(token: T_RPAREN, to); | 
| 1727 |                             queueToken(token: T_RETURN, to); | 
| 1728 |  | 
| 1729 |                             /* if(self::element()) then */ | 
| 1730 |                             queueToken(token: T_IF, to); | 
| 1731 |                             queueToken(token: T_LPAREN, to); | 
| 1732 |                             queueToken(token: T_SELF, to); | 
| 1733 |                             queueToken(token: T_COLONCOLON, to); | 
| 1734 |                             queueToken(token: T_ELEMENT, to); | 
| 1735 |                             queueToken(token: T_LPAREN, to); | 
| 1736 |                             queueToken(token: T_RPAREN, to); | 
| 1737 |                             queueToken(token: T_RPAREN, to); | 
| 1738 |                             queueToken(token: T_THEN, to); | 
| 1739 |  | 
| 1740 |                             /* element internal {node-name()} {$body} */ | 
| 1741 |                             queueToken(token: T_ELEMENT, to); | 
| 1742 |                             queueToken(token: T_INTERNAL, to); | 
| 1743 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1744 |                             queueToken(token: Token(T_NCNAME, QLatin1String("node-name" )), to); // TODO what if the default ns changes? | 
| 1745 |                             queueToken(token: T_LPAREN, to); | 
| 1746 |                             queueToken(token: T_DOT, to); | 
| 1747 |                             queueToken(token: T_RPAREN, to); | 
| 1748 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1749 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1750 |                             queueToken(token: T_DOLLAR, to); | 
| 1751 |                             queueToken(token: Token(T_NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name | 
| 1752 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1753 |  | 
| 1754 |                             /* else if(self::document-node()) then */ | 
| 1755 |                             queueToken(token: T_ELSE, to); | 
| 1756 |                             queueToken(token: T_IF, to); | 
| 1757 |                             queueToken(token: T_LPAREN, to); | 
| 1758 |                             queueToken(token: T_SELF, to); | 
| 1759 |                             queueToken(token: T_COLONCOLON, to); | 
| 1760 |                             queueToken(token: T_DOCUMENT_NODE, to); | 
| 1761 |                             queueToken(token: T_LPAREN, to); | 
| 1762 |                             queueToken(token: T_RPAREN, to); | 
| 1763 |                             queueToken(token: T_RPAREN, to); | 
| 1764 |                             queueToken(token: T_THEN, to); | 
| 1765 |  | 
| 1766 |                             /* document internal {$body} */ | 
| 1767 |                             queueToken(token: T_DOCUMENT, to); | 
| 1768 |                             queueToken(token: T_INTERNAL, to); | 
| 1769 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1770 |                             queueToken(token: T_DOLLAR, to); | 
| 1771 |                             queueToken(token: Token(T_NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name | 
| 1772 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1773 |  | 
| 1774 |                             /* else . */ | 
| 1775 |                             queueToken(token: T_ELSE, to); | 
| 1776 |                             queueToken(token: T_DOT, to); | 
| 1777 |  | 
| 1778 |                             break; | 
| 1779 |                         } | 
| 1780 |                         case XSLTTokenLookup::ProcessingInstruction: | 
| 1781 |                         { | 
| 1782 |                             queueToken(token: T_PROCESSING_INSTRUCTION, to); | 
| 1783 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1784 |                             queueAVT(expr: readAttribute(localName: QLatin1String("name" )), to); | 
| 1785 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1786 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1787 |                             queueSelectOrSequenceConstructor(code: ReportContext::XTSE0880, emptynessAllowed: true, to); | 
| 1788 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1789 |                             break; | 
| 1790 |                         } | 
| 1791 |                         case Document: | 
| 1792 |                         { | 
| 1793 |                             handleValidationAttributes(isLRE: false); | 
| 1794 |  | 
| 1795 |                             // TODO base-URI | 
| 1796 |                             queueToken(token: T_DOCUMENT, to); | 
| 1797 |                             queueToken(token: T_INTERNAL, to); | 
| 1798 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1799 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1800 |                             insideSequenceConstructor(to); | 
| 1801 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1802 |                             break; | 
| 1803 |                         } | 
| 1804 |                         case Element: | 
| 1805 |                         { | 
| 1806 |                             handleValidationAttributes(isLRE: false); | 
| 1807 |  | 
| 1808 |                             // TODO base-URI | 
| 1809 |                             queueToken(token: T_ELEMENT, to); | 
| 1810 |                             queueToken(token: T_INTERNAL, to); | 
| 1811 |  | 
| 1812 |                             /* The name. */ | 
| 1813 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1814 |                             // TODO only strings allowed, not qname values. | 
| 1815 |                             queueAVT(expr: readAttribute(localName: QLatin1String("name" )), to); | 
| 1816 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1817 |  | 
| 1818 |                             /* The sequence constructor. */ | 
| 1819 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1820 |                             pushState(nextState: InsideSequenceConstructor); | 
| 1821 |                             insideSequenceConstructor(to); | 
| 1822 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1823 |                             break; | 
| 1824 |                         } | 
| 1825 |                         case Attribute: | 
| 1826 |                         { | 
| 1827 |                             handleValidationAttributes(isLRE: false); | 
| 1828 |  | 
| 1829 |                             // TODO base-URI | 
| 1830 |                             queueToken(token: T_ATTRIBUTE, to); | 
| 1831 |                             queueToken(token: T_INTERNAL, to); | 
| 1832 |  | 
| 1833 |                             /* The name. */ | 
| 1834 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1835 |                             // TODO only strings allowed, not qname values. | 
| 1836 |                             queueAVT(expr: readAttribute(localName: QLatin1String("name" )), to); | 
| 1837 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1838 |  | 
| 1839 |                             /* The sequence constructor. */ | 
| 1840 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1841 |                             queueSimpleContentConstructor(code: ReportContext::XTSE0840, | 
| 1842 |                                                           emptynessAllowed: true, to); | 
| 1843 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1844 |                             break; | 
| 1845 |                         } | 
| 1846 |                         case Namespace: | 
| 1847 |                         { | 
| 1848 |                             queueToken(token: T_NAMESPACE, to); | 
| 1849 |  | 
| 1850 |                             /* The name. */ | 
| 1851 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1852 |                             queueAVT(expr: readAttribute(localName: QLatin1String("name" )), to); | 
| 1853 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1854 |  | 
| 1855 |                             /* The sequence constructor. */ | 
| 1856 |                             queueToken(token: T_CURLY_LBRACE, to); | 
| 1857 |                             queueSelectOrSequenceConstructor(code: ReportContext::XTSE0910, | 
| 1858 |                                                              emptynessAllowed: false, to); | 
| 1859 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 1860 |                             break; | 
| 1861 |                         } | 
| 1862 |                         case PerformSort: | 
| 1863 |                         { | 
| 1864 |                             /* For: | 
| 1865 |                              * <xsl:perform-sort select="$in"> | 
| 1866 |                              *      <xsl:sort select="@key"/> | 
| 1867 |                              * </xsl:perform-sort> | 
| 1868 |                              * | 
| 1869 |                              * we generate: | 
| 1870 |                              * | 
| 1871 |                              * $in map sort order by @key | 
| 1872 |                              *         return . | 
| 1873 |                              *         end_sort | 
| 1874 |                              */ | 
| 1875 |  | 
| 1876 |                             /* In XQuery, the sort keys appear after the expression | 
| 1877 |                              * supplying the initial sequence, while in | 
| 1878 |                              * xsl:perform-sort, if a sequence constructor is used, | 
| 1879 |                              * they appear in the opposite order. Hence, we need to | 
| 1880 |                              * reorder it. */ | 
| 1881 |  | 
| 1882 |                             /* We store the attributes of xsl:perform-sort, before | 
| 1883 |                              * queueSorting() advances the reader. */ | 
| 1884 |                             const QXmlStreamAttributes atts(m_currentAttributes); | 
| 1885 |  | 
| 1886 |                             TokenSource::Queue sorts; | 
| 1887 |                             queueSorting(oneSortRequired: true, to: &sorts); | 
| 1888 |                             queueSelectOrSequenceConstructor(code: ReportContext::XTSE1040, | 
| 1889 |                                                              emptynessAllowed: true, | 
| 1890 |                                                              to, | 
| 1891 |                                                              attsP: &atts); | 
| 1892 |                             /* queueSelectOrSequenceConstructor() positions us on EndElement. */ | 
| 1893 |                             effectiveInitialAdvance = false; | 
| 1894 |                             queueToken(token: T_MAP, to); | 
| 1895 |                             queueToken(token: T_SORT, to); | 
| 1896 |                             *to += sorts; | 
| 1897 |                             queueToken(token: T_RETURN, to); | 
| 1898 |                             queueToken(token: T_DOT, to); | 
| 1899 |                             queueToken(token: T_END_SORT, to); | 
| 1900 |  | 
| 1901 |                             break; | 
| 1902 |                         } | 
| 1903 |                         case Message: | 
| 1904 |                         { | 
| 1905 |                             // TODO | 
| 1906 |                             queueEmptySequence(to); | 
| 1907 |                             skipSubTree(); | 
| 1908 |                             break; | 
| 1909 |                         } | 
| 1910 |                         case ApplyTemplates: | 
| 1911 |                         { | 
| 1912 |                             if(hasAttribute(localName: QLatin1String("select" ))) | 
| 1913 |                                 queueExpression(expr: readAttribute(localName: QLatin1String("select" )), to); | 
| 1914 |                             else | 
| 1915 |                             { | 
| 1916 |                                 queueToken(token: T_CHILD, to); | 
| 1917 |                                 queueToken(token: T_COLONCOLON, to); | 
| 1918 |                                 queueToken(token: T_NODE, to); | 
| 1919 |                                 queueToken(token: T_LPAREN, to); | 
| 1920 |                                 queueToken(token: T_RPAREN, to); | 
| 1921 |                             } | 
| 1922 |  | 
| 1923 |                             bool hasMode = hasAttribute(localName: QLatin1String("mode" )); | 
| 1924 |                             QString mode; | 
| 1925 |  | 
| 1926 |                             if(hasMode) | 
| 1927 |                                 mode = readAttribute(localName: QLatin1String("mode" )).trimmed(); | 
| 1928 |  | 
| 1929 |                             queueToken(token: T_FOR_APPLY_TEMPLATE, to); | 
| 1930 |  | 
| 1931 |                             TokenSource::Queue sorts; | 
| 1932 |                             queueSorting(oneSortRequired: false, to: &sorts, speciallyTreatWhitespace: true); | 
| 1933 |  | 
| 1934 |                             if(!sorts.isEmpty()) | 
| 1935 |                             { | 
| 1936 |                                 queueToken(token: T_SORT, to); | 
| 1937 |                                 *to += sorts; | 
| 1938 |                                 queueToken(token: T_RETURN, to); | 
| 1939 |                             } | 
| 1940 |  | 
| 1941 |                             queueToken(token: T_APPLY_TEMPLATE, to); | 
| 1942 |  | 
| 1943 |                             if(hasMode) | 
| 1944 |                             { | 
| 1945 |                                 queueToken(token: T_MODE, to); | 
| 1946 |                                 queueToken(token: Token(mode.startsWith(c: QLatin1Char('#')) ? T_NCNAME : T_QNAME, mode), to); | 
| 1947 |                             } | 
| 1948 |  | 
| 1949 |                             queueToken(token: T_LPAREN, to); | 
| 1950 |                             queueWithParams(parentName: ApplyTemplates, to, initialAdvance: false); | 
| 1951 |                             queueToken(token: T_RPAREN, to); | 
| 1952 |  | 
| 1953 |                             if(!sorts.isEmpty()) | 
| 1954 |                                 queueToken(token: T_END_SORT, to); | 
| 1955 |  | 
| 1956 |                             break; | 
| 1957 |                         } | 
| 1958 |                         default: | 
| 1959 |                             unexpectedContent(); | 
| 1960 |                     } | 
| 1961 |                 } | 
| 1962 |                 else | 
| 1963 |                 { | 
| 1964 |                     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: true); | 
| 1965 |                     handleStandardAttributes(isXSLTElement: false); | 
| 1966 |                     handleValidationAttributes(isLRE: false); | 
| 1967 |  | 
| 1968 |                     /* We're generating an element constructor. */ | 
| 1969 |                     queueNamespaceDeclarations(to, queueOnExit: &onExitTokens); // TODO same in the isXSLT() branch | 
| 1970 |                     queueToken(token: T_ELEMENT, to); | 
| 1971 |                     queueToken(token: T_INTERNAL, to); | 
| 1972 |                     queueToken(token: Token(T_QNAME, qualifiedName().toString()), to); | 
| 1973 |                     queueToken(token: T_CURLY_LBRACE, to); | 
| 1974 |                     const int len = m_currentAttributes.count(); | 
| 1975 |  | 
| 1976 |                     for(int i = 0; i < len; ++i) | 
| 1977 |                     { | 
| 1978 |                         const QXmlStreamAttribute &at = m_currentAttributes.at(i); | 
| 1979 |  | 
| 1980 |                         /* We don't want to generate constructors for XSL-T attributes. */ | 
| 1981 |                         if(at.namespaceUri() == CommonNamespaces::XSLT) | 
| 1982 |                             continue; | 
| 1983 |  | 
| 1984 |                         queueToken(token: T_ATTRIBUTE, to); | 
| 1985 |                         queueToken(token: T_INTERNAL, to); | 
| 1986 |  | 
| 1987 |                         queueToken(token: Token(at.prefix().isEmpty() ? T_NCNAME : T_QNAME, at.qualifiedName().toString()), to); | 
| 1988 |                         queueToken(token: T_CURLY_LBRACE, to); | 
| 1989 |                         queueAVT(expr: at.value().toString(), to); | 
| 1990 |                         queueToken(token: T_CURLY_RBRACE, to); | 
| 1991 |                         queueToken(token: T_COMMA, to); | 
| 1992 |                     } | 
| 1993 |  | 
| 1994 |                     pushState(nextState: InsideSequenceConstructor); | 
| 1995 |                     insideSequenceConstructor(to); | 
| 1996 |                     Q_ASSERT(tokenType() == QXmlStreamReader::EndElement || hasError()); | 
| 1997 |                 } | 
| 1998 |  | 
| 1999 |                 continue; | 
| 2000 |             } | 
| 2001 |             case QXmlStreamReader::EndElement: | 
| 2002 |             { | 
| 2003 |                 queueTextConstructor(chars&: characters, hasWrittenExpression, to); | 
| 2004 |                 leaveState(); | 
| 2005 |  | 
| 2006 |                 if(!hasWrittenExpression && queueEmptyOnEmpty) | 
| 2007 |                     queueEmptySequence(to); | 
| 2008 |  | 
| 2009 |                 queueOnExit(source&: onExitTokens, destination: to); | 
| 2010 |  | 
| 2011 |                 if(isXSLT()) | 
| 2012 |                 { | 
| 2013 |                     Q_ASSERT(!isElement(Sequence)); | 
| 2014 |  | 
| 2015 |                     switch(currentElementName()) | 
| 2016 |                     { | 
| 2017 |                         /* Fallthrough all these. */ | 
| 2018 |                         case When: | 
| 2019 |                         case Choose: | 
| 2020 |                         case ForEach: | 
| 2021 |                         case Otherwise: | 
| 2022 |                         case PerformSort: | 
| 2023 |                         case Message: | 
| 2024 |                         case ResultDocument: | 
| 2025 |                         case Copy: | 
| 2026 |                         case CallTemplate: | 
| 2027 |                         case Text: | 
| 2028 |                         case ValueOf: | 
| 2029 |                         { | 
| 2030 |                             hasWrittenExpression = true; | 
| 2031 |                             break; | 
| 2032 |                         } | 
| 2033 |                         case If: | 
| 2034 |                         { | 
| 2035 |                             queueToken(token: T_RPAREN, to); | 
| 2036 |                             queueToken(token: T_ELSE, to); | 
| 2037 |                             queueEmptySequence(to); | 
| 2038 |                             break; | 
| 2039 |                         } | 
| 2040 |                         case Function: | 
| 2041 |                         { | 
| 2042 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 2043 |                             queueToken(token: T_SEMI_COLON, to); | 
| 2044 |                             break; | 
| 2045 |                         } | 
| 2046 |                         case Template: | 
| 2047 |                         { | 
| 2048 |                             endStorageOfCurrent(to: &m_tokenSource); | 
| 2049 |                             /* TODO, fallthrough to Function. */ | 
| 2050 |                             queueToken(token: T_CURLY_RBRACE, to); | 
| 2051 |                             queueToken(token: T_SEMI_COLON, to); | 
| 2052 |                             break; | 
| 2053 |                         } | 
| 2054 |                         default: | 
| 2055 |                             ; | 
| 2056 |                     } | 
| 2057 |                 } | 
| 2058 |                 else | 
| 2059 |                 { | 
| 2060 |                     /* We're closing a direct element constructor. */ | 
| 2061 |                     hasWrittenExpression = true; | 
| 2062 |                     queueToken(token: T_CURLY_RBRACE, to); | 
| 2063 |                 } | 
| 2064 |  | 
| 2065 |                 return hasWrittenExpression; | 
| 2066 |             } | 
| 2067 |             case QXmlStreamReader::ProcessingInstruction: | 
| 2068 |             case QXmlStreamReader::Comment: | 
| 2069 |                 /* We do nothing, we just ignore them. */ | 
| 2070 |                 continue; | 
| 2071 |             case QXmlStreamReader::Characters: | 
| 2072 |             { | 
| 2073 |                 if(whitespaceToSkip()) | 
| 2074 |                     continue; | 
| 2075 |                 else | 
| 2076 |                 { | 
| 2077 |                     characters += text().toString(); | 
| 2078 |                     continue; | 
| 2079 |                 } | 
| 2080 |             } | 
| 2081 |             default: | 
| 2082 |                 ; | 
| 2083 |         } | 
| 2084 |     } | 
| 2085 |  | 
| 2086 |     leaveState(); | 
| 2087 |     return hasWrittenExpression; | 
| 2088 | } | 
| 2089 |  | 
| 2090 | bool XSLTTokenizer::isStylesheetElement() const | 
| 2091 | { | 
| 2092 |     Q_ASSERT(isXSLT()); | 
| 2093 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || | 
| 2094 |              tokenType() == QXmlStreamReader::EndElement); | 
| 2095 |  | 
| 2096 |     const NodeName name = currentElementName(); | 
| 2097 |     return name == Stylesheet || name == Transform; | 
| 2098 | } | 
| 2099 |  | 
| 2100 | void XSLTTokenizer::skipBodyOfParam(const ReportContext::ErrorCode code) | 
| 2101 | { | 
| 2102 |     Q_ASSERT(isXSLT()); | 
| 2103 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 2104 |     const NodeName name(currentElementName()); | 
| 2105 |  | 
| 2106 |     if(skipSubTree()) | 
| 2107 |     { | 
| 2108 |         error(message: QtXmlPatterns::tr(sourceText: "Element %1 cannot have a sequence constructor." ) | 
| 2109 |                                           .arg(a: formatKeyword(keyword: toString(token: name))), | 
| 2110 |               code); | 
| 2111 |     } | 
| 2112 | } | 
| 2113 |  | 
| 2114 | void XSLTTokenizer::queueWithParams(const XSLTTokenLookup::NodeName parentName, | 
| 2115 |                                     TokenSource::Queue *const to, | 
| 2116 |                                     const bool initialAdvance) | 
| 2117 | { | 
| 2118 |     Q_ASSERT(parentName == ApplyTemplates || parentName == CallTemplate); | 
| 2119 |  | 
| 2120 |     bool effectiveInitialAdvance = initialAdvance; | 
| 2121 |     bool hasQueuedParam = false; | 
| 2122 |  | 
| 2123 |     while(!atEnd()) | 
| 2124 |     { | 
| 2125 |         if(effectiveInitialAdvance) | 
| 2126 |             readNext(); | 
| 2127 |         else | 
| 2128 |             effectiveInitialAdvance = true; | 
| 2129 |  | 
| 2130 |         switch(tokenType()) | 
| 2131 |         { | 
| 2132 |             case QXmlStreamReader::StartElement: | 
| 2133 |             { | 
| 2134 |                 if(hasQueuedParam) | 
| 2135 |                     queueToken(token: T_COMMA, to); | 
| 2136 |  | 
| 2137 |                 if(isXSLT() && isElement(name: WithParam)) | 
| 2138 |                 { | 
| 2139 |                     if(hasAttribute(localName: QLatin1String("tunnel" )) && attributeYesNo(localName: QLatin1String("tunnel" ))) | 
| 2140 |                         queueToken(token: T_TUNNEL, to); | 
| 2141 |  | 
| 2142 |                     queueVariableDeclaration(variableType: WithParamVariable, to); | 
| 2143 |                     hasQueuedParam = true; | 
| 2144 |                     continue; | 
| 2145 |                 } | 
| 2146 |                 else | 
| 2147 |                     unexpectedContent(); | 
| 2148 |                 Q_FALLTHROUGH(); | 
| 2149 |             } | 
| 2150 |             case QXmlStreamReader::EndElement: | 
| 2151 |             { | 
| 2152 |                 if(isElement(name: parentName)) | 
| 2153 |                     return; | 
| 2154 |                 else | 
| 2155 |                     continue; | 
| 2156 |             } | 
| 2157 |             case QXmlStreamReader::ProcessingInstruction: | 
| 2158 |             case QXmlStreamReader::Comment: | 
| 2159 |                 continue; | 
| 2160 |             case QXmlStreamReader::Characters: | 
| 2161 |                 if(whitespaceToSkip()) | 
| 2162 |                     continue; | 
| 2163 |                 else | 
| 2164 |                     return; | 
| 2165 |             default: | 
| 2166 |                 unexpectedContent(); | 
| 2167 |         } | 
| 2168 |     } | 
| 2169 |     unexpectedContent(); | 
| 2170 | } | 
| 2171 |  | 
| 2172 | void XSLTTokenizer::queueParams(const XSLTTokenLookup::NodeName parentName, | 
| 2173 |                                 TokenSource::Queue *const to) | 
| 2174 | { | 
| 2175 |     bool hasQueuedParam = false; | 
| 2176 |  | 
| 2177 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 2178 |  | 
| 2179 |     while(!atEnd()) | 
| 2180 |     { | 
| 2181 |         switch(readNext()) | 
| 2182 |         { | 
| 2183 |             case QXmlStreamReader::StartElement: | 
| 2184 |             { | 
| 2185 |                 if(isXSLT() && isElement(name: Param)) | 
| 2186 |                 { | 
| 2187 |                     if(hasQueuedParam) | 
| 2188 |                         queueToken(token: T_COMMA, to); | 
| 2189 |  | 
| 2190 |                     validateElement(); | 
| 2191 |  | 
| 2192 |                     if(parentName == Function && m_currentAttributes.hasAttribute(qualifiedName: QLatin1String("select" ))) | 
| 2193 |                     { | 
| 2194 |                         error(message: QtXmlPatterns::tr(sourceText: "The attribute %1 cannot appear on %2, when it is a child of %3." ) | 
| 2195 |                                          .arg(args: formatKeyword(keyword: QLatin1String("select" )), | 
| 2196 |                                               args: formatKeyword(keyword: QLatin1String("param" )), | 
| 2197 |                                               args: formatKeyword(keyword: QLatin1String("function" ))), | 
| 2198 |                               code: ReportContext::XTSE0760); | 
| 2199 |                     } | 
| 2200 |  | 
| 2201 |                     if(parentName == Function && m_currentAttributes.hasAttribute(qualifiedName: QLatin1String("required" ))) | 
| 2202 |                     { | 
| 2203 |                         error(message: QtXmlPatterns::tr(sourceText: "The attribute %1 cannot appear on %2, when it is a child of %3." ) | 
| 2204 |                                          .arg(args: formatKeyword(keyword: QLatin1String("required" )), | 
| 2205 |                                               args: formatKeyword(keyword: QLatin1String("param" )), | 
| 2206 |                                               args: formatKeyword(keyword: QLatin1String("function" ))), | 
| 2207 |                               code: ReportContext::XTSE0010); | 
| 2208 |                     } | 
| 2209 |  | 
| 2210 |                     const bool hasTunnel = m_currentAttributes.hasAttribute(qualifiedName: QLatin1String("tunnel" )); | 
| 2211 |                     const bool isTunnel = hasTunnel ? attributeYesNo(localName: QLatin1String("tunnel" )) : false; | 
| 2212 |  | 
| 2213 |                     if(isTunnel) | 
| 2214 |                     { | 
| 2215 |                         if(parentName == Function) | 
| 2216 |                         { | 
| 2217 |                             /* See W3C public report 5650: http://www.w3.org/Bugs/Public/show_bug.cgi?id=5650 */ | 
| 2218 |                             error(message: QtXmlPatterns::tr(sourceText: "A parameter in a function cannot be declared to be a tunnel." ), | 
| 2219 |                                   code: ReportContext::XTSE0010); | 
| 2220 |                         } | 
| 2221 |                         else | 
| 2222 |                             queueToken(token: T_TUNNEL, to); | 
| 2223 |                     } | 
| 2224 |  | 
| 2225 |                     hasQueuedParam = true; | 
| 2226 |                     queueVariableDeclaration(variableType: parentName == Function ? FunctionParameter : TemplateParameter, to); | 
| 2227 |                     continue; | 
| 2228 |                 } | 
| 2229 |                 else | 
| 2230 |                     return; | 
| 2231 |             } | 
| 2232 |             case QXmlStreamReader::Characters: | 
| 2233 |             { | 
| 2234 |                 if(whitespaceToSkip()) | 
| 2235 |                     continue; | 
| 2236 |                 Q_FALLTHROUGH(); | 
| 2237 |             } | 
| 2238 |             case QXmlStreamReader::EndElement: | 
| 2239 |                 return; | 
| 2240 |             default: | 
| 2241 |                 ; | 
| 2242 |         } | 
| 2243 |     } | 
| 2244 | } | 
| 2245 |  | 
| 2246 | bool XSLTTokenizer::skipSubTree(const bool exitOnContent) | 
| 2247 | { | 
| 2248 |     bool hasContent = false; | 
| 2249 |     int depth = 0; | 
| 2250 |  | 
| 2251 |     while(!atEnd()) | 
| 2252 |     { | 
| 2253 |         switch(readNext()) | 
| 2254 |         { | 
| 2255 |             case QXmlStreamReader::Characters: | 
| 2256 |             { | 
| 2257 |                 if(whitespaceToSkip()) | 
| 2258 |                     continue; | 
| 2259 |                 else | 
| 2260 |                 { | 
| 2261 |                     hasContent = true; | 
| 2262 |                     if(exitOnContent) | 
| 2263 |                         return true; | 
| 2264 |  | 
| 2265 |                     break; | 
| 2266 |                 } | 
| 2267 |             } | 
| 2268 |             case QXmlStreamReader::StartElement: | 
| 2269 |             { | 
| 2270 |                 hasContent = true; | 
| 2271 |                 if(exitOnContent) | 
| 2272 |                     return true; | 
| 2273 |  | 
| 2274 |                 ++depth; | 
| 2275 |                 break; | 
| 2276 |             } | 
| 2277 |             case QXmlStreamReader::EndElement: | 
| 2278 |             { | 
| 2279 |                 --depth; | 
| 2280 |                 break; | 
| 2281 |             } | 
| 2282 |             default: | 
| 2283 |                 continue; | 
| 2284 |         } | 
| 2285 |  | 
| 2286 |         if(depth == -1) | 
| 2287 |             return hasContent; | 
| 2288 |     } | 
| 2289 |  | 
| 2290 |     checkForParseError(); | 
| 2291 |     return hasContent; | 
| 2292 | } | 
| 2293 |  | 
| 2294 | void XSLTTokenizer::parseFallbacksOnly() | 
| 2295 | { | 
| 2296 |     Q_ASSERT(isXSLT()); | 
| 2297 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 2298 |  | 
| 2299 |     skipSubTree(); | 
| 2300 |     Q_ASSERT(tokenType() == QXmlStreamReader::EndElement); | 
| 2301 | } | 
| 2302 |  | 
| 2303 | void XSLTTokenizer::insideAttributeSet() | 
| 2304 | { | 
| 2305 |     while(!atEnd()) | 
| 2306 |     { | 
| 2307 |         switch(readNext()) | 
| 2308 |         { | 
| 2309 |             case QXmlStreamReader::StartElement: | 
| 2310 |             { | 
| 2311 |                 if(isXSLT() && isElement(name: AttributeSet)) | 
| 2312 |                 { | 
| 2313 |                     // TODO | 
| 2314 |                     skipSubTree(); | 
| 2315 |                 } | 
| 2316 |                 else | 
| 2317 |                     unexpectedContent(); | 
| 2318 |             } | 
| 2319 |             case QXmlStreamReader::EndElement: | 
| 2320 |                 return; | 
| 2321 |             case QXmlStreamReader::ProcessingInstruction: | 
| 2322 |             case QXmlStreamReader::Comment: | 
| 2323 |                 continue; | 
| 2324 |             case QXmlStreamReader::Characters: | 
| 2325 |                 if(whitespaceToSkip()) | 
| 2326 |                     continue; | 
| 2327 |                 Q_FALLTHROUGH(); | 
| 2328 |             default: | 
| 2329 |                 unexpectedContent(); | 
| 2330 |         } | 
| 2331 |     } | 
| 2332 |     unexpectedContent(); | 
| 2333 | } | 
| 2334 |  | 
| 2335 | void XSLTTokenizer::insideStylesheetModule() | 
| 2336 | { | 
| 2337 |     while(!atEnd()) | 
| 2338 |     { | 
| 2339 |         switch(readNext()) | 
| 2340 |         { | 
| 2341 |             case QXmlStreamReader::StartElement: | 
| 2342 |             { | 
| 2343 |                 if(isXSLT()) | 
| 2344 |                 { | 
| 2345 |                     handleStandardAttributes(isXSLTElement: true); | 
| 2346 |                     handleXSLTVersion(to: 0, queueOnExit: 0, isXSLTElement: true, atts: 0, generateCode: false); | 
| 2347 |                     validateElement(); | 
| 2348 |  | 
| 2349 |                     /* Handle the various declarations. */ | 
| 2350 |                     switch(currentElementName()) | 
| 2351 |                     { | 
| 2352 |                         case Template: | 
| 2353 |                             insideTemplate(); | 
| 2354 |                             break; | 
| 2355 |                         case Function: | 
| 2356 |                             insideFunction(); | 
| 2357 |                             break; | 
| 2358 |                         case Variable: | 
| 2359 |                             queueVariableDeclaration(variableType: VariableDeclaration, to: &m_tokenSource); | 
| 2360 |                             break; | 
| 2361 |                         case Param: | 
| 2362 |                             queueVariableDeclaration(variableType: GlobalParameter, to: &m_tokenSource); | 
| 2363 |                             break; | 
| 2364 |                         case ImportSchema: | 
| 2365 |                         { | 
| 2366 |                             error(message: QtXmlPatterns::tr(sourceText: "This processor is not Schema-aware and "  | 
| 2367 |                                                                "therefore %1 cannot be used." ).arg(a: formatKeyword(keyword: toString(token: ImportSchema))), | 
| 2368 |                                   code: ReportContext::XTSE1660); | 
| 2369 |                             break; | 
| 2370 |                         } | 
| 2371 |                         case Output: | 
| 2372 |                         { | 
| 2373 |                             // TODO | 
| 2374 |                             skipSubTree(); | 
| 2375 |                             break; | 
| 2376 |                         } | 
| 2377 |                         case StripSpace: | 
| 2378 |                         case PreserveSpace: | 
| 2379 |                         { | 
| 2380 |                             // TODO @elements | 
| 2381 |                             skipSubTree(exitOnContent: true); | 
| 2382 |                             readNext(); | 
| 2383 |  | 
| 2384 |                             if(!isEndElement()) | 
| 2385 |                                 unexpectedContent(); | 
| 2386 |                             break; | 
| 2387 |                         } | 
| 2388 |                         case Include: | 
| 2389 |                         { | 
| 2390 |                             // TODO | 
| 2391 |                             if(skipSubTree(exitOnContent: true)) | 
| 2392 |                                 unexpectedContent(); | 
| 2393 |                             break; | 
| 2394 |                         } | 
| 2395 |                         case Import: | 
| 2396 |                         { | 
| 2397 |                             // TODO | 
| 2398 |                             if(skipSubTree(exitOnContent: true)) | 
| 2399 |                                 unexpectedContent(); | 
| 2400 |                             break; | 
| 2401 |                         } | 
| 2402 |                         case Key: | 
| 2403 |                         { | 
| 2404 |                             // TODO | 
| 2405 |                             skipSubTree(); | 
| 2406 |                             break; | 
| 2407 |                         } | 
| 2408 |                         case AttributeSet: | 
| 2409 |                             insideAttributeSet(); | 
| 2410 |                             break; | 
| 2411 |                         default: | 
| 2412 |                             if(m_processingMode.top() != ForwardCompatible) | 
| 2413 |                                 unexpectedContent(); | 
| 2414 |                     } | 
| 2415 |                 } | 
| 2416 |                 else | 
| 2417 |                 { | 
| 2418 |                     /* We have a user-defined data element. See section 3.6.2. */ | 
| 2419 |  | 
| 2420 |                     if(namespaceUri().isEmpty()) | 
| 2421 |                     { | 
| 2422 |                         error(message: QtXmlPatterns::tr(sourceText: "Top level stylesheet elements must be "  | 
| 2423 |                                                            "in a non-null namespace, which %1 isn't." ).arg(a: formatKeyword(keyword: name())), | 
| 2424 |                               code: ReportContext::XTSE0130); | 
| 2425 |                     } | 
| 2426 |                     else | 
| 2427 |                         skipSubTree(); | 
| 2428 |                 } | 
| 2429 |                 break; | 
| 2430 |             } | 
| 2431 |             case QXmlStreamReader::Characters: | 
| 2432 |             { | 
| 2433 |                 /* Regardless of xml:space, we skip whitespace, see step 4 in | 
| 2434 |                  * 4.2 Stripping Whitespace from the Stylesheet. */ | 
| 2435 |                 if(isWhitespace()) | 
| 2436 |                     continue; | 
| 2437 |  | 
| 2438 |                 unexpectedContent(code: ReportContext::XTSE0120); | 
| 2439 |                 break; | 
| 2440 |             } | 
| 2441 |             case QXmlStreamReader::EndElement: | 
| 2442 |             { | 
| 2443 |                 if(isXSLT()) | 
| 2444 |                     leaveState(); | 
| 2445 |  | 
| 2446 |                 break; | 
| 2447 |             } | 
| 2448 |             default: | 
| 2449 |                 ; | 
| 2450 |         } | 
| 2451 |     } | 
| 2452 |     checkForParseError(); | 
| 2453 | } | 
| 2454 |  | 
| 2455 | bool XSLTTokenizer::readToggleAttribute(const QString &localName, | 
| 2456 |                                         const QString &isTrue, | 
| 2457 |                                         const QString &isFalse, | 
| 2458 |                                         const QXmlStreamAttributes *const attsP) const | 
| 2459 | { | 
| 2460 |     const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes); | 
| 2461 |     Q_ASSERT(atts.hasAttribute(localName)); | 
| 2462 |     const QString value(atts.value(qualifiedName: localName).toString()); | 
| 2463 |  | 
| 2464 |     if(value == isTrue) | 
| 2465 |         return true; | 
| 2466 |     else if(value == isFalse) | 
| 2467 |         return false; | 
| 2468 |     else | 
| 2469 |     { | 
| 2470 |         error(message: QtXmlPatterns::tr(sourceText: "The value for attribute %1 on element %2 must either "  | 
| 2471 |                                            "be %3 or %4, not %5." ).arg(args: formatKeyword(keyword: localName), | 
| 2472 |                                                                        args: formatKeyword(keyword: name()), | 
| 2473 |                                                                        args: formatData(data: isTrue), | 
| 2474 |                                                                        args: formatData(data: isFalse), | 
| 2475 |                                                                        args: formatData(data: value)), | 
| 2476 |               code: ReportContext::XTSE0020); | 
| 2477 |         /* Silences a compiler warning. */ | 
| 2478 |         return false; | 
| 2479 |     } | 
| 2480 | } | 
| 2481 |  | 
| 2482 | int XSLTTokenizer::readAlternativeAttribute(const QHash<QString, int> &alternatives, | 
| 2483 |                                             const QXmlStreamAttribute &attr) const | 
| 2484 | { | 
| 2485 |     const QString value(attr.value().toString().trimmed()); | 
| 2486 |  | 
| 2487 |     if(alternatives.contains(akey: value)) | 
| 2488 |         return alternatives[value]; | 
| 2489 |  | 
| 2490 |     error(message: QtXmlPatterns::tr(sourceText: "Attribute %1 cannot have the value %2." ) | 
| 2491 |                                        .arg(args: formatKeyword(keyword: attr.name().toString()), | 
| 2492 |                                             args: formatData(data: attr.value().toString())), | 
| 2493 |           code: ReportContext::XTSE0020); | 
| 2494 |     return 0; /* Silence compiler warning. */ | 
| 2495 | } | 
| 2496 |  | 
| 2497 | bool XSLTTokenizer::attributeYesNo(const QString &localName) const | 
| 2498 | { | 
| 2499 |     return readToggleAttribute(localName, isTrue: QLatin1String("yes" ), isFalse: QLatin1String("no" )); | 
| 2500 | } | 
| 2501 |  | 
| 2502 | void XSLTTokenizer::queueSorting(const bool oneSortRequired, | 
| 2503 |                                  TokenSource::Queue *const to, | 
| 2504 |                                  const bool speciallyTreatWhitespace) | 
| 2505 | { | 
| 2506 |     Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); | 
| 2507 |  | 
| 2508 |     const NodeName elementName(currentElementName()); | 
| 2509 |     bool hasQueuedOneSort = false; | 
| 2510 |  | 
| 2511 |     while(!atEnd()) | 
| 2512 |     { | 
| 2513 |         switch(readNext()) | 
| 2514 |         { | 
| 2515 |             case QXmlStreamReader::EndElement: | 
| 2516 |             { | 
| 2517 |                 /* Let's say we have no sequence constructor, but only | 
| 2518 |                  * ignorable space. In that case we will actually loop | 
| 2519 |                  * infinitely if we don't have this check. */ | 
| 2520 |                 if(isXSLT()) | 
| 2521 |                 { | 
| 2522 |                     switch(currentElementName()) | 
| 2523 |                     { | 
| 2524 |                         case PerformSort: | 
| 2525 |                         case ForEach: | 
| 2526 |                         case ApplyTemplates: | 
| 2527 |                             return; | 
| 2528 |                         default: | 
| 2529 |                             ; | 
| 2530 |                     } | 
| 2531 |                 } | 
| 2532 |                 continue; | 
| 2533 |             } | 
| 2534 |             case QXmlStreamReader::StartElement: | 
| 2535 |             { | 
| 2536 |                 if(isXSLT() && isElement(name: Sort)) | 
| 2537 |                 { | 
| 2538 |                     if(hasQueuedOneSort) | 
| 2539 |                         queueToken(token: T_COMMA, to); | 
| 2540 |  | 
| 2541 |                     /* sorts are by default stable. */ | 
| 2542 |                     if(hasAttribute(localName: QLatin1String("stable" ))) | 
| 2543 |                     { | 
| 2544 |                         if(hasQueuedOneSort) | 
| 2545 |                         { | 
| 2546 |                             error(message: QtXmlPatterns::tr(sourceText: "The attribute %1 can only appear on "  | 
| 2547 |                                                                "the first %2 element." ).arg(args: formatKeyword(keyword: QLatin1String("stable" )), | 
| 2548 |                                                                                             args: formatKeyword(keyword: QLatin1String("sort" ))), | 
| 2549 |                                   code: ReportContext::XTSE0020); | 
| 2550 |                         } | 
| 2551 |  | 
| 2552 |                         if(attributeYesNo(localName: QLatin1String("stable" ))) | 
| 2553 |                             queueToken(token: T_STABLE, to); | 
| 2554 |                     } | 
| 2555 |  | 
| 2556 |                     if(!hasQueuedOneSort) | 
| 2557 |                     { | 
| 2558 |                         queueToken(token: T_ORDER, to); | 
| 2559 |                         queueToken(token: T_BY, to); | 
| 2560 |                     } | 
| 2561 |  | 
| 2562 |                     /* We store a copy such that we can use them after | 
| 2563 |                      * queueSelectOrSequenceConstructor() advances the reader. */ | 
| 2564 |                     const QXmlStreamAttributes atts(m_currentAttributes); | 
| 2565 |  | 
| 2566 |                     const int before = to->count(); | 
| 2567 |  | 
| 2568 |                     // TODO This doesn't work as is. @data-type can be an AVT. | 
| 2569 |                     if(atts.hasAttribute(qualifiedName: QLatin1String("data-type" ))) | 
| 2570 |                     { | 
| 2571 |                         if(readToggleAttribute(localName: QLatin1String("data-type" ), | 
| 2572 |                                                isTrue: QLatin1String("text" ), | 
| 2573 |                                                isFalse: QLatin1String("number" ), | 
| 2574 |                                                attsP: &atts)) | 
| 2575 |                             queueToken(token: Token(T_NCNAME, QLatin1String("string" )), to); | 
| 2576 |                         else | 
| 2577 |                             queueToken(token: Token(T_NCNAME, QLatin1String("number" )), to); | 
| 2578 |                     } | 
| 2579 |                     /* We queue these parantheses for the sake of the function | 
| 2580 |                      * call for attribute data-type. In the case we don't have | 
| 2581 |                      * such an attribute, the parantheses are just redundant. */ | 
| 2582 |                     queueToken(token: T_LPAREN, to); | 
| 2583 |                     queueSelectOrSequenceConstructor(code: ReportContext::XTSE1015, | 
| 2584 |                                                      emptynessAllowed: true, | 
| 2585 |                                                      to, | 
| 2586 |                                                      attsP: 0, | 
| 2587 |                                                      queueEmptyOnEmpty: false); | 
| 2588 |                     /* If neither a select attribute or a sequence constructor is supplied, | 
| 2589 |                      * we're supposed to use the context item. */ | 
| 2590 |                     queueToken(token: T_RPAREN, to); | 
| 2591 |                     if(before == to->count()) | 
| 2592 |                         queueToken(token: T_DOT, to); | 
| 2593 |  | 
| 2594 |                     // TODO case-order | 
| 2595 |                     // TODO lang | 
| 2596 |  | 
| 2597 |                     // TODO This doesn't work as is. @order can be an AVT, and so can case-order and lang. | 
| 2598 |                     if(atts.hasAttribute(qualifiedName: QLatin1String("order" )) && readToggleAttribute(localName: QLatin1String("order" ), | 
| 2599 |                                                                                        isTrue: QLatin1String("descending" ), | 
| 2600 |                                                                                        isFalse: QLatin1String("ascending" ), | 
| 2601 |                                                                                        attsP: &atts)) | 
| 2602 |                     { | 
| 2603 |                         queueToken(token: T_DESCENDING, to); | 
| 2604 |                     } | 
| 2605 |                     else | 
| 2606 |                     { | 
| 2607 |                         /* This is the default. */ | 
| 2608 |                         queueToken(token: T_ASCENDING, to); | 
| 2609 |                     } | 
| 2610 |  | 
| 2611 |                     if(atts.hasAttribute(qualifiedName: QLatin1String("collation" ))) | 
| 2612 |                     { | 
| 2613 |                         queueToken(token: T_INTERNAL, to); | 
| 2614 |                         queueToken(token: T_COLLATION, to); | 
| 2615 |                         queueAVT(expr: atts.value(qualifiedName: QLatin1String("collation" )).toString(), to); | 
| 2616 |                     } | 
| 2617 |  | 
| 2618 |                     hasQueuedOneSort = true; | 
| 2619 |                     continue; | 
| 2620 |                 } | 
| 2621 |                 else | 
| 2622 |                     break; | 
| 2623 |             } | 
| 2624 |             case QXmlStreamReader::Characters: | 
| 2625 |             { | 
| 2626 |                 if(speciallyTreatWhitespace && isWhitespace()) | 
| 2627 |                     continue; | 
| 2628 |  | 
| 2629 |                 if (whitespaceToSkip()) | 
| 2630 |                     continue; | 
| 2631 |  | 
| 2632 |                 /* We have an instruction which is a text node, we're done. */ | 
| 2633 |                 break; | 
| 2634 |             } | 
| 2635 |             case QXmlStreamReader::ProcessingInstruction: | 
| 2636 |             case QXmlStreamReader::Comment: | 
| 2637 |                 continue; | 
| 2638 |             default: | 
| 2639 |                 unexpectedContent(); | 
| 2640 |  | 
| 2641 |         }; | 
| 2642 |         if(oneSortRequired && !hasQueuedOneSort) | 
| 2643 |         { | 
| 2644 |             error(message: QtXmlPatterns::tr(sourceText: "At least one %1 element must appear as child of %2." ) | 
| 2645 |                                               .arg(args: formatKeyword(keyword: QLatin1String("sort" )), args: formatKeyword(keyword: toString(token: elementName))), | 
| 2646 |                   code: ReportContext::XTSE0010); | 
| 2647 |         } | 
| 2648 |         else | 
| 2649 |             return; | 
| 2650 |     } | 
| 2651 |     checkForParseError(); | 
| 2652 | } | 
| 2653 |  | 
| 2654 | void XSLTTokenizer::insideFunction() | 
| 2655 | { | 
| 2656 |     queueToken(token: T_DECLARE, to: &m_tokenSource); | 
| 2657 |     queueToken(token: T_FUNCTION, to: &m_tokenSource); | 
| 2658 |     queueToken(token: T_INTERNAL, to: &m_tokenSource); | 
| 2659 |     queueToken(token: Token(T_QNAME, readAttribute(localName: QLatin1String("name" ))), to: &m_tokenSource); | 
| 2660 |     queueToken(token: T_LPAREN, to: &m_tokenSource); | 
| 2661 |     const QString expectedType(hasAttribute(localName: QLatin1String("as" )) ? readAttribute(localName: QLatin1String("as" )): QString()); | 
| 2662 |  | 
| 2663 |     if(hasAttribute(localName: QLatin1String("override" ))) | 
| 2664 |     { | 
| 2665 |         /* We currently have no external functions, so we don't pass it on currently. */ | 
| 2666 |         attributeYesNo(localName: QLatin1String("override" )); | 
| 2667 |     } | 
| 2668 |  | 
| 2669 |     queueParams(parentName: Function, to: &m_tokenSource); | 
| 2670 |  | 
| 2671 |     queueToken(token: T_RPAREN, to: &m_tokenSource); | 
| 2672 |  | 
| 2673 |     if(!expectedType.isNull()) | 
| 2674 |     { | 
| 2675 |         queueToken(token: T_AS, to: &m_tokenSource); | 
| 2676 |         queueSequenceType(expr: expectedType); | 
| 2677 |     } | 
| 2678 |  | 
| 2679 |     QStack<Token> onExitTokens; | 
| 2680 |     handleXMLBase(to: &m_tokenSource, queueOnExit: &onExitTokens, isInstruction: true, atts: &m_currentAttributes); | 
| 2681 |     handleXSLTVersion(to: &m_tokenSource, queueOnExit: &onExitTokens, isXSLTElement: true); | 
| 2682 |     queueToken(token: T_CURLY_LBRACE, to: &m_tokenSource); | 
| 2683 |  | 
| 2684 |     pushState(nextState: InsideSequenceConstructor); | 
| 2685 |     insideSequenceConstructor(to: &m_tokenSource, onExitTokens, initialAdvance: false); | 
| 2686 |     /* We don't queue CURLY_RBRACE, because it's done in | 
| 2687 |      * insideSequenceConstructor(). */ | 
| 2688 | } | 
| 2689 |  | 
| 2690 | XPATHLTYPE XSLTTokenizer::currentSourceLocator() const | 
| 2691 | { | 
| 2692 |     XPATHLTYPE retval; | 
| 2693 |     retval.first_line = lineNumber(); | 
| 2694 |     retval.first_column = columnNumber(); | 
| 2695 |     return retval; | 
| 2696 | } | 
| 2697 |  | 
| 2698 | QT_END_NAMESPACE | 
| 2699 |  |