W3C

VoiceXML 2.1
Implementation Report

Version: Feb 16, 2007

Editors:

Matt Oshry, Tellme Networks

Table of Contents

1. Introduction

The VoiceXML 2.1 Specification entered the Candidate Recommendation period on 13 June 2005. In order to clarify some issues regarding the content model of the <foreach> element, VoiceXML 2.1 was re-published as a Working Draft on 15 September 2006.

The planned date for entering Proposed Recommendation is 25 April 2007. Preparation of an Implementation Report is a key criteria for moving beyond the Candidate Recommendation. This document describes the requirements for the Implementation Report and the process that the Voice Browser Working Group will follow in preparing the report.

1.1 Implementation Report Objectives

  1. Must verify that the specification is implementable.
  2. Must demonstrate interoperability of implementations of the specification.

1.2 Implementation Report Non-objectives

  1. The IR does not attempt conformance testing of implementations.

2. Work During the Candidate Recommendation Period

During the CR period, the Working Group will carry out the following activities:

  1. Clarification and improvement of the exposition of the specification.
  2. Disposing of Comments that were communicated to the WG during the CR period.
  3. Preparation of an Implementation Report meeting the criteria outlined in this document.

3. Participating in Implementation Report

You are invited to contribute to the assessment of the W3C VoiceXML 2.1 Specification by participating in the Implementation Report process.

4. Entrance Criteria to PR

The VBWG established the following entrance criteria to Proposed Recommendation in the Request for CR:

  1. Sufficient reports of implementation experience have been gathered to demonstrate that VoiceXML processors based on the specification are implementable and have compatible behavior.
  2. Specific Implementation Report requirements (outlined below) have been met.
  3. Formal responses to all comments received by the Working Group.

5. Implementation Report Requirements

5.1 Detailed requirements for Implementation Report

  1. Testimonials from implementers will be included in the IR when provided to document the utility and implementability of the specification.
  2. IR must cover all specified features in the specification. For each feature the IR should indicate:
  3. Feature status is a factor in test coverage in the report:

5.2 VoiceXML Processor Coverage

Require implementations and testimonial statements for at least two VoiceXML User Agents supporting both ASR and DTMF.

5.3 Notes on Testing Speech Recognition

  1. Each test contains zero or one input and corresponding outputs. To completely perform a test the input requested by the prompt, if any, must be used.
  2. A test report must indicate the outcome of each test. Possible outcomes are "pass", "fail" or "not-impl". "Pass" requires output of the VoiceXML processor that is equivalent to the output specified in the respective test file for all inputs in this test file. A report must document the way test output was verified. "Not-impl" means the VoiceXML processor has not implemented the specific feature required by a test.
  3. A test report may contain an additional comment for each test. If a test fails a comment should be added (see also Detailed requirements for Implementation Report).
  4. Every attempt has been made to keep the tests language-neutral through the use of the Test API described in Appendix A. When a test uses hard-coded prompts and/or hard-coded grammars, however, US English is used.
  5. Some tests contain notes that should be read before executing them. These notes can be found in comments contained within the main test document and preceding the test code.

5.4 Out of Scope

  1. IR will not cover VoiceXML processor recognition performance.
  2. IR will not cover Semantic Interpretation of grammars.

6. Systems

IBM VoiceXML 2.1 Implementation Report

Executive Summary

IBM is pleased to submit the VoiceXML 2.1 Implementation Report, and provide support for the new features provided in the Candidate Recommendation. As a contributor to the recommendation, IBM continues its leadership in support of open standards in the speech community. By supporting the recommendation in its suite of speech products and its flagship, IBM Websphere Voice Server, IBM demonstrates it commitment to the speech market and VoiceXML. Recognizing the significant new features of the recommendation, IBM is pleased with continual growth of the speech standard and remains committed to its success by ongoing participation with future proposals and support for the upcoming 3.0 recommendation.

VoiceXML 2.1 - Loquendo VoxNauta Implementation Report

Executive Summary

As a leading player in the speech technologies and voice platforms market, Loquendo believes that VoiceXML 2.1 Candidate Recommendation complements VoiceXML 2.0 and increases its scope by adding eight extremely useful features to VoiceXML 2.0. As a matter of fact, two of these features were originally proposed by Loquendo. VoiceXML 2.1 will further promote the creation of efficient and powerful speech applications and it will boost the speech market, by enabling service providers, content creators, operators and voice portals to deliver a much richer user experience. Loquendo is delighted to contribute by submitting the VoiceXML 2.1 Implementation Report, which further demonstrates the effectiveness of the VoiceXML 2.0 recommendation. Loquendo will continue to strongly support the activities of the W3C Voice Browser and Multimodal Interaction Working Groups and of the VoiceXML Forum, as part of its commitment to actively contributing to the evolution of their respective specifications.

SandCherry VoiceXML 2.1 Implementation Report

Executive Summary

SandCherry supports the VoiceXML 2.1 Last Call Working Draft and is pleased to submit our VoiceXML 2.1 Implementation Report. SandCherry is committed to furthering voice standards, and is an active member in the W3C Voice Browser Working Group and the VoiceXML Forum. SandCherry has implemented the VoiceXML 2.1 specification in the SandCherry Voice Platform 3.2, and will continue to embrace and promote the enhanced capabilities and interoperability that are achieved by the efforts of the W3C Voice Browser Working Group.

Tellme Networks

Executive Summary

VoiceXML 2.0 is currently in use by thousands of voice applications, automating millions of phone calls every week. The enhancements specified in VoiceXML 2.1 make it even easier for developers to provide end-users with a rich and engaging voice application experience. The Implementation Report test suite demonstrates the maturity of VoiceXML 2.1 and further strengthens the significance of VoiceXML within the IVR industry. The momentum of VoiceXML remains high due to the need for standards-based voice technology in the enterprise. Tellme is proud to be part of the W3C-led effort to ensure the standard continues to be implemented in real world scenarios. It is also a great privilege to meet the goals of our clients by making VoiceXML technology a centerpiece of their customer service operations.

Vocalocity VXI 3.4

Executive Summary

Vocalocity enthusiastically supports the VoiceXML 2.1 Candidate Recommendation and is pleased to submit our VoiceXML Implementation Report. VoiceXML 2.1 adds a number of useful and commonly implemented extensions to VoiceXML 2.0. It is a significant step towards true application portability and standardization. As an active, contributing member of the W3C Voice Browser Work Group and of the VoiceXML Forum, Vocalocity is committed to raising visibility of the compelling advantages in cost, quality, and time-to-market that VoiceXML and related standards offer. We believe VoiceXML 2.1 further promotes these advantages.

VoiceGenie 7 Media Platform

Executive Summary

VoiceGenie is delighted that the VoiceXML 2.1 specification is approaching recommendation status. The VoiceGenie 7 Media Platform, which provides undisputed performance and reliability leadership, already delivers complete support for VoiceXML 2.1. VoiceXML 2.1 extends the VoiceXML 2.0 standard with industry-proven features already in common use, and thus further advances the standardization and interoperability of the VoiceXML specification. Beyond this, VoiceXML 2.1 adds support for new development models, and exciting capabilities supporting the construction of richer and more robust user interfaces using the acknowledged standard for implementing next-generation touch-tone and speech applications. As a participant in the W3C Voice Browser and Multimodal Interaction Working Groups, and as a board member of the VoiceXML Forum, VoiceGenie remains strongly dedicated to helping telecomm service providers and enterprises use open standards to rapidly and cost effectively deploy next generation services and enhanced contact center solutions.

Voxpilot Open Media Platform

Executive Summary

Voxpilot Ltd has implemented the W3C VoiceXML 2.1 specification in its Voxpilot Open Media Platform 2.5. This platform includes rich support for speech processing media resources through MRCP in addition to a variety of IP telephony, PSTN, and video protocols. Voxpilot's experience with VoiceXML 2.1 has been very positive. VoiceXML 2.1 is a boon for interoperability and flexibility since it standardises many powerful capabilities that have proven indispensible in production deployments since the release of VoiceXML 2.0.

7. Test Results

The following table lists the assertions that were gleaned from the VoiceXML 2.1 Specification. The ID column uniquely identifies the assertion and links to a corresponding test. The Spec column identifies the section of the specification from which the assertion was derived. The Req column indicates whether or not the spec requires the platform to implement the feature described by the assertion. The Auto column indicates whether or not the associated test can be automated or requires manual execution. The Abstract column describes the assertion. The Results column tabulates the results submitted by the VoiceXML platform contributors enumerated in Section 6, Systems.

IDSpecReqAutoAbstractResults
 PassFailN/I
