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
51QT_BEGIN_NAMESPACE
52
53using namespace QPatternist;
54
55Tokenizer::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
67XSLTTokenizer::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
83bool XSLTTokenizer::isAnyAttributeAllowed() const
84{
85 return m_processingMode.top() == ForwardCompatible;
86}
87
88void XSLTTokenizer::setParserContext(const ParserContext::Ptr &parseInfo)
89{
90 m_parseInfo = parseInfo;
91}
92
93void XSLTTokenizer::validateElement() const
94{
95 MaintainingReader<XSLTTokenLookup>::validateElement(elementName: currentElementName());
96}
97
98QSet<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
120ElementDescription<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
476QHash<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
488bool XSLTTokenizer::whitespaceToSkip() const
489{
490 return m_stripWhitespace.top() && isWhitespace();
491}
492
493void 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
543void 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
551QString 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
578int XSLTTokenizer::commenceScanOnly()
579{
580 /* Do nothing, return a dummy value. */
581 return 0;
582}
583
584void XSLTTokenizer::resumeTokenizationFrom(const int position)
585{
586 /* Do nothing. */
587 Q_UNUSED(position);
588}
589
590void 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
647void 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
679void 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
740void 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
780Tokenizer::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
824bool 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
833inline 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
843void XSLTTokenizer::queueOnExit(QStack<Token> &source,
844 TokenSource::Queue *const destination)
845{
846 while(!source.isEmpty())
847 queueToken(token: source.pop(), ts: destination);
848}
849
850void 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
962void 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
970void XSLTTokenizer::pushState(const State nextState)
971{
972 m_state.push(t: nextState);
973}
974
975void XSLTTokenizer::leaveState()
976{
977 m_state.pop();
978}
979
980void 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
1082void 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
1097void 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
1107void XSLTTokenizer::queueSequenceType(const QString &expr)
1108{
1109 m_tokenSource.enqueue(t: TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(),
1110 XQueryTokenizer::ItemType)));
1111}
1112
1113void 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
1122void XSLTTokenizer::queueEmptySequence(TokenSource::Queue *const to)
1123{
1124 queueToken(token: T_LPAREN, to);
1125 queueToken(token: T_RPAREN, to);
1126}
1127
1128void 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
1253bool 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
1294void 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
1335void 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
1350void 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
1466void XSLTTokenizer::startStorageOfCurrent(TokenSource::Queue *const to)
1467{
1468 queueToken(token: T_CURRENT, to);
1469 queueToken(token: T_CURLY_LBRACE, to);
1470}
1471
1472void XSLTTokenizer::endStorageOfCurrent(TokenSource::Queue *const to)
1473{
1474 queueToken(token: T_CURLY_RBRACE, to);
1475}
1476
1477void 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
1510bool 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
1518bool 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
2090bool 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
2100void 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
2114void 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
2172void 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
2246bool 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
2294void XSLTTokenizer::parseFallbacksOnly()
2295{
2296 Q_ASSERT(isXSLT());
2297 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
2298
2299 skipSubTree();
2300 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement);
2301}
2302
2303void 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
2335void 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
2455bool 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
2482int 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
2497bool XSLTTokenizer::attributeYesNo(const QString &localName) const
2498{
2499 return readToggleAttribute(localName, isTrue: QLatin1String("yes"), isFalse: QLatin1String("no"));
2500}
2501
2502void 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
2654void 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
2690XPATHLTYPE XSLTTokenizer::currentSourceLocator() const
2691{
2692 XPATHLTYPE retval;
2693 retval.first_line = lineNumber();
2694 retval.first_column = columnNumber();
2695 return retval;
2696}
2697
2698QT_END_NAMESPACE
2699

source code of qtxmlpatterns/src/xmlpatterns/parser/qxslttokenizer.cpp