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

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. /***********************************************************************
  23.  * This class performs several jobs.  Among them:
  24.  *
  25.  * 1) <grammar> contents are collapsed and blindly copied.
  26.  *
  27.  * 2) Explicit and implicit prompts are converted into <prompt> elements with
  28.  *    full SSML headers and trailers.
  29.  *    NOTE: <value>, <enumerate>, and <audio> don't contribute to the copy
  30.  *          depth as they require special handling.
  31.  *
  32.  * 3) PCDATA consisting only of whitespace is discarded.
  33.  *
  34.  * 4) <error>, <help>, <noinput> and <nomatch> are converted into a <catch>
  35.  *    with the appropriate event name.
  36.  *
  37.  * 5) <choice> elements may be assigned dtmf values.
  38.  *
  39.  * 6) Attribute conflicts are detected whenever possible.
  40.  *
  41.  * 7) <metadata> is ignored (except in <grammar> or <prompt>).
  42.  *
  43.  * 8) Assign xml:base and xml:lang to <grammar>, <prompt>, <field>, and <menu>.
  44.  * 
  45.  * NOTE: By performing these duties, the DocumentConverter greatly simplifies
  46.  *       the rest of the interpreter.  Unfortunately, this means that this
  47.  *       code is tricky to write and maintain.  PLEASE BE VERY CAREFUL THAT
  48.  *       YOU UNDERSTAND WHAT IS HAPPENING HERE BEFORE MAKING ANY CHANGES.
  49.  * 
  50.  ***********************************************************************
  51.  *
  52.  * 1 Grammar processing
  53.  *
  54.  * The content of <grammar> elements is blindly copied.  This is controlled
  55.  * by 'copyDepth' and 'inGrammar'.  This is possible because VoiceXML
  56.  * grammars are entirely static.  Reducing them down to PCDATA at this stage
  57.  * allows the interpreter to treat the entire grammar as a black box without
  58.  * regard to supported types.
  59.  *
  60.  ***********************************************************************
  61.  * 
  62.  * 2 Prompt processing 
  63.  *
  64.  * A frequently noted defect of VoiceXML is that prompts may be both explicitly
  65.  * declared (i.e. within <prompt>) or implicitly declared as raw CDATA.  In
  66.  * this code, we convert all implicit prompts into explicit ones and collapse
  67.  * most of the SSML into PCDATA.  At the end, prompts are composed only of 
  68.  * those elements requiring special handling (i.e. <audio>, <enumerate>,  
  69.  * <value>, and <foreach>) and PCDATA.
  70.  *
  71.  * In the case of <foreach>, there is extra logic for handling executable
  72.  * content within a <foreach> element.
  73.  *
  74.  ***********************************************************************
  75.  * 
  76.  * 3 Whitespace stripping
  77.  *
  78.  * Because of implicit prompts, the XML parser is required to treat most
  79.  * whitespace as significant information.  These 'empty prompts' are stripped
  80.  * at this level to simplify later processing.
  81.  *
  82.  * Likewise, <choice> and <option> content is whitespace stripped (though this
  83.  * task must be performed by the VXMLDocumentRep since Xerces may split PCDATA
  84.  * across multiple calls to DocumentConverter::characters).
  85.  *
  86.  ***********************************************************************
  87.  * 
  88.  * 4 Event collapsing
  89.  *
  90.  * VoiceXML defines <error>, <help>, <noinput> and <nomatch> as aliases
  91.  * for <catch event="...">.  These are converted at this layer (see
  92.  * startElement) to simplify later processing.
  93.  *
  94.  ***********************************************************************
  95.  * 
  96.  * 5 Menu choice processing
  97.  *
  98.  * DTMF values may be assigned to <choice>s if necessary.
  99.  *
  100.  ***********************************************************************
  101.  * 
  102.  * 6 Attribute conflict detection
  103.  *
  104.  * There are many cases in the VoiceXML specification where conflicting
  105.  * attributes result in error.badfetch.  These should (in future) be
  106.  * detected by the XML schema and are caught at this level.
  107.  *
  108.  * One difficult case appears inside of <grammar> and <script> where either
  109.  * the 'src' attribute or content is legal, but not both.  As grammars may
  110.  * not appear inside scripts and vice versa, this is detected with a simple
  111.  * scheme using the variables 'contentForbidden' and 'hasContent'.
  112.  *
  113.  ***********************************************************************
  114.  *
  115.  * 7 <metadata> stripped
  116.  *
  117.  * This content has no runtime impact.  It is stripped at this level
  118.  * UNLESS it is inside a <grammar> or <prompt> tag.
  119.  *
  120.  ***********************************************************************
  121.  *
  122.  * 8 <prompt> & <grammar> always have xml:base and xml:lang attributes.
  123.  *   <field>, <menu> always has xml:lang
  124.  *
  125.  * In an effort to keep these elements independent from the rest of VXML
  126.  * this attribute is always set.  This allows the SRGS or SSML to
  127.  * incorporate an xml:base in the root element which might be useful if
  128.  * the contents are being sent to a seperate server.
  129.  *
  130.  * Likewise, a language is required for the type attribute on fields (used
  131.  * for builtins) and for generating the CDATA grammars in menu choices.
  132.  *
  133.  ***********************************************************************/
  134.  
  135. #include "DocumentConverter.hpp"
  136. #include <sax2/Attributes.hpp>
  137. #include <sax/SAXParseException.hpp>
  138. #include <sax/Locator.hpp>
  139. #include <vector>
  140. #include <algorithm>
  141. #include <sstream>                     // by ProcessNodeFinal
  142. #include "VXMLDocument.hpp"
  143. #include "VXMLDocumentRep.hpp"
  144. #include "XMLChConverter.hpp"
  145. #include <iostream>
  146. #include <algorithm>
  147. //#############################################################################
  148. enum {
  149.   PRIV_ELEM_RangeStart    = 0x200,   // This should always be the first entry
  150.   // Events
  151.   PRIV_ELEM_ERROR,
  152.   PRIV_ELEM_HELP,
  153.   PRIV_ELEM_NOINPUT,
  154.   PRIV_ELEM_NOMATCH,
  155.   PRIV_ELEM_METADATA,
  156.   // SSML
  157.   PRIV_ELEM_BREAK,
  158.   PRIV_ELEM_DESC,
  159.   PRIV_ELEM_EMPHASIS,
  160.   PRIV_ELEM_PARAGRAPH,
  161.   PRIV_ELEM_PHONEME,
  162.   PRIV_ELEM_PROSODY,
  163.   PRIV_ELEM_SAYAS,
  164.   PRIV_ELEM_SENTENCE,
  165.   PRIV_ELEM_SUB,
  166.   PRIV_ELEM_VOICE
  167. };
  168. enum {
  169.   PRIV_ATTRIB_RangeStart  = 0x200,   // This should always be the first entry
  170.   PRIV_ATTRIB_SCHEMALOC
  171. };
  172. struct VXMLElementInfo {
  173. public:
  174.   const VXIchar * key;
  175.   int value;
  176.   VXMLElementInfo(const VXIchar * k, int v)
  177.     : key(k), value(v)  { }
  178.   VXMLElementInfo(const VXMLElementInfo & k)
  179.     : key(k.key), value(k.value)   { }
  180.   VXMLElementInfo& operator=(const VXMLElementInfo & k)
  181.     { key = k.key; value = k.value; return *this; }
  182. };
  183. bool operator< (const VXMLElementInfo & x, const VXMLElementInfo & y)
  184. { if (x.key == NULL || y.key == NULL) return false;
  185.   return wcscmp(x.key, y.key) < 0; }
  186. struct VXMLAttribute {
  187. public:
  188.   const VXIchar * key;
  189.   int value;
  190.   VXMLAttribute(const VXIchar * k, int v)
  191.     : key(k), value(v)  { }
  192.   VXMLAttribute(const VXMLAttribute & k)
  193.     : key(k.key), value(k.value)       { }
  194.   VXMLAttribute& operator=(const VXMLAttribute & k)
  195.     { key = k.key; value = k.value; return *this; }
  196. };
  197. bool operator< (const VXMLAttribute & x, const VXMLAttribute & y)
  198. { if (x.key == NULL || y.key == NULL) return false;
  199.   return wcscmp(x.key, y.key) < 0; }
  200. typedef std::vector<VXMLAttribute>   TABLE_ATTRS;
  201. typedef std::vector<VXMLElementInfo> TABLE_ELEMS;
  202. TABLE_ATTRS  g_attrs;
  203. TABLE_ELEMS  g_elems;
  204. //#############################################################################
  205.  
  206. static void InitializeTables()
  207. {
  208.   // (1) Elements
  209.   // (1.1) VXML
  210.   g_elems.push_back(VXMLElementInfo(L"assign"      , NODE_ASSIGN));
  211.   g_elems.push_back(VXMLElementInfo(L"audio"       , NODE_AUDIO));
  212.   g_elems.push_back(VXMLElementInfo(L"block"       , NODE_BLOCK));
  213.   g_elems.push_back(VXMLElementInfo(L"cancel"      , NODE_CANCEL));
  214.   g_elems.push_back(VXMLElementInfo(L"catch"       , NODE_CATCH));
  215.   g_elems.push_back(VXMLElementInfo(L"choice"      , NODE_CHOICE));
  216.   g_elems.push_back(VXMLElementInfo(L"clear"       , NODE_CLEAR));
  217.   g_elems.push_back(VXMLElementInfo(L"data"        , NODE_DATA));
  218.   g_elems.push_back(VXMLElementInfo(L"disconnect"  , NODE_DISCONNECT));
  219.   g_elems.push_back(VXMLElementInfo(L"else"        , NODE_ELSE));
  220.   g_elems.push_back(VXMLElementInfo(L"elseif"      , NODE_ELSEIF));
  221.   g_elems.push_back(VXMLElementInfo(L"enumerate"   , NODE_ENUMERATE));
  222.   g_elems.push_back(VXMLElementInfo(L"exit"        , NODE_EXIT));
  223.   g_elems.push_back(VXMLElementInfo(L"field"       , NODE_FIELD));
  224.   g_elems.push_back(VXMLElementInfo(L"filled"      , NODE_FILLED));
  225.   g_elems.push_back(VXMLElementInfo(L"foreach"     , NODE_FOREACH));
  226.   g_elems.push_back(VXMLElementInfo(L"form"        , NODE_FORM));
  227.   g_elems.push_back(VXMLElementInfo(L"goto"        , NODE_GOTO));
  228.   g_elems.push_back(VXMLElementInfo(L"grammar"     , NODE_GRAMMAR));
  229.   g_elems.push_back(VXMLElementInfo(L"if"          , NODE_IF));
  230.   g_elems.push_back(VXMLElementInfo(L"initial"     , NODE_INITIAL));
  231.   g_elems.push_back(VXMLElementInfo(L"link"        , NODE_LINK));
  232.   g_elems.push_back(VXMLElementInfo(L"log"         , NODE_LOG));
  233.   g_elems.push_back(VXMLElementInfo(L"mark"        , NODE_MARK));
  234.   g_elems.push_back(VXMLElementInfo(L"menu"        , NODE_MENU));
  235.   g_elems.push_back(VXMLElementInfo(L"meta"        , NODE_META));
  236.   g_elems.push_back(VXMLElementInfo(L"object"      , NODE_OBJECT));
  237.   g_elems.push_back(VXMLElementInfo(L"option"      , NODE_OPTION));
  238.   g_elems.push_back(VXMLElementInfo(L"param"       , NODE_PARAM));
  239.   g_elems.push_back(VXMLElementInfo(L"prompt"      , NODE_PROMPT));
  240.   g_elems.push_back(VXMLElementInfo(L"property"    , NODE_PROPERTY));
  241.   g_elems.push_back(VXMLElementInfo(L"record"      , NODE_RECORD));
  242.   g_elems.push_back(VXMLElementInfo(L"return"      , NODE_RETURN));
  243.   g_elems.push_back(VXMLElementInfo(L"reprompt"    , NODE_REPROMPT));
  244.   g_elems.push_back(VXMLElementInfo(L"script"      , NODE_SCRIPT));
  245.   g_elems.push_back(VXMLElementInfo(L"subdialog"   , NODE_SUBDIALOG));
  246.   g_elems.push_back(VXMLElementInfo(L"submit"      , NODE_SUBMIT));
  247.   g_elems.push_back(VXMLElementInfo(L"throw"       , NODE_THROW));
  248.   g_elems.push_back(VXMLElementInfo(L"transfer"    , NODE_TRANSFER));
  249.   g_elems.push_back(VXMLElementInfo(L"value"       , NODE_VALUE));
  250.   g_elems.push_back(VXMLElementInfo(L"var"         , NODE_VAR));
  251.   g_elems.push_back(VXMLElementInfo(L"vxml"        , NODE_VXML));
  252.   // (1.2) from Defaults document
  253.   g_elems.push_back(VXMLElementInfo(L"defaults"    , DEFAULTS_ROOT));
  254.   g_elems.push_back(VXMLElementInfo(L"language"    , DEFAULTS_LANGUAGE));
  255.   // (1.3) Internals elements (these are converted to others)
  256.   g_elems.push_back(VXMLElementInfo(L"error"       , PRIV_ELEM_ERROR));
  257.   g_elems.push_back(VXMLElementInfo(L"help"        , PRIV_ELEM_HELP));
  258.   g_elems.push_back(VXMLElementInfo(L"noinput"     , PRIV_ELEM_NOINPUT));
  259.   g_elems.push_back(VXMLElementInfo(L"nomatch"     , PRIV_ELEM_NOMATCH));
  260.   g_elems.push_back(VXMLElementInfo(L"metadata"    , PRIV_ELEM_METADATA));
  261.   // (1.4) SSML
  262.   g_elems.push_back(VXMLElementInfo(L"break"       , PRIV_ELEM_BREAK));
  263.   g_elems.push_back(VXMLElementInfo(L"desc"        , PRIV_ELEM_DESC));
  264.   g_elems.push_back(VXMLElementInfo(L"emphasis"    , PRIV_ELEM_EMPHASIS));
  265.   g_elems.push_back(VXMLElementInfo(L"p"           , PRIV_ELEM_PARAGRAPH));
  266.   g_elems.push_back(VXMLElementInfo(L"paragraph"   , PRIV_ELEM_PARAGRAPH));
  267.   g_elems.push_back(VXMLElementInfo(L"phoneme"     , PRIV_ELEM_PHONEME));
  268.   g_elems.push_back(VXMLElementInfo(L"prosody"     , PRIV_ELEM_PROSODY));
  269.   g_elems.push_back(VXMLElementInfo(L"s"           , PRIV_ELEM_SENTENCE));
  270.   g_elems.push_back(VXMLElementInfo(L"say-as"      , PRIV_ELEM_SAYAS));
  271.   g_elems.push_back(VXMLElementInfo(L"sentence"    , PRIV_ELEM_SENTENCE));
  272.   g_elems.push_back(VXMLElementInfo(L"sub"         , PRIV_ELEM_SUB));
  273.   g_elems.push_back(VXMLElementInfo(L"voice"       , PRIV_ELEM_VOICE));
  274.   // (2) Element attributes
  275.   // (2.1) VXML 1.0
  276.   g_attrs.push_back(VXMLAttribute(L"_itemname"     , ATTRIBUTE__ITEMNAME));
  277.   g_attrs.push_back(VXMLAttribute(L"aai"           , ATTRIBUTE_AAI));
  278.   g_attrs.push_back(VXMLAttribute(L"aaiexpr"       , ATTRIBUTE_AAIEXPR));
  279.   g_attrs.push_back(VXMLAttribute(L"accept"        , ATTRIBUTE_ACCEPT));
  280.   g_attrs.push_back(VXMLAttribute(L"application"   , ATTRIBUTE_APPLICATION));
  281.   g_attrs.push_back(VXMLAttribute(L"archive"       , ATTRIBUTE_ARCHIVE));
  282.   g_attrs.push_back(VXMLAttribute(L"array"         , ATTRIBUTE_ARRAY));
  283.   g_attrs.push_back(VXMLAttribute(L"bargein"       , ATTRIBUTE_BARGEIN));
  284.   g_attrs.push_back(VXMLAttribute(L"bargeintype"   , ATTRIBUTE_BARGEINTYPE));
  285.   g_attrs.push_back(VXMLAttribute(L"base"          , ATTRIBUTE_BASE));
  286.   g_attrs.push_back(VXMLAttribute(L"beep"          , ATTRIBUTE_BEEP));
  287.   g_attrs.push_back(VXMLAttribute(L"bridge"        , ATTRIBUTE_BRIDGE));
  288.   g_attrs.push_back(VXMLAttribute(L"charset"       , ATTRIBUTE_CHARSET));
  289.   g_attrs.push_back(VXMLAttribute(L"classid"       , ATTRIBUTE_CLASSID));
  290.   g_attrs.push_back(VXMLAttribute(L"codebase"      , ATTRIBUTE_CODEBASE));
  291.   g_attrs.push_back(VXMLAttribute(L"codetype"      , ATTRIBUTE_CODETYPE));
  292.   g_attrs.push_back(VXMLAttribute(L"cond"          , ATTRIBUTE_COND));
  293.   g_attrs.push_back(VXMLAttribute(L"connecttimeout", ATTRIBUTE_CONNECTTIME));
  294.   g_attrs.push_back(VXMLAttribute(L"content"       , ATTRIBUTE_CONTENT));
  295.   g_attrs.push_back(VXMLAttribute(L"count"         , ATTRIBUTE_COUNT));
  296.   g_attrs.push_back(VXMLAttribute(L"data"          , ATTRIBUTE_DATA));
  297.   g_attrs.push_back(VXMLAttribute(L"dest"          , ATTRIBUTE_DEST));
  298.   g_attrs.push_back(VXMLAttribute(L"destexpr"      , ATTRIBUTE_DESTEXPR));
  299.   g_attrs.push_back(VXMLAttribute(L"dtmf"          , ATTRIBUTE_DTMF));
  300.   g_attrs.push_back(VXMLAttribute(L"dtmfterm"      , ATTRIBUTE_DTMFTERM));
  301.   g_attrs.push_back(VXMLAttribute(L"enctype"       , ATTRIBUTE_ENCTYPE));
  302.   g_attrs.push_back(VXMLAttribute(L"event"         , ATTRIBUTE_EVENT));
  303.   g_attrs.push_back(VXMLAttribute(L"eventexpr"     , ATTRIBUTE_EVENTEXPR));
  304.   g_attrs.push_back(VXMLAttribute(L"expr"          , ATTRIBUTE_EXPR));
  305.   g_attrs.push_back(VXMLAttribute(L"expritem"      , ATTRIBUTE_EXPRITEM));
  306.   g_attrs.push_back(VXMLAttribute(L"fetchaudio"    , ATTRIBUTE_FETCHAUDIO));
  307.   g_attrs.push_back(VXMLAttribute(L"fetchhint"     , ATTRIBUTE_FETCHHINT));
  308.   g_attrs.push_back(VXMLAttribute(L"fetchtimeout"  , ATTRIBUTE_FETCHTIMEOUT));
  309.   g_attrs.push_back(VXMLAttribute(L"finalsilence"  , ATTRIBUTE_FINALSILENCE));
  310.   g_attrs.push_back(VXMLAttribute(L"http-equiv"    , ATTRIBUTE_HTTP_EQUIV));
  311.   g_attrs.push_back(VXMLAttribute(L"id"            , ATTRIBUTE_ID));
  312.   g_attrs.push_back(VXMLAttribute(L"item"          , ATTRIBUTE_ITEM));
  313.   g_attrs.push_back(VXMLAttribute(L"label"         , ATTRIBUTE_LABEL));
  314.   g_attrs.push_back(VXMLAttribute(L"maxage"        , ATTRIBUTE_MAXAGE));
  315.   g_attrs.push_back(VXMLAttribute(L"maxstale"      , ATTRIBUTE_MAXSTALE));
  316.   g_attrs.push_back(VXMLAttribute(L"maxtime"       , ATTRIBUTE_MAXTIME));
  317.   g_attrs.push_back(VXMLAttribute(L"message"       , ATTRIBUTE_MESSAGE));
  318.   g_attrs.push_back(VXMLAttribute(L"messageexpr"   , ATTRIBUTE_MESSAGEEXPR));
  319.   g_attrs.push_back(VXMLAttribute(L"method"        , ATTRIBUTE_METHOD));
  320.   g_attrs.push_back(VXMLAttribute(L"modal"         , ATTRIBUTE_MODAL));
  321.   g_attrs.push_back(VXMLAttribute(L"mode"          , ATTRIBUTE_MODE));
  322.   g_attrs.push_back(VXMLAttribute(L"name"          , ATTRIBUTE_NAME));
  323.   g_attrs.push_back(VXMLAttribute(L"nameexpr"      , ATTRIBUTE_NAMEEXPR));
  324.   g_attrs.push_back(VXMLAttribute(L"namelist"      , ATTRIBUTE_NAMELIST));
  325.   g_attrs.push_back(VXMLAttribute(L"next"          , ATTRIBUTE_NEXT));
  326.   g_attrs.push_back(VXMLAttribute(L"nextitem"      , ATTRIBUTE_NEXTITEM));
  327.   g_attrs.push_back(VXMLAttribute(L"root"          , ATTRIBUTE_ROOT));
  328.   g_attrs.push_back(VXMLAttribute(L"scope"         , ATTRIBUTE_SCOPE));
  329.   g_attrs.push_back(VXMLAttribute(L"slot"          , ATTRIBUTE_SLOT));
  330.   g_attrs.push_back(VXMLAttribute(L"src"           , ATTRIBUTE_SRC));
  331.   g_attrs.push_back(VXMLAttribute(L"srcexpr"       , ATTRIBUTE_SRCEXPR));
  332.   g_attrs.push_back(VXMLAttribute(L"tag-format"    , ATTRIBUTE_TAGFORMAT));
  333.   g_attrs.push_back(VXMLAttribute(L"timeout"       , ATTRIBUTE_TIMEOUT));
  334.   g_attrs.push_back(VXMLAttribute(L"transferaudio" , ATTRIBUTE_TRANSFERAUDIO));
  335.   g_attrs.push_back(VXMLAttribute(L"type"          , ATTRIBUTE_TYPE));
  336.   g_attrs.push_back(VXMLAttribute(L"value"         , ATTRIBUTE_VALUE));
  337.   g_attrs.push_back(VXMLAttribute(L"valuetype"     , ATTRIBUTE_VALUETYPE));
  338.   g_attrs.push_back(VXMLAttribute(L"version"       , ATTRIBUTE_VERSION));
  339.   g_attrs.push_back(VXMLAttribute(L"weight"        , ATTRIBUTE_WEIGHT));
  340.   g_attrs.push_back(VXMLAttribute(L"xml:lang"      , ATTRIBUTE_XMLLANG));
  341.   // (2.2) Internals attributes (these are converted to others)
  342.   g_attrs.push_back(VXMLAttribute(L"schemaLocation", PRIV_ATTRIB_SCHEMALOC));
  343.   // (3) Final stuff.
  344.   std::sort(g_attrs.begin(), g_attrs.end());
  345.   std::sort(g_elems.begin(), g_elems.end());
  346. }
  347. static void DeinitializeTables()
  348. {
  349.   g_attrs.clear();
  350.   g_elems.clear();
  351. }
  352. static bool ConvertElement(const VXIchar * name, int & result)
  353. {
  354.   if (name == NULL) return false;
  355.   TABLE_ELEMS::iterator i = std::lower_bound(g_elems.begin(), g_elems.end(),
  356.                                              VXMLElementInfo(name, NODE_IF));
  357.   if (wcscmp(name, (*i).key) != 0) return false;
  358.   result = (*i).value;
  359.   return true;
  360. }
  361. static bool ConvertAttribute(const VXIchar * name, int & result)
  362. {
  363.   if (name == NULL) return false;
  364.   TABLE_ATTRS::iterator i = std::lower_bound(g_attrs.begin(), g_attrs.end(),
  365.                                              VXMLAttribute(name,ATTRIBUTE_ID));
  366.   if (wcscmp(name, (*i).key) != 0) return false;
  367.   result = (*i).value;
  368.   return true;
  369. }
  370. inline static void TrimUriBase(vxistring & uri)
  371. {
  372.   vxistring::size_type pos = uri.rfind(L'?');
  373.   if( pos != vxistring::npos )
  374.     uri.erase(pos);    
  375. }
  376. //#############################################################################
  377. class DummyLocator : public Locator {
  378. public:
  379.   DummyLocator()  { }
  380.   virtual ~DummyLocator() { }
  381.   virtual const XMLCh* getPublicId() const    { return NULL; }
  382.   virtual const XMLCh* getSystemId() const    { return NULL; }
  383.   virtual XMLSSize_t getLineNumber() const    { return -1; }
  384.   virtual XMLSSize_t getColumnNumber() const  { return -1; }
  385. };
  386. static DummyLocator DUMMYLOCATOR;
  387. //#############################################################################
  388. bool DocumentConverter::Initialize()
  389. {
  390.   InitializeTables();
  391.   return true;
  392. }
  393. void DocumentConverter::Deinitialize()
  394. {
  395.   DeinitializeTables();
  396. }
  397. DocumentConverter::DocumentConverter()
  398.   : locator(&DUMMYLOCATOR), doc(NULL), documentLevel(DOCUMENT),
  399.     copyDepth(0), pcdataImpliesPrompt(true), contentForbidden(false), hasContent(true),
  400.     inCDATA(false), foreachImplicit(false)
  401. {
  402. }
  403. DocumentConverter::~DocumentConverter()
  404. {
  405.   ResetDocument();
  406. }
  407. // remove all irrelavant part from the base uri
  408. void DocumentConverter::SetBaseUri(const VXIchar* const base) 
  409.   if( base != NULL ) {
  410.     baseUri = base; 
  411.   }
  412.   else baseUri=L"";  
  413. }
  414. void DocumentConverter::RestoreBaseURLFromCache(const VXMLDocument & doc)
  415. {
  416.   vxistring baseURL;
  417.   doc.GetBaseURL(baseURL);
  418.   if( !baseURL.empty() ) SetBaseUri(baseURL.c_str());
  419. }
  420. // Restore default language id if read from cache
  421. void DocumentConverter::RestoreDefaultLangFromCache(const VXMLDocument & doc)
  422. {
  423.   doc.GetDefaultLang(defaultLang);
  424. }
  425. void DocumentConverter::ParseException(const VXIchar * message) const
  426. {
  427.   if (message == NULL) throw SAXParseException(NULL, *locator);
  428.   VXIcharToXMLCh text(message);
  429.   throw SAXParseException(text.c_str(), *locator);
  430. }
  431. void DocumentConverter::startDocument()
  432. {
  433.   version = -1;
  434.   choiceNumber = 0;
  435.   inGrammar = false;
  436.   implicitPrompt = false;
  437.   explicitPrompt = 0;
  438.   copyDepth = 0;
  439.   ignoreDepth = 0;
  440.   pcdataImpliesPrompt = true;
  441.   
  442.   // Don't know the language yet!
  443.   documentLang=L"";
  444.   if (doc != NULL) VXMLDocumentRep::Release(doc);
  445.   doc = new VXMLDocumentRep();
  446.   if (doc == NULL)
  447.     ParseException(L"unable to allocate memory for element");
  448. }
  449. void DocumentConverter::endDocument()
  450. {
  451.   if ( !filledNameList.empty() && !namedControlItems.empty() ){
  452.     while(!filledNameList.empty()){
  453.       vxistring namelist = filledNameList.front();
  454.       filledNameList.pop_front();
  455.       std::basic_stringstream<VXIchar> namestream(namelist);
  456.       while (namestream.good()) {
  457.         vxistring temp;
  458.         namestream >> temp;
  459.         if (!temp.empty()){
  460.           if ( std::find(
  461.               namedControlItems.begin(),
  462.               namedControlItems.end(),
  463.               temp) != namedControlItems.end()){
  464.               ParseException(L"filled namelist cannot contain control items");
  465.             }
  466.         }
  467.       }
  468.     }
  469.   }
  470. void DocumentConverter::ResetDocument()
  471. {
  472.   if (doc != NULL) VXMLDocumentRep::Release(doc);
  473.   doc = NULL;
  474. }
  475. VXMLDocumentRep * DocumentConverter::GetDocument()
  476. {
  477.   VXMLDocumentRep * temp = doc;
  478.   doc = NULL;
  479.   return temp;
  480. }
  481. void DocumentConverter::setDocumentLocator(const Locator* const loc)
  482. {
  483.   if (loc == NULL)
  484.     locator = &DUMMYLOCATOR;
  485.   else
  486.     locator = loc;
  487. }
  488. void DocumentConverter::startElement(const XMLCh* const uri,
  489.                                      const XMLCh* const localname,
  490.                                      const XMLCh* const qname,
  491.                                      const Attributes & attrs)
  492. {
  493.   // (0) Just ignore this?
  494.   if (ignoreDepth > 0) {
  495.     ++ignoreDepth;
  496.     return;
  497.   }
  498.   // (1) Copy this element or look it up?
  499.   // (1.1) Inside a <grammar>, we can blindly copy this data.
  500.   if (inGrammar) {
  501.     CopyElementStart(localname, attrs);
  502.     return;
  503.   }
  504.   // (1.2) Convert name string to integer value.
  505.   XMLChToVXIchar elementName(localname);
  506.   int rawElemType;
  507.   bool conversionFailed = !ConvertElement(elementName.c_str(), rawElemType);
  508.   // (1.3) Copy almost anything inside a <prompt>.
  509.   if (explicitPrompt) {
  510.     if (conversionFailed) {
  511.       CopyElementStart(localname, attrs);
  512.       return;
  513.     }
  514.     switch (rawElemType) {
  515.     // elements that could be children of <foreach> but cannot 
  516.     // be converted to SSML text
  517.     case NODE_AUDIO:
  518.     case NODE_VALUE:
  519.     case NODE_ENUMERATE:
  520.     case NODE_FOREACH:
  521.     case NODE_MARK:
  522.     case NODE_ASSIGN:
  523.     case NODE_EXIT:
  524.     case NODE_LOG:
  525.     case NODE_IF:
  526.     case NODE_ELSE:
  527.     case NODE_ELSEIF:
  528.     case NODE_CLEAR:
  529.     case NODE_DATA:
  530.     case NODE_DISCONNECT:
  531.     case NODE_GOTO:
  532.     case NODE_SUBMIT:
  533.     case NODE_VAR:
  534.     case NODE_RETURN:
  535.     case NODE_SCRIPT:
  536.     case NODE_REPROMPT:
  537.     case NODE_THROW:
  538.       break;
  539.     // ignore nested prompts
  540.     case NODE_PROMPT:
  541.       explicitPrompt++;
  542.       return;
  543.     // all other elements in an explicit prompt get
  544.     // converted to SSML text now
  545.     default:
  546.       CopyElementStart(localname, attrs);
  547.       return;
  548.     }
  549.   }
  550.   // (1.4) Print errors for all other conversion failures.
  551.   if (conversionFailed) {
  552.     vxistring temp(L"unrecognized element - ");
  553.     temp += elementName.c_str();
  554.     ParseException(temp.c_str());
  555.   }
  556.   // (2) Check for ignored nodes and do version number processing.
  557.   if (rawElemType == PRIV_ELEM_METADATA) {
  558.     ignoreDepth = 1;
  559.     return;
  560.   }
  561.   // (2.1) Catch illegal nodes and do version number processing.  Prompt and
  562.   //       grammar copying flags are set in the same pass.
  563.   switch (rawElemType) {
  564.   case DEFAULTS_ROOT:
  565.     version = 2.0f;
  566.     break;
  567.   case NODE_VXML:
  568.     {
  569.       for (unsigned int index = 0; index < attrs.getLength(); ++index) {
  570.         if (!Compare(attrs.getLocalName(index), L"version")) continue;
  571.         const XMLCh * attributeValue = attrs.getValue(index);
  572.         if (Compare(attributeValue, L"2.0")) version = 2.0f;
  573.         else if (Compare(attributeValue, L"2.1")) version = 2.1f;
  574.         else ParseException(L"unsupported vxml version");
  575.         break;
  576.       }
  577.     }
  578.     break;
  579.   case NODE_GRAMMAR:      // Copy grammar contents.
  580.     inGrammar = true;
  581.     copyDepth = 0;
  582.     break;
  583.   case NODE_PROMPT:
  584.     // if inside a foreach element, simply ignore prompt elements
  585.     if (foreachImplicit) {
  586.       return;
  587.     }
  588.     // keep track of the number of explicit prompt elements we
  589.     // reach since a foreach element could follow, which in
  590.     // turn could contain prompt elements.
  591.     explicitPrompt++;
  592.     if (explicitPrompt == 1)
  593.       copyDepth = 0;
  594.     break;
  595.   case NODE_SCRIPT:
  596.   case NODE_CHOICE:       // These may have PCDATA which is not a prompt
  597.   case NODE_OPTION:
  598.   case NODE_LOG:   
  599.     pcdataImpliesPrompt = false;
  600.     break;
  601.   default:
  602.     break;
  603.   }
  604.   // (2.2) Is this the beginning or end of an implicit prompt?
  605.   switch (rawElemType) {
  606.   // these will start an implicit prompt, but cannot be converted
  607.   // to SSML text because they must be evaluated during runtime
  608.   case NODE_AUDIO:
  609.   case NODE_VALUE:        // NOTE: A <value> may also appear in <log>.
  610.   case NODE_ENUMERATE:
  611.   case NODE_MARK:
  612.     if (pcdataImpliesPrompt && !implicitPrompt && !explicitPrompt)
  613.       StartImplicitPrompt();
  614.     break;
  615.   // a foreach that isn't within a <prompt> will start an
  616.   // implicit prompt
  617.   case NODE_FOREACH:
  618.     if (implicitPrompt)
  619.       foreachImplicit = true;
  620.     else if (!implicitPrompt && !explicitPrompt) {
  621.       foreachImplicit = true;
  622.       StartImplicitPrompt();
  623.     }
  624.     break;
  625.   // SSML elements will trigger an implicit prompt, and get
  626.   // converted to SSML text now
  627.   case PRIV_ELEM_BREAK:   
  628.   case PRIV_ELEM_EMPHASIS:
  629.   case PRIV_ELEM_PARAGRAPH:
  630.   case PRIV_ELEM_PHONEME:
  631.   case PRIV_ELEM_PROSODY:
  632.   case PRIV_ELEM_SAYAS:
  633.   case PRIV_ELEM_SENTENCE:
  634.   case PRIV_ELEM_VOICE:
  635.     if (!implicitPrompt)
  636.       StartImplicitPrompt();
  637.     CopyElementStart(localname, attrs);
  638.     return;
  639.   // executable content that shouldn't end an implicit prompt if
  640.   // if within a foreach
  641.   case NODE_ASSIGN:
  642.   case NODE_EXIT:
  643.   case NODE_LOG:
  644.   case NODE_IF:
  645.   case NODE_ELSE:
  646.   case NODE_ELSEIF:
  647.   case NODE_CLEAR:
  648.   case NODE_DATA:
  649.   case NODE_DISCONNECT:
  650.   case NODE_GOTO:
  651.   case NODE_SUBMIT:
  652.   case NODE_VAR:
  653.   case NODE_RETURN:
  654.   case NODE_SCRIPT:
  655.   case NODE_REPROMPT:
  656.   case NODE_THROW:
  657.     if (foreachImplicit)
  658.       break;
  659. // fall through intentional
  660.   default:
  661.     if (implicitPrompt) {
  662.       if (copyDepth == 0)
  663.         EndImplicitPrompt();
  664.       else
  665.         ParseException(L"illegal prompt content");
  666.     }
  667.     break;
  668.   }
  669.   // (2.4) Convert nodes as necessary.
  670.   VXMLElementType elemType = VXMLElementType(rawElemType);
  671.   switch (rawElemType) {
  672.   case PRIV_ELEM_ERROR:
  673.   case PRIV_ELEM_HELP:
  674.   case PRIV_ELEM_NOINPUT:
  675.   case PRIV_ELEM_NOMATCH:
  676.     elemType = NODE_CATCH;
  677.     break;
  678.   default:
  679.     break;
  680.   }
  681.   if (elemType > PRIV_ELEM_RangeStart) {
  682.     vxistring temp(L"internal error for element - ");
  683.     temp += elementName.c_str();
  684.     ParseException(temp.c_str());
  685.   }
  686.   // (3) Create new element.
  687.   try {
  688.     hasContent = (elemType != NODE_SCRIPT && elemType != NODE_GRAMMAR);
  689.     doc->StartElement(elemType, documentLevel);
  690.   }
  691.   catch (const VXMLDocumentModel::OutOfMemory &) {
  692.     ParseException(L"unable to allocate memory for element");
  693.   }
  694.   catch (const VXMLDocumentModel::InternalError &) {
  695.     ParseException(L"corrupted document tree; unable to add element");
  696.   }
  697.   // (4) Add attributes to element.
  698.   int attrGroup1 = 0; 
  699.   int attrGroup2 = 0; 
  700.   int attrGroup3 = 0; 
  701.   // (4.1) Do special handling for converted nodes.
  702.   switch (rawElemType) {
  703.   case PRIV_ELEM_ERROR:
  704.     ProcessNodeAttribute(elemType, ATTRIBUTE_EVENT, L"error");
  705.     break;
  706.   case PRIV_ELEM_HELP:
  707.     ProcessNodeAttribute(elemType, ATTRIBUTE_EVENT, L"help");
  708.     break;
  709.   case PRIV_ELEM_NOINPUT:
  710.     ProcessNodeAttribute(elemType, ATTRIBUTE_EVENT, L"noinput");
  711.     break;
  712.   case PRIV_ELEM_NOMATCH:
  713.     ProcessNodeAttribute(elemType, ATTRIBUTE_EVENT, L"nomatch");
  714.     break;
  715.   default:
  716.     break;
  717.   }
  718.   bool foundResourceBaseURI  = false;
  719.   bool foundResourceBaseLang = false;
  720.   for (unsigned int index = 0; index < attrs.getLength(); ++index) {
  721.     // (4.2) Convert string to integer.
  722.     int attrType;
  723.     XMLChToVXIchar attributeName(attrs.getLocalName(index));
  724.     if (Compare(attrs.getQName(index), L"xml:lang")) {
  725.       attrType = ATTRIBUTE_XMLLANG;     
  726.     }
  727.     else if (!ConvertAttribute(attributeName.c_str(), attrType)) {
  728.       vxistring temp(L"unrecognized attribute - ");
  729.       temp += attributeName.c_str();
  730.       ParseException(temp.c_str());
  731.     }
  732.     // (4.3) Impose attribute restrictions not representable in schema 1.0.
  733.     switch (elemType) {
  734.     case NODE_AUDIO:
  735.       if (attrType == ATTRIBUTE_SRC)          ++attrGroup1;
  736.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  737.       break;
  738.     case NODE_DATA:
  739.       if (attrType == ATTRIBUTE_SRC)          ++attrGroup1;
  740.       if (attrType == ATTRIBUTE_SRCEXPR)      ++attrGroup1;
  741.       break;
  742.     case NODE_CHOICE:
  743.     case NODE_LINK:
  744.       if (attrType == ATTRIBUTE_NEXT)         ++attrGroup1;
  745.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  746.       if (attrType == ATTRIBUTE_EVENT)        ++attrGroup1;
  747.       if (attrType == ATTRIBUTE_EVENTEXPR)    ++attrGroup1;
  748.       // and...
  749.       if (attrType == ATTRIBUTE_MESSAGE)      ++attrGroup2;
  750.       if (attrType == ATTRIBUTE_MESSAGEEXPR)  ++attrGroup2;
  751.       break;
  752.     case NODE_EXIT:
  753.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  754.       if (attrType == ATTRIBUTE_NAMELIST)     ++attrGroup1;
  755.       break;
  756.     case NODE_GOTO:
  757.       if (attrType == ATTRIBUTE_NEXT)         ++attrGroup1;
  758.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  759.       if (attrType == ATTRIBUTE_NEXTITEM)     ++attrGroup1;
  760.       if (attrType == ATTRIBUTE_EXPRITEM)     ++attrGroup1;
  761.       break;
  762.     case NODE_GRAMMAR:
  763.       if (attrType == ATTRIBUTE_SRC){
  764.          contentForbidden = true;
  765.          ++attrGroup1;
  766.       }
  767.       if (attrType == ATTRIBUTE_SRCEXPR){
  768.          contentForbidden = true;
  769.          ++attrGroup1;
  770.       }
  771.       break;
  772.     case NODE_MARK:
  773.       if (attrType == ATTRIBUTE_NAME)         ++attrGroup1;
  774.       if (attrType == ATTRIBUTE_NAMEEXPR)     ++attrGroup1;
  775.       break;
  776.     case NODE_META:
  777.       if (attrType == ATTRIBUTE_NAME)         ++attrGroup1;
  778.       if (attrType == ATTRIBUTE_HTTP_EQUIV)   ++attrGroup1;
  779.       break;
  780.     case NODE_PARAM:
  781.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  782.       if (attrType == ATTRIBUTE_VALUE)        ++attrGroup1;
  783.       break;
  784.     case NODE_RETURN:
  785.       if (attrType == ATTRIBUTE_EVENT)        ++attrGroup1;
  786.       if (attrType == ATTRIBUTE_EVENTEXPR)    ++attrGroup1;
  787.       if (attrType == ATTRIBUTE_NAMELIST)     ++attrGroup1;
  788.       // and...
  789.       if (attrType == ATTRIBUTE_MESSAGE)      ++attrGroup2;
  790.       if (attrType == ATTRIBUTE_MESSAGEEXPR)  ++attrGroup2;
  791.       break;
  792.     case NODE_SCRIPT:
  793.       if (attrType == ATTRIBUTE_SRC){
  794.          contentForbidden = true;
  795.          ++attrGroup1;
  796.       }
  797.       if (attrType == ATTRIBUTE_SRCEXPR){
  798.          contentForbidden = true;
  799.          ++attrGroup1;
  800.       }
  801.       break;
  802.     case NODE_SUBDIALOG:
  803.       if (attrType == ATTRIBUTE_SRC)          ++attrGroup1;
  804.       if (attrType == ATTRIBUTE_SRCEXPR)      ++attrGroup1;
  805.       break;
  806.     case NODE_SUBMIT:
  807.       if (attrType == ATTRIBUTE_NEXT)         ++attrGroup1;
  808.       if (attrType == ATTRIBUTE_EXPR)         ++attrGroup1;
  809.       break;
  810.     case NODE_THROW:
  811.       if (attrType == ATTRIBUTE_EVENT)        ++attrGroup1;
  812.       if (attrType == ATTRIBUTE_EVENTEXPR)    ++attrGroup1;
  813.       // and...
  814.       if (attrType == ATTRIBUTE_MESSAGE)      ++attrGroup2;
  815.       if (attrType == ATTRIBUTE_MESSAGEEXPR)  ++attrGroup2;
  816.       break;
  817.     case NODE_TRANSFER:
  818.       if (attrType == ATTRIBUTE_DEST)         ++attrGroup1;
  819.       if (attrType == ATTRIBUTE_DESTEXPR)     ++attrGroup1;
  820.       // and...
  821.       if (attrType == ATTRIBUTE_AAI)          ++attrGroup2;
  822.       if (attrType == ATTRIBUTE_AAIEXPR)      ++attrGroup2;
  823.       // and...
  824.       if (attrType == ATTRIBUTE_BRIDGE)       ++attrGroup3;
  825.       if (attrType == ATTRIBUTE_TYPE)         ++attrGroup3;
  826.       break;
  827.     }
  828.     // Handle base URI resolution and xml:lang for <prompt> & <grammar>
  829.     switch (elemType) {
  830.     case NODE_PROMPT:
  831.     case NODE_GRAMMAR: 
  832.       if (attrType == ATTRIBUTE_XMLLANG)   foundResourceBaseLang = true;
  833.       else if (attrType == ATTRIBUTE_BASE) foundResourceBaseURI  = true;
  834.       else if (attrType == ATTRIBUTE_TYPE) {
  835.         // set grammar type for this grammar element
  836.         XMLChToVXIchar attributeValue(attrs.getValue(index));
  837.         SetGrammarType(attributeValue.c_str());
  838.       }
  839.       break;
  840.     case NODE_VXML:
  841.       if (attrType == ATTRIBUTE_BASE) {
  842.         XMLChToVXIchar attributeValue(attrs.getValue(index));
  843.         SetBaseUri(attributeValue.c_str());
  844.         doc->rootBaseURL = attributeValue.c_str();
  845.         continue;
  846.       }
  847.       else if (attrType == ATTRIBUTE_XMLLANG) {
  848.         XMLChToVXIchar attributeValue(attrs.getValue(index));
  849.         documentLang = attributeValue.c_str();
  850.         continue;
  851.       }
  852.       break;
  853.     case DEFAULTS_ROOT:
  854.       if (attrType == ATTRIBUTE_XMLLANG) {
  855.         XMLChToVXIchar attributeValue(attrs.getValue(index));
  856.         defaultLang = attributeValue.c_str();
  857.         documentLang = defaultLang;
  858.         continue;
  859.       }
  860.       break;
  861.     }
  862.     
  863.     // (4.5) Handle internal values.
  864.     XMLChToVXIchar attributeValue(attrs.getValue(index));
  865.     ProcessNodeAttribute(elemType, attrType, attributeValue.c_str());
  866.   }
  867.   // Use the default language for this document if necessary.
  868.   if (elemType == NODE_VXML && documentLang.empty()) {
  869.     documentLang = defaultLang;
  870.   }
  871.       
  872.   // Assign an xml:base and/or xml:lang if not already specified for every
  873.   // <grammar>, <prompt>, and <field> (because of builtins).
  874.   if (elemType == NODE_PROMPT || elemType == NODE_GRAMMAR ||
  875.       elemType == NODE_FIELD  || elemType == NODE_MENU)
  876.   {
  877.     if (!foundResourceBaseURI) 
  878.       doc->AddAttribute(ATTRIBUTE_BASE, baseUri.c_str());
  879.     if (!foundResourceBaseLang) {
  880.       doc->AddAttribute(ATTRIBUTE_XMLLANG, documentLang.c_str());      
  881.     }
  882.   }
  883.   // (5) Generate errors for attribute restrictions.
  884.   switch (elemType) {
  885.   case NODE_AUDIO:
  886.     if (attrGroup1 != 1)
  887.       ParseException(L"exactly one of 'src' or 'expr' must be specified");
  888.     break;
  889.   case NODE_DATA:
  890.     if (attrGroup1 != 1)
  891.       ParseException(L"exactly one of 'src' or 'srcexpr' must be specified");
  892.     break;
  893.   case NODE_CHOICE:
  894.   case NODE_LINK:
  895.     if (attrGroup1 != 1)
  896.       ParseException(L"exactly one of 'next', 'expr', 'event' or 'eventexpr' "
  897.                      L"must be specified");
  898.     if (attrGroup2 > 1)
  899.       ParseException(L"exactly one of 'message' or 'messageexpr' "
  900.                      L"may be specified");
  901.     break;
  902.   case NODE_EXIT:
  903.     if (attrGroup1 > 1)
  904.       ParseException(L"exactly one of 'expr' or 'namelist' "
  905.                      L"may be specified");
  906.     break;
  907.   case NODE_GOTO:
  908.     if (attrGroup1 != 1)
  909.       ParseException(L"exactly one of 'next', 'expr', 'nextitem' or 'expritem'"
  910.                      L" must be specified");
  911.     break;
  912.   case NODE_MARK:
  913.     if (attrGroup1 != 1)
  914.       ParseException(L"exactly one of 'name' or 'nameexpr' "
  915.                      L"must be specified");
  916.     break;
  917.   case NODE_META:
  918.     if (attrGroup1 != 1)
  919.       ParseException(L"exactly one of 'name' or 'http-equiv' "
  920.                      L"must be specified");
  921.     break;
  922.   case NODE_PARAM:
  923.     if (attrGroup1 != 1)
  924.       ParseException(L"exactly one of 'expr' or 'value' must be specified");
  925.     break;
  926.   case NODE_RETURN:
  927.     if (attrGroup1 > 1)
  928.       ParseException(L"exactly one of 'event', 'eventexpr' or 'namelist' "
  929.                      L"may be specified");
  930.     if (attrGroup2 > 1)
  931.       ParseException(L"exactly one of 'message' or 'messageexpr' "
  932.                      L"may be specified");
  933.     break;
  934.   case NODE_GRAMMAR:
  935.   case NODE_SCRIPT:
  936.     if (attrGroup1 > 1)
  937.       ParseException(L"exactly one of 'src' or 'srcexpr' must be specified");
  938.     break;
  939.   case NODE_SUBDIALOG:
  940.     if (attrGroup1 != 1)
  941.       ParseException(L"exactly one of 'src' or 'srcexpr' must be specified");
  942.     break;
  943.   case NODE_SUBMIT:
  944.     if (attrGroup1 != 1)
  945.       ParseException(L"exactly one of 'next' or 'expr' must be specified");
  946.     break;
  947.   case NODE_THROW:
  948.     if (attrGroup1 != 1)
  949.       ParseException(L"exactly one of 'event' or 'eventexpr' "
  950.                      L"must be specified");
  951.     if (attrGroup2 > 1)
  952.       ParseException(L"exactly one of 'message' or 'messageexpr' "
  953.                      L"may be specified");
  954.     break;
  955.   case NODE_TRANSFER:
  956.     if (attrGroup1 > 1)
  957.       ParseException(L"exactly one of 'dest' or 'destexpr' may be specified");
  958.     if (attrGroup2 > 1)
  959.       ParseException(L"exactly one of 'aai' or 'aaiexpr' may be specified");
  960.     if (attrGroup3 > 1)
  961.       ParseException(L"exactly one of 'bridge' or 'type' may be specified");
  962.     break;
  963.   }
  964.   // (6) Verify the node.
  965.   ProcessNodeFinal(elemType);
  966. }
  967. void DocumentConverter::endElement(const XMLCh* const uri,
  968.                                    const XMLCh* const localname,
  969.                                    const XMLCh* const qname)
  970. {
  971.   // (0) Just ignore this?
  972.   if (ignoreDepth > 0) {
  973.     --ignoreDepth;
  974.     return;
  975.   }
  976.   // (1) Copy this element or look it up?
  977.   // (1.1) Inside a <grammar>, we can blindly copy this data.
  978.   if (inGrammar && copyDepth == 0) inGrammar = false;
  979.   if (inGrammar) {
  980.     CopyElementClose(localname);
  981.     return;
  982.   }
  983.   // (1.2) Convert name string to integer value.
  984.   XMLChToVXIchar elementName(localname);
  985.   int elemType;
  986.   bool conversionFailed = !ConvertElement(elementName.c_str(), elemType);
  987.   // (1.3) Handle elements inside a <prompt>.
  988.   if (explicitPrompt) {
  989.     if (conversionFailed) {
  990.       CopyElementClose(localname);
  991.       return;
  992.     }
  993.     switch (elemType) {
  994.     // these elements don't get copied as text within a prompt
  995.     case NODE_FOREACH:
  996.     case NODE_AUDIO:
  997.     case NODE_VALUE:
  998.     case NODE_ENUMERATE:
  999.     case NODE_MARK:
  1000.     case NODE_ASSIGN:
  1001.     case NODE_EXIT:
  1002.     case NODE_LOG:
  1003.     case NODE_IF:
  1004.     case NODE_ELSE:
  1005.     case NODE_ELSEIF:
  1006.     case NODE_CLEAR:
  1007.     case NODE_DATA:
  1008.     case NODE_DISCONNECT:
  1009.     case NODE_GOTO:
  1010.     case NODE_SUBMIT:
  1011.     case NODE_VAR:
  1012.     case NODE_RETURN:
  1013.     case NODE_SCRIPT:
  1014.     case NODE_REPROMPT:
  1015.     case NODE_THROW:
  1016.       break;
  1017.     case NODE_PROMPT:
  1018.       if (foreachImplicit) {
  1019.         return;
  1020.       }
  1021.       explicitPrompt--;
  1022.       if (explicitPrompt > 0) {
  1023.         return;
  1024.       }
  1025.       doc->AddContent(L"</speak>", 8);
  1026.       break;
  1027.     // since we are in an explicit prompt, this handles
  1028.     // closing SSML elements
  1029.     default:
  1030.       CopyElementClose(localname);
  1031.       return;
  1032.     }
  1033.   }
  1034.   // (1.4) Print errors for all other conversion failures.
  1035.   if (conversionFailed) {
  1036.     vxistring temp(L"unrecognized element - ");
  1037.     temp += elementName.c_str();
  1038.     ParseException(temp.c_str());
  1039.   }
  1040.   // (2) Check for ending of implicit prompts.
  1041.   if (implicitPrompt) {
  1042.     switch (elemType) {
  1043.     // none of these elements should end an implicit prompt
  1044.     // because we don't know what's to follow
  1045.     case NODE_AUDIO:
  1046.     case NODE_VALUE:
  1047.     case NODE_ENUMERATE:
  1048.       break;
  1049.     // These implicitly imply a prompt and the contents
  1050.     // are done converting to text
  1051.     case PRIV_ELEM_BREAK:  
  1052.     case PRIV_ELEM_EMPHASIS:
  1053.     case PRIV_ELEM_PARAGRAPH:
  1054.     case PRIV_ELEM_PHONEME:
  1055.     case PRIV_ELEM_PROSODY:
  1056.     case PRIV_ELEM_SAYAS:
  1057.     case PRIV_ELEM_SENTENCE:
  1058.     case PRIV_ELEM_VOICE:
  1059.       CopyElementClose(localname);
  1060.       return;
  1061.     // if we're in an implicit prompt and encounter a prompt element,
  1062.     // it's because we're withing a foreach... so we just ignore
  1063.     // the prompt element.
  1064. case NODE_PROMPT:
  1065. return;
  1066.     // we're out of the foreach implicit prompt
  1067.     case NODE_FOREACH:
  1068.       foreachImplicit = false;
  1069.   break;
  1070. // executable content that could be within a foreach
  1071.     case NODE_ASSIGN:
  1072.     case NODE_EXIT:
  1073.     case NODE_LOG:
  1074.     case NODE_IF:
  1075.     case NODE_ELSE:
  1076.     case NODE_ELSEIF:
  1077.     case NODE_CLEAR:
  1078.     case NODE_DATA:
  1079.     case NODE_DISCONNECT:
  1080.     case NODE_GOTO:
  1081.     case NODE_SUBMIT:
  1082.     case NODE_MARK:
  1083.     case NODE_VAR:
  1084.     case NODE_RETURN:
  1085.     case NODE_SCRIPT:
  1086.     case NODE_REPROMPT:
  1087.     case NODE_THROW:
  1088.       if (foreachImplicit)
  1089.         break;
  1090.       // fall through intentional
  1091.     default:
  1092.       EndImplicitPrompt();
  1093.       break;
  1094.     }
  1095.   }
  1096.   // (3) Reverse special handling from startElement()
  1097.   switch (elemType) {
  1098.   case NODE_CHOICE:          // These may have PCDATA which is not a prompt
  1099.   case NODE_OPTION:
  1100.     doc->PruneWhitespace();
  1101.     pcdataImpliesPrompt = true;
  1102.   case NODE_LOG:
  1103.     pcdataImpliesPrompt = true;
  1104.     break;
  1105.   case NODE_SCRIPT:
  1106.     pcdataImpliesPrompt = true;
  1107.     // Intentional fall through!
  1108.   case NODE_GRAMMAR:
  1109.     if (contentForbidden ^ hasContent)
  1110.       contentForbidden = false;
  1111.     else
  1112.       ParseException(L"either the 'src' attribute or inlined content "
  1113.                      L"may be provided, not both");
  1114.     break;
  1115.   default:
  1116.     break;
  1117.   }
  1118.   // (4) This element is complete.
  1119.   try {
  1120.     doc->EndElement();
  1121.   }
  1122.   catch (const VXMLDocumentModel::InternalError &) {
  1123.     ParseException(L"corrupted document tree; unable to complete element");
  1124.   }
  1125. }
  1126. void DocumentConverter::characters(const XMLCh* const chars,
  1127.                                    const unsigned int length)
  1128. {
  1129.   // (0) Just ignore this?
  1130.   if (chars == NULL || length == 0 || ignoreDepth > 0) return;
  1131.   // (1) Ignore non-empty prompts consisting of pure whitespace
  1132.   unsigned int l;
  1133.   for (l = 0; l < length; ++l) {
  1134.     XMLCh c = chars[l];
  1135.     if (c == ' ' || c == 'n' || c == 'r' || c == 't') continue;
  1136.     break;
  1137.   }
  1138.   if (l == length) return;
  1139.   // (2) Is this an implicit prompt?
  1140.   if (pcdataImpliesPrompt && !explicitPrompt && !implicitPrompt && !inGrammar)
  1141.     StartImplicitPrompt();
  1142.   // (3) Add content to the document model.
  1143.   try {
  1144.     XMLChToVXIchar data(chars);
  1145.     hasContent = true;
  1146.     
  1147.     if( (inGrammar && (GetGrammarType().empty() || GetGrammarType() == L"application/srgs+xml")) ||
  1148.         (explicitPrompt || implicitPrompt)
  1149.         ) 
  1150.     {
  1151.       if (!inCDATA)
  1152.         doc->AddContent(data.c_str(), wcslen(data.c_str()));
  1153.       else {
  1154.         doc->AddContent(L"<![CDATA[", 9);
  1155.         doc->AddContent(data.c_str(), wcslen(data.c_str()));
  1156.         doc->AddContent(L"]]>", 3);
  1157.       }
  1158.     } else {
  1159.       doc->AddContent(data.c_str(), wcslen(data.c_str()));
  1160.     }
  1161.   }
  1162.   catch (const VXMLDocumentModel::OutOfMemory &) {
  1163.     ParseException(L"unable to allocate memory for content");
  1164.   }
  1165.   catch (const VXMLDocumentModel::InternalError &) {
  1166.     ParseException(L"corrupted document tree; unable to add content");
  1167.   }
  1168. }
  1169. void DocumentConverter::ignorableWhitespace(const XMLCh* const chars,
  1170.                                             const unsigned int l)
  1171. { }
  1172. void DocumentConverter::processingInstruction(const XMLCh* const target,
  1173.                                               const XMLCh* const data)
  1174. { }
  1175. //#############################################################################
  1176. void DocumentConverter::ProcessNodeAttribute(VXMLElementType elemType,
  1177.                                              int attrType,
  1178.                                              const VXIchar* const value)
  1179. {
  1180.   // This shouldn't ever happen, if it does, we'll just ignore it.
  1181.   if (value == NULL) return;
  1182.   switch (elemType) {
  1183.   case NODE_BLOCK:
  1184.   case NODE_INITIAL:
  1185.     if (attrType == ATTRIBUTE_NAME)
  1186.       namedControlItems.push_back(value);
  1187.   case NODE_FIELD:
  1188.   case NODE_OBJECT:
  1189.   case NODE_RECORD:
  1190.   case NODE_TRANSFER:
  1191.     // We associate a hidden interal name with this element.
  1192.     if (attrType == ATTRIBUTE_NAME) attrType = ATTRIBUTE__ITEMNAME;
  1193.     break;
  1194.   case NODE_CLEAR:
  1195.     if (attrType == ATTRIBUTE_NAMELIST && !value[0])
  1196.       ParseException(L"the namelist attribute on clear cannot be empty");
  1197.     break;
  1198.   case NODE_FORM:
  1199.   case NODE_MENU:
  1200.     // We associate a hidden interal name with this element.
  1201.     if (attrType == ATTRIBUTE_ID) attrType = ATTRIBUTE__ITEMNAME;
  1202.     break;
  1203.   case NODE_FILLED:
  1204.     if (doc->GetParentType() != NODE_FORM)
  1205.       ParseException(L"attributes valid on filled only at form level");
  1206.     if (attrType == ATTRIBUTE_NAMELIST && !value[0])
  1207.       ParseException(L"the namelist attribute on filled cannot be empty");
  1208.     if (attrType == ATTRIBUTE_NAMELIST){
  1209.       if ( vxistring( value ) != L"any")
  1210.         filledNameList.push_back(value);
  1211.     }
  1212.     break;
  1213.   case NODE_GRAMMAR:
  1214.     if (doc->GetParentType() != NODE_FORM && attrType == ATTRIBUTE_SCOPE)
  1215.       ParseException(L"the scope attribute is valid only on grammars at form "
  1216.                      L"level");
  1217.     break;
  1218.   case NODE_SUBDIALOG:
  1219.     // We associate a hidden interal name with this element.
  1220.     if (attrType == ATTRIBUTE_NAME) attrType = ATTRIBUTE__ITEMNAME;
  1221.     // modal was dropped in 2.0.
  1222.     if (attrType == ATTRIBUTE_MODAL && version != 1.0f)
  1223.       ParseException(L"the modal attribute on subdialog was dropped after "
  1224.                      L"1.0");
  1225.     break;
  1226.   case NODE_VALUE:
  1227.     if (attrType == ATTRIBUTE_EXPR) break;
  1228.     if (doc->GetParentType() == NODE_LOG)
  1229.       ParseException(L"only the expr attribute is valid on value elements "
  1230.                      L"within a log element");
  1231.     break;
  1232.   case NODE_VXML:
  1233.     // version is processed elsewhere and may be ignored.
  1234.     if (attrType == ATTRIBUTE_VERSION) return;
  1235.     if (attrType == PRIV_ATTRIB_SCHEMALOC) return;
  1236.     break;
  1237.   default:
  1238.     break;
  1239.   }
  1240.   // (4.3) Add the attribute to the element.
  1241.   if (attrType > PRIV_ATTRIB_RangeStart)
  1242.     ParseException(L"internal error during attribute processing");
  1243.   doc->AddAttribute(VXMLAttributeType(attrType), value);
  1244. }
  1245. void DocumentConverter::ProcessNodeFinal(VXMLElementType elemType)
  1246. {
  1247.   // Convert attributes.
  1248.   vxistring attr;
  1249.   switch (elemType) {
  1250.   case NODE_BLOCK:
  1251.   case NODE_FIELD:
  1252.   case NODE_FORM:
  1253.   case NODE_INITIAL:
  1254.   case NODE_MENU:
  1255.   case NODE_OBJECT:
  1256.   case NODE_RECORD:
  1257.   case NODE_SUBDIALOG:
  1258.   case NODE_TRANSFER:
  1259.     // Name the 'unnamed' elements as neccessary.
  1260.     if (!doc->GetAttribute(ATTRIBUTE__ITEMNAME, attr)) {
  1261.       vxistring variable;
  1262.       VXMLDocumentModel::CreateHiddenVariable(variable);
  1263.       doc->AddAttribute(ATTRIBUTE__ITEMNAME, variable);
  1264.     }
  1265.     break;
  1266.   case NODE_FILLED:
  1267.     if (!doc->GetAttribute(ATTRIBUTE_MODE, attr))
  1268.       doc->AddAttribute(ATTRIBUTE_MODE, L"all");
  1269.     break;
  1270.   case NODE_VXML:
  1271.   case DEFAULTS_ROOT:
  1272.     VXMLDocumentModel::CreateHiddenVariable(attr);
  1273.     doc->AddAttribute(ATTRIBUTE__ITEMNAME, attr);
  1274.     break;
  1275.   default:
  1276.     break;
  1277.   }
  1278.   // Generate DTMF sequences for <choice> elements in <menu> if necessary.
  1279.   if (elemType == NODE_MENU) {
  1280.     if (doc->GetAttribute(ATTRIBUTE_DTMF, attr) && attr == L"true")
  1281.       choiceNumber = 1;
  1282.     else
  1283.       choiceNumber = 0;
  1284.   }
  1285.   if (elemType == NODE_CHOICE && choiceNumber > 0 && choiceNumber < 10 &&
  1286.       !doc->GetAttribute(ATTRIBUTE_DTMF, attr))
  1287.   {
  1288.     std::basic_stringstream<VXIchar> countString;
  1289.     countString << choiceNumber;
  1290.     doc->AddAttribute(ATTRIBUTE_DTMF, countString.str());
  1291.     ++choiceNumber;
  1292.   }
  1293.   if (elemType == NODE_PROMPT) {
  1294.     vxistring base, lang;
  1295.     doc->GetAttribute(ATTRIBUTE_BASE, base);
  1296.     doc->GetAttribute(ATTRIBUTE_XMLLANG, lang);
  1297.     // remove all irrelavant part from the base uri
  1298.     TrimUriBase(base);
  1299.     
  1300.     doc->AddContent(L"<?xml version='1.0'?><speak version='1.0' xmlns="
  1301.                     L"'http://www.w3.org/2001/10/synthesis' xml:base='", 96);
  1302.     doc->AddContent(base.c_str(), base.length());
  1303.     doc->AddContent(L"' xml:lang='", 12);
  1304.     doc->AddContent(lang.c_str(), lang.length());
  1305.     doc->AddContent(L"'>", 2);
  1306.     doc->MarkSSMLHeader();
  1307.   }
  1308. }
  1309. void DocumentConverter::CopyElementStart(const XMLCh* const localname,
  1310.                                          const Attributes &attrs)
  1311. {
  1312.   ++copyDepth;
  1313.   doc->AddContent(L"<", 1);
  1314.   XMLChToVXIchar data(localname);
  1315.   doc->AddContent(data.c_str(), wcslen(data.c_str()));
  1316.   for (unsigned int index = 0; index < attrs.getLength(); ++index) {
  1317.     doc->AddContent(L" ", 1);
  1318.     // Determine the attribute name
  1319.     if (Compare(attrs.getQName(index), L"xml:lang"))
  1320.       doc->AddContent(L"xml:lang", 8); 
  1321.     else {
  1322.       XMLChToVXIchar name(attrs.getLocalName(index));
  1323.       doc->AddContent(name.c_str(), wcslen(name.c_str()));
  1324.     }
  1325.       
  1326.     doc->AddContent(L"="", 2);
  1327.     XMLChToVXIchar value(attrs.getValue(index));
  1328.     unsigned int start = 0;
  1329.     unsigned int pos = 0;
  1330.     VXIchar c;
  1331.     // We need to escape three characters: (<,>,&) -> (&lt;,&gt;,&amp;)
  1332.     do {
  1333.       c = *(value.c_str() + pos);
  1334.       if (c == L'<') {
  1335.         if (start != pos)
  1336.            doc->AddContent(value.c_str() + start, pos - start);
  1337.         start = pos + 1;
  1338.         doc->AddContent(L"&lt;", 4);
  1339.       }
  1340.       else if (c == L'>') {
  1341.         if (start != pos)
  1342.            doc->AddContent(value.c_str() + start, pos - start);
  1343.         start = pos + 1;
  1344.         doc->AddContent(L"&gt;", 4);
  1345.       }
  1346.       else if (c == L'&') {
  1347.         if (start != pos)
  1348.            doc->AddContent(value.c_str() + start, pos - start);
  1349.         start = pos + 1;
  1350.         doc->AddContent(L"&amp;", 5);
  1351.       }
  1352.       
  1353.       ++pos;
  1354.     } while (c != L'');
  1355.     if (pos > start)
  1356.       doc->AddContent(value.c_str() + start, pos - start - 1); // -1 to kill off final 
  1357.     doc->AddContent(L""", 1);
  1358.   }
  1359.   doc->AddContent(L">", 1);
  1360. }
  1361. void DocumentConverter::CopyElementClose(const XMLCh* const localname)
  1362. {
  1363.   doc->AddContent(L"</", 2);
  1364.   XMLChToVXIchar data(localname);
  1365.   doc->AddContent(data.c_str(), wcslen(data.c_str()));
  1366.   doc->AddContent(L">", 1);
  1367.   --copyDepth;
  1368. }
  1369. void DocumentConverter::StartImplicitPrompt()
  1370. {
  1371.   implicitPrompt = true;
  1372.   doc->StartElement(NODE_PROMPT, documentLevel);
  1373.   
  1374.   vxistring tmpBaseUri = baseUri;
  1375.   TrimUriBase(tmpBaseUri);
  1376.   doc->AddContent(L"<?xml version='1.0'?><speak version='1.0' xmlns="
  1377.                   L"'http://www.w3.org/2001/10/synthesis' xml:base='", 96);
  1378.   doc->AddContent(tmpBaseUri.c_str(), tmpBaseUri.length());
  1379.   doc->AddContent(L"' xml:lang='", 12);
  1380.   doc->AddContent(documentLang.c_str(), documentLang.length());
  1381.   doc->AddContent(L"'>", 2);
  1382.   doc->MarkSSMLHeader();
  1383.   
  1384.   copyDepth = 0;
  1385. }
  1386. void DocumentConverter::EndImplicitPrompt()
  1387. {
  1388.   doc->AddContent(L"</speak>", 8);
  1389.   doc->EndElement();
  1390.   implicitPrompt = false;
  1391.   foreachImplicit = false;
  1392. }
  1393. void DocumentConverter::comment( const XMLCh* const chars, const unsigned int length )
  1394. {
  1395. }
  1396. void DocumentConverter::startCDATA ()
  1397. {
  1398.   inCDATA = true;
  1399. }
  1400. void DocumentConverter::endCDATA ()
  1401. {
  1402.   inCDATA = false;
  1403. }
  1404. void DocumentConverter::startEntity (const XMLCh* const name)
  1405. {
  1406. }
  1407. void DocumentConverter::endEntity (const XMLCh* const name)
  1408. {
  1409. }
  1410. void DocumentConverter::startDTD
  1411. (
  1412.     const   XMLCh* const    name
  1413.     , const   XMLCh* const    publicId
  1414.     , const   XMLCh* const    systemId
  1415. )
  1416. {
  1417. }
  1418. void DocumentConverter::endDTD ()
  1419. {
  1420. }