1 [sec-grammar_expr] Yes. Yes. Grammar element may include a srcexpr attribute to dynamically generate grammar URL 7 0 0
2 [sec-grammar_expr] Yes. Yes. If both src and srcexpr are included, error.badfetch is thrown 7 0 0
3 [sec-grammar_expr] Yes. Yes. If both srcexpr and inline grammar are included, error.badfetch is thrown 7 0 0
4 [sec-grammar_expr] Yes. Yes. If none of src, srcexpr nor inline grammar are included, error.badfetch is thrown 7 0 0
5 [sec-grammar_expr] Yes. Yes. srcexpr must be evaluated every time the grammar element needs to be executed 7 0 0
7 [sec-grammar_expr] Yes. Yes. If srcexpr cannot be evaluated, an error.semantic is thrown 7 0 0
9 [sec-script_expr] Yes. Yes. Script element may include an srcexpr attribute to dynamically generate script URL 7 0 0
10 [sec-script_expr] Yes. Yes. If both src and srcexpr are included, error.badfetch is thrown 7 0 0
11 [sec-script_expr] Yes. Yes. If none of src, srcexpr nor inline script are included, error.badfetch is thrown 7 0 0
12 [sec-script_expr] Yes. Yes. srcexpr must be evaluated every time the script element needs to be executed 7 0 0
13 [sec-script_expr] Yes. Yes. If both srcexpr and inline script are included, error.badfetch is thrown 7 0 0
14 [sec-script_expr] Yes. Yes. If both src and inline script are included, error.badfetch is thrown 7 0 0
15 [sec-script_expr] Yes. Yes. If srcexpr cannot be evaluated, an error.semantic is thrown 7 0 0
16 [sec-mark] Yes. Yes. When a <mark> is executed during the processing of a form item, the interpreter sets shadow variables, the names of which correspond to the properties of the application.lastresult$ object. The value of each shadow variable must be identical to the value of the corresponding application.lastresult$ property. 6 1 0
17 [sec-mark] Yes. Yes. VoiceXML 2.1 extends the <mark> element to support the nameexpr attribute, which is an ECMAScript expression that evaluates to the name of the <mark>. 6 1 0
18 [sec-mark] Yes. Yes. Exactly one of "name" and "nameexpr" must be specified; otherwise, an error.badfetch event is thrown. 7 0 0
19 [sec-mark] Yes. Yes. The markname and marktime properties of the application.lastresult$ object must be set whenever the application.lastresult$ object is assigned and a <mark> has been executed. 6 1 0
20 [sec-mark] Yes. Yes. If no <mark> was executed, the markname and marktime properties of application.lastresult$ are undefined. 6 1 0
21 [sec-data] Yes. Yes. If content is not returned within the specified fetchtimeout, an error.badfetch event is thrown. 7 0 0
22 [sec-data] Yes. Yes. The interpreter fetches the URI specified by the src attribute. 7 0 0
23 [sec-data] No. Yes. If the name attribute is specified, the interpreter exposes the DOM through the ECMAScript variable corresponding to the value of the name attribute. 7 0 0
24 [sec-data] Yes. Yes. If the name attribute is present, and the interpreter doesn't support DOM, the interpreter must throw error.unsupported.data.name. 4 0 3
25 [sec-data] Yes. Yes. The interpreter evaluates the srcexpr attribute as an ECMAScript expression when the <data> element needs to be executed. The result of evaluating the srcexpr attribute is the URI to be fetched. 7 0 0
26 [sec-data] Yes. Yes. If srcexpr cannot be evaluated, error.semantic is thrown. 7 0 0
27 [sec-data] Yes. Yes. Exactly one of "src" or "srcexpr" must be specified; otherwise, an error.badfetch event is thrown. 7 0 0
28 [sec-data] Yes. Yes. Interpreters must support the methods GET and POST when submitting an HTTP request. GET is the default method. 7 0 0
29 [sec-data] Yes. Yes. Specifying a URL that points to a non-existent resources causes the interpreter to throw a catchable error.badfetch event. 7 0 0
30 [sec-data] Yes. Yes. If the name attribute is specified, the interpreter supports the DOM, and the interpreter retrieves an XML document that is not well-formed, the interpreter must throw a catchable error.badfetch. 7 0 0
31 [sec-data] Yes. Yes. The <data> element can occur in executable content or as a child of <form> or <vxml>. 7 0 0
32 [sec-data] Yes. Yes. The <data> element shares the same scoping rules as the <var> element. 7 0 0
33 [sec-data] Yes. Yes. If a <data> element has the same name as a variable already declared in the same scope, the variable is assigned a reference to the DOM exposed by the <data> element. 7 0 0
34 [sec-data] Yes. Yes. If use of the DOM causes a DOMException to be thrown, but the DOMException is not caught by an ECMAScript catch handler, the VoiceXML interpreter throws error.semantic. 7 0 0
35 [sec-data] Yes. Yes. When an ECMAScript variable is submitted to the server its value is first converted into a string before being submitted. 7 0 0
36 [sec-data] Yes. Yes. The default encoding type of the submitted document is "application/x-www-form-urlencoded". 7 0 0
37 [sec-data] Yes. Yes. If the enctype attribute is set to "multipart/form-data", the interpreters must submit the document using that encoding type. 7 0 0
38 [sec-data] Yes. Yes. If the namelist is not specified, no variables are submitted. 6 1 0
39 [sec-data] Yes. Yes. If <data> has a namelist attribute, all and only those variables are submitted. 7 0 0
40 [sec-data] Yes. Yes. If a namelist is supplied, it may contain individual variable references which are submitted with the same qualification used in the namelist. 7 0 0
41 [sec-data] Yes. No. If specified, fetchaudio plays during a long fetch. 7 0 0
42 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the code property of the DOMException object as specified in DOM Level 2 . 6 1 0
43 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the documentElement property of the Document object as specified in DOM Level 2 . 7 0 0
44 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getElementsByTagName method of the Document object as specified in DOM Level 2 . 7 0 0
45 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getElementsByTagNameNS method of the Document object as specified in DOM Level 2 . 7 0 0
46 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getElementById method of the Document object as specified in DOM Level 2 . 7 0 0
47 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the nodeName property of the Node object as specified in DOM Level 2 . 7 0 0
48 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the nodeValue property of the Node object as specified in DOM Level 2 . 7 0 0
49 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the nodeType property of the Node object as specified in DOM Level 2 . 6 0 1
50 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the parentNode property of the Node object as specified in DOM Level 2 . 7 0 0
51 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the childNodes property of the Node object as specified in DOM Level 2 . 7 0 0
52 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the firstChild property of the Node object as specified in DOM Level 2 . 7 0 0
53 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the lastChild property of the Node object as specified in DOM Level 2 . 7 0 0
54 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the previousSibling property of the Node object as specified in DOM Level 2 . 7 0 0
55 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the nextSibling property of the Node object as specified in DOM Level 2 . 7 0 0
56 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the attributes property of the Node object as specified in DOM Level 2 . 7 0 0
57 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the ownerDocument property of the Node object as specified in DOM Level 2 . 7 0 0
58 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the namespaceURI property of the Node object as specified in DOM Level 2 . 7 0 0
59 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the prefix property of the Node object as specified in DOM Level 2 . 7 0 0
60 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the localName property of the Node object as specified in DOM Level 2 . 7 0 0
61 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the hasChildNodes method of the Node object as specified in DOM Level 2 . 7 0 0
63 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the hasAttributes method of the Node object as specified in DOM Level 2 . 7 0 0
64 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the length property of the NodeList object as specified in DOM Level 2 . 7 0 0
65 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the item method of the NodeList object as specified in DOM Level 2 . 7 0 0
66 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the length property of the NamedNodeMap object as specified in DOM Level 2 . 7 0 0
67 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getNamedItem method of the NamedNodeMap object as specified in DOM Level 2 . 7 0 0
68 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the item method of the NamedNodeMap object as specified in DOM Level 2 . 7 0 0
69 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getNamedItemNS method of the NamedNodeMap object as specified in DOM Level 2 . 7 0 0
70 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the data property of the CharacterData object as specified in DOM Level 2 . 7 0 0
71 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the length property of the CharacterData object as specified in DOM Level 2 . 7 0 0
72 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the substringData method of the CharacterData object as specified in DOM Level 2 . 7 0 0
73 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the name property of the Attr object as specified in DOM Level 2 . 7 0 0
74 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the specified property of the Attr object as specified in DOM Level 2 . 7 0 0
75 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the value property of the Attr object as specified in DOM Level 2 . 7 0 0
76 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the ownerElement property of the Attr object as specified in DOM Level 2 . 7 0 0
77 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the tagName property of the Element object as specified in DOM Level 2 . 7 0 0
78 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getAttribute method of the Element object as specified in DOM Level 2 . 7 0 0
79 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getAttributeNode method of the Element object as specified in DOM Level 2 . 7 0 0
80 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getElementsByTagName method of the Element object as specified in DOM Level 2 . 7 0 0
81 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getAttributeNS method of the Element object as specified in DOM Level 2 . 7 0 0
82 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getAttributeNodeNS method of the Element object as specified in DOM Level 2 . 7 0 0
83 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the getElementsByTagNameNS method of the Element object as specified in DOM Level 2 . 7 0 0
84 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the hasAttribute method of the Element object as specified in DOM Level 2 . 7 0 0
85 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the hasAttributeNS method of the Element object as specified in DOM Level 2 . 7 0 0
86 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the target property of the ProcessingInstruction object as specified in DOM Level 2 . 7 0 0
87 [sec-data-dom] No. Yes. The DOM exposed by the <data> tag must support the data property of the ProcessingInstruction object as specified in DOM Level 2 . 7 0 0
88 [sec-foreach] Yes. Yes. The <foreach> element can occur as a child of a <prompt> element. 7 0 0
89 [sec-foreach] Yes. Yes. The array attribute must be an ECMAScript expression that evaluates to an array; otherwise an error.semantic is thrown. 7 0 0
90 [sec-foreach] Yes. Yes. The item variable stores each array item upon iteration of the loop. A new variable will be declared if it is not already defined within the parent's scope. 7 0 0
92 [sec-reco_reco] Yes. No. When the "recordutterance" property is set to true, the "recording" shadow variable of an input item (e.g. field) stores a reference to the recording when audio is collected from the user. Utterance recordings can be played back using the expr attribute of the audio element. 7 0 0
94 [sec-reco_reco] Yes. Yes. when property "recordutterance" is set to true, an input item has been filled and has its shadow variables assigned, "recordingsize" must be set in the shadow variable for that input item variable 7 0 0
95 [sec-reco_reco] Yes. Yes. When the shadow variable "recordingsize" is set, it must contain the size of the recorded audio in bytes 7 0 0
96 [sec-reco_reco] Yes. Yes. when property "recordutterance" is set to true, an input item has been filled and has its shadow variables assigned, "recordingduration" must be set in the shadow variable for that input item variable 7 0 0
97 [sec-reco_reco] Yes. Yes. When the shadow variable "recordingduration" is set, it must contain the duration of the recorded audio in milliseconds 7 0 0
98 [sec-reco_reco] Yes. Yes. application.lastresult$ must have the shadow variable "recording" set to the same value as the form item shadow variable 7 0 0
99 [sec-reco_reco] Yes. Yes. application.lastresult$ must have the shadow variable "recordingsize" set to the same value as the form item shadow variable 7 0 0
100 [sec-reco_reco] Yes. Yes. application.lastresult$ must have the shadow variable "recordingduration" set to the same value as the form item shadow variable 7 0 0
101 [sec-reco_reco] Yes. Yes. In the case of <link> and <menu>, the interpreter only sets the application.lastresult$ properties. 7 0 0
102 [sec-reco_reco] No. Yes. <record> may record user utterances while attempting recognition 2 0 5
103 [sec-reco_reco] Yes. Yes. enctype must be "multipart/form-data" when sending the recording on the namelist 7 0 0
104 [sec-reco_reco] Yes. Yes. type must be "POST" when sending the recording on the namelist 7 0 0
105 [sec-reco_reco-type] Yes. Yes. set the media format of recorded audio during recognition using the recordutterancetype property 7 0 0
106 [sec-reco_reco-type] Yes. No. The platform must support the audio file format 'audio/basic' as specified in Appendix E of the VoiceXML 2.0 specification. 7 0 0
108 [sec-disconnect] Yes. No. The default for namelist of a disconnect element is to return no variables; this means the interpreter context will receive an empty ECMAScript object. 7 0 0
109 [sec-disconnect] Yes. No. The variables specified in the namelist attribute of the disconnect element are returned to the interpreter context. (The precise mechanism by which these variables are made available to the interpreter context is platform specific.) 7 0 0
111 [sec-disconnect] Yes. Yes. If the namelist attribute of a disconnect element contains one or more undeclared variables, the interpreter throws error.semantic. 7 0 0
112 [sec-transfer] No. No. If type is specified and bridge not specified, type takes precedence over the default value of the bridge attribute. 6 0 1
113 [sec-transfer] No. No. If type="consultation" an outgoing call is attempted to the specified destination. 4 0 3
114 [sec-transfer] No. No. If type="consultation" and the caller disconnects while the outgoing call is attempted, connection.disconnect.hangup is thrown. 4 0 3
115 [sec-transfer] No. No. If type="consultation" and the caller cancels the transfer attempt while the outgoing call is attempted by using a DTMF command, the transfer form item variable is filled with near_end_disconnect. 4 0 3
116 [sec-transfer] No. No. If type="consultation" and the caller cancels the transfer attempt while the outgoing call is attempted by using a voice command, the transfer form item variable is filled with near_end_disconnect. 1 0 6
117 [sec-transfer] No. No. If type="consultation" and an outgoing call is attempted to a specified destination that is busy, the transfer form item variable will be filled with busy. 4 0 3
118 [sec-transfer] No. No. If type="consultation" and an outgoing call is attempted to a specified destination but an intermediate network is busy, the transfer form item variable will be filled with network_busy. 4 0 3
119 [sec-transfer] No. No. If type="consultation" and the outgoing call is not answered within the time specified by connecttimeout, the transfer attempt terminates, and the transfer form item variable is filled with noanswer. 4 0 3
120 [sec-transfer] No. No. If type="consultation" and the transfer attempt ends but the reason is not known, the transfer form item variable is filled with unknown. 4 0 3
121 [sec-transfer] No. No. If type="consultation" and the outgoing call is answered, the platform disconnects from the conversation, connection.disconnect.transfer is thrown, the transfer form item variable is undefined, and the calling and called parties remain connected. 4 0 3
122 [sec-transfer] No. Yes. If type="consultation" and the platform does not support consultation transfer, error.unsupported.transfer.consultation is thrown. 4 0 3
123 [sec-transfer] No. No. The transfer element's "connecttimeout" value is not honored unless either "bridge" is set to true or "type" is set to "bridge" or "consultation". 5 0 2
124 [sec-transfer] No. Yes. Exactly one of "bridge" or "type" may be specified; otherwise an error.badfetch event is thrown. 6 0 1
125 [sec-transfer] No. No. The "maxtime" value will only be honored if the "bridge" attribute is set to "true", or the "type" is set to "bridge" 5 0 2
126 [sec-transfer] No. No. When "transferaudio" is specified, and the audio file has not completed playing, the audio will cease upon a far-end connection. 3 0 4
127 [sec-reco_reco] Yes. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after filling a <field> using speech input 7 0 0
128 [sec-reco_reco] Yes. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after filling a <field> using speech input 7 0 0
129 [sec-reco_reco] Yes. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after filling a <field> using speech input 7 0 0
130 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after terminating a <record> with speech 5 0 2
131 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after termininating a <record> using speech 4 0 3
132 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after terminating a <record> using speech 4 0 3
133 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after terminating a <transfer> using speech 3 0 4
134 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after terminating a <transfer> using speech 3 0 4
135 [sec-reco_reco] No. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after terminating a <transfer> using speech 3 0 4
136 [sec-reco_reco] Yes. Yes. Recorded utterance may be posted using <submit> 7 0 0
137 [sec-reco_reco] Yes. Yes. Recorded utterance may be posted using <subdialog> 7 0 0
138 [sec-reco_reco] Yes. Yes. Recorded utterance may be posted using <data> 7 0 0
141 [sec-mark] Yes. Yes. If nameexpr cannot be evaluated, an error.semantic event is thrown. 7 0 0
142 [sec-data] Yes. Yes. If the datafetchhint property is set to "safe", content of that type is never fetched until it is needed 7 0 0
143 [sec-data] Yes. No. A cached data resource must be reloaded if the datamaxage property for its type is less than its current age. 7 0 0
144 [sec-data] Yes. No. A cached data resource must be reloaded if the datamaxstale property for its type is less than its current staleness. 7 0 0
145 [sec-disconnect] Yes. No. The <disconnect> namelist and the <exit> namelist are processed independently. If the interpreter executes both a <disconnect> namelist and an <exit> namelist, both sets of variables are available to the interpreter context. (The precise mechanism by which these variables are made available to the interpreter context is platform specific.) 7 0 0
146 [sec-reco_reco] No. Yes. <transfer> may record user utterances while attempting recognition 1 0 6
147 [sec-reco_reco-type] Yes. No. platform must support audio file format audio/x-alaw-basic from VXML 2.0 7 0 0
148 [sec-reco_reco-type] Yes. No. platform must support audio file format audio/x-wav from VXML 2.0 7 0 0
149 [sec-foreach] Yes. Yes. The array attribute must evaluate to an ECMAScript Array; otherwise, an error.semantic event is thrown. (Tests various data types from foreach appearing within a block.) 3 0 4
150 [sec-foreach] Yes. Yes. The array attribute must evaluate to an ECMAScript Array; otherwise, an error.semantic event is thrown. (Tests various data types from foreach appearing within a prompt in a block.) 3 0 4
151 [sec-foreach] Yes. Yes. The array attribute must evaluate to an ECMAScript Array; otherwise, an error.semantic event is thrown. (Tests various data types from foreach appearing within a prompt in a field.) 3 0 4
152 [sec-foreach] Yes. Yes. The assigned value could be undefined for a sparse array. (Tests sparse array handling.) 3 0 4
153 [sec-foreach] Yes. Yes. The foreach element operates on a shallow copy of the array specified by the array attribute. 3 0 4
154 [sec-foreach] Yes. Yes. A shallow copy of the corresponding array element is assigned to the item variable. 3 0 4
155 [sec-foreach] Yes. Yes. Executable content is not allowed within foreach contained within a prompt. (This tests a prompt inside block.) 3 0 4
156 [sec-foreach] Yes. Yes. Executable content is not allowed within foreach contained within a prompt (this test includes the prompt in a field). 3 0 4

