1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <QStringList>
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"
50 #include "qxslttokenizer_p.h"
54 using namespace QPatternist;
56 Tokenizer::Token SingleTokenContainer::nextToken(YYLTYPE *const location)
59 return Tokenizer::Token(END_OF_FILE);
62 *location = m_location;
63 m_hasDelivered = true;
68 XSLTTokenizer::XSLTTokenizer(QIODevice *const queryDevice,
70 const ReportContext::Ptr &context,
71 const NamePool::Ptr &np) : Tokenizer(location)
72 , MaintainingReader<XSLTTokenLookup>(createElementDescriptions(), createStandardAttributes(), context, queryDevice)
73 , m_location(location)
75 /* We initialize after all name constants. */
76 , m_validationAlternatives(createValidationAlternatives())
81 pushState(OutsideDocumentElement);
84 bool XSLTTokenizer::isAnyAttributeAllowed() const
86 return m_processingMode.top() == ForwardCompatible;
89 void XSLTTokenizer::setParserContext(const ParserContext::Ptr &parseInfo)
91 m_parseInfo = parseInfo;
94 void XSLTTokenizer::validateElement() const
96 MaintainingReader<XSLTTokenLookup>::validateElement(currentElementName());
99 QSet<XSLTTokenizer::NodeName> XSLTTokenizer::createStandardAttributes()
101 QSet<NodeName> retval;
104 ReservedForAttributes = 6
109 retval.insert(DefaultCollation);
110 retval.insert(ExcludeResultPrefixes);
111 retval.insert(ExtensionElementPrefixes);
112 retval.insert(UseWhen);
113 retval.insert(Version);
114 retval.insert(XpathDefaultNamespace);
116 Q_ASSERT(retval.count() == ReservedForAttributes);
121 ElementDescription<XSLTTokenLookup>::Hash XSLTTokenizer::createElementDescriptions()
123 ElementDescription<XSLTTokenLookup>::Hash result;
126 ReservedForElements = 40
128 result.reserve(ReservedForElements);
130 /* xsl:apply-templates */
132 ElementDescription<XSLTTokenLookup> &e = result[ApplyTemplates];
133 e.optionalAttributes.insert(Select);
134 e.optionalAttributes.insert(Mode);
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);
147 /* xsl:text, xsl:choose and xsl:otherwise */
149 ElementDescription<XSLTTokenLookup> &e = result[Text];
150 result.insert(Choose, e);
151 result.insert(Otherwise, e);
156 ElementDescription<XSLTTokenLookup> &e = result[Stylesheet];
158 e.requiredAttributes.insert(Version);
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);
171 result[Transform] = result[Stylesheet];
176 ElementDescription<XSLTTokenLookup> &e = result[ValueOf];
177 e.optionalAttributes.insert(Separator);
178 e.optionalAttributes.insert(Select);
183 ElementDescription<XSLTTokenLookup> &e = result[Variable];
185 e.requiredAttributes.insert(Name);
187 e.optionalAttributes.insert(Select);
188 e.optionalAttributes.insert(As);
191 /* xsl:when & xsl:if */
193 ElementDescription<XSLTTokenLookup> &e = result[When];
195 e.requiredAttributes.insert(Test);
197 result.insert(If, e);
200 /* xsl:sequence, xsl:for-each */
202 ElementDescription<XSLTTokenLookup> &e = result[Sequence];
204 e.requiredAttributes.insert(Select);
206 result.insert(ForEach, e);
211 ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::Comment];
213 e.optionalAttributes.insert(Select);
216 /* xsl:processing-instruction */
218 ElementDescription<XSLTTokenLookup> &e = result[XSLTTokenLookup::ProcessingInstruction];
220 e.requiredAttributes.insert(Name);
221 e.optionalAttributes.insert(Select);
226 ElementDescription<XSLTTokenLookup> &e = result[Document];
228 e.optionalAttributes.insert(Validation);
229 e.optionalAttributes.insert(Type);
234 ElementDescription<XSLTTokenLookup> &e = result[Element];
236 e.requiredAttributes.insert(Name);
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);
247 ElementDescription<XSLTTokenLookup> &e = result[Attribute];
249 e.requiredAttributes.insert(Name);
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);
260 ElementDescription<XSLTTokenLookup> &e = result[Function];
262 e.requiredAttributes.insert(Name);
264 e.optionalAttributes.insert(As);
265 e.optionalAttributes.insert(Override);
270 ElementDescription<XSLTTokenLookup> &e = result[Param];
272 e.requiredAttributes.insert(Name);
274 e.optionalAttributes.insert(Select);
275 e.optionalAttributes.insert(As);
276 e.optionalAttributes.insert(Required);
277 e.optionalAttributes.insert(Tunnel);
282 ElementDescription<XSLTTokenLookup> &e = result[Namespace];
284 e.requiredAttributes.insert(Name);
285 e.optionalAttributes.insert(Select);
288 /* xsl:call-template */
290 ElementDescription<XSLTTokenLookup> &e = result[CallTemplate];
291 e.requiredAttributes.insert(Name);
294 /* xsl:perform-sort */
296 ElementDescription<XSLTTokenLookup> &e = result[PerformSort];
297 e.requiredAttributes.insert(Select);
302 ElementDescription<XSLTTokenLookup> &e = result[Sort];
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);
314 /* xsl:import-schema */
316 ElementDescription<XSLTTokenLookup> &e = result[ImportSchema];
318 e.optionalAttributes.reserve(2);
319 e.optionalAttributes.insert(Namespace);
320 e.optionalAttributes.insert(SchemaLocation);
325 ElementDescription<XSLTTokenLookup> &e = result[Message];
327 e.optionalAttributes.reserve(2);
328 e.optionalAttributes.insert(Select);
329 e.optionalAttributes.insert(Terminate);
334 ElementDescription<XSLTTokenLookup> &e = result[CopyOf];
336 e.requiredAttributes.insert(Select);
338 e.optionalAttributes.reserve(2);
339 e.optionalAttributes.insert(CopyNamespaces);
340 e.optionalAttributes.insert(Type);
341 e.optionalAttributes.insert(Validation);
346 ElementDescription<XSLTTokenLookup> &e = result[Copy];
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);
358 ElementDescription<XSLTTokenLookup> &e = result[Output];
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);
380 /* xsl:attribute-set */
382 ElementDescription<XSLTTokenLookup> &e = result[AttributeSet];
384 e.requiredAttributes.insert(Name);
385 e.optionalAttributes.insert(UseAttributeSets);
388 /* xsl:include and xsl:import. */
390 ElementDescription<XSLTTokenLookup> &e = result[Include];
391 e.requiredAttributes.insert(Href);
397 ElementDescription<XSLTTokenLookup> &e = result[WithParam];
398 e.requiredAttributes.insert(Name);
400 e.optionalAttributes.insert(Select);
401 e.optionalAttributes.insert(As);
402 e.optionalAttributes.insert(Tunnel);
405 /* xsl:strip-space */
407 ElementDescription<XSLTTokenLookup> &e = result[StripSpace];
408 e.requiredAttributes.insert(Elements);
410 result.insert(PreserveSpace, e);
413 /* xsl:result-document */
415 ElementDescription<XSLTTokenLookup> &e = result[ResultDocument];
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);
441 ElementDescription<XSLTTokenLookup> &e = result[Key];
443 e.requiredAttributes.insert(Name);
444 e.requiredAttributes.insert(Match);
446 e.optionalAttributes.insert(Use);
447 e.optionalAttributes.insert(Collation);
450 /* xsl:analyze-string */
452 ElementDescription<XSLTTokenLookup> &e = result[AnalyzeString];
454 e.requiredAttributes.insert(Select);
455 e.requiredAttributes.insert(Regex);
457 e.optionalAttributes.insert(Flags);
460 /* xsl:matching-substring */
462 /* We insert a default constructed value. */
463 result[MatchingSubstring];
466 /* xsl:non-matching-substring */
468 /* We insert a default constructed value. */
469 result[NonMatchingSubstring];
472 Q_ASSERT(result.count() == ReservedForElements);
477 QHash<QString, int> XSLTTokenizer::createValidationAlternatives()
479 QHash<QString, int> retval;
481 retval.insert(QLatin1String("preserve"), 0);
482 retval.insert(QLatin1String("strip"), 1);
483 retval.insert(QLatin1String("strict"), 2);
484 retval.insert(QLatin1String("lax"), 3);
489 bool XSLTTokenizer::whitespaceToSkip() const
491 return m_stripWhitespace.top() && isWhitespace();
494 void XSLTTokenizer::unexpectedContent(const ReportContext::ErrorCode code) const
498 ReportContext::ErrorCode effectiveCode = code;
502 case QXmlStreamReader::StartElement:
506 switch(currentElementName())
509 effectiveCode = ReportContext::XTSE0170;
512 effectiveCode = ReportContext::XTSE0190;
519 message = QtXmlPatterns::tr("Element %1 is not allowed at this location.")
520 .arg(formatKeyword(name()));
523 case QXmlStreamReader::Characters:
525 if(whitespaceToSkip())
528 message = QtXmlPatterns::tr("Text nodes are not allowed at this location.");
531 case QXmlStreamReader::Invalid:
533 /* It's an issue with well-formedness. */
534 message = escape(errorString());
541 error(message, effectiveCode);
544 void XSLTTokenizer::checkForParseError() const
548 error(QtXmlPatterns::tr("Parse error: %1").arg(escape(errorString())), ReportContext::XTSE0010);
552 QString XSLTTokenizer::readElementText()
560 case QXmlStreamReader::Characters:
562 result += text().toString();
565 case QXmlStreamReader::Comment:
567 case QXmlStreamReader::ProcessingInstruction:
569 case QXmlStreamReader::EndElement:
576 checkForParseError();
580 int XSLTTokenizer::commenceScanOnly()
582 /* Do nothing, return a dummy value. */
586 void XSLTTokenizer::resumeTokenizationFrom(const int position)
592 void 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)
599 const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT);
600 const QXmlStreamAttributes effectiveAtts(atts ? *atts : attributes());
602 if(!effectiveAtts.hasAttribute(ns, QLatin1String("version")))
605 const QString attribute(effectiveAtts.value(ns, QLatin1String("version")).toString());
606 const AtomicValue::Ptr number(Decimal::fromLexical(attribute));
608 if(number->hasError())
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);
620 queueToken(Token(XSLT_VERSION, attribute), to);
621 queueToken(CURLY_LBRACE, to);
624 const xsDecimal version = number->as<Numeric>()->toDecimal();
626 m_processingMode.push(NormalProcessing);
627 else if(version == 1.0)
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);
635 m_parseInfo->staticContext->setCompatModeEnabled(true);
636 m_parseInfo->isBackwardsCompat.push(true);
639 else if(version > 2.0)
640 m_processingMode.push(ForwardCompatible);
641 else if(version < 2.0)
642 m_processingMode.push(BackwardsCompatible);
646 queueOnExit->push(CURLY_RBRACE);
649 void XSLTTokenizer::handleXMLBase(TokenSource::Queue *const to,
650 QStack<Token> *const queueOnExit,
651 const bool isInstruction,
652 const QXmlStreamAttributes *atts)
654 const QXmlStreamAttributes effectiveAtts(atts ? *atts : m_currentAttributes);
656 if(effectiveAtts.hasAttribute(QLatin1String("xml:base")))
658 const QStringRef val(effectiveAtts.value(QLatin1String("xml:base")));
664 queueToken(BASEURI, to);
665 queueToken(Token(STRING_LITERAL, val.toString()), to);
666 queueToken(CURLY_LBRACE, to);
667 queueOnExit->push(CURLY_RBRACE);
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);
681 void XSLTTokenizer::handleStandardAttributes(const bool isXSLTElement)
683 /* We're not necessarily StartElement, that's why we have atts passed in. */
684 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
686 if(m_hasHandledStandardAttributes)
689 m_hasHandledStandardAttributes = true;
691 const QString ns(isXSLTElement ? QString() : CommonNamespaces::XSLT);
692 const int len = m_currentAttributes.count();
694 for(int i = 0; i < len; ++i)
696 const QXmlStreamAttribute &att = m_currentAttributes.at(i);
698 if(att.qualifiedName() == QLatin1String("xml:space"))
700 const QStringRef val(m_currentAttributes.value(CommonNamespaces::XML, QLatin1String("space")));
702 /* We raise an error if the value is not recognized.
704 * Extensible Markup Language (XML) 1.0 (Fourth Edition), 2.10
705 * White Space Handling:
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));
718 if(att.namespaceUri() != ns)
721 switch(toToken(att.name()))
727 case UseAttributeSets:
730 /* These are handled by other function such as
731 * handleValidationAttributes() and handleXSLTVersion(). */
735 if(!isXSLTElement) /* validateElement() will take care of it, and we
736 * don't want to flag non-standard XSL-T attributes. */
738 error(QtXmlPatterns::tr("Unknown XSL-T attribute %1.")
739 .arg(formatKeyword(att.name())),
740 ReportContext::XTSE0805);
747 void XSLTTokenizer::handleValidationAttributes(const bool isLRE) const
749 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
751 const QString ns(isLRE ? QString() : CommonNamespaces::XSLT);
753 const bool hasValidation = hasAttribute(ns, QLatin1String("validation"));
754 const bool hasType = hasAttribute(ns, QLatin1String("type"));
756 if(!hasType && !hasValidation)
759 if(hasType && hasValidation)
761 error(QtXmlPatterns::tr("Attribute %1 and %2 are mutually exclusive.")
762 .arg(formatKeyword(QLatin1String("validation")),
763 formatKeyword(QLatin1String("type"))),
764 ReportContext::XTSE1505);
767 /* QXmlStreamReader surely doesn't make this easy. */
768 QXmlStreamAttribute validationAttribute;
769 int len = m_currentAttributes.count();
771 for(int i = 0; i < len; ++i)
773 const QXmlStreamAttribute &at = m_currentAttributes.at(i);
774 if(at.name() == QLatin1String("validation") && at.namespaceUri() == ns)
775 validationAttribute = at;
778 Q_ASSERT_X(!validationAttribute.name().isNull(), Q_FUNC_INFO,
779 "We should always find the attribute.");
781 /* We don't care about the return value, we just want to check it's a valid
783 readAlternativeAttribute(m_validationAlternatives,
784 validationAttribute);
787 Tokenizer::Token XSLTTokenizer::nextToken(YYLTYPE *const sourceLocator)
789 Q_UNUSED(sourceLocator);
791 if(m_tokenSource.isEmpty())
793 switch(m_state.top())
795 case OutsideDocumentElement:
796 outsideDocumentElement();
798 case InsideStylesheetModule:
799 insideStylesheetModule();
801 case InsideSequenceConstructor:
802 insideSequenceConstructor(&m_tokenSource);
806 if(m_tokenSource.isEmpty())
808 *sourceLocator = currentSourceLocator();
809 return Token(END_OF_FILE);
812 return m_tokenSource.head()->nextToken(sourceLocator);
818 const Token candidate(m_tokenSource.head()->nextToken(sourceLocator));
819 if(candidate.type == END_OF_FILE)
820 m_tokenSource.dequeue();
824 while(!m_tokenSource.isEmpty());
826 /* Now we will resume parsing inside the regular XSL-T(XML) file. */
827 return nextToken(sourceLocator);
831 bool XSLTTokenizer::isElement(const XSLTTokenLookup::NodeName &name) const
834 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement ||
835 tokenType() == QXmlStreamReader::EndElement);
837 return currentElementName() == name;
840 inline bool XSLTTokenizer::isXSLT() const
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
847 return namespaceUri() == CommonNamespaces::XSLT;
850 void XSLTTokenizer::queueOnExit(QStack<Token> &source,
851 TokenSource::Queue *const destination)
853 while(!source.isEmpty())
854 queueToken(source.pop(), destination);
857 void XSLTTokenizer::outsideDocumentElement()
863 case QXmlStreamReader::StartElement:
865 /* First, we synthesize one of the built-in templates,
866 * see section 6.6 Built-in Template Rules.
868 * Note that insideStylesheetModule() can be called multiple
869 * times so we can't do it there. */
871 /* Start with the one for text nodes and attributes.
872 * declare template matches (text() | @*) mode #all
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);
892 queueToken(MODE, &m_tokenSource);
893 queueToken(Token(NCNAME, QLatin1String("#all")), &m_tokenSource);
894 queueToken(CURLY_LBRACE, &m_tokenSource);
897 queueToken(TEXT, &m_tokenSource);
898 queueToken(CURLY_LBRACE, &m_tokenSource);
899 queueToken(DOT, &m_tokenSource);
900 queueToken(CURLY_RBRACE, &m_tokenSource);
903 queueToken(CURLY_RBRACE, &m_tokenSource);
904 queueToken(SEMI_COLON, &m_tokenSource);
907 if(isXSLT() && isStylesheetElement())
909 handleStandardAttributes(true);
910 QStack<Token> onExitTokens;
911 handleXMLBase(&m_tokenSource, &onExitTokens, false);
912 handleXSLTVersion(&m_tokenSource, &onExitTokens, true, 0, false, true);
914 queueNamespaceDeclarations(&m_tokenSource, 0, true);
916 /* We're a regular stylesheet. */
918 pushState(InsideStylesheetModule);
919 insideStylesheetModule();
923 /* We're a simplified stylesheet. */
925 if(!hasAttribute(CommonNamespaces::XSLT, QLatin1String("version")))
927 error(QtXmlPatterns::tr("In a simplified stylesheet module, attribute %1 must be present.")
928 .arg(formatKeyword(QLatin1String("version"))),
929 ReportContext::XTSE0010);
932 QStack<Token> onExitTokens;
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);
945 handleXSLTVersion(&m_tokenSource, &onExitTokens, false, 0, true);
946 handleStandardAttributes(false);
948 insideSequenceConstructor(&m_tokenSource, false);
950 queueOnExit(onExitTokens, &m_tokenSource);
951 queueToken(CURLY_RBRACE, &m_tokenSource);
952 queueToken(CURLY_RBRACE, &m_tokenSource);
953 queueToken(SEMI_COLON, &m_tokenSource);
956 queueToken(APPLY_TEMPLATE, &m_tokenSource);
957 queueToken(LPAREN, &m_tokenSource);
958 queueToken(RPAREN, &m_tokenSource);
966 checkForParseError();
969 void XSLTTokenizer::queueToken(const Token &token,
970 TokenSource::Queue *const to)
972 TokenSource::Queue *const effective = to ? to : &m_tokenSource;
974 effective->enqueue(TokenSource::Ptr(new SingleTokenContainer(token, currentSourceLocator())));
977 void XSLTTokenizer::pushState(const State nextState)
979 m_state.push(nextState);
982 void XSLTTokenizer::leaveState()
987 void XSLTTokenizer::insideTemplate()
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"));
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);
1006 else if(!hasMatch && !hasName)
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);
1015 queueToken(DECLARE, &m_tokenSource);
1016 queueToken(TEMPLATE, &m_tokenSource);
1020 queueToken(NAME, &m_tokenSource);
1021 queueToken(Token(QNAME, readAttribute(QLatin1String("name"))), &m_tokenSource);
1026 queueToken(MATCHES, &m_tokenSource);
1027 queueExpression(readAttribute(QLatin1String("match")), &m_tokenSource);
1032 const QString modeString(readAttribute(QLatin1String("mode")).simplified());
1034 if(modeString.isEmpty())
1036 error(QtXmlPatterns::tr("At least one mode must be specified in the %1-attribute on element %2.")
1037 .arg(formatKeyword(QLatin1String("mode")),
1038 formatKeyword(QLatin1String("template"))),
1039 ReportContext::XTSE0500);
1042 queueToken(MODE, &m_tokenSource);
1044 const QStringList modeList(modeString.split(QLatin1Char(' ')));
1046 for(int i = 0; i < modeList.count(); ++i)
1048 const QString &mode = modeList.at(i);
1050 queueToken(Token(mode.contains(QLatin1Char(':')) ? QNAME : NCNAME, mode), &m_tokenSource);
1052 if(i < modeList.count() - 1)
1053 queueToken(COMMA, &m_tokenSource);
1059 queueToken(PRIORITY, &m_tokenSource);
1060 queueToken(Token(STRING_LITERAL, readAttribute(QLatin1String("priority"))), &m_tokenSource);
1063 QStack<Token> onExitTokens;
1064 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
1066 /* queueParams moves the reader so we need to freeze the attributes. */
1067 const QXmlStreamAttributes atts(m_currentAttributes);
1068 handleStandardAttributes(true);
1069 queueToken(LPAREN, &m_tokenSource);
1070 queueParams(Template, &m_tokenSource);
1071 queueToken(RPAREN, &m_tokenSource);
1075 queueToken(AS, &m_tokenSource);
1076 queueSequenceType(atts.value(QLatin1String("as")).toString());
1079 queueToken(CURLY_LBRACE, &m_tokenSource);
1081 handleXMLBase(&m_tokenSource, &onExitTokens, true, &atts);
1082 handleXSLTVersion(&m_tokenSource, &onExitTokens, true, &atts);
1083 pushState(InsideSequenceConstructor);
1084 startStorageOfCurrent(&m_tokenSource);
1085 insideSequenceConstructor(&m_tokenSource, onExitTokens, false);
1086 queueOnExit(onExitTokens, &m_tokenSource);
1089 void XSLTTokenizer::queueExpression(const QString &expr,
1090 TokenSource::Queue *const to,
1091 const bool wrapWithParantheses)
1093 TokenSource::Queue *const effectiveTo = to ? to : &m_tokenSource;
1095 if(wrapWithParantheses)
1096 queueToken(LPAREN, effectiveTo);
1098 effectiveTo->enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI())));
1100 if(wrapWithParantheses)
1101 queueToken(RPAREN, effectiveTo);
1104 void XSLTTokenizer::queueAVT(const QString &expr,
1105 TokenSource::Queue *const to)
1107 queueToken(AVT, to);
1108 queueToken(LPAREN, to);
1109 to->enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(),
1110 XQueryTokenizer::QuotAttributeContent)));
1111 queueToken(RPAREN, to);
1114 void XSLTTokenizer::queueSequenceType(const QString &expr)
1116 m_tokenSource.enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(),
1117 XQueryTokenizer::ItemType)));
1120 void XSLTTokenizer::commencingExpression(bool &hasWrittenExpression,
1121 TokenSource::Queue *const to)
1123 if(hasWrittenExpression)
1124 queueToken(COMMA, to);
1126 hasWrittenExpression = true;
1129 void XSLTTokenizer::queueEmptySequence(TokenSource::Queue *const to)
1131 queueToken(LPAREN, to);
1132 queueToken(RPAREN, to);
1135 void XSLTTokenizer::insideChoose(TokenSource::Queue *const to)
1137 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
1138 bool hasHandledOtherwise = false;
1139 bool hasEncounteredAtLeastOneWhen = false;
1145 case QXmlStreamReader::StartElement:
1149 QStack<Token> onExitTokens;
1150 handleStandardAttributes(true);
1153 switch(currentElementName())
1157 if(hasHandledOtherwise)
1159 error(QtXmlPatterns::tr("Element %1 must come last.")
1160 .arg(formatKeyword(QLatin1String("otherwise"))),
1161 ReportContext::XTSE0010);
1165 queueToken(LPAREN, to);
1166 queueExpression(readAttribute(QLatin1String("test")), to);
1167 queueToken(RPAREN, to);
1168 queueToken(THEN, to);
1169 queueToken(LPAREN, to);
1170 pushState(InsideSequenceConstructor);
1171 insideSequenceConstructor(to);
1172 queueToken(RPAREN, to);
1173 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement);
1174 queueToken(ELSE, to);
1175 hasEncounteredAtLeastOneWhen = true;
1176 queueOnExit(onExitTokens, to);
1181 if(!hasEncounteredAtLeastOneWhen)
1183 error(QtXmlPatterns::tr("At least one %1-element must occur before %2.")
1184 .arg(formatKeyword(QLatin1String("when")),
1185 formatKeyword(QLatin1String("otherwise"))),
1186 ReportContext::XTSE0010);
1188 else if(hasHandledOtherwise)
1190 error(QtXmlPatterns::tr("Only one %1-element can appear.")
1191 .arg(formatKeyword(QLatin1String("otherwise"))),
1192 ReportContext::XTSE0010);
1195 pushState(InsideSequenceConstructor);
1196 queueToken(LPAREN, to);
1197 insideSequenceConstructor(to, to);
1198 queueToken(RPAREN, to);
1199 hasHandledOtherwise = true;
1200 queueOnExit(onExitTokens, to);
1204 unexpectedContent();
1208 unexpectedContent();
1211 case QXmlStreamReader::EndElement:
1215 switch(currentElementName())
1219 if(!hasEncounteredAtLeastOneWhen)
1221 error(QtXmlPatterns::tr("At least one %1-element must occur inside %2.")
1222 .arg(formatKeyword(QLatin1String("when")),
1223 formatKeyword(QLatin1String("choose"))),
1224 ReportContext::XTSE0010);
1227 if(!hasHandledOtherwise)
1228 queueEmptySequence(to);
1234 unexpectedContent();
1238 unexpectedContent();
1241 case QXmlStreamReader::Comment:
1243 case QXmlStreamReader::ProcessingInstruction:
1245 case QXmlStreamReader::Characters:
1247 /* We ignore regardless of what xml:space says, see step 4 in
1248 * 4.2 Stripping Whitespace from the Stylesheet. */
1255 unexpectedContent();
1259 checkForParseError();
1262 bool XSLTTokenizer::queueSelectOrSequenceConstructor(const ReportContext::ErrorCode code,
1263 const bool emptynessAllowed,
1264 TokenSource::Queue *const to,
1265 const QXmlStreamAttributes *const attsP,
1266 const bool queueEmptyOnEmpty)
1268 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || attsP);
1269 const NodeName elementName(currentElementName());
1270 const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes);
1272 if(atts.hasAttribute(QLatin1String("select")))
1274 queueExpression(atts.value(QLatin1String("select")).toString(), to);
1276 /* First, verify that we don't have a body. */
1277 if(skipSubTree(true))
1279 error(QtXmlPatterns::tr("When attribute %1 is present on %2, a sequence "
1280 "constructor cannot be used.").arg(formatKeyword(QLatin1String("select")),
1281 formatKeyword(toString(elementName))),
1289 pushState(InsideSequenceConstructor);
1290 if(!insideSequenceConstructor(to, true, queueEmptyOnEmpty) && !emptynessAllowed)
1292 error(QtXmlPatterns::tr("Element %1 must have either a %2-attribute "
1293 "or a sequence constructor.").arg(formatKeyword(toString(elementName)),
1294 formatKeyword(QLatin1String("select"))),
1303 void XSLTTokenizer::queueSimpleContentConstructor(const ReportContext::ErrorCode code,
1304 const bool emptynessAllowed,
1305 TokenSource::Queue *const to,
1306 const bool selectOnlyFirst)
1308 queueToken(INTERNAL_NAME, to);
1309 queueToken(Token(NCNAME, QLatin1String("generic-string-join")), to);
1310 queueToken(LPAREN, to);
1312 /* We have to read the attribute before calling
1313 * queueSelectOrSequenceConstructor(), since it advances the reader. */
1314 const bool hasSeparator = m_currentAttributes.hasAttribute(QLatin1String("separator"));
1315 const QString separatorAVT(m_currentAttributes.value(QLatin1String("separator")).toString());
1317 queueToken(LPAREN, to);
1318 const bool viaSelectAttribute = queueSelectOrSequenceConstructor(code, emptynessAllowed, to);
1319 queueToken(RPAREN, to);
1323 queueToken(LBRACKET, to);
1324 queueToken(Token(NUMBER, QChar::fromLatin1('1')), to);
1325 queueToken(RBRACKET, to);
1328 queueToken(COMMA, to);
1331 queueAVT(separatorAVT, to);
1334 /* The default value depends on whether the value is from @select, or from
1335 * the sequence constructor. */
1336 queueToken(Token(STRING_LITERAL, viaSelectAttribute ? QString(QLatin1Char(' '))
1341 queueToken(RPAREN, to);
1344 void XSLTTokenizer::queueTextConstructor(QString &chars,
1345 bool &hasWrittenExpression,
1346 TokenSource::Queue *const to)
1348 if(!chars.isEmpty())
1350 commencingExpression(hasWrittenExpression, to);
1351 queueToken(TEXT, to);
1352 queueToken(CURLY_LBRACE, to);
1353 queueToken(Token(STRING_LITERAL, chars), to);
1354 queueToken(CURLY_RBRACE, to);
1359 void XSLTTokenizer::queueVariableDeclaration(const VariableType variableType,
1360 TokenSource::Queue *const to)
1362 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
1364 if(variableType == VariableInstruction)
1366 queueToken(LET, to);
1367 queueToken(INTERNAL, to);
1369 else if(variableType == VariableDeclaration || variableType == GlobalParameter)
1371 queueToken(DECLARE, to);
1372 queueToken(VARIABLE, to);
1373 queueToken(INTERNAL, to);
1376 queueToken(DOLLAR, to);
1378 queueExpression(readAttribute(QLatin1String("name")), to, false);
1380 const bool hasAs = m_currentAttributes.hasAttribute(QLatin1String("as"));
1384 queueSequenceType(m_currentAttributes.value(QLatin1String("as")).toString());
1387 if(variableType == FunctionParameter)
1389 skipBodyOfParam(ReportContext::XTSE0760);
1393 /* We must do this here, because queueSelectOrSequenceConstructor()
1394 * advances the reader. */
1395 const bool hasSelect = hasAttribute(QLatin1String("select"));
1396 const bool isRequired = hasAttribute(QLatin1String("required")) ? attributeYesNo(QLatin1String("required")) : false;
1398 TokenSource::Queue storage;
1399 queueSelectOrSequenceConstructor(ReportContext::XTSE0620, true, &storage, 0, false);
1401 /* XSL-T has some wicked rules, see
1402 * 9.3 Values of Variables and Parameters. */
1404 const bool hasQueuedContent = !storage.isEmpty();
1406 /* The syntax for global parameters is:
1408 * declare variable $var external := 'defaultValue';
1410 if(variableType == GlobalParameter)
1411 queueToken(EXTERNAL, to);
1415 if(hasQueuedContent)
1417 error(QtXmlPatterns::tr("When a parameter is required, a default value "
1418 "cannot be supplied through a %1-attribute or "
1419 "a sequence constructor.").arg(formatKeyword(QLatin1String("select"))),
1420 ReportContext::XTSE0010);
1425 if(hasQueuedContent)
1427 queueToken(ASSIGN, to);
1429 if(!hasSelect && !hasAs && !hasQueuedContent)
1430 queueToken(Token(STRING_LITERAL, QString()), to);
1431 else if(hasAs || hasSelect)
1432 queueToken(LPAREN, to);
1435 queueToken(DOCUMENT, to);
1436 queueToken(INTERNAL, to);
1437 queueToken(CURLY_LBRACE, to);
1444 queueToken(ASSIGN, to);
1445 queueToken(Token(STRING_LITERAL, QString()), to);
1447 else if(variableType == VariableDeclaration || variableType == VariableInstruction)
1449 queueToken(ASSIGN, to);
1450 queueEmptySequence(to);
1454 /* storage has tokens if hasSelect or hasQueuedContent is true. */
1455 if(hasSelect | hasQueuedContent)
1458 if(hasQueuedContent)
1460 if(!hasSelect && !hasAs && !hasQueuedContent)
1461 queueToken(Token(STRING_LITERAL, QString()), to);
1462 else if(hasAs || hasSelect)
1463 queueToken(RPAREN, to);
1465 queueToken(CURLY_RBRACE, to);
1469 if(variableType == VariableInstruction)
1470 queueToken(RETURN, to);
1471 else if(variableType == VariableDeclaration || variableType == GlobalParameter)
1472 queueToken(SEMI_COLON, to);
1475 void XSLTTokenizer::startStorageOfCurrent(TokenSource::Queue *const to)
1477 queueToken(CURRENT, to);
1478 queueToken(CURLY_LBRACE, to);
1481 void XSLTTokenizer::endStorageOfCurrent(TokenSource::Queue *const to)
1483 queueToken(CURLY_RBRACE, to);
1486 void XSLTTokenizer::queueNamespaceDeclarations(TokenSource::Queue *const to,
1487 QStack<Token> *const queueOnExit,
1488 const bool isDeclaration)
1490 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
1491 Q_ASSERT_X(isDeclaration || queueOnExit,
1493 "If isDeclaration is false, queueOnExit must be passed.");
1495 const QXmlStreamNamespaceDeclarations nss(namespaceDeclarations());
1497 for(int i = 0; i < nss.count(); ++i)
1499 const QXmlStreamNamespaceDeclaration &at = nss.at(i);
1500 queueToken(DECLARE, to);
1501 queueToken(NAMESPACE, to);
1502 queueToken(Token(NCNAME, at.prefix().toString()), to);
1503 queueToken(G_EQ, to);
1504 queueToken(Token(STRING_LITERAL, at.namespaceUri().toString()), to);
1508 queueToken(INTERNAL, to);
1509 queueToken(SEMI_COLON, to);
1513 queueToken(CURLY_LBRACE, to);
1514 queueOnExit->push(CURLY_RBRACE);
1519 bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to,
1520 const bool initialAdvance,
1521 const bool queueEmptyOnEmpty)
1523 QStack<Token> onExitTokens;
1524 return insideSequenceConstructor(to, onExitTokens, initialAdvance, queueEmptyOnEmpty);
1527 bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to,
1528 QStack<Token> &onExitTokens,
1529 const bool initialAdvance,
1530 const bool queueEmptyOnEmpty)
1532 bool effectiveInitialAdvance = initialAdvance;
1533 bool hasWrittenExpression = false;
1535 /* Buffer which all text nodes, that might be split up by comments,
1536 * processing instructions and CDATA sections, are appended to. */
1541 if(effectiveInitialAdvance)
1544 effectiveInitialAdvance = true;
1548 case QXmlStreamReader::StartElement:
1550 queueTextConstructor(characters, hasWrittenExpression, to);
1551 handleXMLBase(to, &onExitTokens);
1553 pushState(InsideSequenceConstructor);
1555 commencingExpression(hasWrittenExpression, to);
1559 handleXSLTVersion(&m_tokenSource, &onExitTokens, true);
1560 handleStandardAttributes(true);
1563 queueNamespaceDeclarations(to, &onExitTokens);
1565 switch(currentElementName())
1570 queueToken(LPAREN, to);
1572 queueExpression(readAttribute(QLatin1String("test")), to);
1573 queueToken(RPAREN, to);
1574 queueToken(THEN, to);
1576 queueToken(LPAREN, to);
1577 pushState(InsideSequenceConstructor);
1578 insideSequenceConstructor(to);
1589 /* We generate a computed text node constructor. */
1590 queueToken(TEXT, to);
1591 queueToken(CURLY_LBRACE, to);
1593 queueSimpleContentConstructor(ReportContext::XTSE0870, true, to,
1594 !hasAttribute(QLatin1String("separator")) && m_processingMode.top() == BackwardsCompatible);
1595 queueToken(CURLY_RBRACE, to);
1600 queueExpression(readAttribute(QLatin1String("select")), to);
1601 parseFallbacksOnly();
1606 queueToken(TEXT, to);
1607 queueToken(CURLY_LBRACE, to);
1609 queueToken(Token(STRING_LITERAL, readElementText()), to);
1610 queueToken(CURLY_RBRACE, to);
1615 queueVariableDeclaration(VariableInstruction, to);
1617 /* We wrap the children in parantheses since we may
1618 * queue several expressions using the comma operator,
1619 * and in that case the let-binding is only in-scope
1620 * for the first expression. */
1621 queueToken(LPAREN, to);
1623 /* We don't want a comma outputted, we're expecting an
1624 * expression now. */
1625 hasWrittenExpression = false;
1627 onExitTokens.push(RPAREN);
1633 queueToken(CALL_TEMPLATE, to);
1634 queueToken(Token(QNAME, readAttribute(QLatin1String("name"))), to);
1635 queueToken(LPAREN, to);
1636 queueWithParams(CallTemplate, to);
1637 queueToken(RPAREN, to);
1642 queueExpression(readAttribute(QLatin1String("select")), to);
1643 queueToken(MAP, to);
1644 pushState(InsideSequenceConstructor);
1646 TokenSource::Queue sorts;
1647 queueSorting(false, &sorts);
1652 startStorageOfCurrent(to);
1653 insideSequenceConstructor(to, false);
1654 endStorageOfCurrent(to);
1658 queueToken(SORT, to);
1660 queueToken(RETURN, to);
1661 startStorageOfCurrent(to);
1662 insideSequenceConstructor(to, false);
1663 endStorageOfCurrent(to);
1664 queueToken(END_SORT, to);
1669 case XSLTTokenLookup::Comment:
1671 queueToken(COMMENT, to);
1672 queueToken(INTERNAL, to);
1673 queueToken(CURLY_LBRACE, to);
1674 queueSelectOrSequenceConstructor(ReportContext::XTSE0940, true, to);
1675 queueToken(CURLY_RBRACE, to);
1680 queueExpression(readAttribute(QLatin1String("select")), to);
1683 if(readNext() == QXmlStreamReader::EndElement)
1687 error(QtXmlPatterns::tr("Element %1 cannot have children.").arg(formatKeyword(QLatin1String("copy-of"))),
1688 ReportContext::XTSE0010);
1698 case ResultDocument:
1701 pushState(InsideSequenceConstructor);
1702 insideSequenceConstructor(to);
1708 * <xsl:copy>expr</xsl:copy>
1713 * if(self::element()) then
1714 * element internal {node-name()} {$body}
1715 * else if(self::document-node()) then
1716 * document internal {$body}
1717 * else (: This includes comments, processing-instructions,
1718 * attributes, and comments. :)
1721 * TODO node identity is the same as the old node.
1722 * TODO namespace bindings are lost when elements are constructed
1725 /* let $body := expr */
1726 queueToken(LET, to);
1727 queueToken(INTERNAL, to);
1728 queueToken(DOLLAR, to);
1729 queueToken(Token(NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name
1730 queueToken(ASSIGN, to);
1731 queueToken(LPAREN, to);
1732 pushState(InsideSequenceConstructor);
1733 /* Don't queue an empty sequence, we want the dot. */
1734 insideSequenceConstructor(to);
1735 queueToken(RPAREN, to);
1736 queueToken(RETURN, to);
1738 /* if(self::element()) then */
1740 queueToken(LPAREN, to);
1741 queueToken(SELF, to);
1742 queueToken(COLONCOLON, to);
1743 queueToken(ELEMENT, to);
1744 queueToken(LPAREN, to);
1745 queueToken(RPAREN, to);
1746 queueToken(RPAREN, to);
1747 queueToken(THEN, to);
1749 /* element internal {node-name()} {$body} */
1750 queueToken(ELEMENT, to);
1751 queueToken(INTERNAL, to);
1752 queueToken(CURLY_LBRACE, to);
1753 queueToken(Token(NCNAME, QLatin1String("node-name")), to); // TODO what if the default ns changes?
1754 queueToken(LPAREN, to);
1755 queueToken(DOT, to);
1756 queueToken(RPAREN, to);
1757 queueToken(CURLY_RBRACE, to);
1758 queueToken(CURLY_LBRACE, to);
1759 queueToken(DOLLAR, to);
1760 queueToken(Token(NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name
1761 queueToken(CURLY_RBRACE, to);
1763 /* else if(self::document-node()) then */
1764 queueToken(ELSE, to);
1766 queueToken(LPAREN, to);
1767 queueToken(SELF, to);
1768 queueToken(COLONCOLON, to);
1769 queueToken(DOCUMENT_NODE, to);
1770 queueToken(LPAREN, to);
1771 queueToken(RPAREN, to);
1772 queueToken(RPAREN, to);
1773 queueToken(THEN, to);
1775 /* document internal {$body} */
1776 queueToken(DOCUMENT, to);
1777 queueToken(INTERNAL, to);
1778 queueToken(CURLY_LBRACE, to);
1779 queueToken(DOLLAR, to);
1780 queueToken(Token(NCNAME, QString(QLatin1Char('b'))), to); // TODO we need an internal name
1781 queueToken(CURLY_RBRACE, to);
1784 queueToken(ELSE, to);
1785 queueToken(DOT, to);
1789 case XSLTTokenLookup::ProcessingInstruction:
1791 queueToken(PROCESSING_INSTRUCTION, to);
1792 queueToken(CURLY_LBRACE, to);
1793 queueAVT(readAttribute(QLatin1String("name")), to);
1794 queueToken(CURLY_RBRACE, to);
1795 queueToken(CURLY_LBRACE, to);
1796 queueSelectOrSequenceConstructor(ReportContext::XTSE0880, true, to);
1797 queueToken(CURLY_RBRACE, to);
1802 handleValidationAttributes(false);
1805 queueToken(DOCUMENT, to);
1806 queueToken(INTERNAL, to);
1807 queueToken(CURLY_LBRACE, to);
1808 pushState(InsideSequenceConstructor);
1809 insideSequenceConstructor(to);
1810 queueToken(CURLY_RBRACE, to);
1815 handleValidationAttributes(false);
1818 queueToken(ELEMENT, to);
1819 queueToken(INTERNAL, to);
1822 queueToken(CURLY_LBRACE, to);
1823 // TODO only strings allowed, not qname values.
1824 queueAVT(readAttribute(QLatin1String("name")), to);
1825 queueToken(CURLY_RBRACE, to);
1827 /* The sequence constructor. */
1828 queueToken(CURLY_LBRACE, to);
1829 pushState(InsideSequenceConstructor);
1830 insideSequenceConstructor(to);
1831 queueToken(CURLY_RBRACE, to);
1836 handleValidationAttributes(false);
1839 queueToken(ATTRIBUTE, to);
1840 queueToken(INTERNAL, to);
1843 queueToken(CURLY_LBRACE, to);
1844 // TODO only strings allowed, not qname values.
1845 queueAVT(readAttribute(QLatin1String("name")), to);
1846 queueToken(CURLY_RBRACE, to);
1848 /* The sequence constructor. */
1849 queueToken(CURLY_LBRACE, to);
1850 queueSimpleContentConstructor(ReportContext::XTSE0840,
1852 queueToken(CURLY_RBRACE, to);
1857 queueToken(NAMESPACE, to);
1860 queueToken(CURLY_LBRACE, to);
1861 queueAVT(readAttribute(QLatin1String("name")), to);
1862 queueToken(CURLY_RBRACE, to);
1864 /* The sequence constructor. */
1865 queueToken(CURLY_LBRACE, to);
1866 queueSelectOrSequenceConstructor(ReportContext::XTSE0910,
1868 queueToken(CURLY_RBRACE, to);
1874 * <xsl:perform-sort select="$in">
1875 * <xsl:sort select="@key"/>
1876 * </xsl:perform-sort>
1880 * $in map sort order by @key
1885 /* In XQuery, the sort keys appear after the expression
1886 * supplying the initial sequence, while in
1887 * xsl:perform-sort, if a sequence constructor is used,
1888 * they appear in the opposite order. Hence, we need to
1891 /* We store the attributes of xsl:perform-sort, before
1892 * queueSorting() advances the reader. */
1893 const QXmlStreamAttributes atts(m_currentAttributes);
1895 TokenSource::Queue sorts;
1896 queueSorting(true, &sorts);
1897 queueSelectOrSequenceConstructor(ReportContext::XTSE1040,
1901 /* queueSelectOrSequenceConstructor() positions us on EndElement. */
1902 effectiveInitialAdvance = false;
1903 queueToken(MAP, to);
1904 queueToken(SORT, to);
1906 queueToken(RETURN, to);
1907 queueToken(DOT, to);
1908 queueToken(END_SORT, to);
1915 queueEmptySequence(to);
1919 case ApplyTemplates:
1921 if(hasAttribute(QLatin1String("select")))
1922 queueExpression(readAttribute(QLatin1String("select")), to);
1925 queueToken(CHILD, to);
1926 queueToken(COLONCOLON, to);
1927 queueToken(NODE, to);
1928 queueToken(LPAREN, to);
1929 queueToken(RPAREN, to);
1932 bool hasMode = hasAttribute(QLatin1String("mode"));
1936 mode = readAttribute(QLatin1String("mode")).trimmed();
1938 queueToken(FOR_APPLY_TEMPLATE, to);
1940 TokenSource::Queue sorts;
1941 queueSorting(false, &sorts, true);
1943 if(!sorts.isEmpty())
1945 queueToken(SORT, to);
1947 queueToken(RETURN, to);
1950 queueToken(APPLY_TEMPLATE, to);
1954 queueToken(MODE, to);
1955 queueToken(Token(mode.startsWith(QLatin1Char('#')) ? NCNAME : QNAME, mode), to);
1958 queueToken(LPAREN, to);
1959 queueWithParams(ApplyTemplates, to, false);
1960 queueToken(RPAREN, to);
1962 if(!sorts.isEmpty())
1963 queueToken(END_SORT, to);
1968 unexpectedContent();
1974 handleXSLTVersion(&m_tokenSource, &onExitTokens, true);
1975 handleStandardAttributes(false);
1976 handleValidationAttributes(false);
1978 /* We're generating an element constructor. */
1979 queueNamespaceDeclarations(to, &onExitTokens); // TODO same in the isXSLT() branch
1980 queueToken(ELEMENT, to);
1981 queueToken(INTERNAL, to);
1982 queueToken(Token(QNAME, qualifiedName().toString()), to);
1983 queueToken(CURLY_LBRACE, to);
1984 const int len = m_currentAttributes.count();
1986 for(int i = 0; i < len; ++i)
1988 const QXmlStreamAttribute &at = m_currentAttributes.at(i);
1990 /* We don't want to generate constructors for XSL-T attributes. */
1991 if(at.namespaceUri() == CommonNamespaces::XSLT)
1994 queueToken(ATTRIBUTE, to);
1995 queueToken(INTERNAL, to);
1997 queueToken(Token(at.prefix().isEmpty() ? NCNAME : QNAME, at.qualifiedName().toString()), to);
1998 queueToken(CURLY_LBRACE, to);
1999 queueAVT(at.value().toString(), to);
2000 queueToken(CURLY_RBRACE, to);
2001 queueToken(COMMA, to);
2004 pushState(InsideSequenceConstructor);
2005 insideSequenceConstructor(to);
2006 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement || hasError());
2010 unexpectedContent();
2013 case QXmlStreamReader::EndElement:
2015 queueTextConstructor(characters, hasWrittenExpression, to);
2018 if(!hasWrittenExpression && queueEmptyOnEmpty)
2019 queueEmptySequence(to);
2021 queueOnExit(onExitTokens, to);
2025 Q_ASSERT(!isElement(Sequence));
2027 switch(currentElementName())
2029 /* Fallthrough all these. */
2036 case ResultDocument:
2042 hasWrittenExpression = true;
2047 queueToken(RPAREN, to);
2048 queueToken(ELSE, to);
2049 queueEmptySequence(to);
2054 queueToken(CURLY_RBRACE, to);
2055 queueToken(SEMI_COLON, to);
2060 endStorageOfCurrent(&m_tokenSource);
2061 /* TODO, fallthrough to Function. */
2062 queueToken(CURLY_RBRACE, to);
2063 queueToken(SEMI_COLON, to);
2072 /* We're closing a direct element constructor. */
2073 hasWrittenExpression = true;
2074 queueToken(CURLY_RBRACE, to);
2077 return hasWrittenExpression;
2079 case QXmlStreamReader::ProcessingInstruction:
2081 case QXmlStreamReader::Comment:
2082 /* We do nothing, we just ignore them. */
2084 case QXmlStreamReader::Characters:
2086 if(whitespaceToSkip())
2090 characters += text().toString();
2100 return hasWrittenExpression;
2103 bool XSLTTokenizer::isStylesheetElement() const
2106 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement ||
2107 tokenType() == QXmlStreamReader::EndElement);
2109 const NodeName name = currentElementName();
2110 return name == Stylesheet || name == Transform;
2113 void XSLTTokenizer::skipBodyOfParam(const ReportContext::ErrorCode code)
2116 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
2117 const NodeName name(currentElementName());
2121 error(QtXmlPatterns::tr("Element %1 cannot have a sequence constructor.")
2122 .arg(formatKeyword(toString(name))),
2127 void XSLTTokenizer::queueWithParams(const XSLTTokenLookup::NodeName parentName,
2128 TokenSource::Queue *const to,
2129 const bool initialAdvance)
2131 Q_ASSERT(parentName == ApplyTemplates || parentName == CallTemplate);
2133 bool effectiveInitialAdvance = initialAdvance;
2134 bool hasQueuedParam = false;
2138 if(effectiveInitialAdvance)
2141 effectiveInitialAdvance = true;
2145 case QXmlStreamReader::StartElement:
2148 queueToken(COMMA, to);
2150 if(isXSLT() && isElement(WithParam))
2152 if(hasAttribute(QLatin1String("tunnel")) && attributeYesNo(QLatin1String("tunnel")))
2153 queueToken(TUNNEL, to);
2155 queueVariableDeclaration(WithParamVariable, to);
2156 hasQueuedParam = true;
2160 unexpectedContent();
2162 case QXmlStreamReader::EndElement:
2164 if(isElement(parentName))
2169 case QXmlStreamReader::ProcessingInstruction:
2171 case QXmlStreamReader::Comment:
2173 case QXmlStreamReader::Characters:
2174 if(whitespaceToSkip())
2179 unexpectedContent();
2182 unexpectedContent();
2185 void XSLTTokenizer::queueParams(const XSLTTokenLookup::NodeName parentName,
2186 TokenSource::Queue *const to)
2188 bool hasQueuedParam = false;
2190 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
2196 case QXmlStreamReader::StartElement:
2198 if(isXSLT() && isElement(Param))
2201 queueToken(COMMA, to);
2205 if(parentName == Function && m_currentAttributes.hasAttribute(QLatin1String("select")))
2207 error(QtXmlPatterns::tr("The attribute %1 cannot appear on %2, when it is a child of %3.")
2208 .arg(formatKeyword(QLatin1String("select")),
2209 formatKeyword(QLatin1String("param")),
2210 formatKeyword(QLatin1String("function"))),
2211 ReportContext::XTSE0760);
2214 if(parentName == Function && m_currentAttributes.hasAttribute(QLatin1String("required")))
2216 error(QtXmlPatterns::tr("The attribute %1 cannot appear on %2, when it is a child of %3.")
2217 .arg(formatKeyword(QLatin1String("required")),
2218 formatKeyword(QLatin1String("param")),
2219 formatKeyword(QLatin1String("function"))),
2220 ReportContext::XTSE0010);
2223 const bool hasTunnel = m_currentAttributes.hasAttribute(QLatin1String("tunnel"));
2224 const bool isTunnel = hasTunnel ? attributeYesNo(QLatin1String("tunnel")) : false;
2228 if(parentName == Function)
2230 /* See W3C public report 5650: http://www.w3.org/Bugs/Public/show_bug.cgi?id=5650 */
2231 error(QtXmlPatterns::tr("A parameter in a function cannot be declared to be a tunnel."),
2232 ReportContext::XTSE0010);
2235 queueToken(TUNNEL, to);
2238 hasQueuedParam = true;
2239 queueVariableDeclaration(parentName == Function ? FunctionParameter : TemplateParameter, to);
2245 case QXmlStreamReader::Characters:
2247 if(whitespaceToSkip())
2251 case QXmlStreamReader::EndElement:
2259 bool XSLTTokenizer::skipSubTree(const bool exitOnContent)
2261 bool hasContent = false;
2268 case QXmlStreamReader::Characters:
2270 if(whitespaceToSkip())
2281 case QXmlStreamReader::StartElement:
2290 case QXmlStreamReader::EndElement:
2303 checkForParseError();
2307 void XSLTTokenizer::parseFallbacksOnly()
2310 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
2313 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement);
2316 void XSLTTokenizer::insideAttributeSet()
2322 case QXmlStreamReader::StartElement:
2324 if(isXSLT() && isElement(AttributeSet))
2330 unexpectedContent();
2332 case QXmlStreamReader::EndElement:
2334 case QXmlStreamReader::ProcessingInstruction:
2336 case QXmlStreamReader::Comment:
2338 case QXmlStreamReader::Characters:
2339 if(whitespaceToSkip())
2343 unexpectedContent();
2346 unexpectedContent();
2349 void XSLTTokenizer::insideStylesheetModule()
2355 case QXmlStreamReader::StartElement:
2359 handleStandardAttributes(true);
2360 handleXSLTVersion(0, 0, true, 0, false);
2363 /* Handle the various declarations. */
2364 switch(currentElementName())
2373 queueVariableDeclaration(VariableDeclaration, &m_tokenSource);
2376 queueVariableDeclaration(GlobalParameter, &m_tokenSource);
2380 error(QtXmlPatterns::tr("This processor is not Schema-aware and "
2381 "therefore %1 cannot be used.").arg(formatKeyword(toString(ImportSchema))),
2382 ReportContext::XTSE1660);
2400 unexpectedContent();
2406 if(skipSubTree(true))
2407 unexpectedContent();
2413 if(skipSubTree(true))
2414 unexpectedContent();
2424 insideAttributeSet();
2427 if(m_processingMode.top() != ForwardCompatible)
2428 unexpectedContent();
2433 /* We have a user-defined data element. See section 3.6.2. */
2435 if(namespaceUri().isEmpty())
2437 error(QtXmlPatterns::tr("Top level stylesheet elements must be "
2438 "in a non-null namespace, which %1 isn't.").arg(formatKeyword(name())),
2439 ReportContext::XTSE0130);
2446 case QXmlStreamReader::Characters:
2448 /* Regardless of xml:space, we skip whitespace, see step 4 in
2449 * 4.2 Stripping Whitespace from the Stylesheet. */
2453 unexpectedContent(ReportContext::XTSE0120);
2456 case QXmlStreamReader::EndElement:
2467 checkForParseError();
2470 bool XSLTTokenizer::readToggleAttribute(const QString &localName,
2471 const QString &isTrue,
2472 const QString &isFalse,
2473 const QXmlStreamAttributes *const attsP) const
2475 const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes);
2476 Q_ASSERT(atts.hasAttribute(localName));
2477 const QString value(atts.value(localName).toString());
2481 else if(value == isFalse)
2485 error(QtXmlPatterns::tr("The value for attribute %1 on element %2 must either "
2486 "be %3 or %4, not %5.").arg(formatKeyword(localName),
2487 formatKeyword(name()),
2489 formatData(isFalse),
2491 ReportContext::XTSE0020);
2492 /* Silences a compiler warning. */
2497 int XSLTTokenizer::readAlternativeAttribute(const QHash<QString, int> &alternatives,
2498 const QXmlStreamAttribute &attr) const
2500 const QString value(attr.value().toString().trimmed());
2502 if(alternatives.contains(value))
2503 return alternatives[value];
2505 error(QtXmlPatterns::tr("Attribute %1 cannot have the value %2.")
2506 .arg(formatKeyword(attr.name().toString()),
2507 formatData(attr.value().toString())),
2508 ReportContext::XTSE0020);
2509 return 0; /* Silence compiler warning. */
2512 bool XSLTTokenizer::attributeYesNo(const QString &localName) const
2514 return readToggleAttribute(localName, QLatin1String("yes"), QLatin1String("no"));
2517 void XSLTTokenizer::queueSorting(const bool oneSortRequired,
2518 TokenSource::Queue *const to,
2519 const bool speciallyTreatWhitespace)
2521 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
2523 const NodeName elementName(currentElementName());
2524 bool hasQueuedOneSort = false;
2530 case QXmlStreamReader::EndElement:
2532 /* Let's say we have no sequence constructor, but only
2533 * ignorable space. In that case we will actually loop
2534 * infinitely if we don't have this check. */
2537 switch(currentElementName())
2543 case ApplyTemplates:
2551 case QXmlStreamReader::StartElement:
2553 if(isXSLT() && isElement(Sort))
2555 if(hasQueuedOneSort)
2556 queueToken(COMMA, to);
2558 /* sorts are by default stable. */
2559 if(hasAttribute(QLatin1String("stable")))
2561 if(hasQueuedOneSort)
2563 error(QtXmlPatterns::tr("The attribute %1 can only appear on "
2564 "the first %2 element.").arg(formatKeyword(QLatin1String("stable")),
2565 formatKeyword(QLatin1String("sort"))),
2566 ReportContext::XTSE0020);
2569 if(attributeYesNo(QLatin1String("stable")))
2570 queueToken(STABLE, to);
2573 if(!hasQueuedOneSort)
2575 queueToken(ORDER, to);
2579 /* We store a copy such that we can use them after
2580 * queueSelectOrSequenceConstructor() advances the reader. */
2581 const QXmlStreamAttributes atts(m_currentAttributes);
2583 const int before = to->count();
2585 // TODO This doesn't work as is. @data-type can be an AVT.
2586 if(atts.hasAttribute(QLatin1String("data-type")))
2588 if(readToggleAttribute(QLatin1String("data-type"),
2589 QLatin1String("text"),
2590 QLatin1String("number"),
2592 queueToken(Token(NCNAME, QLatin1String("string")), to);
2594 queueToken(Token(NCNAME, QLatin1String("number")), to);
2596 /* We queue these parantheses for the sake of the function
2597 * call for attribute data-type. In the case we don't have
2598 * such an attribute, the parantheses are just redundant. */
2599 queueToken(LPAREN, to);
2600 queueSelectOrSequenceConstructor(ReportContext::XTSE1015,
2605 /* If neither a select attribute or a sequence constructor is supplied,
2606 * we're supposed to use the context item. */
2607 queueToken(RPAREN, to);
2608 if(before == to->count())
2609 queueToken(DOT, to);
2614 // TODO This doesn't work as is. @order can be an AVT, and so can case-order and lang.
2615 if(atts.hasAttribute(QLatin1String("order")) && readToggleAttribute(QLatin1String("order"),
2616 QLatin1String("descending"),
2617 QLatin1String("ascending"),
2620 queueToken(DESCENDING, to);
2624 /* This is the default. */
2625 queueToken(ASCENDING, to);
2628 if(atts.hasAttribute(QLatin1String("collation")))
2630 queueToken(INTERNAL, to);
2631 queueToken(COLLATION, to);
2632 queueAVT(atts.value(QLatin1String("collation")).toString(), to);
2635 hasQueuedOneSort = true;
2641 case QXmlStreamReader::Characters:
2643 if(speciallyTreatWhitespace && isWhitespace())
2646 if(QXmlStreamReader::Characters && whitespaceToSkip())
2649 /* We have an instruction which is a text node, we're done. */
2652 case QXmlStreamReader::ProcessingInstruction:
2654 case QXmlStreamReader::Comment:
2657 unexpectedContent();
2660 if(oneSortRequired && !hasQueuedOneSort)
2662 error(QtXmlPatterns::tr("At least one %1 element must appear as child of %2.")
2663 .arg(formatKeyword(QLatin1String("sort")), formatKeyword(toString(elementName))),
2664 ReportContext::XTSE0010);
2669 checkForParseError();
2672 void XSLTTokenizer::insideFunction()
2674 queueToken(DECLARE, &m_tokenSource);
2675 queueToken(FUNCTION, &m_tokenSource);
2676 queueToken(INTERNAL, &m_tokenSource);
2677 queueToken(Token(QNAME, readAttribute(QLatin1String("name"))), &m_tokenSource);
2678 queueToken(LPAREN, &m_tokenSource);
2679 const QString expectedType(hasAttribute(QLatin1String("as")) ? readAttribute(QLatin1String("as")): QString());
2681 if(hasAttribute(QLatin1String("override")))
2683 /* We currently have no external functions, so we don't pass it on currently. */
2684 attributeYesNo(QLatin1String("override"));
2687 queueParams(Function, &m_tokenSource);
2689 queueToken(RPAREN, &m_tokenSource);
2691 if(!expectedType.isNull())
2693 queueToken(AS, &m_tokenSource);
2694 queueSequenceType(expectedType);
2697 QStack<Token> onExitTokens;
2698 handleXMLBase(&m_tokenSource, &onExitTokens, true, &m_currentAttributes);
2699 handleXSLTVersion(&m_tokenSource, &onExitTokens, true);
2700 queueToken(CURLY_LBRACE, &m_tokenSource);
2702 pushState(InsideSequenceConstructor);
2703 insideSequenceConstructor(&m_tokenSource, onExitTokens, false);
2704 /* We don't queue CURLY_RBRACE, because it's done in
2705 * insideSequenceConstructor(). */
2708 YYLTYPE XSLTTokenizer::currentSourceLocator() const
2711 retval.first_line = lineNumber();
2712 retval.first_column = columnNumber();