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

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 "GrammarManager.hpp"
  23. #include "SimpleLogger.hpp"
  24. #include "CommonExceptions.hpp"
  25. #include "PropertyList.hpp"
  26. #include "VXML.h"
  27. #include "DocumentModel.hpp"
  28. #include <sstream>
  29. //#############################################################################
  30. // Grammar description class
  31. //#############################################################################
  32. const VXIchar * const GrammarManager::DTMFTerm      = L"@dtmfterm";
  33. const VXIchar * const GrammarManager::FinalSilence  = L"@finalsilence";
  34. const VXIchar * const GrammarManager::MaxTime       = L"@maxtime";
  35. const VXIchar * const GrammarManager::RecordingType = L"@rectype";
  36. enum GrammarScope {
  37.   GRS_NONE,
  38.   GRS_FIELD,
  39.   GRS_DIALOG,
  40.   GRS_DOC
  41. };
  42. class GrammarInfo {
  43. public:
  44.   GrammarInfo(VXIrecGrammar *g, const vxistring &id, const VXMLElement &elem,
  45.               unsigned long gSeq, const VXIchar *srcexpr = NULL, const VXIchar *mime = NULL);
  46.   ~GrammarInfo();
  47.   VXIrecGrammar * GetRecGrammar() const    { return recgrammar; }
  48.   void SetRecGrammar(VXIrecGrammar *rg) { recgrammar = rg; }
  49.   void SetEnabled(bool b, GrammarScope aScope = GRS_NONE)
  50.   {
  51.     enabled = b;
  52.     activatedScope = aScope;
  53.   }
  54.   GrammarScope GetScope(void) const        { return activatedScope; }
  55.   unsigned long GetSequence(void) const    { return grammarSeq; }
  56.   bool IsEnabled() const                   { return enabled; }
  57.   bool IsDynamic() const                   { return !srcexpr.empty(); }
  58.   bool IsScope(GrammarScope s) const       { return s == scope; }
  59.   bool IsField(const vxistring & f) const  { return f == field; }
  60.   bool IsDialog(const vxistring & d) const { return d == dialog; }
  61.   bool IsDoc(const vxistring & d) const    { return d == docID; }
  62.   void GetElement(VXMLElement & e) const   { e = element; }
  63.   const VXIchar *GetSrcExpr() const { return srcexpr.c_str(); }
  64.   const VXIchar *GetMimeType() const { return mimetype.c_str(); }
  65. private:
  66.   void _Initialize(const VXMLElement & elem);
  67.   // This sets the field & dialog names.
  68.   const VXMLElement element;
  69.   VXIrecGrammar *   recgrammar;   // grammar handle returned from rec interface
  70.   GrammarScope      scope;
  71.   vxistring         field;
  72.   vxistring         dialog;
  73.   vxistring         docID;
  74.   vxistring         srcexpr;
  75.   vxistring         mimetype;
  76.   bool              enabled;
  77.   // store current activated info to determine precedence
  78.   GrammarScope      activatedScope;
  79.   unsigned long     grammarSeq;
  80. };
  81. GrammarInfo::GrammarInfo(VXIrecGrammar * g,
  82.                          const vxistring & id,
  83.                          const VXMLElement & elem,
  84.                          unsigned long gSeq,
  85.  const VXIchar *expr, const VXIchar *mime)
  86.     : element(elem), recgrammar(g), scope(GRS_NONE), docID(id), enabled(false),
  87.       srcexpr(expr?expr:L""), mimetype(mime?mime:L""), activatedScope(GRS_NONE), 
  88.       grammarSeq(gSeq)
  89. {
  90.   // (1) Determine the grammar scope.
  91.   // (1.1) Obtain default from position in the tree.
  92.   VXMLElement par = elem;
  93.   while (par != 0) {
  94.     VXMLElementType name = par.GetName();
  95.     if (name == NODE_INITIAL || name == NODE_FIELD ||
  96.         name == NODE_RECORD  || name == NODE_TRANSFER)
  97.       { scope = GRS_FIELD;  break; }
  98.     else if (name == NODE_FORM || name == NODE_MENU)
  99.       { scope = GRS_DIALOG; break; }
  100.     else if (name == NODE_VXML || name == DEFAULTS_LANGUAGE ||
  101.              name == DEFAULTS_ROOT)
  102.       { scope = GRS_DOC;    break; }
  103.     par = par.GetParent();
  104.   }
  105.   // (1.2) if scope explicitly specified in grammar or parent, override!
  106.   vxistring str;
  107.   elem.GetAttribute(ATTRIBUTE_SCOPE, str);
  108.   if (str.empty() && par != 0) {
  109.     par.GetAttribute(ATTRIBUTE_SCOPE, str);
  110.   }
  111.   if (!str.empty()) {
  112.     if (str == L"dialog")        scope = GRS_DIALOG;
  113.     else if (str == L"document") scope = GRS_DOC;
  114.   }
  115.   // (2) Do remaining initialization.
  116.   _Initialize(elem);
  117. }
  118. GrammarInfo::~GrammarInfo()
  119. {
  120.   // NOTE: 'recgrammar' must be freed externally.
  121. }
  122. void GrammarInfo::_Initialize(const VXMLElement & elem)
  123. {
  124.   if (elem == 0) return;
  125.   VXMLElementType name = elem.GetName();
  126.   if (name == NODE_FIELD || name == NODE_INITIAL ||
  127.       name == NODE_RECORD || name == NODE_TRANSFER)
  128.     elem.GetAttribute(ATTRIBUTE__ITEMNAME, field);
  129.   else if (name == NODE_FORM || name == NODE_MENU) {
  130.     elem.GetAttribute(ATTRIBUTE__ITEMNAME, dialog);
  131.     return;
  132.   }
  133.   _Initialize(elem.GetParent());
  134. }
  135. //#############################################################################
  136. class GrammarInfoUniv {
  137. public:
  138.   GrammarInfoUniv(VXIrecGrammar * g, const VXMLElement & e, const vxistring & l,
  139.                   const vxistring & n, unsigned long seq)
  140.     : element(e), recgrammar(g), languageID(l), name(n), enabled(false),
  141.       grammarSeq(seq) { }
  142.   ~GrammarInfoUniv() { } // NOTE: 'recgrammar' must be freed externally.
  143. public:
  144.   void SetEnabled(bool b)                  { enabled = b; }
  145.   bool IsEnabled() const                   { return enabled; }
  146.   VXIrecGrammar * GetRecGrammar() const    { return recgrammar; }
  147.   unsigned long GetSequence() const        { return grammarSeq; }
  148.   const vxistring & GetLanguage() const    { return languageID; }
  149.   const vxistring & GetName() const        { return name; }
  150.   void GetElement(VXMLElement & e) const   { e = element; }
  151. private:
  152.   const VXMLElement element;
  153.   VXIrecGrammar *   recgrammar;   // grammar handle returned from rec interface
  154.   vxistring         languageID;
  155.   vxistring         name;
  156.   bool              enabled;
  157.   unsigned long     grammarSeq;
  158. };
  159. //#############################################################################
  160. // Grammar description class
  161. //#############################################################################
  162. GrammarManager::GrammarManager(VXIrecInterface * r, const SimpleLogger & l)
  163.   : log(l), vxirec(r), grammarSequence(0)
  164. {
  165. }
  166. GrammarManager::~GrammarManager()
  167. {
  168.   ReleaseGrammars();
  169. }
  170. void GrammarManager::ThrowSpecificEventError(VXIrecResult err, OpType opType)
  171. {
  172.   switch (err) {
  173.     case VXIrec_RESULT_SUCCESS: break;
  174.     case VXIrec_RESULT_NO_RESOURCE:
  175.       throw VXIException::InterpreterEvent(EV_ERROR_NORESOURCE);
  176.     case VXIrec_RESULT_NO_AUTHORIZATION:
  177.       throw VXIException::InterpreterEvent(EV_ERROR_NOAUTHORIZ);
  178.     case VXIrec_RESULT_MAX_SPEECH_TIMEOUT:
  179.       throw VXIException::InterpreterEvent(EV_MAXSPEECH);
  180.     case VXIrec_RESULT_UNSUPPORTED_FORMAT:
  181.       throw VXIException::InterpreterEvent(EV_UNSUPPORT_FORMAT);
  182.     case VXIrec_RESULT_UNSUPPORTED_LANGUAGE:
  183.       throw VXIException::InterpreterEvent(EV_UNSUPPORT_LANGUAGE);
  184.     case VXIrec_RESULT_UNSUPPORTED_BUILTIN:
  185.       throw VXIException::InterpreterEvent(EV_UNSUPPORT_BUILTIN);
  186.     case VXIrec_RESULT_FETCH_TIMEOUT:
  187.     case VXIrec_RESULT_FETCH_ERROR:
  188.       throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH);
  189.     case VXIrec_RESULT_OUT_OF_MEMORY:
  190.       throw VXIException::OutOfMemory();
  191.     // these events are supported for hotword transfer functionality
  192.     case VXIrec_RESULT_CONNECTION_NO_AUTHORIZATION:
  193.       throw VXIException::InterpreterEvent(EV_TELEPHONE_NOAUTHORIZ);
  194.     case VXIrec_RESULT_CONNECTION_BAD_DESTINATION:
  195.       throw VXIException::InterpreterEvent(EV_TELEPHONE_BAD_DEST);
  196.     case VXIrec_RESULT_CONNECTION_NO_ROUTE:
  197.       throw VXIException::InterpreterEvent(EV_TELEPHONE_NOROUTE);
  198.     case VXIrec_RESULT_CONNECTION_NO_RESOURCE:
  199.       throw VXIException::InterpreterEvent(EV_TELEPHONE_NORESOURCE);
  200.     case VXIrec_RESULT_UNSUPPORTED_URI:
  201.       throw VXIException::InterpreterEvent(EV_TELEPHONE_UNSUPPORT_URI);
  202.     default:
  203.       if( opType == GRAMMAR )
  204.         throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR);
  205.   }
  206. }
  207. void GrammarManager::LoadGrammars(const VXMLElement& doc,
  208.                                   vxistring & documentID,
  209.                                   PropertyList & properties,
  210.                                   bool isDefaults)
  211. {
  212.   if (doc == 0) return;
  213.   // (1) Retrieve the ID for this document.  This is important for grammar
  214.   // activation.
  215.   if (doc.GetName() == NODE_VXML)
  216.     doc.GetAttribute(ATTRIBUTE__ITEMNAME, documentID);
  217.   // (2) Recursively find and build all grammars on this page.
  218.   if (isDefaults)
  219.     BuildUniversals(doc, properties);
  220.   else {
  221.     VXIMapHolder temp(NULL);
  222.     BuildGrammars(doc, documentID, properties, temp);
  223.   }
  224. }
  225. VXIrecGrammar * GrammarManager::BuildInlineGrammar(const VXMLElement & element,
  226.                                                 const VXIMapHolder & localProps)
  227. {
  228.   vxistring text, header, trailer;
  229.   // (1) Get CDATA in this element
  230.   GetEnclosedText(log, element, text);
  231.   // (2) Get attributes
  232.   vxistring mimeType, mode, root, tagFormat, xmlBase, xmlLang;
  233.   element.GetAttribute(ATTRIBUTE_TYPE, mimeType);
  234.   element.GetAttribute(ATTRIBUTE_MODE, mode);
  235.   element.GetAttribute(ATTRIBUTE_ROOT, root);
  236.   element.GetAttribute(ATTRIBUTE_TAGFORMAT, tagFormat);
  237.   element.GetAttribute(ATTRIBUTE_BASE, xmlBase);
  238.   element.GetAttribute(ATTRIBUTE_XMLLANG, xmlLang);
  239.   // (3) Determine the mimetype.
  240.   // if mimetype is empty but root is defined
  241.   // assume that this is srgs+xml mimetype
  242.   if( mimeType.empty() && !root.empty())
  243.     mimeType = L"application/srgs+xml";
  244.   if( mimeType.empty() && mode == L"dtmf" )
  245.     mimeType = REC_MIME_CHOICE_DTMF;
  246.   // (4) Is this an SRGS grammar?
  247.   if (mimeType.find(L"application/srgs+xml") == 0 &&
  248.       (mimeType.length() == 20 || mimeType[20] == L';'))
  249.   {
  250.     // All grammars have a language assigned during parsing.
  251.     header = L"<?xml version='1.0'?>"
  252.              L"<grammar xmlns='http://www.w3.org/2001/06/grammar'"
  253.              L" version='1.0' mode='";
  254.     header += mode;
  255.     header += L"' root='";
  256.     header += root;
  257.     if (!tagFormat.empty()) {
  258.       header += L"' tag-format='";
  259.       header += tagFormat;
  260.     }
  261.     if (!xmlLang.empty()) {
  262.       header += L"' xml:lang='";
  263.       header += xmlLang;
  264.     }
  265.     if (!xmlBase.empty()) {
  266.       header += L"' xml:base='";
  267.       // remove query-string (info. after ?) from the base if exists
  268.       vxistring::size_type qPos = xmlBase.find_first_of(L"?");
  269.       header += (qPos == vxistring::npos ? xmlBase : xmlBase.substr(0, qPos));
  270.     }
  271.     header += L"'>";
  272.     trailer = L"</grammar>";
  273.   }
  274.   // (5) Create the grammar.
  275.   if (log.IsLogging(2)) {
  276.     log.StartDiagnostic(2) << L"GrammarManager::LoadGrammars - type="
  277.            << mimeType << L", grammar=" << header << text << trailer << L">";
  278.     log.EndDiagnostic();
  279.   }
  280.   VXIrecGrammar * vg = NULL;
  281.   if (header.empty() && trailer.empty())
  282.     vg = GrammarManager::CreateGrammarFromString(vxirec, log, text,
  283.                                                  mimeType.c_str(),
  284.                                                  localProps);
  285.   else
  286.     vg = GrammarManager::CreateGrammarFromString(vxirec, log,
  287.                                                  header + text + trailer,
  288.                                                  mimeType.c_str(),
  289.                                                  localProps);
  290.   if (vg == NULL)
  291.     throw VXIException::InterpreterEvent(EV_ERROR_BAD_INLINE);
  292.   return vg;
  293. }
  294. // This function is used to recursively walk through the tree, loading and
  295. // speech or dtmf grammars which are found.
  296. //
  297. // NOTE: There is a rather messy optimization with properties, levelProperties,
  298. //       and localProps.  This made the code much harder to read but had
  299. //       significant performance benefits.  Be very careful!
  300. //
  301. void GrammarManager::BuildGrammars(const VXMLElement& doc,
  302.                                   const vxistring & documentID,
  303.                                   PropertyList & properties,
  304.                                   VXIMapHolder & levelProperties,
  305.                                   int menuAcceptLevel)
  306. {
  307.   // (1) Look for grammars in current nodes.
  308.   for (VXMLNodeIterator it(doc); it; ++it) {
  309.     VXMLNode child = *it;
  310.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  311.     const VXMLElement & element = reinterpret_cast<const VXMLElement &>(child);
  312.     VXMLElementType elementName = element.GetName();
  313.     // (2) Handle <grammar>
  314.     if (elementName == NODE_GRAMMAR) {
  315.       vxistring src;
  316.       // skip dynamic grammars
  317.       element.GetAttribute(ATTRIBUTE_SRCEXPR, src);
  318.       if (!src.empty()) {
  319.         vxistring mime;
  320.         element.GetAttribute(ATTRIBUTE_TYPE, mime);
  321.         if (mime.empty()) mime = VXI_MIME_SRGS;
  322.         AddGrammar(NULL, documentID, element, src.c_str(), mime.c_str());
  323.         continue;
  324.       }
  325.       element.GetAttribute(ATTRIBUTE_SRC, src);
  326.       // (2.1) Get the recognizer properties associated with this element.
  327.       if (levelProperties.GetValue() == NULL)
  328.         levelProperties.Acquire(GetRecProperties(properties));
  329.       VXIMapHolder localProps(NULL);
  330.       localProps = levelProperties;
  331.       SetGrammarLoadProperties(element, properties, localProps);
  332.       // (2.2) Does the grammar come from an external URI?
  333.       if (!src.empty()) {
  334.         if (log.IsLogging(2)) {
  335.           log.StartDiagnostic(2) << L"GrammarManager::LoadGrammars - <grammar "
  336.             L"src="" << src << L"">";
  337.           log.EndDiagnostic();
  338.         }
  339.         // (2.2.1) Generate error if fragment-only URI in external grammar
  340.         if (!src.empty() && src[0] == '#') {
  341.             log.LogError(215);
  342.             throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  343.                 L"a fragment-only URI is not permited in external grammar");
  344.         }
  345.         VXIMapHolder fetchobj;
  346.         if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  347.         properties.GetFetchobjCacheAttrs(element, PropertyList::Grammar,
  348.                                          fetchobj);
  349.         // (2.2.2) Load the grammar from the URI.
  350.         vxistring mimeType;
  351.         element.GetAttribute(ATTRIBUTE_TYPE, mimeType);
  352.         if (mimeType.empty()) mimeType = VXI_MIME_SRGS;
  353.         VXIrecGrammar * vg
  354.           = GrammarManager::CreateGrammarFromURI(vxirec, log, src,
  355.                                                  mimeType.c_str(),
  356.                                                  fetchobj.GetValue(),
  357.                                                  localProps);
  358.         AddGrammar(vg, documentID, element);
  359.       }
  360.       // (2.3) Otherwise this is an inlined grammar.
  361.       else {
  362.         log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <grammar>");
  363.         AddGrammar(BuildInlineGrammar(element, localProps),
  364.                    documentID, element);
  365.       }
  366.     }
  367.     // (3) Handle <link>.  Properties cannot be defined in <link>.
  368.     else if (elementName == NODE_LINK) {
  369.       log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <link>");
  370.       // (3.1) Create DTMF grammar is specified.
  371.       vxistring dtmf;
  372.       element.GetAttribute(ATTRIBUTE_DTMF, dtmf);
  373.       if (!dtmf.empty()) {
  374.         // Flatten grammar properties if necessary.
  375.         if (levelProperties.GetValue() == NULL)
  376.           levelProperties.Acquire(GetRecProperties(properties));
  377.         // NOTE: We don't need to worry about xml:lang, xml:base, or weight;
  378.         //       This is a generated (no xml:base) dtmf (xml:lang) grammar.
  379.         VXIrecGrammar * vg = NULL;
  380.         vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf,
  381.                                                      REC_MIME_CHOICE_DTMF,
  382.                                                      levelProperties);
  383.         if (vg == NULL)
  384.           throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);
  385.         AddGrammar(vg, documentID, element);
  386.       }
  387.       // (3.2) Create child grammars.
  388.       BuildGrammars(element, documentID, properties, levelProperties);
  389.     }
  390.     // (4) Handle <field>.
  391.     else if (elementName == NODE_FIELD) {
  392.       log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <field>");
  393.       // (4.1) Get the properties from the field.
  394.       bool doPop = properties.PushProperties(element);
  395.       VXIMapHolder localProps(NULL);
  396.       if (doPop || levelProperties.GetValue() == NULL) 
  397.         localProps.Acquire(GetRecProperties(properties));
  398.       else
  399.         localProps = levelProperties;
  400.       // Set weight, language, and fetchhint.
  401.       SetGrammarLoadProperties(element, properties, localProps);
  402.       // (4.2) Build option grammar (if necessary).
  403.       BuildOptionGrammars(documentID, element, localProps);
  404.       // (4.3) Add the built-in grammars (if they exist).
  405.       VXIrecGrammar * vg = NULL;
  406.       vxistring type;
  407.       element.GetAttribute(ATTRIBUTE_TYPE, type);
  408.       if (!type.empty()) {
  409.         vxistring newuri(L"builtin:grammar/");
  410.         newuri += type;
  411.         vg = GrammarManager::CreateGrammarFromURI(vxirec, log, newuri,
  412.                                                   NULL, NULL, localProps);
  413.         AddGrammar(vg, documentID, element);
  414.         newuri = L"builtin:dtmf/";
  415.         newuri += type;
  416.         vg = GrammarManager::CreateGrammarFromURI(vxirec, log, newuri,
  417.                                                   NULL, NULL, localProps);
  418.         AddGrammar(vg, documentID, element);
  419.       }
  420.       // (4.4) Recursively add grammars (this handles <grammar>)
  421.       BuildGrammars(element, documentID, properties, localProps);
  422.       if (doPop) properties.PopProperties();
  423.     }
  424.     // (5) Handle <menu>.
  425.     else if (elementName == NODE_MENU) {
  426.       log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <menu>");
  427.       // (5.1) Get the properties from the menu.
  428.       bool doPop = properties.PushProperties(element);
  429.       VXIMapHolder localProps(NULL);
  430.       if (doPop || levelProperties.GetValue() == NULL) 
  431.         localProps.Acquire(GetRecProperties(properties));
  432.       else
  433.         localProps = levelProperties;
  434.       // Set weight, language, and fetchhint.
  435.       SetGrammarLoadProperties(element, properties, localProps);
  436.       // (5.2) Get grammar accept attribute & handle <choice>s.
  437.       vxistring accept;
  438.       if (element.GetAttribute(ATTRIBUTE_ACCEPT, accept) == true &&
  439.           accept == L"approximate")
  440.       {
  441.         BuildGrammars(element, documentID, properties, localProps, 1);
  442.       }
  443.       else
  444.         BuildGrammars(element, documentID, properties, localProps, 0);
  445.       // (5.3) Undo pop if necessary.
  446.       if (doPop) properties.PopProperties();
  447.     }
  448.     // (6) Handle <choice>
  449.     else if (elementName == NODE_CHOICE) {
  450.       log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <choice>");
  451.       // (6.1) If there is a <grammar> tag, it overrides any implicit grammar.
  452.       // (6.1.1) Check for <grammar> element.
  453.       bool foundGrammar = false;
  454.       for (VXMLNodeIterator it(element); it; ++it) {
  455.         VXMLNode child = *it;
  456.         if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  457.         const VXMLElement & temp = reinterpret_cast<const VXMLElement&>(child);
  458.         if (temp.GetName() != NODE_GRAMMAR) continue;
  459.         foundGrammar = true;
  460.         break;
  461.       }
  462.       // (6.1.2) If found, apply recursion.
  463.       if (foundGrammar) {
  464.         // <choice> nodes can't contain properties.  Don't need to call Push.
  465.         BuildGrammars(element, documentID, properties, levelProperties);
  466.       }
  467.       // (6.2) DTMF & CDATA grammars.
  468.       vxistring dtmf;
  469.       element.GetAttribute(ATTRIBUTE_DTMF, dtmf);
  470.       vxistring text;
  471.       if (!foundGrammar)
  472.         GetEnclosedText(log, element, text);
  473.       if (!dtmf.empty() || !text.empty()) {
  474.         // (6.2.1) Set up properties.
  475.         // This should not ever be necessary, but just in case...
  476.         if (levelProperties.GetValue() == NULL) {
  477.           levelProperties.Acquire(GetRecProperties(properties));
  478.           log.LogError(999, SimpleLogger::MESSAGE,
  479.                        L"GrammarManager::BuildGrammars levelProperties "
  480.                        L"not already built for a <choice>");
  481.         }
  482.         // (6.2.2) Build DTMF grammars
  483.         if (!dtmf.empty()) {
  484.           VXIrecGrammar * vg = GrammarManager::CreateGrammarFromString(
  485.                                                      vxirec, log, dtmf,
  486.                                                      REC_MIME_CHOICE_DTMF,
  487.                                                      levelProperties);
  488.           if (vg == NULL)
  489.             throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);
  490.           AddGrammar(vg, documentID, element);
  491.         }
  492.         // (6.2.3) Build CDATA grammars (if not overriden by explicit <grammar>
  493.         if (!text.empty()) {
  494.           // Set accept property.  This changes the pseudo-read-only
  495.           // levelProperties, so we'll undo the change after creating the
  496.           // grammar.
  497.           vxistring accept;
  498.           if (element.GetAttribute(ATTRIBUTE_ACCEPT, accept)) {
  499.             if (accept == L"approximate")
  500.               AddParamValue(levelProperties, REC_GRAMMAR_ACCEPTANCE, 1);
  501.             else
  502.               AddParamValue(levelProperties, REC_GRAMMAR_ACCEPTANCE, 0);
  503.           }
  504.           else
  505.             AddParamValue(levelProperties, REC_GRAMMAR_ACCEPTANCE,
  506.                           menuAcceptLevel ? 1 : 0);
  507.           VXIrecGrammar * vg = GrammarManager::CreateGrammarFromString(
  508.                                                        vxirec, log, text,
  509.                                                        REC_MIME_CHOICE,
  510.                                                        levelProperties);
  511.           if (vg == NULL)
  512.             throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);
  513.           AddGrammar(vg, documentID, element);
  514.           // As promised, undo the REC_GRAMMAR_ACCEPTANCE property.
  515.           VXIMapDeleteProperty(levelProperties.GetValue(),
  516.                                REC_GRAMMAR_ACCEPTANCE);
  517.         }
  518.       }
  519.     }
  520.     // (7) Otherwise, nothing was found at this level.  Use recursion to check
  521.     //     the next level down.
  522.     else {
  523.       bool doPop = properties.PushProperties(element);
  524.       if (doPop) {
  525.         VXIMapHolder temp(NULL);
  526.         BuildGrammars(element, documentID, properties, temp, menuAcceptLevel);
  527.       }
  528.       else
  529.         BuildGrammars(element, documentID, properties, levelProperties,
  530.                       menuAcceptLevel);
  531.       if (doPop) properties.PopProperties();
  532.     }
  533.   }
  534. }
  535. // The defaults have a very simple structure.  The element in this case is the
  536. // desired language.  This has an 'id' attribute and contains the <link> and 
  537. // <property> elements.
  538. //
  539. void GrammarManager::BuildUniversals(const VXMLElement& doc,
  540.                                      PropertyList & properties)
  541. {
  542.   // (1) Collect all the properties defined at this level.  This is the only
  543.   //     point at which properties may be declared.
  544.   properties.PushProperties(doc);
  545.   VXIMapHolder localProps(GetRecProperties(properties));
  546.   // (2) Get the name of the language.
  547.   vxistring languageID;
  548.   if (!doc.GetAttribute(ATTRIBUTE_ID, languageID)) {
  549.     log.LogError(999, SimpleLogger::MESSAGE, L"defaults document corrupted - "
  550.                  L"no id attribute on <language> element");
  551.     throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH);
  552.   }
  553.   // (3) Find each link.
  554.   for (VXMLNodeIterator temp3(doc); temp3; ++temp3) {
  555.     VXMLNode child = *temp3;
  556.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  557.     const VXMLElement & link = reinterpret_cast<const VXMLElement&>(child);
  558.     if (link.GetName() != NODE_LINK) continue;
  559.     // (3.1) Get the name of the link.
  560.     
  561.     vxistring linkName;
  562.     if (!link.GetAttribute(ATTRIBUTE_EVENT, linkName)) {
  563.       log.LogError(999, SimpleLogger::MESSAGE, L"defaults document corrupted "
  564.                    L"- no event attribute on <link> element");
  565.       throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH);
  566.     }
  567.     // (3.2) Process dtmf grammars for this link.
  568.     vxistring dtmf;
  569.     link.GetAttribute(ATTRIBUTE_DTMF, dtmf);
  570.     if (!dtmf.empty()) {
  571.       // NOTE: We don't need to worry about xml:lang, xml:base, or weight;
  572.       //       This is a generated (no xml:base) dtmf (xml:lang) grammar.
  573.       VXIrecGrammar * vg = NULL;
  574.       vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf,
  575.                                                    REC_MIME_CHOICE_DTMF,
  576.                                                    localProps);
  577.       if (vg == NULL)
  578.         throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);
  579.       AddUniversal(vg, link, languageID, linkName);
  580.     }
  581.     // (4) And each grammar.
  582.     for (VXMLNodeIterator temp4(link); temp4; ++temp4) {
  583.       VXMLNode child = *temp4;
  584.       if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  585.       const VXMLElement & gram = reinterpret_cast<const VXMLElement &>(child);
  586.       if (gram.GetName() != NODE_GRAMMAR) continue;
  587.       // (4.1) Get caching properties on the <grammar> element.
  588.       SetGrammarLoadProperties(gram, properties, localProps);
  589.       // (4.2) Process remote grammars
  590.       vxistring src;
  591.       gram.GetAttribute(ATTRIBUTE_SRC, src);
  592.       if (!src.empty()) {
  593.         // (4.2.1) Generate error if fragment-only URI in external grammar
  594.         if (!src.empty() && src[0] == '#') {
  595.           log.LogError(215);
  596.           throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  597.                 L"a fragment-only URI is not permited in external grammar");
  598.         }
  599.         VXIMapHolder fetchobj;
  600.         if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  601.         properties.GetFetchobjCacheAttrs(gram, PropertyList::Grammar, fetchobj);
  602.         // (4.2.2) Load the grammar from the URI.
  603.         vxistring mimeType;
  604.         gram.GetAttribute(ATTRIBUTE_TYPE, mimeType);
  605.         VXIrecGrammar * vg
  606.           = GrammarManager::CreateGrammarFromURI(vxirec, log, src,
  607.                                                  mimeType.c_str(),
  608.                                                  fetchobj.GetValue(),
  609.                                                  localProps);
  610.         AddUniversal(vg, gram, languageID, linkName);
  611.       }
  612.       else {
  613.         AddUniversal(BuildInlineGrammar(gram, localProps),
  614.                      gram, languageID, linkName);
  615.       }
  616.     } // end <grammar>
  617.   } // end <link>
  618.   properties.PopProperties();
  619. }
  620. class VXIVectorHolder {
  621. public:
  622.   VXIVectorHolder() : _v(NULL) { _v = VXIVectorCreate(); }
  623.   ~VXIVectorHolder()           { if (_v != NULL) VXIVectorDestroy(&_v); }
  624.   VXIVector * GetValue() const { return _v; }
  625. private:
  626.   VXIVectorHolder(const VXIVectorHolder &); /* intentionally not defined. */
  627.   VXIVector * _v;
  628. };
  629. void GrammarManager::BuildOptionGrammars(const vxistring & documentID,
  630.                                          const VXMLElement & element,
  631.                                          const VXIMapHolder & props)
  632. {
  633.   log.LogDiagnostic(2, L"GrammarManager::BuildOptionGrammars()");
  634.   // (1) Create new vectors to hold the grammar.
  635.   VXIVectorHolder speechUtts;
  636.   VXIVectorHolder speechVals;
  637.   VXIVectorHolder dtmfUtts;
  638.   VXIVectorHolder dtmfVals;
  639.   VXIVectorHolder gramAcceptanceAttrs;
  640.   if (speechUtts.GetValue() == NULL || speechVals.GetValue() == NULL ||
  641.       dtmfUtts.GetValue()   == NULL || dtmfVals.GetValue()   == NULL ||
  642.       gramAcceptanceAttrs.GetValue() == NULL)
  643.     throw VXIException::OutOfMemory();
  644.   // (2) Get each option.
  645.   for (VXMLNodeIterator it(element); it; ++it) {
  646.     VXMLNode child = *it;
  647.     // (2.1) Ignore anything which isn't an option.
  648.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  649.     VXMLElement & domElem = reinterpret_cast<VXMLElement &>(child);
  650.     if (domElem.GetName() != NODE_OPTION) continue;
  651.     // (2.2) Get attributes and CDATA.
  652.     vxistring value;
  653.     domElem.GetAttribute(ATTRIBUTE_VALUE, value);
  654.     vxistring dtmf;
  655.     domElem.GetAttribute(ATTRIBUTE_DTMF, dtmf);
  656.     vxistring accept;
  657.     domElem.GetAttribute(ATTRIBUTE_ACCEPT, accept);
  658.     vxistring text;
  659.     GrammarManager::GetEnclosedText(log, domElem, text);
  660.     if (value.empty()) value = text;
  661.     if (value.empty()) value = dtmf;
  662.     // (2.3) Add to vectors as appropriate.
  663.     if (!text.empty()) {
  664.       VXIVectorAddElement(speechUtts.GetValue(),
  665.          reinterpret_cast<VXIValue *>(VXIStringCreate(text.c_str())));
  666.       VXIVectorAddElement(speechVals.GetValue(),
  667.          reinterpret_cast<VXIValue *>(VXIStringCreate(value.c_str())));
  668.     }
  669.     if (!dtmf.empty()) {
  670.       VXIVectorAddElement(dtmfUtts.GetValue(),
  671.          reinterpret_cast<VXIValue *>(VXIStringCreate(dtmf.c_str())));
  672.       VXIVectorAddElement(dtmfVals.GetValue(),
  673.          reinterpret_cast<VXIValue *>(VXIStringCreate(value.c_str())));
  674.     }
  675.     if( !text.empty() || !dtmf.empty() )
  676.     {
  677.       if (!accept.empty() && accept == L"approximate") {
  678.         VXIVectorAddElement(gramAcceptanceAttrs.GetValue(),
  679.           reinterpret_cast<VXIValue *>(VXIIntegerCreate(1)));
  680.         } else {
  681.           VXIVectorAddElement(gramAcceptanceAttrs.GetValue(),
  682.             reinterpret_cast<VXIValue *>(VXIIntegerCreate(0)));
  683.         }
  684.     }
  685.   }
  686.   // (3) Add grammars.
  687.   VXIrecGrammar * grammar;
  688.   if (VXIVectorLength(speechUtts.GetValue()) > 0) {
  689.     VXIrecResult err = vxirec->LoadGrammarOption(vxirec, props.GetValue(),
  690.                                                  speechUtts.GetValue(),
  691.                                                  speechVals.GetValue(),
  692.                                                  gramAcceptanceAttrs.GetValue(),
  693.                                                  FALSE, &grammar);
  694.     if( err != VXIrec_RESULT_SUCCESS )
  695.       ThrowSpecificEventError(err, GRAMMAR);
  696.     AddGrammar(grammar, documentID, element);
  697.   }
  698.   if (VXIVectorLength(dtmfUtts.GetValue()) > 0) {
  699.     VXIrecResult err = vxirec->LoadGrammarOption(vxirec, props.GetValue(),
  700.                                                  dtmfUtts.GetValue(),
  701.                                                  dtmfVals.GetValue(),
  702.                                                  gramAcceptanceAttrs.GetValue(),
  703.                                                  TRUE, &grammar);
  704.     if( err != VXIrec_RESULT_SUCCESS )
  705.       ThrowSpecificEventError(err, GRAMMAR);
  706.     AddGrammar(grammar, documentID, element);
  707.   }
  708.   log.LogDiagnostic(2, L"GrammarManager::BuildOptionGrammar - end");
  709. }
  710. bool GrammarManager::GetEnclosedText(const SimpleLogger & log,
  711.                                      const VXMLElement & doc, vxistring & str)
  712. {
  713.   log.LogDiagnostic(2, L"GrammarManager::GetEnclosedText()");
  714.   // Clear the result first...
  715.   str.erase();
  716.   for (VXMLNodeIterator it(doc); it; ++it) {
  717.     if ((*it).GetType() == VXMLNode::Type_VXMLContent) {
  718.       if (!str.empty()) str += ' ';
  719.       VXMLNode temp = *it;
  720.       str = reinterpret_cast<VXMLContent &>(temp).GetValue();
  721.     }
  722.   }
  723.   log.LogDiagnostic(2, L"GrammarManager::GetEnclosedText - end");
  724.   return !str.empty();
  725. }
  726. VXIrecGrammar*
  727. GrammarManager::CreateGrammarFromURI(VXIrecInterface * vxirec,
  728.                                      const SimpleLogger & log,
  729.                                      const vxistring & source,
  730.                                      const VXIchar * type,
  731.                                      const VXIMap * fetchProps,
  732.                                      const VXIMapHolder & recProps)
  733. {
  734.   if (log.IsLogging(3)) {
  735.     log.StartDiagnostic(3) << L"GrammarManager::LoadGrammar (type="
  736.                            << ((type == NULL) ? L"NULL" : type)
  737.                            << L"): " << source;
  738.     log.EndDiagnostic();
  739.   }
  740.   VXIrecGrammar * vg;
  741.   VXIrecResult err = vxirec->LoadGrammarURI(vxirec, recProps.GetValue(),
  742.                                             type,
  743.                                             source.c_str(),
  744.                                             fetchProps, &vg);
  745.     if( err != VXIrec_RESULT_SUCCESS )
  746.       ThrowSpecificEventError(err, GRAMMAR);
  747.   log.LogDiagnostic(2, L"GrammarManager::CreateGrammarFromURI - success");
  748.   return vg;
  749. }
  750. VXIrecGrammar*
  751. GrammarManager::CreateGrammarFromString(VXIrecInterface * vxirec,
  752.                                         const SimpleLogger & log,
  753.                                         const vxistring & source,
  754.                                         const VXIchar * type,
  755.                                         const VXIMapHolder & recProps)
  756. {
  757.   if (log.IsLogging(3)) {
  758.     log.StartDiagnostic(3) << L"GrammarManager::LoadGrammar (type=" << type
  759.                            << L"): " << source;
  760.     log.EndDiagnostic();
  761.   }
  762.   VXIrecGrammar * grammar;
  763.   VXIrecResult err = vxirec->LoadGrammarString(vxirec, recProps.GetValue(),
  764.                                                type, source.c_str(), &grammar);
  765.   if( err != VXIrec_RESULT_SUCCESS )
  766.       ThrowSpecificEventError(err, GRAMMAR);
  767.   log.LogDiagnostic(2, L"GrammarManager::CreateGrammarFromString - success");
  768.   return grammar;
  769. }
  770. void GrammarManager::AddGrammar(VXIrecGrammar * gr,
  771.                                 const vxistring & documentID,
  772.                                 const VXMLElement & elem,
  773. const VXIchar *expr, const VXIchar *type)
  774. {
  775.   GrammarInfo* gp = new GrammarInfo(gr, documentID, elem, GetGrammarSequence(), expr, type);
  776.   if (gp == NULL) throw VXIException::OutOfMemory();
  777.   if (log.IsLogging(2)) {
  778.     log.StartDiagnostic(2) << L"GrammarManager::AddGrammar(" << gr << L")"
  779.                            << L" seq: " << gp->GetSequence();
  780.     log.EndDiagnostic();
  781.   }
  782.   grammars.push_back(gp);
  783. }
  784. void GrammarManager::AddUniversal(VXIrecGrammar * gr,
  785.                                   const VXMLElement & elem,
  786.                                   const vxistring & langID,
  787.                                   const vxistring & name)
  788. {
  789.   GrammarInfoUniv * gp = new GrammarInfoUniv(gr, elem, langID, name,
  790.                                              GetGrammarSequence());
  791.   if (gp == NULL) throw VXIException::OutOfMemory();
  792.   if (log.IsLogging(2)) {
  793.     log.StartDiagnostic(2) << L"GrammarManager::AddGrammar(" << gr << L")"
  794.                            << L" seq: " << gp->GetSequence();
  795.     log.EndDiagnostic();
  796.   }
  797.   universals.push_back(gp);
  798. }
  799. void GrammarManager::DisableAllGrammars()
  800. {
  801.   if (log.IsLogging(2)) {
  802.     log.StartDiagnostic(2) << L"GrammarManager::DisableAllGrammars: Enter";
  803.     log.EndDiagnostic();
  804.   }
  805.   for (GRAMMARS::iterator i = grammars.begin(); i != grammars.end(); ++i) {
  806.     if ((*i)->IsEnabled()) {
  807.       vxirec->DeactivateGrammar(vxirec, (*i)->GetRecGrammar());
  808.       (*i)->SetEnabled(false);
  809.       if ((*i)->IsDynamic()){
  810.         if (log.IsLogging(2)) {
  811.           log.StartDiagnostic(2) << L"GrammarManager::DisableAllGrammars: Free dynamic grammar";
  812.           log.EndDiagnostic();
  813.         }
  814.         VXIrecGrammar * grammar = (*i)->GetRecGrammar();
  815.         vxirec->FreeGrammar(vxirec, &grammar);
  816.         (*i)->SetRecGrammar(NULL);
  817.       }
  818.     }
  819.   }
  820.   for (UNIVERSALS::iterator j = universals.begin(); j != universals.end(); ++j){
  821.     if ((*j)->IsEnabled()) {
  822.       vxirec->DeactivateGrammar(vxirec, (*j)->GetRecGrammar());
  823.       (*j)->SetEnabled(false);
  824.     }
  825.   }
  826.   if (log.IsLogging(2)) {
  827.     log.StartDiagnostic(2) << L"GrammarManager::DisableAllGrammars: Leave";
  828.     log.EndDiagnostic();
  829.   }
  830. }
  831. void GrammarManager::ReleaseGrammars()
  832. {
  833.   while (!grammars.empty()) {
  834.     GrammarInfo * gp = grammars.back();
  835.     grammars.pop_back();
  836.     VXIrecGrammar * grammar = gp->GetRecGrammar();
  837.     if (grammar) {
  838.       if (log.IsLogging(2)) {
  839.         log.StartDiagnostic(2) << L"GrammarManager::ReleaseGrammars(" << grammar
  840.                                << L")";
  841.         log.EndDiagnostic();
  842.       }
  843.       if (gp->IsEnabled())
  844.         vxirec->DeactivateGrammar(vxirec, grammar);
  845.       vxirec->FreeGrammar(vxirec, &grammar);
  846.       delete gp;
  847.     }
  848.   }
  849.   while (!universals.empty()) {
  850.     GrammarInfoUniv * gp = universals.back();
  851.     universals.pop_back();
  852.     VXIrecGrammar * grammar = gp->GetRecGrammar();
  853.     if (log.IsLogging(2)) {
  854.       log.StartDiagnostic(2) << L"GrammarManager::ReleaseGrammars(" << grammar
  855.                              << L")";
  856.       log.EndDiagnostic();
  857.     }
  858.     vxirec->FreeGrammar(vxirec, &grammar);
  859.     delete gp;
  860.   }
  861. }
  862. bool GrammarManager::EnableGrammars(const vxistring & documentID,
  863.                                     const vxistring & dialogName,
  864.                                     const vxistring & fieldName,
  865.                                     const VXIMapHolder & properties,
  866.                                     bool isModal,
  867.                                     Scripter &script,
  868.                                     PropertyList & propList)
  869. {
  870.   bool enabled = false;
  871.   if (log.IsLogging(2)) {
  872.     log.StartDiagnostic(2) << L"GrammarManager::EnableGrammar: Enter";
  873.     log.EndDiagnostic();
  874.   }
  875.   // (1) Do ordinary grammars...
  876.   // They are activated in reverse order.  This should activate them
  877.   // in the order of precedence the VXMl spec dictates (3.1.4)
  878.   for (GRAMMARS::iterator i = grammars.end(); i != grammars.begin();)
  879.   {
  880.     i--;
  881.     bool docsMatch    = (*i)->IsDoc(documentID);
  882.     bool dialogsMatch = docsMatch    && (*i)->IsDialog(dialogName);
  883.     bool fieldsMatch  = dialogsMatch && (*i)->IsField(fieldName);
  884.     bool fieldGram = (!fieldName.empty() && fieldsMatch);
  885.     bool dialogGram = (!isModal && (*i)->IsScope(GRS_DIALOG) && dialogsMatch);
  886.     bool docGram = (!isModal && (*i)->IsScope(GRS_DOC));
  887.     GrammarScope cScope = GRS_NONE;
  888.     if ( fieldGram ||
  889.         // Enable those field grammars matching our (field, form) pair
  890.          dialogGram ||
  891.         // Enable form grammars & dialog scope fields, if not modal
  892.          docGram
  893.         // Enable document level grammars, if not modal
  894.        )
  895.     {
  896.       // Dynamic grammars have to be loaded prior to activation.
  897.       bool dynamic = (*i)->IsDynamic();
  898.       if (dynamic) {
  899.         vxistring src;
  900.         try {
  901.           script.EvalScriptToString((*i)->GetSrcExpr(), src);
  902.         }
  903.         catch(...) {
  904.           // per spec, throw error.semantic on eval error
  905.           throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  906.         }
  907.         if (log.IsLogging(2)) {
  908.           log.StartDiagnostic(2) << L"GrammarManager::EnableGrammar, Load Dynamic: "
  909.                                  << (*i)->GetSrcExpr();
  910.           log.EndDiagnostic();
  911.         }
  912.         VXIMapHolder localProps(GetRecProperties(propList));
  913.         VXMLElement elem;
  914.         (*i)->GetElement(elem);
  915.         SetGrammarLoadProperties(elem, propList, localProps);
  916.         VXIMapHolder fetchobj;
  917.         if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
  918.         propList.GetFetchobjCacheAttrs(elem,PropertyList::Grammar,fetchobj);
  919.         VXIrecGrammar * vg
  920.           = GrammarManager::CreateGrammarFromURI(vxirec, log, src,
  921.                                                  (*i)->GetMimeType(),
  922.                                                  fetchobj.GetValue(),
  923.                                                  localProps);
  924.         (*i)->SetRecGrammar(vg);
  925.       }
  926.       if (log.IsLogging(2)) {
  927.         log.StartDiagnostic(2) << L"GrammarManager::EnableGrammar("
  928.                                << (*i)->GetRecGrammar() << L")";
  929.         log.EndDiagnostic();
  930.       }
  931.       // activate the grammar
  932.       VXIrecResult err = vxirec->ActivateGrammar(vxirec, properties.GetValue(),
  933.                                                  (*i)->GetRecGrammar());
  934.       if( err != VXIrec_RESULT_SUCCESS )
  935.        ThrowSpecificEventError(err, GRAMMAR);
  936.       // determine grammar scope
  937.       if (fieldGram)
  938.         cScope = GRS_FIELD;
  939.       else if (dialogGram)
  940.         cScope = GRS_DIALOG;
  941.       else if (docGram)
  942.         cScope = GRS_DOC;
  943.      (*i)->SetEnabled(true, cScope);
  944.       enabled = true;
  945.     }
  946.   }
  947.   // (2) Do universals
  948. #pragma message("Universals should be sensitive to the active language.")
  949.   // (2.1) Get the property.
  950.   const VXIValue * val = VXIMapGetProperty(properties.GetValue(),
  951.                                            PROP_UNIVERSALS);
  952.   if (val == NULL || VXIValueGetType(val) != VALUE_STRING)
  953.     return enabled;
  954.   const VXIchar * temp = VXIStringCStr(reinterpret_cast<const VXIString*>(val));
  955.   // (2.2) Convert the property into a set of tokens separated by the delimiter
  956.   //       characters.
  957.   const VXIchar * DELIM = L"|";
  958.   vxistring request = DELIM;
  959.   for (; *temp != ''; ++temp) {
  960.     VXIchar t = *temp;
  961.     if (t == ' ' || t == 't' || t == 'r' || t == 'n')
  962.       request += DELIM;
  963.     else 
  964.       request += t;
  965.   }
  966.   request += DELIM;
  967.   // (2.3) If the universals string is 'none', we are done.
  968.   if (request == L"|none|")
  969.     return enabled;
  970.   // (2.4) Check for all anywhere in the string.
  971.   bool doAll = (request.find(L"|all|") != vxistring::npos);
  972.   // (2.5) Now walk the list.
  973.   for (UNIVERSALS::iterator j = universals.begin(); j != universals.end(); ++j)
  974.   {
  975.     bool doThis = doAll;
  976.     if (!doThis) {
  977.       vxistring delimName = DELIM;
  978.       delimName += (*j)->GetName();
  979.       delimName += DELIM;
  980.       doThis = (request.find(delimName) != vxistring::npos);
  981.     }
  982.     if (doThis) {
  983.       VXIrecResult err = vxirec->ActivateGrammar(vxirec, properties.GetValue(),
  984.                                                  (*j)->GetRecGrammar());
  985.       if (err != VXIrec_RESULT_SUCCESS)
  986.         ThrowSpecificEventError(err, GRAMMAR);
  987.       (*j)->SetEnabled(true);
  988.       enabled = true;
  989.     }
  990.   }
  991.   return enabled;
  992. }
  993. // This function is responsible for calling the VXIrec level Recognize function
  994. // and then mapping the grammar back to the corresponding VXML node.
  995. //
  996. GrammarManager::RecResult GrammarManager::Recognize(const VXIMapHolder        & properties,
  997.                               VXIrecRecognitionResult * & result)
  998. {
  999.   // (1) Attempt a recognition.
  1000.   VXIrecResult err = vxirec->Recognize(vxirec, properties.GetValue(), &result);
  1001.   if (err != VXIrec_RESULT_SUCCESS && result != NULL) {
  1002.     result->Destroy(&result);
  1003. result = NULL;
  1004.   }
  1005.   // (2) Process return value.
  1006.   switch (err) {
  1007.   case VXIrec_RESULT_SUCCESS:
  1008.     break;
  1009.   default:
  1010.     // try to throw any specific event for the error before bail out
  1011.     ThrowSpecificEventError(err, RECOGNIZE);
  1012.     // throw an internal error
  1013.     log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - "
  1014.       L"VXIrecInterface::Recognize returned " << int (err);
  1015.     log.EndDiagnostic();
  1016.     log.LogError(420, SimpleLogger::MESSAGE,
  1017.                  L"function did not return the expected VXIrecSUCCESS result");
  1018.     return GrammarManager::InternalError;
  1019.   }
  1020.   // (3) Is the result structure valid?
  1021.   if (result == NULL) {
  1022.     log.LogError(420, SimpleLogger::MESSAGE,
  1023.                  L"function returned VXIrecSUCCESS but did not allocate "
  1024.                  L"an answer structure");
  1025.     return GrammarManager::InternalError;
  1026.   }
  1027.   if (result->xmlresult == NULL) {
  1028.     log.LogError(420, SimpleLogger::MESSAGE,
  1029.                  L"function returned VXIrecSUCCESS but did not allocate "
  1030.                  L"an XML result");
  1031.     result->Destroy(&result);    
  1032. result = NULL;
  1033.     return GrammarManager::InternalError;
  1034.   }
  1035.   return GrammarManager::Success;
  1036. }
  1037. bool GrammarManager::FindMatchedElement(const vxistring & id,
  1038.                                         VXMLElement & match) const
  1039. {
  1040.   unsigned long trash;
  1041.   return FindMatchedElement(id, match, trash);
  1042. }
  1043. // Find the VXML element associated with the matched grammar.
  1044. //
  1045. bool GrammarManager::FindMatchedElement(const vxistring & id,
  1046.                                         VXMLElement & match,
  1047.                                         unsigned long & grammarInfo) const
  1048. {
  1049.   const VXIrecGrammar * bestGrammar = NULL;
  1050.   if (id.empty()) return false;
  1051.   VXIrecResult err = vxirec->GetMatchedGrammar(vxirec, id.c_str(),
  1052.                                                &bestGrammar);
  1053.   if (err != VXIrec_RESULT_SUCCESS) {
  1054.     log.LogError(420, SimpleLogger::MESSAGE,
  1055.                  L"function failed to return a grammar match");
  1056.     return false;
  1057.   }
  1058.   for (GRAMMARS::const_iterator i = grammars.begin(); i!=grammars.end(); ++i) {
  1059.     if ((*i)->GetRecGrammar() != bestGrammar) continue;
  1060.     if (!(*i)->IsEnabled()) {
  1061.       log.StartDiagnostic(0) << L"GrammarManager::FindMatchedElement - "
  1062.           L"Grammar <" << (*i)->GetRecGrammar() << L"> is not activated!";
  1063.       log.EndDiagnostic();
  1064.       log.LogError(420, SimpleLogger::MESSAGE,
  1065.                    L"function returned an inactive grammar");
  1066.       return false;
  1067.     }
  1068.     (*i)->GetElement(match);
  1069.     grammarInfo = (*i)->GetSequence();
  1070.     return true;
  1071.   }
  1072.   for (UNIVERSALS::const_iterator j = universals.begin();
  1073.        j != universals.end(); ++j)
  1074.   {
  1075.     if ((*j)->GetRecGrammar() != bestGrammar) continue;
  1076.     if (!(*j)->IsEnabled()) {
  1077.       log.LogError(420, SimpleLogger::MESSAGE,
  1078.                    L"function returned an inactive grammar");
  1079.       return false;
  1080.     }
  1081.     (*j)->GetElement(match);
  1082.     grammarInfo = (*j)->GetSequence();
  1083.     return true;
  1084.   }
  1085.   log.LogError(420, SimpleLogger::MESSAGE,
  1086.                L"function returned a non-existent grammar");
  1087.   return false;
  1088. }
  1089. // compare the precedence of info1 against info2, return 1 if info1 has
  1090. // higher precedence than info2, -1 otherwise, 0 if both has the same precedence
  1091. int GrammarManager::CompareGrammar(const unsigned long grammar1,
  1092.                                    const unsigned long grammar2)
  1093. {
  1094.   // Is either grammar an universal?
  1095.   bool firstIsUniversal = false;
  1096.   bool secondIsUniversal = false;
  1097.   for (UNIVERSALS::const_iterator j = universals.begin();
  1098.        j != universals.end(); ++j)
  1099.   {
  1100.     unsigned long temp = (*j)->GetSequence();
  1101.     if (temp == grammar1) firstIsUniversal = true;
  1102.     if (temp == grammar2) secondIsUniversal = true;
  1103.   }
  1104.   if (firstIsUniversal && secondIsUniversal) {
  1105.     if (grammar1 < grammar2) return 1;
  1106.     if (grammar1 > grammar2) return -1;
  1107.     return 0;
  1108.   }
  1109.   if (firstIsUniversal) return 1;
  1110.   if (secondIsUniversal) return -1;
  1111.   // The remaining grammars are not universals.
  1112.   const GrammarInfo * info1 = NULL;
  1113.   const GrammarInfo * info2 = NULL;
  1114.   for (GRAMMARS::const_iterator i = grammars.begin(); i!=grammars.end(); ++i) {
  1115.     unsigned long temp = (*i)->GetSequence();
  1116.     if (temp == grammar1) info1 = *i;
  1117.     if (temp == grammar2) info2 = *i;
  1118.   }
  1119.   if (info1 && info2) {
  1120.     GrammarScope s1 = info1->GetScope();
  1121.     GrammarScope s2 = info2->GetScope();
  1122.     if( info1 == info2 ) return 0;
  1123.     if( s1 == GRS_NONE || s2 == GRS_NONE ) return 0; // unable to determine
  1124.     if( s1 < s2 ) return 1;
  1125.     if( s1 > s2 ) return -1;
  1126.     if( s1 == s2 ) {
  1127.       // if both have the same precedence, use document order
  1128.       // to determine
  1129.       if( info1->GetSequence() < info2->GetSequence() )
  1130.         return 1;
  1131.       else if(info1->GetSequence() > info2->GetSequence())
  1132.         return -1;
  1133.       else return 0;
  1134.     }
  1135.   }
  1136.   return 0; // unable to determine
  1137. }
  1138. GrammarManager::RecResult GrammarManager::Record(const VXIMapHolder & properties,
  1139.                            bool flushBeforeRecord,
  1140.                            VXIrecRecordResult * & resultStruct)
  1141. {
  1142.   const VXIMap *finalProp = NULL;
  1143.   VXIMapHolder addlProp(NULL);
  1144.   if (flushBeforeRecord) {
  1145.     addlProp = properties;
  1146.     AddParamValue(addlProp, REC_DTMF_FLUSH_QUEUE, true);
  1147.     finalProp = addlProp.GetValue();
  1148.   } else {
  1149.     finalProp = properties.GetValue();
  1150.   }
  1151.   VXIrecResult err = vxirec->Record(vxirec, finalProp, &resultStruct);
  1152.   if (err != VXIrec_RESULT_SUCCESS) {
  1153.     // try to throw a specific event for the error before bailing out
  1154.     ThrowSpecificEventError(err, RECORD);
  1155.     
  1156.     // throw an internal error if specific event cannot be thrown
  1157.     log.StartDiagnostic(0) << L"GrammarManager::Record - "
  1158.       L"VXIrecInterface::Record returned " << int (err);
  1159.     log.EndDiagnostic();
  1160.     log.LogError(421, SimpleLogger::MESSAGE,
  1161.                  L"function did not return the expected VXIrecSUCCESS result");
  1162.     if (resultStruct) {
  1163.       resultStruct->Destroy(&resultStruct);
  1164.       resultStruct = NULL;
  1165.     }
  1166.     return GrammarManager::InternalError;
  1167.   }
  1168.   if (resultStruct == NULL) {
  1169.     log.LogError(421, SimpleLogger::MESSAGE,
  1170.                  L"function returned VXIrecSUCCESS but did not allocate "
  1171.                  L"an answer structure");
  1172.     return GrammarManager::InternalError;
  1173.   }
  1174.   if (resultStruct->waveform == NULL && resultStruct->xmlresult == NULL) {
  1175.     log.LogError(421, SimpleLogger::MESSAGE,
  1176.                  L"function did not produce a recording or XML result");
  1177.     return GrammarManager::InternalError;
  1178.   }
  1179.   if (resultStruct->waveform != NULL && resultStruct->duration == 0) {
  1180.     log.LogError(421, SimpleLogger::MESSAGE,
  1181.                  L"function returned invalid recording duration");
  1182.     return GrammarManager::InternalError;
  1183.   }
  1184.   return GrammarManager::Success;
  1185. }
  1186. void GrammarManager::SetInputMode(const VXIchar *mode, VXIMapHolder &props) const
  1187. {
  1188.   int modeVal = REC_INPUT_MODE_DTMF_SPEECH;
  1189.   if( mode ) {
  1190.     vxistring value(mode);
  1191.     if (value == L"voice dtmf" || value == L"dtmf voice")
  1192.       modeVal = REC_INPUT_MODE_DTMF_SPEECH;
  1193.     else if (value == L"voice")
  1194.       modeVal = REC_INPUT_MODE_SPEECH;
  1195.     else if (value == L"dtmf")
  1196.       modeVal = REC_INPUT_MODE_DTMF;
  1197.     else {
  1198.       log.StartDiagnostic(0) << L"GrammarManager::SetInputMode - "
  1199.         L"Property '" << REC_INPUT_MODES << L"' contains unrecognized value "" << value <<
  1200.         L"", deliberately set to "voice dtmf"";
  1201.       log.EndDiagnostic();
  1202.     }
  1203.   }
  1204.   AddParamValue(props, REC_INPUT_MODES, modeVal);     
  1205. }                              
  1206. VXIMap * GrammarManager::GetRecProperties(const PropertyList & props,
  1207.                                           int timeout) const
  1208. {
  1209.   VXIMapHolder m;
  1210.   if (m.GetValue() == NULL) throw VXIException::OutOfMemory();
  1211.   // (1) Retrieve flattened property list.
  1212.   props.GetProperties(m);
  1213.   // (2) Convert & manipulate the properties.
  1214.   const VXIchar * j;
  1215.   VXIint time;
  1216.   VXIflt32 fraction;
  1217.   // (2.1) Language
  1218.   j = props.GetProperty(PropertyList::Language);
  1219.   if (j != NULL)
  1220.     AddParamValue(m, REC_LANGUAGE, j);
  1221.   // (2.2) Completion timeout
  1222.   j = props.GetProperty(PROP_COMPLETETIME);
  1223.   if (j != NULL ) {
  1224.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1225.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1226.     AddParamValue(m, REC_TIMEOUT_COMPLETE, time);
  1227.   }
  1228.   
  1229.   // (2.3) Incompletion timeout
  1230.   j = props.GetProperty(PROP_INCOMPLETETIME);
  1231.   if (j != NULL ) {
  1232.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1233.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1234.     AddParamValue(m, REC_TIMEOUT_INCOMPLETE, time);
  1235.   }
  1236.   
  1237.   // (2.4) Inter-Digit timeout
  1238.   j = props.GetProperty(PROP_INTERDIGITTIME);
  1239.   if (j != NULL ) {
  1240.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1241.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1242.     AddParamValue(m, REC_DTMF_TIMEOUT_INTERDIGIT, time);
  1243.   }
  1244.   
  1245.   // (2.5) Inter-Digit timeout
  1246.   j = props.GetProperty(PROP_TERMTIME);
  1247.   if (j != NULL ) {
  1248.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1249.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1250.     AddParamValue(m, REC_DTMF_TIMEOUT_TERMINATOR, time);
  1251.   }
  1252.   
  1253.   // (2.6) Confidence level
  1254.   j = props.GetProperty(PROP_CONFIDENCE);
  1255.   if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
  1256.     AddParamValue(m, REC_CONFIDENCE_LEVEL, fraction);
  1257.   // (2.7) Barge-in sensitivity level
  1258.   j = props.GetProperty(PROP_SENSITIVITY);
  1259.   if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
  1260.     AddParamValue(m, REC_SENSITIVITY, fraction);
  1261.   // (2.8) Performance tradeoff - speed vs. accuracy
  1262.   j = props.GetProperty(PROP_SPEEDVSACC);
  1263.   if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
  1264.     AddParamValue(m, REC_SPEED_VS_ACCURACY, fraction);
  1265.   // (2.9) DTMF terminator character
  1266.   j = props.GetProperty(PROP_TERMCHAR);
  1267.   if (j != NULL) {
  1268.     if (wcslen(j) < 2)
  1269.       AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, j);
  1270.     else {
  1271.       log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - "
  1272.         L"Unable to set " << REC_DTMF_TERMINATOR_CHAR << L" from value "" <<
  1273.         j << L"".  Defaulting to '#'.";
  1274.       log.EndDiagnostic();
  1275.       // Should we use the default, or rather not set anything ?
  1276.       AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, L"#");
  1277.     }
  1278.   }
  1279.   // (2.10) Input modes
  1280.   j = props.GetProperty(PROP_INPUTMODES);
  1281.   SetInputMode(j, m);
  1282.   // (2.11) Timeout settings
  1283.   if (timeout == -1) {
  1284.     j = props.GetProperty(PROP_TIMEOUT);
  1285.     if (j != NULL)
  1286.       if( !PropertyList::ConvertTimeToMilliseconds(log, j, timeout) )
  1287.         throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1288.   }
  1289.   if (timeout != -1) {
  1290.     AddParamValue(m, REC_DTMF_TIMEOUT, timeout);
  1291.     AddParamValue(m, REC_TIMEOUT, timeout);
  1292.   }
  1293.   // (2.12) Bargein type
  1294.   int bargintype = REC_BARGEIN_SPEECH;
  1295.   j = props.GetProperty(PROP_BARGEINTYPE);
  1296.   if (j != NULL) {
  1297.     vxistring value(j);
  1298.     if (value == L"hotword")
  1299.       bargintype = REC_BARGEIN_HOTWORD;
  1300.     else if (value == L"speech")
  1301.       bargintype = REC_BARGEIN_SPEECH;
  1302.     else {
  1303.       log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - "
  1304.         L"Property '" << REC_BARGEIN_TYPE << L"' contains unrecognized value "" << value <<
  1305.         L"", deliberately set to "speech"";
  1306.       log.EndDiagnostic();
  1307.     }
  1308.   }
  1309.   AddParamValue(m, REC_BARGEIN_TYPE, bargintype);
  1310.   // (2.13) maxnbest property
  1311.   j = props.GetProperty(PROP_MAXNBEST);
  1312.   if (j != NULL) {
  1313.     VXIint nbest = 1;
  1314.     std::basic_stringstream<VXIchar> nbestStream(j);
  1315.     nbestStream >> nbest;
  1316.     AddParamValue(m, REC_RESULT_NBEST_SIZE, nbest);
  1317.   }
  1318.   // (2.14) maxspeechtimeout property
  1319.   j = props.GetProperty(PROP_MAXSPEECHTIME);
  1320.   if (j != NULL) {
  1321.     if( !PropertyList::ConvertTimeToMilliseconds(log, j, timeout) )
  1322.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1323.     AddParamValue(m, REC_TIMEOUT_SPEECH, timeout);
  1324.   }
  1325.   // (2.15) recordutterance properties
  1326.   j = props.GetProperty(PROP_RECORDUTTERANCE);
  1327.   if (j != NULL)
  1328.     AddParamValue(m, REC_RECORDUTTERANCE, j);
  1329.   j = props.GetProperty(PROP_RECORDUTTERANCETYPE);
  1330.   if (j != NULL)
  1331.     AddParamValue(m, REC_RECORDUTTERANCETYPE, j);
  1332.   return m.Release();
  1333. }
  1334. VXIMap * GrammarManager::GetRecordProperties(const PropertyList & props,
  1335.                                              int timeout) const
  1336. {
  1337.   VXIMapHolder m;
  1338.   if (m.GetValue() == NULL) throw VXIException::OutOfMemory();
  1339.   // (1) Retrieve flattened property list.
  1340.   props.GetProperties(m);
  1341.   // (2) Convert & manipulate the properties.
  1342.   const VXIchar * j;
  1343.   VXIint time;
  1344.   // (2.1) Completion timeout
  1345.   j = props.GetProperty(GrammarManager::MaxTime);
  1346.   if (j != NULL ) {
  1347.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1348.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1349.     AddParamValue(m, REC_MAX_RECORDING_TIME, time);
  1350.   }
  1351.   
  1352.   // (2.2) Final silence
  1353.   j = props.GetProperty(GrammarManager::FinalSilence);
  1354.   if (j != NULL ) {
  1355.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1356.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1357.     AddParamValue(m, REC_TIMEOUT_COMPLETE, time);
  1358.   }
  1359.   
  1360.   // (2.3) Type
  1361.   j = props.GetProperty(GrammarManager::RecordingType);
  1362.   if (j != NULL)
  1363.     AddParamValue(m, REC_RECORD_MIME_TYPE, j);
  1364.   // (2.4) DTMF terminates record?
  1365.   j = props.GetProperty(GrammarManager::DTMFTerm);
  1366.   if (j != NULL) {
  1367.     int dtmfterm;
  1368.     if (vxistring(j) == L"false")
  1369.       dtmfterm = 0;
  1370.     else
  1371.       dtmfterm = 1;
  1372.     AddParamValue(m, REC_TERMINATED_ON_DTMF, dtmfterm);
  1373.   }
  1374.   // (2.5) Timeout settings
  1375.   if (timeout == -1) {
  1376.     j = props.GetProperty(PROP_TIMEOUT);
  1377.     if (j != NULL)
  1378.       if(!PropertyList::ConvertTimeToMilliseconds(log, j, timeout))
  1379.         throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1380.   }
  1381.   if (timeout != -1) {
  1382.     AddParamValue(m, REC_DTMF_TIMEOUT, timeout);
  1383.     AddParamValue(m, REC_TIMEOUT, timeout);
  1384.   }
  1385.   // (2.6) Inter-Digit timeout
  1386.   j = props.GetProperty(PROP_INTERDIGITTIME);
  1387.   if (j != NULL ) {
  1388.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1389.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1390.     AddParamValue(m, REC_DTMF_TIMEOUT_INTERDIGIT, time);
  1391.   }
  1392.   
  1393.   // (2.7) Terminator char timeout
  1394.   j = props.GetProperty(PROP_TERMTIME);
  1395.   if (j != NULL ) {
  1396.     if(!props.ConvertTimeToMilliseconds(log, j, time))
  1397.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
  1398.     AddParamValue(m, REC_DTMF_TIMEOUT_TERMINATOR, time);
  1399.   } 
  1400.   // (2.8) DTMF terminator character
  1401.   j = props.GetProperty(PROP_TERMCHAR);
  1402.   if ((j != NULL) && (j[0] != L'')) {
  1403.     if (wcslen(j) == 1)
  1404.       AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, j);
  1405.     else {
  1406.       log.StartDiagnostic(0) << L"GrammarManager::GetRecordProperties - "
  1407.         L"Unable to set " << REC_DTMF_TERMINATOR_CHAR << L" from value "" <<
  1408.         j << L"".  Defaulting to '#'.";
  1409.       log.EndDiagnostic();
  1410.       // Should we use the default, or rather not set anything ?
  1411.       AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, L"#");
  1412.     }
  1413.   }
  1414.   // (2.9) Input modes
  1415.   j = props.GetProperty(PROP_INPUTMODES);
  1416.   SetInputMode(j, m);
  1417.   // (3) Done
  1418.   return m.Release();
  1419. }
  1420. void GrammarManager::SetGrammarLoadProperties(const VXMLElement & element, 
  1421.                                               const PropertyList& props,
  1422.                                               VXIMapHolder & properties) const
  1423. {
  1424.   vxistring attr;
  1425.   const VXIchar * j;
  1426.   // (1) Set weight
  1427.   VXIflt32 val;
  1428.   if (element.GetAttribute(ATTRIBUTE_WEIGHT, attr) == true &&
  1429.       PropertyList::ConvertValueToFraction(log, attr, val))
  1430.   {
  1431.     AddParamValue(properties, REC_GRAMMAR_WEIGHT, val);
  1432.   }
  1433.   else
  1434.     AddParamValue(properties, REC_GRAMMAR_WEIGHT, 1.0f);
  1435.   // (1.1) Set Grammar Mode; as a convenience
  1436.   attr = L"";
  1437.   if( element.GetAttribute(ATTRIBUTE_MODE, attr ) == true )
  1438.   {
  1439.       AddParamValue(properties, REC_GRAMMAR_MODE, attr );
  1440.   }
  1441.   // (2) Set language; parsing should ensure that this value is always defined
  1442.   attr = L"";
  1443.   if (!element.GetAttribute(ATTRIBUTE_XMLLANG, attr)) {
  1444.     log.LogError(999, SimpleLogger::MESSAGE,
  1445.                  L"GrammarManager::SetGrammarLoadProperties did not "
  1446.                  L"find a language");
  1447.   }
  1448.   AddParamValue(properties, REC_LANGUAGE, attr);
  1449.   // (3) Set prefetch status
  1450.   attr = L"";
  1451.   if (element.GetAttribute(ATTRIBUTE_FETCHHINT, attr) != true) {
  1452.     const VXIValue * v = VXIMapGetProperty(properties.GetValue(),
  1453.                                            L"grammarfetchhint");
  1454.     if (VXIValueGetType(v) == VALUE_STRING)
  1455.       attr = VXIStringCStr(reinterpret_cast<const VXIString *>(v));
  1456.   }
  1457.   if (attr == L"prefetch")
  1458.     AddParamValue(properties, REC_PREFETCH_REQUEST, 1);
  1459.   else
  1460.     AddParamValue(properties, REC_PREFETCH_REQUEST, 0); 
  1461.     
  1462.   // (4) Input Mode
  1463.   j = props.GetProperty(PROP_INPUTMODES);
  1464.   SetInputMode(j, properties);
  1465. }