W3C

VoiceXML 2.1
Implementation Report Plan

Version: June 13, 2005

Contributors:

Matt Oshry, Tellme Networks (Chief Editor)
Paolo Baggia, Loquendo
Michael Bodell, Tellme Networks
David Burke, VoxPilot
Emily Candell, Comverse
Matt Henry, Voxeo
Hakan Kilic, Scansoft
Brien Muschett, IBM
Ken Rehor, Vocalocity

Table of Contents

1. Introduction

The VoiceXML 2.1 Specification entered the Candidate Recommendation period on 13 June 2005.

The planned date for entering Proposed Recommendation is 1 August 2005. 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

The public is 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

Note: The testimonials with pink background from "Acme Labs" and "Beta Inc." are just examples and will be replaced with the actually submitted testimonials.

Acme Labs

Executive Summary

The W3C VoiceXML 2.1 Specification is well-written, easily implementable and extremely useful. Acme Labs used it to make waffles, to transcribe telephone calls, to chase roadrunners, and to straighten the Tower of Pisa.

Beta Inc.

Exec Summary

The W3C VoiceXML 2.1 Specification is extremely useful, easily implementable and well-written. Beta Inc. used it to make waffles, to transcribe telephone calls, to chase roadrunners, and to straighten the Tower of Pisa.

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
42 D Optional. Yes. The DOM exposed by the <data> tag must support the code property of the DOMException object as specified in DOM Level 2 . 0 0 0
43 D Optional. Yes. The DOM exposed by the <data> tag must support the documentElement property of the Document object as specified in DOM Level 2 . 0 0 0
44 D Optional. Yes. The DOM exposed by the <data> tag must support the getElementsByTagName method of the Document object as specified in DOM Level 2 . 0 0 0
45 D Optional. Yes. The DOM exposed by the <data> tag must support the getElementsByTagNameNS method of the Document object as specified in DOM Level 2 . 0 0 0
46 D Optional. Yes. The DOM exposed by the <data> tag must support the getElementById method of the Document object as specified in DOM Level 2 . 0 0 0
47 D Optional. Yes. The DOM exposed by the <data> tag must support the nodeName property of the Node object as specified in DOM Level 2 . 0 0 0
48 D Optional. Yes. The DOM exposed by the <data> tag must support the nodeValue property of the Node object as specified in DOM Level 2 . 0 0 0
49 D Optional. Yes. The DOM exposed by the <data> tag must support the nodeType property of the Node object as specified in DOM Level 2 . 0 0 0
50 D Optional. Yes. The DOM exposed by the <data> tag must support the parentNode property of the Node object as specified in DOM Level 2 . 0 0 0
51 D Optional. Yes. The DOM exposed by the <data> tag must support the childNodes property of the Node object as specified in DOM Level 2 . 0 0 0
52 D Optional. Yes. The DOM exposed by the <data> tag must support the firstChild property of the Node object as specified in DOM Level 2 . 0 0 0
53 D Optional. Yes. The DOM exposed by the <data> tag must support the lastChild property of the Node object as specified in DOM Level 2 . 0 0 0
54 D Optional. Yes. The DOM exposed by the <data> tag must support the previousSibling property of the Node object as specified in DOM Level 2 . 0 0 0
55 D Optional. Yes. The DOM exposed by the <data> tag must support the nextSibling property of the Node object as specified in DOM Level 2 . 0 0 0
56 D Optional. Yes. The DOM exposed by the <data> tag must support the attributes property of the Node object as specified in DOM Level 2 . 0 0 0
57 D Optional. Yes. The DOM exposed by the <data> tag must support the ownerDocument property of the Node object as specified in DOM Level 2 . 0 0 0
58 D Optional. Yes. The DOM exposed by the <data> tag must support the namespaceURI property of the Node object as specified in DOM Level 2 . 0 0 0
59 D Optional. Yes. The DOM exposed by the <data> tag must support the prefix property of the Node object as specified in DOM Level 2 . 0 0 0
60 D Optional. Yes. The DOM exposed by the <data> tag must support the localName property of the Node object as specified in DOM Level 2 . 0 0 0
61 D Optional. Yes. The DOM exposed by the <data> tag must support the hasChildNodes method of the Node object as specified in DOM Level 2 . 0 0 0
63 D Optional. Yes. The DOM exposed by the <data> tag must support the hasAttributes method of the Node object as specified in DOM Level 2 . 0 0 0
64 D Optional. Yes. The DOM exposed by the <data> tag must support the length property of the NodeList object as specified in DOM Level 2 . 0 0 0
65 D Optional. Yes. The DOM exposed by the <data> tag must support the item method of the NodeList object as specified in DOM Level 2 . 0 0 0
66 D Optional. Yes. The DOM exposed by the <data> tag must support the length property of the NamedNodeMap object as specified in DOM Level 2 . 0 0 0
67 D Optional. Yes. The DOM exposed by the <data> tag must support the getNamedItem method of the NamedNodeMap object as specified in DOM Level 2 . 0 0 0
68 D Optional. Yes. The DOM exposed by the <data> tag must support the item method of the NamedNodeMap object as specified in DOM Level 2 . 0 0 0
69 D Optional. Yes. The DOM exposed by the <data> tag must support the getNamedItemNS method of the NamedNodeMap object as specified in DOM Level 2 . 0 0 0
70 D Optional. Yes. The DOM exposed by the <data> tag must support the data property of the CharacterData object as specified in DOM Level 2 . 0 0 0
71 D Optional. Yes. The DOM exposed by the <data> tag must support the length property of the CharacterData object as specified in DOM Level 2 . 0 0 0
72 D Optional. Yes. The DOM exposed by the <data> tag must support the substringData method of the CharacterData object as specified in DOM Level 2 . 0 0 0
73 D Optional. Yes. The DOM exposed by the <data> tag must support the name property of the Attr object as specified in DOM Level 2 . 0 0 0
74 D Optional. Yes. The DOM exposed by the <data> tag must support the specified property of the Attr object as specified in DOM Level 2 . 0 0 0
75 D Optional. Yes. The DOM exposed by the <data> tag must support the value property of the Attr object as specified in DOM Level 2 . 0 0 0
76 D Optional. Yes. The DOM exposed by the <data> tag must support the ownerElement property of the Attr object as specified in DOM Level 2 . 0 0 0
77 D Optional. Yes. The DOM exposed by the <data> tag must support the tagName property of the Element object as specified in DOM Level 2 . 0 0 0
78 D Optional. Yes. The DOM exposed by the <data> tag must support the getAttribute method of the Element object as specified in DOM Level 2 . 0 0 0
79 D Optional. Yes. The DOM exposed by the <data> tag must support the getAttributeNode method of the Element object as specified in DOM Level 2 . 0 0 0
80 D Optional. Yes. The DOM exposed by the <data> tag must support the getElementsByTagName method of the Element object as specified in DOM Level 2 . 0 0 0
81 D Optional. Yes. The DOM exposed by the <data> tag must support the getAttributeNS method of the Element object as specified in DOM Level 2 . 0 0 0
82 D Optional. Yes. The DOM exposed by the <data> tag must support the getAttributeNodeNS method of the Element object as specified in DOM Level 2 . 0 0 0
83 D Optional. Yes. The DOM exposed by the <data> tag must support the getElementsByTagNameNS method of the Element object as specified in DOM Level 2 . 0 0 0
84 D Optional. Yes. The DOM exposed by the <data> tag must support the hasAttribute method of the Element object as specified in DOM Level 2 . 0 0 0
85 D Optional. Yes. The DOM exposed by the <data> tag must support the hasAttributeNS method of the Element object as specified in DOM Level 2 . 0 0 0
86 D Optional. Yes. The DOM exposed by the <data> tag must support the target property of the ProcessingInstruction object as specified in DOM Level 2 . 0 0 0
87 D Optional. Yes. The DOM exposed by the <data> tag must support the data property of the ProcessingInstruction object as specified in DOM Level 2 . 0 0 0
1 2 Required. Yes. Grammar element may include a srcexpr attribute to dynamically generate grammar URL 0 0 0
2 2 Required. Yes. If both src and srcexpr are included, error.badfetch is thrown 0 0 0
3 2 Required. Yes. If both srcexpr and inline grammar are included, error.badfetch is thrown 0 0 0
4 2 Required. Yes. If none of src, srcexpr nor inline grammar are included, error.badfetch is thrown 0 0 0
5 2 Required. Yes. srcexpr must be evaluated every time the grammar element needs to be executed 0 0 0
6 2 Required. Yes. If both srcexpr and inline grammar are included, error.badfetch is thrown 0 0 0
7 2 Required. Yes. If srcexpr cannot be evaluated, an error.semantic is thrown 0 0 0
8 2 Required. Yes. If both src and inline grammar are included, error.badfetch is thrown 0 0 0
9 3 Required. Yes. Script element may include an srcexpr attribute to dynamically generate script URL 0 0 0
10 3 Required. Yes. If both src and srcexpr are included, error.badfetch is thrown 0 0 0
11 3 Required. Yes. If none of src, srcexpr nor inline script are included, error.badfetch is thrown 0 0 0
12 3 Required. Yes. srcexpr must be evaluated every time the script element needs to be executed 0 0 0
13 3 Required. Yes. If both srcexpr and inline script are included, error.badfetch is thrown 0 0 0
14 3 Required. Yes. If both src and inline script are included, error.badfetch is thrown 0 0 0
15 3 Required. Yes. If srcexpr cannot be evaluated, an error.semantic is thrown 0 0 0
16 4 Required. 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. 0 0 0
17 4 Required. 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>. 0 0 0
18 4 Required. Yes. Exactly one of "name" and "nameexpr" must be specified; otherwise, an error.badfetch event is thrown. 0 0 0
19 4 Required. 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. 0 0 0
20 4 Required. Yes. If no <mark> was executed, the markname and marktime properties of application.lastresult$ are undefined. 0 0 0
141 4 Required. Yes. If nameexpr cannot be evaluated, an error.semantic event is thrown. 0 0 0
21 5 Required. Yes. If content is not returned within the specified fetchtimeout, an error.badfetch event is thrown. 0 0 0
22 5 Required. Yes. The interpreter fetches the URI specified by the src attribute. 0 0 0
23 5 Optional. Yes. If the name attribute is specified, the interpreter exposes the DOM through the ECMAScript variable corresponding to the value of the name attribute. 0 0 0
24 5 Optional. Yes. If the name attribute is present, and the interpreter doesn't support DOM, the interpreter must throw error.unsupported.data.name. 0 0 0
25 5 Required. 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. 0 0 0
26 5 Required. Yes. If srcexpr cannot be evaluated, error.semantic is thrown. 0 0 0
27 5 Required. Yes. Exactly one of "src" or "srcexpr" must be specified; otherwise, an error.badfetch event is thrown. 0 0 0
28 5 Required. Yes. Interpreters must support the methods GET and POST when submitting an HTTP request. GET is the default method. 0 0 0
29 5 Required. Yes. Specifying a URL that points to a non-existent resources causes the interpreter to throw a catchable error.badfetch event. 0 0 0
30 5 Required. Yes. If the name attribute is specified, and the interpreter retrieves an XML document that is not well-formed, the interpreter must throw a catchable error.badfetch. 0 0 0
31 5 Required. Yes. The <data> element can occur in executable content or as a child of <form> or <vxml>. 0 0 0
32 5 Required. Yes. The <data> element shares the same scoping rules as the <var> element. 0 0 0
33 5 Required. 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. 0 0 0
34 5 Required. 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. 0 0 0
35 5 Required. Yes. When an ECMAScript variable is submitted to the server its value is first converted into a string before being submitted. 0 0 0
36 5 Required. Yes. The default encoding type of the submitted document is "application/x-www-form-urlencoded". 0 0 0
37 5 Required. Yes. If the enctype attribute is set to "multipart/form-data", the interpreters must submit the document using that encoding type. 0 0 0
38 5 Required. Yes. If the namelist is not specified, no variables are submitted. 0 0 0
39 5 Required. Yes. If <data> has a namelist attribute, all and only those variables are submitted. 0 0 0
40 5 Required. Yes. If a namelist is supplied, it may contain individual variable references which are submitted with the same qualification used in the namelist. 0 0 0
41 5 Required. Yes. If specified, fetchaudio plays during a long fetch. 0 0 0
142 5 Required. Yes. If the datafetchhint property is set to "safe", content of that type is never fetched until it is needed 0 0 0
143 5 Required. Yes. A cached data resource must be reloaded if the datamaxage property for its type is less than its current age. 0 0 0
144 5 Required. Yes. A cached data resource must be reloaded if the datamaxstale property for its type is less than its current staleness. 0 0 0
88 6 Required. Yes. The <foreach> element can occur as a child of a <prompt> element. 0 0 0
89 6 Required. Yes. The array attribute must be an ECMAScript expression that evaluates to an array; otherwise an error.semantic is thrown. 0 0 0
90 6 Required. 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. 0 0 0
92 7 Required. Yes. 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. 0 0 0
93 7 Required. Yes. When the shadow variable "record" is set, it must contain a reference to the recorded audio 0 0 0
94 7 Required. Yes. when property "recordutterance" is set to true, "recordingsize" must be set in the shadow variable for the form item variable 0 0 0
95 7 Required. Yes. When the shadow variable "recordingsize" is set, it must contain the size of the recorded audio in bytes 0 0 0
96 7 Required. Yes. when property "recordutterance" is set to true, "recordingduration" must be set in the shadow variable for the form item variable 0 0 0
97 7 Required. Yes. When the shadow variable "recordingduration" is set, it must contain the duration of the recorded audio in milliseconds 0 0 0
98 7 Required. Yes. application.lastresult$ must have the shadow variable "recording" set to the same value as the form item shadow variable 0 0 0
99 7 Required. Yes. application.lastresult$ must have the shadow variable "recordingsize" set to the same value as the form item shadow variable 0 0 0
100 7 Required. Yes. application.lastresult$ must have the shadow variable "recordingduration" set to the same value as the form item shadow variable 0 0 0
101 7 Required. Yes. In the case of <link> and <menu>, the interpreter only sets the application.lastresult$ properties. 0 0 0
102 7 Optional. Yes. <record> and <transfer> may record user utterances while attempting recognition 0 0 0
103 7 Required. Yes. enctype must be "multipart/form-data" when sending the recording on the namelist 0 0 0
104 7 Required. Yes. type must be "POST" when sending the recording on the namelist 0 0 0
127 7 Required. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after filling a <field> using speech input 0 0 0
128 7 Required. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after filling a <field> using speech input 0 0 0
129 7 Required. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after filling a <field> using speech input 0 0 0
130 7 Optional. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after terminating a <record> with speech 0 0 0
131 7 Optional. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after termininating a <record> using speech 0 0 0
132 7 Optional. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after terminating a <record> using speech 0 0 0
133 7 Optional. Yes. If recordutterance property is set to false, recording shadow variable will be set to undefined after terminating a <transfer> using speech 0 0 0
134 7 Optional. Yes. If recordutterance property is set to false, recordingsize shadow variable will be set to undefined after terminating a <transfer> using speech 0 0 0
135 7 Optional. Yes. If recordutterance property is set to false, recordingduration shadow variable will be set to undefined after terminating a <transfer> using speech 0 0 0
136 7 Required. Yes. Recorded utterance may be posted using <submit> 0 0 0
137 7 Required. Yes. Recorded utterance may be posted using <subdialog> 0 0 0
138 7 Required. Yes. Recorded utterance may be posted using <data> 0 0 0
91 7.1 Required. Yes. recordutterancetype defaults to a platform specified type 0 0 0
105 7.1 Required. Yes. set the media format of recorded audio during recognition using the recordutterancetype property 0 0 0
106 7.1 Required. Yes. platform must support audio file formats from VXML 2.0 0 0 0
107 7.1 Required. Yes. platform may support other audio file formats in addition to VXML 2.0 ones. 0 0 0
108 8 Required. Yes. The default for disconnect element is to return no variables; this means the interpreter context will receive an empty ECMAScript object. 0 0 0
109 8 Required. Yes. If specified, the namelist attribute of disconnect element contains the names of the variables to be returned to interpreter context. 0 0 0
111 8 Required. Yes. If the namelist attribute of a disconnect element contains one or more undeclared variables, the interpreter throws error.semantic. 0 0 0
112 9 Optional. Yes. If type is specified and bridge not specified, type takes precedence over the default value of the bridge attribute. 0 0 0
113 9 Optional. Yes. If type="consultation" an outgoing call is attempted to the specified destination. 0 0 0
114 9 Optional. Yes. If type="consultation" and the caller disconnects while the outgoing call is attempted, connection.disconnect.hangup is thrown. 0 0 0
115 9 Optional. Yes. 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. 0 0 0
116 9 Optional. Yes. 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. 0 0 0
117 9 Optional. Yes. 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. 0 0 0
118 9 Optional. Yes. 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. 0 0 0
119 9 Optional. Yes. 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. 0 0 0
120 9 Optional. Yes. If type="consultation" and the transfer attempt ends but the reason is not known, the transfer form item variable is filled with unknown. 0 0 0
121 9 Optional. Yes. 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. 0 0 0
122 9 Optional. Yes. If type="consultation" and the platform does not support consultation transfer, error.unsupported.transfer.consultation is thrown. 0 0 0
123 9 Optional. Yes. The transfer element's "connecttimeout" value is not honored unless either "bridge" is set to true or "type" is set to "bridge". 0 0 0
124 9 Optional. Yes. Either the "bridge" or "type" attribute must be specified, else an "error.badfetch" event is thrown. 0 0 0
125 9 Optional. Yes. The "maxtime" value will only be honored if the "bridge" attribute is set to "true", or the "type" is set to "bridge" 0 0 0
126 9 Optional. Yes. When "transferaudio" is specified, and the audio file has not completed playing, the audio will cease upon a far-end connection. 0 0 0

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 VoiceXML 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>