Appendices

Appendix A - Test assertion XML API definition

This appendix describes a framework for authoring VoiceXML tests. The framework abstracts the markup used to define interactions with the user, allowing vendors to use their own test infrastructure by applying an XSLT transformation to the test source. Modifications to the test infrastructure should require a change to the XSLT template only followed by re-transformation of the test source.

The test API described in this document uses the complete schema specified in the VoiceXML 2.1 specification with the addition of a set of twelve elements in their own namespace (http://www.w3.org/2002/vxml-conformance) that greatly simplify the process of authoring tests and abstract the implementation details of the testing infrastructure. The elements are divided into four categories:

A.1 Final status indicators

These elements are used to signal the completion of a test and, as such, SHOULD terminate test execution. They can appear anywhere that VXML executable.content is legal (e.g. block, event handlers, filled). Final status tags may be rendered in different ways. The most likely candidates are an exit with a return value, a submit, or a log followed by an exit.

pass
Indicates successful test completion. A transformation yielding a manually executed test could play the audio "pass" to the user running the test. A transformation yielding an automated test could play a sequence of DTMF tones indicating success to the test driver and submit a pass result to a reporting server. The element is allowed anywhere executable content is allowed in a VoiceXML document.
fail
Indicates unexpected results from the interpreter. A transformation yielding a manually executed test could play the audio "fail" to the user running the test. A transformation yielding an automated test could play a sequence of DTMF tones indicating failure to the test driver and submit a fail result to a reporting server. The element is allowed anywhere executable content is allowed in a VoiceXML document. The element supports the following optional attributes:
reasonOptional. A simple text string explaining the probable cause for failure.
exprOptional. An ECMAScript expression that evaluates to a string at run-time providing information about the probable cause for failure.

A.2 Intermediate status indicators

These elements may be used to signal transitions in a multi-part test. They may appear anywhere that VXML executable.content is legal (e.g. block, event handlers, filled). Intermediate status tags may be rendered in different ways. The most likely candidates are using log or prompt or ignoring the element entirely.

comment
Specifies English language content that describes intermediate test results. The element is allowed anywhere executable content is allowed in a VoiceXML document and may contain plain text and zero or more value elements in any order. Note the following caveats with respect to the comment element:

A.3 Input operations

These tags may be used at any point that input is required (e.g. initial, field, record, transfer) or on input-related event handlers (e.g. noinput and nomatch). These elements may be rendered in several different ways A few possibilities include playing DTMF prompts to control an external test driver, using properties to control the recognition result, or using platform-specific test features.

If these tags define prompts, they MUST provide separate prompts with counts 1 through 3.

speech
Sends a predefined utterance to a user or test driver. The response from the user or driver is expected to be an utterance matching a grammar. The speech element is allowed anywhere audio can be played in a VoiceXML document. Supports the following required attribute:
valueRequired. Uniquely identifies the utterance.
dtmf
Sends a request to a user or test driver to type or playback one or more DTMF digits. Supports the following required attribute:
valueRequired. The sequence of DTMF digits.
recording
Sends a request to a user or test driver to speak or playback audio to be recorded by the interpreter via execution of the record element. Supports the following required attribute:
valueRequired. A unique identifier that specifies the desired recording from the user or test driver. The possible values for the attribute include the following:
nonspeechThe driver must recite a recording at least 5 seconds in length that does not contain one of the predefined utterance. An example is the popular nursery rhyme "Little Miss Muffett".
alpha, bravo, ...The driver must recite a recording containing the predefined utterance specified via the value attribute.
noinput
Requests that the test driver or caller play or say nothing in order to cause the interpreter to generate a noinput event. The optional duration attribute allows you to specify how many seconds the driver or caller should remain silent.
nomatch
Requests that the test driver or caller play or say something unrecognizable (out of grammar) in order to cause the interpreter to generate a nomatch event. The optional duration attribute allows you to specify how many seconds the driver or caller should provide the input.
hangup
Requests that the test driver or caller hang up. in order to cause the interpreter to generate a session.disconnect.hangup event.

A.4 Grammar definitions

Grammars are expected to return one of three classes of results:

The utterance attribute specifies which audio input is expected to match this grammar. Grammars may map to a simple result by specifying an interpretation via the interp attribute. More complex results may be returned using the key element.

NOTE: A grammar element may NOT specify an interp attribute and one or more key elements simultaneously.

grammar
Specifies a grammar to be used when recognition is required by the test. Allowed anywhere a grammar element is allowed in a VoiceXML document. Supports the following attributes:
utteranceRequired. Specifies the unique identifier of an utterance.
interpOptional. Specifies a simple result string returned by the interpreter when the utterance is matched.
key
Supports the following attributes:
nameRequired. The string identifying the key. The string should be a valid ECMAScript variable name. Keys are used to express structured recognition results. Each key may contain either PCDATA or one or more key elements. Multiple keys with the same name may appear; this allows arrays to be built for complex results such as the 'pizza' example.
valueOptional. A string representing the value the key. If the value is omitted, the key element element MUST contain one or more key elements.
phrase
Maps to a simple text string. The element is intended for phrase-based grammars such as VoiceXML choice and option elements. The element may also be used to construct SSML prompts. Supports the following required attribute:
utterance Specifies the unique identifier of an utterance.

A.5 Grammar selection

The stylesheet author is required to select eight utterances, represented in the test framework by the first eight symbols in the International Radio Alphabet. In practice, the actual values will be specific to each language and may differ for each recognizer. These words or phrases should be carefully chosen such that speech utterances match corresponding grammars with high confidence scores. For example, setting the value attribute of the speech element to "alpha" should reliably match (i.e. produce a confidence of at least 0.5) a grammar that is rendered when setting the value of the utterance attribute of the grammar element to "alpha". Likewise, speech utterances should either produce low scores or not match grammars corresponding to different utterances. For example, setting the value attribute of the speech element to "alpha" should reliably return a confidence of less that 0.5 when match against the grammar produced by setting the utterance attribute of the grammar element to "bravo".

It should be reiterated that testing recognition accuracy is not a goal of this suite. Hence, the stylesheet author may freely select any eight phrases.

A.6 Predefined utterances

As defined in the Test API markup, the speech element should be used to request a predefined utterance from the user or test driver. In the document type definition (DTD) below, the 'ir-commands' entity defines the set of valid unique identifiers. The list is easily extended, and the XSLT should be updated to handled additional identifiers when processing the speech, grammar, and phrase elements.

A.7 Document Type Definition

The following DTD succintly declares the Test API markup elements, their attributes, and the legal values for those attributes if applicable.

<!ELEMENT conf:pass EMPTY>   
<!ELEMENT conf:fail EMPTY>
<!ATTLIST conf:fail
   reason CDATA #IMPLIED
   expr CDATA #IMPLIED>

<!ENTITY % ir-utterances 'alpha|bravo|charlie|delta|echo|foxtrot|golf|hotel'>
<!ENTITY % ir-commands '%ir-utterances;|help|cancel|exit|yes'>

<!ELEMENT conf:dtmf EMPTY>
<!ATTLIST conf:dtmf
   value CDATA #REQUIRED
   count CDATA #IMPLIED
   pre_mark CDATA #IMPLIED
   post_mark CDATA #IMPLIED>

<!ELEMENT conf:hangup   EMPTY >
<!ELEMENT conf:noinput   EMPTY >
<!ATTLIST conf:noinput
  duration CDATA #IMPLIED
   count CDATA #IMPLIED
   pre_mark CDATA #IMPLIED
   post_mark CDATA #IMPLIED>

<!ELEMENT conf:nomatch   EMPTY >
<!ATTLIST conf:nomatch 
  duration CDATA #IMPLIED
   count CDATA #IMPLIED
   pre_mark CDATA #IMPLIED
   post_mark CDATA #IMPLIED>

<!ELEMENT conf:recording   EMPTY >
<!ATTLIST conf:recording
   value (%ir-utterances;|nonspeech) #REQUIRED 
   count CDATA #IMPLIED
   pre_mark CDATA #IMPLIED
   post_mark CDATA #IMPLIED>

<!-- Add conf:speech to the menu element decl. -->
<!ELEMENT conf:speech EMPTY>
<!ATTLIST conf:speech
   value (%ir-commands;) #REQUIRED
   count CDATA #IMPLIED
   pre_mark CDATA #IMPLIED
   post_mark CDATA #IMPLIED>

<!ELEMENT conf:grammar (conf:key*)>   
<!ATTLIST conf:grammar 
  utterance (%ir-utterances;) #REQUIRED
  interp CDATA #IMPLIED
>

<!ELEMENT conf:key         (#PCDATA | conf:key)* >
<!ATTLIST conf:key
   name  CDATA  #REQUIRED 
   value  CDATA  #IMPLIED
>

<!ELEMENT conf:phrase      EMPTY >
<!ATTLIST conf:phrase
   utterance   (%ir-commands;) #REQUIRED >


<!ELEMENT conf:comment (#PCDATA | value)*>

<!ENTITY % ir-test-ext "conf:pass | conf:fail | conf:comment ">
<!ENTITY % ir-prompts "conf:speech | conf:dtmf | conf:hangup | 
  conf:noinput | conf:nomatch | conf:recording">


To incorporate the test API into the VoiceXML 2.1 DTD to validate test source against the DTD, perform the following steps:

  1. Add "xmlns:conf CDATA #IMPLIED" to the ATTLIST declaration of the vxml element.
  2. Add the test API ELEMENT and ENTITY declarations listed above to the DTD before the 'executable.content' ENTITY declaration.
  3. Add a reference to the 'ir-test-ext' ENTITY to the 'executable.content' ENTITY declaration.
  4. Add a reference to the 'ir-prompts' ENTITY to the following ELEMENT declarations: menu, field, initial, record, transfer, subdialog, and object.
  5. Add conf:phrase to the choice and option ELEMENT declarations.
  6. Add conf:grammar to the 'input' ENTITY declaration and to the link ELEMENT declaration.

Alternatively, you can transform your test source through the XSLT, and validate the output against the VoiceXML 2.1 DTD.

A.8 Test examples

The following examples illustrate the use of the proposed tags. These examples were written to help validate the stylesheet used to generate the tests. These tests should all pass before the stylesheet is applied to the main body of tests.

Example 1

The following example demonstrates simple usage of the conf:pass element. When the block is executed, the transformation of the conf:pass element will be executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <form>
    <block>
      <conf:pass/>
    </block>
  </form>
</vxml>

Example 2

The following example demonstrates simple usage of the conf:fail element. When the block is executed, the transformation of the conf:fail element will be executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <form>
    <block>
      <conf:fail/>
    </block>
  </form>
</vxml>

Example 3

The following example demonstrates usage of the reason attribute of the conf:fail element. When the block is executed, the transformation of the conf:fail element will be executed. Although some interpreters may ignore the reason attribute, others may output the value in a log element or submit it to a server-side script for further processing.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <form>
    <block>
      <conf:fail reason="roulette"/>
    </block>
  </form>
</vxml>

Example 4

The following example demonstrates usage of the reason attribute of the conf:fail element. When the block is executed, the transformation of the conf:fail element will be executed. Although some interpreters may ignore the expr attribute, others may output the value via a value element contained within a log element. In this example, the expr attribute will evaluate to "pete 3" at runtime.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <form>
    <block>
      <conf:fail expr="'pete ' + (1 + 2)"/>
    </block>
  </form>
</vxml>

Example 5

The following example demonstrates usage of the reason attribute of the conf:comment element. Although some interpreters may ignore the element, others may output the contents via a log element. Still others may output the contents via a prompt element. In this example, the interpreter should output "The value of x is 2" at runtime.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <var name="x" expr="1+1"/>
  <form>
    <block>
      <conf:comment>
        The value of x is <value expr="x"/>. 
      </conf:comment>
      <conf:pass/>
    </block>
  </form>
</vxml>

Example 6

The following example sends a request to the tester to hangup and catches the connection.disconnect.hangup event. If the event is caught, the interpreter executes the transformation of the conf:pass element. If some other event is executed or no event fires and the block is executed, the transformation of the conf:fail is executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch><conf:fail expr="_event"/></catch>

  <form>
    <field name="one">
      <catch event="connection.disconnect.hangup">
        <conf:pass/>
      </catch>
      <catch><conf:fail expr="_event"/></catch>
      <conf:hangup/>
      <conf:grammar utterance="alpha"/>
    </field>

    <block><conf:fail reason="block"/></block>
  </form>
</vxml>

Example 7

The following example sends a request for no input to the tester when field 'one' is executed. If the tester is silent and a noinput event is generated, the noinput handler should execute, and the interpreter should execute the noinput element. If some other event is executed or no event fires and the block is executed, the transformation of the conf:fail is executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <catch><conf:fail reason="catch"/></catch>
  <form>
    <field name="one">
      <noinput><conf:pass/></noinput>
      <catch><conf:fail expr="_event"/></catch>
      <conf:noinput/>
      <conf:grammar utterance="alpha"/>
    </field>

    <block><conf:fail reason="block"/></block>
  </form>
</vxml>

Example 8

The following example sends a request for a nomatch to the tester when field 'one' is executed. If the tester says or plays something out of grammar and a nomatch event is generated, the nomatch handler should execute, and the interpreter should execute the nomatch element. If some other event is executed or no event fires and the block is executed, the transformation of the conf:fail is executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">
  <catch><conf:fail expr="_event"/></catch>
  <form>
    <field name="one">
      <nomatch><conf:pass/></nomatch>
      <catch><conf:fail expr="_event"/></catch>
      <conf:nomatch/>
      <conf:grammar utterance="alpha"/>
    </field>

    <block><conf:fail reason="block"/></block>
  </form>
</vxml>

Example 9

The following example demonstrate the usage of the conf:speech and conf:grammar elements. The conf:speech element is transformed into a series of prompts that correspond to the identifier "alpha". The conf:grammar element is transformed into a simple grammar that corresponds the phrase that matches the alpha utterance mapping. Since the interp attribute of the conf:grammar element is not specified the specific value returned by the interpreter when the grammar is matched cannot be tested. What can be tested is whether or not the form item variable one is defined. The block following the field peforms that check.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch><conf:fail expr="_event"/></catch>

  <form>
    <block>
      <if cond="one != undefined">
        <conf:fail reason="initial check"/>
      </if>
    </block>

    <field name="one">
      <conf:speech value="alpha"/>
      <conf:grammar utterance="alpha"/>
    </field>

    <block>
      <if cond="one != undefined"><conf:pass/></if>
      <conf:fail reason="field assignment"/>
    </block>
  </form>
</vxml>

Example 10

The following example demonstrates usage of the interp attribute of the conf:grammar element. If the tester utters the phrase that corresponds to "alpha", the interpreter recognizes the utterance and fills the form item variable one with the value of the interp attribute. When the block following the field is executed, the if element evaluates to true, and the transformation of the conf:pass element is executed.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch><conf:fail reason="catch"/></catch>

  <form>
    <block>
      <if cond="one != undefined">
        <conf:fail reason="initial check"/>
      </if>
    </block>

    <field name="one">
      <conf:speech value="alpha"/>
      <conf:grammar utterance="alpha" interp="peña"/>
    </field>
    
    <block>
      <if cond="one=='peña'"><conf:pass/></if>
      <conf:fail reason="field assignment"/>
    </block>
  </form>
</vxml>

Example 11

The following example demonstrates usage of the conf:key element to generate a structured result from a grammar. The conf:speech element instructs the tester to speak or play the utterance that corresponds to the identifier alpha. Upon recognition of this utterance, the interpreter should assign a reference to an object to the form item variable 'one'. The object should have properties x and y with the respective values 'valX' and 'valY'.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch><conf:fail expr="_event"/></catch>

  <form>
    <block>
      <if cond="one != undefined">
        <conf:fail reason="initial check"/>
      </if>
    </block>

    <field name="one">
      <conf:speech value="alpha"/>
      <conf:grammar utterance="alpha">
        <conf:key name="x" value="valX"/>
        <conf:key name="y" value="valY"/>
      </conf:grammar>
    </field>

    <block>
      <if cond="typeof one == 'object' 
        &amp;&amp; one.x == 'valX' 
        &amp;&amp; one.y == 'valY'">
        <conf:pass/>
      </if>
      <conf:fail reason="field assignment"/>
    </block>
  </form>

</vxml>

Example 12

The following example demonstrates more complex usage of the conf:key element to generate a structured result from a grammar. The conf:speech element instructs the tester to speak or play the utterance that corresponds to the identifier alpha. Upon recognition of this utterance, the interpreter should assign a reference to an object to the form item variable 'one'. The object should have properties x and y. x is a reference to an object and y is assigned the value 'valY'. The object referred to by x has properties a and b with respective values 'valA' and 'valB'.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch><conf:fail expr="_event"/></catch>

  <form>
    <block>
      <if cond="one != undefined">
        <conf:fail reason="initial check"/>
      </if>
    </block>

    <field name="one">
      <conf:speech value="alpha"/>
      <conf:grammar utterance="alpha">
        <conf:key name="x">
          <conf:key name="a" value="valA"/>
          <conf:key name="b" value="valB"/>
        </conf:key>
        <conf:key name="y" value="valY"/>
      </conf:grammar>
    </field>

    <block>
      <if cond="typeof one != 'object'">
        <conf:fail reason="one is not an object"/>
      <elseif cond="one.y != 'valY'"/>
        <conf:fail reason="one.y had bad value"/>
      <elseif cond="typeof one.x != 'object'"/>
        <conf:fail reason="one.x is not an object"/>
      <elseif cond="typeof one.x.a != 'valA'"/>
        <conf:fail reason="one.x.a had bad value"/>
      <elseif cond="typeof one.x.b != 'valB'"/>
        <conf:fail reason="one.x.b had bad value"/>
      <else/>
        <conf:pass/>
      </if>
    </block>
  </form>

</vxml>

Example 13

The following example demonstrates the use of the conf:phrase element to build a locale-independent menu.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml"
   xmlns:conf="http://www.w3.org/2002/vxml-conformance">

  <catch event="menu-done"> <conf:pass/> </catch>
  <catch><conf:fail reason="catch"/></catch>

  <menu>
    <conf:speech value="alpha"/>
    <choice event="menu-done">
      <conf:phrase utterance="alpha"/>
    </choice>
  </menu>

</vxml>

A.9 Sample XSLT Template Definition

The following is a listing of an XSLT that can be used to transform the previous example into a valid VoiceXML 2.1 document.

<?xml version="1.0"?> 
<!-- Copyright 1998-2003 W3C (MIT, ERCIM, Keio), All Rights Reserved. See http://www.w3.org/Consortium/Legal/. -->
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:conf="http://www.w3.org/2002/vxml-conformance"
    xmlns="http://www.w3.org/2001/vxml"
    version="1.0">

<xsl:output cdata-section-elements="script"/>

<!-- ############################################# -->
<!-- D o c u m e n t   H e a d e r s               -->
<!-- ############################################# -->

<!-- Copy everything that doesn't match other rules -->
<xsl:template match="/ | @* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

<!-- strip comments -->
<xsl:template match="comment()"/>

<!-- ############################################# -->
<!-- F i n a l   S t a t u s   I n d i c a t o r s -->
<!-- ############################################# -->

<!-- Success criteria -->
<xsl:template match="conf:pass">
  <prompt>pass</prompt>
  <exit/>
</xsl:template>

<!-- Failure criteria -->
<xsl:template match="conf:fail">
  <prompt>fail</prompt>
  <!-- the following only comes up in case of failure -->
  <xsl:if test="@reason != ''">
    <log>failure reason: <xsl:value-of select="@reason"/></log>
    <prompt><xsl:value-of select="@reason"/></prompt>
  </xsl:if>
  <xsl:if test="@expr != ''">
    <log>failure expression: <value expr="{@expr}"/></log>
    <prompt><value expr="{@expr}"/></prompt>
  </xsl:if>
  <exit/>
</xsl:template>

<!-- ############################################# -->
<!-- I n t e r m e d i a t e   R e p o r t s       -->
<!-- ############################################# -->

<!-- Copy everything doesn't match the other rules -->
<xsl:template match="conf:comment">
  <log>
    <xsl:apply-templates />
  </log>
</xsl:template>

<!-- ############################################# -->
<!-- I n p u t   I n d i c a t o r s               -->
<!-- ############################################# -->

<xsl:template match="conf:hangup">
  <prompt> Hang up now. </prompt>
</xsl:template>

<!-- Recite a recording that DOES contain the specified speech command (alpha, bravo, etc) -->
<xsl:template match="conf:recording[@value]">
  <prompt count="1"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="2"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>' again.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="3"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>' one more time.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:recording[@value and @count='0']">
  <prompt> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:recording[@value and @count &gt; 0]">
  <prompt count="{@count}"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>


<!-- Recite a recording at least 5 seconds in length that 
  does NOT contain a well-defined speech command (alpha, bravo, etc) -->
<!-- Little Miss Muffett sat on her tuffett, eating her curds and whey. 
  Along came a spider ... -->
<!-- Jack and Jill went up the hill to fetch a pail of water. 
  Jack fell down and broke his crown ... -->
<xsl:template match="conf:recording[@value='nonspeech']">
  <prompt count="1"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite your favorite nursery rhyme, for example, 'Little Miss Muffett'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="2"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite your favorite nursery rhyme again.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="3"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite your favorite nursery rhyme one more time.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:recording[@value='nonspeech' and @count='0']">
  <prompt> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite your favorite nursery rhyme, for example, 'Little Miss Muffett'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:recording[@value='nonspeech' and @count &gt; 0]">
  <prompt count="{@count}"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Recite your favorite nursery rhyme, for example, 'Little Miss Muffett'.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<!-- ask the user to keep quiet -->
<xsl:template match="conf:noinput">
  <prompt count="1"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    No input expected. Say nothing 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="2"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    No input expected. Say nothing again 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="3"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
     No input expected. Say nothing one more time 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:noinput[@count = 0]">
  <prompt> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    No input expected. Say nothing 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:noinput[@count &gt; 0]">
  <prompt count="{@count}"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    No input expected. Say nothing 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<!-- ask the user to mumble something out of grammar -->
<xsl:template match="conf:nomatch">
  <prompt count="1"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Say something unrecognizable 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="2"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Say something unrecognizable again 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
  <prompt count="3"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Say something unrecognizable one more time 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:nomatch[@count = 0]">
  <prompt> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Say something unrecognizable 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<xsl:template match="conf:nomatch[@count &gt; 0]">
  <prompt count="{@count}"> 
      <xsl:if test="@pre_mark != ''">
      <mark name="{@pre_mark}"/>
      </xsl:if>
    Say something unrecognizable 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="@post_mark != ''">
      <mark name="{@post_mark}"/>
      </xsl:if>
  </prompt>
</xsl:template>

<!-- special case a conf:speech inside a nomatch handler -->
<xsl:template match="*[name()='nomatch']/conf:speech[@value]" priority="2">
   <xsl:call-template name="emit-prompt">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="pre_mark"><xsl:value-of select="@pre_mark"/></xsl:with-param>
     <xsl:with-param name="post_mark"><xsl:value-of select="@post_mark"/></xsl:with-param>
   </xsl:call-template>
</xsl:template>

<!-- special case a conf:speech inside a noinput handler -->
<xsl:template match="*[name()='noinput']/conf:speech[@value]" priority="2">
   <xsl:call-template name="emit-prompt">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="pre_mark"><xsl:value-of select="@pre_mark"/></xsl:with-param>
     <xsl:with-param name="post_mark"><xsl:value-of select="@post_mark"/></xsl:with-param>
   </xsl:call-template>
</xsl:template>

<!-- special case a conf:speech inside a block -->
<xsl:template match="*[name()='block']/conf:speech[@value]" priority="2">
   <xsl:call-template name="emit-prompt">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="pre_mark"><xsl:value-of select="@pre_mark"/></xsl:with-param>
     <xsl:with-param name="post_mark"><xsl:value-of select="@post_mark"/></xsl:with-param>
   </xsl:call-template>
</xsl:template>

<xsl:template match="conf:speech[@value]">
   <xsl:call-template name="emit-prompt">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <!-- explicit @count overrides tapering behavior -->
     <!-- to get a single prompt (no tapering) with no explicit count attribute, set @count="0" -->
     <xsl:with-param name="count" select="@count"/>
     <xsl:with-param name="taper">
       <xsl:choose>
         <xsl:when test="@count">0</xsl:when>
         <xsl:when test="@taper"><xsl:value-of select="@taper"/></xsl:when>
         <xsl:otherwise>1</xsl:otherwise>
       </xsl:choose>
     </xsl:with-param>
     <xsl:with-param name="pre_mark"><xsl:value-of select="@pre_mark"/></xsl:with-param>
     <xsl:with-param name="post_mark"><xsl:value-of select="@post_mark"/></xsl:with-param>
   </xsl:call-template>
</xsl:template>

<xsl:template match="*[name()='nomatch']/conf:dtmf[@value]" priority="2">
   <xsl:call-template name="emit-dtmf">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="count" select="0"/>
     <xsl:with-param name="pre_mark" select="@pre_mark"/>
     <xsl:with-param name="post_mark" select="@post_mark"/>
   </xsl:call-template>
</xsl:template>

<xsl:template match="*[name()='noinput']/conf:dtmf[@value]" priority="2">
   <xsl:call-template name="emit-dtmf">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="count" select="0"/>
     <xsl:with-param name="pre_mark" select="@pre_mark"/>
     <xsl:with-param name="post_mark" select="@post_mark"/>
   </xsl:call-template>
</xsl:template>

<xsl:template match="*[name()='block']/conf:dtmf[@value]" priority="2">
   <xsl:call-template name="emit-dtmf">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="count" select="0"/>
     <xsl:with-param name="pre_mark" select="@pre_mark"/>
     <xsl:with-param name="post_mark" select="@post_mark"/>
   </xsl:call-template>
</xsl:template>

<xsl:template match="conf:dtmf[@value]">
   <xsl:call-template name="emit-dtmf">
     <xsl:with-param name="value"><xsl:value-of select="@value"/></xsl:with-param>
     <xsl:with-param name="count" select="@count"/>
     <xsl:with-param name="pre_mark" select="@pre_mark"/>
     <xsl:with-param name="post_mark" select="@post_mark"/>
   </xsl:call-template>
</xsl:template>

<!-- ############################################# -->
<!-- G r a m m a r s                               -->
<!-- ############################################# -->

<xsl:template match="conf:grammar[@interp and @utterance]" priority="2">
<xsl:variable name="rootname">CityName<xsl:value-of select="generate-id()"/></xsl:variable>
<grammar type="application/srgs+xml" root="{$rootname}" version="1.0">
  <rule id="{$rootname}" scope="public">
  <one-of>
     <item>
       <xsl:call-template name="emit-utterance">
         <xsl:with-param name="utterance"><xsl:value-of select="@utterance"/></xsl:with-param>
       </xsl:call-template>
      <tag>'<xsl:value-of select="@interp"/>'</tag>
    </item>
  </one-of>
  </rule>
</grammar>
</xsl:template>

<!-- an utterance without an explicit interpretation -->
<xsl:template match="conf:grammar[@utterance]" priority="1">
<xsl:variable name="rootname">CityName<xsl:value-of select="generate-id()"/></xsl:variable>
<grammar type="application/srgs+xml" root="{$rootname}" version="1.0">
  <rule id="{$rootname}" scope="public">
  <one-of>
    <item>
      <xsl:call-template name="emit-utterance">
         <xsl:with-param name="utterance"><xsl:value-of select="@utterance"/></xsl:with-param>
      </xsl:call-template>
    </item>
  </one-of>
  </rule>
</grammar>
</xsl:template>

<xsl:template match="conf:grammar[@utterance and descendant::conf:key]" priority="2">
<xsl:variable name="rootname">CityName<xsl:value-of select="generate-id()"/></xsl:variable>
<grammar type="application/srgs+xml" root="{$rootname}" version="1.0">
  <rule id="{$rootname}" scope="public">
  <one-of>
    <item>
      <xsl:call-template name="emit-utterance">
         <xsl:with-param name="utterance"><xsl:value-of select="@utterance"/></xsl:with-param>
      </xsl:call-template>

    <tag>
      <xsl:apply-templates select="conf:key"/>
    </tag>
    </item>
  </one-of>
  </rule>
</grammar>
</xsl:template>


<xsl:template match="conf:key[@value]" priority="2">
  <xsl:param name="path"/>
  <xsl:choose>  
  <xsl:when test="$path = ''">
    <xsl:text>var </xsl:text>
  </xsl:when>
  <xsl:when test="$path != ''">
    <xsl:value-of select="$path"/><xsl:text>.</xsl:text>
  </xsl:when>
 </xsl:choose>
  
  <xsl:value-of select="@name"/>
  <xsl:text>='</xsl:text>
  <xsl:value-of select="@value"/>
  <xsl:text>'; </xsl:text>
</xsl:template>

<xsl:template match="conf:key" priority="1">
  <xsl:param name="path"/>
 <xsl:choose>
  <xsl:when test="$path = ''">
    <xsl:text>var </xsl:text>
  </xsl:when>
  <xsl:when test="$path != ''">
    <xsl:value-of select="$path"/><xsl:text>.</xsl:text>
  </xsl:when>
</xsl:choose>
  <xsl:value-of select="@name"/><xsl:text>=new Object(); </xsl:text>
  <xsl:apply-templates select="conf:key">
    <xsl:with-param name="path">
      <xsl:if test="$path != ''">
        <xsl:value-of select="$path"/><xsl:text>.</xsl:text>
      </xsl:if>
      <xsl:value-of select="@name"/>
    </xsl:with-param>
  </xsl:apply-templates>
</xsl:template>


<xsl:template match="conf:phrase[@utterance]">
   <xsl:call-template name="emit-name-from-token">
     <xsl:with-param name="token" select="@utterance"/>
   </xsl:call-template>          
</xsl:template>

<!-- ############################################# -->
<!-- H e l p e r  T e m p l a t e s                            -->
<!-- ############################################# -->

<!-- for use in building grammars -->
<xsl:template name="emit-utterance">
<xsl:param name="utterance"/>
  <xsl:choose>
  <xsl:when test="$utterance='alpha'">chicago</xsl:when>
  <xsl:when test="$utterance='bravo'">san francisco</xsl:when>
  <xsl:when test="$utterance='charlie'">new york</xsl:when>
  <xsl:when test="$utterance='delta'">london</xsl:when>
  <xsl:when test="$utterance='echo'">tokyo</xsl:when>
  <xsl:when test="$utterance='foxtrot'">truth or consequences</xsl:when>
  <xsl:when test="$utterance='golf'">hackensack</xsl:when>
  <xsl:when test="$utterance='hotel'">standardsville</xsl:when>
  <xsl:when test="$utterance='help'">help</xsl:when>
  <xsl:when test="$utterance='cancel'">cancel</xsl:when>
  <xsl:when test="$utterance='exit'">exit</xsl:when>
  <xsl:when test="$utterance='yes'">yes</xsl:when>
  </xsl:choose>
</xsl:template>
<!-- Truth or Consequences is a real city in New Mexico, US.  Hackensack
     is located in New Jersey, US very close to the site of the Sept. 2001
     face-to-face.   And finally, yes, there really is a Standardsville.
     It's in Greene County, Virginia, US. -->

<!-- for use in building prompts -->
<xsl:template name="emit-name-from-token">
<xsl:param name="token"/>
  <xsl:choose>
    <xsl:when test="$token = 'alpha'">Chicago</xsl:when>
    <xsl:when test="$token = 'bravo'">San Francisco</xsl:when>
    <xsl:when test="$token = 'charlie'">New York</xsl:when>
    <xsl:when test="$token = 'delta'">London</xsl:when>
    <xsl:when test="$token = 'echo'">Tokyo</xsl:when>
    <xsl:when test="$token = 'foxtrot'">Truth or Consequences</xsl:when>
    <xsl:when test="$token = 'golf'">Hackensack</xsl:when>
    <xsl:when test="$token = 'hotel'">Standardsville</xsl:when>
    <xsl:when test="$token = 'help'">help</xsl:when>
    <xsl:when test="$token = 'cancel'">cancel</xsl:when>
    <xsl:when test="$token = 'exit'">exit</xsl:when>
    <xsl:when test="$token = 'yes'">yes</xsl:when>
  </xsl:choose>
</xsl:template>

<xsl:template name="emit-prompt">
  <xsl:param name="value"/>
  <xsl:param name="count" select="0"/>
  <xsl:param name="pre_mark"/>
  <xsl:param name="post_mark"/>
  
  <xsl:variable name="text_mapping">
    <xsl:call-template name="emit-name-from-token">
      <xsl:with-param name="token" select="$value"/>
    </xsl:call-template>          
  </xsl:variable>
  
  <xsl:choose>
  <xsl:when test="$count &gt;= 0">
    <prompt><xsl:if test="$count &gt; 0"><xsl:attribute name="count"><xsl:value-of select="$count"/></xsl:attribute></xsl:if>
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
       Say '<xsl:value-of select="$text_mapping"/>'.
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
  </xsl:when>
  <xsl:otherwise> <!-- taper -->
    <prompt count="1">
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
      Say '<xsl:value-of select="$text_mapping"/>'.
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
    <prompt count="2"> 
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
      Say '<xsl:value-of select="$text_mapping"/>' again.      
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
    <prompt count="3">       
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
      Say '<xsl:value-of select="$text_mapping"/>' one more time.
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
   </xsl:otherwise>
  </xsl:choose>     
</xsl:template>

<xsl:template name="emit-dtmf">
  <xsl:param name="value"/>
  <xsl:param name="count"/>
  <xsl:param name="pre_mark"/>
  <xsl:param name="post_mark"/>

  <xsl:choose>
    <xsl:when test="$count &gt;= 0">
      <prompt><xsl:if test="$count &gt; 0"><xsl:attribute name="count"><xsl:value-of select="$count"/></xsl:attribute></xsl:if>
      <xsl:if test="$pre_mark != ''">
      <mark name="{$pre_mark}"/>
      </xsl:if>
            Press '<xsl:value-of select="$value"/>'. 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
      <xsl:if test="$post_mark != ''">
      <mark name="{$post_mark}"/>
      </xsl:if>
      </prompt>
    </xsl:when>
  <xsl:otherwise>
    <prompt count="1"> 
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
       Press '<xsl:value-of select="@value"/>'. 
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
    <prompt count="2"> 
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
       Press '<xsl:value-of select="@value"/>' again. 
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
    <prompt count="3"> 
            <xsl:if test="$pre_mark != ''">
            <mark name="{$pre_mark}"/>
            </xsl:if>
       Press '<xsl:value-of select="@value"/>' one more time. 
            <xsl:if test="$post_mark != ''">
            <mark name="{$post_mark}"/>
            </xsl:if>
    </prompt>
  </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

A.10 Transformation output

The following is a listing of the output of Example 9 when transformed through the provided XSLT.

<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml">
  <catch>
    <log>failure expression: 
    <value expr="_event" />
    </log>
    <audio>fail</audio>
    <exit />
  </catch>

  <form>
    <block>
      <if cond="one != undefined">
        <log>failure reason: initial check</log>
        <audio>fail</audio>
        <exit />
      </if>
    </block>

    <field name="one">
      <prompt count="1">Say 'Chicago'.</prompt>
      <prompt count="2">Say 'Chicago' again.</prompt>
      <prompt count="3">Say 'Chicago' one more time.</prompt>

      <grammar type="application/grammar+xml" root="CityName">
        <rule id="CityName" scope="public">
          <one-of>
            <item>Chicago</item>
          </one-of>
        </rule>
      </grammar>
    </field>

    <block>
      <if cond="one != undefined">
        <audio>pass</audio>
        <exit />
      </if>
      <log>failure reason: field assignment</log>
      <audio>fail</audio>
      <exit />
    </block>
  </form>
</vxml>

Appendix B - Abstract CGI (IRCGI) API definition

The VoiceXML 2.1 Implementation Report contains assertions that require server-side support to verify HTTP headers and submitted values. To allow disparate test environments to use different server-side technologies, tests describe the server-side processing using a syntax independent of any particular server-side framework.

This appendix describes a server-agnostic XML API that can be transformed easily using XSLT into any server-side framework. Reference Perl and JSP XSLT templates are provided as examples.

The API supports the following:

The XML API does not require the following:

B.1 Processing Model

A document conforming to this specification, heretofore referred to as an "IRCGI document", indicates the checks to be made on an HTTP request. Supported checks include:

Checks may be nested. The result of the checks determines the next document to be executed. The next document must continue the test and must determine the success or failure of the test. Once the IRCGI document determines the next document, a response is sent, and the IRCGI document terminates.

B.2 Syntax

An IRCGI document consists of the following elements. The DTD can be found below.

ircgi
The root document element. This element must appear as the root document element for all IRCGI documents.
comment
Contains comments that may be accumulated during CGI execution. The comments may be returned in one or more log elements in the body of the HTTP response. This may be used as a primitive debugging facility.
if-header
Checks for the presence and value of an HTTP request header. Supports the following attributes:
nameRequird. The name of the header. If only the name attribute is specified, the CGI must execute the if-header content only if the named header is present in the request.
valueOptional. The expected value of the header. If this attribute is specified, the CGI must execute the if-header content only if the named header is present in the request and its value matches that of the value attribute. This attribute and the starts-with attribute are mutually exclusive.
starts-withOptional. The expected value with which the header begins. If this attribute is specified, the CGI must execute the if-header content only if the named header is present in the request and its value begins with the value of the starts-with attribute. This attribute and the value attribute are mutually exclusive.
ignore-caseOptional. The value match is case-sensitive if the ignore-case attribute is false, the default. The match is not case-sensitive if the attribute is true.
if-method
Checks the HTTP method used to submit request. The CGI must execute the if-method content only if the type attribute's value matches the HTTP method used to submit the request. The match is case-insensitive.
typeRequired. The HTTP method. Typically 'get' or 'post'.
if-parameter
Checks for the presence and value of an HTTP request parameter. Supports the following attributes:
nameRequired. The parameter's name. If only the name attribute is specified, the CGI must execute the if-parameter content only if the named parameter is present in the request.
This attribute and the starts-with attribute are mutually exclusive.
valueOptional. The parameter's expected value. If this attribute is specified, the CGI must execute the if-parameter content only if the named parameter is present in the request and its value matches that of the value attribute.
starts-withOptional. The expected value with which the parameter begins. If this attribute is specified, the CGI must execute the if-parameter content only if the named parameter is present in the request and its value begins with the value of the starts-with attribute. This attribute and the value attribute are mutually exclusive.
ignore-caseOptional. The value match is case-sensitive if the ignore-case attribute is false, the default. The match is not case-sensitive if the attribute is true.
if-all-params
Checks for the presence and value of all HTTP request parameters against an expected set. Supports the following attribute:
refRequired. The id of a <params> element containing the set of expected CGI parameters.
params
Defines a set of expected CGI parameters via one or more contained <param> elements. The params tag is allowed as a child of the root document element, <ircgi>.
idRequired. A unique identifier referenced by an <if-all-params> element.
param
Defines the name and value of an expected CGI parameter.
nameRequired. The name of the CGI request parameter.
valueRequired. The value of the CGI request parameter.
if-upload
Checks for the presence of a file upload. Supports the following attributes:
nameRequired. The name of the parameter containing the uploaded bits. If only the name attribute is specified, the CGI must execute the <if-upload> content only if the named upload is present.
sizeOptional. The expected size of the upload. If this attribute is specified, the CGI must execute the <if-parameter> content only if the named upload is present in the request and its size matches that of the value of the size attribute. The value of this attribute can be a static numeric value or a CGI parameter name. If a name is specified, the size of the file upload is compared to the value of the specified CGI parameter. The value of the CGI parameter must resolve to a numeric value. The size value should be specified in bytes.
next
Selects the next .vxml document and, optionally, sets the HTTP response code. The CGI must generate a response upon executing a next element and must not execute any other elements.
codeOptional. The HTTP response code. If the code attribute is specified, its value must be used as the HTTP response status code. The default is 200 (Ok).
destOptional. The URL of the next document. If this attribute is specified, its value must be used by the response to designate the next document to be visited.
includeOptional. true or false If this attribute is true, the document specified by the dest attribute is returned as the result document. If this attribute is false, the default, the CGI returns a generated VoiceXML document that contains a goto to the document specified by dest. The document generated when include is false may contain the content of comment elements that were encountered.
Only files that are in the directory immediately above the cgi-bin directory (see Usage below) may be included. Attempts to include other files result in an HTTP 403 (Forbidden) response status code.
sleepOptional. An interval for the CGI to sleep before returning its response to the client.
expiresOptional. Sets an Expires HTTP response header to a date calculated by adding the value of the expires attribute to the current time. The value of this attribute should be a positive or negative integer in seconds.

B.3 IRCGI Document Type Definition

The following DTD succintly declares the IRCGI API markup elements, their attributes, and the legal values for those attributes if applicable.

<!ENTITY % ir-checks "if-header | if-method | if-parameter | if-upload | if-all-params" >

<!ELEMENT ircgi (params | comment | %ir-checks; | next)*>

<!ELEMENT comment (#PCDATA) >

<!ELEMENT if-parameter (comment | %ir-checks; | next)* >
<!ATTLIST if-parameter 
  name CDATA #REQUIRED
  value CDATA #IMPLIED
  starts-with CDATA #IMPLIED
  ignore-case (true|false) "false" >

<!ELEMENT if-all-params (comment | %ir-checks; | next)* >
<!ATTLIST if-all-params
  ref IDREF #REQUIRED>

<!ELEMENT params (param+)>
<!ATTLIST params
  id ID #REQUIRED>

<!ELEMENT param EMPTY>
<!ATTLIST param 
  name CDATA #REQUIRED
  value CDATA #REQUIRED
>

<!ELEMENT if-upload (comment | %ir-checks; | next)* >
<!ATTLIST if-upload
  name CDATA #REQUIRED
  size CDATA #IMPLIED>

<!ELEMENT if-header (comment | %ir-checks; | next)* >
<!ATTLIST if-header
  name CDATA #REQUIRED
  value CDATA #IMPLIED
  starts-with CDATA #IMPLIED
  ignore-case (true|false) "false" >

<!ELEMENT if-method (comment | %ir-checks; | next)* >
<!ATTLIST if-method
  type (get|post) "get" >

<!ELEMENT next EMPTY>
<!ATTLIST next
  code CDATA "200"
  dest CDATA #IMPLIED
  include (true|false) "false"
  sleep CDATA #IMPLIED
  expires CDATA #IMPLIED>

B.4 Test examples

The following examples illustrate the use of the IRCGI API elements. The examples validate the XSLT used to generate valid CGI from the test source. These tests should all pass before the XSLT is applied to the main body of tests.

Example 1 - Verifying HTTP method

If the HTTP method used is "POST", the next document is "pass.vxml". Otherwise, the next document is "fail.vxml". The match on method name is case-insensitive.

<?xml version="1.0"?>
<!-- Check if request method was 'POST'. -->
<ircgi>
  <if-method value="post">
    <next dest="pass.vxml" /> 
  </if-method>
  <next dest="fail.vxml" /> 
</ircgi>


Example 2 - Verifying parameter presence

If the parameter "p1" was submitted, the next document is "pass.vxml". Otherwise, the next document is "fail.vxml". The value of "p1" does not matter because the value attribute was specified.

<?xml version="1.0"?>
<!--
Check if parameter p1 is present.
-->
<ircgi>
  <if-parameter name="p1">
      <next dest="../pass.vxml" /> 
  </if-parameter>
  <next dest="../fail.vxml" /> 
</ircgi>


Example 3 - Verifying parameter values

If parameter "p1" has the value "42" and if parameter "p2" has the value "quiche", the next document is "pass.vxml". Otherwise, the next document is "fail.vxml".

This document includes comment elements whose content may be included in a log element in the response document to aid in debugging.

<?xml version="1.0"?>
<!-- Check if p1 ==  42 and p2 == 'quiche'. -->
<ircgi>
  <if-parameter name="p1" value="42">
    <comment>p1 is 42.</comment>
    <if-parameter name="p2" value="quiche">
      <comment> p2 is quiche.</comment>
      <next dest="../pass.vxml" /> 
    </if-parameter>
    <comment> p2 is not quiche.</comment>
    <next dest="../fail.vxml" />
  </if-parameter>
  <comment>p1 is not 42.</comment>
  <next dest="../fail.vxml" /> 
</ircgi>


Example 4 - Verifying header presence and value

If the "User-Agent" HTTP header is present but is empty, the next document is "fail.vxml". If the "User-Agent" header is present and not empty, the next document is "pass.vxml". If the "User-Agent" header is not present, the next document is "fail.vxml".

<?xml version="1.0" ?>
<ircgi>
  <if-header name="User-Agent">
    <if-header name="User-Agent" value="" >
      <comment>
        User-Agent header present, but empty.
      </comment>
      <next dest="../fail.vxml" />
    </if-header>
    <comment>
      User-Agent header was supplied and not empty.
    </comment>
    <next dest="../pass.vxml" />
  </if-header>
  <comment>
    User-Agent header was not supplied.
  </comment>
  <next dest="../fail.vxml" />
</ircgi>

Example 5 - Setting HTTP response status code

If the parameter "p1" was submitted, the next document is "pass.vxml". Otherwise, the HTTP response code is set to "404".

<?xml version="1.0"?>
<!-- Send 404 if p1 not present -->
<ircgi>
  <if-parameter name="p1">
    <next dest="../pass.vxml" /> 
  </if-parameter>
  <next code="404" /> 
</ircgi>


Example 6 - Included and generated result documents

If the parameter "p1" was "include", then parameter "p2" will be checked. If parameter "p2" was "pass", then the response will be the content of the file "pass.vxml" because the next element's include attribute is true. If parameter "p2" is not "pass", the response will be the content of the file "fail.vxml" because the next element's include attribute is false.

If parameter "p1" was not "include", the response document will be generated by the ircgi and include a goto to "fail.vxml" because the include attribute was false, by default. The generated document may contain a log element containing the content of the IRCGI document's comment element.

<?xml version="1.0"?>
<!-- If p1 == 'include', include 'pass.vxml' if p2 == 'pass'. -->
<ircgi>
  <if-parameter name="p1" value="include">
    <if-parameter name="p2" value="pass">
      <next dest="../pass.vxml" include="true"/> 
    </if-parameter>
    <next dest="../fail.vxml" include="true" />
  </if-parameter>
  <comment>p1 is not 'include'.</comment>
  <next dest="../fail.vxml" /> 
</ircgi>


Example 7 - Sleeping before returning a response

This example navigates to a document "fail.vxml" after sleeping for five seconds.

<?xml version="1.0"?>
<ircgi>
  <next dest="../fail.vxml" sleep="5" /> 
</ircgi>


Example 8 - Using starts-with

This example checks the Content-Type header for a partial match on "multipart/form-data".

The starts-with attribute is used instead of the value attribute since the boundary portion of the value cannot be controlled or predetermined. An example of a Content-Type header value when the encoding is set to "multipart/form-data" follows:

multipart/form-data; boundary=---------------------------7d39216110392
<ircgi>
  <if-header name="Content-Type">
    <if-header name="Content-Type" starts-with="multipart/form-data" ignore-case="true">
      <next dest="../pass.vxml" />
    </if-header>
    <comment>
      Content-Type header was not multipart/form-data .
    </comment>
    <next dest="../fail.vxml" />
  </if-header>
  <next dest="../fail.vxml" />
</ircgi>

Example 9 - Returning the server-calculated epoch

The following example includes a .js document. The .js document includes a single statement that sets a variable to the value of the special variable __EPOCH__. At runtime, the CGI detects the special variable and replaces it with the server-calculated number of seconds since 'the epoch'. This feature is useful in testing to verify the caching behavior of a VoiceXML interpreter by making multiple IRCGI requests and comparing the values of __EPOCH__. If the values differ, the document was fetched from the Web. If not, the document was retrieved from the browser's local cache.

The IRCGI follows:

<ircgi>
  <next sleep="2" dest="../epoch.js" include="true"/>
</ircgi>

The .js document follows:

var epoch = __EPOCH__;

Example 10 - Setting Expires

This example returns the document "cache_me.vxml" along with an Expires header set to 60 seconds after the CGI is requested.

<ircgi>
  <next dest="../cache_me.vxml" include="true" expires="60"/>
</ircgi>

Example 11 - Verifying file uploads

If the CGI parameter named "recording" is a valid file upload and its size is equal to the value of the CGI parameter 'recsize' the next document is "pass.vxml". Otherwise, the next document is "fail.vxml".

<?xml version="1.0"?>
<!--
Check if upload was of a specified size.
-->
<ircgi>
  <!-- 
    @name represents the upload parameter name
    @size can refer to a static numeric value or, 
      as shown here, to a CGI parameter name
  -->
  <if-upload name="recording" size="recsize">
      <next dest="../pass.vxml" /> 
  </if-upload>
  <next dest="../fail.vxml" /> 
</ircgi>


Example 12 - Verifying all CGI parameters

The following IRCGI expects precisely 3 CGI parameters the names and values of which are designated by the contents of the <params> element referenced by the <if-all-params> element.

<?xml version="1.0"?>
<!-- Check if submitted parameters are in the set plist1 -->
<ircgi>
  <params id="plist1">
  <param name="p1" value="42"/>
  <param name="p2.dinner.main" value="quiche"/>
  <param name="p3" value="a 1 c"/>
  </params>
  <if-all-params ref="plist1">
      <next dest="../pass.vxml" /> 
  </if-all-params>
  <next dest="../fail.vxml" />
</ircgi>


B.5 Usage

For security purposes, transformed IRCGI documents are deployed to an isolated directory, named "cgi-bin" under the assertion directory. Other, non-IRCGI, server-side programs that may be needed are also located in the "cgi-bin" directory.

B.6 Transformation requirements

The source IRCGI documents must be transformed into files that are executable in the test environment, such as Perl or JSP files. The output files must reside in the "cgi-bin" directory and must have the ".ircgi" file extension. The file extension must be ".ircgi" because this is how other test documents (.txml) will refer to them. The .txml to .vxml transformation process cannot automatically change ".ircgi" ".pl" or ".jsp" because some tests may use ECMAScript expressions to build the reference to the ".ircgi" document.

B.7 Reference XSLT documents

Two sample XSL stylesheets are provided.

The following XSLT document can be used to transform an IRCGI document to JSP:

<?xml version="1.0"?>
<!-- Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved. See http://www.w3.org/Consortium/Legal/. -->
<!-- Transforms an ircgi test file into a Java Server Page.-->
<!-- 
    NOTE: This JSP requires the com.oreilly.servlet package available at http://www.servlets.com/cos/
    The source code, object code, and documentation in the com.oreilly.servlet package
    (http://www.servlets.com/cos/) is copyright and owned by Jason Hunter.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/" >
  <xsl:apply-templates />
</xsl:template>

<xsl:template match="ircgi">
  <xsl:call-template name="header" />
  <xsl:call-template name="declarations" />
  <xsl:call-template name="determineResult" />
</xsl:template>

<!--   Official contentType is 'application/voicexml+xml'.
  Change contentType to 'text/xml' to view generated JSP in IE.

   NOTE: The "header" template content below must be on a single line so that whitespace is not
   introduced at the beginning of the resulting XML document. XML parsers will complain. 
   Line breaks are included here for readability in the documentation.
 -->
<xsl:template name="header" >&lt;%@ page language="java" contentType="text/xml" %>
&lt;%@ page import="java.io.*" %>
&lt;%@ page import="java.net.*" %>
&lt;%@ page import="java.util.*" %>
&lt;%@ page import="com.oreilly.servlet.multipart.*" %>
</xsl:template>

<!--   Define a Result class to hold the destination, comments, and
  HTTP status code that will be set by 'determineResult' method.
-->
<xsl:template name="declarations">&lt;%!
    
    // Handles server side includes so they can be parsed
    private class JSPIncluder
    {
        void readInput(HttpServletRequest request, String strIncludePath) throws JspException
        {
            URLConnection conn;

            // Get URL
            StringBuffer strUrl = request.getRequestURL();
            String strUri = strUrl.toString();
            int nFindSlash = strUri.lastIndexOf("/");
            if (nFindSlash != -1)
            {
                strUri = strUri.substring(0, nFindSlash + 1);              
            }
            strUri += strIncludePath;
            // Open connection
            try
            {
                conn = (new URL(strUri)).openConnection();
                conn.setDoInput(true);
                conn.setDoOutput(false);
                conn.connect();
            }
            catch (Exception e)
            {
                throw new JspException(e.toString());
            }

            // Read in contents
            try
            {
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                StringBuffer buff = new StringBuffer();
                char[] chars = new char[2048];
                int nLen;

                while ((nLen = in.read(chars, 0, chars.length)) &gt;= 0)
                {
                    buff.append(chars, 0, nLen);
                }
                m_strBuffer = buff.toString();
                in.close();
            }
            catch (Exception e)
            {
                throw new JspException(e.toString());
            }
        }

        boolean replace(String strFind, String strReplace)
        {
            boolean bFound = false;
            if (m_strBuffer != null &amp;&amp; m_strBuffer.length() > 0)
            {
                int a = 0;
                int b = 0;
                while (true)
                {
                    a = m_strBuffer.indexOf(strFind, b);
                    if (a != -1)
                    {
                        m_strBuffer = m_strBuffer.substring(0, a) + strReplace + m_strBuffer.substring(a + strFind.length());
                        b = a + strReplace.length();
                        bFound = true;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return bFound;
        }
        
        void doOutput(PageContext context) throws JspException
        {
            JspWriter out = context.getOut();
            try
            {
                out.print(m_strBuffer.toString());
            }
            catch (Exception e)
            {
                throw new JspException(e.toString());
            }   
        }
        private String m_strBuffer;
    }
    
    // Handles multipart/form-data 
    private class MultiPartHandler 
    {
        HttpServletRequest request;
        
        public MultiPartHandler(HttpServletRequest req)
        {
            request = req;
        }
        
        public boolean find(String strFind, int nOfSize) throws JspException 
        {            
            MultipartParser parser;
            Part part;
            String strName;
            if((request.getContentType() != null)&amp;&amp;(request.getContentType().startsWith("multipart/form-data")))
            {
                try
                {
                    parser = new MultipartParser(request, request.getContentLength());
                    while ((part = parser.readNextPart()) != null)
                    {
                        strName = part.getName();
                        if(strName.equals(strFind))
                        {
                            if (nOfSize == -1)
                            {
                                return true;
                            }
                            else
                            {
                                if (part.isFile())
                                {
                                    InputStream stream = ((FilePart)part).getInputStream();
                                    if (getSizeOfStream(stream) == nOfSize)
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new JspException(e.toString());
                }
            }
            
            return false;        
         }
         
         private long getSizeOfStream(InputStream stream)
         {
            if (null == stream) return 0;
            
            int nRead = 0;
            int nSize = 0;
            byte[] temp = new byte[1024];
            try 
            {
                while ((nRead = stream.read(temp)) != -1)
                {
                    nSize += nRead;
                }  
            }
            catch (IOException e) {}
            
            return nSize;         
         }
     }
    
  private class Result {
    String dest;
    long sleep = 0;
    boolean expiresHeaderSet = false;
    long expires = 0;
    boolean include = false;
    StringBuffer comments = new StringBuffer();
    int statusCode = 200;
  }
  private final String NL = System.getProperty("line.separator");
  private void determineResult(HttpServletRequest request, Result result, MultiPartHandler multipart) throws JspException
  {
    <xsl:apply-templates />
  }
%&gt;</xsl:template>


<!--  Create Result object and call 'determineResult' to set its fields.
  Return VoiceXML document only if HTTP response status code is 200.
  Otherwise, return just the status code.
-->
<xsl:template name="determineResult" >&lt;%
    Result myResult = new Result();
    MultiPartHandler myMultiPart = new MultiPartHandler(request);
    determineResult(request, myResult, myMultiPart);
    response.setStatus(myResult.statusCode);
    
    if (myResult.sleep &gt; 0)
    {
        try
        {
            Thread.sleep(myResult.sleep * 1000);
        }
        catch (InterruptedException e)
        {
            throw new JspException(e.toString());
        }
    }
    
    if (myResult.expiresHeaderSet)
    {
        Date now = new Date();
        long nMillis = now.getTime();
        response.setDateHeader("Expires", nMillis + myResult.expires*1000);
    }
    
    if (myResult.include) 
    {
        Date now = new Date();
        long nMillis = now.getTime();
        String strEpoch = String.valueOf(nMillis);
        JSPIncluder includer = new JSPIncluder();
        includer.readInput(request, myResult.dest);
        includer.replace("__EPOCH__", strEpoch);
        includer.doOutput(pageContext);
    }
    else {%&gt;<xsl:call-template name="vxml" />&lt;%}%&gt;
</xsl:template>


<xsl:template match="if-parameter" >
  if (<xsl:call-template name="genIfExpr">
        <xsl:with-param name="type" select="'Parameter'" />
      </xsl:call-template>) {
    <xsl:apply-templates />
  }
</xsl:template>


<xsl:template match="if-header" >
  if (<xsl:call-template name="genIfExpr">
        <xsl:with-param name="type" select="'Header'" />
      </xsl:call-template>) {
    <xsl:apply-templates />
  }
</xsl:template>


<xsl:template match="if-all-params">
<xsl:choose>
<xsl:when test="/ircgi/params[@id=current()/@ref][not(param)]">
  if (request.getParameterMap().isEmpty()) {
      <xsl:apply-templates />
  }
</xsl:when>
<xsl:otherwise>
  if (<xsl:for-each select="/ircgi/params[@id=current()/@ref]/param">
        <xsl:call-template name="genIfExpr">
          <xsl:with-param name="type" select="'Parameter'" />
        </xsl:call-template>
        <xsl:if test="position() != last()">&amp;&amp;</xsl:if>
      </xsl:for-each>) {
    <xsl:apply-templates />
  }
</xsl:otherwise>
</xsl:choose>
</xsl:template>


<xsl:template match="if-upload" >
  int nSize = -1;
  <xsl:if test="@size">
    if (null != request.getParameter("<xsl:value-of select='@size'/>"))
    {
      try {
        nSize = Integer.parseInt(request.getParameter("<xsl:value-of select='@size'/>"));
      }
      catch (NumberFormatException e) { }
    }
    else
    {
      try {
        nSize = Integer.parseInt("<xsl:value-of select='@size'/>");
      }
      catch (NumberFormatException e) { }
    }
  </xsl:if>  
  if (multipart.find("<xsl:value-of select="@name"/>", nSize))
  {
    <xsl:apply-templates />
  } 
</xsl:template>


<!--  Generate an expression that determines when the condition of an if-parameter / if-header
  element is true.  The 'type' parameter determines the
  HttpServletRequest method to use to get the value to be checked.
-->
<xsl:template name="genIfExpr" >
  <xsl:param name="type" />
  <xsl:param name="size" />
  <xsl:variable name="method">
    <xsl:choose>
      <xsl:when test="@ignore-case = 'true'" >equalsIgnoreCase</xsl:when>
      <xsl:otherwise>equals</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="@value">
      request.get<xsl:value-of select="$type"/>
        ("<xsl:value-of select="@name"/>") != null &amp;&amp;
      request.get<xsl:value-of select="$type"/>
        ("<xsl:value-of select="@name"/>").<xsl:value-of select="$method"/>
        ("<xsl:value-of select="@value" />")
    </xsl:when>
    <xsl:when test="@starts-with">
       request.get<xsl:value-of select="$type"/>
        ("<xsl:value-of select="@name"/>") != null &amp;&amp;
       request.get<xsl:value-of select="$type"/>
        ("<xsl:value-of select="@name"/>").startsWith("<xsl:value-of select="@starts-with"/>")
    </xsl:when>     
    <xsl:otherwise>
      request.get<xsl:value-of select="$type"/>("<xsl:value-of select="@name"/>") != null 
        || multipart.find("<xsl:value-of select="@name"/>", -1)
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>


<xsl:template match="if-method" >
  if (request.getMethod().equalsIgnoreCase("<xsl:value-of select="@type" />")) {
    <xsl:apply-templates />
  }
</xsl:template>


<!--  Disarm double quotes and newline characters from comments, then
  add to result's comment buffer for use later in 'log' element.
-->
<xsl:template match="comment" >
  result.comments.append
    ("<xsl:value-of select="translate(., '&#034;&#013;&#010;', '   ')" />".trim());
  result.comments.append(NL);
</xsl:template>


<xsl:template match="next" >
  <xsl:if test="@code" >
    result.statusCode = <xsl:value-of select="@code" />;
  </xsl:if>
  <xsl:if test="@dest" >
      result.dest = "<xsl:value-of select="@dest" />";
  </xsl:if>
  <xsl:if test="@include = 'true'">
    result.include = true;
  </xsl:if>  
  <xsl:if test="@sleep">
      result.sleep = <xsl:value-of select="@sleep" />;
  </xsl:if>  
  <xsl:if test="@expires">
      result.expiresHeaderSet = true;
      result.expires = <xsl:value-of select="@expires" />;
  </xsl:if>
    return;
</xsl:template>


<!--  Generate VoiceXML document that does a 'goto' to the document
  indicated in myResult.  If comment buffer is not empty, include
  a 'log' element to aid in debugging.
-->
<xsl:template name="vxml" ><![CDATA[<?xml version="1.0" ?>
<vxml version="2.1" xmlns="http://www.w3.org/2001/vxml">
  <form>
    <block><% String comments = myResult.comments.toString();
   if (comments.length()>0) {%>
      <log>]]>
        <xsl:text disable-output-escaping="yes">
          &lt;![CDATA[&lt;%= comments %&gt;]]&gt;
        </xsl:text><![CDATA[
      </log><%}%>
      <goto next="<%= myResult.dest %>"/>
    </block>
  </form>
</vxml> ]]>
</xsl:template>

</xsl:stylesheet>

The following XSLT document can be used to transform an IRCGI document to Perl:

<?xml version="1.0"?>
<!-- Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved. See http://www.w3.org/Consortium/Legal/. -->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/" >
  <xsl:apply-templates />
</xsl:template>

<!-- root document element -->
<xsl:template match="ircgi">
  <xsl:call-template name="header" />
  <xsl:apply-templates />
  <xsl:call-template name="footer" />
</xsl:template>

<!-- handle CGI parameter checks -->
<xsl:template match="if-parameter" >
  $val = param("<xsl:value-of select="@name"/>");
  <xsl:call-template name="check-value">
    <xsl:with-param name="value" select="@value"/>
    <xsl:with-param name="starts-with" select="@starts-with"/>
    <xsl:with-param name="ignore-case" select="@ignore-case"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="if-all-params[@ref]" >
  if (CheckAllParams({<xsl:for-each select="/ircgi/params[@id=current()/@ref]/param">'<xsl:value-of select="@name"/>' 
        => '<xsl:value-of select="@value"/>'<xsl:if test="position() != last()">,</xsl:if></xsl:for-each>}, \@comments)) {
    <xsl:apply-templates/>
  }
</xsl:template>


<!-- 
if-upload - perform checks on a file upload
@name - does an upload exist with the given name
@size - (optional) check its size. if the value is a number, compare directly. 
       if it starts with alpha or '_', see if there's a param with that name, and compare against the value
@type - (optional) verify that the uploaded data is the specified type
   <if-upload name="recording" size="recsize">
      <next dest="../pass.vxml"/>
   </if-upload>
-->
<xsl:template match="if-upload">
  if (CheckUpload('<xsl:value-of select="@name"/>', 
    <xsl:choose><xsl:when test="@size">'<xsl:value-of select="@size"/>'</xsl:when>
    <xsl:otherwise>undef</xsl:otherwise></xsl:choose>, \@comments)) {
    <xsl:apply-templates/>
  }
</xsl:template>

<!-- handle HTTP Request header checks -->
<xsl:template match="if-header">
  $val = $ENV{<xsl:call-template name="map-header"><xsl:with-param name="header" select="@name"/></xsl:call-template>};
  <xsl:call-template name="check-value">
    <xsl:with-param name="value" select="@value"/>
    <xsl:with-param name="starts-with" select="@starts-with"/>
    <xsl:with-param name="ignore-case" select="@ignore-case"/>
  </xsl:call-template>
</xsl:template>

<!-- in Perl, the Request-Method is just another HTTP Request header -->
<xsl:template match="if-method">
 $val = $ENV{REQUEST_METHOD};
  <xsl:call-template name="check-value">
    <xsl:with-param name="value">
      <xsl:choose>
        <xsl:when test="@type"><xsl:value-of select="@type"/></xsl:when>
        <xsl:otherwise>get</xsl:otherwise> <!-- default http request method -->
      </xsl:choose>
    </xsl:with-param>
    <xsl:with-param name="ignore-case" select="'true'"/>
  </xsl:call-template>
</xsl:template>

<!-- strip comment elements -->
<xsl:template match="comment">
  push @comments, qq {<xsl:value-of select="."/>};
</xsl:template>

<!-- handle next elements -->
<xsl:template match="next">
    return {
  <xsl:choose>
    <xsl:when test="not(@code) and not(@dest)">
      status => "200",
    </xsl:when>
  <xsl:otherwise>
    <xsl:if test="@dest">
      next => "<xsl:value-of select="@dest" />", 
      <xsl:if test="@include = 'true'">
        include => 1,
      </xsl:if>
    </xsl:if>
    <xsl:if test="@code">
      status => "<xsl:value-of select="@code" />",
    </xsl:if>    
  </xsl:otherwise>
  </xsl:choose>
  <xsl:if test="@sleep">
    sleep => "<xsl:value-of select="@sleep"/>",    
  </xsl:if>
  <xsl:if test="@expires">
    expires => int(<xsl:value-of select="@expires"/>),
  </xsl:if>
    comments => \@comments};
</xsl:template>

<!-- check the value, if any, and continue processing child elements -->
<xsl:template name="check-value">
<xsl:param name="value"/>
<xsl:param name="starts-with"/>
<xsl:param name="ignore-case"/>
<xsl:param name="equals-upload-size"/>
<xsl:choose>
<xsl:when test="$starts-with">
  my $starts = '<xsl:value-of select="$starts-with"/>';
  if (defined($val) &amp;&amp; ($val =~ /^$starts/<xsl:if test="$ignore-case='true'">i</xsl:if>)) {
    <xsl:apply-templates/>
  }
</xsl:when>
<xsl:when test="$value"> <!-- XSLT 1.0 11.2: empty attr == missing attr -->
  <xsl:choose>
  <xsl:when test="$value=''">
    if (defined($val) &amp;&amp; ($val =~ /^\s*$/)) {
      <xsl:apply-templates/>
    }
  </xsl:when>
  <xsl:otherwise>
    my $match = '<xsl:value-of select="$value"/>';
    if (defined($val) &amp;&amp; ($val =~ /^$match$/<xsl:if test="$ignore-case='true'">i</xsl:if>)) {
      <xsl:apply-templates/>
    }
  </xsl:otherwise>
  </xsl:choose>
</xsl:when>
<xsl:otherwise>
  if (defined($val)) {
    <xsl:apply-templates/>
  }
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="header">#!/usr/local/bin/perl -w
use strict;
use CGI qw(param);
use CGI::Util qw(expires);
use IO::Handle ();

# limit sleep time to 1 minute to prevent DOS attack
use constant SLEEP_LIMIT => 60;

# forward decls
sub GetStatusText;
sub Run;
sub JumpTo;
sub GetContentType;
sub ExpiresFromDelta;
sub GetFileSize;
sub CheckUpload;
sub CheckAllParams;

my $rhRetval = Run();
my $next = $rhRetval->{next}; # where to Mr. Magoo?
my $statusCode = $rhRetval->{status};
my $statusText = "unknown status";
my $ctype = GetContentType($next);
my $raComments = $rhRetval->{comments};
my $bInclude = $rhRetval->{include};
my $expires_delta = $rhRetval->{expires};
my $epoch = time;

if (defined($next) &amp;&amp; defined($bInclude) &amp;&amp; 1 == $bInclude) {
  # restrict paths when allowing source inclusion
  if (($next =~ /^\//) || ($next =~ /\/\.\./)) {
    $statusCode = 403;
  }
}

my $sleep = $rhRetval->{sleep};
if (defined($sleep)) {
  if (($sleep =~ /^\d+$/) &amp;&amp; ($sleep &lt;= SLEEP_LIMIT)) {
    sleep $sleep;
  } else {
    push @$raComments, "Bad sleep interval $sleep";
  }
}

print "Content-Type: $ctype\n";
if (defined($expires_delta)) {
  print ExpiresFromDelta($expires_delta) . "\n";
}
if(defined($statusCode)) {
  $statusText = GetStatusText($statusCode);
  print "Status: $statusCode $statusText\n\n";
} else {
  print "\n";
}

if (!defined($next)) {
  print "$statusText\n";
} else {
  my $content;
  if ($bInclude) {
    $! = 0; # clear i/o errs
    open HINCLUDE, $next;
    if ($! != 0) {
      push @$raComments, "Unable to open $next";
      $content = JumpTo($next, $raComments);
      print STDERR "Unable to open $next\n";
    } else {
      my $eor = $/;
      undef $/;
      $content = &lt;HINCLUDE&gt;;
      # allow caching tests to be performed by interpolating __EPOCH__
      $content =~ s/__EPOCH__/$epoch/g;
      close HINCLUDE;
      $/ = $eor;  
    }
  } else {
    $content = JumpTo($next, $raComments);
  }

  print $content;
}

# Return a simple VoiceXML document that navigates 
#   to the URI specified by $next
# Dump the comments in the array $raComments to the call log
sub JumpTo
{
  my($next, $raComments) = @_;

<![CDATA[
  my $content = <<EOF;
<?xml version="1.0"?>
<vxml version="2.1"
  xmlns="http://www.w3.org/2001/vxml"
>
<form>
  <block>
EOF
]]>
foreach my $comment (@$raComments) {
  $content .= qq{&lt;log>$comment &lt;/log>\n};
}
<![CDATA[
$content .= <<EOF;
    <goto next="$next"/>
  </block>
</form>
</vxml>
EOF
]]>

  $content;
}


# Determine what to do next
# Return a hash containing one or more of the following keys:
#   next - the next document to navigate to 
#   code - the HTTP response code
#   comments - a reference to an array of comments to aid in debugging
sub Run
{
  my $val; # temp var to stash param or header value
  my @comments = (); # array of comments obtained while processing
</xsl:template>

<xsl:template name="footer" >
}

# Map a status code to an informative string
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1
sub GetStatusText
{
  my($code) = @_;

  my $rhCodes = {100 => "Continue",
  101 => "Switching Protocols",
  200 => "OK",
  201 => "Created",
  202 => "Accepted",
  203 => "Non-Authoritative Information",
  204 => "No Content",
  205 => "Reset Content",
  206 => "Partial Content",
  300 => "Multiple Choices",
  301 => "Moved Permanently",
  302 => "Found",
  303 => "See Other",
  304 => "Not Modified",
  305 => "Use Proxy",
  307 => "Temporary Redirect",
  400 => "Bad Request",
  401 => "Unauthorized",
  402 => "Payment Required",
  403 => "Forbidden",
  404 => "Not Found",
  405 => "Method Not Allowed",
  406 => "Not Acceptable",
  407 => "Proxy Authentication Required",
  408 => "Request Time-out",
  409 => "Conflict",
  410 => "Gone",
  411 => "Length Required",
  412 => "Precondition Failed",
  413 => "Request Entity Too Large",
  414 => "Request-URI Too Large",
  415 => "Unsupported Media Type",
  416 => "Requested range not satisfiable",
  417 => "Expectation Failed",
  500 => "Internal Server Error",
  501 => "Not Implemented",
  502 => "Bad Gateway",
  503 => "Service Unavailable",
  504 => "Gateway Time-out",
  505 => "HTTP Version not supported extension-code"};

  return (exists($rhCodes->{$code}) ? $rhCodes->{$code} : "invalid status code");
}

sub GetContentType
{
  my($next) = @_;

  my $ctype = "text/plain";
  if (defined($next)) {
    my $rhTypes = {'txml' => 'text/xml', 'vxml' => 'text/xml', 
      'xml' => 'text/xml', 'srgs' => 'text/xml'};
    my @parts = split /\./, $next;    
    my $ext = $parts[0];
    if (exists($rhTypes->{$ext})) {
      $ctype = $rhTypes->{$ext};
    }    
  }
  
  $ctype;
}

# return an expires header given seconds since epoch
sub ExpiresFromDelta
{
  my($delta) = @_;
  $delta = (($delta >= 0 &amp;&amp; $delta !~ /^\+/) ? "+" : "") . $delta . "s";
  "Expires: " . expires($delta);
}

# check the size of the file referenced by $fh
sub GetFileSize
{
  my($fh) = @_;
  my $size = 0;
  if (defined($fh)) {
    my $io = IO::Handle->new_from_fd(fileno($fh), 'r');
    if (defined($io)) {
      my @stats = $io->stat();
      $size = $stats[7];
    }
  }
  $size;
}

# check the validity of the named file upload
# optionally validate its size
sub CheckUpload
{
  my($name, $compsize, $raComments) = @_;
  my $retval = 0;
  my $size = GetFileSize(param($name));
  if (defined($compsize)) {
    if ($compsize =~ /^\d+$/) {
      # if all digits, assume static value for comparison
      $compsize = int($compsize);
    } elsif ($compsize =~ /^[a-zA-Z_]/) {   
      # assume a reference to another parameter
      my $psize = param($compsize);
      if (defined($psize)) {
        # we expect digits
        if ($psize =~ /^\d+$/) {
          $compsize = int($psize);
        } else {
          push @$raComments, 'invalid if-upload/@size ' . $compsize . '; expected number';
        }
      } else {
        push @$raComments, 'unable to retrieve if-upload/@size ' . $compsize;
      }
    } else {
      # yikes. bad variable name
      push @$raComments, 'bad if-upload/@size ' . $compsize;
    }
    if ($size == $compsize) {
      $retval = 1;
    } else {
      push @$raComments, 'file size ' . $size . ' did not match expected size ' . $compsize;
    }
  } else {
    # just check if we got an upload with this name
    if ($size &gt; 0) {
      $retval = 1;
    } else {
      push @$raComments, 'no file upload found for ' . $name;
    }
  }

  $retval;
}

# loop through the CGI params received in the HTTP request and verify that their names/values
# exist in the hash reference. also verify that all of the expected values were passed in the request
sub CheckAllParams
{
  my($rhExpectedParams, $raComments) = @_;
  my $ret = 1;
  my @params = param();
  # loop through what we got in the HTTP request, and see if we expected it
  foreach my $name (@params) {
    my $vSubmitted = param($name);
    my $vExpected = $rhExpectedParams->{$name};
    if (!defined($vExpected)) { # if the values aren't equal, we'll catch it during the next loop
      push @$raComments, "unexpected '$name' in request'";
      $ret = 0;
    }
  }

  # loop through what we expected to get, and see if we received it
  foreach my $expected (keys %$rhExpectedParams) {
    my $vExpected = $rhExpectedParams->{$expected};
    my $vSubmitted = param($expected);
    if (!defined($vSubmitted)) {
      push @$raComments, "expected '$expected' not found";
      $ret = 0;
    } elsif ($vExpected ne $vSubmitted) {
      push @$raComments, "expected '$expected' to be '$vExpected' not '$vSubmitted'";
      $ret = 0;
    }
  }

  $ret;
}
</xsl:template>

<!-- 
  the headers we're willing to expose.
  allowing arbitrary header requests is a security risk
-->
<xsl:template name="map-header">
<xsl:param name="header"/>
<xsl:choose>
  <xsl:when test="$header = 'User-Agent'">HTTP_USER_AGENT</xsl:when>
  <xsl:when test="$header = 'Request-Method'">REQUEST_METHOD</xsl:when>
  <xsl:when test="$header = 'Content-Type'">CONTENT_TYPE</xsl:when>
  <xsl:otherwise>__UNKNOWN__<xsl:value-of select="$header"/></xsl:otherwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

B.8 Test developer requirements

The directory convention requires test developers to ensure that paths from and to other test documents follow the convention.

The following example contains a reference to an IRCGI document from a VoiceXML document:

<goto next="cgi-bin/ua.ircgi" />

The following example contains a reference to a VoiceXML document from an IRCGI document:

<next dest="../pass.vxml"/>

B.9 Web server administrator requirements

Given the directory convention, Web server administrators must do the following:

The following example configures Apache to execute documents with a .ircgi extension as a Perl CGI:

Addhandler cgi-script .ircgi

The following example configures Tomcat to run transformed files with a .ircgi extension as a JSP:

<servlet-mapping>
  <servlet-name>jsp</servlet-name>
  <url-pattern>*.ircgi</url-pattern>
</servlet-mapping>

Appendix C - Acknowledgements

The VoiceXML 2.1 Implementation Report includes 147 assertions and corresponding tests. The Voice Browser Working Group would like to further acknowledge the contributions of several individuals for authoring and reviewing assertions and for reviewing tests: