source: trunk/src/xmlpatterns/parser/qxslttokenizer.cpp@ 1036

Last change on this file since 1036 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 96.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtXmlPatterns module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QStringList>
43
44#include "qbuiltintypes_p.h"
45#include "qcommonnamespaces_p.h"
46#include "qquerytransformparser_p.h"
47#include "qxquerytokenizer_p.h"
48#include "qpatternistlocale_p.h"
49
50#include "qxslttokenizer_p.h"
51
52QT_BEGIN_NAMESPACE
53
54using namespace QPatternist;
55
56Tokenizer::Token SingleTokenContainer::nextToken(YYLTYPE *const location)
57{
58 if(m_hasDelivered)
59 return Tokenizer::Token(END_OF_FILE);
60 else
61 {
62 *location = m_location;
63 m_hasDelivered = true;
64 return m_token;
65 }
66}
67
68XSLTTokenizer::XSLTTokenizer(QIODevice *const queryDevice,
69 const QUrl &location,
70 const ReportContext::Ptr &context,
71 const NamePool::Ptr &np) : Tokenizer(location)
72 , MaintainingReader<XSLTTokenLookup>(createElementDescriptions(), createStandardAttributes(), context, queryDevice)
73 , m_location(location)
74 , m_namePool(np)
75 /* We initialize after all name constants. */
76 , m_validationAlternatives(createValidationAlternatives())
77 , m_parseInfo(0)
78{
79 Q_ASSERT(m_namePool);
80
81 pushState(OutsideDocumentElement);
82}
83
84bool XSLTTokenizer::isAnyAttributeAllowed() const
85{
86 return m_processingMode.top() == ForwardCompatible;
87}
88
89void XSLTTokenizer::setParserContext(const ParserContext::Ptr &parseInfo)
90{
91 m_parseInfo = parseInfo;
92}
93
94void XSLTTokenizer::validateElement() const
95{
96 MaintainingReader<XSLTTokenLookup>::validateElement(currentElementName());
97}
98
99QSet<XSLTTokenizer::NodeName> XSLTTokenizer::createStandardAttributes()
100{
101 QSet<NodeName> retval;
102 enum
103 {
104 ReservedForAttributes = 6
105 };
106
107 retval.reserve(6);
108
109 retval.insert(DefaultCollation);
110 retval.insert(ExcludeResultPrefixes);
111 retval.insert(ExtensionElementPrefixes);
112 retval.insert(UseWhen);
113 retval.insert(Version);
114 retval.insert(XpathDefaultNamespace);
115
116 Q_ASSERT(retval.count() == ReservedForAttributes);
117
118 return retval;
119}
120
121ElementDescription<XSLTTokenLookup>::Hash XSLTTokenizer::createElementDescriptions()
122{
123 ElementDescription<XSLTTokenLookup>::Hash result;
124 enum
125 {
126 ReservedForElements = 40
127 };
128 result.reserve(ReservedForElements);
129
130 /* xsl:apply-templates */
131 {
132 ElementDescription<XSLTTokenLookup> &e = result[ApplyTemplates];
133 e.optionalAttributes.insert(Select);
134 e.optionalAttributes.insert(Mode);
135 }
136
137 /* xsl:template */
138 {
139 ElementDescription<XSLTTokenLookup> &e = result[Template];
140 e.optionalAttributes.insert(Match);
141 e.optionalAttributes.insert(Name);
142 e.optionalAttributes.insert(Mode);
143 e.optionalAttributes.insert(Priority);
144 e.optionalAttributes.insert(As);
145 }
146
147 /* xsl:text, xsl:choose and xsl:otherwise */
148 {
149 ElementDescription<XSLTTokenLookup> &e = result[Text];
150 result.insert(Choose, e);
151 result.insert(Otherwise, e);
152 }
153
154 /* xsl:stylesheet */
155 {
156 ElementDescription<XSLTTokenLookup> &e = result[Stylesheet];
157
158 e.requiredAttributes.insert(Version);
159
160 e.optionalAttributes.insert(Id);
161 e.optionalAttributes.insert(ExtensionElementPrefixes);
162 e.optionalAttributes.insert(ExcludeResultPrefixes);
163 e.optionalAttributes.insert(XpathDefaultNamespace);
164 e.optionalAttributes.insert(DefaultValidation);
165 e.optionalAttributes.insert(DefaultCollation);
166 e.optionalAttributes.insert(InputTypeAnnotations);
167 }
168
169 /* xsl:transform */
170 {
171 result[Transform] = result[Stylesheet];
172 }
173
174 /* xsl:value-of */
175 {
176 ElementDescription<XSLTTokenLookup> &e = result[ValueOf];
177 e.optionalAttributes.insert(Separator);
178 e.optionalAttributes.insert(Select);
179 }
180
181 /* xsl:variable */
182 {
183 ElementDescription<XSLTTokenLookup> &e = result[Variable];
184
185 e.requiredAttributes.insert(Name);
186
187 e.optionalAttributes.insert(Select);
188 e.optionalAttributes.insert(As);
189 }
190
191 /* xsl:when & xsl:if */
192 {
193 ElementDescription<XSLTTokenLookup> &e = result[When];
194
195 e.requiredAttributes.insert(Test);
196
197 result.insert(If, e);
198 }
199
200 /* xsl:sequence, xsl:for-each */
201 {
202 ElementDescription<XSLTTokenLookup> &e = result[Sequence];
203
204 e.requiredAttributes.insert(Select);
205
206 result.insert(ForEach, e);
207 }
208
209 /* xsl:comment */
210 {
211 ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::Comment];
212
213 e.optionalAttributes.insert(Select);
214 }
215
216 /* xsl:processing-instruction */
217 {
218 ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::ProcessingInstruction];
219
220 e.requiredAttributes.insert(Name);
221 e.optionalAttributes.insert(Select);
222 }
223
224 /* xsl:document */
225 {
226 ElementDescription<XSLTTokenLookup> &e = result[Document];
227
228 e.optionalAttributes.insert(Validation);
229 e.optionalAttributes.insert(Type);
230 }
231
232 /* xsl:element */
233 {
234 ElementDescription<XSLTTokenLookup> &e = result[Element];
235
236 e.requiredAttributes.insert(Name);
237
238 e.optionalAttributes.insert(Namespace);
239 e.optionalAttributes.insert(InheritNamespaces);
240 e.optionalAttributes.insert(UseAttributeSets);
241 e.optionalAttributes.insert(Validation);
242 e.optionalAttributes.insert(Type);
243 }
244
245 /* xsl:attribute */
246 {
247 ElementDescription<XSLTTokenLookup> &e = result[Attribute];
248
249 e.requiredAttributes.insert(Name);
250
251 e.optionalAttributes.insert(Namespace);
252 e.optionalAttributes.insert(Select);
253 e.optionalAttributes.insert(Separator);
254 e.optionalAttributes.insert(Validation);
255 e.optionalAttributes.insert(Type);
256 }
257
258 /* xsl:function */
259 {
260 ElementDescription<XSLTTokenLookup> &e = result[Function];
261
262 e.requiredAttributes.insert(Name);
263
264 e.optionalAttributes.insert(As);
265 e.optionalAttributes.insert(Override);
266 }
267
268 /* xsl:param */
269 {
270 ElementDescription<XSLTTokenLookup> &e = result[Param];
271
272 e.requiredAttributes.insert(Name);
273
274 e.optionalAttributes.insert(Select);
275 e.optionalAttributes.insert(As);
276 e.optionalAttributes.insert(Required);
277 e.optionalAttributes.insert(Tunnel);
278 }
279
280 /* xsl:namespace */
281 {
282 ElementDescription<XSLTTokenLookup> &e = result[Namespace];
283
284 e.requiredAttributes.insert(Name);
285 e.optionalAttributes.insert(Select);
286 }
287
288 /* xsl:call-template */
289 {
290 ElementDescription<XSLTTokenLookup> &e = result[CallTemplate];
291 e.requiredAttributes.insert(Name);
292 }
293
294 /* xsl:perform-sort */
295 {
296 ElementDescription<XSLTTokenLookup> &e = result[PerformSort];
297 e.requiredAttributes.insert(Select);
298 }
299
300 /* xsl:sort */
301 {
302 ElementDescription<XSLTTokenLookup> &e = result[Sort];
303
304 e.optionalAttributes.reserve(7);
305 e.optionalAttributes.insert(Select);
306 e.optionalAttributes.insert(Lang);
307 e.optionalAttributes.insert(Order);
308 e.optionalAttributes.insert(Collation);
309 e.optionalAttributes.insert(Stable);
310 e.optionalAttributes.insert(CaseOrder);
311 e.optionalAttributes.insert(DataType);
312 }
313
314 /* xsl:import-schema */
315 {
316 ElementDescription<XSLTTokenLookup> &e = result[ImportSchema];
317
318 e.optionalAttributes.reserve(2);
319 e.optionalAttributes.insert(Namespace);
320 e.optionalAttributes.insert(SchemaLocation);
321 }
322
323 /* xsl:message */
324 {
325 ElementDescription<XSLTTokenLookup> &e = result[Message];
326
327 e.optionalAttributes.reserve(2);
328 e.optionalAttributes.insert(Select);
329 e.optionalAttributes.insert(Terminate);
330 }
331
332 /* xsl:copy-of */
333 {
334 ElementDescription<XSLTTokenLookup> &e = result[CopyOf];
335
336 e.requiredAttributes.insert(Select);
337
338 e.optionalAttributes.reserve(2);
339 e.optionalAttributes.insert(CopyNamespaces);
340 e.optionalAttributes.insert(Type);
341 e.optionalAttributes.insert(Validation);
342 }
343
344 /* xsl:copy */
345 {
346 ElementDescription<XSLTTokenLookup> &e = result[Copy];
347
348 e.optionalAttributes.reserve(5);
349 e.optionalAttributes.insert(CopyNamespaces);
350 e.optionalAttributes.insert(InheritNamespaces);
351 e.optionalAttributes.insert(UseAttributeSets);
352 e.optionalAttributes.insert(Type);
353 e.optionalAttributes.insert(Validation);
354 }
355
356 /* xsl:output */
357 {
358 ElementDescription<XSLTTokenLookup> &e = result[Output];
359
360 e.optionalAttributes.reserve(17);
361 e.optionalAttributes.insert(Name);
362 e.optionalAttributes.insert(Method);
363 e.optionalAttributes.insert(ByteOrderMark);
364 e.optionalAttributes.insert(CdataSectionElements);
365 e.optionalAttributes.insert(DoctypePublic);
366 e.optionalAttributes.insert(DoctypeSystem);
367 e.optionalAttributes.insert(Encoding);
368 e.optionalAttributes.insert(EscapeUriAttributes);
369 e.optionalAttributes.insert(IncludeContentType);
370 e.optionalAttributes.insert(Indent);
371 e.optionalAttributes.insert(MediaType);
372 e.optionalAttributes.insert(NormalizationForm);
373 e.optionalAttributes.insert(OmitXmlDeclaration);
374 e.optionalAttributes.insert(Standalone);
375 e.optionalAttributes.insert(UndeclarePrefixes);
376 e.optionalAttributes.insert(UseCharacterMaps);
377 e.optionalAttributes.insert(Version);
378 }
379
380 /* xsl:attribute-set */
381 {
382 ElementDescription<XSLTTokenLookup> &e = result[AttributeSet];
383
384 e.requiredAttributes.insert(Name);
385 e.optionalAttributes.insert(UseAttributeSets);
386 }
387
388 /* xsl:include and xsl:import. */
389 {
390 ElementDescription<XSLTTokenLookup> &e = result[Include];
391 e.requiredAttributes.insert(Href);
392 result[Import] = e;
393 }
394
395 /* xsl:with-param */
396 {
397 ElementDescription<XSLTTokenLookup> &e = result[WithParam];
398 e.requiredAttributes.insert(Name);
399
400 e.optionalAttributes.insert(Select);
401 e.optionalAttributes.insert(As);
402 e.optionalAttributes.insert(Tunnel);
403 }
404
405 /* xsl:strip-space */
406 {
407 ElementDescription<XSLTTokenLookup> &e = result[StripSpace];
408 e.requiredAttributes.insert(Elements);
409
410 result.insert(PreserveSpace, e);
411 }
412
413 /* xsl:result-document */
414 {
415 ElementDescription<XSLTTokenLookup> &e = result[ResultDocument];
416
417 e.optionalAttributes.insert(ByteOrderMark);
418 e.optionalAttributes.insert(CdataSectionElements);
419 e.optionalAttributes.insert(DoctypePublic);
420 e.optionalAttributes.insert(DoctypeSystem);
421 e.optionalAttributes.insert(Encoding);
422 e.optionalAttributes.insert(EscapeUriAttributes);
423 e.optionalAttributes.insert(Format);
424 e.optionalAttributes.insert(Href);
425 e.optionalAttributes.insert(IncludeContentType);
426 e.optionalAttributes.insert(Indent);
427 e.optionalAttributes.insert(MediaType);
428 e.optionalAttributes.insert(Method);
429 e.optionalAttributes.insert(NormalizationForm);
430 e.optionalAttributes.insert(OmitXmlDeclaration);
431 e.optionalAttributes.insert(OutputVersion);
432 e.optionalAttributes.insert(Standalone);
433 e.optionalAttributes.insert(Type);
434 e.optionalAttributes.insert(UndeclarePrefixes);
435 e.optionalAttributes.insert(UseCharacterMaps);
436 e.optionalAttributes.insert(Validation);
437 }
438
439 /* xsl:key */
440 {
441 ElementDescription<XSLTTokenLookup> &e = result[Key];
442
443 e.requiredAttributes.insert(Name);
444 e.requiredAttributes.insert(Match);
445
446 e.optionalAttributes.insert(Use);
447 e.optionalAttributes.insert(Collation);
448 }
449
450 /* xsl:analyze-string */
451 {
452 ElementDescription<XSLTTokenLookup> &e = result[AnalyzeString];
453
454 e.requiredAttributes.insert(Select);
455 e.requiredAttributes.insert(Regex);
456
457 e.optionalAttributes.insert(Flags);
458 }
459
460 /* xsl:matching-substring */
461 {
462 /* We insert a default constructed value. */
463 result[MatchingSubstring];
464 }
465
466 /* xsl:non-matching-substring */
467 {
468 /* We insert a default constructed value. */
469 result[NonMatchingSubstring];
470 }
471
472 Q_ASSERT(result.count() == ReservedForElements);
473
474 return result;
475}
476
477QHash<QString, int> XSLTTokenizer::createValidationAlternatives()
478{
479 QHash<QString, int> retval;
480
481 retval.insert(QLatin1String("preserve"), 0);
482 retval.insert(QLatin1String("strip"), 1);
483 retval.insert(QLatin1String("strict"), 2);
484 retval.insert(QLatin1String("lax"), 3);
485
486 return retval;
487}
488
489bool XSLTTokenizer::whitespaceToSkip() const
490{
491 return m_stripWhitespace.top() && isWhitespace();
492}
493
494void XSLTTokenizer::unexpectedContent(const ReportContext::ErrorCode code) const
495{
496 QString message;
497
498 ReportContext::ErrorCode effectiveCode = code;
499
500 switch(tokenType())
501 {
502 case QXmlStreamReader::StartElement:
503 {
504 if(isXSLT())
505 {
506 switch(currentElementName())
507 {
508 case Include:
509 effectiveCode = ReportContext::XTSE0170;
510 break;
511 case Import:
512 effectiveCode = ReportContext::XTSE0190;
513 break;
514 default:
515 ;
516 }
517 }
518
519 message = QtXmlPatterns::tr("Element %1 is not allowed at this location.")
520 .arg(formatKeyword(name()));
521 break;
522 }
523 case QXmlStreamReader::Characters:
524 {
525 if(whitespaceToSkip())
526 return;
527
528 message = QtXmlPatterns::tr("Text nodes are not allowed at this location.");
529 break;
530 }
531 case QXmlStreamReader::Invalid:
532 {
533 /* It's an issue with well-formedness. */
534 message = escape(errorString());
535 break;
536 }
537 default:
538 Q_ASSERT(false);
539 }
540
541 error(message, effectiveCode);
542}
543
544void XSLTTokenizer::checkForParseError() const
545{
546 if(hasError())
547 {
548 error(QtXmlPatterns::tr("Parse error: %1").arg(escape(errorString())), ReportContext::XTSE0010);
549 }
550}
551
552QString XSLTTokenizer::readElementText()
553{
554 QString result;
555
556 while(!atEnd())
557 {
558 switch(readNext())
559 {
560 case QXmlStreamReader::Characters:
561 {
562 result += text().toString();
563 continue;
564 }
565 case QXmlStreamReader::Comment:
566 /* Fallthrough. */
567 case QXmlStreamReader::ProcessingInstruction:
568 continue;
569 case QXmlStreamReader::EndElement:
570 return result;
571 default:
572 unexpectedContent();
573 }
574 }
575
576 checkForParseError();
577 return result;
578}
579
580int XSLTTokenizer::commenceScanOnly()
581{
582 /* Do nothing, return a dummy value. */
583 return 0;
584}
585
586void XSLTTokenizer::resumeTokenizationFrom(const int position)
587{
588 /* Do nothing. */
589 Q_UNUSED(position);
590}
591
592void XSLTTokenizer::handleXSLTVersion(TokenSource::Queue *const to,
593 QStack<Token> *const queueOnExit,
594 const bool isXSLTElement,
595 const QXmlStreamAttributes *atts,
596 const bool generateCode,
597 const bool setGlobalVersion)
598{
599 const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT);
600 const QXmlStreamAttributes effectiveAtts(atts ? *atts : attributes());
601
602 if(!effectiveAtts.hasAttribute(ns, QLatin1String("version")))
603 return;
604
605 const QString attribute(effectiveAtts.value(ns, QLatin1String("version")).toString());
606 const AtomicValue::Ptr number(Decimal::fromLexical(attribute));
607
608 if(number->hasError())
609 {
610 error(QtXmlPatterns::tr("The value of the XSL-T version attribute "
611 "must be a value of type %1, which %2 isn't.").arg(formatType(m_namePool, BuiltinTypes::xsDecimal),
612 formatData(attribute)),
613 ReportContext::XTSE0110);
614 }
615 else
616 {
617
618 if(generateCode)
619 {
620 queueToken(Token(XSLT_VERSION, attribute), to);
621 queueToken(CURLY_LBRACE, to);
622 }
623
624 const xsDecimal version = number->as<Numeric>()->toDecimal();
625 if(version == 2.0)
626 m_processingMode.push(NormalProcessing);
627 else if(version == 1.0)
628 {
629 /* See section 3.6 Stylesheet Element discussing this. */
630 warning(QtXmlPatterns::tr("Running an XSL-T 1.0 stylesheet with a 2.0 processor."));
631 m_processingMode.push(BackwardsCompatible);
632
633 if(setGlobalVersion)
634 {
635 m_parseInfo->staticContext->setCompatModeEnabled(true);
636 m_parseInfo->isBackwardsCompat.push(true);
637 }
638 }
639 else if(version > 2.0)
640 m_processingMode.push(ForwardCompatible);
641 else if(version < 2.0)
642 m_processingMode.push(BackwardsCompatible);
643 }
644
645 if(generateCode)
646 queueOnExit->push(CURLY_RBRACE);
647}
648
649void XSLTTokenizer::handleXMLBase(TokenSource::Queue *const to,
650 QStack<Token> *const queueOnExit,
651 const bool isInstruction,
652 const QXmlStreamAttributes *atts)
653{
654 const QXmlStreamAttributes effectiveAtts(atts ? *atts : m_currentAttributes);
655
656 if(effectiveAtts.hasAttribute(QLatin1String("xml:base")))
657 {
658 const QStringRef val(effectiveAtts.value(QLatin1String("xml:base")));
659
660 if(!val.isEmpty())
661 {
662 if(isInstruction)
663 {
664 queueToken(BASEURI, to);
665 queueToken(Token(STRING_LITERAL, val.toString()), to);
666 queueToken(CURLY_LBRACE, to);
667 queueOnExit->push(CURLY_RBRACE);
668 }
669 else
670 {
671 queueToken(DECLARE, to);
672 queueToken(BASEURI, to);
673 queueToken(INTERNAL, to);
674 queueToken(Token(STRING_LITERAL, val.toString()), to);
675 queueToken(SEMI_COLON, to);
676 }
677 }
678 }
679}
680
681void XSLTTokenizer::handleStandardAttributes(const bool isXSLTElement)
682{
683 /* We're not necessarily StartElement, that's why we have atts passed in. */
684 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
685
686 if(m_hasHandledStandardAttributes)
687 return;
688
689 m_hasHandledStandardAttributes = true;
690
691 const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT);
692 const int len = m_currentAttributes.count();
693
694 for(int i = 0; i < len; ++i)
695 {
696 const QXmlStreamAttribute &att = m_currentAttributes.at(i);
697
698 if(att.qualifiedName() == QLatin1String("xml:space"))
699 {
700 const QStringRef val(m_currentAttributes.value(CommonNamespaces::XML, QLatin1String("space")));
701
702 /* We raise an error if the value is not recognized.
703 *
704 * Extensible Markup Language (XML) 1.0 (Fourth Edition), 2.10
705 * White Space Handling:
706 *
707 * 'This specification does not give meaning to any value of
708 * xml:space other than "default" and "preserve". It is an error
709 * for other values to be specified; the XML processor may report
710 * the error or may recover by ignoring the attribute specification
711 * or by reporting the (erroneous) value to the application.' */
712 m_stripWhitespace.push(readToggleAttribute(QLatin1String("xml:space"),
713 QLatin1String("default"),
714 QLatin1String("preserve"),
715 &m_currentAttributes));
716 }
717
718 if(att.namespaceUri() != ns)
719 continue;
720
721 switch(toToken(att.name()))
722 {
723 case Type:
724 /* Fallthrough. */
725 case Validation:
726 /* Fallthrough. */
727 case UseAttributeSets:
728 /* Fallthrough. */
729 case Version:
730 /* These are handled by other function such as
731 * handleValidationAttributes() and handleXSLTVersion(). */
732 continue;
733 default:
734 {
735 if(!isXSLTElement) /* validateElement() will take care of it, and we
736 * don't want to flag non-standard XSL-T attributes. */
737 {
738 error(QtXmlPatterns::tr("Unknown XSL-T attribute %1.")
739 .arg(formatKeyword(att.name())),
740 ReportContext::XTSE0805);
741 }
742 }
743 }
744 }
745}
746
747void XSLTTokenizer::handleValidationAttributes(const bool isLRE) const
748{
749 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
750
751 const QString ns(isLRE ? QString() : CommonNamespaces::XSLT);
752
753 const bool hasValidation = hasAttribute(ns, QLatin1String("validation"));
754 const bool hasType = hasAttribute(ns, QLatin1String("type"));
755
756 if(!hasType && !hasValidation)
757 return;
758
759 if(hasType && hasValidation)
760 {
761 error(QtXmlPatterns::tr("Attribute %1 and %2 are mutually exclusive.")
762 .arg(formatKeyword(QLatin1String("validation")),
763 formatKeyword(QLatin1String("type"))),
764 ReportContext::XTSE1505);
765 }
766
767 /* QXmlStreamReader surely doesn't make this easy. */
768 QXmlStreamAttribute validationAttribute;
769 int len = m_currentAttributes.count();
770
771 for(int i = 0; i < len; ++i)
772 {
773 const QXmlStreamAttribute &at = m_currentAttributes.at(i);
774 if(at.name() == QLatin1String("validation") && at.namespaceUri() == ns)
775 validationAttribute = at;
776 }
777
778 Q_ASSERT_X(!validationAttribute.name().isNull(), Q_FUNC_INFO,
779 "We should always find the attribute.");
780
781 /* We don't care about the return value, we just want to check it's a valid
782 * one. */
783 readAlternativeAttribute(m_validationAlternatives,
784 validationAttribute);
785}
786
787Tokenizer::Token XSLTTokenizer::nextToken(YYLTYPE *const sourceLocator)
788{
789 Q_UNUSED(sourceLocator);
790
791 if(m_tokenSource.isEmpty())
792 {
793 switch(m_state.top())
794 {
795 case OutsideDocumentElement:
796 outsideDocumentElement();
797 break;
798 case InsideStylesheetModule:
799 insideStylesheetModule();
800 break;
801 case InsideSequenceConstructor:
802 insideSequenceConstructor(&m_tokenSource);
803 break;
804 }
805
806 if(m_tokenSource.isEmpty())
807 {
808 *sourceLocator = currentSourceLocator();
809 return Token(END_OF_FILE);
810 }
811 else
812 return m_tokenSource.head()->nextToken(sourceLocator);
813 }
814 else
815 {
816 do
817 {
818 const Token candidate(m_tokenSource.head()->nextToken(sourceLocator));
819 if(candidate.type == END_OF_FILE)
820 m_tokenSource.dequeue();
821 else
822 return candidate;
823 }
824 while(!m_tokenSource.isEmpty());
825
826 /* Now we will resume parsing inside the regular XSL-T(XML) file. */
827 return nextToken(sourceLocator);
828 }
829}
830
831bool XSLTTokenizer::isElement(const XSLTTokenLookup::NodeName &name) const
832{
833 Q_ASSERT(isXSLT());
834 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement ||
835 tokenType() == QXmlStreamReader::EndElement);
836
837 return currentElementName() == name;
838}
839
840inline bool XSLTTokenizer::isXSLT() const
841{
842 Q_ASSERT_X(tokenType() == QXmlStreamReader::StartElement ||
843 tokenType() == QXmlStreamReader::EndElement,
844 Q_FUNC_INFO, "The current token state must be StartElement or EndElement.");
845 /* Possible optimization: let MaintainingReader set an m_isXSLT which we
846 * read. */
847 return namespaceUri() == CommonNamespaces::XSLT;
848}
849
850void XSLTTokenizer::queueOnExit(QStack<Token> &source,
851 TokenSource::Queue *const destination)
852{
853 while(!source.isEmpty())
854 queueToken(source.pop(), destination);
855}
856
857void XSLTTokenizer::outsideDocumentElement()
858{
859 while(!atEnd())
860 {
861 switch(readNext())
862 {
863 case QXmlStreamReader::StartElement:
864 {
865 /* First, we synthesize one of the built-in templates,
866 * see section 6.6 Built-in Template Rules.
867 *
868 * Note that insideStylesheetModule() can be called multiple
869 * times so we can't do it there. */
870 {
871 /* Start with the one for text nodes and attributes.
872 * declare template matches (text() | @*) mode #all
873 * {
874 * text{.}
875 * };
876 */
877
878 /* declare template matches (text() | @*) */
879 queueToken(DECLARE, &m_tokenSource);
880 queueToken(TEMPLATE, &m_tokenSource);
881 queueToken(MATCHES, &m_tokenSource);
882 queueToken(LPAREN, &m_tokenSource);
883 queueToken(TEXT, &m_tokenSource);
884 queueToken(LPAREN, &m_tokenSource);
885 queueToken(RPAREN, &m_tokenSource);
886 queueToken(BAR, &m_tokenSource);
887 queueToken(AT_SIGN, &m_tokenSource);
888 queueToken(STAR, &m_tokenSource);
889 queueToken(RPAREN, &m_tokenSource);
890
891 /* mode #all */
892 queueToken(MODE, &m_tokenSource);
893 queueToken(Token(NCNAME, QLatin1String("#all")), &m_tokenSource);
894 queueToken(CURLY_LBRACE, &m_tokenSource);
895
896 /* text{.} { */
897 queueToken(TEXT, &m_tokenSource);
898 queueToken(CURLY_LBRACE, &m_tokenSource);
899 queueToken(DOT, &m_tokenSource);
900 queueToken(CURLY_RBRACE, &m_tokenSource);
901
902 /* }; */
903 queueToken(CURLY_RBRACE, &m_tokenSource);
904 queueToken(SEMI_COLON, &m_tokenSource);
905 }
906
907 if(isXSLT() && isStylesheetElement())
908 {
909 handleStandardAttributes(true);
910 QStack<Token> onExitTokens;
911 handleXMLBase(&m_tokenSource, &onExitTokens, false);
912 handleXSLTVersion(&m_tokenSource, &onExitTokens, true, 0, false, true);
913 validateElement();
914 queueNamespaceDeclarations(&m_tokenSource, 0, true);
915
916 /* We're a regular stylesheet. */
917
918 pushState(InsideStylesheetModule);
919 insideStylesheetModule();
920 }
921 else
922 {
923 /* We're a simplified stylesheet. */
924
925 if(!hasAttribute(CommonNamespaces::XSLT, QLatin1String("version")))
926 {
927 error(QtXmlPatterns::tr("In a simplified stylesheet module, attribute %1 must be present.")
928 .arg(formatKeyword(QLatin1String("version"))),
929 ReportContext::XTSE0010);
930 }
931
932 QStack<Token> onExitTokens;
933
934 /* We synthesize this as exemplified in
935 * 3.7 Simplified Stylesheet Modules. */
936 queueToken(DECLARE, &m_tokenSource);
937 queueToken(TEMPLATE, &m_tokenSource);
938 queueToken(MATCHES, &m_tokenSource);
939 queueToken(LPAREN, &m_tokenSource);
940 queueToken(SLASH, &m_tokenSource);
941 queueToken(RPAREN, &m_tokenSource);
942 queueToken(CURLY_LBRACE, &m_tokenSource);
943 pushState(InsideSequenceConstructor);
944
945 handleXSLTVersion(&m_tokenSource, &onExitTokens, false, 0, true);
946 handleStandardAttributes(false);
947
948 insideSequenceConstructor(&m_tokenSource, false);
949
950 queueOnExit(onExitTokens, &m_tokenSource);
951 queueToken(CURLY_RBRACE, &m_tokenSource);
952 queueToken(CURLY_RBRACE, &m_tokenSource);
953 queueToken(SEMI_COLON, &m_tokenSource);
954 }
955
956 queueToken(APPLY_TEMPLATE, &m_tokenSource);
957 queueToken(LPAREN, &m_tokenSource);
958 queueToken(RPAREN, &m_tokenSource);
959
960 break;
961 }
962 default:
963 /* Do nothing. */;
964 }
965 }
966 checkForParseError();
967}
968
969void XSLTTokenizer::queueToken(const Token &token,
970 TokenSource::Queue *const to)
971{
972 TokenSource::Queue *const effective = to ? to : &m_tokenSource;
973
974 effective->enqueue(TokenSource::Ptr(new SingleTokenContainer(token, currentSourceLocator())));
975}
976
977void XSLTTokenizer::pushState(const State nextState)
978{
979 m_state.push(nextState);
980}
981
982void XSLTTokenizer::leaveState()
983{
984 m_state.pop();
985}
986
987void XSLTTokenizer::insideTemplate()
988{
989 const bool hasPriority = hasAttribute(QLatin1String("priority"));
990 const bool hasMatch = hasAttribute(QLatin1String("match"));
991 const bool hasName = hasAttribute(QLatin1String("name"));
992 const bool hasMode = hasAttribute(QLatin1String("mode"));
993 const bool hasAs = hasAttribute(QLatin1String("as"));
994
995 if(!hasMatch &&
996 (hasMode ||
997 hasPriority))
998 {
999 error(QtXmlPatterns::tr("If element %1 has no attribute %2, it cannot have attribute %3 or %4.")
1000 .arg(formatKeyword(QLatin1String("template")),
1001 formatKeyword(QLatin1String("match")),
1002 formatKeyword(QLatin1String("mode")),
1003 formatKeyword(QLatin1String("priority"))),
1004 ReportContext::XTSE0500);
1005 }
1006 else if(!hasMatch && !hasName)
1007 {
1008 error(QtXmlPatterns::tr("Element %1 must have at least one of the attributes %2 or %3.")
1009 .arg(formatKeyword(QLatin1String("template")),
1010 formatKeyword(QLatin1String("name")),
1011 formatKeyword(QLatin1String("match"))),
1012 ReportContext::XTSE0500);
1013 }
1014
1015 queueToken(DECLARE, &m_tokenSource);