<!ELEMENT conf:hangup   EMPTY >
<!ELEMENT conf:noinput   EMPTY >
<!ATTLIST conf:noinput
  duration CDATA #IMPLIED>
<!ELEMENT conf:nomatch   EMPTY >
<!ATTLIST conf:nomatch 
  duration CDATA #IMPLIED>

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

<!-- Add conf:speech to the menu element decl. -->
<!ELEMENT conf:speech EMPTY>
<!ATTLIST conf:speech
   value (%ir-commands;) #REQUIRED
   count 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"> 
    Recite a sentence containing the word 
    '<xsl:call-template name="emit-name-from-token">
        <xsl:with-param name="token" select="@value"/>
      </xsl:call-template>'.
  </prompt>
  <prompt count="2"> 
    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.
  </prompt>
  <prompt count="3"> 
    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.
  </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"> 
    Recite your favorite nursery rhyme, for example, 'Little Miss Muffett'.
  </prompt>
  <prompt count="2"> Recite your favorite nursery rhyme again.</prompt>
  <prompt count="3"> Recite your favorite nursery rhyme one more time.</prompt>
</xsl:template>

<xsl:template match="conf:noinput">
  <prompt count="1"> No input expected. Say nothing 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
  <prompt count="2"> No input expected. Say nothing again 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
  <prompt count="3"> No input expected. Say nothing one more time 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
