1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtScxml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QSCXMLCOMPILER_P_H
41#define QSCXMLCOMPILER_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include "qscxmlcompiler.h"
55
56#include <QtCore/qdir.h>
57#include <QtCore/qfileinfo.h>
58#include <QtCore/qset.h>
59#include <QtCore/qsharedpointer.h>
60#include <QtCore/qstringlist.h>
61#include <QtCore/qstring.h>
62#include <QtCore/qxmlstream.h>
63
64QT_BEGIN_NAMESPACE
65
66namespace DocumentModel {
67
68struct XmlLocation
69{
70 int line;
71 int column;
72
73 XmlLocation(int theLine, int theColumn): line(theLine), column(theColumn) {}
74};
75
76struct If;
77struct Send;
78struct Invoke;
79struct Script;
80struct AbstractState;
81struct State;
82struct Transition;
83struct HistoryState;
84struct Scxml;
85class NodeVisitor;
86struct Node {
87 XmlLocation xmlLocation;
88
89 Node(const XmlLocation &theLocation): xmlLocation(theLocation) {}
90 virtual ~Node();
91 virtual void accept(NodeVisitor *visitor) = 0;
92
93 virtual If *asIf() { return nullptr; }
94 virtual Send *asSend() { return nullptr; }
95 virtual Invoke *asInvoke() { return nullptr; }
96 virtual Script *asScript() { return nullptr; }
97 virtual State *asState() { return nullptr; }
98 virtual Transition *asTransition() { return nullptr; }
99 virtual HistoryState *asHistoryState() { return nullptr; }
100 virtual Scxml *asScxml() { return nullptr; }
101 AbstractState *asAbstractState();
102
103private:
104 Q_DISABLE_COPY(Node)
105};
106
107struct DataElement: public Node
108{
109 QString id;
110 QString src;
111 QString expr;
112 QString content;
113
114 DataElement(const XmlLocation &xmlLocation): Node(xmlLocation) {}
115 void accept(NodeVisitor *visitor) override;
116};
117
118struct Param: public Node
119{
120 QString name;
121 QString expr;
122 QString location;
123
124 Param(const XmlLocation &xmlLocation): Node(xmlLocation) {}
125 void accept(NodeVisitor *visitor) override;
126};
127
128struct DoneData: public Node
129{
130 QString contents;
131 QString expr;
132 QVector<Param *> params;
133
134 DoneData(const XmlLocation &xmlLocation): Node(xmlLocation) {}
135 void accept(NodeVisitor *visitor) override;
136};
137
138struct Instruction: public Node
139{
140 Instruction(const XmlLocation &xmlLocation): Node(xmlLocation) {}
141 virtual ~Instruction() {}
142};
143
144typedef QVector<Instruction *> InstructionSequence;
145typedef QVector<InstructionSequence *> InstructionSequences;
146
147struct Send: public Instruction
148{
149 QString event;
150 QString eventexpr;
151 QString type;
152 QString typeexpr;
153 QString target;
154 QString targetexpr;
155 QString id;
156 QString idLocation;
157 QString delay;
158 QString delayexpr;
159 QStringList namelist;
160 QVector<Param *> params;
161 QString content;
162 QString contentexpr;
163
164 Send(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
165 Send *asSend() override { return this; }
166 void accept(NodeVisitor *visitor) override;
167};
168
169struct ScxmlDocument;
170struct Invoke: public Instruction
171{
172 QString type;
173 QString typeexpr;
174 QString src;
175 QString srcexpr;
176 QString id;
177 QString idLocation;
178 QStringList namelist;
179 bool autoforward;
180 QVector<Param *> params;
181 InstructionSequence finalize;
182
183 QSharedPointer<ScxmlDocument> content;
184
185 Invoke(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
186 Invoke *asInvoke() override { return this; }
187 void accept(NodeVisitor *visitor) override;
188};
189
190struct Raise: public Instruction
191{
192 QString event;
193
194 Raise(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
195 void accept(NodeVisitor *visitor) override;
196};
197
198struct Log: public Instruction
199{
200 QString label, expr;
201
202 Log(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
203 void accept(NodeVisitor *visitor) override;
204};
205
206struct Script: public Instruction
207{
208 QString src;
209 QString content;
210
211 Script(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
212 Script *asScript() override { return this; }
213 void accept(NodeVisitor *visitor) override;
214};
215
216struct Assign: public Instruction
217{
218 QString location;
219 QString expr;
220 QString content;
221
222 Assign(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
223 void accept(NodeVisitor *visitor) override;
224};
225
226struct If: public Instruction
227{
228 QStringList conditions;
229 InstructionSequences blocks;
230
231 If(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
232 If *asIf() override { return this; }
233 void accept(NodeVisitor *visitor) override;
234};
235
236struct Foreach: public Instruction
237{
238 QString array;
239 QString item;
240 QString index;
241 InstructionSequence block;
242
243 Foreach(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
244 void accept(NodeVisitor *visitor) override;
245};
246
247struct Cancel: public Instruction
248{
249 QString sendid;
250 QString sendidexpr;
251
252 Cancel(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
253 void accept(NodeVisitor *visitor) override;
254};
255
256struct StateOrTransition: public Node
257{
258 StateOrTransition(const XmlLocation &xmlLocation): Node(xmlLocation) {}
259};
260
261struct StateContainer
262{
263 StateContainer()
264 : parent(nullptr)
265 {}
266
267 StateContainer *parent;
268
269 virtual ~StateContainer() {}
270 virtual void add(StateOrTransition *s) = 0;
271 virtual AbstractState *asAbstractState() { return nullptr; }
272 virtual State *asState() { return nullptr; }
273 virtual Scxml *asScxml() { return nullptr; }
274};
275
276struct AbstractState: public StateContainer
277{
278 QString id;
279
280 AbstractState *asAbstractState() override { return this; }
281};
282
283struct State: public AbstractState, public StateOrTransition
284{
285 enum Type { Normal, Parallel, Final };
286
287 QStringList initial;
288 QVector<DataElement *> dataElements;
289 QVector<StateOrTransition *> children;
290 InstructionSequences onEntry;
291 InstructionSequences onExit;
292 DoneData *doneData;
293 QVector<Invoke *> invokes;
294 Type type;
295
296 Transition *initialTransition; // when not set, it is filled during verification
297
298 State(const XmlLocation &xmlLocation)
299 : StateOrTransition(xmlLocation)
300 , doneData(nullptr)
301 , type(Normal)
302 , initialTransition(nullptr)
303 {}
304
305 void add(StateOrTransition *s) override
306 {
307 Q_ASSERT(s);
308 children.append(t: s);
309 }
310
311 State *asState() override { return this; }
312
313 void accept(NodeVisitor *visitor) override;
314};
315
316struct Transition: public StateOrTransition
317{
318 enum Type { Internal, External, Synthetic };
319 QStringList events;
320 QScopedPointer<QString> condition;
321 QStringList targets;
322 InstructionSequence instructionsOnTransition;
323 Type type;
324
325 QVector<AbstractState *> targetStates; // when not set, it is filled during verification
326
327 Transition(const XmlLocation &xmlLocation)
328 : StateOrTransition(xmlLocation)
329 , type(External)
330 {}
331
332 Transition *asTransition() override { return this; }
333
334 void accept(NodeVisitor *visitor) override;
335};
336
337struct HistoryState: public AbstractState, public StateOrTransition
338{
339 enum Type { Deep, Shallow };
340 Type type;
341 QVector<StateOrTransition *> children;
342
343 HistoryState(const XmlLocation &xmlLocation)
344 : StateOrTransition(xmlLocation)
345 , type(Shallow)
346 {}
347
348 void add(StateOrTransition *s) override
349 {
350 Q_ASSERT(s);
351 children.append(t: s);
352 }
353
354 Transition *defaultConfiguration()
355 { return children.isEmpty() ? nullptr : children.first()->asTransition(); }
356
357 HistoryState *asHistoryState() override { return this; }
358 void accept(NodeVisitor *visitor) override;
359};
360
361struct Scxml: public StateContainer, public Node
362{
363 enum DataModelType {
364 NullDataModel,
365 JSDataModel,
366 CppDataModel
367 };
368 enum BindingMethod {
369 EarlyBinding,
370 LateBinding
371 };
372
373 QStringList initial;
374 QString name;
375 DataModelType dataModel;
376 QString cppDataModelClassName;
377 QString cppDataModelHeaderName;
378 BindingMethod binding;
379 QVector<StateOrTransition *> children;
380 QVector<DataElement *> dataElements;
381 QScopedPointer<Script> script;
382 InstructionSequence initialSetup;
383
384 Transition *initialTransition;
385
386 Scxml(const XmlLocation &xmlLocation)
387 : Node(xmlLocation)
388 , dataModel(NullDataModel)
389 , binding(EarlyBinding)
390 , initialTransition(nullptr)
391 {}
392
393 void add(StateOrTransition *s) override
394 {
395 Q_ASSERT(s);
396 children.append(t: s);
397 }
398
399 Scxml *asScxml() override { return this; }
400
401 void accept(NodeVisitor *visitor) override;
402};
403
404struct ScxmlDocument
405{
406 const QString fileName;
407 Scxml *root;
408 QVector<AbstractState *> allStates;
409 QVector<Transition *> allTransitions;
410 QVector<Node *> allNodes;
411 QVector<InstructionSequence *> allSequences;
412 QVector<ScxmlDocument *> allSubDocuments; // weak pointers
413 bool isVerified;
414
415 ScxmlDocument(const QString &fileName)
416 : fileName(fileName)
417 , root(nullptr)
418 , isVerified(false)
419 {}
420
421 ~ScxmlDocument()
422 {
423 delete root;
424 qDeleteAll(c: allNodes);
425 qDeleteAll(c: allSequences);
426 }
427
428 State *newState(StateContainer *parent, State::Type type, const XmlLocation &xmlLocation)
429 {
430 Q_ASSERT(parent);
431 State *s = newNode<State>(xmlLocation);
432 s->parent = parent;
433 s->type = type;
434 allStates.append(t: s);
435 parent->add(s);
436 return s;
437 }
438
439 HistoryState *newHistoryState(StateContainer *parent, const XmlLocation &xmlLocation)
440 {
441 Q_ASSERT(parent);
442 HistoryState *s = newNode<HistoryState>(xmlLocation);
443 s->parent = parent;
444 allStates.append(t: s);
445 parent->add(s);
446 return s;
447 }
448
449 Transition *newTransition(StateContainer *parent, const XmlLocation &xmlLocation)
450 {
451 Transition *t = newNode<Transition>(xmlLocation);
452 allTransitions.append(t);
453 if (parent != nullptr) {
454 parent->add(s: t);
455 }
456 return t;
457 }
458
459 template<typename T>
460 T *newNode(const XmlLocation &xmlLocation)
461 {
462 T *node = new T(xmlLocation);
463 allNodes.append(node);
464 return node;
465 }
466
467 InstructionSequence *newSequence(InstructionSequences *container)
468 {
469 Q_ASSERT(container);
470 InstructionSequence *is = new InstructionSequence;
471 allSequences.append(t: is);
472 container->append(t: is);
473 return is;
474 }
475};
476
477class Q_SCXML_EXPORT NodeVisitor
478{
479public:
480 virtual ~NodeVisitor();
481
482 virtual void visit(DataElement *) {}
483 virtual void visit(Param *) {}
484 virtual bool visit(DoneData *) { return true; }
485 virtual void endVisit(DoneData *) {}
486 virtual bool visit(Send *) { return true; }
487 virtual void endVisit(Send *) {}
488 virtual bool visit(Invoke *) { return true; }
489 virtual void endVisit(Invoke *) {}
490 virtual void visit(Raise *) {}
491 virtual void visit(Log *) {}
492 virtual void visit(Script *) {}
493 virtual void visit(Assign *) {}
494 virtual bool visit(If *) { return true; }
495 virtual void endVisit(If *) {}
496 virtual bool visit(Foreach *) { return true; }
497 virtual void endVisit(Foreach *) {}
498 virtual void visit(Cancel *) {}
499 virtual bool visit(State *) { return true; }
500 virtual void endVisit(State *) {}
501 virtual bool visit(Transition *) { return true; }
502 virtual void endVisit(Transition *) {}
503 virtual bool visit(HistoryState *) { return true; }
504 virtual void endVisit(HistoryState *) {}
505 virtual bool visit(Scxml *) { return true; }
506 virtual void endVisit(Scxml *) {}
507
508 void visit(InstructionSequence *sequence)
509 {
510 Q_ASSERT(sequence);
511 for (Instruction *instruction : qAsConst(t&: *sequence)) {
512 Q_ASSERT(instruction);
513 instruction->accept(visitor: this);
514 }
515 }
516
517 void visit(const QVector<DataElement *> &dataElements)
518 {
519 for (DataElement *dataElement : dataElements) {
520 Q_ASSERT(dataElement);
521 dataElement->accept(visitor: this);
522 }
523 }
524
525 void visit(const QVector<StateOrTransition *> &children)
526 {
527 for (StateOrTransition *child : children) {
528 Q_ASSERT(child);
529 child->accept(visitor: this);
530 }
531 }
532
533 void visit(const InstructionSequences &sequences)
534 {
535 for (InstructionSequence *sequence : sequences) {
536 Q_ASSERT(sequence);
537 visit(sequence);
538 }
539 }
540
541 void visit(const QVector<Param *> &params)
542 {
543 for (Param *param : params) {
544 Q_ASSERT(param);
545 param->accept(visitor: this);
546 }
547 }
548};
549
550} // DocumentModel namespace
551
552class Q_SCXML_EXPORT QScxmlCompilerPrivate
553{
554public:
555 static QScxmlCompilerPrivate *get(QScxmlCompiler *compiler);
556
557 QScxmlCompilerPrivate(QXmlStreamReader *reader);
558
559 bool verifyDocument();
560 DocumentModel::ScxmlDocument *scxmlDocument() const;
561
562 QString fileName() const;
563 void setFileName(const QString &fileName);
564
565 QScxmlCompiler::Loader *loader() const;
566 void setLoader(QScxmlCompiler::Loader *loader);
567
568 bool readDocument();
569 void parseSubDocument(DocumentModel::Invoke *parentInvoke,
570 QXmlStreamReader *reader,
571 const QString &fileName);
572 bool parseSubElement(DocumentModel::Invoke *parentInvoke,
573 QXmlStreamReader *reader,
574 const QString &fileName);
575 QByteArray load(const QString &name, bool *ok);
576
577 QVector<QScxmlError> errors() const;
578
579 void addError(const QString &msg);
580 void addError(const DocumentModel::XmlLocation &location, const QString &msg);
581 QScxmlStateMachine *instantiateStateMachine() const;
582 void instantiateDataModel(QScxmlStateMachine *stateMachine) const;
583
584private:
585 DocumentModel::AbstractState *currentParent() const;
586 DocumentModel::XmlLocation xmlLocation() const;
587 bool maybeId(const QXmlStreamAttributes &attributes, QString *id);
588 DocumentModel::If *lastIf();
589 bool checkAttributes(const QXmlStreamAttributes &attributes,
590 const QStringList &requiredNames,
591 const QStringList &optionalNames);
592
593 bool preReadElementScxml();
594 bool preReadElementState();
595 bool preReadElementParallel();
596 bool preReadElementInitial();
597 bool preReadElementTransition();
598 bool preReadElementFinal();
599 bool preReadElementHistory();
600 bool preReadElementOnEntry();
601 bool preReadElementOnExit();
602 bool preReadElementRaise();
603 bool preReadElementIf();
604 bool preReadElementElseIf();
605 bool preReadElementElse();
606 bool preReadElementForeach();
607 bool preReadElementLog();
608 bool preReadElementDataModel();
609 bool preReadElementData();
610 bool preReadElementAssign();
611 bool preReadElementDoneData();
612 bool preReadElementContent();
613 bool preReadElementParam();
614 bool preReadElementScript();
615 bool preReadElementSend();
616 bool preReadElementCancel();
617 bool preReadElementInvoke();
618 bool preReadElementFinalize();
619
620 bool postReadElementScxml();
621 bool postReadElementState();
622 bool postReadElementParallel();
623 bool postReadElementInitial();
624 bool postReadElementTransition();
625 bool postReadElementFinal();
626 bool postReadElementHistory();
627 bool postReadElementOnEntry();
628 bool postReadElementOnExit();
629 bool postReadElementRaise();
630 bool postReadElementIf();
631 bool postReadElementElseIf();
632 bool postReadElementElse();
633 bool postReadElementForeach();
634 bool postReadElementLog();
635 bool postReadElementDataModel();
636 bool postReadElementData();
637 bool postReadElementAssign();
638 bool postReadElementDoneData();
639 bool postReadElementContent();
640 bool postReadElementParam();
641 bool postReadElementScript();
642 bool postReadElementSend();
643 bool postReadElementCancel();
644 bool postReadElementInvoke();
645 bool postReadElementFinalize();
646
647 bool readElement();
648
649 void resetDocument();
650 void currentStateUp();
651 bool flushInstruction();
652
653private:
654 struct ParserState {
655 enum Kind {
656 Scxml,
657 State,
658 Parallel,
659 Transition,
660 Initial,
661 Final,
662 OnEntry,
663 OnExit,
664 History,
665 Raise,
666 If,
667 ElseIf,
668 Else,
669 Foreach,
670 Log,
671 DataModel,
672 Data,
673 Assign,
674 DoneData,
675 Content,
676 Param,
677 Script,
678 Send,
679 Cancel,
680 Invoke,
681 Finalize,
682 None
683 };
684 Kind kind;
685 QString chars;
686 DocumentModel::Instruction *instruction;
687 DocumentModel::InstructionSequence *instructionContainer;
688
689 bool collectChars();
690
691 ParserState(Kind someKind = None);
692 ~ParserState() { }
693
694 bool validChild(ParserState::Kind child) const;
695 static bool validChild(ParserState::Kind parent, ParserState::Kind child);
696 static bool isExecutableContent(ParserState::Kind kind);
697 static Kind nameToParserStateKind(const QStringRef &name);
698 static QStringList requiredAttributes(Kind kind);
699 static QStringList optionalAttributes(Kind kind);
700 };
701
702public:
703 class DefaultLoader: public QScxmlCompiler::Loader
704 {
705 public:
706 DefaultLoader();
707 QByteArray load(const QString &name,
708 const QString &baseDir,
709 QStringList *errors) override final;
710 };
711
712private:
713 bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlCompilerPrivate::ParserState::Kind kind);
714 ParserState &current();
715 ParserState &previous();
716 bool hasPrevious() const;
717
718private:
719 QString m_fileName;
720 QSet<QString> m_allIds;
721
722 QScopedPointer<DocumentModel::ScxmlDocument> m_doc;
723 DocumentModel::StateContainer *m_currentState;
724 DefaultLoader m_defaultLoader;
725 QScxmlCompiler::Loader *m_loader;
726
727 QXmlStreamReader *m_reader;
728 QVector<ParserState> m_stack;
729 QVector<QScxmlError> m_errors;
730};
731
732QT_END_NAMESPACE
733
734#endif // QSCXMLCOMPILER_P_H
735

source code of qtscxml/src/scxml/qscxmlcompiler_p.h