VXI.cpp
上传用户:xqtpzdz
上传日期:2022-05-21
资源大小:1764k
文件大小:151k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. /****************License************************************************
  2.  * Vocalocity OpenVXI
  3.  * Copyright (C) 2004-2005 by Vocalocity, Inc. All Rights Reserved.
  4.  * This program is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU General Public License
  6.  * as published by the Free Software Foundation; either version 2
  7.  * of the License, or (at your option) any later version.
  8.  *  
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17.  * Vocalocity, the Vocalocity logo, and VocalOS are trademarks or 
  18.  * registered trademarks of Vocalocity, Inc. 
  19.  * OpenVXI is a trademark of Scansoft, Inc. and used under license 
  20.  * by Vocalocity.
  21.  ***********************************************************************/
  22. #include "VXI.hpp"
  23. #include "DocumentParser.hpp"
  24. #include "SimpleLogger.hpp"
  25. #include "ValueLogging.hpp"            // for VXIValue dumping
  26. #include "Scripter.hpp"                // for use in ExecutionContext
  27. #include "GrammarManager.hpp"          // for use in ExecutionContext
  28. #include "Counters.hpp"                // for EventCounter
  29. #include "PropertyList.hpp"            // for PropertyList
  30. #include "PromptManager.hpp"           // for PromptManager
  31. #include "AnswerParser.hpp"            // for AnswerParser
  32. #include "VXML.h"                      // for node & attribute names
  33. #include "VXIrec.h"                    // for parameters from rec answer
  34. #include "VXIlog.h"                    // for event IDs
  35. #include "VXIobject.h"                 // for Object type
  36. #include "VXIinet.h"
  37. #include "DialogEventCounter.hpp"
  38. #include "TokenList.hpp"
  39. #include "AccessControl.hpp"
  40. #include <sstream>
  41. #include <algorithm>                   // for sort, set_intersection, etc.
  42. #include <xercesc/dom/DOMDocument.hpp>
  43. static const wchar_t * const SCOPE_Session       = L"session";
  44. static const wchar_t * const SCOPE_Defaults      = L"$_platDefaults_";
  45. static const wchar_t * const SCOPE_Application   = L"application";
  46. static const wchar_t * const SCOPE_Document      = L"document";
  47. static const wchar_t * const SCOPE_Dialog        = L"dialog";
  48. static const wchar_t * const SCOPE_Local         = L"local";
  49. static const wchar_t * const SCOPE_Anonymous     = L"$_execAnonymous_";
  50. static const wchar_t * const SESSION_INET        = L"com.vocalocity.inet";
  51. static const wchar_t * const GENERIC_DEFAULTS    = L"*";
  52. const int DEFAULT_MAX_EXE_STACK_DEPTH = 50;
  53. const int DEFAULT_MAX_EVENT_RETHROWS  = 6;
  54. const int DEFAULT_MAX_EVENT_COUNT     = 12;
  55. // ------*---------*---------*---------*---------*---------*---------*---------
  56. static vxistring toString(const VXIString * s)
  57. {
  58.   if (s == NULL) return L"";
  59.   const VXIchar * temp = VXIStringCStr(s);
  60.   if (temp == NULL) return L"";
  61.   return temp;
  62. }
  63. static vxistring toString(const VXIchar * s)
  64. {
  65.   if (s == NULL) return L"";
  66.   return s;
  67. }
  68. /*
  69.  * Implementation of the PromptManager PromptTranslator interface.
  70.  */
  71. class VXIPromptTranslator : public PromptTranslator {
  72. public:
  73.   virtual VXIValue * EvaluateExpression(const vxistring & expression) {
  74.     if (expression.empty()) return NULL;
  75.     return jsi.EvalScriptToValue(expression); 
  76.   }
  77.   virtual void SetVariable(const vxistring & name, const vxistring & value) {
  78.     if (name.empty()) return;
  79.     jsi.MakeVar(name, (VXIValue*)NULL);
  80.     jsi.SetString(name, value); 
  81.   }
  82.   virtual bool TestCondition(const vxistring & script) {
  83.     return jsi.TestCondition(script);
  84.   }
  85.   virtual void EvalScript(const vxistring & script) {
  86.     jsi.EvalScript(script);
  87.   }
  88.   VXIPromptTranslator(Scripter & j) : jsi(j) { }
  89.   virtual ~VXIPromptTranslator() { }
  90. private:
  91.   Scripter & jsi;
  92. };
  93. /*
  94.  * Implementation of the AnswerParser AnswerTranslator interface.
  95.  */
  96. class VXIAnswerTranslator : public AnswerTranslator {
  97. public:
  98.   virtual void EvaluateExpression(const vxistring & expression) {
  99.     if (expression.empty()) return;
  100.     jsi.EvalScript(expression); 
  101.   }
  102.   virtual void SetString(const vxistring & var, const vxistring & val) {
  103.     jsi.SetString(var, val); 
  104.   }
  105.   VXIAnswerTranslator(Scripter & j) : jsi(j) { }
  106.   virtual ~VXIAnswerTranslator() { }
  107. private:
  108.   Scripter & jsi;
  109. };
  110. // Thrown after a successful recognition.
  111. //
  112. class AnswerInformation {
  113. public:
  114.   VXMLElement element;
  115.   VXMLElement dialog;
  116.   AnswerInformation(VXMLElement & e, VXMLElement & d)
  117.     : element(e), dialog(d)  { }
  118. };
  119. class JumpReturn {
  120. public:
  121.   JumpReturn() { }
  122. };
  123. class JumpItem {
  124. public:
  125.   VXMLElement item;
  126.   JumpItem(const VXMLElement & i) : item(i) { }
  127. };
  128. class JumpDialog {
  129. public:
  130.   VXMLElement dialog;
  131.   JumpDialog(const VXMLElement d) : dialog(d)  { }
  132. };
  133. class JumpDoc {
  134. public:
  135.   VXMLElement  defaults;
  136.   VXMLDocument application;
  137.   vxistring    applicationURI;
  138.   VXMLDocument document;
  139.   vxistring    documentURI;
  140.   VXMLElement  documentDialog;
  141.   bool         isSubdialog;
  142.   bool         isSubmitElement;
  143.   PropertyList properties;
  144.   
  145.   JumpDoc(const VXMLElement & def, const VXMLDocument & app,
  146.           const vxistring & appURI, const VXMLDocument & doc, const vxistring & docURI,
  147.           const VXMLElement & docdial, bool issub, bool issubmit, const PropertyList & p)
  148.     : defaults(def), application(app), applicationURI(appURI),
  149.       document(doc), documentURI(docURI), documentDialog(docdial), isSubdialog(issub), 
  150.       isSubmitElement(issubmit), properties(p)
  151.   { }
  152.   ~JumpDoc() { }
  153. };
  154. //#############################################################################
  155. // ExecutionContext & Utilities
  156. //#############################################################################
  157. class ExecutionContext {
  158. public:
  159.   // These are used by the main run loops, event handling, and subdialog.
  160.   // Each of these gets initialized by InstallDocument.
  161.   VXMLElement    platDefaults;
  162.   VXMLDocument   application;
  163.   vxistring      applicationName;
  164.   vxistring      applicationURI;
  165.   VXIMapHolder   appProperties;
  166.   
  167.   VXMLDocument   document;
  168.   vxistring      documentName;
  169.   vxistring      documentURI;
  170.   VXMLElement    currentDialog;
  171.   VXMLElement    currentFormItem;
  172.   // Limited purpose members.
  173.   VXMLElement    eventSource;        // For prompting (enumerate) during events
  174.   VXMLElement    lastItem;           // Set by <subdialog>
  175. public: // These are used more generally.
  176.   Scripter       script;
  177.   GrammarManager gm;
  178.   PromptTracker  promptcounts;
  179.   DialogEventCounter eventcounts;
  180.   PropertyList   properties;
  181.   TokenList formitems;
  182.   bool playingPrompts;
  183.   ExecutionContext * next;
  184.   ExecutionContext(VXIrecInterface * r, VXIjsiInterface * j,
  185.                    const SimpleLogger & l, ExecutionContext * n)
  186.     : script(j), gm(r, l), next(n), playingPrompts(true), properties(l) { }
  187.     // may throw VXIException::OutOfMemory()
  188.   ~ExecutionContext() { }
  189. };
  190. //#############################################################################
  191. // Creation and Run
  192. //#############################################################################
  193. VXI::VXI()
  194.   : parser(NULL), log(NULL), inet(NULL), rec(NULL), jsi(NULL), tel(NULL),
  195.     exe(NULL), stackDepth(0), sdParams(NULL), sdResult(NULL), sdEvent(NULL),
  196.     updateDefaultDoc(true), mutex(), uriPlatDefaults(), uriBeep(), uriCurrent(),
  197.     lineHungUp(false), stopRequested(false), haveExternalEvents(false),defAccessControl(false)
  198. {
  199.   try {
  200.     parser = new DocumentParser();
  201.   }
  202.   catch (const VXIException::OutOfMemory &) {
  203.     parser = NULL;
  204.     throw;
  205.   }
  206.   try {
  207.     pm = new PromptManager();
  208.   }
  209.   catch (...) {
  210.     delete parser;
  211.     parser = NULL;
  212.     pm = NULL;
  213.     throw;
  214.   }
  215.   if (pm == NULL) {
  216.     delete parser;
  217.     parser = NULL;
  218.     throw VXIException::OutOfMemory();
  219.   }
  220. }
  221. VXI::~VXI()
  222. {
  223.   while (exe != NULL) {
  224.     ExecutionContext * temp = exe->next;
  225.     delete exe;
  226.     exe = temp;
  227.   }
  228.   delete pm;
  229.   delete parser;
  230. }
  231. // Determine if the user has already hung-up
  232. // if it is the case, throw the exit element to indicate
  233. // end of call
  234. void VXI::CheckLineStatus()
  235. {
  236.   VXItelStatus status;
  237.   int telResult = tel->GetStatus(tel, &status);
  238.   bool needToThrow = false;
  239.   if (telResult != VXItel_RESULT_SUCCESS) {
  240.     needToThrow = true;
  241.     log->LogError(201);
  242.   }
  243.   else if (status == VXItel_STATUS_INACTIVE)
  244.     needToThrow = true;
  245.   if (needToThrow) {
  246.     mutex.Lock();
  247.     bool alreadyHungup = lineHungUp;
  248.     lineHungUp = true;
  249.     mutex.Unlock();
  250.     if (alreadyHungup) {
  251.       log->LogDiagnostic(1, L"VXI::CheckLineStatus - Call has been hung-up, "
  252.                          L"exiting...");
  253.       throw VXIException::Exit(NULL);
  254.     }
  255.     else
  256.       throw VXIException::InterpreterEvent(EV_TELEPHONE_HANGUP);
  257.   }
  258. }
  259. bool VXI::SetRuntimeProperty(PropertyID id, const VXIValue * value)
  260. {
  261.   bool set = false;
  262.   mutex.Lock();
  263.   switch (id) {
  264.   case VXI::BeepURI:
  265.     if (VXIValueGetType(value) == VALUE_STRING) {
  266.       const VXIchar * uri = VXIStringCStr(reinterpret_cast<const VXIString *>(value));
  267.       if (uri == NULL) uriBeep.erase();
  268.       else uriBeep = uri;
  269.   set = true;
  270.     }
  271.     break;
  272.   case VXI::PlatDefaultsURI:
  273.     if (VXIValueGetType(value) == VALUE_STRING) {
  274.       const VXIchar * uri = VXIStringCStr(reinterpret_cast<const VXIString *>(value));
  275.       if (uri == NULL) uriPlatDefaults.erase();
  276.       else uriPlatDefaults = uri;
  277.       updateDefaultDoc = true;
  278.   set = true;
  279.     }
  280.     break;
  281.   case VXI::DefaultAccessControl:
  282.     if (VXIValueGetType(value) == VALUE_INTEGER) {
  283.       int def = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(value));
  284.   defAccessControl = def ? true : false;
  285.       set = true;
  286.     }
  287. break;
  288.   }
  289.   mutex.Unlock();
  290.   return set;
  291. }
  292. void VXI::GetRuntimeProperty(PropertyID id, vxistring & value) const
  293. {
  294.   mutex.Lock();
  295.   switch (id) {
  296.   case VXI::BeepURI:           value = uriBeep;          break;
  297.   case VXI::PlatDefaultsURI:   value = uriPlatDefaults;  break;
  298.   }
  299.   mutex.Unlock();
  300. }
  301. void VXI::GetRuntimeProperty(PropertyID id, int &value) const
  302. {
  303.   mutex.Lock();
  304.   switch (id) {
  305.   case VXI::DefaultAccessControl: value = defAccessControl; break;
  306.   }
  307.   mutex.Unlock();
  308. }
  309. void VXI::DeclareStopRequest(bool doStop)
  310. {
  311.   mutex.Lock();
  312.   stopRequested = doStop;
  313.   mutex.Unlock();
  314. }
  315. bool VXI::DeclareExternalEvent(const VXIchar * event, const VXIchar * message)
  316. {
  317.   // Check event
  318.   if (event == NULL || *event == L'' || *event == L'.') return 1;
  319.   bool lastWasPeriod = false;
  320.   // This could be stricter
  321.   for (const VXIchar * i = event; *i != L''; ++i) {
  322.     VXIchar c = *i;
  323.     if (c == L'.') {
  324.       if (lastWasPeriod) 
  325.   return false;
  326.       lastWasPeriod = true;
  327.     }
  328. else {
  329.       if (c == L' ' || c == L't' || c == L'n' || c == L'r') 
  330.   return false;
  331.       lastWasPeriod = false;
  332.     }
  333.   }
  334.   mutex.Lock();
  335.   externalEvents.push_back(event);
  336.   if (message == NULL || message == L'')
  337.     externalMessages.push_back(event);
  338.   else
  339.     externalMessages.push_back(message);
  340.   haveExternalEvents = true;
  341.   mutex.Unlock();
  342.   return true;
  343. }
  344. bool VXI::ClearExternalEventQueue()
  345. {
  346.   mutex.Lock();
  347.   if( !externalEvents.empty() )
  348.   {
  349.     haveExternalEvents = false;
  350.     externalEvents.clear();
  351.     externalMessages.clear();          
  352.   }
  353.   mutex.Unlock();
  354.   return true;
  355. }
  356. int VXI::Run(const VXIchar * initialDocument,
  357.              const VXIchar * sessionScript,
  358.              SimpleLogger * resourceLog,
  359.              VXIinetInterface * resourceInet,
  360.              VXIcacheInterface * resourceCache,
  361.              VXIjsiInterface * resourceJsi,
  362.              VXIrecInterface * resourceRec,
  363.              VXIpromptInterface * resourcePrompt,
  364.              VXItelInterface * resourceTel,
  365.              VXIobjectInterface * resourceObject,
  366.              VXIValue ** resultValue)
  367. {
  368.   // (1) Check arguments.
  369.   // (1.1) Check external resources
  370.   if (resourceLog == NULL || resourceInet   == NULL || resourceJsi == NULL ||
  371.       resourceRec == NULL || resourcePrompt == NULL || resourceTel == NULL)
  372.     return 1;
  373.   log = resourceLog;
  374.   inet = resourceInet;
  375.   jsi = resourceJsi;
  376.   rec = resourceRec;
  377.   tel = resourceTel;
  378.   if (stopRequested) {
  379.     DeclareStopRequest(false);
  380.     if (log->IsLogging(2)) {
  381.       log->StartDiagnostic(2) << L"VXI::Run( STOP HAS BEEN REQUESTED ) exiting..."; 
  382.       log->EndDiagnostic();
  383.     }
  384.     return 4;
  385.   }
  386.   // These may be NULL.
  387.   object = resourceObject;
  388.   cache  = resourceCache;
  389.   if (!pm->ConnectResources(log, resourcePrompt, this)) return 1;
  390.     
  391.   if (log->IsLogging(2)) {
  392.     log->StartDiagnostic(2) << L"VXI::Run(" << initialDocument << L")";
  393.     log->EndDiagnostic();
  394.   }
  395.   // (1.2) Check document
  396.   if (initialDocument == NULL || wcslen(initialDocument) == 0) {
  397.     log->LogError(201);
  398.     return 1;
  399.   }
  400.   // (2) Delegate real work to RunDocumentLoop & handle the serious errors.
  401.   int exitCode;
  402.   try {
  403.     exitCode = RunDocumentLoop(initialDocument, 
  404.                               (sessionScript ? sessionScript : L""), 
  405.                               resultValue);
  406.     pm->PlayAll();
  407.   }
  408.   catch (VXIException::InterpreterEvent & e) {
  409.     log->LogError(207, SimpleLogger::EXCEPTION, e.GetValue().c_str());
  410.     exitCode = 0;
  411.   }
  412.   catch (const VXIException::Exit & e) {
  413.     *resultValue = e.exprResult;
  414.     exitCode = 0;
  415.   }
  416.   catch (const VXIException::Fatal &) {
  417.     log->LogError(209);
  418.     exitCode = -2;
  419.   }
  420.   catch (const VXIException::OutOfMemory &) {
  421.     PopExecutionContext();
  422.     log->LogError(202);
  423.     exitCode = 1;
  424.   }
  425.   catch (const VXIException::JavaScriptError &) {
  426.     log->LogError(212);
  427.     exitCode = -2;
  428.   }
  429.   catch (const VXIException::StopRequest &) {
  430.     exitCode = 4;
  431.   }
  432.   catch (const JumpDialog &) {
  433.     log->LogError(999, SimpleLogger::MESSAGE, L"unexpected jump to a dialog");
  434.     exitCode = -2;
  435.   }
  436.   catch (const JumpDoc &) {
  437.     log->LogError(999, SimpleLogger::MESSAGE,L"unexpected jump to a document");
  438.     exitCode = -2;
  439.   }
  440.   catch (const JumpItem &) {
  441.     log->LogError(999, SimpleLogger::MESSAGE, L"unexpected jump to an item");
  442.     exitCode = -2;
  443.   }
  444.   catch (const JumpReturn &) {
  445.     log->LogError(999, SimpleLogger::MESSAGE,
  446.                   L"unexpected jump from a return element");
  447.     exitCode = -2;
  448.   }
  449.   // Clean up execution contexts.
  450.   try {
  451.     while (exe != NULL)
  452.       PopExecutionContext();
  453.   }
  454.   catch (const VXIException::JavaScriptError &) {
  455.     log->LogError(212);
  456.     exitCode = -2;
  457.   }
  458.   // Clean up event flags.
  459.   lineHungUp = false;
  460.   stopRequested = false;
  461.   //haveExternalEvents = false;
  462.   //externalEvents.clear();
  463.   //externalMessages.clear();
  464.   return exitCode;
  465. }
  466. int VXI::RunDocumentLoop(const vxistring & initialDocument,
  467.                          const vxistring & sessionScript,
  468.                          VXIValue ** resultValue)
  469. {
  470.   // (1) Load the document containing the default handlers.
  471.   if (updateDefaultDoc) {
  472.     log->LogDiagnostic(2, L"VXI::RunDocumentLoop - loading defaultDoc.");
  473.     // (1.1) Get URI if possible.
  474.     vxistring defaultsUri;
  475.     GetRuntimeProperty(VXI::PlatDefaultsURI, defaultsUri);
  476.   
  477.     // (1.2) Load platform defaults.
  478.     try {
  479.       VXIMapHolder domDefaultProp;
  480.       if (log->IsLogging(4) && !defaultsUri.empty()) {
  481.         log->StartDiagnostic(4) << L"VXI: Loading platform defaults <"
  482.                                 << defaultsUri << L">";
  483.         log->EndDiagnostic();
  484.       }
  485.       AttemptDocumentLoad(defaultsUri, VXIMapHolder(NULL), domDefaultDoc,
  486.                           domDefaultProp, true);
  487.     }
  488.     catch (const VXIException::InterpreterEvent & e) {
  489.       log->LogError(221, SimpleLogger::EXCEPTION, e.GetValue().c_str());
  490.       return 3;
  491.     }
  492.     
  493.     // Only reset this flag if the load has succeeded
  494.     updateDefaultDoc = false;
  495.   }
  496.   // (2) Create a new execution context and initialize the defaults.
  497.   // (2.1) Create new execution context.
  498.   if (!PushExecutionContext(sessionScript)) return -1;
  499.   // (3) Execute the document.
  500.   bool firstTime = true;
  501.   VXIint32 loopCount = 0;
  502.   while (1) {
  503.     if (stopRequested) throw VXIException::StopRequest();
  504.     try {
  505.       if (firstTime) {
  506.         firstTime = false;
  507.         // Jump to the initial document.  Any events which occur while
  508.         // loading the initial document are handled by the defaults.
  509.         log->LogDiagnostic(2, L"VXI::RunDocumentLoop - loading initial "
  510.                            L"document.");
  511.         try {
  512.           PerformTransition(exe->platDefaults, initialDocument);
  513.         }
  514.         catch (const VXIException::InterpreterEvent & e) {
  515.           DoEvent(exe->platDefaults, e);
  516.           PopExecutionContext();
  517.           throw VXIException::Exit(NULL);
  518.         }
  519.       }
  520.       log->LogDiagnostic(2, L"VXI::RunDocumentLoop - new document");
  521.       RunInnerLoop();
  522.       break;
  523.     }
  524.     // Execute a new document
  525.     catch (JumpDoc & e) {
  526.       // if <subdialog>, we need to create a new context, while
  527.       // storing the current.
  528.       if (e.isSubdialog)
  529.         if (!PushExecutionContext(sessionScript)) return -1;
  530.       if (log->IsLogging(4)) {
  531.         if (e.isSubdialog)
  532.           log->StartDiagnostic(4) << L"VXI: Subdialog transition <"
  533.                                   << e.documentURI << L">";
  534.         else
  535.           log->StartDiagnostic(4) << L"VXI: Document transition <"
  536.                                   << e.documentURI << L">";
  537.         log->EndDiagnostic();
  538.       }
  539.       InstallDocument(e);
  540.     }
  541.     // Handle <return>
  542.     catch (JumpReturn &) {
  543.       // clean up the current context, and restore the original.
  544.       PopExecutionContext();
  545.       if (log->IsLogging(4)) {
  546.         log->StartDiagnostic(4) << L"VXI: Subdialog returned to <"
  547.                                   << exe->documentURI << L">";
  548.         log->EndDiagnostic();
  549.       }
  550.     }
  551.     // Handle <exit> by exiting the document loop.
  552.     catch (const VXIException::Exit & e) {
  553.       if (resultValue != NULL) *resultValue = e.exprResult;
  554.       break;
  555.     }
  556.   }
  557.   // Done with document execution...clean up.
  558.   PopExecutionContext();
  559.   return 0;
  560. }
  561. void VXI::InstallDocument(JumpDoc & e)
  562. {
  563.   // (1) Check to see what needs to be initialized.
  564.   //  If the transition is from leaf-to-root occurs in <submit> then
  565.   //  the root context is initialized, otherwise it is preserved.
  566.   bool leafToRoot = !e.isSubmitElement && (!e.documentURI.empty()) 
  567.                     && (exe->applicationURI == e.documentURI);
  568.   bool reinitApplication
  569.     = e.isSubdialog ||
  570.       (e.applicationURI.empty() && exe->applicationURI.empty()) ||
  571.       (!leafToRoot && (e.applicationURI != exe->applicationURI));
  572.   // (2) Set the easy stuff.
  573.   exe->platDefaults   = e.defaults;
  574.   exe->application    = e.application;
  575.   exe->applicationURI = e.applicationURI;
  576.   exe->document       = e.document;
  577.   exe->documentURI    = e.documentURI;
  578.   exe->currentDialog  = e.documentDialog;
  579.   exe->currentFormItem  = VXMLElement();
  580.   exe->eventSource    = VXMLElement();
  581.   exe->properties     = e.properties;
  582.   exe->gm.ReleaseGrammars();
  583.   VXMLElement documentRoot = exe->document.GetRoot();
  584.   VXMLElement applicationRoot = exe->application.GetRoot();
  585.   if (log->IsLogging(2)) {
  586.     log->StartDiagnostic(2) << L"VXI::InstallDocument(documentURI:[" << e.documentURI
  587.                             << L"], appURI:[" << e.applicationURI << L"])";
  588.     log->EndDiagnostic();
  589.   }
  590.   
  591.   // (3) Load grammars.  The grammars are reloaded using the current
  592.   // understanding of the properties.  During activation, the actual
  593.   // properties may differ slightly.
  594.   VXIPromptTranslator translator(exe->script);
  595.   try {
  596.     exe->documentName = L"";
  597.     exe->gm.LoadGrammars(documentRoot, exe->documentName, exe->properties);
  598.     pm->PreloadPrompts(documentRoot, exe->properties, translator);
  599.   }
  600.   catch (const VXIException::InterpreterEvent & e) {
  601.     DoEvent(documentRoot, e);
  602.     throw VXIException::Exit(NULL);
  603.   }
  604.   try {
  605.     vxistring temp;
  606.     exe->gm.LoadGrammars(applicationRoot, temp, exe->properties);
  607.     // don't want to preload prompts if the  root application already loaded
  608.     if( reinitApplication )
  609.       pm->PreloadPrompts(applicationRoot, exe->properties, translator);
  610.   }
  611.   catch (const VXIException::InterpreterEvent & e) {
  612.     DoEvent(applicationRoot, e);
  613.     throw VXIException::Exit(NULL);
  614.   }
  615.   try {
  616.     vxistring temp;
  617.     exe->gm.LoadGrammars(exe->platDefaults, temp, exe->properties, true);
  618.     pm->PreloadPrompts(exe->platDefaults, exe->properties, translator);
  619.   }
  620.   catch (const VXIException::InterpreterEvent & e) {
  621.     DoEvent(exe->platDefaults, e);
  622.     throw VXIException::Exit(NULL);
  623.   }
  624.   // (4) Clear existing ECMA script scopes.
  625.   Scripter & script = exe->script;
  626.   if (script.CurrentScope(SCOPE_Anonymous)) script.PopScope();
  627.   if (script.CurrentScope(SCOPE_Local))     script.PopScope();
  628.   if (script.CurrentScope(SCOPE_Dialog))    script.PopScope();
  629.   if (script.CurrentScope(SCOPE_Document))  script.PopScope();
  630.   if (script.CurrentScope(SCOPE_Application) && reinitApplication)
  631.     script.PopScope();
  632.   if (reinitApplication && !script.CurrentScope(SCOPE_Defaults)) {
  633.     log->LogError(999, SimpleLogger::MESSAGE,
  634.                   L"ECMAScript scope inconsistent");
  635.     throw VXIException::Fatal();
  636.   }
  637.   // (5) And set the new ones.
  638.   try {
  639.     if (script.CurrentScope(SCOPE_Defaults)) {
  640.       script.PushScope(SCOPE_Application);
  641.       exe->script.MakeVar(L"lastresult$", L"undefined");
  642.       if (!exe->applicationURI.empty()) {
  643.         ProcessRootScripts(applicationRoot);
  644.       }
  645.     }
  646.   }
  647.   catch (const VXIException::InterpreterEvent & e) {
  648.     DoEvent(applicationRoot, e);
  649.     throw VXIException::Exit(NULL);
  650.   }
  651.   try {
  652.     script.PushScope(SCOPE_Document, leafToRoot || exe->applicationURI.empty());
  653.     if (!leafToRoot) {
  654.       ProcessRootScripts(documentRoot);
  655.     }
  656.   }
  657.   catch (const VXIException::InterpreterEvent & e) {
  658.     DoEvent(documentRoot, e);
  659.     throw VXIException::Exit(NULL);
  660.   }
  661. }
  662. void VXI::PerformTransition(const VXMLElement & elem,
  663.                             const vxistring & rawURI,
  664.                             VXIMap * rawSubmitData,
  665.                             bool isSubdialog,
  666.                             bool isSubmitElement)
  667. {
  668.   if (log->IsLogging(2)) {
  669.     log->StartDiagnostic(2) << L"VXI::PerformTransition(" << rawURI << L")";
  670.     log->EndDiagnostic();
  671.   }
  672.   VXIMapHolder submitData(rawSubmitData);
  673.   // (1) Determine fetch properties for document load.
  674.   // (1.1) Create empty fetch object.  Now we need to fill this in.
  675.   VXIMapHolder fetchobj;
  676.   if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  677.    
  678.   // (1.2) Set URIs for the Jump.
  679.   vxistring uri((rawURI.empty() ? L"" : rawURI));
  680.   vxistring fragment;
  681.   const VXIchar * tempStr = L"";
  682.   // (1.2.1) Divide raw URI into uri + fragment.
  683.   exe->properties.GetFetchobjURIs(elem, fetchobj, uri, fragment);
  684.   // (1.2.2) Handle the degenerate case.
  685.   if (uri.empty() && fragment.empty()) {
  686.     log->StartDiagnostic(0) << L"VXI::PerformTransition - invalid URI, ""
  687.                             << (rawURI.empty() ? L"NULL" : rawURI.c_str()) << L""";
  688.     log->EndDiagnostic();
  689.     throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
  690.   }
  691.   // (1.2.3) In the fragment only case, just go to the indicated item.
  692.   if (uri.empty()) {
  693.     VXMLElement targetElement = FindDialog(elem, fragment);
  694.     if (targetElement == 0) {
  695.       log->StartDiagnostic(0) << L"VXI::PerformTransition - non-existent "
  696.                                  L"dialog, "" << (rawURI.empty() ? L"NULL" : rawURI.c_str()) << L""";
  697.       log->EndDiagnostic();
  698.       throw VXIException::InterpreterEvent(EV_ERROR_BADDIALOG);
  699.     }
  700.     if (!isSubdialog)
  701.       throw JumpDialog(targetElement);
  702.     else {
  703.       PropertyList subprop(exe->properties);
  704.       subprop.PopPropertyLevel(FIELD_PROP);
  705.       vxistring absoluteURI(L"");
  706.       tempStr = exe->properties.GetProperty(PropertyList::AbsoluteURI);
  707.       if (tempStr) absoluteURI = tempStr;
  708.       
  709.       throw JumpDoc(exe->platDefaults, exe->application, exe->applicationURI,
  710.                     exe->document, absoluteURI, targetElement, isSubdialog, isSubmitElement,
  711.                     subprop);
  712.     }
  713.   }
  714.   // (1.3) Get remaining fetch properties. At this point, we now need
  715.   //       to assemble seperate fetch properties for fetchaudio, up
  716.   //       until now all the prep work is identical.
  717.   VXIMapHolder fetchAudioFetchobj(NULL);
  718.   fetchAudioFetchobj = fetchobj;
  719.   if (fetchAudioFetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  720.   exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Document,fetchobj);
  721.   exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Audio,
  722.                                         fetchAudioFetchobj);
  723.   if (submitData.GetValue() != NULL) {
  724.     if (!exe->properties.GetFetchobjSubmitAttributes(elem, submitData,
  725.                                                      fetchobj))
  726.     {
  727.       // This should never occur.
  728.       log->StartDiagnostic(0) << L"VXI::PerformTransition - couldn't set "
  729.                                  L"the submit attributes.";
  730.       log->EndDiagnostic();
  731.       throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
  732.     }
  733.     submitData.Release(); // This map is now owned by fetchobj.
  734.   }
  735.   // (2) Load Document.
  736.   // (2.1) Start fetch audio.
  737.   vxistring fetchaudio;
  738.   if (!elem.GetAttribute(ATTRIBUTE_FETCHAUDIO, fetchaudio))
  739.     fetchaudio = toString(exe->properties.GetProperty(L"fetchaudio"));
  740.   if (!fetchaudio.empty()) {
  741.     // Get All properties for fetching audio
  742.     VXIMapHolder fillerProp(VXIMapClone(fetchAudioFetchobj.GetValue()));
  743.     exe->properties.GetProperties(fillerProp);
  744.     pm->PlayAll(); // Finish playing already queued prompts
  745.     pm->PlayFiller(fetchaudio, fillerProp);
  746.   }
  747.   // (2.2) Load document and verify that dialog exists.
  748.   // (2.2.1) Load the VoiceXML document.
  749.   VXMLDocument document;
  750.   VXMLElement documentDialog;
  751.   VXIMapHolder documentFetchProps;
  752.   AttemptDocumentLoad(uri, fetchobj, document, documentFetchProps);
  753.   // (2.2.2) If there was a fragment, does the named dialog exist?
  754.   VXMLElement documentRoot = document.GetRoot();
  755.   documentDialog = FindDialog(documentRoot, fragment);
  756.   if (documentDialog == 0) {
  757.     vxistring msg;
  758.     if (fragment.empty())
  759.       msg = L"no dialog element found in " + uri;
  760.     else
  761.       msg = L"named dialog " + fragment + L" not found in " + uri;
  762.     log->StartDiagnostic(0) << L"VXI::PerformTransition - " << msg;
  763.     log->EndDiagnostic();
  764.     throw VXIException::InterpreterEvent(EV_ERROR_BADDIALOG, msg);
  765.   }
  766.    
  767.   // (3) Get Document language & find associated defaults.
  768.   // (3.1) Create a new property list containing the document properties.
  769.   PropertyList newProperties(*log);
  770. // WARNING: The defaults document has multiple languages and a language-independent '*' group.
  771. // This should really read the '*' group and then overlay the set specific to the active language.
  772.   // (3.2) Extract the language setting.
  773.   const VXIchar * language = newProperties.GetProperty(PropertyList::Language);
  774.   if (language == NULL) language = GENERIC_DEFAULTS;
  775.   // (3.3) Find the language defaults.
  776.   VXMLElement defaults;
  777.   VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
  778.   for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
  779.     VXMLNode child = *it;
  780.     // Look for a language node.
  781.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  782.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  783.     if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
  784.     vxistring id;
  785.     elem.GetAttribute(ATTRIBUTE_ID, id);
  786.     if (id == language || (id == GENERIC_DEFAULTS && defaults == 0))
  787.       defaults = elem;
  788.   }
  789.   if (defaults != 0)
  790.     newProperties.SetProperties(defaults, DEFAULTS_PROP, VXIMapHolder(NULL));
  791.   // newProperties should now contain the properties from the platform defaults plus
  792.   // the properties at document level.
  793.   newProperties.SetProperties(documentRoot, DOC_PROP, documentFetchProps);
  794.   // (3.4) Copy the document properties in and find the absolute URI. 
  795.   vxistring absoluteURI(L"");
  796.   vxistring applicationURI(L"");
  797.   tempStr = newProperties.GetProperty(PropertyList::AbsoluteURI, DOC_PROP);
  798.   if (tempStr == NULL) 
  799.     absoluteURI = uri.empty() ? L"" : uri.c_str();
  800.   else
  801.     absoluteURI = tempStr;
  802.   // (4) Load the application.
  803.   VXMLDocument application;
  804.   // (4.1) Get the application URI.
  805.   vxistring appuri;
  806.   documentRoot.GetAttribute(ATTRIBUTE_APPLICATION, appuri);
  807.   if (!appuri.empty() ) {
  808.     if ( (exe->applicationName.empty() || appuri != exe->applicationName)) {
  809.       // set applcation name
  810.       exe->applicationName = appuri;      
  811.       // (4.2) Initialize application fetch parameters.
  812.       VXIMapHolder appFetchobj;
  813.       if (appFetchobj.GetValue() == NULL)
  814.         throw VXIException::OutOfMemory();
  815.       vxistring appFragment;
  816.       newProperties.GetFetchobjURIs(documentRoot, appFetchobj, appuri,
  817.                                   appFragment);
  818.       if (appuri.empty()) {
  819.         log->LogError(214);
  820.         throw VXIException::InterpreterEvent(EV_ERROR_APP_BADURI);
  821.       }
  822.       newProperties.GetFetchobjCacheAttrs(documentRoot, PropertyList::Document,
  823.                                         appFetchobj);
  824.       // (4.3) Load the application and its properties (we must then restore 
  825.       // document properties) also let document converter 
  826.       // know it's root application
  827.       VXIMapClear(exe->appProperties.GetValue());      
  828.       AttemptDocumentLoad(appuri, appFetchobj, application, exe->appProperties,
  829.                           false, true);
  830.       // (4.3.1) If an application root document specifies an application root
  831.       //         element error.semantic is thrown.   
  832.       VXMLElement documentRootCheck = application.GetRoot();
  833.       vxistring appuriCheck;
  834.       documentRootCheck.GetAttribute(ATTRIBUTE_APPLICATION, appuriCheck);
  835.       if (!appuriCheck.empty())
  836.         throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  837.             L"application root element exists in application root document");
  838.       
  839.       newProperties.SetProperties(application.GetRoot(), APP_PROP, exe->appProperties);
  840.       // (4.4) Get the absolute URI for the application root. 
  841.       // transitioning to new application
  842.       tempStr = newProperties.GetProperty(PropertyList::AbsoluteURI, APP_PROP);
  843.       applicationURI = (tempStr == NULL ? L"" : tempStr);
  844.       // (4.4.1) Generate the final event.
  845.       throw JumpDoc(defaults, application, applicationURI,
  846.                   document, absoluteURI, documentDialog, isSubdialog,
  847.                   isSubmitElement, newProperties);
  848.     }
  849.     else {
  850.       // (4.5) leaf-to-leaf transition with same root application, and since
  851.       // root application has already been loaded, we re-use the previous loaded
  852.       // root document and uri
  853.       newProperties.SetProperties(exe->application.GetRoot(), APP_PROP, exe->appProperties);
  854.       // (4.5.1) Generate the final event.
  855.       throw JumpDoc(defaults, exe->application, exe->applicationURI,
  856.                   document, absoluteURI, documentDialog, isSubdialog,
  857.                   isSubmitElement, newProperties);
  858.     }
  859.   }
  860.   else {
  861.     // (5) Transition to another app, i.e: root app. uri is not the same or empty then
  862.     // we're no longer the same application, remove the application name
  863.     if (!exe->applicationName.empty()) 
  864.       exe->applicationName = L"";
  865.     
  866.     // (5.1) Generate the final event.
  867.     throw JumpDoc(defaults, application, applicationURI,
  868.                   document, absoluteURI, documentDialog, isSubdialog,
  869.                   isSubmitElement, newProperties);
  870.   }
  871. }
  872. // Finds the named dialog in the document.  If the name is empty, the first
  873. // item is returned.
  874. //
  875. VXMLElement VXI::FindDialog(const VXMLElement & start, const vxistring & name)
  876. {
  877.   if (log->IsLogging(2)) {
  878.     log->StartDiagnostic(2) << L"VXI::FindDialog(" << name << L")";
  879.     log->EndDiagnostic();
  880.   }
  881.   // (0) find the root node.
  882.   VXMLElement doc;
  883.   for (doc = start; doc != 0 && doc.GetName() != NODE_VXML;
  884.        doc = doc.GetParent());
  885.   if (doc == 0) return VXMLElement();
  886.   // (1) Walk through all elements at this level and find match.
  887.   for (VXMLNodeIterator it(doc); it; ++it) {
  888.     VXMLNode child = *it;
  889.     // (1.1) Only <form> & <menu> elements are considered.
  890.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  891.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  892.     VXMLElementType nodeName = elem.GetName();
  893.     if (nodeName != NODE_FORM && nodeName != NODE_MENU) continue;
  894.     // (1.2) If no dialog was specified, return the first one.
  895.     if (name.empty()) return elem;
  896.     // (1.3) Otherwise, look for an exact match.
  897.     vxistring id;
  898.     if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, id)) continue;
  899.     if (name == id)
  900.       return elem;
  901.   }
  902.   // (2) User attempted to GOTO to non-existant dialog or have an empty doc!
  903.   log->LogDiagnostic(2, L"VXI::FindDialog - no match found.");
  904.   return VXMLElement();
  905. }
  906. //#############################################################################
  907. // Document Loop
  908. //#############################################################################
  909. // return success/failure (can't throw error here as caller needs
  910. //  a chance to clean up
  911. // Also initialize new context (session scope)
  912. bool VXI::PushExecutionContext(const vxistring & sessionScript)
  913. {
  914.   log->LogDiagnostic(2, L"VXI::PushExecutionContext()");
  915.   if (log->IsLogging(4)) {
  916.     log->StartDiagnostic(4) << L"VXI::PushExecutionContext - stackDepth: "
  917.                             << stackDepth << L", max: " << DEFAULT_MAX_EXE_STACK_DEPTH;
  918.     log->EndDiagnostic();
  919.   }
  920.   // (1) Catch recursive <subdialog> case...
  921.   if (stackDepth >= DEFAULT_MAX_EXE_STACK_DEPTH) {
  922.     log->LogError(211);
  923.     return false;
  924.   }
  925.   // (2) Create new execution context.
  926.   ExecutionContext * ep = new ExecutionContext(rec, jsi, *log, exe);
  927.   if (ep == NULL) throw VXIException::OutOfMemory();
  928.   exe = ep;
  929.   ++stackDepth;
  930.   // (3) Init new context from channel (i.e. set up 'session' scope)
  931.   exe->script.PushScope(SCOPE_Session);
  932.   if (!sessionScript.empty()) exe->script.EvalScript(sessionScript);
  933.   exe->script.SetVarReadOnly(SCOPE_Session);
  934.   log->LogDiagnostic(2, L"VXI::PushExecutionContext - session variables "
  935.                      L"initialized");
  936.   // (4) Init new context defaults (i.e. set up platform defaults)
  937.   // (4.1) Find generic language properties.
  938.   VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
  939.   for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
  940.     VXMLNode child = *it;
  941.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  942.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  943.     if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
  944.     vxistring id;
  945.     elem.GetAttribute(ATTRIBUTE_ID, id);
  946.     if (id != GENERIC_DEFAULTS) continue;
  947.     exe->platDefaults = elem;
  948.     break;
  949.   }
  950.   if (exe->platDefaults == 0) {
  951.     log->LogError(223);
  952.     PopExecutionContext();
  953.     return false;
  954.   }
  955.   // (4.2) Install defaults.  We only need to worry about the properties (for
  956.   // document load) and the ECMA variables and scripts (for catch handlers on
  957.   // load failure).  The grammars & prompts may be safely ignored.
  958.   exe->properties.SetProperties(exe->platDefaults, DEFAULTS_PROP,
  959.                                 VXIMapHolder());
  960.   exe->script.PushScope(SCOPE_Defaults);
  961.   ProcessRootScripts(exe->platDefaults);
  962.   log->LogDiagnostic(2, L"VXI::PushExecutionContext - platform defaults "
  963.                      L"initialized");
  964.   return true;
  965. }
  966. void VXI::PopExecutionContext()
  967. {
  968.   if (log->IsLogging(4)) {
  969.     log->StartDiagnostic(4) << L"VXI::PopExecutionContext - current stackDepth: "
  970.                             << stackDepth << L", max: " << DEFAULT_MAX_EXE_STACK_DEPTH;
  971.     log->EndDiagnostic();
  972.   }
  973.   if (exe == NULL) return;
  974.   ExecutionContext * current = exe;
  975.   exe = current->next;
  976.   --stackDepth;
  977.   delete current;
  978. }
  979. void VXI::AttemptDocumentLoad(const vxistring & uri,
  980.                               const VXIMapHolder & uriProperties,
  981.                               VXMLDocument & doc,
  982.                               VXIMapHolder & docProperties,
  983.                               bool isDefaults,
  984.                               bool isRootApp)
  985. {
  986.   // Reset current uri
  987.   uriCurrent = uri;
  988.   
  989.   // (1) Create map to store document properties.
  990.   if (docProperties.GetValue() == NULL) 
  991.     throw VXIException::OutOfMemory();
  992.   // (2) Fetch the document
  993.   VXIbyte *cbuffer = NULL;
  994.   VXIulong bufferSize = 0;
  995.   int result = parser->FetchDocument(uri.c_str(), uriProperties, inet, cache,
  996.                                      *log, doc, docProperties, 
  997.                                      isDefaults, isRootApp,
  998.                                      &cbuffer, &bufferSize);
  999.   
  1000.   // If we didn't have a parse error, dump the VoiceXML document if
  1001.   // configured to do so, otherwise free the content buffer
  1002.   if ((result != 4) && (cbuffer)) {
  1003.     VXIString *key = NULL, *value = NULL;
  1004.     if ((log->IsLogging(SimpleLogger::DOC_DUMPS)) &&
  1005.         (log->LogContent(VXI_MIME_XML, cbuffer, bufferSize, &key, &value))) {
  1006.       vxistring diagUri(uri);
  1007.       const VXIValue * val = VXIMapGetProperty(docProperties.GetValue(),
  1008.                                                INET_INFO_ABSOLUTE_NAME);
  1009.       if (val != NULL && VXIValueGetType(val) == VALUE_STRING)
  1010.         diagUri = VXIStringCStr(reinterpret_cast<const VXIString *>(val));
  1011.       vxistring temp(L"URL");
  1012.       temp += L": ";
  1013.       temp += diagUri.c_str();
  1014.       temp += L", ";
  1015.       temp += VXIStringCStr(key);
  1016.       temp += L": ";
  1017.       temp += VXIStringCStr(value);
  1018.       log->LogDiagnostic(SimpleLogger::DOC_DUMPS, temp.c_str());
  1019.     }
  1020.     delete [] cbuffer;
  1021.     cbuffer = NULL;
  1022.     if (key) VXIStringDestroy(&key);
  1023.     if (value) VXIStringDestroy(&value);
  1024.   }
  1025.   
  1026.   // (3) Handle error conditions.
  1027.   switch (result) {
  1028.   case -1: // Out of memory
  1029.     throw VXIException::OutOfMemory();
  1030.   case 0:  // Success
  1031.     break;
  1032.   case 1: // Invalid parameter
  1033.   case 2: // Unable to open URI
  1034.     log->LogError(203);
  1035.     // now look at the http status code to throw the 
  1036.     // compiliant error.badfetch.http.<response code> 
  1037.     if (docProperties.GetValue()) {
  1038.       // retrieve response code
  1039.       VXIint iv = 0;
  1040.       const VXIValue *v = 
  1041.         VXIMapGetProperty(docProperties.GetValue(), INET_INFO_HTTP_STATUS);
  1042.       if( v && VXIValueGetType(v) == VALUE_INTEGER &&
  1043.           ((iv = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(v))) > 0)
  1044.          )
  1045.       {
  1046.         std::basic_stringstream<VXIchar> respStream;
  1047.         respStream << EV_ERROR_BADFETCH << L".http.";
  1048.         respStream << iv;
  1049.         
  1050.         throw VXIException::InterpreterEvent(respStream.str().c_str(), uri);    
  1051.       }
  1052.     }                      
  1053.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);  
  1054.   case 3: // Unable to read from URI
  1055.     log->LogError(204);
  1056.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1057.   case 4: // Unable to parse contents of URI
  1058.   { 
  1059.     VXIString *key = NULL, *value = NULL;
  1060.     if ((cbuffer) && 
  1061.         ((log->IsLogging(SimpleLogger::DOC_PARSE_ERROR_DUMPS)) ||
  1062.          (log->IsLogging(SimpleLogger::DOC_DUMPS))) &&
  1063.         (log->LogContent(VXI_MIME_XML, cbuffer, bufferSize, &key, &value)))
  1064.       log->LogError(205, VXIStringCStr(key), VXIStringCStr(value));
  1065.     else
  1066.       log->LogError(205);
  1067.     if (cbuffer) delete [] cbuffer;
  1068.     if (key) VXIStringDestroy(&key);
  1069.     if (value) VXIStringDestroy(&value);
  1070.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1071.   }
  1072.   default:
  1073.     log->LogError(206);
  1074.     throw VXIException::Fatal();
  1075.   }
  1076.   if (doc.GetRoot() == 0) {
  1077.     log->LogError(999, SimpleLogger::MESSAGE,
  1078.                   L"able to fetch initial document but node empty");
  1079.     throw VXIException::Fatal();
  1080.   }
  1081. }
  1082. void VXI::ProcessRootScripts(VXMLElement& doc)
  1083. {
  1084.   if (doc == 0) return;
  1085.   log->LogDiagnostic(2, L"VXI::ProcessRootScripts()");
  1086.   // Do <var> <script> and <meta> <property> elements
  1087.   for (VXMLNodeIterator it(doc); it; ++it) {
  1088.     VXMLNode child = *it;
  1089.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1090.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  1091.     VXMLElementType nodeName = elem.GetName();
  1092.     if (nodeName == NODE_VAR)
  1093.       var_element(elem);
  1094.     else if (nodeName == NODE_META)
  1095.       meta_element(elem);
  1096.     else if (nodeName == NODE_SCRIPT)
  1097.       script_element(elem);
  1098.     else if (nodeName == NODE_DATA)
  1099.       data_element(elem);
  1100.   }
  1101.   log->LogDiagnostic(2, L"VXI::ProcessRootScripts - done");
  1102. }
  1103. //#############################################################################
  1104. // Dialog Loop
  1105. //#############################################################################
  1106. // There are two cases in which this routine may be entered.
  1107. //   A. After a new document is loaded  (lastItem == 0)
  1108. //   B. After a return from <subdialog>
  1109. // and three ways in which the loop may be re-entered.
  1110. //   1. Jump to new dialog.
  1111. //   2. Jump to new form item in the existing dialog.
  1112. //   3. Jump to new dialog after recognizing a document scope grammar.
  1113. //
  1114. // The ECMA script scopes are reset accordingly:
  1115. //   anonymous: A B 1 2 3
  1116. //   local:     A   1 2 3
  1117. //   dialog:    A   1   3
  1118. void VXI::RunInnerLoop()
  1119. {
  1120.   log->LogDiagnostic(2, L"VXI::RunInnerLoop()");
  1121.   if (exe->currentDialog == 0) {
  1122.     log->LogError(999, SimpleLogger::MESSAGE, L"no current active document");
  1123.     return;
  1124.   }
  1125.   exe->currentFormItem = exe->lastItem; // next item to process; <goto> etc.
  1126.   exe->lastItem = VXMLElement();        // reset after <subdialog>
  1127.   bool newDialog = (exe->currentFormItem == 0);
  1128.   exe->playingPrompts = true;           // flag for prompting after events
  1129.   // Initialize flags
  1130.   bool unprocessedAnswer = false;
  1131.   // Run the dialog loop
  1132.   while (1) {
  1133.     if (stopRequested) throw VXIException::StopRequest();
  1134.     try {
  1135.       try {
  1136.         // (1) Adjust scope if necessary
  1137.         if (exe->script.CurrentScope(SCOPE_Anonymous)) exe->script.PopScope();
  1138.         if (exe->script.CurrentScope(SCOPE_Local))  exe->script.PopScope();
  1139.         // (2) Initialize dialog (if necessary)
  1140.         if (newDialog) {
  1141.           newDialog = false;
  1142.           exe->playingPrompts = true;
  1143.           // (2.1) Reset ECMA script scope.
  1144.           if (exe->script.CurrentScope(SCOPE_Dialog)) exe->script.PopScope();
  1145.           exe->script.PushScope(SCOPE_Dialog);
  1146.           // (2.2) Do 'initialization phase' from FIA.
  1147.           VXIMapHolder params(sdParams);
  1148.           sdParams = NULL;
  1149.           FormInit(exe->currentDialog, params);
  1150.           // (2.3) Do 'select phase' from FIA if the item is not already known
  1151.           if (exe->currentFormItem == 0) {
  1152.             DoInnerJump(exe->currentDialog, L"");
  1153.             break;
  1154.           }
  1155.         }
  1156.         // (3) The loop cases.
  1157.         // (3.1) Re-entering loop with an unprocessed recognition result.
  1158.         if (unprocessedAnswer == true) {
  1159.           unprocessedAnswer = false;
  1160.           HandleRemoteMatch(exe->currentDialog, exe->currentFormItem);
  1161.         }
  1162.         // (3.2) Re-entering loop after returning from a <subdialog>.
  1163.         else if (sdResult != NULL) {
  1164.           VXIMapHolder temp(sdResult);
  1165.           sdResult = NULL;
  1166.           ProcessReturn(exe->currentDialog, exe->currentFormItem, temp);
  1167.         }
  1168.         else if (sdEvent != NULL) {
  1169.           // The sdEvent is deallocated in the catch (below).
  1170.           throw VXIException::InterpreterEvent(*sdEvent);
  1171.         }
  1172.         // (3.3) Each time we enter collect phase, we get fresh local scope.
  1173.         // All filled and catched triggered form here will execute in this
  1174.         // scope.  The final local scope is popped when we leave.
  1175.         else {
  1176.           if (exe->script.CurrentScope(SCOPE_Local)) exe->script.PopScope();
  1177.           exe->script.PushScope(SCOPE_Local);
  1178.           mutex.Lock();
  1179.           if (haveExternalEvents) {
  1180.             vxistring event, message;
  1181.             if (externalEvents.size() > 0) {
  1182.               event = externalEvents.front();
  1183.               externalEvents.pop_front();
  1184.               message = externalMessages.front();
  1185.               externalMessages.pop_front();
  1186.             }
  1187.             if (externalEvents.empty()) haveExternalEvents = false;
  1188.             mutex.Unlock();
  1189.             throw VXIException::InterpreterEvent(event, message,
  1190.                                                  exe->currentDialog);
  1191.           }
  1192.           mutex.Unlock();
  1193.           
  1194.           // Do the 'collect phase & process phase' from the FIA.
  1195.           CollectPhase(exe->currentDialog, exe->currentFormItem);
  1196.         }
  1197.       }
  1198.       // Handles document events.
  1199.       catch (const VXIException::InterpreterEvent & e) {
  1200.         // Cleanup sdEvent (if necessary).
  1201.         if (sdEvent != NULL) {
  1202.           delete sdEvent;
  1203.           sdEvent = NULL;
  1204.         }
  1205.         
  1206.         if (log->IsLogging(2)) {
  1207.           log->StartDiagnostic(2) << L"VXI::RunInnerLoop - got exception: "
  1208.                                   << e.GetValue();
  1209.           log->EndDiagnostic();
  1210.         }
  1211.         if (exe->currentFormItem != 0)
  1212.           DoEvent(exe->currentFormItem, e);
  1213.         else
  1214.           DoEvent(exe->currentDialog, e);
  1215.       }
  1216.       DoInnerJump(exe->currentDialog, L"");
  1217.       break;
  1218.     }
  1219.     // Handles a goto a dialog in the same document.
  1220.     catch (JumpDialog & e) {
  1221.       exe->currentDialog = e.dialog;
  1222.       exe->currentFormItem = VXMLElement();
  1223.       newDialog = true;
  1224.     }
  1225.     // Handles <goto nextitem="...">.
  1226.     catch (const JumpItem & e) { 
  1227.       // sets the next form item to execute.
  1228.       exe->currentFormItem = e.item;
  1229.     }
  1230.     // Something down the call stack received a recognition.  it
  1231. // will be handled on the next iteration of the loop.
  1232.     catch (AnswerInformation & e) {
  1233.       if (exe->currentDialog != e.dialog) {
  1234.         exe->currentDialog = e.dialog;
  1235.         newDialog = true;
  1236.       }
  1237.       exe->currentFormItem = e.element;
  1238.       unprocessedAnswer = true;
  1239.     }
  1240.   } // while (1)
  1241.   log->LogDiagnostic(2, L"VXI::RunInnerLoop - done");
  1242. }
  1243. void VXI::ProcessReturn(const VXMLElement& form, const VXMLElement & item,
  1244.                         VXIMapHolder & result)
  1245. {
  1246.   log->LogDiagnostic(2, L"VXI::ProcessReturn()");
  1247.   vxistring filled;
  1248.   item.GetAttribute(ATTRIBUTE__ITEMNAME, filled);
  1249.   exe->script.SetValue(filled, reinterpret_cast<VXIValue*>(result.GetValue()));
  1250.   ProcessFilledElements(filled, form);
  1251. }
  1252. // Perform initialization associated with property tags and form level
  1253. // variables.  Reset the event and prompts counts.
  1254. //
  1255. void VXI::FormInit(const VXMLElement & form, VXIMapHolder & params)
  1256. {
  1257.   log->LogDiagnostic(2, L"VXI::FormInit()");
  1258.   // (1) Set the form properties.
  1259.   exe->properties.SetProperties(form, DIALOG_PROP, VXIMapHolder());
  1260.   // (2) Clear the prompt & event counts when the form is entered.
  1261.   exe->promptcounts.Clear();
  1262.   exe->eventcounts.Clear();
  1263.   exe->formitems.clear();
  1264.   vxistring itemname;
  1265.   form.GetAttribute(ATTRIBUTE__ITEMNAME, itemname);
  1266.   // (3) Walk through the form nodes.  Set up variables as necessary.
  1267.   for (VXMLNodeIterator it(form); it; ++it) {
  1268.     VXMLNode child = *it;
  1269.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1270.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  1271.  
  1272.     // (3.1) Handle <var> and <script> elements.
  1273.     try {
  1274.       VXMLElementType nodeName = elem.GetName();
  1275.       if (nodeName == NODE_VAR) {
  1276.         if (params.GetValue() != NULL) {
  1277.           // (3.1.1) Set matching variables to the value in the param list.
  1278.           // Each located parameter gets removed from the map.
  1279.           vxistring name;
  1280.           elem.GetAttribute(ATTRIBUTE_NAME, name);
  1281.           if (!name.empty()) {
  1282.             const VXIValue * value = VXIMapGetProperty(params.GetValue(),
  1283.                                                      name.c_str());
  1284.             if (value != NULL) {
  1285.               exe->script.MakeVar(name, value);
  1286.               VXIMapDeleteProperty(params.GetValue(), name.c_str());
  1287.               continue;
  1288.             }
  1289.           }
  1290.         }
  1291.         // (3.1.2) Otherwise, follow the normal proceedure.
  1292.         var_element(elem);
  1293.         continue;
  1294.       }
  1295.       else if (nodeName == NODE_SCRIPT)
  1296.         script_element(elem);
  1297.       else if (nodeName == NODE_DATA)
  1298.         data_element(elem);
  1299.       // (3.2) Ignore anything which is not a form item.
  1300.       if (!IsFormItemNode(elem)) continue;
  1301.       // (3.3) Initialize variables for each form item.
  1302.       vxistring name;
  1303.       vxistring expr;
  1304.       elem.GetAttribute(ATTRIBUTE__ITEMNAME, name);
  1305.       // (3.4) Throw on duplicate form item names
  1306.       if (exe->script.IsVarDeclared(name)) {
  1307.         vxistring msg = L"Duplicate form item name: " + name;
  1308.         log->LogError(217, SimpleLogger::MESSAGE, name.c_str());
  1309.         throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, msg);
  1310.       }
  1311.       elem.GetAttribute(ATTRIBUTE_EXPR, expr);
  1312.       exe->script.MakeVar(name, expr);
  1313.       nodeName = elem.GetName();
  1314.       if (nodeName == NODE_FIELD || nodeName == NODE_RECORD ||
  1315.           nodeName == NODE_TRANSFER || nodeName == NODE_INITIAL)
  1316.         exe->script.EvalScript(vxistring(L"var ") + name + L"$ = new Object();");
  1317.       exe->formitems.push_back(name);
  1318.     }
  1319.     catch( VXIException::InterpreterEvent &e ) {
  1320.       DoEvent(form, e);
  1321.       if (exe->script.CurrentScope(SCOPE_Anonymous)) 
  1322.         exe->script.PopScope();
  1323.     }
  1324.   }
  1325.   // (4) Did all incoming parameters get used?
  1326.   if (params.GetValue() != NULL && VXIMapNumProperties(params.GetValue()) != 0) {
  1327.     vxistring unused;
  1328.     const VXIchar *key;
  1329.     const VXIValue *value;
  1330.     VXIMapIterator *vi = VXIMapGetFirstProperty(params.GetValue(), &key,
  1331.                                                   &value);
  1332.     while (vi) {
  1333.       if (unused.empty()) unused = vxistring(L"Unused param(s): " ) + key;
  1334.   else unused += L", " + vxistring(key);
  1335.       if (VXIMapGetNextProperty(vi, &key, &value) != VXIvalue_RESULT_SUCCESS){
  1336.         VXIMapIteratorDestroy(&vi);
  1337.         vi = NULL;
  1338.       }
  1339.     }
  1340. log->LogError(218, SimpleLogger::MESSAGE, unused.c_str());
  1341.     throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1342.   }
  1343.   log->LogDiagnostic(2, L"VXI::FormInit - Done");
  1344. }
  1345. // Returns true if the element is a form item.
  1346. //
  1347. // This is the list from section 6.2 of the VXML 1.0 specification with one
  1348. // addition - <menu>.
  1349. //
  1350. bool VXI::IsFormItemNode(const VXMLElement & doc)
  1351. {
  1352.   VXMLElementType nodeName = doc.GetName();
  1353.   if (nodeName == NODE_FIELD  || nodeName == NODE_INITIAL   ||
  1354.       nodeName == NODE_RECORD || nodeName == NODE_TRANSFER  ||
  1355.       nodeName == NODE_OBJECT || nodeName == NODE_SUBDIALOG ||
  1356.       nodeName == NODE_MENU   || nodeName == NODE_BLOCK )
  1357.     return true;
  1358.   return false;
  1359. }
  1360. bool VXI::IsInputItemNode(const VXMLElement & doc)
  1361. {
  1362.   VXMLElementType nodeName = doc.GetName();
  1363.   if (nodeName == NODE_FIELD     || nodeName == NODE_RECORD ||
  1364.       nodeName == NODE_TRANSFER  || nodeName == NODE_OBJECT ||
  1365.       nodeName == NODE_SUBDIALOG || nodeName == NODE_MENU)
  1366.     return true;
  1367.   return false;
  1368. }
  1369. // Finds the named form item within the dialog.  If the name is empty, the
  1370. // first non-filled item is returned.
  1371. //
  1372. void VXI::DoInnerJump(const VXMLElement & elem, const vxistring & item)
  1373. {
  1374.   if (elem == 0) return;
  1375.   if (log->IsLogging(2)) {
  1376.     log->StartDiagnostic(2) << L"VXI::DoInnerJump(" << item << L")";
  1377.     log->EndDiagnostic();
  1378.   }
  1379.   // find form.
  1380.   VXMLElement current = elem;
  1381.   while (1) {
  1382.     VXMLElementType nodeName = current.GetName();
  1383.     if (nodeName == NODE_MENU)
  1384.       throw JumpItem(current); // Menu is a special case.
  1385.     if (nodeName == NODE_FORM)
  1386.       break;
  1387.     const VXMLElement & parent = current.GetParent();
  1388.     if (parent == 0) {
  1389.       log->LogError(225, SimpleLogger::MESSAGE, item.c_str());
  1390.       throw VXIException::Fatal();
  1391.     }
  1392.     current = parent;
  1393.   };
  1394.   // (2) If the next item is specified (such as from a previous <goto>, look
  1395.   //     for an exact match.
  1396.   if (!item.empty()) {
  1397.     for (VXMLNodeIterator it(current); it; ++it) {
  1398.       VXMLNode child = *it;
  1399.       if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1400.       const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  1401.       if (!IsFormItemNode(elem)) continue;
  1402.       vxistring name;
  1403.       if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, name)) continue;
  1404.       if (item == name) throw JumpItem(elem);
  1405.     }
  1406.   }
  1407.   // (3) Otherwise, find the first non-filled item with a valid condition.
  1408.   else {
  1409.     for (VXMLNodeIterator it(current); it; ++it) {
  1410.       VXMLNode child = *it;
  1411.       if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1412.       const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  1413.       if (!IsFormItemNode(elem)) continue;
  1414.       // Must use itemname here, as could be implicit name
  1415.       vxistring itemname;
  1416.       if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, itemname)) {
  1417.         log->LogError(999, SimpleLogger::MESSAGE,
  1418.                       L"unnamed item found on local jump");
  1419.         throw VXIException::Fatal();
  1420.       }
  1421.       if (exe->script.IsVarDefined(itemname)) continue;
  1422.       // OK if var is undefined, check condition
  1423.       vxistring cond;
  1424.       elem.GetAttribute(ATTRIBUTE_COND, cond);
  1425.       if (cond.empty() || exe->script.TestCondition(cond))
  1426.         throw JumpItem(elem);
  1427.     }
  1428.   }
  1429.   // log->LogDiagnostic(2, L"VXI::DoInnerJump - no match found.");
  1430. }
  1431. //#############################################################################
  1432. // Utility functions
  1433. //#############################################################################
  1434. // do_event() - top level call into event handler; deals with 
  1435. // event counts and defaults.
  1436. //
  1437. void VXI::DoEvent(const VXMLElement & item,
  1438.                   const VXIException::InterpreterEvent & e)
  1439. {
  1440.   // (0) Initial logging
  1441.   if (item == 0) {
  1442.     log->LogDiagnostic(0, L"VXI::DoEvent - invalid argument, ignoring event");
  1443.     return;
  1444.   }
  1445.   else if (log->IsLogging(2)) {
  1446.     log->StartDiagnostic(2) << L"VXI::DoEvent(" << e.GetValue() << L")";
  1447.     log->EndDiagnostic();
  1448.   }
  1449.   // (1) Disable queuing of prompts outside of event handler.
  1450.   exe->playingPrompts = false;
  1451.   VXIint32 numRethrows = 0;
  1452.   VXIException::InterpreterEvent event = e;
  1453.   do {
  1454.     try {
  1455.       // (2) Increments counts associated with this event.
  1456.       exe->eventcounts.Increment(item, event.GetValue());
  1457.       // (3) Process the current event.
  1458.       exe->eventSource = item;
  1459.       bool handled = do_event(item, event);
  1460.       exe->eventSource = VXMLElement();
  1461.       // (4) If the event was handled (a suitable <catch> was found),
  1462.   // this will complete event handling, and return to the FIA.
  1463.       if (handled) {
  1464.         if (log->IsLogging(2)) {
  1465.           log->StartDiagnostic(2) << L"VXI::DoEvent - event processed.";
  1466.           log->EndDiagnostic();
  1467.         }
  1468.         return;
  1469.       }
  1470.       // (5) No one willing to handle this event.  Exit.
  1471.       vxistring exitmessage(L"Unhandled exception: ");
  1472.       exitmessage += event.GetValue();
  1473.       VXIString * val = VXIStringCreate(exitmessage.c_str());
  1474.       if (val == NULL) throw VXIException::OutOfMemory();
  1475.       throw VXIException::Exit(reinterpret_cast<VXIValue*>(val));
  1476.     }
  1477.     // (5) The catch had a <throw> element inside.  Must process the new event.
  1478.     catch (const VXIException::InterpreterEvent & e) {
  1479.       event = e;
  1480.     }
  1481.   } while (++numRethrows < DEFAULT_MAX_EVENT_RETHROWS);
  1482.   // (7) Probable loop - catch X throws X?  Quit handling after a while. 
  1483.   log->LogError(216);
  1484.   vxistring exitmessage(L"Unhandled exception (suspected infinite loop)");
  1485.   VXIString * val = VXIStringCreate(exitmessage.c_str());
  1486.   if (val == NULL) throw VXIException::OutOfMemory();
  1487.   throw VXIException::Exit(reinterpret_cast<VXIValue*>(val));
  1488. }
  1489. bool VXI::do_event(const VXMLElement & item,
  1490.                    const VXIException::InterpreterEvent & e)
  1491. {
  1492.   vxistring event = e.GetValue();
  1493.   if (event.empty()) return false;
  1494.   // (1) Define the variables for the best match.
  1495.   int bestCount = 0;
  1496.   VXMLElement bestMatch;
  1497.   DocumentLevel stage = DOCUMENT;
  1498.   bool done = false;
  1499.   // Start from current item in document.
  1500.   VXMLElement currentNode = item;
  1501.   // (2) Get the count for the current event.
  1502.   int eventCount = exe->eventcounts.GetCount(item, event);
  1503.   if (eventCount == 0)
  1504.     return false;  // this shouldn't happen if increment was called
  1505.   if ( event[event.length() - 1] != '.' )
  1506.     event += L".";
  1507.   do {
  1508.     // (3) Walk through all nodes at this level looking for catch elements.
  1509.     for (VXMLNodeIterator it(currentNode); it; ++it) {
  1510.       VXMLNode child = *it;
  1511.       if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1512.       const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  1513.       // (3.1) Can this node catch events?
  1514.       VXMLElementType nodeName = elem.GetName();
  1515.       if (nodeName != NODE_CATCH) continue;
  1516.       // (3.2) Evaluate catch count;
  1517.       vxistring attr;
  1518.       int catchCount = 1;
  1519.       if (elem.GetAttribute(ATTRIBUTE_COUNT, attr)) {
  1520.         VXIchar * temp;
  1521.         catchCount = int(wcstol(attr.c_str(), &temp, 10));
  1522.       }
  1523.       if ( catchCount > eventCount ) continue;
  1524.       // (3.3) Evaluate its 'cond' attribute.
  1525.       elem.GetAttribute(ATTRIBUTE_COND, attr);
  1526.       if (!attr.empty() && !exe->script.TestCondition(attr)) continue;
  1527.       // (3.4) Get the catch's event list
  1528.       vxistring catchEvent;
  1529.       elem.GetAttribute(ATTRIBUTE_EVENT, catchEvent);
  1530.       // (3.5) Check if this handler can handle the thrown event
  1531.       bool canHandle = false;
  1532.       if ( catchEvent.empty() || catchEvent == L"." ) {
  1533.         canHandle = true;
  1534.       }
  1535.       else {
  1536.         // Is this handler either an exact match, or a
  1537.         // prefix match of the thrown event?
  1538.     TokenList catchList(catchEvent);
  1539.         TokenList::iterator i;
  1540.         for (i = catchList.begin(); i != catchList.end(); ++i) {
  1541.           catchEvent = *i;
  1542.           if ( catchEvent[catchEvent.length() - 1] != '.' )
  1543.             catchEvent += L".";
  1544.           if ( wcsncmp( catchEvent.c_str(), event.c_str(), catchEvent.length() ) == 0 ) {
  1545.             canHandle = true;
  1546.             break;
  1547.           }
  1548.         }
  1549.       }
  1550.       // (3.6) Keep the handler with the highest count less than or 
  1551.       // equal the event count
  1552.       if ( canHandle && ( catchCount > bestCount ) ) {
  1553.         bestCount = catchCount;
  1554.         bestMatch = elem;
  1555.       }
  1556.     }
  1557.     // (4) Decide where to search next.
  1558.     const VXMLElement & parent = currentNode.GetParent();
  1559.     if (parent != 0)
  1560.       currentNode = parent;
  1561.     else {
  1562.       if (stage == DOCUMENT) {
  1563.         stage = APPLICATION;
  1564.         currentNode = exe->application.GetRoot();
  1565.         if (currentNode != 0) continue;
  1566.         // Otherwise, fall through to application level.
  1567.       }
  1568.       if (stage == APPLICATION) {
  1569.         stage = DEFAULTS;
  1570.         // We resort to this level _only_ if no match has been found.
  1571.         if (bestCount < 1) {
  1572.           vxistring language = 
  1573.             toString(exe->properties.GetProperty(PropertyList::Language));
  1574.           // We clear the current node.  It will be set either to the global
  1575.           // language (*) or to an exact match.
  1576.           currentNode = VXMLElement();
  1577.           VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
  1578.           for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
  1579.             VXMLNode child = *it;
  1580.             // Look for a language node.
  1581.             if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1582.             const VXMLElement & elem 
  1583.               = reinterpret_cast<const VXMLElement &>(child);
  1584.             if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
  1585.             vxistring id;
  1586.             elem.GetAttribute(ATTRIBUTE_ID, id);
  1587.             if (id == language || (id == GENERIC_DEFAULTS && currentNode == 0))
  1588.               currentNode = elem;
  1589.           }
  1590.           if (currentNode != 0) continue;
  1591.         }
  1592.       }
  1593.       done = true;
  1594.     }
  1595.   } while (!done);
  1596.   if (bestCount == 0)
  1597.     return false;
  1598.   // Compliance Note:
  1599.   //
  1600.   // Because of the 'as-if-by-copy' semantic, we now execute the catch content,
  1601.   // including relative URIs, in the local scope.  So nothing special needs to
  1602.   // be done.
  1603.   VXIMapHolder vars;
  1604.   VXIValue * temp = NULL;
  1605.   temp = reinterpret_cast<VXIValue *>(VXIStringCreate(e.GetValue().c_str()));
  1606.   if (temp == NULL) throw VXIException::OutOfMemory();
  1607.   VXIMapSetProperty(vars.GetValue(), L"_event", temp);
  1608.   if (e.GetMessage().empty())
  1609.     temp = reinterpret_cast<VXIValue *>(VXIPtrCreate(NULL));
  1610.   else
  1611.     temp =reinterpret_cast<VXIValue*>(VXIStringCreate(e.GetMessage().c_str()));
  1612.   if (temp == NULL) throw VXIException::OutOfMemory();
  1613.   VXIMapSetProperty(vars.GetValue(), L"_message", temp);
  1614.   
  1615.   execute_content(bestMatch, vars, e.GetActiveDialog());
  1616.   return true;
  1617. }
  1618. // Top level call into executable content section.
  1619. // Called from <block>,<catch>, and <filled>
  1620. //
  1621. void VXI::execute_content(const VXMLElement& doc, const VXIMapHolder & vars,
  1622.                           const VXMLElement& activeDialog)
  1623. {
  1624.   log->LogDiagnostic(2, L"VXI::execute_content()");
  1625.   // (1) Due to the complex algorithm for throw element to address
  1626.   // "as-if-by-copy" semantics, the anonymous scope must be retained
  1627.   if (exe->script.CurrentScope(SCOPE_Anonymous) && doc.GetName() != NODE_CATCH) {
  1628.     // (1.1) Add a new scope and allow anonymous variables to be defined. 
  1629.     exe->script.PopScope();
  1630.     exe->script.PushScope(SCOPE_Anonymous);
  1631.   }
  1632.   
  1633.   // (1.2) Add anonymous scope if not exist
  1634.   if( !exe->script.CurrentScope(SCOPE_Anonymous) )
  1635.     exe->script.PushScope(SCOPE_Anonymous);    
  1636.   
  1637.   // (2) Set externally specified variables (if necessary).
  1638.   if (vars.GetValue() != NULL) {
  1639.     const VXIchar * key;
  1640.     const VXIValue * value;
  1641.     VXIMapIterator * i = VXIMapGetFirstProperty(vars.GetValue(), &key, &value);
  1642.     if (VXIValueGetType(value) == VALUE_PTR)
  1643.       exe->script.MakeVar(key, L"");    // Set to ECMAScript undefined
  1644.     else
  1645.       exe->script.MakeVar(key, value);
  1646.     while (VXIMapGetNextProperty(i, &key, &value) == VXIvalue_RESULT_SUCCESS) {
  1647.       if (VXIValueGetType(value) == VALUE_PTR)
  1648.         exe->script.MakeVar(key, L"");  // Set to ECMAScript undefined
  1649.       else
  1650.         exe->script.MakeVar(key, value);
  1651.     }
  1652.     VXIMapIteratorDestroy(&i);
  1653.   }
  1654.   // (3) Walk through the children and execute each node.
  1655.   for (VXMLNodeIterator it(doc); it; ++it) {
  1656.     VXMLNode child = *it;
  1657.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  1658.     const VXMLElement & elem = reinterpret_cast<VXMLElement &>(child);
  1659.     executable_element(elem, activeDialog);
  1660.   }
  1661. }
  1662. // Executable element dispatch
  1663. //
  1664. void VXI::executable_element(const VXMLElement & elem, const VXMLElement & activeDialog)
  1665. {
  1666.   if (elem == 0) {
  1667.     log->LogError(999, SimpleLogger::MESSAGE, L"empty executable element");
  1668.     return;
  1669.   }
  1670.   VXMLElementType nodeName = elem.GetName();
  1671.   if (log->IsLogging(2)) {
  1672.     log->StartDiagnostic(2) << L"VXI::executable_element - " << nodeName;
  1673.     log->EndDiagnostic();
  1674.   }
  1675.   if (nodeName == NODE_VAR)
  1676.     var_element(elem);
  1677.   else if (nodeName == NODE_ASSIGN)
  1678.     assign_element(elem);
  1679.   else if (nodeName == NODE_CLEAR)
  1680.     clear_element(elem);
  1681.   else if (nodeName == NODE_DISCONNECT)
  1682.     disconnect_element(elem);
  1683.   else if (nodeName == NODE_EXIT)
  1684.     exit_element(elem);
  1685.   else if (nodeName == NODE_GOTO)
  1686.     goto_element(elem);
  1687.   else if (nodeName == NODE_IF)
  1688.     if_element(elem);
  1689.   else if (nodeName == NODE_LOG)
  1690.     log_element(elem);
  1691.   else if (nodeName == NODE_PROMPT)
  1692.     executable_prompt(elem);
  1693.   else if (nodeName == NODE_REPROMPT)
  1694.     reprompt_element(elem, activeDialog);
  1695.   else if (nodeName == NODE_RETURN)
  1696.     return_element(elem);
  1697.   else if (nodeName == NODE_SCRIPT)
  1698.     script_element(elem);
  1699.   else if (nodeName == NODE_SUBMIT)
  1700.     submit_element(elem);
  1701.   else if (nodeName == NODE_THROW)
  1702.     throw_element(elem);
  1703.   else if (nodeName == NODE_DATA)
  1704.     data_element(elem);
  1705.   else
  1706.     log->LogError(999, SimpleLogger::MESSAGE,L"unexpected executable element");
  1707. }
  1708. /* 
  1709.  * Process <var> elements in current interp context.
  1710.  *
  1711.  * This differs from assign in that it makes new var in current scope
  1712.  * assign follows scope chain lookup for var name (and throws 
  1713.  *   error if fails)
  1714.  *
  1715.  * This is also used for initialiation of guard vars in field items
  1716.  *
  1717.  * <var> processing is compliated by the need to check for <param> values
  1718.  *  from subdialog calls.
  1719.  */
  1720. void VXI::var_element(const VXMLElement & doc)
  1721. {
  1722.   vxistring name;
  1723.   vxistring expr;
  1724.   doc.GetAttribute(ATTRIBUTE_NAME, name);
  1725.   doc.GetAttribute(ATTRIBUTE_EXPR, expr);
  1726.   if (log->IsLogging(2)) {
  1727.     log->StartDiagnostic(2) << L"VXI::var_element(name="" << name
  1728.                             << L"" expr = "" << expr << L"")";
  1729.     log->EndDiagnostic();
  1730.   }
  1731.   if (name.empty()) return;
  1732. // VXML 2.0 Spec (5.3.1) 
  1733.   if( name.find( L"." ) != vxistring::npos )
  1734.   {
  1735.     log->LogError(219, SimpleLogger::URI, name.c_str());
  1736.     throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  1737.                         L"variables cannot be declared with scope prefix "
  1738.                         L"and ECMAScript script variable names must not include a dot");
  1739.   }
  1740.   exe->script.MakeVar(name, expr);
  1741. }
  1742.  
  1743. // Process <assign> elements in current interpreter context
  1744. //
  1745. void VXI::assign_element(const VXMLElement & doc)
  1746. {
  1747.   vxistring name;
  1748.   doc.GetAttribute(ATTRIBUTE_NAME, name);
  1749.   if (name.empty()) return;
  1750.   vxistring expr;
  1751.   doc.GetAttribute(ATTRIBUTE_EXPR, expr);
  1752.   if (log->IsLogging(2)) {
  1753.     log->StartDiagnostic(2) << L"VXI::assign_element(name="" << name
  1754.                             << L"" expr = "" << expr << L""";
  1755.     log->EndDiagnostic();
  1756.   }
  1757.   exe->script.SetVar(name, expr);
  1758. }
  1759. // Handler for meta elements. Do nothing for now.
  1760. //
  1761. void VXI::meta_element(const VXMLElement & doc)
  1762. {
  1763.   // vxistring & name = doc.GetAttribute("name");
  1764.   // vxistring & name = doc.GetAttribute("content");
  1765.   // vxistring & name = doc.GetAttribute("http-equiv");
  1766. }
  1767. // Handler for clear elements.  This may resets all form items or a user
  1768. // specified subset.
  1769. //
  1770. void VXI::clear_element(const VXMLElement & doc)
  1771. {
  1772.   log->LogDiagnostic(2, L"VXI::clear_element()");
  1773.   // (1) Get the namelist.
  1774.   vxistring namelist;
  1775.   doc.GetAttribute(ATTRIBUTE_NAMELIST, namelist);
  1776.   // (2) Handle the easy case: empty namelist --> clear all
  1777.   if (namelist.empty()) {
  1778.     // (2.1) Clear prompt and event counts.
  1779.     exe->promptcounts.Clear();
  1780.     exe->eventcounts.Clear(false);
  1781.     // (2.2) The list of form items resides in the slot map.
  1782.     TokenList::iterator i;
  1783.     TokenList & formitems = exe->formitems;
  1784.     for (i = formitems.begin(); i != formitems.end(); ++i)
  1785.       exe->script.ClearVar(*i);
  1786.     return;
  1787.   }
  1788.   // (3) Handle case where user specifies form items.
  1789.   TokenList names(namelist);
  1790.   for (TokenList::const_iterator i = names.begin(); i != names.end(); ++i) {
  1791.     // (3.1) Check that the name is a real form item.  A linear search should
  1792.     //       be sufficently fast.
  1793.     TokenList::iterator j;
  1794.     TokenList & formitems = exe->formitems;
  1795.     for (j = formitems.begin(); j != formitems.end(); ++j)
  1796.       if (*i == *j) break;
  1797.     exe->script.ClearVar(*i);
  1798.     if (j != formitems.end()) {
  1799.       // (3.2) Clear the associated counters for form items.
  1800.       exe->promptcounts.Clear(*i);
  1801.       exe->eventcounts.Clear(*i);
  1802.     }
  1803.   }
  1804. }
  1805. void VXI::data_element(const VXMLElement & elem)
  1806. {
  1807.   log->LogDiagnostic(2, L"VXI::data_element()");
  1808.   // (1) Get fetch properties
  1809.   // (1.1) Get URI
  1810.   vxistring uri;
  1811.   elem.GetAttribute(ATTRIBUTE_SRC, uri);
  1812.   if (uri.empty()) {
  1813.     elem.GetAttribute(ATTRIBUTE_SRCEXPR, uri);
  1814.     if (!uri.empty())
  1815.       exe->script.EvalScriptToString(uri, uri);
  1816.   }
  1817.   // (1.2) Get Submit-specific properties.
  1818.   VXIMapHolder submitData;
  1819.   if (submitData.GetValue() == NULL) throw VXIException::OutOfMemory();
  1820.   vxistring att;
  1821.   elem.GetAttribute(ATTRIBUTE_NAMELIST, att);
  1822.   if (!att.empty()) {
  1823.     TokenList names(att);
  1824.     TokenList::const_iterator i;
  1825.     for (i = names.begin(); i != names.end(); ++i) {
  1826.       VXIValue * val = exe->script.GetValue(*i);
  1827.       if (val != NULL)
  1828.         VXIMapSetProperty(submitData.GetValue(), (*i).c_str(), val);
  1829.     }
  1830.   }
  1831.   // (1.3) Get fetch properties.
  1832.   VXIMapHolder fetchobj;
  1833.   if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  1834.   exe->properties.GetFetchobjBase(fetchobj);
  1835.   VXIMapHolder fetchAudioFetchobj(NULL);
  1836.   fetchAudioFetchobj = fetchobj;
  1837.   if (fetchAudioFetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  1838.   exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Data, fetchobj);
  1839.   exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Audio, fetchAudioFetchobj);
  1840.   // (1.4) Add namelist values
  1841.   if (submitData.GetValue() != NULL) {
  1842.     if (!exe->properties.GetFetchobjSubmitAttributes(elem, submitData,
  1843.                                                      fetchobj))
  1844.     {
  1845.       // This should never occur.
  1846.       log->StartDiagnostic(0) << L"VXI::data_element - couldn't set "
  1847.                                  L"the submit attributes.";
  1848.       log->EndDiagnostic();
  1849.       throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
  1850.     }
  1851.     submitData.Release(); // This map is now owned by fetchobj.
  1852.   }
  1853.   // (2) Start fetch audio.
  1854.   vxistring fetchaudio;
  1855.   if (!elem.GetAttribute(ATTRIBUTE_FETCHAUDIO, fetchaudio))
  1856.     fetchaudio = toString(exe->properties.GetProperty(L"fetchaudio"));
  1857.   if (!fetchaudio.empty()) {
  1858.     // Get All properties for fetching audio
  1859.     VXIMapHolder fillerProp(VXIMapClone(fetchAudioFetchobj.GetValue()));
  1860.     exe->properties.GetProperties(fillerProp);
  1861.     pm->PlayAll(); // Finish playing already queued prompts
  1862.     pm->PlayFiller(fetchaudio, fillerProp);
  1863.   }
  1864.   // (3) Get the data
  1865.   VXIMapHolder fetchStatus;
  1866.   DOMDocument *doc = 0;
  1867.   int result = parser->FetchXML(
  1868.       uri.c_str(), fetchobj, fetchStatus,
  1869.       inet, *log, &doc );
  1870.   switch (result) {
  1871.   case -1: // Out of memory
  1872.     throw VXIException::OutOfMemory();
  1873.   case 0:  // Success
  1874.     break;
  1875.   case 1: // Invalid parameter
  1876.   case 2: // Unable to open URI
  1877.     log->LogError(203);
  1878.     // now look at the http status code to throw the 
  1879.     // compiliant error.badfetch.http.<response code> 
  1880.     if (fetchStatus.GetValue()) {
  1881.       // retrieve response code
  1882.       VXIint iv = 0;
  1883.       const VXIValue *v = 
  1884.         VXIMapGetProperty(fetchStatus.GetValue(), INET_INFO_HTTP_STATUS);
  1885.       if( v && VXIValueGetType(v) == VALUE_INTEGER &&
  1886.           ((iv = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(v))) > 0)
  1887.          )
  1888.       {
  1889.         std::basic_stringstream<VXIchar> respStream;
  1890.         respStream << EV_ERROR_BADFETCH << L".http.";
  1891.         respStream << iv;
  1892.         
  1893.         throw VXIException::InterpreterEvent(respStream.str().c_str(), uri);    
  1894.       }
  1895.     }                      
  1896.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1897.   case 3: // Unable to read from URI
  1898.     log->LogError(204);
  1899.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1900.   case 4: // Unable to parse contents of URI
  1901.   { 
  1902.     log->LogError(205);
  1903.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1904.   }
  1905.   default:
  1906.     log->LogError(206);
  1907.     throw VXIException::Fatal();
  1908.   }
  1909.   // (4) Validate per VXML sandboxing
  1910.   const VXIValue * absurl = 
  1911.       VXIMapGetProperty(fetchStatus.GetValue(), INET_INFO_ABSOLUTE_NAME);
  1912.   if (absurl == NULL || VXIValueGetType(absurl) != VALUE_STRING) {
  1913. throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
  1914.   }
  1915.   // (5) Check access
  1916.   AccessControl ac(defAccessControl, doc);
  1917.   if (!ac.canAccess(VXIStringCStr(reinterpret_cast<const VXIString*>(absurl))))
  1918.     throw VXIException::InterpreterEvent(EV_ERROR_NOAUTHORIZ);
  1919.   // (6) Assign var
  1920.   vxistring varname;
  1921.   elem.GetAttribute(ATTRIBUTE_NAME, varname);
  1922.   if (!varname.empty()) {
  1923.     exe->script.MakeVar(varname, doc);
  1924.   }
  1925. }
  1926. // This implementation returns the values as VXIObjects.
  1927. //
  1928. void VXI::exit_element(const VXMLElement & doc)
  1929. {
  1930.   log->LogDiagnostic(2, L"VXI::exit_element()");
  1931.   VXIMapHolder exprMap(NULL); 
  1932.   vxistring namelist;
  1933.   doc.GetAttribute(ATTRIBUTE_NAMELIST, namelist);
  1934.   if (!namelist.empty()) {
  1935.     exprMap.Acquire(VXIMapCreate());
  1936.     if (exprMap.GetValue() == NULL) throw VXIException::OutOfMemory();
  1937.     TokenList names(namelist);
  1938.     TokenList::const_iterator i;
  1939.     for (i = names.begin(); i != names.end(); ++i) {
  1940.       if (!exe->script.IsVarDeclared(*i))
  1941.         throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1942.       VXIValue * val = exe->script.GetValue(*i);
  1943.       if (val != NULL)
  1944.         VXIMapSetProperty(exprMap.GetValue(), (*i).c_str(), val);