</xsl:template>

<xsl:template match="conf:nomatch">
  <prompt count="1"> Say something unrecognizable 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
  <prompt count="2"> Say something unrecognizable again 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
  <prompt count="3"> Say something unrecognizable one more time 
    <xsl:if test="@duration"> for <xsl:value-of select="@duration"/> seconds</xsl:if>.
  </prompt>
</xsl:template>

<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:call-template>
</xsl:template>

<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:call-template>
</xsl:template>

<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: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>
     <xsl:with-param name="taper">1</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="taper">0</xsl:with-param>
   </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="taper">0</xsl:with-param>
   </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="taper">0</xsl:with-param>
   </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="taper">1</xsl:with-param>
   </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="taper">0</xsl:param>
  
  <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="$taper = 1">
    <prompt count="1">
      Say '<xsl:value-of select="$text_mapping"/>'.
    </prompt>
    <prompt count="2"> 
      Say '<xsl:value-of select="$text_mapping"/>' again.      
    </prompt>
    <prompt count="3">       
      Say '<xsl:value-of select="$text_mapping"/>' one more time.
    </prompt>
  </xsl:when>
  <xsl:otherwise>
    <prompt>
      Say '<xsl:value-of select="$text_mapping"/>'.      
    </prompt>
   </xsl:otherwise>
  </xsl:choose>     
