diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bindings/swig/php/uscxml.i | 1 | ||||
-rw-r--r-- | src/uscxml/Interpreter.cpp | 125 | ||||
-rw-r--r-- | src/uscxml/Interpreter.h | 12 | ||||
-rw-r--r-- | src/uscxml/Message.cpp | 2 | ||||
-rw-r--r-- | src/uscxml/Message.h | 200 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft6.cpp | 28 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft6.h | 6 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft7.cpp | 100 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft7.h | 16 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp | 126 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h | 2 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeList.h | 2 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeListCustom.cpp | 52 |
13 files changed, 458 insertions, 214 deletions
diff --git a/src/bindings/swig/php/uscxml.i b/src/bindings/swig/php/uscxml.i index 6180d05..ba58678 100644 --- a/src/bindings/swig/php/uscxml.i +++ b/src/bindings/swig/php/uscxml.i @@ -31,6 +31,7 @@ void*** tsrm_ls; %feature("director") uscxml::InterpreterMonitor; %ignore uscxml::NumAttr; +%ignore uscxml::SCXMLParser; //*********************************************** // Parse the header file to generate wrappers diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index e59ef5f..089406d 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -108,7 +108,7 @@ Interpreter* Interpreter::fromURI(const std::string& uri) { Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& source) { Interpreter* interpreter = new InterpreterDraft6(); - + SCXMLParser* parser = new SCXMLParser(interpreter); if(!parser->parse(source) || !parser->getDocument().hasChildNodes()) { if(parser->_errorHandler.errorsReported()) { @@ -260,24 +260,24 @@ void Interpreter::init() { NodeList<std::string> scxmls = _document.getElementsByTagNameNS(_nsURL, "scxml"); if (scxmls.getLength() > 0) { _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0); - + // setup xpath and check that it works _xpath.setNamespaceContext(_nsContext); Arabica::XPath::NodeSet<std::string> scxmls = _xpath.evaluate("/" + _xpathPrefix + "scxml", _document).asNodeSet(); assert(scxmls.size() > 0); assert(scxmls[0] == _scxml); - + if (_name.length() == 0) _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID()); - + normalize(_document); - + if (_capabilities & CAN_GENERIC_HTTP) _httpServlet = new InterpreterServlet(this); - + _sendQueue = new DelayedEventQueue(); _sendQueue->start(); - + } else { LOG(ERROR) << "Cannot find SCXML element" << std::endl; } @@ -552,6 +552,7 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) { if (contents.size() > 0) { if (HAS_ATTR(contents[0], "expr")) { if (_dataModel) { + /// this is out of spec std::string contentValue = _dataModel.evalAsString(ATTR(contents[0], "expr")); sendReq.content = contentValue; // sendReq.data.atom = contentValue; @@ -560,9 +561,50 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) { LOG(ERROR) << "content element has expr attribute but no datamodel is specified."; } } else if (contents[0].hasChildNodes()) { - sendReq.content = contents[0].getFirstChild().getNodeValue(); -// sendReq.data.atom = sendReq.content; -// sendReq.data.type = Data::VERBATIM; + bool presentAsDOM = false; + NodeList<std::string> contentChilds = contents[0].getChildNodes(); + for (int i = 0; i < contentChilds.getLength(); i++) { + if (contentChilds.item(i).getNodeType() == Node_base::ELEMENT_NODE) { + presentAsDOM = true; + break; + } + } + if (presentAsDOM) { + // use the whole dom + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + sendReq.dom = domFactory.createDocument(contents[0].getNamespaceURI(), "", 0); + for (int i = 0; i < contentChilds.getLength(); i++) { + try { + Node<std::string> newNode = sendReq.dom.importNode(contentChilds.item(i), true); + sendReq.dom.appendChild(newNode); + } catch (...) { + + } + } + std::cout << sendReq.dom << std::endl; + } else { + Node<std::string> textChild = contents[0].getFirstChild(); + while(textChild && textChild.getNodeType() != Node_base::TEXT_NODE) { + textChild = textChild.getNextSibling(); + } + if (textChild && textChild.getNodeType() == Node_base::TEXT_NODE) { + /// create space normalized string + std::istringstream iss(contents[0].getFirstChild().getNodeValue()); + std::stringstream content; + std::string seperator; + do { + std::string token; + iss >> token; + if (token.length() > 0) { + content << seperator << token; + seperator = " "; + } + } while (iss); + sendReq.content = content.str(); + } else { + LOG(ERROR) << "content element has neither text nor element children."; + } + } } else { LOG(ERROR) << "content element does not specify any content."; } @@ -587,6 +629,7 @@ void Interpreter::delayedSend(void* userdata, std::string eventName) { Interpreter* INSTANCE = data->first; SendRequest sendReq = data->second; + // see http://www.w3.org/TR/scxml/#SendTargets if (boost::iequals(sendReq.target, "#_parent")) { // send to parent scxml session if (INSTANCE->_parentQueue != NULL) { @@ -611,7 +654,12 @@ void Interpreter::delayedSend(void* userdata, std::string eventName) { LOG(ERROR) << "Can not send to invoked component '" << invokeId << "', no such invokeId" << std::endl; } } else if (sendReq.target.length() == 0) { - INSTANCE->receive(sendReq); + /** + * If neither the 'target' nor the 'targetexpr' attribute is specified, the + * SCXML Processor must add the event will be added to the external event + * queue of the sending session. + */ + INSTANCE->_externalQueue.push(sendReq); } else { IOProcessor ioProc = INSTANCE->getIOProcessor(sendReq.type); if (ioProc) { @@ -628,7 +676,11 @@ void Interpreter::delayedSend(void* userdata, std::string eventName) { void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { InvokeRequest invokeReq; - invokeReq.dom = element; + + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + invokeReq.dom = domFactory.createDocument(element.getNamespaceURI(), "", 0); + Node<std::string> newNode = invokeReq.dom.importNode(element, true); + invokeReq.dom.appendChild(newNode); try { // type @@ -891,7 +943,7 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content) if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elseif") || - boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else")) { + boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else")) { if (blockIsTrue) { // last block was true, break here break; @@ -917,25 +969,34 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content) std::string array = ATTR(content, "array"); std::string item = ATTR(content, "item"); std::string index = (HAS_ATTR(content, "index") ? ATTR(content, "index") : ""); - uint32_t iterations = _dataModel.getLength(array); - _dataModel.pushContext(); // copy old and enter new context - for (uint32_t iteration = 0; iteration < iterations; iteration++) { - { - // assign array element to item - std::stringstream ss; - ss << array << "[" << iteration << "]"; - _dataModel.assign(item, ss.str()); - } - if (index.length() > 0) { - // assign iteration element to index - std::stringstream ss; - ss << iteration; - _dataModel.assign(index,ss.str()); + uint32_t iterations = 0; + try { + iterations = _dataModel.getLength(array); + try { + _dataModel.pushContext(); // copy old and enter new context + for (uint32_t iteration = 0; iteration < iterations; iteration++) { + { + // assign array element to item + std::stringstream ss; + ss << array << "[" << iteration << "]"; + _dataModel.assign(item, ss.str()); + } + if (index.length() > 0) { + // assign iteration element to index + std::stringstream ss; + ss << iteration; + _dataModel.assign(index,ss.str()); + } + if (content.hasChildNodes()) + executeContent(content.getChildNodes()); + } + _dataModel.popContext(); // leave stacked context + } catch (Event e) { + LOG(ERROR) << "Syntax error in foreach element:" << std::endl << e << std::endl; } - if (content.hasChildNodes()) - executeContent(content.getChildNodes()); + } catch (Event e) { + LOG(ERROR) << "Syntax error in array attribute of foreach element:" << std::endl << e << std::endl; } - _dataModel.popContext(); // leave stacked context } else { LOG(ERROR) << "Expected array and item attributes with foreach element!" << std::endl; } @@ -1225,7 +1286,7 @@ Arabica::XPath::NodeSet<std::string> Interpreter::getInitialStates(Arabica::DOM: assert(isCompound(state) || isParallel(state)); Arabica::XPath::NodeSet<std::string> initialStates; - + // initial attribute at element Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state; if (stateElem.hasAttribute("initial")) { @@ -1233,7 +1294,7 @@ Arabica::XPath::NodeSet<std::string> Interpreter::getInitialStates(Arabica::DOM: } Arabica::XPath::NodeSet<std::string> initStates; - + // initial element as child - but not the implicit generated one NodeSet<std::string> initElems = filterChildElements(_xmlNSPrefix + "initial", state); if(initElems.size() == 1 && !boost::iequals(ATTR(initElems[0], "generated"), "true")) { diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 353233d..5d45e4c 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -77,7 +77,7 @@ public: Arabica::SAX::CatchErrorHandler<std::string> _errorHandler; Interpreter* _interpreter; }; - + class Interpreter { public: enum Binding { @@ -176,10 +176,10 @@ public: void setConfiguration(const std::vector<std::string>& states) { _userDefinedStartConfiguration = states; } - + Arabica::DOM::Node<std::string> getState(const std::string& stateId); Arabica::XPath::NodeSet<std::string> getStates(const std::vector<std::string>& stateIds); - + Arabica::DOM::Document<std::string>& getDocument() { return _document; } @@ -213,7 +213,7 @@ public: static bool isParallel(const Arabica::DOM::Node<std::string>& state); static bool isCompound(const Arabica::DOM::Node<std::string>& state); static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2); - + static std::vector<std::string> tokenizeIdRefs(const std::string& idRefs); bool isInitial(const Arabica::DOM::Node<std::string>& state); @@ -256,7 +256,7 @@ protected: Arabica::XPath::NodeSet<std::string> _configuration; Arabica::XPath::NodeSet<std::string> _statesToInvoke; std::vector<std::string> _userDefinedStartConfiguration; - + DataModel _dataModel; std::map<std::string, Arabica::XPath::NodeSet<std::string> > _historyValue; @@ -309,7 +309,7 @@ protected: /// TODO: We need to remember to adapt them when the DOM is operated upon std::map<std::string, Arabica::DOM::Node<std::string> > _cachedStates; std::map<std::string, URL> _cachedURLs; - + friend class SCXMLParser; }; diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index 845118b..3ada20c 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -182,7 +182,7 @@ Data Data::fromJSON(const std::string& jsonString) { if (jsonString.length() == 0) return data; - + jsmn_parser p; jsmntok_t* t = NULL; diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index 2a49264..176ae86 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -40,19 +40,35 @@ public: return ss.str(); } - std::map<std::string, Data> getCompund() { return compound; } - void setCompound(const std::map<std::string, Data>& compound) { this->compound = compound; } + std::map<std::string, Data> getCompund() { + return compound; + } + void setCompound(const std::map<std::string, Data>& compound) { + this->compound = compound; + } - std::list<Data> getArray() { return array; } - void setArray(const std::list<Data>& array) { this->array = array; } + std::list<Data> getArray() { + return array; + } + void setArray(const std::list<Data>& array) { + this->array = array; + } + + std::string getAtom() { + return atom; + } + void setAtom(const std::string& atom) { + this->atom = atom; + } - std::string getAtom() { return atom; } - void setAtom(const std::string& atom) { this->atom = atom; } + Type getType() { + return type; + } + void setType(const Type type) { + this->type = type; + } - Type getType() { return type; } - void setType(const Type type) { this->type = type; } - #ifdef SWIGIMPORTED protected: #endif @@ -84,29 +100,68 @@ public: return this < &other; } - std::string getName() { return name; } - void setName(const std::string& name) { this->name = name; } + std::string getName() { + return name; + } + void setName(const std::string& name) { + this->name = name; + } + + Type getType() { + return type; + } + void setType(const Type type) { + this->type = type; + } - Type getType() { return type; } - void setType(const Type type) { this->type = type; } + std::string getOrigin() { + return origin; + } + void setOrigin(const std::string& origin) { + this->origin = origin; + } - std::string getOrigin() { return origin; } - void setOrigin(const std::string& origin) { this->origin = origin; } + std::string getOriginType() { + return origintype; + } + void setOriginType(const std::string& originType) { + this->origintype = originType; + } - std::string getOriginType() { return origintype; } - void setOriginType(const std::string& originType) { this->origintype = originType; } + Arabica::DOM::Document<std::string> getDOM() { + return dom; + } + void setDOM(const Arabica::DOM::Document<std::string>& dom) { + this->dom = dom; + } - Arabica::DOM::Node<std::string> getDOM() { return dom; } - void setDOM(const Arabica::DOM::Node<std::string>& dom) { this->dom = dom; } + std::string getContent() { + return content; + } + void setContent(const std::string& content) { + this->content = content; + } - std::string getSendId() { return sendid; } - void setSendId(const std::string& sendId) { this->sendid = sendId; } + std::string getSendId() { + return sendid; + } + void setSendId(const std::string& sendId) { + this->sendid = sendId; + } - std::string getInvokeId() { return invokeid; } - void setInvokeId(const std::string& invokeId) { this->invokeid = invokeId; } + std::string getInvokeId() { + return invokeid; + } + void setInvokeId(const std::string& invokeId) { + this->invokeid = invokeId; + } - Data getData() { return data; } - void setData(const Data& invokeId) { this->data = data; } + Data getData() { + return data; + } + void setData(const Data& invokeId) { + this->data = data; + } static Event fromXML(const std::string& xmlString); Arabica::DOM::Document<std::string> toDocument(); @@ -124,10 +179,11 @@ protected: Type type; std::string origin; std::string origintype; - Arabica::DOM::Node<std::string> dom; + Arabica::DOM::Document<std::string> dom; std::string sendid; std::string invokeid; Data data; + std::string content; #ifndef SWIG friend std::ostream& operator<< (std::ostream& os, const Event& event); @@ -138,22 +194,33 @@ class InvokeRequest : public Event { public: InvokeRequest(Event event) : Event(event) {} InvokeRequest() {} - - std::string getType() { return type; } - void setType(const std::string& type) { this->type = type; } - std::string getSource() { return src; } - void setSource(const std::string& src) { this->src = src; } + std::string getType() { + return type; + } + void setType(const std::string& type) { + this->type = type; + } - std::string getContent() { return content; } - void setContent(const std::string& content) { this->content = content; } - - bool isAutoForwarded() { return autoForward; } - void setAutoForwarded(bool autoForward) { this->autoForward = autoForward; } + std::string getSource() { + return src; + } + void setSource(const std::string& src) { + this->src = src; + } + + bool isAutoForwarded() { + return autoForward; + } + void setAutoForwarded(bool autoForward) { + this->autoForward = autoForward; + } #ifdef SWIG /// TODO: Do we want to set namelist and params as well? - std::map<std::string, std::string> getNameList() { return namelist; } + std::map<std::string, std::string> getNameList() { + return namelist; + } const std::vector<std::string> getNameListKeys() { std::set<std::string> keys; @@ -175,7 +242,7 @@ public: } return paramsMap; } - + const std::vector<std::string> getParamKeys() { std::set<std::string> keys; params_t::iterator paramIter = params.begin(); @@ -185,10 +252,14 @@ public: } return std::vector<std::string>(keys.begin(), keys.end()); } - + #else - std::map<std::string, std::string>& getNameList() { return namelist; } - std::multimap<std::string, std::string>& getParams() { return params; } + std::map<std::string, std::string>& getNameList() { + return namelist; + } + std::multimap<std::string, std::string>& getParams() { + return params; + } #endif static InvokeRequest fromXML(const std::string& xmlString); @@ -204,7 +275,6 @@ protected: #endif std::string type; std::string src; - std::string content; bool autoForward; std::map<std::string, std::string> namelist; std::multimap<std::string, std::string> params; @@ -223,21 +293,32 @@ public: SendRequest() {} SendRequest(Event event) : Event(event) {} - std::string getTarget() { return target; } - void setTarget(const std::string& target) { this->target = target; } + std::string getTarget() { + return target; + } + void setTarget(const std::string& target) { + this->target = target; + } - std::string getType() { return type; } - void setType(const std::string& type) { this->type = type; } + std::string getType() { + return type; + } + void setType(const std::string& type) { + this->type = type; + } - uint32_t getDelayMs() { return delayMs; } - void setDelayMs(uint32_t delayMs) { this->delayMs = delayMs; } - - std::string getContent() { return content; } - void setContent(const std::string& content) { this->content = content; } + uint32_t getDelayMs() { + return delayMs; + } + void setDelayMs(uint32_t delayMs) { + this->delayMs = delayMs; + } #ifdef SWIG /// TODO: Do we want to set namelist and params as well? - std::map<std::string, std::string> getNameList() { return namelist; } + std::map<std::string, std::string> getNameList() { + return namelist; + } const std::vector<std::string> getNameListKeys() { std::set<std::string> keys; @@ -259,7 +340,7 @@ public: } return paramsMap; } - + const std::vector<std::string> getParamKeys() { std::set<std::string> keys; params_t::iterator paramIter = params.begin(); @@ -269,10 +350,14 @@ public: } return std::vector<std::string>(keys.begin(), keys.end()); } - + #else - std::map<std::string, std::string>& getNameList() { return namelist; } - std::multimap<std::string, std::string>& getParams() { return params; } + std::map<std::string, std::string>& getNameList() { + return namelist; + } + std::multimap<std::string, std::string>& getParams() { + return params; + } #endif static SendRequest fromXML(const std::string& xmlString); @@ -290,11 +375,10 @@ protected: std::string target; std::string type; uint32_t delayMs; - std::string content; std::map<std::string, std::string> namelist; std::multimap<std::string, std::string> params; - + typedef std::map<std::string, std::string> namelist_t; typedef std::multimap<std::string, std::string> params_t; diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index d95ae4c..18a42fd 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -67,12 +67,12 @@ void InterpreterDraft6::interpret() { } NodeSet<std::string> initialTransitions; - + if (_userDefinedStartConfiguration.size() == 0) { // try to get initial transition form initial element - initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet(); + initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet(); } - + if (initialTransitions.size() == 0) { Arabica::XPath::NodeSet<std::string> initialStates; if (_userDefinedStartConfiguration.size() > 0) { @@ -82,7 +82,7 @@ void InterpreterDraft6::interpret() { // or fetch per draft initialStates = getInitialStates(); } - + assert(initialStates.size() > 0); for (int i = 0; i < initialStates.size(); i++) { Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); @@ -94,18 +94,18 @@ void InterpreterDraft6::interpret() { initialTransitions.push_back(transitionElem); } } - + assert(initialTransitions.size() > 0); enterStates(initialTransitions); - - assert(hasLegalConfiguration()); + +// assert(hasLegalConfiguration()); mainEventLoop(); if (_parentQueue) { - // send one final event to unblock eventual listeners - Event quit; - quit.name = "done.state.scxml"; - _parentQueue->push(quit); + // send one final event to unblock eventual listeners + Event quit; + quit.name = "done.state.scxml"; + _parentQueue->push(quit); } // set datamodel to null from this thread @@ -234,7 +234,7 @@ void InterpreterDraft6::mainEventLoop() { continue; // assume that we have a legal configuration as soon as the internal queue is empty - assert(hasLegalConfiguration()); + assert(hasLegalConfiguration()); monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { @@ -844,8 +844,8 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& } void InterpreterDraft6::addStatesToEnter(const Arabica::DOM::Node<std::string>& state, - Arabica::XPath::NodeSet<std::string>& statesToEnter, - Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { std::string stateId = ((Arabica::DOM::Element<std::string>)state).getAttribute("id"); #if VERBOSE diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h index 10ff10a..476da05 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.h +++ b/src/uscxml/interpreter/InterpreterDraft6.h @@ -8,17 +8,17 @@ namespace uscxml { class InterpreterDraft6 : public Interpreter { void interpret(); void mainEventLoop(); - + void initializeData(const Arabica::DOM::Node<std::string>& data); void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void addStatesToEnter(const Arabica::DOM::Node<std::string>& state, Arabica::XPath::NodeSet<std::string>& statesToEnter, Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); - + void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void exitInterpreter(); - + Arabica::XPath::NodeSet<std::string> selectEventlessTransitions(); Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event); Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); diff --git a/src/uscxml/interpreter/InterpreterDraft7.cpp b/src/uscxml/interpreter/InterpreterDraft7.cpp index 9542146..7af2d39 100644 --- a/src/uscxml/interpreter/InterpreterDraft7.cpp +++ b/src/uscxml/interpreter/InterpreterDraft7.cpp @@ -27,12 +27,12 @@ procedure interpret(doc): void InterpreterDraft7::interpret() { if (!_isInitialized) init(); - + if (!_scxml) return; - + _sessionId = getUUID(); - + std::string datamodelName; if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) datamodelName = ATTR(_scxml, "datamodel"); @@ -43,7 +43,7 @@ void InterpreterDraft7::interpret() { if(datamodelName.length() > 0 && !_dataModel) { LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; } - + if (_dataModel) { _dataModel.assign("_x.args", _cmdLineOptions); if (_httpServlet) { @@ -52,14 +52,14 @@ void InterpreterDraft7::interpret() { _dataModel.assign("_ioprocessors['http']", data); } } - + setupIOProcessors(); - + _running = true; _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - + // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - + if (_dataModel && _binding == EARLY) { // initialize all data elements NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet(); @@ -73,14 +73,14 @@ void InterpreterDraft7::interpret() { initializeData(topDataElems[i]); } } - + // executeGlobalScriptElements NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet(); for (unsigned int i = 0; i < globalScriptElems.size(); i++) { if (_dataModel) executeContent(globalScriptElems[i]); } - + // initial transition might be implict NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet(); if (initialTransitions.size() == 0) { @@ -97,21 +97,21 @@ void InterpreterDraft7::interpret() { } } enterStates(initialTransitions); - + // assert(hasLegalConfiguration()); mainEventLoop(); - + if (_parentQueue) { // send one final event to unblock eventual listeners Event quit; quit.name = "done.state.scxml"; _parentQueue->push(quit); } - + // set datamodel to null from this thread if(_dataModel) _dataModel = DataModel(); - + } /** @@ -127,7 +127,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da LOG(ERROR) << "Data element has no id!"; return; } - + if (HAS_ATTR(data, "expr")) { std::string value = ATTR(data, "expr"); _dataModel.assign(ATTR(data, "id"), value); @@ -135,7 +135,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da URL srcURL(ATTR(data, "src")); if (!srcURL.isAbsolute()) toAbsoluteURI(srcURL); - + std::stringstream ss; if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) { ss << _cachedURLs[srcURL.asString()]; @@ -144,7 +144,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da _cachedURLs[srcURL.asString()] = srcURL; } _dataModel.assign(ATTR(data, "id"), ss.str()); - + } else if (data.hasChildNodes()) { // search for the text node with the actual script NodeList<std::string> dataChilds = data.getChildNodes(); @@ -156,7 +156,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da } } } - + } catch (Event e) { LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl; } @@ -167,12 +167,12 @@ procedure mainEventLoop(): while running: enabledTransitions = null macrostepDone = false - # Here we handle eventless transitions and transitions + # Here we handle eventless transitions and transitions # triggered by internal events until macrostep is complete while running and not macrostepDone: enabledTransitions = selectEventlessTransitions() if enabledTransitions.isEmpty(): - if internalQueue.isEmpty(): + if internalQueue.isEmpty(): macrostepDone = true else: internalEvent = internalQueue.dequeue() @@ -180,7 +180,7 @@ procedure mainEventLoop(): enabledTransitions = selectTransitions(internalEvent) if not enabledTransitions.isEmpty(): microstep(enabledTransitions.toList()) - # either we're in a final state, and we break out of the loop + # either we're in a final state, and we break out of the loop if not running: break; # or we've completed a macrostep, so we start a new macrostep by waiting for an external event @@ -189,7 +189,7 @@ procedure mainEventLoop(): for inv in state.invoke: invoke(inv) statesToInvoke.clear() - # Invoking may have raised internal error events and we iterate to handle them + # Invoking may have raised internal error events and we iterate to handle them if not internalQueue.isEmpty(): continue # A blocking wait for an external event. Alternatively, if we have been invoked @@ -205,12 +205,12 @@ procedure mainEventLoop(): if inv.invokeid == externalEvent.invokeid: applyFinalize(inv, externalEvent) if inv.autoforward: - send(inv.id, externalEvent) + send(inv.id, externalEvent) enabledTransitions = selectTransitions(externalEvent) if not enabledTransitions.isEmpty(): - microstep(enabledTransitions.toList()) - # End of outer while running loop. If we get here, we have reached a top-level final state or have been cancelled - exitInterpreter() + microstep(enabledTransitions.toList()) + # End of outer while running loop. If we get here, we have reached a top-level final state or have been cancelled + exitInterpreter() */ void InterpreterDraft7::mainEventLoop() { } @@ -224,7 +224,7 @@ procedure exitInterpreter(): for inv in s.invoke: cancelInvoke(inv) configuration.delete(s) - if isFinalState(s) and isScxmlState(s.parent): + if isFinalState(s) and isScxmlState(s.parent): returnDoneEvent(s.donedata) */ void InterpreterDraft7::exitInterpreter() { @@ -246,7 +246,7 @@ function selectEventlessTransitions(): for state in atomicStates: loop: for s in [state].append(getProperAncestors(state, null)): for t in s.transition: - if not t.event and conditionMatch(t): + if not t.event and conditionMatch(t): enabledTransitions.add(t) break loop enabledTransitions = filterPreempted(enabledTransitions) @@ -275,7 +275,7 @@ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::selectTransitions(const procedure enterStates(enabledTransitions): statesToEnter = new OrderedSet() statesForDefaultEntry = new OrderedSet() - computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry) + computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry) for s in statesToEnter.toList().sort(entryOrder): configuration.add(s) statesToInvoke.add(s) @@ -302,14 +302,14 @@ void InterpreterDraft7::enterStates(const Arabica::XPath::NodeSet<std::string>& /** procedure exitStates(enabledTransitions): - statesToExit = computeExitSet(enabledTransitions) + statesToExit = computeExitSet(enabledTransitions) for s in statesToExit: statesToInvoke.delete(s) statesToExit = statesToExit.toList().sort(exitOrder) for s in statesToExit: for h in s.history: if h.type == "deep": - f = lambda s0: isAtomicState(s0) and isDescendant(s0,s) + f = lambda s0: isAtomicState(s0) and isDescendant(s0,s) else: f = lambda s0: s0.parent == s historyValue[h.id] = configuration.toList().filter(f) @@ -330,27 +330,27 @@ function computeExitSet(transitions) domain = getTransitionDomain(t) for s in configuration: if isDescendant(s,domain): - statesToExit.add(s) - return statesToExit + statesToExit.add(s) + return statesToExit */ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions, - const Arabica::XPath::NodeSet<std::string>& statesToExit) { + const Arabica::XPath::NodeSet<std::string>& statesToExit) { } /** procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry) for t in transitions: - statesToEnter.union(getTargetStates(t.target)) + statesToEnter.union(getTargetStates(t.target)) for s in statesToEnter: - addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) - for t in transitions: - ancestor = getTransitionDomain(t) - for s in getTargetStates(t.target)): + addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) + for t in transitions: + ancestor = getTransitionDomain(t) + for s in getTargetStates(t.target)): addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry) */ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { } /** @@ -364,14 +364,14 @@ function removeConflictingTransitions(enabledTransitions): if computeExitSet(t1).hasIntersection(computeExitSet(t2)): if isDescendent(t1.source, t2.source): transitionsToRemove.add(t2) - else: + else: t1Preempted = true break if not t1Preempted: for t3 in transitionsToRemove.toList(): filteredTransitions.delete(t3) filteredTransitions.add(t1) - + return filteredTransitions */ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { @@ -413,11 +413,11 @@ Arabica::DOM::Node<std::string> InterpreterDraft7::getTransitionDomain(const Ara if isParallelState(state): for child in getChildStates(state): if not statesToEnter.some(lambda s: isDescendant(s,child)): - addDescendentStatesToEnter(child,statesToEnter,statesForDefaultEntry) + addDescendentStatesToEnter(child,statesToEnter,statesForDefaultEntry) */ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { } /** @@ -427,12 +427,12 @@ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addDescendentStatesToEnt if isParallelState(anc): for child in getChildStates(anc): if not statesToEnter.some(lambda s: isDescendant(s,child)): - addStatesToEnter(child,statesToEnter,statesForDefaultEntry) + addStatesToEnter(child,statesToEnter,statesForDefaultEntry) */ Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state, - const Arabica::DOM::Node<std::string>& ancestor, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + const Arabica::DOM::Node<std::string>& ancestor, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { } diff --git a/src/uscxml/interpreter/InterpreterDraft7.h b/src/uscxml/interpreter/InterpreterDraft7.h index e784e84..1dc9350 100644 --- a/src/uscxml/interpreter/InterpreterDraft7.h +++ b/src/uscxml/interpreter/InterpreterDraft7.h @@ -19,23 +19,23 @@ class InterpreterDraft7 : public Interpreter { void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions, - const Arabica::XPath::NodeSet<std::string>& statesToExit); + const Arabica::XPath::NodeSet<std::string>& statesToExit); Arabica::XPath::NodeSet<std::string> computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Node<std::string>& transition); Arabica::XPath::NodeSet<std::string> addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); Arabica::XPath::NodeSet<std::string> addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state, - const Arabica::DOM::Node<std::string>& ancestor, - const Arabica::XPath::NodeSet<std::string>& statesToEnter, - const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + const Arabica::DOM::Node<std::string>& ancestor, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); void initializeData(const Arabica::DOM::Node<std::string>& data); diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index 407988e..4072862 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -1,6 +1,8 @@ #include "uscxml/Common.h" #include "V8DataModel.h" +#include "dom/V8DOM.h" #include "dom/V8Document.h" +#include "dom/V8Node.h" #include "dom/V8SCXMLEvent.h" #include "uscxml/Message.h" @@ -118,8 +120,23 @@ void V8DataModel::setEvent(const Event& event) { privData->dom = _dom; eventObj->SetInternalField(0, Arabica::DOM::V8DOM::toExternal(privData)); eventObj.MakeWeak(0, Arabica::DOM::V8SCXMLEvent::jsDestructor); + if (event.dom) { + v8::Handle<v8::Function> retCtor = Arabica::DOM::V8Document::getTmpl()->GetFunction(); + v8::Persistent<v8::Object> retObj = v8::Persistent<v8::Object>::New(retCtor->NewInstance()); - eventObj->Set(v8::String::New("data"), getDataAsValue(event.data)); // set data part of _event + struct Arabica::DOM::V8Document::V8DocumentPrivate* retPrivData = new Arabica::DOM::V8Document::V8DocumentPrivate(); + retPrivData->dom = privData->dom; + retPrivData->nativeObj = (Arabica::DOM::Document<std::string>*)&event.dom; + + retObj->SetInternalField(0, Arabica::DOM::V8DOM::toExternal(retPrivData)); + retObj.MakeWeak(0, Arabica::DOM::V8Document::jsDestructor); + + eventObj->Set(v8::String::New("data"), retObj); // set data part of _event + } else if (event.content.length() > 0) { + eventObj->Set(v8::String::New("data"), v8::String::New(event.content.c_str())); // set data part of _event + } else { + eventObj->Set(v8::String::New("data"), getDataAsValue(event.data)); // set data part of _event + } global->Set(v8::String::New("_event"), eventObj); } @@ -247,9 +264,18 @@ bool V8DataModel::validate(const std::string& location, const std::string& schem uint32_t V8DataModel::getLength(const std::string& expr) { v8::Locker locker; v8::HandleScope handleScope; + v8::TryCatch tryCatch; v8::Context::Scope contextScope(_contexts.back()); - v8::Handle<v8::Array> result = evalAsValue(expr).As<v8::Array>(); - return result->Length(); + v8::Handle<v8::Value> result = evalAsValue(expr); + if (!result.IsEmpty() && result->IsArray()) + return result.As<v8::Array>()->Length(); + + Event exceptionEvent; + exceptionEvent.name = "error.execution"; + exceptionEvent.data.compound["exception"] = Data("'" + expr + "' does not evaluate to an array.", Data::VERBATIM);; + + _interpreter->receiveInternal(exceptionEvent); + throw(exceptionEvent); } void V8DataModel::eval(const std::string& expr) { @@ -280,6 +306,17 @@ std::string V8DataModel::evalAsString(const std::string& expr) { return std::string(*data); } +double V8DataModel::evalAsNumber(const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + v8::Handle<v8::Value> result = evalAsValue(expr); + if (result->IsNumber()) { + return result->ToNumber()->NumberValue(); + } + return 0; +} + void V8DataModel::assign(const std::string& location, const Data& data) { v8::Locker locker; v8::HandleScope handleScope; @@ -308,49 +345,54 @@ v8::Handle<v8::Value> V8DataModel::evalAsValue(const std::string& expr) { if (script.IsEmpty() || result.IsEmpty()) { // throw an exception - assert(tryCatch.HasCaught()); - Event exceptionEvent; - exceptionEvent.name = "error.execution"; - - std::string exceptionString(*v8::String::AsciiValue(tryCatch.Exception())); - exceptionEvent.data.compound["exception"] = Data(exceptionString, Data::VERBATIM);; - - v8::Handle<v8::Message> message = tryCatch.Message(); - if (!message.IsEmpty()) { - std::string filename(*v8::String::AsciiValue(message->GetScriptResourceName())); - exceptionEvent.data.compound["filename"] = Data(filename, Data::VERBATIM); - - std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine())); - size_t startpos = sourceLine.find_first_not_of(" \t"); - if(std::string::npos != startpos) // removoe leading white space - sourceLine = sourceLine.substr(startpos); - - exceptionEvent.data.compound["sourceline"] = Data(sourceLine, Data::VERBATIM); - - std::stringstream ssLineNumber; - int lineNumber = message->GetLineNumber(); - ssLineNumber << lineNumber; - exceptionEvent.data.compound["linenumber"] = Data(ssLineNumber.str()); - - int startColumn = message->GetStartColumn(); - int endColumn = message->GetEndColumn(); - std::stringstream ssUnderline; - for (int i = 0; i < startColumn; i++) - ssUnderline << " "; - for (int i = startColumn; i < endColumn; i++) - ssUnderline << "^"; - exceptionEvent.data.compound["sourcemark"] = Data(ssUnderline.str(), Data::VERBATIM); - - std::string stackTrace(*v8::String::AsciiValue(tryCatch.StackTrace())); - exceptionEvent.data.compound["stacktrace"] = Data(stackTrace, Data::VERBATIM); + if (tryCatch.HasCaught()) + throwExceptionEvent(tryCatch); + } - } + return result; +} + +void V8DataModel::throwExceptionEvent(const v8::TryCatch& tryCatch) { + assert(tryCatch.HasCaught()); + Event exceptionEvent; + exceptionEvent.name = "error.execution"; + + std::string exceptionString(*v8::String::AsciiValue(tryCatch.Exception())); + exceptionEvent.data.compound["exception"] = Data(exceptionString, Data::VERBATIM);; + + v8::Handle<v8::Message> message = tryCatch.Message(); + if (!message.IsEmpty()) { + std::string filename(*v8::String::AsciiValue(message->GetScriptResourceName())); + exceptionEvent.data.compound["filename"] = Data(filename, Data::VERBATIM); + + std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine())); + size_t startpos = sourceLine.find_first_not_of(" \t"); + if(std::string::npos != startpos) // removoe leading white space + sourceLine = sourceLine.substr(startpos); + + exceptionEvent.data.compound["sourceline"] = Data(sourceLine, Data::VERBATIM); + + std::stringstream ssLineNumber; + int lineNumber = message->GetLineNumber(); + ssLineNumber << lineNumber; + exceptionEvent.data.compound["linenumber"] = Data(ssLineNumber.str()); + + int startColumn = message->GetStartColumn(); + int endColumn = message->GetEndColumn(); + std::stringstream ssUnderline; + for (int i = 0; i < startColumn; i++) + ssUnderline << " "; + for (int i = startColumn; i < endColumn; i++) + ssUnderline << "^"; + exceptionEvent.data.compound["sourcemark"] = Data(ssUnderline.str(), Data::VERBATIM); + + std::string stackTrace(*v8::String::AsciiValue(tryCatch.StackTrace())); + exceptionEvent.data.compound["stacktrace"] = Data(stackTrace, Data::VERBATIM); - _interpreter->receiveInternal(exceptionEvent); - throw(exceptionEvent); } - return result; + _interpreter->receiveInternal(exceptionEvent); + throw(exceptionEvent); } }
\ No newline at end of file diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h index 18e1ea4..6d4ca63 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h @@ -52,6 +52,7 @@ public: virtual std::string evalAsString(const std::string& expr); virtual bool evalAsBool(const std::string& expr); + virtual double evalAsNumber(const std::string& expr); static v8::Handle<v8::Value> jsIn(const v8::Arguments& args); static v8::Handle<v8::Value> jsPrint(const v8::Arguments& args); @@ -63,6 +64,7 @@ protected: v8::Handle<v8::Value> evalAsValue(const std::string& expr); virtual v8::Handle<v8::Value> getDataAsValue(const Data& data); + void throwExceptionEvent(const v8::TryCatch& tryCatch); }; diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeList.h b/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeList.h index fb1b510..c454e65 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeList.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeList.h @@ -43,6 +43,7 @@ public: static v8::Handle<v8::Value> itemCallback(const v8::Arguments&); static v8::Handle<v8::Value> lengthAttrGetter(v8::Local<v8::String> property, const v8::AccessorInfo& info); + static v8::Handle<v8::Value> indexedPropertyCustomGetter(uint32_t, const v8::AccessorInfo&); static v8::Persistent<v8::FunctionTemplate> Tmpl; static v8::Handle<v8::FunctionTemplate> getTmpl() { @@ -60,6 +61,7 @@ public: instance->SetAccessor(v8::String::NewSymbol("length"), V8NodeList::lengthAttrGetter, 0, v8::External::New(0), static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None)); + instance->SetIndexedPropertyHandler(V8NodeList::indexedPropertyCustomGetter, 0); prototype->Set(v8::String::NewSymbol("item"), v8::FunctionTemplate::New(V8NodeList::itemCallback, v8::Undefined()), static_cast<v8::PropertyAttribute>(v8::DontDelete)); diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeListCustom.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeListCustom.cpp new file mode 100644 index 0000000..c05c5a4 --- /dev/null +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/dom/V8NodeListCustom.cpp @@ -0,0 +1,52 @@ +#include "V8NodeList.h" +#include "V8Element.h" +#include "V8Node.h" + +namespace Arabica { +namespace DOM { + +v8::Handle<v8::Value> V8NodeList::indexedPropertyCustomGetter(uint32_t index, const v8::AccessorInfo &info) { + v8::Local<v8::Object> self = info.Holder(); + V8NodeListPrivate* privData = V8DOM::toClassPtr<V8NodeListPrivate >(self->GetInternalField(0)); + + if (privData->nativeObj->getLength() >= index) { + switch(privData->nativeObj->item(index).getNodeType()) { + case Node_base::ELEMENT_NODE: { + Arabica::DOM::Element<std::string>* retVal = new Arabica::DOM::Element<std::string>(privData->nativeObj->item(index)); + + v8::Handle<v8::Function> retCtor = V8Element::getTmpl()->GetFunction(); + v8::Persistent<v8::Object> retObj = v8::Persistent<v8::Object>::New(retCtor->NewInstance()); + + struct V8Element::V8ElementPrivate* retPrivData = new V8Element::V8ElementPrivate(); + retPrivData->dom = privData->dom; + retPrivData->nativeObj = retVal; + + retObj->SetInternalField(0, V8DOM::toExternal(retPrivData)); + + retObj.MakeWeak(0, V8Element::jsDestructor); + return retObj; + } + default: { + Arabica::DOM::Node<std::string>* retVal = new Arabica::DOM::Node<std::string>(privData->nativeObj->item(index)); + + v8::Handle<v8::Function> retCtor = V8Node::getTmpl()->GetFunction(); + v8::Persistent<v8::Object> retObj = v8::Persistent<v8::Object>::New(retCtor->NewInstance()); + + struct V8Node::V8NodePrivate* retPrivData = new V8Node::V8NodePrivate(); + retPrivData->dom = privData->dom; + retPrivData->nativeObj = retVal; + + retObj->SetInternalField(0, V8DOM::toExternal(retPrivData)); + + retObj.MakeWeak(0, V8Node::jsDestructor); + return retObj; + } + } + } + + return v8::Undefined(); + +} + +} +}
\ No newline at end of file |