#ifndef RUNTIME_H_SQ1MBKGN #define RUNTIME_H_SQ1MBKGN #include "uscxml/Common.h" #include "uscxml/URL.h" #include #include #include #include #include #include #include #include #include #include #include "uscxml/concurrency/tinythread.h" #include "uscxml/concurrency/eventqueue/DelayedEventQueue.h" #include "uscxml/concurrency/BlockingQueue.h" #include "uscxml/Message.h" #include "uscxml/Factory.h" #define ORIG_ENTERSTATES namespace uscxml { class HTTPServletInvoker; class InterpreterMonitor { public: virtual ~InterpreterMonitor() {} virtual void onStableConfiguration(Interpreter* interpreter) {} virtual void beforeCompletion(Interpreter* interpreter) {} virtual void afterCompletion(Interpreter* interpreter) {} virtual void beforeMicroStep(Interpreter* interpreter) {} virtual void beforeTakingTransitions(Interpreter* interpreter, const Arabica::XPath::NodeSet& transitions) {} virtual void beforeEnteringStates(Interpreter* interpreter, const Arabica::XPath::NodeSet& statesToEnter) {} virtual void afterEnteringStates(Interpreter* interpreter) {} virtual void beforeExitingStates(Interpreter* interpreter, const Arabica::XPath::NodeSet& statesToExit) {} virtual void afterExitingStates(Interpreter* interpreter) {} }; class NumAttr { public: NumAttr(const std::string& str) { size_t valueStart = str.find_first_of("0123456789."); if (valueStart != std::string::npos) { size_t valueEnd = str.find_last_of("0123456789."); if (valueEnd != std::string::npos) { value = str.substr(valueStart, (valueEnd - valueStart) + 1); size_t unitStart = str.find_first_not_of(" \t", valueEnd + 1); if (unitStart != std::string::npos) { size_t unitEnd = str.find_last_of(" \t"); if (unitEnd != std::string::npos && unitEnd > unitStart) { unit = str.substr(unitStart, unitEnd - unitStart); } else { unit = str.substr(unitStart, str.length() - unitStart); } } } } } std::string value; std::string unit; }; class Interpreter : protected Arabica::SAX2DOM::Parser { public: enum Binding { EARLY = 0, LATE = 1 }; enum Capabilities { CAN_NOTHING = 0, CAN_BASIC_HTTP = 1, CAN_GENERIC_HTTP = 2, }; virtual ~Interpreter(); static Interpreter* fromDOM(const Arabica::DOM::Node& node); static Interpreter* fromXML(const std::string& xml); static Interpreter* fromURI(const std::string& uri); static Interpreter* fromInputSource(Arabica::SAX::InputSource& source); virtual void startPrefixMapping(const std::string& /* prefix */, const std::string& /* uri */); void start(); static void run(void*); void join() { if (_thread != NULL) _thread->join(); }; void interpret(); void addMonitor(InterpreterMonitor* monitor) { _monitors.insert(monitor); } void removeMonitor(InterpreterMonitor* monitor) { _monitors.erase(monitor); } void setBaseURI(std::string baseURI) { _baseURI = URL(baseURI); } URL getBaseURI() { return _baseURI; } bool toAbsoluteURI(URL& uri); void setCmdLineOptions(int argc, char** argv); Data getCmdLineOptions() { return _cmdLineOptions; } HTTPServletInvoker* getHTTPServlet() { return _httpServlet; } DataModel getDataModel() { return _dataModel; } void setParentQueue(uscxml::concurrency::BlockingQueue* parentQueue) { _parentQueue = parentQueue; } std::string getXPathPrefix() { return _xpathPrefix; } std::string getXMLPrefix() { return _xmlNSPrefix; } Arabica::XPath::StandardNamespaceContext& getNSContext() { return _nsContext; } std::string getXMLPrefixForNS(const std::string& ns) { if (_nsToPrefix.find(ns) != _nsToPrefix.end()) return _nsToPrefix[ns] + ":"; return ""; } void receive(const Event& event, bool toFront = false) { if (toFront) { _externalQueue.push_front(event); } else { _externalQueue.push(event); } } void receiveInternal(const Event& event) { _internalQueue.push_back(event); } Event getCurrentEvent() { return _currEvent; } Arabica::XPath::NodeSet getConfiguration() { return _configuration; } Arabica::DOM::Node getState(const std::string& stateId); Arabica::DOM::Document& getDocument() { return _document; } void setCapabilities(unsigned int capabilities) { _capabilities = capabilities; } void setName(const std::string& name); const std::string& getName() { return _name; } const std::string& getSessionId() { return _sessionId; } bool runOnMainThread(int fps, bool blocking = true); static bool isMember(const Arabica::DOM::Node& node, const Arabica::XPath::NodeSet& set); void dump(); bool hasLegalConfiguration(); static bool isState(const Arabica::DOM::Node& state); static bool isPseudoState(const Arabica::DOM::Node& state); static bool isTransitionTarget(const Arabica::DOM::Node& elem); static bool isTargetless(const Arabica::DOM::Node& transition); static bool isAtomic(const Arabica::DOM::Node& state); static bool isFinal(const Arabica::DOM::Node& state); static bool isHistory(const Arabica::DOM::Node& state); static bool isParallel(const Arabica::DOM::Node& state); static bool isCompound(const Arabica::DOM::Node& state); static bool isDescendant(const Arabica::DOM::Node& s1, const Arabica::DOM::Node& s2); bool isInitial(const Arabica::DOM::Node& state); Arabica::DOM::Node getInitialState(Arabica::DOM::Node state = Arabica::DOM::Node()); static Arabica::XPath::NodeSet getChildStates(const Arabica::DOM::Node& state); Arabica::XPath::NodeSet getTargetStates(const Arabica::DOM::Node& transition); static Arabica::XPath::NodeSet filterChildElements(const std::string& tagname, const Arabica::DOM::Node& node); static Arabica::XPath::NodeSet filterChildElements(const std::string& tagName, const Arabica::XPath::NodeSet& nodeSet); static const std::string getUUID(); protected: Interpreter(); void init(); void normalize(const Arabica::DOM::Document& node); void setupIOProcessors(); void mainEventLoop(); bool _stable; tthread::thread* _thread; tthread::mutex _mutex; URL _baseURI; Arabica::DOM::Document _document; Arabica::DOM::Element _scxml; Arabica::XPath::XPath _xpath; Arabica::XPath::StandardNamespaceContext _nsContext; std::string _xmlNSPrefix; // the actual prefix for elements in the xml file std::string _xpathPrefix; // prefix mapped for xpath, "scxml" is _xmlNSPrefix is empty but _nsURL set std::string _nsURL; // ough to be "http://www.w3.org/2005/07/scxml" std::map _nsToPrefix; bool _running; bool _done; bool _isInitialized; Binding _binding; Arabica::XPath::NodeSet _configuration; Arabica::XPath::NodeSet _statesToInvoke; DataModel _dataModel; std::map > _historyValue; std::list _internalQueue; uscxml::concurrency::BlockingQueue _externalQueue; uscxml::concurrency::BlockingQueue* _parentQueue; DelayedEventQueue* _sendQueue; Event _currEvent; HTTPServletInvoker* _httpServlet; std::set _monitors; static URL toBaseURI(const URL& url); void microstep(const Arabica::XPath::NodeSet& enabledTransitions); void executeTransitionContent(const Arabica::XPath::NodeSet& enabledTransitions); void executeContent(const Arabica::DOM::Node& content); void executeContent(const Arabica::DOM::NodeList& content); void executeContent(const Arabica::XPath::NodeSet& content); void initializeData(const Arabica::DOM::Node& data); void exitInterpreter(); #ifdef ORIG_ENTERSTATES void enterStates(const Arabica::XPath::NodeSet& enabledTransitions); void addStatesToEnter(const Arabica::DOM::Node& state, Arabica::XPath::NodeSet& statesToEnter, Arabica::XPath::NodeSet& statesForDefaultEntry); #endif #ifdef ENTERSTATES_02_2013 void enterStates(const Arabica::XPath::NodeSet& enabledTransitions); void computeEntrySet(const Arabica::XPath::NodeSet& transitions, Arabica::XPath::NodeSet& statesToEnter, Arabica::XPath::NodeSet& statesForDefaultEntry); void addDescendentStatesToEnter(const Arabica::DOM::Node& state, Arabica::XPath::NodeSet& statesToEnter, Arabica::XPath::NodeSet& statesForDefaultEntry); void addAncestorStatesToEnter(const Arabica::DOM::Node& state, const Arabica::DOM::Node& ancestor, Arabica::XPath::NodeSet& statesToEnter, Arabica::XPath::NodeSet& statesForDefaultEntry); Arabica::DOM::Node getTransitionDomain(const Arabica::DOM::Node& transition); #endif void exitStates(const Arabica::XPath::NodeSet& enabledTransitions); Arabica::XPath::NodeSet selectEventlessTransitions(); Arabica::XPath::NodeSet selectTransitions(const std::string& event); Arabica::DOM::Node getSourceState(const Arabica::DOM::Node& transition); Arabica::DOM::Node findLCCA(const Arabica::XPath::NodeSet& states); Arabica::XPath::NodeSet getProperAncestors(const Arabica::DOM::Node& s1, const Arabica::DOM::Node& s2); void send(const Arabica::DOM::Node& element); void invoke(const Arabica::DOM::Node& element); void cancelInvoke(const Arabica::DOM::Node& element); void returnDoneEvent(const Arabica::DOM::Node& state); void internalDoneSend(const Arabica::DOM::Node& state); static void delayedSend(void* userdata, std::string eventName); static bool nameMatch(const std::string& transitionEvent, const std::string& event); Arabica::XPath::NodeSet filterPreempted(const Arabica::XPath::NodeSet& enabledTransitions); bool isPreemptingTransition(const Arabica::DOM::Node& t1, const Arabica::DOM::Node& t2); bool isWithinSameChild(const Arabica::DOM::Node& transition); bool hasConditionMatch(const Arabica::DOM::Node& conditional); bool isInFinalState(const Arabica::DOM::Node& state); bool parentIsScxmlState(Arabica::DOM::Node state); // Arabica::DOM::Node getTransitionSubgraph(const Arabica::DOM::Node& transition); static std::vector tokenizeIdRefs(const std::string& idRefs); static boost::uuids::random_generator uuidGen; long _lastRunOnMainThread; std::string _name; std::string _sessionId; unsigned int _capabilities; Data _cmdLineOptions; IOProcessor getIOProcessor(const std::string& type); // IOProcessor* getIOProcessorForId(const std::string& sendId); std::map _ioProcessors; std::map > _sendIds; std::map _invokers; std::map _autoForwardees; std::map, ExecutableContent> _executableContent; /// We need to remember to adapt them when the DOM is operated upon std::map > _cachedStates; std::map _cachedURLs; }; } #endif