</xsl:template>

<xsl:template name="emit-dtmf">
  <xsl:param name="value"/>
  <xsl:param name="taper">0</xsl:param>

  <xsl:choose>
  <xsl:when test="$taper = 1">
    <prompt count="1"> Press '<xsl:value-of select="@value"/>'. </prompt>
    <prompt count="2"> Press '<xsl:value-of select="@value"/>' again. </prompt>
    <prompt count="3"> Press '<xsl:value-of select="@value"/>' one more time. </prompt>
  </xsl:when>
  <xsl:otherwise>
    <prompt> Press '<xsl:value-of select="$value"/>'. </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.
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" >

<!ELEMENT ircgi (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-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>

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-2003 W3C (MIT, ERCIM, Keio), All Rights Reserved. See http://www.w3.org/Consortium/Legal/. -->
<!-- Transforms an ircgi test file into a Java Server Page.-->
<!-- 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.
 -->
<xsl:template name="header" >&lt;%@ page language="java" contentType="text/xml" %>&lt;%@ page import="java.io.BufferedInputStream" %>&lt;%@ page import="java.io.InputStreamReader" %>&lt;%@ page import="java.net.URL" %>&lt;%@ page import="java.net.URLConnection" %>&lt;%@ page import="java.io.BufferedReader" %>&lt;%@ page import="java.util.Date" %>&lt;%@ page import="com.oreilly.servlet.multipart.MultipartParser" %>&lt;%@ page import="com.oreilly.servlet.multipart.Part" %></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;
        
        private MultiPartHandler(HttpServletRequest req)
        {
            request = req;
        }
        
        boolean find(String strFind) 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))
                        {
                            return true;
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new JspException(e.toString());
                }
            }
            
            return false;        
         }
     }
    
  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>


<!--  Generate an expression that determines when the condition of an 'if-*'  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: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"/>")
    </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-2003 W3C (MIT, ERCIM, Keio), All Rights Reserved. See http://www.w3.org/Consortium/Legal/. -->
<!-- Brought to you by... matto@tellme.com on 10/21/2002 -->
<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>

<!-- 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: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);

# 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;

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);
}
</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>