|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
52 QT_BEGIN_NAMESPACE |
|
53 |
|
54 using namespace QPatternist; |
|
55 |
|
56 Tokenizer::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 |
|
68 XSLTTokenizer::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 |
|
84 bool XSLTTokenizer::isAnyAttributeAllowed() const |
|
85 { |
|
86 return m_processingMode.top() == ForwardCompatible; |
|
87 } |
|
88 |
|
89 void XSLTTokenizer::setParserContext(const ParserContext::Ptr &parseInfo) |
|
90 { |
|
91 m_parseInfo = parseInfo; |
|
92 } |
|
93 |
|
94 void XSLTTokenizer::validateElement() const |
|
95 { |
|
96 MaintainingReader<XSLTTokenLookup>::validateElement(currentElementName()); |
|
97 } |
|
98 |
|
99 QSet<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 |
|
121 ElementDescription<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 |
|
477 QHash<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 |
|
489 bool XSLTTokenizer::whitespaceToSkip() const |
|
490 { |
|
491 return m_stripWhitespace.top() && isWhitespace(); |
|
492 } |
|
493 |
|
494 void 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 |
|
544 void XSLTTokenizer::checkForParseError() const |
|
545 { |
|
546 if(hasError()) |
|
547 { |
|
548 error(QtXmlPatterns::tr("Parse error: %1").arg(escape(errorString())), ReportContext::XTSE0010); |
|
549 } |
|
550 } |
|
551 |
|
552 QString 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 |
|
580 int XSLTTokenizer::commenceScanOnly() |
|
581 { |
|
582 /* Do nothing, return a dummy value. */ |
|
583 return 0; |
|
584 } |
|
585 |
|
586 void XSLTTokenizer::resumeTokenizationFrom(const int position) |
|
587 { |
|
588 /* Do nothing. */ |
|
589 Q_UNUSED(position); |
|
590 } |
|
591 |
|
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) |
|
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 |
|
649 void 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 |
|
681 void 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 |
|
747 void 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 |
|
787 Tokenizer::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 |
|
831 bool 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 |
|
840 inline 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 |
|
850 void XSLTTokenizer::queueOnExit(QStack<Token> &source, |
|
851 TokenSource::Queue *const destination) |
|
852 { |
|
853 while(!source.isEmpty()) |
|
854 queueToken(source.pop(), destination); |
|
855 } |
|
856 |
|
857 void 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 |
|
969 void 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 |
|
977 void XSLTTokenizer::pushState(const State nextState) |
|
978 { |
|
979 m_state.push(nextState); |
|
980 } |
|
981 |
|
982 void XSLTTokenizer::leaveState() |
|
983 { |
|
984 m_state.pop(); |
|
985 } |
|
986 |
|
987 void 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); |
|
1016 queueToken(TEMPLATE, &m_tokenSource); |
|
1017 |
|
1018 if(hasName) |
|
1019 { |
|
1020 queueToken(NAME, &m_tokenSource); |
|
1021 queueToken(Token(QNAME, readAttribute(QLatin1String("name"))), &m_tokenSource); |
|
1022 } |
|
1023 |
|
1024 if(hasMatch) |
|
1025 { |
|
1026 queueToken(MATCHES, &m_tokenSource); |
|
1027 queueExpression(readAttribute(QLatin1String("match")), &m_tokenSource); |
|
1028 } |
|
1029 |
|
1030 if(hasMode) |
|
1031 { |
|
1032 const QString modeString(readAttribute(QLatin1String("mode")).simplified()); |
|
1033 |
|
1034 if(modeString.isEmpty()) |
|
1035 { |
|
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); |
|
1040 } |
|
1041 |
|
1042 queueToken(MODE, &m_tokenSource); |
|
1043 |
|
1044 const QStringList modeList(modeString.split(QLatin1Char(' '))); |
|
1045 |
|
1046 for(int i = 0; i < modeList.count(); ++i) |
|
1047 { |
|
1048 const QString &mode = modeList.at(i); |
|
1049 |
|
1050 queueToken(Token(mode.contains(QLatin1Char(':')) ? QNAME : NCNAME, mode), &m_tokenSource); |
|
1051 |
|
1052 if(i < modeList.count() - 1) |
|
1053 queueToken(COMMA, &m_tokenSource); |
|
1054 } |
|
1055 } |
|
1056 |
|
1057 if(hasPriority) |
|
1058 { |
|
1059 queueToken(PRIORITY, &m_tokenSource); |
|
1060 queueToken(Token(STRING_LITERAL, readAttribute(QLatin1String("priority"))), &m_tokenSource); |
|
1061 } |
|
1062 |
|
1063 QStack<Token> onExitTokens; |
|
1064 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
1065 |
|
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); |
|
1072 |
|
1073 if(hasAs) |
|
1074 { |
|
1075 queueToken(AS, &m_tokenSource); |
|
1076 queueSequenceType(atts.value(QLatin1String("as")).toString()); |
|
1077 } |
|
1078 |
|
1079 queueToken(CURLY_LBRACE, &m_tokenSource); |
|
1080 |
|
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); |
|
1087 } |
|
1088 |
|
1089 void XSLTTokenizer::queueExpression(const QString &expr, |
|
1090 TokenSource::Queue *const to, |
|
1091 const bool wrapWithParantheses) |
|
1092 { |
|
1093 TokenSource::Queue *const effectiveTo = to ? to : &m_tokenSource; |
|
1094 |
|
1095 if(wrapWithParantheses) |
|
1096 queueToken(LPAREN, effectiveTo); |
|
1097 |
|
1098 effectiveTo->enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI()))); |
|
1099 |
|
1100 if(wrapWithParantheses) |
|
1101 queueToken(RPAREN, effectiveTo); |
|
1102 } |
|
1103 |
|
1104 void XSLTTokenizer::queueAVT(const QString &expr, |
|
1105 TokenSource::Queue *const to) |
|
1106 { |
|
1107 queueToken(AVT, to); |
|
1108 queueToken(LPAREN, to); |
|
1109 to->enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(), |
|
1110 XQueryTokenizer::QuotAttributeContent))); |
|
1111 queueToken(RPAREN, to); |
|
1112 } |
|
1113 |
|
1114 void XSLTTokenizer::queueSequenceType(const QString &expr) |
|
1115 { |
|
1116 m_tokenSource.enqueue(TokenSource::Ptr(new XQueryTokenizer(expr, queryURI(), |
|
1117 XQueryTokenizer::ItemType))); |
|
1118 } |
|
1119 |
|
1120 void XSLTTokenizer::commencingExpression(bool &hasWrittenExpression, |
|
1121 TokenSource::Queue *const to) |
|
1122 { |
|
1123 if(hasWrittenExpression) |
|
1124 queueToken(COMMA, to); |
|
1125 else |
|
1126 hasWrittenExpression = true; |
|
1127 } |
|
1128 |
|
1129 void XSLTTokenizer::queueEmptySequence(TokenSource::Queue *const to) |
|
1130 { |
|
1131 queueToken(LPAREN, to); |
|
1132 queueToken(RPAREN, to); |
|
1133 } |
|
1134 |
|
1135 void XSLTTokenizer::insideChoose(TokenSource::Queue *const to) |
|
1136 { |
|
1137 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
1138 bool hasHandledOtherwise = false; |
|
1139 bool hasEncounteredAtLeastOneWhen = false; |
|
1140 |
|
1141 while(!atEnd()) |
|
1142 { |
|
1143 switch(readNext()) |
|
1144 { |
|
1145 case QXmlStreamReader::StartElement: |
|
1146 { |
|
1147 if(isXSLT()) |
|
1148 { |
|
1149 QStack<Token> onExitTokens; |
|
1150 handleStandardAttributes(true); |
|
1151 validateElement(); |
|
1152 |
|
1153 switch(currentElementName()) |
|
1154 { |
|
1155 case When: |
|
1156 { |
|
1157 if(hasHandledOtherwise) |
|
1158 { |
|
1159 error(QtXmlPatterns::tr("Element %1 must come last.") |
|
1160 .arg(formatKeyword(QLatin1String("otherwise"))), |
|
1161 ReportContext::XTSE0010); |
|
1162 } |
|
1163 |
|
1164 queueToken(IF, to); |
|
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); |
|
1177 break; |
|
1178 } |
|
1179 case Otherwise: |
|
1180 { |
|
1181 if(!hasEncounteredAtLeastOneWhen) |
|
1182 { |
|
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); |
|
1187 } |
|
1188 else if(hasHandledOtherwise) |
|
1189 { |
|
1190 error(QtXmlPatterns::tr("Only one %1-element can appear.") |
|
1191 .arg(formatKeyword(QLatin1String("otherwise"))), |
|
1192 ReportContext::XTSE0010); |
|
1193 } |
|
1194 |
|
1195 pushState(InsideSequenceConstructor); |
|
1196 queueToken(LPAREN, to); |
|
1197 insideSequenceConstructor(to, to); |
|
1198 queueToken(RPAREN, to); |
|
1199 hasHandledOtherwise = true; |
|
1200 queueOnExit(onExitTokens, to); |
|
1201 break; |
|
1202 } |
|
1203 default: |
|
1204 unexpectedContent(); |
|
1205 } |
|
1206 } |
|
1207 else |
|
1208 unexpectedContent(); |
|
1209 break; |
|
1210 } |
|
1211 case QXmlStreamReader::EndElement: |
|
1212 { |
|
1213 if(isXSLT()) |
|
1214 { |
|
1215 switch(currentElementName()) |
|
1216 { |
|
1217 case Choose: |
|
1218 { |
|
1219 if(!hasEncounteredAtLeastOneWhen) |
|
1220 { |
|
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); |
|
1225 } |
|
1226 |
|
1227 if(!hasHandledOtherwise) |
|
1228 queueEmptySequence(to); |
|
1229 return; |
|
1230 } |
|
1231 case Otherwise: |
|
1232 continue; |
|
1233 default: |
|
1234 unexpectedContent(); |
|
1235 } |
|
1236 } |
|
1237 else |
|
1238 unexpectedContent(); |
|
1239 break; |
|
1240 } |
|
1241 case QXmlStreamReader::Comment: |
|
1242 /* Fallthrough. */ |
|
1243 case QXmlStreamReader::ProcessingInstruction: |
|
1244 continue; |
|
1245 case QXmlStreamReader::Characters: |
|
1246 { |
|
1247 /* We ignore regardless of what xml:space says, see step 4 in |
|
1248 * 4.2 Stripping Whitespace from the Stylesheet. */ |
|
1249 if(isWhitespace()) |
|
1250 continue; |
|
1251 /* Fallthrough. */ |
|
1252 } |
|
1253 default: |
|
1254 /* Fallthrough. */ |
|
1255 unexpectedContent(); |
|
1256 break; |
|
1257 } |
|
1258 } |
|
1259 checkForParseError(); |
|
1260 } |
|
1261 |
|
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) |
|
1267 { |
|
1268 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || attsP); |
|
1269 const NodeName elementName(currentElementName()); |
|
1270 const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes); |
|
1271 |
|
1272 if(atts.hasAttribute(QLatin1String("select"))) |
|
1273 { |
|
1274 queueExpression(atts.value(QLatin1String("select")).toString(), to); |
|
1275 |
|
1276 /* First, verify that we don't have a body. */ |
|
1277 if(skipSubTree(true)) |
|
1278 { |
|
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))), |
|
1282 code); |
|
1283 } |
|
1284 |
|
1285 return true; |
|
1286 } |
|
1287 else |
|
1288 { |
|
1289 pushState(InsideSequenceConstructor); |
|
1290 if(!insideSequenceConstructor(to, true, queueEmptyOnEmpty) && !emptynessAllowed) |
|
1291 { |
|
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"))), |
|
1295 code); |
|
1296 |
|
1297 } |
|
1298 |
|
1299 return false; |
|
1300 } |
|
1301 } |
|
1302 |
|
1303 void XSLTTokenizer::queueSimpleContentConstructor(const ReportContext::ErrorCode code, |
|
1304 const bool emptynessAllowed, |
|
1305 TokenSource::Queue *const to, |
|
1306 const bool selectOnlyFirst) |
|
1307 { |
|
1308 queueToken(INTERNAL_NAME, to); |
|
1309 queueToken(Token(NCNAME, QLatin1String("generic-string-join")), to); |
|
1310 queueToken(LPAREN, to); |
|
1311 |
|
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()); |
|
1316 |
|
1317 queueToken(LPAREN, to); |
|
1318 const bool viaSelectAttribute = queueSelectOrSequenceConstructor(code, emptynessAllowed, to); |
|
1319 queueToken(RPAREN, to); |
|
1320 |
|
1321 if(selectOnlyFirst) |
|
1322 { |
|
1323 queueToken(LBRACKET, to); |
|
1324 queueToken(Token(NUMBER, QChar::fromLatin1('1')), to); |
|
1325 queueToken(RBRACKET, to); |
|
1326 } |
|
1327 |
|
1328 queueToken(COMMA, to); |
|
1329 |
|
1330 if(hasSeparator) |
|
1331 queueAVT(separatorAVT, to); |
|
1332 else |
|
1333 { |
|
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(' ')) |
|
1337 : QString()), |
|
1338 to); |
|
1339 } |
|
1340 |
|
1341 queueToken(RPAREN, to); |
|
1342 } |
|
1343 |
|
1344 void XSLTTokenizer::queueTextConstructor(QString &chars, |
|
1345 bool &hasWrittenExpression, |
|
1346 TokenSource::Queue *const to) |
|
1347 { |
|
1348 if(!chars.isEmpty()) |
|
1349 { |
|
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); |
|
1355 chars.clear(); |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 void XSLTTokenizer::queueVariableDeclaration(const VariableType variableType, |
|
1360 TokenSource::Queue *const to) |
|
1361 { |
|
1362 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
1363 |
|
1364 if(variableType == VariableInstruction) |
|
1365 { |
|
1366 queueToken(LET, to); |
|
1367 queueToken(INTERNAL, to); |
|
1368 } |
|
1369 else if(variableType == VariableDeclaration || variableType == GlobalParameter) |
|
1370 { |
|
1371 queueToken(DECLARE, to); |
|
1372 queueToken(VARIABLE, to); |
|
1373 queueToken(INTERNAL, to); |
|
1374 } |
|
1375 |
|
1376 queueToken(DOLLAR, to); |
|
1377 |
|
1378 queueExpression(readAttribute(QLatin1String("name")), to, false); |
|
1379 |
|
1380 const bool hasAs = m_currentAttributes.hasAttribute(QLatin1String("as")); |
|
1381 if(hasAs) |
|
1382 { |
|
1383 queueToken(AS, to); |
|
1384 queueSequenceType(m_currentAttributes.value(QLatin1String("as")).toString()); |
|
1385 } |
|
1386 |
|
1387 if(variableType == FunctionParameter) |
|
1388 { |
|
1389 skipBodyOfParam(ReportContext::XTSE0760); |
|
1390 return; |
|
1391 } |
|
1392 |
|
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; |
|
1397 |
|
1398 TokenSource::Queue storage; |
|
1399 queueSelectOrSequenceConstructor(ReportContext::XTSE0620, true, &storage, 0, false); |
|
1400 |
|
1401 /* XSL-T has some wicked rules, see |
|
1402 * 9.3 Values of Variables and Parameters. */ |
|
1403 |
|
1404 const bool hasQueuedContent = !storage.isEmpty(); |
|
1405 |
|
1406 /* The syntax for global parameters is: |
|
1407 * |
|
1408 * declare variable $var external := 'defaultValue'; |
|
1409 */ |
|
1410 if(variableType == GlobalParameter) |
|
1411 queueToken(EXTERNAL, to); |
|
1412 |
|
1413 if(isRequired) |
|
1414 { |
|
1415 if(hasQueuedContent) |
|
1416 { |
|
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); |
|
1421 } |
|
1422 } |
|
1423 else |
|
1424 { |
|
1425 if(hasQueuedContent) |
|
1426 { |
|
1427 queueToken(ASSIGN, to); |
|
1428 |
|
1429 if(!hasSelect && !hasAs && !hasQueuedContent) |
|
1430 queueToken(Token(STRING_LITERAL, QString()), to); |
|
1431 else if(hasAs || hasSelect) |
|
1432 queueToken(LPAREN, to); |
|
1433 else |
|
1434 { |
|
1435 queueToken(DOCUMENT, to); |
|
1436 queueToken(INTERNAL, to); |
|
1437 queueToken(CURLY_LBRACE, to); |
|
1438 } |
|
1439 } |
|
1440 else |
|
1441 { |
|
1442 if(!hasAs) |
|
1443 { |
|
1444 queueToken(ASSIGN, to); |
|
1445 queueToken(Token(STRING_LITERAL, QString()), to); |
|
1446 } |
|
1447 else if(variableType == VariableDeclaration || variableType == VariableInstruction) |
|
1448 { |
|
1449 queueToken(ASSIGN, to); |
|
1450 queueEmptySequence(to); |
|
1451 } |
|
1452 } |
|
1453 |
|
1454 /* storage has tokens if hasSelect or hasQueuedContent is true. */ |
|
1455 if(hasSelect | hasQueuedContent) |
|
1456 *to += storage; |
|
1457 |
|
1458 if(hasQueuedContent) |
|
1459 { |
|
1460 if(!hasSelect && !hasAs && !hasQueuedContent) |
|
1461 queueToken(Token(STRING_LITERAL, QString()), to); |
|
1462 else if(hasAs || hasSelect) |
|
1463 queueToken(RPAREN, to); |
|
1464 else |
|
1465 queueToken(CURLY_RBRACE, to); |
|
1466 } |
|
1467 } |
|
1468 |
|
1469 if(variableType == VariableInstruction) |
|
1470 queueToken(RETURN, to); |
|
1471 else if(variableType == VariableDeclaration || variableType == GlobalParameter) |
|
1472 queueToken(SEMI_COLON, to); |
|
1473 } |
|
1474 |
|
1475 void XSLTTokenizer::startStorageOfCurrent(TokenSource::Queue *const to) |
|
1476 { |
|
1477 queueToken(CURRENT, to); |
|
1478 queueToken(CURLY_LBRACE, to); |
|
1479 } |
|
1480 |
|
1481 void XSLTTokenizer::endStorageOfCurrent(TokenSource::Queue *const to) |
|
1482 { |
|
1483 queueToken(CURLY_RBRACE, to); |
|
1484 } |
|
1485 |
|
1486 void XSLTTokenizer::queueNamespaceDeclarations(TokenSource::Queue *const to, |
|
1487 QStack<Token> *const queueOnExit, |
|
1488 const bool isDeclaration) |
|
1489 { |
|
1490 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
1491 Q_ASSERT_X(isDeclaration || queueOnExit, |
|
1492 Q_FUNC_INFO, |
|
1493 "If isDeclaration is false, queueOnExit must be passed."); |
|
1494 |
|
1495 const QXmlStreamNamespaceDeclarations nss(namespaceDeclarations()); |
|
1496 |
|
1497 for(int i = 0; i < nss.count(); ++i) |
|
1498 { |
|
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); |
|
1505 |
|
1506 if(isDeclaration) |
|
1507 { |
|
1508 queueToken(INTERNAL, to); |
|
1509 queueToken(SEMI_COLON, to); |
|
1510 } |
|
1511 else |
|
1512 { |
|
1513 queueToken(CURLY_LBRACE, to); |
|
1514 queueOnExit->push(CURLY_RBRACE); |
|
1515 } |
|
1516 } |
|
1517 } |
|
1518 |
|
1519 bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to, |
|
1520 const bool initialAdvance, |
|
1521 const bool queueEmptyOnEmpty) |
|
1522 { |
|
1523 QStack<Token> onExitTokens; |
|
1524 return insideSequenceConstructor(to, onExitTokens, initialAdvance, queueEmptyOnEmpty); |
|
1525 } |
|
1526 |
|
1527 bool XSLTTokenizer::insideSequenceConstructor(TokenSource::Queue *const to, |
|
1528 QStack<Token> &onExitTokens, |
|
1529 const bool initialAdvance, |
|
1530 const bool queueEmptyOnEmpty) |
|
1531 { |
|
1532 bool effectiveInitialAdvance = initialAdvance; |
|
1533 bool hasWrittenExpression = false; |
|
1534 |
|
1535 /* Buffer which all text nodes, that might be split up by comments, |
|
1536 * processing instructions and CDATA sections, are appended to. */ |
|
1537 QString characters; |
|
1538 |
|
1539 while(!atEnd()) |
|
1540 { |
|
1541 if(effectiveInitialAdvance) |
|
1542 readNext(); |
|
1543 else |
|
1544 effectiveInitialAdvance = true; |
|
1545 |
|
1546 switch(tokenType()) |
|
1547 { |
|
1548 case QXmlStreamReader::StartElement: |
|
1549 { |
|
1550 queueTextConstructor(characters, hasWrittenExpression, to); |
|
1551 handleXMLBase(to, &onExitTokens); |
|
1552 |
|
1553 pushState(InsideSequenceConstructor); |
|
1554 |
|
1555 commencingExpression(hasWrittenExpression, to); |
|
1556 |
|
1557 if(isXSLT()) |
|
1558 { |
|
1559 handleXSLTVersion(&m_tokenSource, &onExitTokens, true); |
|
1560 handleStandardAttributes(true); |
|
1561 validateElement(); |
|
1562 |
|
1563 queueNamespaceDeclarations(to, &onExitTokens); |
|
1564 |
|
1565 switch(currentElementName()) |
|
1566 { |
|
1567 case If: |
|
1568 { |
|
1569 queueToken(IF, to); |
|
1570 queueToken(LPAREN, to); |
|
1571 |
|
1572 queueExpression(readAttribute(QLatin1String("test")), to); |
|
1573 queueToken(RPAREN, to); |
|
1574 queueToken(THEN, to); |
|
1575 |
|
1576 queueToken(LPAREN, to); |
|
1577 pushState(InsideSequenceConstructor); |
|
1578 insideSequenceConstructor(to); |
|
1579 |
|
1580 break; |
|
1581 } |
|
1582 case Choose: |
|
1583 { |
|
1584 insideChoose(to); |
|
1585 break; |
|
1586 } |
|
1587 case ValueOf: |
|
1588 { |
|
1589 /* We generate a computed text node constructor. */ |
|
1590 queueToken(TEXT, to); |
|
1591 queueToken(CURLY_LBRACE, to); |
|
1592 |
|
1593 queueSimpleContentConstructor(ReportContext::XTSE0870, true, to, |
|
1594 !hasAttribute(QLatin1String("separator")) && m_processingMode.top() == BackwardsCompatible); |
|
1595 queueToken(CURLY_RBRACE, to); |
|
1596 break; |
|
1597 } |
|
1598 case Sequence: |
|
1599 { |
|
1600 queueExpression(readAttribute(QLatin1String("select")), to); |
|
1601 parseFallbacksOnly(); |
|
1602 break; |
|
1603 } |
|
1604 case Text: |
|
1605 { |
|
1606 queueToken(TEXT, to); |
|
1607 queueToken(CURLY_LBRACE, to); |
|
1608 |
|
1609 queueToken(Token(STRING_LITERAL, readElementText()), to); |
|
1610 queueToken(CURLY_RBRACE, to); |
|
1611 break; |
|
1612 } |
|
1613 case Variable: |
|
1614 { |
|
1615 queueVariableDeclaration(VariableInstruction, to); |
|
1616 |
|
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); |
|
1622 |
|
1623 /* We don't want a comma outputted, we're expecting an |
|
1624 * expression now. */ |
|
1625 hasWrittenExpression = false; |
|
1626 |
|
1627 onExitTokens.push(RPAREN); |
|
1628 |
|
1629 break; |
|
1630 } |
|
1631 case CallTemplate: |
|
1632 { |
|
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); |
|
1638 break; |
|
1639 } |
|
1640 case ForEach: |
|
1641 { |
|
1642 queueExpression(readAttribute(QLatin1String("select")), to); |
|
1643 queueToken(MAP, to); |
|
1644 pushState(InsideSequenceConstructor); |
|
1645 |
|
1646 TokenSource::Queue sorts; |
|
1647 queueSorting(false, &sorts); |
|
1648 |
|
1649 |
|
1650 if(sorts.isEmpty()) |
|
1651 { |
|
1652 startStorageOfCurrent(to); |
|
1653 insideSequenceConstructor(to, false); |
|
1654 endStorageOfCurrent(to); |
|
1655 } |
|
1656 else |
|
1657 { |
|
1658 queueToken(SORT, to); |
|
1659 *to += sorts; |
|
1660 queueToken(RETURN, to); |
|
1661 startStorageOfCurrent(to); |
|
1662 insideSequenceConstructor(to, false); |
|
1663 endStorageOfCurrent(to); |
|
1664 queueToken(END_SORT, to); |
|
1665 } |
|
1666 |
|
1667 break; |
|
1668 } |
|
1669 case XSLTTokenLookup::Comment: |
|
1670 { |
|
1671 queueToken(COMMENT, to); |
|
1672 queueToken(INTERNAL, to); |
|
1673 queueToken(CURLY_LBRACE, to); |
|
1674 queueSelectOrSequenceConstructor(ReportContext::XTSE0940, true, to); |
|
1675 queueToken(CURLY_RBRACE, to); |
|
1676 break; |
|
1677 } |
|
1678 case CopyOf: |
|
1679 { |
|
1680 queueExpression(readAttribute(QLatin1String("select")), to); |
|
1681 // TODO |
|
1682 |
|
1683 if(readNext() == QXmlStreamReader::EndElement) |
|
1684 break; |
|
1685 else |
|
1686 { |
|
1687 error(QtXmlPatterns::tr("Element %1 cannot have children.").arg(formatKeyword(QLatin1String("copy-of"))), |
|
1688 ReportContext::XTSE0010); |
|
1689 } |
|
1690 break; |
|
1691 } |
|
1692 case AnalyzeString: |
|
1693 { |
|
1694 // TODO |
|
1695 skipSubTree(); |
|
1696 break; |
|
1697 } |
|
1698 case ResultDocument: |
|
1699 { |
|
1700 // TODO |
|
1701 pushState(InsideSequenceConstructor); |
|
1702 insideSequenceConstructor(to); |
|
1703 break; |
|
1704 } |
|
1705 case Copy: |
|
1706 { |
|
1707 /* We translate: |
|
1708 * <xsl:copy>expr</xsl:copy> |
|
1709 * into: |
|
1710 * |
|
1711 * let $body := expr |
|
1712 * return |
|
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. :) |
|
1719 * . |
|
1720 * |
|
1721 * TODO node identity is the same as the old node. |
|
1722 * TODO namespace bindings are lost when elements are constructed |
|
1723 */ |
|
1724 |
|
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); |
|
1737 |
|
1738 /* if(self::element()) then */ |
|
1739 queueToken(IF, to); |
|
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); |
|
1748 |
|
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); |
|
1762 |
|
1763 /* else if(self::document-node()) then */ |
|
1764 queueToken(ELSE, to); |
|
1765 queueToken(IF, 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); |
|
1774 |
|
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); |
|
1782 |
|
1783 /* else . */ |
|
1784 queueToken(ELSE, to); |
|
1785 queueToken(DOT, to); |
|
1786 |
|
1787 break; |
|
1788 } |
|
1789 case XSLTTokenLookup::ProcessingInstruction: |
|
1790 { |
|
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); |
|
1798 break; |
|
1799 } |
|
1800 case Document: |
|
1801 { |
|
1802 handleValidationAttributes(false); |
|
1803 |
|
1804 // TODO base-URI |
|
1805 queueToken(DOCUMENT, to); |
|
1806 queueToken(INTERNAL, to); |
|
1807 queueToken(CURLY_LBRACE, to); |
|
1808 pushState(InsideSequenceConstructor); |
|
1809 insideSequenceConstructor(to); |
|
1810 queueToken(CURLY_RBRACE, to); |
|
1811 break; |
|
1812 } |
|
1813 case Element: |
|
1814 { |
|
1815 handleValidationAttributes(false); |
|
1816 |
|
1817 // TODO base-URI |
|
1818 queueToken(ELEMENT, to); |
|
1819 queueToken(INTERNAL, to); |
|
1820 |
|
1821 /* The name. */ |
|
1822 queueToken(CURLY_LBRACE, to); |
|
1823 // TODO only strings allowed, not qname values. |
|
1824 queueAVT(readAttribute(QLatin1String("name")), to); |
|
1825 queueToken(CURLY_RBRACE, to); |
|
1826 |
|
1827 /* The sequence constructor. */ |
|
1828 queueToken(CURLY_LBRACE, to); |
|
1829 pushState(InsideSequenceConstructor); |
|
1830 insideSequenceConstructor(to); |
|
1831 queueToken(CURLY_RBRACE, to); |
|
1832 break; |
|
1833 } |
|
1834 case Attribute: |
|
1835 { |
|
1836 handleValidationAttributes(false); |
|
1837 |
|
1838 // TODO base-URI |
|
1839 queueToken(ATTRIBUTE, to); |
|
1840 queueToken(INTERNAL, to); |
|
1841 |
|
1842 /* The name. */ |
|
1843 queueToken(CURLY_LBRACE, to); |
|
1844 // TODO only strings allowed, not qname values. |
|
1845 queueAVT(readAttribute(QLatin1String("name")), to); |
|
1846 queueToken(CURLY_RBRACE, to); |
|
1847 |
|
1848 /* The sequence constructor. */ |
|
1849 queueToken(CURLY_LBRACE, to); |
|
1850 queueSimpleContentConstructor(ReportContext::XTSE0840, |
|
1851 true, to); |
|
1852 queueToken(CURLY_RBRACE, to); |
|
1853 break; |
|
1854 } |
|
1855 case Namespace: |
|
1856 { |
|
1857 queueToken(NAMESPACE, to); |
|
1858 |
|
1859 /* The name. */ |
|
1860 queueToken(CURLY_LBRACE, to); |
|
1861 queueAVT(readAttribute(QLatin1String("name")), to); |
|
1862 queueToken(CURLY_RBRACE, to); |
|
1863 |
|
1864 /* The sequence constructor. */ |
|
1865 queueToken(CURLY_LBRACE, to); |
|
1866 queueSelectOrSequenceConstructor(ReportContext::XTSE0910, |
|
1867 false, to); |
|
1868 queueToken(CURLY_RBRACE, to); |
|
1869 break; |
|
1870 } |
|
1871 case PerformSort: |
|
1872 { |
|
1873 /* For: |
|
1874 * <xsl:perform-sort select="$in"> |
|
1875 * <xsl:sort select="@key"/> |
|
1876 * </xsl:perform-sort> |
|
1877 * |
|
1878 * we generate: |
|
1879 * |
|
1880 * $in map sort order by @key |
|
1881 * return . |
|
1882 * end_sort |
|
1883 */ |
|
1884 |
|
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 |
|
1889 * reorder it. */ |
|
1890 |
|
1891 /* We store the attributes of xsl:perform-sort, before |
|
1892 * queueSorting() advances the reader. */ |
|
1893 const QXmlStreamAttributes atts(m_currentAttributes); |
|
1894 |
|
1895 TokenSource::Queue sorts; |
|
1896 queueSorting(true, &sorts); |
|
1897 queueSelectOrSequenceConstructor(ReportContext::XTSE1040, |
|
1898 true, |
|
1899 to, |
|
1900 &atts); |
|
1901 /* queueSelectOrSequenceConstructor() positions us on EndElement. */ |
|
1902 effectiveInitialAdvance = false; |
|
1903 queueToken(MAP, to); |
|
1904 queueToken(SORT, to); |
|
1905 *to += sorts; |
|
1906 queueToken(RETURN, to); |
|
1907 queueToken(DOT, to); |
|
1908 queueToken(END_SORT, to); |
|
1909 |
|
1910 break; |
|
1911 } |
|
1912 case Message: |
|
1913 { |
|
1914 // TODO |
|
1915 queueEmptySequence(to); |
|
1916 skipSubTree(); |
|
1917 break; |
|
1918 } |
|
1919 case ApplyTemplates: |
|
1920 { |
|
1921 if(hasAttribute(QLatin1String("select"))) |
|
1922 queueExpression(readAttribute(QLatin1String("select")), to); |
|
1923 else |
|
1924 { |
|
1925 queueToken(CHILD, to); |
|
1926 queueToken(COLONCOLON, to); |
|
1927 queueToken(NODE, to); |
|
1928 queueToken(LPAREN, to); |
|
1929 queueToken(RPAREN, to); |
|
1930 } |
|
1931 |
|
1932 bool hasMode = hasAttribute(QLatin1String("mode")); |
|
1933 QString mode; |
|
1934 |
|
1935 if(hasMode) |
|
1936 mode = readAttribute(QLatin1String("mode")).trimmed(); |
|
1937 |
|
1938 queueToken(FOR_APPLY_TEMPLATE, to); |
|
1939 |
|
1940 TokenSource::Queue sorts; |
|
1941 queueSorting(false, &sorts, true); |
|
1942 |
|
1943 if(!sorts.isEmpty()) |
|
1944 { |
|
1945 queueToken(SORT, to); |
|
1946 *to += sorts; |
|
1947 queueToken(RETURN, to); |
|
1948 } |
|
1949 |
|
1950 queueToken(APPLY_TEMPLATE, to); |
|
1951 |
|
1952 if(hasMode) |
|
1953 { |
|
1954 queueToken(MODE, to); |
|
1955 queueToken(Token(mode.startsWith(QLatin1Char('#')) ? NCNAME : QNAME, mode), to); |
|
1956 } |
|
1957 |
|
1958 queueToken(LPAREN, to); |
|
1959 queueWithParams(ApplyTemplates, to, false); |
|
1960 queueToken(RPAREN, to); |
|
1961 |
|
1962 if(!sorts.isEmpty()) |
|
1963 queueToken(END_SORT, to); |
|
1964 |
|
1965 break; |
|
1966 } |
|
1967 default: |
|
1968 unexpectedContent(); |
|
1969 } |
|
1970 continue; |
|
1971 } |
|
1972 else |
|
1973 { |
|
1974 handleXSLTVersion(&m_tokenSource, &onExitTokens, true); |
|
1975 handleStandardAttributes(false); |
|
1976 handleValidationAttributes(false); |
|
1977 |
|
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(); |
|
1985 |
|
1986 for(int i = 0; i < len; ++i) |
|
1987 { |
|
1988 const QXmlStreamAttribute &at = m_currentAttributes.at(i); |
|
1989 |
|
1990 /* We don't want to generate constructors for XSL-T attributes. */ |
|
1991 if(at.namespaceUri() == CommonNamespaces::XSLT) |
|
1992 continue; |
|
1993 |
|
1994 queueToken(ATTRIBUTE, to); |
|
1995 queueToken(INTERNAL, to); |
|
1996 |
|
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); |
|
2002 } |
|
2003 |
|
2004 pushState(InsideSequenceConstructor); |
|
2005 insideSequenceConstructor(to); |
|
2006 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement || hasError()); |
|
2007 continue; |
|
2008 } |
|
2009 |
|
2010 unexpectedContent(); |
|
2011 break; |
|
2012 } |
|
2013 case QXmlStreamReader::EndElement: |
|
2014 { |
|
2015 queueTextConstructor(characters, hasWrittenExpression, to); |
|
2016 leaveState(); |
|
2017 |
|
2018 if(!hasWrittenExpression && queueEmptyOnEmpty) |
|
2019 queueEmptySequence(to); |
|
2020 |
|
2021 queueOnExit(onExitTokens, to); |
|
2022 |
|
2023 if(isXSLT()) |
|
2024 { |
|
2025 Q_ASSERT(!isElement(Sequence)); |
|
2026 |
|
2027 switch(currentElementName()) |
|
2028 { |
|
2029 /* Fallthrough all these. */ |
|
2030 case When: |
|
2031 case Choose: |
|
2032 case ForEach: |
|
2033 case Otherwise: |
|
2034 case PerformSort: |
|
2035 case Message: |
|
2036 case ResultDocument: |
|
2037 case Copy: |
|
2038 case CallTemplate: |
|
2039 case Text: |
|
2040 case ValueOf: |
|
2041 { |
|
2042 hasWrittenExpression = true; |
|
2043 break; |
|
2044 } |
|
2045 case If: |
|
2046 { |
|
2047 queueToken(RPAREN, to); |
|
2048 queueToken(ELSE, to); |
|
2049 queueEmptySequence(to); |
|
2050 break; |
|
2051 } |
|
2052 case Function: |
|
2053 { |
|
2054 queueToken(CURLY_RBRACE, to); |
|
2055 queueToken(SEMI_COLON, to); |
|
2056 break; |
|
2057 } |
|
2058 case Template: |
|
2059 { |
|
2060 endStorageOfCurrent(&m_tokenSource); |
|
2061 /* TODO, fallthrough to Function. */ |
|
2062 queueToken(CURLY_RBRACE, to); |
|
2063 queueToken(SEMI_COLON, to); |
|
2064 break; |
|
2065 } |
|
2066 default: |
|
2067 ; |
|
2068 } |
|
2069 } |
|
2070 else |
|
2071 { |
|
2072 /* We're closing a direct element constructor. */ |
|
2073 hasWrittenExpression = true; |
|
2074 queueToken(CURLY_RBRACE, to); |
|
2075 } |
|
2076 |
|
2077 return hasWrittenExpression; |
|
2078 } |
|
2079 case QXmlStreamReader::ProcessingInstruction: |
|
2080 /* Fallthrough. */ |
|
2081 case QXmlStreamReader::Comment: |
|
2082 /* We do nothing, we just ignore them. */ |
|
2083 continue; |
|
2084 case QXmlStreamReader::Characters: |
|
2085 { |
|
2086 if(whitespaceToSkip()) |
|
2087 continue; |
|
2088 else |
|
2089 { |
|
2090 characters += text().toString(); |
|
2091 continue; |
|
2092 } |
|
2093 } |
|
2094 default: |
|
2095 ; |
|
2096 } |
|
2097 } |
|
2098 |
|
2099 leaveState(); |
|
2100 return hasWrittenExpression; |
|
2101 } |
|
2102 |
|
2103 bool XSLTTokenizer::isStylesheetElement() const |
|
2104 { |
|
2105 Q_ASSERT(isXSLT()); |
|
2106 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement || |
|
2107 tokenType() == QXmlStreamReader::EndElement); |
|
2108 |
|
2109 const NodeName name = currentElementName(); |
|
2110 return name == Stylesheet || name == Transform; |
|
2111 } |
|
2112 |
|
2113 void XSLTTokenizer::skipBodyOfParam(const ReportContext::ErrorCode code) |
|
2114 { |
|
2115 Q_ASSERT(isXSLT()); |
|
2116 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
2117 const NodeName name(currentElementName()); |
|
2118 |
|
2119 if(skipSubTree()) |
|
2120 { |
|
2121 error(QtXmlPatterns::tr("Element %1 cannot have a sequence constructor.") |
|
2122 .arg(formatKeyword(toString(name))), |
|
2123 code); |
|
2124 } |
|
2125 } |
|
2126 |
|
2127 void XSLTTokenizer::queueWithParams(const XSLTTokenLookup::NodeName parentName, |
|
2128 TokenSource::Queue *const to, |
|
2129 const bool initialAdvance) |
|
2130 { |
|
2131 Q_ASSERT(parentName == ApplyTemplates || parentName == CallTemplate); |
|
2132 |
|
2133 bool effectiveInitialAdvance = initialAdvance; |
|
2134 bool hasQueuedParam = false; |
|
2135 |
|
2136 while(!atEnd()) |
|
2137 { |
|
2138 if(effectiveInitialAdvance) |
|
2139 readNext(); |
|
2140 else |
|
2141 effectiveInitialAdvance = true; |
|
2142 |
|
2143 switch(tokenType()) |
|
2144 { |
|
2145 case QXmlStreamReader::StartElement: |
|
2146 { |
|
2147 if(hasQueuedParam) |
|
2148 queueToken(COMMA, to); |
|
2149 |
|
2150 if(isXSLT() && isElement(WithParam)) |
|
2151 { |
|
2152 if(hasAttribute(QLatin1String("tunnel")) && attributeYesNo(QLatin1String("tunnel"))) |
|
2153 queueToken(TUNNEL, to); |
|
2154 |
|
2155 queueVariableDeclaration(WithParamVariable, to); |
|
2156 hasQueuedParam = true; |
|
2157 continue; |
|
2158 } |
|
2159 else |
|
2160 unexpectedContent(); |
|
2161 } |
|
2162 case QXmlStreamReader::EndElement: |
|
2163 { |
|
2164 if(isElement(parentName)) |
|
2165 return; |
|
2166 else |
|
2167 continue; |
|
2168 } |
|
2169 case QXmlStreamReader::ProcessingInstruction: |
|
2170 /* Fallthrough. */ |
|
2171 case QXmlStreamReader::Comment: |
|
2172 continue; |
|
2173 case QXmlStreamReader::Characters: |
|
2174 if(whitespaceToSkip()) |
|
2175 continue; |
|
2176 else |
|
2177 return; |
|
2178 default: |
|
2179 unexpectedContent(); |
|
2180 } |
|
2181 } |
|
2182 unexpectedContent(); |
|
2183 } |
|
2184 |
|
2185 void XSLTTokenizer::queueParams(const XSLTTokenLookup::NodeName parentName, |
|
2186 TokenSource::Queue *const to) |
|
2187 { |
|
2188 bool hasQueuedParam = false; |
|
2189 |
|
2190 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
2191 |
|
2192 while(!atEnd()) |
|
2193 { |
|
2194 switch(readNext()) |
|
2195 { |
|
2196 case QXmlStreamReader::StartElement: |
|
2197 { |
|
2198 if(isXSLT() && isElement(Param)) |
|
2199 { |
|
2200 if(hasQueuedParam) |
|
2201 queueToken(COMMA, to); |
|
2202 |
|
2203 validateElement(); |
|
2204 |
|
2205 if(parentName == Function && m_currentAttributes.hasAttribute(QLatin1String("select"))) |
|
2206 { |
|
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); |
|
2212 } |
|
2213 |
|
2214 if(parentName == Function && m_currentAttributes.hasAttribute(QLatin1String("required"))) |
|
2215 { |
|
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); |
|
2221 } |
|
2222 |
|
2223 const bool hasTunnel = m_currentAttributes.hasAttribute(QLatin1String("tunnel")); |
|
2224 const bool isTunnel = hasTunnel ? attributeYesNo(QLatin1String("tunnel")) : false; |
|
2225 |
|
2226 if(isTunnel) |
|
2227 { |
|
2228 if(parentName == Function) |
|
2229 { |
|
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); |
|
2233 } |
|
2234 else |
|
2235 queueToken(TUNNEL, to); |
|
2236 } |
|
2237 |
|
2238 hasQueuedParam = true; |
|
2239 queueVariableDeclaration(parentName == Function ? FunctionParameter : TemplateParameter, to); |
|
2240 continue; |
|
2241 } |
|
2242 else |
|
2243 return; |
|
2244 } |
|
2245 case QXmlStreamReader::Characters: |
|
2246 { |
|
2247 if(whitespaceToSkip()) |
|
2248 continue; |
|
2249 /* Fallthrough. */ |
|
2250 } |
|
2251 case QXmlStreamReader::EndElement: |
|
2252 return; |
|
2253 default: |
|
2254 ; |
|
2255 } |
|
2256 } |
|
2257 } |
|
2258 |
|
2259 bool XSLTTokenizer::skipSubTree(const bool exitOnContent) |
|
2260 { |
|
2261 bool hasContent = false; |
|
2262 int depth = 0; |
|
2263 |
|
2264 while(!atEnd()) |
|
2265 { |
|
2266 switch(readNext()) |
|
2267 { |
|
2268 case QXmlStreamReader::Characters: |
|
2269 { |
|
2270 if(whitespaceToSkip()) |
|
2271 continue; |
|
2272 else |
|
2273 { |
|
2274 hasContent = true; |
|
2275 if(exitOnContent) |
|
2276 return true; |
|
2277 |
|
2278 break; |
|
2279 } |
|
2280 } |
|
2281 case QXmlStreamReader::StartElement: |
|
2282 { |
|
2283 hasContent = true; |
|
2284 if(exitOnContent) |
|
2285 return true; |
|
2286 |
|
2287 ++depth; |
|
2288 break; |
|
2289 } |
|
2290 case QXmlStreamReader::EndElement: |
|
2291 { |
|
2292 --depth; |
|
2293 break; |
|
2294 } |
|
2295 default: |
|
2296 continue; |
|
2297 } |
|
2298 |
|
2299 if(depth == -1) |
|
2300 return hasContent; |
|
2301 } |
|
2302 |
|
2303 checkForParseError(); |
|
2304 return hasContent; |
|
2305 } |
|
2306 |
|
2307 void XSLTTokenizer::parseFallbacksOnly() |
|
2308 { |
|
2309 Q_ASSERT(isXSLT()); |
|
2310 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
2311 |
|
2312 skipSubTree(); |
|
2313 Q_ASSERT(tokenType() == QXmlStreamReader::EndElement); |
|
2314 } |
|
2315 |
|
2316 void XSLTTokenizer::insideAttributeSet() |
|
2317 { |
|
2318 while(!atEnd()) |
|
2319 { |
|
2320 switch(readNext()) |
|
2321 { |
|
2322 case QXmlStreamReader::StartElement: |
|
2323 { |
|
2324 if(isXSLT() && isElement(AttributeSet)) |
|
2325 { |
|
2326 // TODO |
|
2327 skipSubTree(); |
|
2328 } |
|
2329 else |
|
2330 unexpectedContent(); |
|
2331 } |
|
2332 case QXmlStreamReader::EndElement: |
|
2333 return; |
|
2334 case QXmlStreamReader::ProcessingInstruction: |
|
2335 /* Fallthrough. */ |
|
2336 case QXmlStreamReader::Comment: |
|
2337 continue; |
|
2338 case QXmlStreamReader::Characters: |
|
2339 if(whitespaceToSkip()) |
|
2340 continue; |
|
2341 /* Fallthrough. */ |
|
2342 default: |
|
2343 unexpectedContent(); |
|
2344 } |
|
2345 } |
|
2346 unexpectedContent(); |
|
2347 } |
|
2348 |
|
2349 void XSLTTokenizer::insideStylesheetModule() |
|
2350 { |
|
2351 while(!atEnd()) |
|
2352 { |
|
2353 switch(readNext()) |
|
2354 { |
|
2355 case QXmlStreamReader::StartElement: |
|
2356 { |
|
2357 if(isXSLT()) |
|
2358 { |
|
2359 handleStandardAttributes(true); |
|
2360 handleXSLTVersion(0, 0, true, 0, false); |
|
2361 validateElement(); |
|
2362 |
|
2363 /* Handle the various declarations. */ |
|
2364 switch(currentElementName()) |
|
2365 { |
|
2366 case Template: |
|
2367 insideTemplate(); |
|
2368 break; |
|
2369 case Function: |
|
2370 insideFunction(); |
|
2371 break; |
|
2372 case Variable: |
|
2373 queueVariableDeclaration(VariableDeclaration, &m_tokenSource); |
|
2374 break; |
|
2375 case Param: |
|
2376 queueVariableDeclaration(GlobalParameter, &m_tokenSource); |
|
2377 break; |
|
2378 case ImportSchema: |
|
2379 { |
|
2380 error(QtXmlPatterns::tr("This processor is not Schema-aware and " |
|
2381 "therefore %1 cannot be used.").arg(formatKeyword(toString(ImportSchema))), |
|
2382 ReportContext::XTSE1660); |
|
2383 break; |
|
2384 } |
|
2385 case Output: |
|
2386 { |
|
2387 // TODO |
|
2388 skipSubTree(); |
|
2389 break; |
|
2390 } |
|
2391 case StripSpace: |
|
2392 /* Fallthrough. */ |
|
2393 case PreserveSpace: |
|
2394 { |
|
2395 // TODO @elements |
|
2396 skipSubTree(true); |
|
2397 readNext(); |
|
2398 |
|
2399 if(!isEndElement()) |
|
2400 unexpectedContent(); |
|
2401 break; |
|
2402 } |
|
2403 case Include: |
|
2404 { |
|
2405 // TODO |
|
2406 if(skipSubTree(true)) |
|
2407 unexpectedContent(); |
|
2408 break; |
|
2409 } |
|
2410 case Import: |
|
2411 { |
|
2412 // TODO |
|
2413 if(skipSubTree(true)) |
|
2414 unexpectedContent(); |
|
2415 break; |
|
2416 } |
|
2417 case Key: |
|
2418 { |
|
2419 // TODO |
|
2420 skipSubTree(); |
|
2421 break; |
|
2422 } |
|
2423 case AttributeSet: |
|
2424 insideAttributeSet(); |
|
2425 break; |
|
2426 default: |
|
2427 if(m_processingMode.top() != ForwardCompatible) |
|
2428 unexpectedContent(); |
|
2429 } |
|
2430 } |
|
2431 else |
|
2432 { |
|
2433 /* We have a user-defined data element. See section 3.6.2. */ |
|
2434 |
|
2435 if(namespaceUri().isEmpty()) |
|
2436 { |
|
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); |
|
2440 } |
|
2441 else |
|
2442 skipSubTree(); |
|
2443 } |
|
2444 break; |
|
2445 } |
|
2446 case QXmlStreamReader::Characters: |
|
2447 { |
|
2448 /* Regardless of xml:space, we skip whitespace, see step 4 in |
|
2449 * 4.2 Stripping Whitespace from the Stylesheet. */ |
|
2450 if(isWhitespace()) |
|
2451 continue; |
|
2452 |
|
2453 unexpectedContent(ReportContext::XTSE0120); |
|
2454 break; |
|
2455 } |
|
2456 case QXmlStreamReader::EndElement: |
|
2457 { |
|
2458 if(isXSLT()) |
|
2459 leaveState(); |
|
2460 |
|
2461 break; |
|
2462 } |
|
2463 default: |
|
2464 ; |
|
2465 } |
|
2466 } |
|
2467 checkForParseError(); |
|
2468 } |
|
2469 |
|
2470 bool XSLTTokenizer::readToggleAttribute(const QString &localName, |
|
2471 const QString &isTrue, |
|
2472 const QString &isFalse, |
|
2473 const QXmlStreamAttributes *const attsP) const |
|
2474 { |
|
2475 const QXmlStreamAttributes atts(attsP ? *attsP : m_currentAttributes); |
|
2476 Q_ASSERT(atts.hasAttribute(localName)); |
|
2477 const QString value(atts.value(localName).toString()); |
|
2478 |
|
2479 if(value == isTrue) |
|
2480 return true; |
|
2481 else if(value == isFalse) |
|
2482 return false; |
|
2483 else |
|
2484 { |
|
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()), |
|
2488 formatData(isTrue), |
|
2489 formatData(isFalse), |
|
2490 formatData(value)), |
|
2491 ReportContext::XTSE0020); |
|
2492 /* Silences a compiler warning. */ |
|
2493 return false; |
|
2494 } |
|
2495 } |
|
2496 |
|
2497 int XSLTTokenizer::readAlternativeAttribute(const QHash<QString, int> &alternatives, |
|
2498 const QXmlStreamAttribute &attr) const |
|
2499 { |
|
2500 const QString value(attr.value().toString().trimmed()); |
|
2501 |
|
2502 if(alternatives.contains(value)) |
|
2503 return alternatives[value]; |
|
2504 |
|
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. */ |
|
2510 } |
|
2511 |
|
2512 bool XSLTTokenizer::attributeYesNo(const QString &localName) const |
|
2513 { |
|
2514 return readToggleAttribute(localName, QLatin1String("yes"), QLatin1String("no")); |
|
2515 } |
|
2516 |
|
2517 void XSLTTokenizer::queueSorting(const bool oneSortRequired, |
|
2518 TokenSource::Queue *const to, |
|
2519 const bool speciallyTreatWhitespace) |
|
2520 { |
|
2521 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); |
|
2522 |
|
2523 const NodeName elementName(currentElementName()); |
|
2524 bool hasQueuedOneSort = false; |
|
2525 |
|
2526 while(!atEnd()) |
|
2527 { |
|
2528 switch(readNext()) |
|
2529 { |
|
2530 case QXmlStreamReader::EndElement: |
|
2531 { |
|
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. */ |
|
2535 if(isXSLT()) |
|
2536 { |
|
2537 switch(currentElementName()) |
|
2538 { |
|
2539 case PerformSort: |
|
2540 /* Fallthrough. */ |
|
2541 case ForEach: |
|
2542 /* Fallthrough. */ |
|
2543 case ApplyTemplates: |
|
2544 return; |
|
2545 default: |
|
2546 ; |
|
2547 } |
|
2548 } |
|
2549 continue; |
|
2550 } |
|
2551 case QXmlStreamReader::StartElement: |
|
2552 { |
|
2553 if(isXSLT() && isElement(Sort)) |
|
2554 { |
|
2555 if(hasQueuedOneSort) |
|
2556 queueToken(COMMA, to); |
|
2557 |
|
2558 /* sorts are by default stable. */ |
|
2559 if(hasAttribute(QLatin1String("stable"))) |
|
2560 { |
|
2561 if(hasQueuedOneSort) |
|
2562 { |
|
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); |
|
2567 } |
|
2568 |
|
2569 if(attributeYesNo(QLatin1String("stable"))) |
|
2570 queueToken(STABLE, to); |
|
2571 } |
|
2572 |
|
2573 if(!hasQueuedOneSort) |
|
2574 { |
|
2575 queueToken(ORDER, to); |
|
2576 queueToken(BY, to); |
|
2577 } |
|
2578 |
|
2579 /* We store a copy such that we can use them after |
|
2580 * queueSelectOrSequenceConstructor() advances the reader. */ |
|
2581 const QXmlStreamAttributes atts(m_currentAttributes); |
|
2582 |
|
2583 const int before = to->count(); |
|
2584 |
|
2585 // TODO This doesn't work as is. @data-type can be an AVT. |
|
2586 if(atts.hasAttribute(QLatin1String("data-type"))) |
|
2587 { |
|
2588 if(readToggleAttribute(QLatin1String("data-type"), |
|
2589 QLatin1String("text"), |
|
2590 QLatin1String("number"), |
|
2591 &atts)) |
|
2592 queueToken(Token(NCNAME, QLatin1String("string")), to); |
|
2593 else |
|
2594 queueToken(Token(NCNAME, QLatin1String("number")), to); |
|
2595 } |
|
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, |
|
2601 true, |
|
2602 to, |
|
2603 0, |
|
2604 false); |
|
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); |
|
2610 |
|
2611 // TODO case-order |
|
2612 // TODO lang |
|
2613 |
|
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"), |
|
2618 &atts)) |
|
2619 { |
|
2620 queueToken(DESCENDING, to); |
|
2621 } |
|
2622 else |
|
2623 { |
|
2624 /* This is the default. */ |
|
2625 queueToken(ASCENDING, to); |
|
2626 } |
|
2627 |
|
2628 if(atts.hasAttribute(QLatin1String("collation"))) |
|
2629 { |
|
2630 queueToken(INTERNAL, to); |
|
2631 queueToken(COLLATION, to); |
|
2632 queueAVT(atts.value(QLatin1String("collation")).toString(), to); |
|
2633 } |
|
2634 |
|
2635 hasQueuedOneSort = true; |
|
2636 continue; |
|
2637 } |
|
2638 else |
|
2639 break; |
|
2640 } |
|
2641 case QXmlStreamReader::Characters: |
|
2642 { |
|
2643 if(speciallyTreatWhitespace && isWhitespace()) |
|
2644 continue; |
|
2645 |
|
2646 if(QXmlStreamReader::Characters && whitespaceToSkip()) |
|
2647 continue; |
|
2648 |
|
2649 /* We have an instruction which is a text node, we're done. */ |
|
2650 break; |
|
2651 } |
|
2652 case QXmlStreamReader::ProcessingInstruction: |
|
2653 /* Fallthrough. */ |
|
2654 case QXmlStreamReader::Comment: |
|
2655 continue; |
|
2656 default: |
|
2657 unexpectedContent(); |
|
2658 |
|
2659 }; |
|
2660 if(oneSortRequired && !hasQueuedOneSort) |
|
2661 { |
|
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); |
|
2665 } |
|
2666 else |
|
2667 return; |
|
2668 } |
|
2669 checkForParseError(); |
|
2670 } |
|
2671 |
|
2672 void XSLTTokenizer::insideFunction() |
|
2673 { |
|
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()); |
|
2680 |
|
2681 if(hasAttribute(QLatin1String("override"))) |
|
2682 { |
|
2683 /* We currently have no external functions, so we don't pass it on currently. */ |
|
2684 attributeYesNo(QLatin1String("override")); |
|
2685 } |
|
2686 |
|
2687 queueParams(Function, &m_tokenSource); |
|
2688 |
|
2689 queueToken(RPAREN, &m_tokenSource); |
|
2690 |
|
2691 if(!expectedType.isNull()) |
|
2692 { |
|
2693 queueToken(AS, &m_tokenSource); |
|
2694 queueSequenceType(expectedType); |
|
2695 } |
|
2696 |
|
2697 QStack<Token> onExitTokens; |
|
2698 handleXMLBase(&m_tokenSource, &onExitTokens, true, &m_currentAttributes); |
|
2699 handleXSLTVersion(&m_tokenSource, &onExitTokens, true); |
|
2700 queueToken(CURLY_LBRACE, &m_tokenSource); |
|
2701 |
|
2702 pushState(InsideSequenceConstructor); |
|
2703 insideSequenceConstructor(&m_tokenSource, onExitTokens, false); |
|
2704 /* We don't queue CURLY_RBRACE, because it's done in |
|
2705 * insideSequenceConstructor(). */ |
|
2706 } |
|
2707 |
|
2708 YYLTYPE XSLTTokenizer::currentSourceLocator() const |
|
2709 { |
|
2710 YYLTYPE retval; |
|
2711 retval.first_line = lineNumber(); |
|
2712 retval.first_column = columnNumber(); |
|
2713 return retval; |
|
2714 } |
|
2715 |
|
2716 QT_END_NAMESPACE |
|
2717 |