GrammarInfo::GrammarInfo(VXIrecGrammar * g, const vxistring & id, const VXMLElement & elem) : element(elem), recgrammar(g), scope(GRS_NONE), docID(id), enabled(false) { // (1) Determine the grammar scope. // (1.1) Obtain default from position in the tree. VXMLElement par = elem; while (par != 0) { VXMLElementType name = par.GetName(); if (name == NODE_INITIAL || name == NODE_FIELD || name == NODE_RECORD || name == NODE_TRANSFER) { scope = GRS_FIELD; break; } else if (name == NODE_FORM || name == NODE_MENU) { scope = GRS_DIALOG; break; } else if (name == NODE_VXML) { scope = GRS_DOC; break; } par = par.GetParent(); } // (1.2) if scope explicitly specified in grammar or parent, override! vxistring str; elem.GetAttribute(ATTRIBUTE_SCOPE, str); if (str.empty() && par != 0) { par.GetAttribute(ATTRIBUTE_SCOPE, str); } if (!str.empty()) { if (str == L"dialog") scope = GRS_DIALOG; else if (str == L"document") scope = GRS_DOC; } // (2) Do remaining initialization. _Initialize(elem); }
void GrammarInfo::_Initialize(const VXMLElement & elem) { if (elem == 0) return; VXMLElementType name = elem.GetName(); if (name == NODE_FIELD || name == NODE_INITIAL || name == NODE_TRANSFER) elem.GetAttribute(ATTRIBUTE__ITEMNAME, field); else if (name == NODE_FORM || name == NODE_MENU) { elem.GetAttribute(ATTRIBUTE__ITEMNAME, dialog); return; } _Initialize(elem.GetParent()); }
// -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; }
// 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(); } } }