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 
64 QT_BEGIN_NAMESPACE
65 
66 namespace DocumentModel {
67 
68 struct XmlLocation
69 {
70     int line;
71     int column;
72 
XmlLocationXmlLocation73     XmlLocation(int theLine, int theColumn): line(theLine), column(theColumn) {}
74 };
75 
76 struct If;
77 struct Send;
78 struct Invoke;
79 struct Script;
80 struct AbstractState;
81 struct State;
82 struct Transition;
83 struct HistoryState;
84 struct Scxml;
85 class NodeVisitor;
86 struct Node {
87     XmlLocation xmlLocation;
88 
NodeNode89     Node(const XmlLocation &theLocation): xmlLocation(theLocation) {}
90     virtual ~Node();
91     virtual void accept(NodeVisitor *visitor) = 0;
92 
asIfNode93     virtual If *asIf() { return nullptr; }
asSendNode94     virtual Send *asSend() { return nullptr; }
asInvokeNode95     virtual Invoke *asInvoke() { return nullptr; }
asScriptNode96     virtual Script *asScript() { return nullptr; }
asStateNode97     virtual State *asState() { return nullptr; }
asTransitionNode98     virtual Transition *asTransition() { return nullptr; }
asHistoryStateNode99     virtual HistoryState *asHistoryState() { return nullptr; }
asScxmlNode100     virtual Scxml *asScxml() { return nullptr; }
101     AbstractState *asAbstractState();
102 
103 private:
104     Q_DISABLE_COPY(Node)
105 };
106 
107 struct DataElement: public Node
108 {
109     QString id;
110     QString src;
111     QString expr;
112     QString content;
113 
DataElementDataElement114     DataElement(const XmlLocation &xmlLocation): Node(xmlLocation) {}
115     void accept(NodeVisitor *visitor) override;
116 };
117 
118 struct Param: public Node
119 {
120     QString name;
121     QString expr;
122     QString location;
123 
ParamParam124     Param(const XmlLocation &xmlLocation): Node(xmlLocation) {}
125     void accept(NodeVisitor *visitor) override;
126 };
127 
128 struct DoneData: public Node
129 {
130     QString contents;
131     QString expr;
132     QVector<Param *> params;
133 
DoneDataDoneData134     DoneData(const XmlLocation &xmlLocation): Node(xmlLocation) {}
135     void accept(NodeVisitor *visitor) override;
136 };
137 
138 struct Instruction: public Node
139 {
InstructionInstruction140     Instruction(const XmlLocation &xmlLocation): Node(xmlLocation) {}
~InstructionInstruction141     virtual ~Instruction() {}
142 };
143 
144 typedef QVector<Instruction *> InstructionSequence;
145 typedef QVector<InstructionSequence *> InstructionSequences;
146 
147 struct 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 
SendSend164     Send(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
asSendSend165     Send *asSend() override { return this; }
166     void accept(NodeVisitor *visitor) override;
167 };
168 
169 struct ScxmlDocument;
170 struct 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 
InvokeInvoke185     Invoke(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
asInvokeInvoke186     Invoke *asInvoke() override { return this; }
187     void accept(NodeVisitor *visitor) override;
188 };
189 
190 struct Raise: public Instruction
191 {
192     QString event;
193 
RaiseRaise194     Raise(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
195     void accept(NodeVisitor *visitor) override;
196 };
197 
198 struct Log: public Instruction
199 {
200     QString label, expr;
201 
LogLog202     Log(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
203     void accept(NodeVisitor *visitor) override;
204 };
205 
206 struct Script: public Instruction
207 {
208     QString src;
209     QString content;
210 
ScriptScript211     Script(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
asScriptScript212     Script *asScript() override { return this; }
213     void accept(NodeVisitor *visitor) override;
214 };
215 
216 struct Assign: public Instruction
217 {
218     QString location;
219     QString expr;
220     QString content;
221 
AssignAssign222     Assign(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
223     void accept(NodeVisitor *visitor) override;
224 };
225 
226 struct If: public Instruction
227 {
228     QStringList conditions;
229     InstructionSequences blocks;
230 
IfIf231     If(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
asIfIf232     If *asIf() override { return this; }
233     void accept(NodeVisitor *visitor) override;
234 };
235 
236 struct Foreach: public Instruction
237 {
238     QString array;
239     QString item;
240     QString index;
241     InstructionSequence block;
242 
ForeachForeach243     Foreach(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
244     void accept(NodeVisitor *visitor) override;
245 };
246 
247 struct Cancel: public Instruction
248 {
249     QString sendid;
250     QString sendidexpr;
251 
CancelCancel252     Cancel(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
253     void accept(NodeVisitor *visitor) override;
254 };
255 
256 struct StateOrTransition: public Node
257 {
StateOrTransitionStateOrTransition258     StateOrTransition(const XmlLocation &xmlLocation): Node(xmlLocation) {}
259 };
260 
261 struct StateContainer
262 {
StateContainerStateContainer263     StateContainer()
264         : parent(nullptr)
265     {}
266 
267     StateContainer *parent;
268 
~StateContainerStateContainer269     virtual ~StateContainer() {}
270     virtual void add(StateOrTransition *s) = 0;
asAbstractStateStateContainer271     virtual AbstractState *asAbstractState() { return nullptr; }
asStateStateContainer272     virtual State *asState() { return nullptr; }
asScxmlStateContainer273     virtual Scxml *asScxml() { return nullptr; }
274 };
275 
276 struct AbstractState: public StateContainer
277 {
278     QString id;
279 
asAbstractStateAbstractState280     AbstractState *asAbstractState() override { return this; }
281 };
282 
283 struct 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 
StateState298     State(const XmlLocation &xmlLocation)
299         : StateOrTransition(xmlLocation)
300         , doneData(nullptr)
301         , type(Normal)
302         , initialTransition(nullptr)
303     {}
304 
addState305     void add(StateOrTransition *s) override
306     {
307         Q_ASSERT(s);
308         children.append(s);
309     }
310 
asStateState311     State *asState() override { return this; }
312 
313     void accept(NodeVisitor *visitor) override;
314 };
315 
316 struct 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 
TransitionTransition327     Transition(const XmlLocation &xmlLocation)
328         : StateOrTransition(xmlLocation)
329         , type(External)
330     {}
331 
asTransitionTransition332     Transition *asTransition() override { return this; }
333 
334     void accept(NodeVisitor *visitor) override;
335 };
336 
337 struct HistoryState: public AbstractState, public StateOrTransition
338 {
339     enum Type { Deep, Shallow };
340     Type type;
341     QVector<StateOrTransition *> children;
342 
HistoryStateHistoryState343     HistoryState(const XmlLocation &xmlLocation)
344         : StateOrTransition(xmlLocation)
345         , type(Shallow)
346     {}
347 
addHistoryState348     void add(StateOrTransition *s) override
349     {
350         Q_ASSERT(s);
351         children.append(s);
352     }
353 
defaultConfigurationHistoryState354     Transition *defaultConfiguration()
355     { return children.isEmpty() ? nullptr : children.first()->asTransition(); }
356 
asHistoryStateHistoryState357     HistoryState *asHistoryState() override { return this; }
358     void accept(NodeVisitor *visitor) override;
359 };
360 
361 struct 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 
ScxmlScxml386     Scxml(const XmlLocation &xmlLocation)
387         : Node(xmlLocation)
388         , dataModel(NullDataModel)
389         , binding(EarlyBinding)
390         , initialTransition(nullptr)
391     {}
392 
addScxml393     void add(StateOrTransition *s) override
394     {
395         Q_ASSERT(s);
396         children.append(s);
397     }
398 
asScxmlScxml399     Scxml *asScxml() override { return this; }
400 
401     void accept(NodeVisitor *visitor) override;
402 };
403 
404 struct 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 
ScxmlDocumentScxmlDocument415     ScxmlDocument(const QString &fileName)
416         : fileName(fileName)
417         , root(nullptr)
418         , isVerified(false)
419     {}
420 
~ScxmlDocumentScxmlDocument421     ~ScxmlDocument()
422     {
423         delete root;
424         qDeleteAll(allNodes);
425         qDeleteAll(allSequences);
426     }
427 
newStateScxmlDocument428     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(s);
435         parent->add(s);
436         return s;
437     }
438 
newHistoryStateScxmlDocument439     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(s);
445         parent->add(s);
446         return s;
447     }
448 
newTransitionScxmlDocument449     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(t);
455         }
456         return t;
457     }
458 
459     template<typename T>
newNodeScxmlDocument460     T *newNode(const XmlLocation &xmlLocation)
461     {
462         T *node = new T(xmlLocation);
463         allNodes.append(node);
464         return node;
465     }
466 
newSequenceScxmlDocument467     InstructionSequence *newSequence(InstructionSequences *container)
468     {
469         Q_ASSERT(container);
470         InstructionSequence *is = new InstructionSequence;
471         allSequences.append(is);
472         container->append(is);
473         return is;
474     }
475 };
476 
477 class Q_SCXML_EXPORT NodeVisitor
478 {
479 public:
480     virtual ~NodeVisitor();
481 
visit(DataElement *)482     virtual void visit(DataElement *) {}
visit(Param *)483     virtual void visit(Param *) {}
visit(DoneData *)484     virtual bool visit(DoneData *) { return true; }
endVisit(DoneData *)485     virtual void endVisit(DoneData *) {}
visit(Send *)486     virtual bool visit(Send *) { return true; }
endVisit(Send *)487     virtual void endVisit(Send *) {}
visit(Invoke *)488     virtual bool visit(Invoke *) { return true; }
endVisit(Invoke *)489     virtual void endVisit(Invoke *) {}
visit(Raise *)490     virtual void visit(Raise *) {}
visit(Log *)491     virtual void visit(Log *) {}
visit(Script *)492     virtual void visit(Script *) {}
visit(Assign *)493     virtual void visit(Assign *) {}
visit(If *)494     virtual bool visit(If *) { return true; }
endVisit(If *)495     virtual void endVisit(If *) {}
visit(Foreach *)496     virtual bool visit(Foreach *) { return true; }
endVisit(Foreach *)497     virtual void endVisit(Foreach *) {}
visit(Cancel *)498     virtual void visit(Cancel *) {}
visit(State *)499     virtual bool visit(State *) { return true; }
endVisit(State *)500     virtual void endVisit(State *) {}
visit(Transition *)501     virtual bool visit(Transition *) { return true; }
endVisit(Transition *)502     virtual void endVisit(Transition *) {}
visit(HistoryState *)503     virtual bool visit(HistoryState *) { return true; }
endVisit(HistoryState *)504     virtual void endVisit(HistoryState *) {}
visit(Scxml *)505     virtual bool visit(Scxml *) { return true; }
endVisit(Scxml *)506     virtual void endVisit(Scxml *) {}
507 
visit(InstructionSequence * sequence)508     void visit(InstructionSequence *sequence)
509     {
510         Q_ASSERT(sequence);
511         for (Instruction *instruction : qAsConst(*sequence)) {
512             Q_ASSERT(instruction);
513             instruction->accept(this);
514         }
515     }
516 
visit(const QVector<DataElement * > & dataElements)517     void visit(const QVector<DataElement *> &dataElements)
518     {
519         for (DataElement *dataElement : dataElements) {
520             Q_ASSERT(dataElement);
521             dataElement->accept(this);
522         }
523     }
524 
visit(const QVector<StateOrTransition * > & children)525     void visit(const QVector<StateOrTransition *> &children)
526     {
527         for (StateOrTransition *child : children) {
528             Q_ASSERT(child);
529             child->accept(this);
530         }
531     }
532 
visit(const InstructionSequences & sequences)533     void visit(const InstructionSequences &sequences)
534     {
535         for (InstructionSequence *sequence : sequences) {
536             Q_ASSERT(sequence);
537             visit(sequence);
538         }
539     }
540 
visit(const QVector<Param * > & params)541     void visit(const QVector<Param *> &params)
542     {
543         for (Param *param : params) {
544             Q_ASSERT(param);
545             param->accept(this);
546         }
547     }
548 };
549 
550 } // DocumentModel namespace
551 
552 class Q_SCXML_EXPORT QScxmlCompilerPrivate
553 {
554 public:
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 
584 private:
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 
653 private:
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);
~ParserStateParserState692         ~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 
702 public:
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 
712 private:
713     bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlCompilerPrivate::ParserState::Kind kind);
714     ParserState &current();
715     ParserState &previous();
716     bool hasPrevious() const;
717 
718 private:
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 
732 QT_END_NAMESPACE
733 
734 #endif // QSCXMLCOMPILER_P_H
735