inline void AddParamValue(VXIMapHolder & m, const vxistring & name, VXIflt32 value) { if (m.GetValue() == NULL) return; VXIFloat * val = VXIFloatCreate(value); if (val == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(m.GetValue(), name.c_str(), reinterpret_cast<VXIValue *>(val)); }
inline void AddParamValue(VXIMapHolder & m, const vxistring & name, bool value) { if (m.GetValue() == NULL) return; VXIBoolean * val = VXIBooleanCreate((value ? TRUE : FALSE)); if (val == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(m.GetValue(), name.c_str(), reinterpret_cast<VXIValue *>(val)); }
VXIMap * GrammarManager::GetRecordProperties(const PropertyList & props, int timeout) const { VXIMapHolder m; if (m.GetValue() == NULL) throw VXIException::OutOfMemory(); // (1) Retrieve flattened property list. props.GetProperties(m); // (2) Convert & manipulate the properties. const VXIchar * j; VXIint time; // (2.1) Completion timeout j = props.GetProperty(GrammarManager::MaxTime); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_MAX_RECORDING_TIME, time); // (2.2) Final silence j = props.GetProperty(GrammarManager::FinalSilence); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_TIMEOUT_COMPLETE, time); // (2.3) Type j = props.GetProperty(GrammarManager::RecordingType); if (j != NULL) AddParamValue(m, REC_RECORD_MIME_TYPE, j); // (2.4) DTMF terminates record? j = props.GetProperty(GrammarManager::DTMFTerm); if (j != NULL) { int dtmfterm; if (vxistring(j) == L"false") dtmfterm = 0; else dtmfterm = 1; AddParamValue(m, REC_TERMINATED_ON_DTMF, dtmfterm); } // (2.5) Timeout settings if (timeout == -1) { j = props.GetProperty(PROP_TIMEOUT); if (j != NULL) PropertyList::ConvertTimeToMilliseconds(log, j, timeout); } if (timeout != -1) { AddParamValue(m, REC_DTMF_TIMEOUT, timeout); AddParamValue(m, REC_TIMEOUT, timeout); } // (3) Done return m.Release(); }
bool GrammarManager::EnableGrammars(const vxistring & documentID, const vxistring & dialogName, const vxistring & fieldName, const VXIMapHolder & properties, bool isModal) { bool enabled = false; for (GRAMMARS::iterator i = grammars.begin(); i != grammars.end(); ++i) { bool docsMatch = (*i)->IsDoc(documentID); bool dialogsMatch = docsMatch && (*i)->IsDialog(dialogName); bool fieldsMatch = dialogsMatch && (*i)->IsField(fieldName); if ((!fieldName.empty() && fieldsMatch) || // Enable those field grammars matching our (field, form) pair (!isModal && (*i)->IsScope(GRS_DIALOG) && dialogsMatch) || // Enable form grammars & dialog scope fields, if not modal (!isModal && (*i)->IsScope(GRS_DOC)) // Enable document level grammars, if not modal ) { if (log.IsLogging(2)) { log.StartDiagnostic(2) << L"GrammarManager::EnableGrammar(" << (*i) << L")"; log.EndDiagnostic(); } VXIrecResult err = vxirec->ActivateGrammar(vxirec, properties.GetValue(), (*i)->GetRecGrammar()); if (err != VXIrec_RESULT_SUCCESS) throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR); (*i)->SetEnabled(true); enabled = true; } } return enabled; }
// -2: Internal error int DocumentParser::FetchDocument(const VXIchar * url, const VXIMapHolder & properties, VXIinetInterface * inet, VXIcacheInterface * cache, SimpleLogger & log, VXMLDocument & document, VXIMapHolder & docProperties, bool isDefaults, bool isRootApp, VXIbyte **content, VXIulong *size) { int result; int path_only = 0; VXMLDocument *cached_parse_tree = NULL; if (log.IsLogging(2)) { log.StartDiagnostic(2) << L"DocumentParser::FetchDocument(" << url << L")"; log.EndDiagnostic(); } // (1) Load the VXML DTD for validation. This will override an externally // specified DTD if the user provides a link. try { if (isDefaults) { MemBufInputSource membuf( VALIDATOR_DATA + DUMMY_VXML_DEFAULTS_DOC, DUMMY_VXML_DEFAULTS_DOC_SIZE, "vxml 1.0 defaults"); parser->parse(membuf); converter->ResetDocument(); // Throw this document away. } if (!isDefaults && !loadedVXML20) { // Preload the VXML 2.1 schema. VXIcharToXMLCh name(L"http://www.w3.org/TR/voicexml21/vxml.xsd"); LockLoadGrammar(); parser->loadGrammar(name.c_str(), Grammar::SchemaGrammarType, true); // Reuse cached grammars if available. parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true); UnlockLoadGrammar(); loadedVXML20 = true; } } catch (const XMLException & exception) { if (!isDefaults && !loadedVXML20) UnlockLoadGrammar(); XMLChToVXIchar message(exception.getMessage()); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - XML parsing " L"error from DOM: " << message; log.EndDiagnostic(); log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD"); return 4; } catch (const SAXParseException & exception) { if (!isDefaults && !loadedVXML20) UnlockLoadGrammar(); XMLChToVXIchar sysid(exception.getSystemId()); XMLChToVXIchar message(exception.getMessage()); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Parse error " << L"in file \"" << sysid << L"\", line " << exception.getLineNumber() << L", column " << exception.getColumnNumber() << L" - " << message; log.EndDiagnostic(); log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD"); return 4; } catch (...) { if (!isDefaults && !loadedVXML20) UnlockLoadGrammar(); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Unknown Parse error"; log.EndDiagnostic(); log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD"); return 4; } // (2) Load the url's content into memory. const VXIbyte * buffer = NULL; VXIulong bufSize = 0; vxistring docURL; bool isDefaultDoc = false; if (isDefaults && wcslen(url) == 0) { buffer = VALIDATOR_DATA + VXML_DEFAULTS; bufSize = VXML_DEFAULTS_SIZE; docURL = L"builtin defaults"; result = 0; isDefaultDoc = true; } else { path_only = 1; result = DocumentParser::FetchBuffer (url, properties, docProperties, inet, log, buffer, bufSize, docURL, 1, &cached_parse_tree); } if (result != 0) { if (log.IsLogging(0)) { log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - exiting " L"with error result " << result; log.EndDiagnostic(); } return result; // may return { -1, 1, 2, 3 } } // store buffer for reference if (content) { VXIbyte *tempbuf = new VXIbyte[bufSize]; if(tempbuf == NULL) { log.LogError(202); if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer); return -1; } if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "(Copy) Allocated http_get buffer " << Pointer_to_Std_String((const void *) tempbuf) << " of size " << bufSize; voiceglue_log ((char) LOG_DEBUG, logstring); }; memcpy(tempbuf, buffer, bufSize); if (size != NULL) *size = bufSize; *content = tempbuf; } // (3) Pull the document from cache. vxistring baseURL; VXMLDocument doc; // if (!DocumentStorageSingleton::Instance()->Retrieve(doc, buffer, bufSize, docURL.c_str())) { if (cached_parse_tree == NULL) { // No cached parse tree, must perform my own parse // and return results to voiceglue perl // (3.1) Set the base uri for this document, but // ignore if it is the default doc. Note: only want to do this if // not pulling from the cache otherwise the base is messed up if(!isDefaultDoc) converter->SetBaseUri(docURL.c_str()); // (4) Not in cache; parse buffer into our VXML document representation try { VXIcharToXMLCh membufURL(docURL.c_str()); // Set Document level if( isDefaults ) converter->SetDocumentLevel(DEFAULTS); else if( isRootApp ) converter->SetDocumentLevel(APPLICATION); else converter->SetDocumentLevel(DOCUMENT); // Parse the script if (path_only) { vxistring vxi_string_path = Std_String_to_vxistring ((const char *) buffer); VXIcharToXMLCh wide_path(vxi_string_path.c_str()); LocalFileInputSource filesource(wide_path.c_str()); parser->parse(filesource); } else { MemBufInputSource membuf(buffer, bufSize, membufURL.c_str(), false); parser->parse(membuf); }; } catch (const XMLException & exception) { if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer); if (path_only) voiceglue_sendipcmsg ("VXMLParse 0 -\n"); if (log.IsLogging(0)) { XMLChToVXIchar message(exception.getMessage()); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - XML " L"parsing error from DOM: " << message; log.EndDiagnostic(); } return 4; } catch (const SAXParseException & exception) { VXIString *key = NULL, *value = NULL; if (log.LogContent(VXI_MIME_XML, buffer, bufSize, &key, &value)) { vxistring temp(L""); temp += VXIStringCStr(key); temp += L": "; temp += VXIStringCStr(value); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - buffer is saved in: " << temp; log.EndDiagnostic(); } if (key) VXIStringDestroy(&key); if (value) VXIStringDestroy(&value); if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer); if (path_only) voiceglue_sendipcmsg ("VXMLParse 0 -\n"); if (log.IsLogging(0)) { XMLChToVXIchar sysid(exception.getSystemId()); XMLChToVXIchar message(exception.getMessage()); log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Parse " << L"error in file \"" << sysid << L"\", line " << exception.getLineNumber() << L", column " << exception.getColumnNumber() << L" - " << message; log.EndDiagnostic(); } return 4; } // (5) Write the parsed tree address out to the voiceglue cache doc = converter->GetDocument(); // Set the current uri as base uri if there isn't a "xml:base" attribute doc.GetBaseURL(baseURL); if( baseURL.empty() ) doc.SetBaseURL(docURL); // store default language if( isDefaults ) { vxistring dlang; converter->GetDefaultLang(dlang); doc.SetDefaultLang(dlang); } // Put into voiceglue cache as standalone persistent VXMLDocument object if (path_only) { VXMLDocument *permanent_doc = new VXMLDocument (doc); std::ostringstream ipc_msg_out; ipc_msg_out << "VXMLParse 1 " << Pointer_to_Std_String ((void *) permanent_doc) << "\n"; voiceglue_sendipcmsg (ipc_msg_out); } // DocumentStorageSingleton::Instance()->Store(doc, buffer, bufSize, docURL.c_str()); } else { // The document is already in the memory cache doc = *cached_parse_tree; // Need to set the base uri converter->RestoreBaseURLFromCache(doc); // Restore default language if( isDefaults ) { converter->RestoreDefaultLangFromCache(doc); } } if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer); // (6) Parse was successful, process document. We want only the top level // <vxml> node. const VXMLElement root = doc.GetRoot(); VXMLElementType nodeName = root.GetName(); // If we're looking for the defaults, we can exit early. if (isDefaults && nodeName == DEFAULTS_ROOT) { log.LogDiagnostic(2, L"DocumentParser::FetchDocument(): Default document - success"); document = doc; return 0; } else if (nodeName != NODE_VXML) { document = VXMLDocument(); if (log.IsLogging(0)) { log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - unable to " L"find " << NODE_VXML << L" in document."; log.EndDiagnostic(); } return 4; } vxistring temp; // If the properties map is NULL, don't bother with the remaining settings if (docProperties.GetValue() != NULL) { // Retrieve the properties and put them into the map. VXIString * str = NULL; temp = docURL; str = VXIStringCreate(temp.c_str()); if (str == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(docProperties.GetValue(), PropertyList::AbsoluteURI, reinterpret_cast<VXIValue *>(str)); str = VXIStringCreate(converter->GetBaseUri().empty() ? L"" : converter->GetBaseUri().c_str()); if (str == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(docProperties.GetValue(), PropertyList::BaseURI, reinterpret_cast<VXIValue *>(str)); // store encoding str = VXIStringCreate(encoding.empty() ? L"UTF-8" : encoding.c_str()); if (str == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(docProperties.GetValue(), PropertyList::SourceEncoding, reinterpret_cast<VXIValue *>(str)); // store xml:lang if (root.GetAttribute(ATTRIBUTE_XMLLANG, temp)) { str = VXIStringCreate(temp.c_str()); if (str == NULL) throw VXIException::OutOfMemory(); VXIMapSetProperty(docProperties.GetValue(), PropertyList::Language, reinterpret_cast<VXIValue *>(str)); } } log.LogDiagnostic(2, L"DocumentParser::FetchDocument(): VXML document - success"); document = doc; return 0; }
// 1: Invalid parameter // 2: Unable to open URL // 3: Unable to read from URL int DocumentParser::FetchBuffer(const VXIchar * url, const VXIMapHolder & properties, VXIMapHolder & streamInfo, VXIinetInterface * inet, SimpleLogger & log, const VXIbyte * & result, VXIulong & read, vxistring & docURL, int parseVXMLDocument, VXMLDocument * * document) { if (log.IsLogging(2)) { log.StartDiagnostic(2) << L"DocumentParser::FetchBuffer(" << url << L", " << properties.GetValue() << L")"; log.EndDiagnostic(); } // Set url for error report log.SetUri( url ? url : L"NONE" ); if (inet == NULL || url == NULL || wcslen(url) == 0) return 1; // (1) Open URL VXIinetStream * stream; // VXIMapHolder streamInfo; if (streamInfo.GetValue() == NULL) { return -1; } VXIinetOpenMode open_mode = INET_MODE_READ; if (parseVXMLDocument) { open_mode = INET_MODE_FILE; }; if (inet->Open(inet, L"vxi", url, open_mode, 0, properties.GetValue(), streamInfo.GetValue(), &stream) != 0) { if (log.IsLogging(0)) { log.StartDiagnostic(0) << L"DocumentParser::FetchBuffer - could not " L"open URL: " << url; log.EndDiagnostic(); } return 2; } // (2) Determine document size & absolute URL const VXIValue * tempURL = NULL; tempURL = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_ABSOLUTE_NAME); if (tempURL == NULL || VXIValueGetType(tempURL) != VALUE_STRING) { inet->Close(inet, &stream); if (log.IsLogging(0)) { log.StartDiagnostic(0) << L"DocumentParser::FetchBuffer - could not " L"retrieve absolute path of document at URL: " << url; log.EndDiagnostic(); } return 2; } docURL = VXIStringCStr(reinterpret_cast<const VXIString *>(tempURL)); VXIbyte * buffer = NULL; if (parseVXMLDocument) { // Decode parse tree addr (* document) = NULL; const VXIValue* wide_parse_tree_addr = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_PARSE_TREE); std::string parse_tree_addr = VXIValue_to_Std_String (wide_parse_tree_addr); if (parse_tree_addr.length() > 2) { void *parse_tree_pointer = Std_String_to_Pointer (parse_tree_addr); if (parse_tree_pointer != NULL) { (* document) = (VXMLDocument *) parse_tree_pointer; if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "Received VXMLDocument parse tree at " << Pointer_to_Std_String ((const void *) (* document)); voiceglue_log ((char) LOG_DEBUG, logstring); }; }; }; // Decode path const VXIValue* wide_path = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_LOCAL_FILE_PATH); std::string path = VXIValue_to_Std_String (wide_path); int bufSize = path.length() + 1; buffer = new VXIbyte[bufSize]; read = bufSize; if(buffer == NULL) { log.LogError(202); return -1; } memcpy (buffer, path.c_str(), bufSize); } else { // Read into memory buffer const VXIValue * tempSize = NULL; tempSize = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_SIZE_BYTES); if (tempSize == NULL || VXIValueGetType(tempSize) != VALUE_INTEGER) { inet->Close(inet, &stream); if (log.IsLogging(0)) { log.StartDiagnostic(0) << L"DocumentParser::FetchBuffer - could not " L"retrieve size of document at URL: " << url; log.EndDiagnostic(); } return 2; } VXIint32 bufSize = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(tempSize)); if (bufSize < 2047) bufSize = 2047; ++bufSize; buffer = new VXIbyte[bufSize]; if(buffer == NULL) { log.LogError(202); return -1; } if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "Allocated http_get buffer " << Pointer_to_Std_String((const void *) buffer) << " of size " << bufSize; voiceglue_log ((char) LOG_DEBUG, logstring); }; bool reachedEnd = false; read = 0; while (!reachedEnd) { VXIulong bytesRead = 0; switch (inet->Read(inet, buffer+read, bufSize-read, &bytesRead, stream)) { case VXIinet_RESULT_SUCCESS: read += bytesRead; break; case VXIinet_RESULT_END_OF_STREAM: read += bytesRead; reachedEnd = true; // exit while break; case VXIinet_RESULT_WOULD_BLOCK: VXItrdThreadYield(); break; default: inet->Close(inet, &stream); delete[] buffer; log.LogDiagnostic(0, L"DocumentParser::FetchBuffer - " L"could not read from URL."); return 3; } if (read == static_cast<VXIulong>(bufSize)) { // The number of bytes read exceeds the number expected. // Double the size and keep reading. VXIbyte * temp = new VXIbyte[2*bufSize]; if(temp == NULL) { log.LogError(202); delete [] buffer; if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "Released http_get buffer " << Pointer_to_Std_String((const void *) buffer); voiceglue_log ((char) LOG_DEBUG, logstring); }; return -1; } if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "Allocated http_get buffer " << Pointer_to_Std_String((const void *) temp) << " of size " << (2*bufSize); voiceglue_log ((char) LOG_DEBUG, logstring); }; memcpy(static_cast<void *>(temp), static_cast<void *>(buffer), bufSize * sizeof(VXIbyte)); delete[] buffer; if (voiceglue_loglevel() >= LOG_DEBUG) { std::ostringstream logstring; logstring << "Released http_get buffer " << Pointer_to_Std_String((const void *) buffer); voiceglue_log ((char) LOG_DEBUG, logstring); }; buffer = temp; bufSize *= 2; } } inet->Close(inet, &stream); }; result = buffer; log.LogDiagnostic(2, L"DocumentParser::FetchBuffer - success"); return 0; }
int GrammarManager::Record(const VXIMapHolder & properties, VXIrecRecordResult * & resultStruct) { // (1) Do record. VXIrecResult err = vxirec->Record(vxirec, properties.GetValue(), &resultStruct); if (err == VXIrec_RESULT_OUT_OF_MEMORY) { log.LogDiagnostic(0, L"GrammarManager::Record - Out of memory."); return GrammarManager::OutOfMemory; } if (err == VXIrec_RESULT_BAD_MIME_TYPE) { log.LogDiagnostic(0, L"GrammarManager::Record - Unsupported mime type."); return GrammarManager::BadMimeType; } if (err == VXIrec_RESULT_DISCONNECT) { // Here is the case that the call is being disconnected before the // recording even starts return GrammarManager::Disconnect; } if (err != VXIrec_RESULT_SUCCESS) { log.StartDiagnostic(0) << L"GrammarManager::Record - " L"VXIrecInterface::Record returned " << int (err); log.EndDiagnostic(); log.LogError(421, SimpleLogger::MESSAGE, L"function did not return the expected VXIrecSUCCESS result"); return GrammarManager::InternalError; } if (resultStruct == NULL) { // Here is the case that the call is being disconnected before the recording even starts return GrammarManager::Disconnect; } // (2) Process all non-Successful results. switch (resultStruct->status) { case REC_STATUS_SUCCESS: // Record produced a recording break; case REC_STATUS_TIMEOUT: // No signal was detected return GrammarManager::Timeout; case REC_STATUS_ERROR: // An error aborted record return GrammarManager::Error; case REC_STATUS_DISCONNECT: // Caller has disconnected without a recording return GrammarManager::Disconnect; case REC_STATUS_FAILURE: // This should never happen return GrammarManager::InternalError; default: log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - " L"VXIrecInterface::Recognize returned status " << int (resultStruct->status); log.EndDiagnostic(); log.LogError(420, SimpleLogger::MESSAGE, L"function returned an invalid VXIrecStatus code"); return GrammarManager::InternalError; } // (3) Verify that in the success case, the information is valid. if (resultStruct->waveform == NULL) { log.LogError(421, SimpleLogger::MESSAGE, L"function did not produce a recording"); return GrammarManager::InternalError; } // Technically recordings could exceed an hour. These users may just // change the source. if (resultStruct->duration == 0 || resultStruct->duration > 3600000) { log.LogError(421, SimpleLogger::MESSAGE, L"function returned invalid recording duration"); return GrammarManager::InternalError; } return GrammarManager::Success; }
// This function is responsible for calling the VXIrec level Recognize function // and then mapping the grammar back to the corresponding VXML node. // int GrammarManager::Recognize(const VXIMapHolder & properties, RecognitionAnswer & recAnswer, VXMLElement & recNode) { recNode = VXMLElement(); VXIrecRecognitionResult * answer = NULL; // (1) Do VXIrec level Recognitize and process return value. // (1.1) Recognize. VXIrecResult err = vxirec->Recognize(vxirec, properties.GetValue(), &answer); if (err == VXIrec_RESULT_OUT_OF_MEMORY) { log.LogDiagnostic(0,L"GrammarManager::InternalRecognize - Out of memory."); return GrammarManager::OutOfMemory; } if (err == VXIrec_RESULT_DISCONNECT) { // Here is the case that the call is being disconnected before the // recognition even starts return GrammarManager::Disconnect; } // (1.2) Anything other than success indicates that recognition failed // (badly). The normal error conditions are returned through the recogntion // result structure. if (err != VXIrec_RESULT_SUCCESS) { log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - " L"VXIrecInterface::Recognize returned " << int (err); log.EndDiagnostic(); log.LogError(420, SimpleLogger::MESSAGE, L"function did not return the expected VXIrecSUCCESS result"); return GrammarManager::InternalError; } // (1.3) The answer structure must be defined. if (answer == NULL) { // Here is the case that the call is being disconnected before the recording even starts return GrammarManager::Disconnect; } // (1.4) Attach the answer structure returned by VXIrec and the waveform to // the recAnswer class. recAnswer.Bind(answer); recAnswer.waveform = answer->waveform; // (2) Process all non-Successful results. // (2.1) First the status code. switch (answer->status) { case REC_STATUS_SUCCESS: // Recognition returned a hypothesis case REC_STATUS_DTMFSUCCESS: // Recognition returned a hypothesis break; case REC_STATUS_FAILURE: // Speech detected, no likely hypothesis return GrammarManager::Failure; case REC_STATUS_TIMEOUT: // No speech was detected return GrammarManager::Timeout; case REC_STATUS_DISCONNECT: // Caller has disconnected; no hypothesis return GrammarManager::Disconnect; case REC_STATUS_ERROR: // An error aborted recognition return GrammarManager::Error; default: log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - " L"VXIrecInterface::Recognize returned status " << int(answer->status); log.EndDiagnostic(); log.LogError(420, SimpleLogger::MESSAGE, L"function returned an invalid VXIrecStatus code"); return GrammarManager::InternalError; } // (2.2) Recognition success. Verify that the input mode was set correctly. switch (answer->mode) { case REC_INPUT_MODE_DTMF: case REC_INPUT_MODE_SPEECH: break; default: log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - " L"VXIrecInterface::Recognize returned mode " << int(answer->mode); log.EndDiagnostic(); log.LogError(420, SimpleLogger::MESSAGE, L"function returned an invalid VXIrecInputMode value"); return GrammarManager::InternalError; } // (3) Walk through results array and sanity check the results. // (3.1) How many answers are there? if (answer->results == NULL) { log.LogError(420, SimpleLogger::MESSAGE, L"function returned a null results vector"); return GrammarManager::InternalError; } unsigned int NUM_ANSWERS = VXIVectorLength(answer->results); if (NUM_ANSWERS == 0) { log.LogError(420, SimpleLogger::MESSAGE, L"function returned an empty results vector"); return GrammarManager::InternalError; } const VXIMap * bestAnswer = NULL; for (unsigned int i = 0; i < NUM_ANSWERS; ++i) { // (3.2) Find each element in the result vector. const VXIValue * temp = VXIVectorGetElement(answer->results, i); if (temp == NULL) { log.LogError(400, SimpleLogger::MESSAGE, L"VXIVectorGetElement failed to return answer."); return GrammarManager::InternalError; } if (VXIValueGetType(temp) != VALUE_MAP) { log.LogError(420, SimpleLogger::MESSAGE, L"function returned an invalid results vector"); return GrammarManager::InternalError; } const VXIMap * nthAnswer = reinterpret_cast<const VXIMap*>(temp); if (i == 0) bestAnswer = nthAnswer; // (3.3) Validate types of member keys. temp = VXIMapGetProperty(nthAnswer, REC_KEYS); if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) { log.LogError(420, SimpleLogger::MESSAGE, L"REC_KEYS must be defined with type vector"); return GrammarManager::InternalError; } const VXIVector * keys = reinterpret_cast<const VXIVector *>(temp); temp = VXIMapGetProperty(nthAnswer, REC_VALUES); if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) { log.LogError(420, SimpleLogger::MESSAGE, L"REC_VALUES must be defined with type vector"); return GrammarManager::InternalError; } const VXIVector * values = reinterpret_cast<const VXIVector *>(temp); temp = VXIMapGetProperty(nthAnswer, REC_CONF); if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) { log.LogError(420, SimpleLogger::MESSAGE, L"REC_CONF must be defined with type vector"); return GrammarManager::InternalError; } const VXIVector * scores = reinterpret_cast<const VXIVector *>(temp); temp = VXIMapGetProperty(nthAnswer, REC_RAW); if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) { log.LogError(420, SimpleLogger::MESSAGE, L"REC_RAW must be defined with type vector"); return GrammarManager::InternalError; } const VXIVector * utts = reinterpret_cast<const VXIVector *>(temp); // (3.4) Validate vector lengths. const VXIunsigned length = VXIVectorLength(keys); if (length < 1) { log.LogError(420, SimpleLogger::MESSAGE, L"result vector must have length of at least one"); throw VXIException::Fatal(); } if (length != VXIVectorLength(values) || length != VXIVectorLength(scores) || length != VXIVectorLength(utts)) { log.LogError(420, SimpleLogger::MESSAGE, L"result vector length must all match"); return GrammarManager::InternalError; } temp = VXIVectorGetElement(keys, 0); if (temp == NULL || VXIValueGetType(temp) != VALUE_STRING) { log.LogError(420, SimpleLogger::MESSAGE, L"first REC_KEYS must be defined with type string"); return GrammarManager::InternalError; } // (3.5) Copy into results class. recAnswer.keys.push_back(keys); recAnswer.values.push_back(values); recAnswer.scores.push_back(scores); recAnswer.utts.push_back(utts); } recAnswer.numAnswers = NUM_ANSWERS; // (4) Find the VXML element associated with the matched grammar. // (4.1) Find the grammar corresponding to the best answer. const VXIValue * temp = VXIMapGetProperty(bestAnswer, REC_GRAMMAR); if (temp == NULL || VXIValueGetType(temp) != VALUE_PTR) { log.LogError(420, SimpleLogger::MESSAGE, L"unable to obtain grammar key from 'results' #0"); return GrammarManager::InternalError; } const VXIPtr * tempptr = reinterpret_cast<const VXIPtr *>(temp); const VXIrecGrammar * bestGrammar = reinterpret_cast<const VXIrecGrammar*>(VXIPtrValue(tempptr)); // (4.2) Map the VXIrecGrammar pointer back to the source VXMLElement. for (GRAMMARS::iterator j = grammars.begin(); j != grammars.end(); ++j) { if ((answer->mode == REC_INPUT_MODE_DTMF && REC_STATUS_DTMFSUCCESS != answer->status) && (*j)->GetRecGrammar() != bestGrammar) continue; if (!(*j)->IsEnabled()) { if (j == grammars.end()) { log.LogError(420, SimpleLogger::MESSAGE, L"function returned an inactive grammar"); return GrammarManager::InternalError; } continue; } (*j)->GetElement(recNode); if (answer->mode == REC_INPUT_MODE_DTMF) return GrammarManager::SuccessDTMF; return GrammarManager::Success; } log.LogError(420, SimpleLogger::MESSAGE, L"function returned a non-existent grammar"); return GrammarManager::InternalError; }
// This function is used to recursively walk through the tree, loading and // speech or dtmf grammars which are found. // void GrammarManager::LoadGrammars(const VXMLElement& doc, vxistring & documentID, PropertyList & properties) { if (doc == 0) return; VXIMapHolder recProps(NULL); // Initialize an empty holder. // (1) Retrieve the ID for this document. This is important for grammar // activation. if (doc.GetName() == NODE_VXML) doc.GetAttribute(ATTRIBUTE__ITEMNAME, documentID); // (2) Look for grammars in current nodes. for (VXMLNodeIterator it(doc); it; ++it) { VXMLNode child = *it; if (child.GetType() != VXMLNode::Type_VXMLElement) continue; const VXMLElement & element = reinterpret_cast<const VXMLElement &>(child); VXMLElementType elementName = element.GetName(); VXIrecGrammar * vg = NULL; if (recProps.GetValue() == NULL) recProps.Acquire(GetRecProperties(properties)); // (3) Handle <grammar> & <dtmf> if (elementName == NODE_GRAMMAR || elementName == NODE_DTMF) { vxistring src; element.GetAttribute(ATTRIBUTE_SRC, src); // (3.1) Override the language setting (if specified as an attribute) vxistring lang; if (element.GetAttribute(ATTRIBUTE_XMLLANG, lang) == true) AddParamValue(recProps, REC_LANGUAGE, lang); // (3.2) Does the grammar come from an external URI? if (!src.empty()) { if (log.IsLogging(2)) { log.StartDiagnostic(2) << L"GrammarManager::LoadGrammars - <grammar " L"src=\"" << src << L"\">"; log.EndDiagnostic(); } VXIMapHolder fetchobj; if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory(); vxistring fragment; properties.GetFetchobjCacheAttrs(element, PropertyList::Grammar, fetchobj); properties.GetFetchobjURIs(element, fetchobj, src, fragment); if (!fragment.empty()) log.LogError(215); vxistring mimeType; element.GetAttribute(ATTRIBUTE_TYPE, mimeType); VXIrecResult err = vxirec->LoadGrammarURI(vxirec, recProps.GetValue(), mimeType.c_str(), src.c_str(), fetchobj.GetValue(), &vg); if (err != VXIrec_RESULT_SUCCESS) throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR); if (vg) AddGrammar(vg, documentID, element); } // (3.3) Otherwise this is an inlined grammar. else { log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <grammar>"); vxistring text; GetEnclosedText(log, element, text); vxistring mimeType; element.GetAttribute(ATTRIBUTE_TYPE, mimeType); if (mimeType.empty() && elementName == NODE_DTMF) vg = GrammarManager::CreateGrammarFromString(vxirec, log, text, REC_MIME_GENERIC_DTMF, recProps); else vg = GrammarManager::CreateGrammarFromString(vxirec, log, text, mimeType.c_str(), recProps); if (vg == NULL) // "Error loading in-line grammar %s",text throw VXIException::InterpreterEvent(EV_ERROR_BAD_INLINE); AddGrammar(vg, documentID, element); } } // (4) Handle <choice> else if (elementName == NODE_CHOICE) { log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <choice>"); // (4.1) If there is a <grammar> tag, it overrides any implicit grammar. // (4.1.1) Check for <grammar> element. bool foundGrammar = false; for (VXMLNodeIterator it(element); it; ++it) { VXMLNode child = *it; if (child.GetType() != VXMLNode::Type_VXMLElement) continue; const VXMLElement & temp = reinterpret_cast<const VXMLElement&>(child); if (temp.GetName() != NODE_GRAMMAR) continue; foundGrammar = true; break; } // (4.1.2) If found, apply recursion. if (foundGrammar) { // <choice> nodes can't contain properties. Don't need to call Push. LoadGrammars(element, documentID, properties); } // (4.1.3) Otherwise, construct a grammar from the prompt text. else { vxistring text; GetEnclosedText(log, element, text); // The text may be empty, e.g. for a dtmf only grammar. if (!text.empty()) { VXIrecGrammar * vg = NULL; vg = GrammarManager::CreateGrammarFromString(vxirec, log, text, REC_MIME_CHOICE, recProps); if (vg == NULL) // "Error loading in-line grammar %s",text throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE); AddGrammar(vg, documentID, element); } } // (4.2) Create associated DTMF grammar. // // Either an explict dtmf choice is given or implicit numbers are // generated for the first nine. When both are specified, the explicit // choice wins. This will quite possibly create unintentional (and // undetected) duplicates.... vxistring dtmf; element.GetAttribute(ATTRIBUTE_DTMF, dtmf); if (!dtmf.empty()) { VXIrecGrammar * vg = NULL; vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf, REC_MIME_CHOICE_DTMF, recProps); if (vg == NULL) throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE); AddGrammar(vg, documentID, element); } } // (5) Handle <field>. else if (elementName == NODE_FIELD) { log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <field>"); vxistring gram; VXIrecGrammar * vg; // Build option grammar (if one exists). GrammarManager::BuildOptionGrammar(log, element, false, gram); if (!gram.empty()) { vg = GrammarManager::CreateGrammarFromString(vxirec, log, gram, REC_MIME_OPTION, recProps); if (vg == NULL) throw VXIException::InterpreterEvent(EV_ERROR_BAD_OPTION); AddGrammar(vg, documentID, element); } // Build option dtmf grammar (if one exists). gram = L""; GrammarManager::BuildOptionGrammar(log, element, true, gram); if (gram.length() != 0) { vg = GrammarManager::CreateGrammarFromString(vxirec, log, gram, REC_MIME_OPTION_DTMF, recProps); if (vg == NULL) throw VXIException::InterpreterEvent(EV_ERROR_BAD_OPTION); AddGrammar(vg, documentID, element); } // Add the built-in grammars (if they exist). vg = NULL; vxistring type; element.GetAttribute(ATTRIBUTE_TYPE, type); if (!type.empty()) { vxistring newuri(L"builtin:grammar/"); newuri += type; if (vxirec->LoadGrammarURI(vxirec, recProps.GetValue(), NULL, newuri.c_str(), NULL, &vg) != VXIrec_RESULT_SUCCESS) { throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR); } if (vg) AddGrammar(vg, documentID, element); newuri = L"builtin:dtmf/"; newuri += type; if (vxirec->LoadGrammarURI(vxirec, recProps.GetValue(), NULL, newuri.c_str(), NULL, &vg) != VXIrec_RESULT_SUCCESS) { throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR); } if (vg) AddGrammar(vg, documentID, element); } // Recursively add grammars (this handles <grammar>) bool doPop = properties.PushProperties(element); LoadGrammars(element, documentID, properties); if (doPop) properties.PopProperties(); } // (6) Handle <link>. else if (elementName == NODE_LINK) { log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <link>"); // (6.1) Get properties at this level. bool doPop = properties.PushProperties(element); // (6.2) Create DTMF grammar is specified. vxistring dtmf; element.GetAttribute(ATTRIBUTE_DTMF, dtmf); if (!dtmf.empty()) { VXIrecGrammar * vg = NULL; vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf, REC_MIME_CHOICE_DTMF, recProps); if (vg == NULL) throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE); AddGrammar(vg, documentID, element); } // (6.3) Create child grammars. LoadGrammars(element, documentID, properties); if (doPop) properties.PopProperties(); } // (7) Handle <record> and <transfer>. else if (elementName == NODE_RECORD || elementName == NODE_TRANSFER) { // The DTD allows <grammar> elements, but these don't make sense. We // will therefore ignore them. } // (8) Handle <transfer> else if ( elementName == NODE_TRANSFER ) { bool doPop = properties.PushProperties(element); LoadGrammars(element, documentID, properties); if (doPop) properties.PopProperties(); } // (9) Otherwise, nothing was found at this level. Use recursion to check // the next level down. else { bool doPop = properties.PushProperties(element); LoadGrammars(element, documentID, properties); if (doPop) properties.PopProperties(); } } }
VXIMap * GrammarManager::GetRecProperties(const PropertyList & props, int timeout) const { VXIMapHolder m; if (m.GetValue() == NULL) throw VXIException::OutOfMemory(); // (1) Retrieve flattened property list. props.GetProperties(m); // (2) Convert & manipulate the properties. // TBD #pragma message ("GrammarManager::GetRecProperties - handle REC_RESULT_SAVE_WAVEFORM ???") // TBD #pragma message ("GrammarManager::GetRecProperties - handle REC_RESULT_NBEST_SIZE ???") const VXIchar * j; VXIint time; VXIflt32 fraction; // (2.1) Language j = props.GetProperty(PropertyList::Language); if (j != NULL) AddParamValue(m, REC_LANGUAGE, j); // (2.2) Completion timeout j = props.GetProperty(PROP_COMPLETETIME); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_TIMEOUT_COMPLETE, time); // (2.3) Incompletion timeout j = props.GetProperty(PROP_INCOMPLETETIME); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_TIMEOUT_INCOMPLETE, time); // (2.4) Inter-Digit timeout j = props.GetProperty(PROP_INTERDIGITTIME); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_DTMF_TIMEOUT_INTERDIGIT, time); // (2.5) Inter-Digit timeout j = props.GetProperty(PROP_TERMTIME); if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time)) AddParamValue(m, REC_DTMF_TIMEOUT_TERMINATOR, time); // (2.6) Confidence level j = props.GetProperty(PROP_CONFIDENCE); if (j != NULL && props.ConvertValueToFraction(log, j, fraction)) AddParamValue(m, REC_CONFIDENCE_LEVEL, fraction); // (2.7) Barge-in sensitivity level j = props.GetProperty(PROP_SENSITIVITY); if (j != NULL && props.ConvertValueToFraction(log, j, fraction)) AddParamValue(m, REC_SENSITIVITY, fraction); // (2.8) Performance tradeoff - speed vs. accuracy j = props.GetProperty(PROP_SPEEDVSACC); if (j != NULL && props.ConvertValueToFraction(log, j, fraction)) AddParamValue(m, REC_SPEED_VS_ACCURACY, fraction); // (2.9) DTMF terminator character j = props.GetProperty(PROP_TERMCHAR); if ((j != NULL) && (j[0] != L'\0')) { if (wcslen(j) == 1) AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, j); else { log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - " L"Unable to set " << REC_DTMF_TERMINATOR_CHAR << L" from value \"" << j << L"\". Defaulting to '#'."; log.EndDiagnostic(); // Should we use the default, or rather not set anything ? AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, L"#"); } } // (2.10) Input modes int mode = REC_INPUT_MODE_DTMF_SPEECH; j = props.GetProperty(PROP_INPUTMODES); if (j != NULL) { vxistring value(j); if (value == L"voice dtmf" || value == L"dtmf voice") mode = REC_INPUT_MODE_DTMF_SPEECH; else if (value == L"voice") mode = REC_INPUT_MODE_SPEECH; else if (value == L"dtmf") mode = REC_INPUT_MODE_DTMF; else { log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - " L"Unable to set " << REC_INPUT_MODES << L" from value \"" << value << L"\"."; log.EndDiagnostic(); } } AddParamValue(m, REC_INPUT_MODES, mode); // (2.11) Timeout settings if (timeout == -1) { j = props.GetProperty(PROP_TIMEOUT); if (j != NULL) PropertyList::ConvertTimeToMilliseconds(log, j, timeout); } if (timeout != -1) { AddParamValue(m, REC_DTMF_TIMEOUT, timeout); AddParamValue(m, REC_TIMEOUT, timeout); } // (3) Done return m.Release(); }