dd avaya component developer guide

97
Avaya Speech Applications Builder Component Developer’s Guide May 15, 2004

Upload: nixon-patel

Post on 26-Oct-2015

41 views

Category:

Documents


0 download

DESCRIPTION

DD Avaya designer document

TRANSCRIPT

Page 1: DD Avaya Component Developer Guide

Avaya Speech Applications BuilderComponent Developer’s GuideMay 15, 2004

Page 2: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 2 of 97

© 2004 Avaya Inc.

All Rights Reserved.

Notice

While reasonable efforts were made to ensure that theinformation in this document was complete and accurate at thetime of printing, Avaya Inc. can assume no liability for any errors.Changes and corrections to the information in this documentmay be incorporated in future releases.

Preventing toll fraud

"Toll fraud" is the unauthorized use of your telecommunicationssystem by an unauthorized party (for example, anyone who isnot a corporate employee, agent, subcontractor, or personworking on your company's behalf). Be aware that there may bea risk of toll fraud associated with your system and that, if tollfraud occurs, it can result in substantial additional charges foryour telecommunications services.

Avaya fraud intervention

If you suspect that you are being victimized by toll fraud and youneed technical assistance or support, call Technical ServiceCenter Toll Fraud Intervention Hotline at +1-800-643-2353 forthe United States and Canada. For additional support telephonenumbers, see the Avaya Web site:

http://www.avaya.com

Select Support, then select Escalation Lists. This Web siteincludes telephone numbers for escalation within the UnitedStates. For escalation telephone numbers outside the UnitedStates, select Global Escalation List.

Providing telecommunications security

Telecommunications security (of voice, data, and videocommunications) is the prevention of any type of intrusion to(that is, either unauthorized or malicious access to or use of)your company's telecommunications equipment by some party.

Your company's "telecommunications equipment" includes boththis Avaya product and any other voice/data/video equipmentthat could be accessed via this Avaya product (that is,"networked equipment").

An "outside party" is anyone who is not a corporate employee,agent, subcontractor, or person working on your company'sbehalf. Whereas, a "malicious party" is anyone (includingsomeone who may be otherwise authorized) who accesses yourtelecommunications equipment with either malicious ormischievous intent.

Such intrusions may be either to/through synchronous (time-multiplexed and/or circuit-based) or asynchronous (character-,message-, or packet-based) equipment or interfaces for reasonsof:

• Use (of capabilities special to the accessedequipment)

• Theft (such as, of intellectual property, financialassets, or toll-facility access)

• Eavesdropping (privacy invasions to humans)

• Mischief (troubling, but apparently innocuous,tampering)

• Harm (such as harmful tampering, data loss oralteration, regardless of motive or intent)

Be aware that there may be a risk of unauthorized intrusionsassociated with your system and/or its networked equipment.Also realize that, if such an intrusion should occur, it could resultin a variety of losses to your company (including, but not limitedto, human and data privacy, intellectual property, materialassets, financial resources, labor costs, and legal costs).

Your responsibility for your company's telecommunicationssecurity

The final responsibility for securing both this system and itsnetworked equipment rests with you, an Avaya customer'ssystem administrator, your telecommunications peers, and yourmanagers. Base the fulfillment of your responsibility on acquiredknowledge and resources from a variety of sources, including,but not limited to:

• Installation documents

• System administration documents

• Security documents

• Hardware-/software-based security tools

• Shared information between you and your peers

• Telecommunications security experts

To prevent intrusions to your telecommunications equipment,you and your peers should carefully program and configure:

• Your Avaya-provided telecommunications systemsand their interfaces

• Your Avaya-provided software applications, as well astheir underlying hardware/software platforms andinterfaces

• Any other equipment networked to your Avayaproducts.

Part 15: Class A Statement

For the MCC1, SCC1, G600, and CMC1 Media Gateways:

Note: This equipment has been tested and found to comply withthe limits for a Class A digital device, pursuant to Part 15 of theFCC Rules. These limits are designed to provide reasonableprotection against harmful interference when the equipment isoperated in a commercial environment. This equipmentgenerates, uses, and can radiate radio frequency energy and, ifnot installed and used in accordance with the instruction manual,may cause harmful interference to radio communications.Operation of this equipment in a residential area is likely tocause harmful interference, in which case the user will berequired to correct the interference at his own expense.

Part 15: Class B Statement

For the G700 Media Gateway:

Note: This equipment has been tested and found to comply withthe limits for a Class B digital device, pursuant to Part 15 of theFCC Rules. These limits are designed to provide reasonableprotection against harmful interference in a residentialinstallation. This equipment generates, uses, and can radiateradio-frequency energy and, if not installed and used inaccordance with the instructions, may cause harmful interferenceto radio communications. However, there is no guarantee thatinterference will not occur in a particular installation. If thisequipment does cause harmful interference to radio or televisionreception, which can be determined by turning the equipment offand on, the user is encouraged to try to correct the interferenceby one or more of the following measures:

• Reorient the receiving television or radio antennawherethis may be done safely.

• To the extent possible, relocate the receiver withrespect to the telephone equipment.

• Where the telephone equipment requires AC power,plug the telephone into a different AC outlet so that thetelephone equipment and receiver are on differentbranch circuits.

• Consult the Dealer or an experienced radio/TVtechnician for help.

Page 3: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 3 of 97

Canadian Department of Communications (DOC)Interference Information

For MCC1, SCC1, G600, and CMC1 Media Gateways:

This Class A digital apparatus complies with Canadian ICES-003.

Cet appareil numérique de la classe A est conforme à la normeNMB-003 du Canada.

For the G700 Media Gateway:

This Class B digital apparatus complies with Canadian ICES-003.

Cet appareil numérique de la classe B est conforme à la normeNMB-003 du Canada.

This equipment meets the applicable Industry Canada Terminal

Equipment Technical Specifications. This is confirmed by theregistration number. The abbreviation, IC, before the registrationnumber signifies that registration was performed based on aDeclaration of Conformity indicating that Industry Canadatechnical specifications were met. It does not imply that IndustryCanada approved the equipment.

Japan

For the MCC1, SCC1, G600, and CMC1 Media Gateways:

This is a Class A product based on the standard of the VoluntaryControl Council for Interference by Information TechnologyEquipment (VCCI). If this equipment is used in a domesticenvironment, radio disturbance may occur, in which case, theuser may be required to take corrective actions.

For the G700 Media Gateway:

This is a Class B product based on the standard of the VoluntaryControl Council for Interference by Information TechnologyEquipment (VCCI). If this equipment is used in a domesticenvironment, radio disturbance may occur, in which case, theuser may be required to take corrective actions.

Part 15: Personal Computer Statement

This equipment has been certified to comply with the limits for aClass B computing device, pursuant to Subpart J of Part 15 ofFCC Rules. Only peripherals (computing input/output devices,terminals, printers, etc.) certified to comply with the Class B limitsmay be attached to this computer. Operation with noncertifiedperipherals is likely to result in interference to radio andtelevision reception.

Part 68: Answer-Supervision Signaling

Allowing this equipment to be operated in a manner that doesnot provide proper answer-supervision signaling is in violation ofPart 68 rules. This equipment returns answer-supervision signalsto the public switched network when:

• answered by the called station,

• answered by the attendant, or

• routed to a recorded announcement that can beadministered by the CPE user.

This equipment returns answer-supervision signals on all directinward dialed (DID) calls forwarded back to the public switchedtelephone network. Permissible exceptions are:

• A call is unanswered.

• A busy tone is received.

• A reorder tone is received.

DECLARATIONS OF CONFORMITY

US FCC Part 68 Supplier’s Declaration of Conformity (SDoC)

Avaya Inc. in the United States of America hereby certifies thatthe Avaya switch equipment described in this document andbearing a TIA TSB-168 label identification number complies withthe Federal Communications Commission’s (FCC) Rules andRegulations 47 CFR Part 68, and the Administrative Council onTerminal Attachments (ACTA) adopted technical criteria.

Avaya further asserts that Avaya handset equipped terminalequipment described in this document complies with Paragraph68.316 of the FCC Rules and Regulations defining Hearing AidCompatibility and is deemed compatible with hearing aids.Copies of SDoCs signed by the Responsible Party in the US canbe obtained by contacting your local sales representative andare available on the following Web site:

http://www.avaya.com/support

All Avaya switch products are compliant with Part 68 of the FCCrules, but many have been registered with the FCC before theSDoC process was available. A list of all Avaya registeredproducts may be found at:

http://www.part68.org/

by conducting a search using "Avaya" as manufacturer.

European Union Declarations of Conformity

Avaya Inc. declares that the equipment specified in thisdocument bearing the "CE" (Conformité Europeénne) markconforms to the European Union Radio and TelecommunicationsTerminal Equipment Directive (1999/5/EC), including theElectromagnetic Compatibility Directive (89/336/EEC) and LowVoltage Directive (73/23/EEC). This equipment has beencertified to meet CTR3 Basic Rate Interface (BRI) and CTR4Primary Rate Interface (PRI) and subsets thereof in CTR12 andCTR13, as applicable. Copies of these Declarations ofConformity (DoCs) signed by the Vice President of R&D, AvayaInc., can be obtained by contacting your local salesrepresentative and are available on the following Web site:

http://www.avaya.com/support

TCP/IP facilities

Customers may experience differences in product performance,reliability, and security, depending upon networkconfigurations/design and topologies, even when the productperforms as warranted.

Warranty

Avaya Inc. provides a limited warranty on this product. Refer toyour sales agreement to establish the terms of the limitedwarranty. In addition, Avaya’s standard warranty language, aswell as information regarding support for this product, whileunder warranty, is available through the following Web site:

http://www.avaya.com/support

Link disclaimer

Avaya Inc. is not responsible for the contents or reliability of anylinked Web sites and does not necessarily endorse the products,services, or information described or offered within them. Wecannot guarantee that these links will work all of the time and wehave no control over the availability of the linked pages.

Page 4: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 4 of 97

Trademarks

Avaya is a trademark of Avaya Inc.

Insert all other Avaya Trademarks here, then delete thisparagraph. DO NOT include other company’s trademarks.

All trademarks identified by the ® or ™ are registeredtrademarks or trademarks, respectively, of Avaya Inc. All othertrademarks are the property of their respective owners.

Ordering information: Avaya Publications Center

Voice: +1-207-866-6701

1-800-457-1764 (Toll-free, U.S. and Canada only)

Fax: +1-207-626-7269

1-800-457-1764 (Toll-free, U.S. and Canada only)

Write: Globalware Solutions

200 Ward Hill Avenue

Haverhill, MA 01835 USA

Attention: Avaya Account Manager

Web: http://www.avayadocs.com

E-mail: [email protected]

Order:

Avaya support

Avaya provides a telephone number for you to use to reportproblems or to ask questions about your contact center. Thesupport telephone number is1-800-242-2121 in the UnitedStates. For additional support telephone numbers, see the AvayaWeb site:

http://www.avaya.com

Select Support, then select Escalation Lists. This Web siteincludes telephone numbers for escalation within the UnitedStates. For escalation telephone numbers outside the UnitedStates, select Global Escalation List.

Comments

To comment on this document, send e-mail [email protected].

Acknowledgment

This document was written by the CRM InformationDevelopment group.

Page 5: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 5 of 97

Contents

Contents ............................................................................................................................................. 5Introduction........................................................................................................................................ 7

Scope 7

Targeted Audience 7

Reference Documentation 7

Overview............................................................................................................................................. 8

Background 8

System Requirements 9

SAB Dialog Components ................................................................................................................. 10

Typical Dialog Flow 10

Types of Components 11

Folder Data 12

Writing a Process Component......................................................................................................... 13

IStep, AbstractStep and Component Lifecycle 13

Calling External Systems 14

Handling Arrays in Components 14

Handling Complex Folder Data in Components 14

Writing a Rule Component............................................................................................................... 15

IRule, AbstractRule and Component Lifecycle 15

Writing a Dialog Component............................................................................................................ 17

VoiceDialog, DialogStep and Base Classes 17

VoiceDialog Implementation and Lifecycle 18

DialogStep Implementation and Lifecycle 20

Prompts 21

Variable Handlers 25

Grammars 25

Building a Voice Dialog and its Wrapper 27

Testing Dialog Components 47

Component Packaging and Deployment......................................................................................... 49

Packaging the Component In a Jar Archive 49

Using the Component in the Configurator 51

Upgrading Configuration Data and Component Versions 51

Appendix A: Process Step Example and Code............................................................................... 52

Example Code for Process Component 52

Page 6: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 6 of 97

Appendix B: Rule Component Example and Code ......................................................................... 63

Rule Component Example 63

Rule Component Code 67

Appendix C: Dialog Component Example and Code ...................................................................... 71

Dialog Component Example 71

Page 7: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 7 of 97

Introduction

Scope

This manual is designed to help application developers create new components for the SpeechApplications Builder (SAB) Configurator which is the graphical user interface of the SAB. It providesthe developer with detailed, step-by-step instructions for writing, wrapping and deploying thecomponent code into the SAB tool environment.

Targeted Audience

This manual is primarily aimed at application developers who wish to create customized componentsto supplement the existing library of pre-built SAB components.

Note: A reasonable knowledge of Java programming is assumed.

Reference Documentation

Other related documents are as follows:

• Speech Applications Builder (SAB) Installation Guide

• Speech Applications Builder (SAB) Configurator User Guide

• Speech Applications Builder (SAB) Platform Configuration and Deployment Guide

Note: The code provided in this manual is copyright to Avaya and may be used for training and demonstrationpurposes only. They may not, however, be reproduced for commercial purposes.

Page 8: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 8 of 97

Overview

The Speech Applications Builder (SAB) components are represented in the Configurator’s DialogModel by three types of icons: a flat square for a process component; a speech-bubble for dialogs anda diamond-shaped junction for rule components and connecting paths through the dialog flow. Eachcomponent represents a ‘Step’ in the dialog flow, walking the caller through to a result. Figure 1 belowshows the three component types:

Figure 1. SAB Component Types

Background

SAB is a network-based development tool that assists organizations and Call Centers to set up,create and manage telephone interaction with their customers. Automated Voice services areconvenient communication solutions for a particular business process, often performing the task ofdata collection using tried and tested dialogs like “Address Collection” and “Credit Card Verification”.

The main function of SAB is to build re-usable dialogs, but it can also handle calls and voicerecognition, providing a live channel for picking up audio input and interacting with callers.

The SAB platform gives you the infrastructure for writing standard dialogs using building blocks (orcomponents). It is the library of ready-made SAB components that helps you to quickly create, deliver

DialogComponent

ProcessComponent Rule

Component

Page 9: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 9 of 97

and maintain a voice recognition service. The components delivered with SAB are regularly usedwithin dialogs and are predictable in nature and therefore convenient for testing and delivery of routinedialogs. You can design and deploy your own dialog components when the standard components donot provide the desired functionality.

System Requirements

You will need the following:

• SAB application (check with system administrator for version number).

• Java Dev environment. (JDK 1.4.Runtime environment. The runtime of SAB is 1.3 compatible.Depending upon the target deployment platform, a specific component may be required to be 1.3compatible, but this is determined on an individual basis.

As an Application Developer you do not need direct knowledge of VoiceXML (VXML), although some idea ofhow VXML works is helpful background information; a definitive guide can be found athttp://www.w3.org/TR/voicexml20/.

You do need good knowledge of Java and Java servlets. We recommend “Java Servlet Programming”, J.Hunter et al. (O’Reilly, UK, 2001).

Note: VXML is the HTML of the voice world and it allows us to provide voice services using Web-basedtechnologies such as servlets and HTTP request/response interactions.

Page 10: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 10 of 97

SAB Dialog Components

The Speech Applications Builder (SAB) Configurator works on the principle that each part of atelephone conversation is a re-usable task-oriented dialog component handling interaction withcallers, processing collected data or deciding on the next appropriate step in the dialog. If you lookcarefully at telephony interaction, there are usually three types of repetitive activity for every call:

• Process activity – manipulation of collected data and integration with other systems

• Rule activity – determining dialog direction

• Dialog activity - voice interaction (prompts and recognition)

The three types of activities are the basis for the three types of components and the type you use orcreate depends on the task you want to perform.

Typical Dialog Flow

The dialog flow can automate a very simple process such as redirecting callers to an agent. Forexample, a call comes into a call centre. It is received by an automated SAB application. Theapplication greets and then asks the caller for their PIN, and on picking up a result, either transfers thecaller to an Agent or delivers a ‘PIN not recognized’ prompt and ends the call. To create this type ofapplication, you would use the following components:

Name Function Type of Component

CallReceive Captures incoming call data Dialog

SayDialog Greets Caller Dialog

NumberQuestion Asks a question, confirming anddisambiguating, if necessary

Dialog

VerifyPIN Calculates validity of PIN Process

Pin Validity Check Determines direction of call on true/falsecalculation

Rule

CallRedirect Transfers the call to another line Dialog

SayDialog Speaks to the caller Dialog

HangupDialog Contains set parameters for call closing Dialog

Page 11: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 11 of 97

Types of Components

Figure 2: Component Objects displays a selection of components in a SAB application flow, indicatingthe progress of a call between the components and the types of objects involved.

All components are described in the SAB Configurator as Steps and Rules and their functiondetermines what they encapsulate.

Process Steps

These components simply encapsulate a piece of process functionality, which might be performing acalculation or interfacing with a backend system. The implementation of a Process Step is a singleJava class that conforms to a specific interface, and which may call any other helper classes asrequired.

Rules

These components perform some processing in their current context, and return a Boolean value:either true or false. The implementation is again a single Java class, which may call any other helpersas required.

Dialog Steps

These encapsulate an interaction, or a series of interactions, with the caller. Their implementation ismore complex than the other components, since they are typically constructed as a composition ofother Dialog components.

Figure 2. Component Objects

START Call Database Rule

CallReceiveStep

Call Receive Dialog

NumberStep

Number Dialog

Single Result

Confirm Dialog

Wrapped

START Call Database Rule

CallReceiveStep CallReceiveStep

Call Receive Dialog

NumberStep NumberStep

Number Dialog

Single Result

Confirm Dialog

Wrapped by the NumberStep

DB

Page 12: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 12 of 97

Folder Data

This is XML data carried through the dialog flow in a defined folder.

XML data is organized into variables with their own datatypes and name. Each datatype will decidethe volume it can load. Components are written as standalone entities, and are generally notdependent on other components to do their task.

However, in order to achieve meaningful results with the process, we need interaction betweencomponents. This interaction is generally in terms of information that is passed from one componentto another. This information is passed in the form of xml and is called a folder. Each entity inside theXML passed is called a folder variable. Each folder variable has a name and a data type where thename is used to refer to the variable and the data type controls the value that can be stored in thevariable. The data types used are xsd data types (for example, xsd:date, xsd:string). Components canaccess the folder and retrieve or set the values for the variables. They can even add or removevariables from the folder.

Page 13: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 13 of 97

Writing a Process Component

A process component is the simplest type of Step. It implements a narrowly defined piece offunctionality. Developing a Process Step is simple and becomes complex only when the task to beperformed demands complexity.

IStep, AbstractStep and Component Lifecycle

The AbstractStep class provides a base implementation of the IStep interface. The methods to beimplemented by the Process Step developer fall into two groups. The first serves to describe thecomponent:

• createStepTypeDefinition(Document)

• getDescription()

The Step Type Definition returned by the first of these methods describes the component in terms ofits appearance on screen, its versioning and any configuration properties exposed by it.

The getDescription() method simply returns a human-readable description of the component and itstask.

The remaining two methods allow the component to be configured and perform its actual task:

• doInit(StepDefinition)

• process(Folder, RunnableStepAssignment)

The first method initializes the component with the configuration supplied by the Configurator user.The options for the configuration are described by the Step Type Definition – the Step Definitionpassed into the doInit() method contains the actual values for the various options.

The doInit() method is called whenever the configuration is updated (at design time), or when thecomponent is first deployed (at run time). It is always called before the component is asked to serviceprocess requests. The code in this method should extract the required configuration, and store thesettings as instance variables, since the configuration is tied to the instance of the component. Atruntime, the method will be called only once, since it is required that components are immutable interms of their configuration.

The process() method performs the actual work of the component. The runtime environment invokes itwhenever the dialog flow reaches the component. The current Folder is passed in as the firstargument - this should be used to provide any dynamic data upon which the Process Step performsits task, and to store any resultant data for use by the rest of the flow.

The process method will only be called at runtime; it may be invoked by multiple threads concurrently,and so it should be thread-safe. In particular, the process() method should not usually modify theinstance variables of the Step object, since any changes would be reflected in all calls currently beinghandled.

See Appendix A Process Component Example and Code to see a working example and the code used.

Page 14: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 14 of 97

Calling External Systems

The process() method of a Process Step may perform any required functionality, such as invoking anexternal system. Standard Java libraries and third party utilities may be used to perform the work; theonly additional requirement is that the class is exposed with the standard interface.

It is worth noting that any function performed within the process() method must be safe for use byconcurrent threads – if your external system cannot handle concurrent access, then you must takeprecautions to enforce serialization of access.

Handling Arrays in Components

The platform supports using the collection of values for a single field. There are two ways in whichthese can be used. One way is to use them as multiple values for a single field on the stepconfiguration. This is achieved by setting isArrayType flag on addField() while increateStepTypeDefinition(). If a field has been declared in this way,StepDefinition.getValue(fieldName) from doInit() will return a java.util.List.

The other way is to use folder variables which hold multiple values. With this,Folder.getElement(xpath) will return an org.jdom.Element and you must traverse through the data.Also, you can specify if the variable field added is for scalar variables or array variables.

Handling Complex Folder Data in Components

The platform supports using user defined complex data types. To do this, the component developermust specify the name of the complex data type while using addVariableField() fromcreateStepTypeDefinition(). As with array fields, Folder.getElement(xpath) will return anorg.jdom.Element and the developer must traverse through the data.

Page 15: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 15 of 97

Writing a Rule Component

A Rule determines the path of execution of a dialog flow or temporarily halts the execution, waiting fora certain condition to exist before permitting the flow to continue. Depending on the evaluation of aRule either a Step is executed or another Rule is evaluated.

When a Rule is used to determine what Step is to be executed next it is called a Transition Rule.Transition Rules determine the path of execution. By putting another Rule on a result branch of aRule, complex decision logic can be achieved from simpler Rules.

IRule, AbstractRule and Component Lifecycle

Rules must implement the IRule interface. AbstractRule provides a suitable base class – it should beextended for all user-defined rules.

Rule classes are structured in much the same way as Process Steps. Refer to the “Writing a ProcessComponent” section of this document as some details covered in that section will not be repeatedhere. However, all the necessary code to build a rule, along with comments, is provided in thissection.

As with the Process Step, the methods to be implemented fall into two groups: those to describe thecomponent and those to configure and use it. The descriptor methods differ slightly from those for theProcess Step:

• createRuleTypeDefinition(Document)

• getRuleDescription()

• getRuleDescriptionWhenTrue()

• getRuleDescriptionWhenFalse()

The Rule Type Definition returned by the first method returns exactly the same information as theStep Type Definition for the Process Step. Rules may expose arbitrary properties to be configured bythe Configurator user, in much the same way as all other components. Typically, rules will beconfigured with references to Folder variables to be examined.

The additional getRuleDescriptionWhen* methods provide additional human-readable messages forthe Configurator users.

The last two methods allow the rule to be configured and used:

• doInit(RuleDefinition)

• doEvaluate(Folder, RunnableStepAssignment)

The first of these works exactly like the doInit() method in the Process Step; it allows the rulecomponent to be initialised using configuration data entered by the Configurator user. It will be calledrepeatedly at design time (whenever the configuration is updated by the user), and once at runtimeimmediately after the component is instantiated.

The doEvaluate() method is called at runtime and performs the actual body of the rule. It must returneither true or false. It may examine the current state of any Folder data, and perform any necessarycalculations. As with Process Steps, Rules may be invoked by several concurrent threads and somust be written with thread safety in mind. The doEvaluate() method should not typically alterinstance variables in the rule class (unless, for example, a loop counter is required – in this case, caremust be taken to synchronize access to the instance variables).

Page 16: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 16 of 97

See Appendix B: Rule Component Example and Code for a working Rule Component example and its code.

Page 17: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 17 of 97

Writing a Dialog Component

Dialog components perform voice interaction with the caller. A voice interaction could include:

• asking the caller a question

• iterating a message to the caller

• transferring the caller to an agent

VoiceDialog, DialogStep and Base Classes

Writing Dialog components can be relatively more complicated than writing Process or Rulecomponents. This is due to a fundamental difference in the way they are built: while Process and Rulecomponents are typically built to perform one small, focused, stand-alone task, Dialog componentsare typically built up from other Dialog components to encapsulate more tailored or complexfunctionality.

For example, the Yes/No dialog asks the user for a yes/no answer to a simple question. A morecomplex Dialog component may ask the user for some broader input, such as the NumberDialog. Ifthe system is not confident of the caller's response, it may then ask the caller to confirm it - i.e. "Didyou say X?". This is implemented in NumberDialog so that it contains a YesNoDialog to ask thisconfirmation question. It also contains a SingleResultQuestionDialog to ask the main numberquestion. This generic question component allows any wrapping components to specify the promptsand grammars to use, along with other aspects of its setup.

A complex Dialog component may ask for some composite data, for example the collection of creditcard details. A component such as this may wrap many smaller components, for example aDateDialog to ask for each date, and a NumberDialog to ask for an issue number.

Each Top Level Dialog component must still be exposed to the SAB system as a DialogStep. Theyexpose the dialog’s interface in the GUI. The Steps extend the base class DialogStep (incom.netdecisions.ccp.model.vr); the wrapped implementations implement the VoiceDialog interface(in com.fluencyvoice.runner.dialog.voice).

Component Granularity - Strategies for Functionality and Re-use

Since VoiceDialog implementations are generally constructed from other VoiceDialog objects, anyfunctionality exposed as a VoiceDialog object is available for use in a composite component. Iffunctionality is defined only as a DialogStep, it may not be re-used to make a more complexcomponent (although it could still be combined with other components to form a rich dialog flow withinthe Configurator).

For example, consider the Number component in SAB. This could be implemented as a simplewrapper DialogStep, using a raw SingleResultQuestionDialog component. However, there are twoissues with this approach:

• The implementation of the NumberDialog requires that it can have a valid number range set uponit. In this case, if the caller speaks a number outside this range, a message is played to them andthe question asked again. This involves not just a SingleResultQuestionDialog, but aMessageDialog too. A DialogStep is not capable of wrapping several VoiceDialog componentsand so there must be a single VoiceDialog that provides the entire component's flow.

Page 18: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 18 of 97

• If the Number dialog component was written as a DialogStep around a more general questioncomponent, it would not be available as a simple, complete component for larger components tobuild upon. For example suppose a Debit Card component is required that allows the user to sayan Issue number. It would be useful to simply delegate the gathering of the number to a sub-component; this is possible if we have a specific VoiceDialog implementation for numbers.

You can write a DialogStep wrapper for an existing VoiceDialog component if your new component:

• performs a more specific task than an already existing component

• is going to be implemented by wrapping that existing component and modifying its settings

• is not likely to be used as part of a larger component

Note that this may apply to a new, custom-type question you want to ask the caller; it could well be implementedby wrapping a SingleResultQuestionDialog and encapsulating the specification of a grammar, default promptsand your results handling technique.

VoiceDialog Implementation and Lifecycle

The VoiceDialog interface specifies what is required of reusable "voice building blocks" in SAB.

The important features of VoiceDialog components are as follows:

• They are typically constructed as composites of smaller or more general VoiceDialog sub-components.

• They have an asynchronous request/response API: a component is started by a request beingpassed in, and when it has completed its work, it will issue a response to the caller.

• They expose their configuration as JavaBean properties (which may only be set at design andinitialization time, not at run time).

As with all other components in SAB, VoiceDialog components are used in a multi-threadedenvironment and must be written accordingly.

A VoiceDialog implementation should be based on the AbstractVoiceDialog base class. There arefour abstract methods in that class that must be implemented by a VoiceDialog component. The firsttwo of these return meta-data about requests that the component will accept and the responses it mayissue:

• doGetDialogRequests()

• doGetDialogResponses()

Each of these methods returns an array of classes for the requests and responses. The remaining twomethods to be implemented handle requests and responses received by the component:

• doHandleRequest(DialogRequest)

• doHandleResponse(DialogResponse)

Constructing Composite and Tailored VoiceDialogs

When building a VoiceDialog implementation it is typical to wrap other VoiceDialogs. A newVoiceDialog typically provides one or both of the following:

• A more tailored encapsulation of another VoiceDialog. For example, wrappingSingleResultQuestionDialog to ask for numbers, as in the case of NumberDialog.

Page 19: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 19 of 97

• A collection of several nested VoiceDialogs which together perform a more complex compositeflow. An example would be credit card details collection.

The Request/Response Cycle

The flow of control through a tree of VoiceDialog components is determined by passing Requests andResponses between them. A StartRequest is accepted by the top-level VoiceDialog. It causes thecomponent to perform its task by invoking sub-dialog components, and performing any requiredprocessing. When the component has finished its task, it issues a Response to the caller. This variesdepending on the outcome of the interaction with the caller. Some examples of response types areSomethingGatheredResponse, NotAnsweredResponse or CallEndedResponse.

VoiceDialog

VoiceDialog

VoiceDialog

VoiceDialog

VoiceDialog

VoiceDialog

VoiceDialog

request

response

StartRequest

Furtherrequest tosub-dialog

1

2

3

45

6

7

Figure 3. Request/Response Cycle in VoiceDialog Components

Figure 3 above illustrates a possible sequence of activity when a composite VoiceDialog is handling arequest.

Note that the structure of the sub-components in the tree is static, but the ones that are invoked (and theordering of invocation) may vary, depending on the circumstances of the request and the course of theinteraction with the user.

Managing Data Between Interactions

A VoiceDialog is a multithreaded component, and accordingly, when developing VoiceDialogs, youmust ensure that they code appropriately. In particular, it is vital to avoid:

• Storing of data for an interaction in an instance field of the component. Instance data must only beused for configuration properties of a component, which is not specific to any one call and willremain unchanged at runtime.

• Setting any properties on sub-dialog components at runtime; for example, modifying the promptsset on a sub-dialog during the doHandleRequest() or doHandleResponse() methods.Configuration properties must only be set at design and initialization time, not at runtime.

Page 20: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 20 of 97

Consider, for example, a component that collects credit card details from the user, asking first thenumber and then the expiry date. This is done by first invoking a nested component to collect thenumber, and another one to collect the date. When both are collected, a response is issuedcontaining both pieces of data. This means that the component must remember the caller's responseto the first question while the second is being asked. To do this, SAB provides the DataManager.

The DataManager is retrieved from the DataManagerFactory (in the com.fluencyvoice.runner.corepackage), and allows components to store data in the following scopes:

Request Scope

Data is associated with the current caller and with one component instance, and is available only forthe duration of the component's handling of the single request. Data in this scope should beSerializable, and depending on the DataManager in use, may be available in all VMs running theapplication.

Session Scope

Data is associated with the current caller, but is available to components until is it explicitly destroyed.Data in this scope should be Serializable, and depending on the DataManager in use, may beavailable to all VMs running the application.

Application Scope

Data is associated for all callers in the current application, in the current VM. This scope is generallynot used by components, but provides an alternative to "static" data (when it need to be specific to theapplication, not truly global).

To put data into the Session scope of the DataManager from within a component, use the followingcode:

DataManagerFactory.getDataManager().putRequestData(this.getId(), SOME_KEY, data);

To retrieve that data, use the following code:

data = DataManagerFactory.getDataManager().getRequestData(getId(), SOME_KEY);

The exact implementation of DataManager used by the SAB runtime depends on the deploymentrequirements. (Refer to the Speech Applications Builder (SAB) Platform Configuration andDeployment Guide for further details).

DialogStep Implementation and Lifecycle

DialogSteps expose VoiceDialogs to the SAB Configurator and runtime system; each of them wrap asingle VoiceDialog object. When writing components you should extend the base DialogStep baseclass which requires a number of methods to be implemented.

The first of these methods, as with Process Steps, serves to describe the component:

• createStepTypeDefinition(Document)

• getStepDefinition()

These methods work in the same way as those for Process Steps.

Page 21: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 21 of 97

The next methods deal with initializing and configuring the component:

• initStep(StepDefinition)

• createDialog() - throws a StepInitialization exception

• configureDialog(Dialog) - throws a StepInitialization exception

These are called by the SAB system in this order. The first fetches configuration data from theStepDefinition, and stores it in the instance data of the class. The second method actually creates theVoiceDialog object that this step wraps. The third method then configures that VoiceDialog with theconfiguration stored in the instance data of the DialogStep. This involves calling the JavaBeanproperty set methods on the VoiceDialog.

Steps and VoiceDialogs handle configuration data externally. Steps set up their own configurationfrom a definition passed into the initStep() method, while VoiceDialogs expose all their configurationas JavaBean properties.

The remaining two implementation methods listed below define how DialogRequests are created tostart the VoiceDialog, and how any DialogResponses are handled:

• convertToRequest(DialogFolder) - throws a Business Process exception

• addResultToFolderData(DialogFolder, DialogResponse) - throws a Business Process exception

The first of these should return a DialogRequest that will start the VoiceDialog. Many VoiceDialogshave requests that can take dynamic data (for example, data used in a dynamic prompt), and thisdynamic data can be retrieved from the folder by the convertToRequest() method.

The second method, addResultToFolderData, should perform any updates to the folder based on theresponse from the VoiceDialog.

Note that DialogSteps provide one path out of the step for every type of response issued by the VoiceDialog (i.e.defined by its getDialogResponses() method). Each of these paths can lead to a different part of the flow in theapplication. If a VoiceDialog returns no information apart from the type of the response it has issued, then noupdates are needed to the folder.

Prompts

Prompts are an important part of an application's interaction with the caller. Prompts specify what theuser will hear and are an essential part of the SAB environment.

Prompts can be simple scripts, (such as “Hello”), that are used for Text-to-Speech synthesis, pre-recorded audio (.wav files), or complex and nested structures. The more complex prompts cangenerate a mixture of audio and text-to-speech playback, concatenated from both dynamic and staticdata.

Prompts are configured as properties on VoiceDialog components, and are encapsulated withinimplementations of the DialogPrompt interface.

The DialogPrompt Interface

The com.fluencyvoice.runner.dialog.voice.prompt package includes the DialogPrompt interface thatmust be implemented by all prompt objects. It provides a number of methods, both to accessdescriptive data about the prompt and to allow the rendering of the prompt.

Page 22: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 22 of 97

DialogPrompts themselves are typically set as fixed properties (fixed during runtime) on a VoiceDialogand any dynamic behavior is achieved by the prompt object when it is rendered, using any availabledynamic data as appropriate.

The SAB Configurator builds up prompts that are configured in the tool into the appropriateDialogPrompt structures; what follows in this section is intended only for use when a componentdeveloper has to hard-code prompt definitions (for example, to specify default prompts).

Basic Prompts using TTS and Audio Files

The most basic type of prompt that can be set on a VoiceDialog is the BasicDialogPrompt. It allowsyou to specify a piece of Text-to-Speech (TTS), known as the prompt's script, to be spoken by theVoice platform. A BasicDialogPrompt is declared and set as a property of a VoiceDialog in this way:

// Construct a new MessageDialog

welcomeDialog = new MessageDialog();

// Construct the BasicDialogPrompt

BasicDialogPrompt welcomePrompt

= new BasicDialogPrompt("Welcome to our application.");

// Set the prompt on the message component

welcome.setPrompt(welcomePrompt);

The BasicDialogPrompt also allows the developer to specify audio files to play, rather than relying onTTS. It is often better to use pre-recorded prompts to interact with the caller because they provide theuser with a higher quality experience and playback of audio does not require as much processing asText to Speech. This is achieved by adding a URL reference to a .wav file, in addition to the followingscript:

// Construct the BasicDialogPrompt with a reference to a ".wav" file

BasicDialogPrompt welcomePrompt

= new BasicDialogPrompt("Welcome to our application.",

"resource:///myapp/welcome.wav");

Note that the URL for the prompt given in this example is prefixed with "resource://"; this is a specialprefix for SAB that will result in the resource being loaded from the classpath. In this case, the promptwill be loaded as a Java resource from the location "/myapp/welcome.wav" on the classpath. OtherURL types may be used to reference prompts - either specifying a different server or a different way toload the prompt other than off the classpath.

Note: It is recommended that you always provide a text script for the prompt. The advantages are that:

• If the audio file is not present, the text will be used as a backup.

• It is much easier to identify, read and maintain code when the actual text is available, as well as possiblycryptic URLs that point to your pre-recorded audio.

• With the text script available SAB can produce readable logs that allow you to determine the actualinteraction with the caller. This is very valuable when trying to debug live systems.

Page 23: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 23 of 97

Concatenating Prompts

When dealing with text scripts it is very easy to modify the contents of the Prompt because this can bedone in the Java code. When handling pre-recorded audio this becomes much more difficult. To helpwith the manipulation of prompts, SAB provides the ConcatenatedPrompt class. This class createsone prompt from a collection of sub-prompts.

Welcome to MoonTrekking. Are you anexisting customer?

Yes/NoDialog Replies

Yes

No Thank you for calling

We are transferringyou to an Agent.

Welcome back

Concatenation

Figure 4. Concatenation

Each sub-prompt can then specify a different piece of pre-recorded audio or TTS that can play backas a single concatenated prompt.

In our sample application, we use this for the message that is announced before we transfer the callerto an agent. We currently have two prompts for this, but a large part of each script is the same. Byusing the ConcatenatedPrompt we can record part common to both and prefix it with an appropriatesub-prompt depending on how the caller answered the question.

Note: Avaya does not recommend breaking up these particular prompts in a real application. Where it is easy torecord script as a single file this often produces a better experience. You might, however, run into situationswhere you need to use concatenation.

Escalating Prompts

Escalating Prompts are used to deal with recognition errors such as the caller staying silent or notspeaking clearly. An escalating prompt is a prompt that can deliver more and more explicit instructionencouraging the caller to speak, or speak more clearly. The original prompt is repeated with changesto the output, for example, “Say your First and Last Name”…. “Please say your First and Last Name”.“Please say your First and Last Name, for example, John Brown.”

Escalation is usually applied when implementing error handlers. The core SAB componentsautomatically invoke escalating prompts during error handling. The EscalatingDialogPrompt allows forthe addition of prompts to be played in order as the prompt is repeatedly invoked.

Question Please say your name?

No Match I didn’t understand. What’s your name?

I still didn’t understand. Say your name, please.

No Input I didn’t hear anything.

I still didn’t hear. Please hold while I transfer you to an agent.

Figure 5. Escalating Prompt

Page 24: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 24 of 97

Prompts can be built up using both nested concatenated and escalating prompts as the followingexample demonstrates.

Escalation ⇓

Say your first and last name

Concatenation ⇒ Welcome to MoonBanking

Please say your first and lastname

Please say your first and lastname, for example, John Brown

Figure 6. Concatenation and Escalation

Using Dynamic Data in Prompts

In a voice application there are many situations where you want to change the script of a promptbased on the data you collect. This could be because you do not know the exact content of the data;information like dates, times, addresses and figures are too varied or unpredictable to be supportedby static prompts.

Static Data Dynamic Data

Thank you forcalling Moon

Money.

“Your savingsaccount balance

on...” Tuesday 4thOctober 2002.

...is...

“One thousandeight hundred andforty two pounds.”

Figure 7. Static and Dynamic Data

The voice prompt says the following:

“Thank you for calling Moon Money. Your savings account balance on Tuesday 4th October2002, is one thousand eight hundred and forty two pounds”.

To handle this issue, the DialogPrompt interface supports the concept of dynamic data. Dynamic datais passed into the prompt object allowing it to build a prompt based on an interpretation of that data.

Page 25: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 25 of 97

As a general rule, this prompt is a concatenated prompt joining static pieces to dynamic data asshown in Figure 7.

The concept of dynamic prompt data is used throughout SAB and many dialogs come with promptclasses that can announce data such as dates, names and monetary amounts. These classesannounce the data as dynamic data and produce an appropriate prompt based on the interpretation ofthat data.

Variable Handlers

In addition to these DialogPrompts, the platform also supports Variable Handlers. A variable handlerworks in a similar way to a dialog prompt in that it renders some dynamic data in a predefined way.For instance, a money variable handler might take the value of 2.45 and read it back in TTS as ‘twodollars and forty-five cents’.

The difference between a dialog prompt and a variable handler is that a dialog prompt is associatedwith a particular voice dialog at the code level whereas a variable handler can be assigned from thetool, making them more flexible.

Grammars

Grammars define how a user’s speech is recognized and interpreted. Grammars define:

• Words that may be spoken

• The patterns in which those words may occur

• The language of the spoken words

• What these words mean, and how they should be interpreted (for instance, in a Number grammar,the user saying 'double five' would be interpreted as '55').

One of the benefits of the SAB platform is that most of the components provided encapsulate thedefinition of grammars; for example, if you want to collect a number from the user, you can simply usea NumberDialog. Component developers will at times require new grammars to be defined.

A grammar must be one of two types:

• Predefined: the grammar is static and defined in an external file.

• Dynamic: generated on the fly at runtime, for example in response to one of the caller's earlieranswers or a query to a database.

SAB supports both types of grammars via the same infrastructure - the VoiceGrammar interface (inthe package com.fluencyvoice.runner.widget.voice.grammar). There are different VoiceGrammarimplementations for the different types of grammar, and there are different renderers for the differentspeech platforms.

Pre-defined Grammars

Predefined grammars should be used where the caller's responses are known at design time. Forexample, if the caller is asked to say a number, their response can be interpreted by a standardgrammar.

Page 26: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 26 of 97

In code, predefined grammars are defined using the PredefinedGrammar class:

questionVoiceDialog.setQuestionGrammar(

new PredefinedGrammar(

"Some_Rule",

"com/myapp/Some_Grammar",

new String[] { "my_slot" }));

This declaration includes the grammar's rule name, path where the rule can be found and an array ofslots returned by the grammar.

Since different Speech platforms (for example, Nuance and IBM) use different formats for theirgrammar files (and in some cases, require different files for the different acoustic models theysupport), SAB provides a way to map the grammar path given to an actual grammar file to serve tothe recognition server. This is done using Grammar Packages - each grammar package correspondsto a particular set of files for a particular platform. Grammar files are typically served from theclasspath; a component requiring grammars will typically provide them in companion Jar files.

As an example of Grammar Packages, Nuance uses NGO (Nuance Grammar Object) files that aretied to acoustic models and so the Nuance acoustic model (English-UK-1.7.0) is supported by theSAB acoustic model package (grammar-nuance-english-uk-1.7.0). When the above grammarreference to the file key "com/myapp/Some_Grammar" is used on a system with this grammarpackage, it will be mapped to the file reference:

"grammar-nuance-english-uk-1.7.0/com/myapp/Some_Grammar.ngo"

In this way, SAB enables grammars for multiple different grammar packages to be on the classpathconcurrently; only those that are currently required will be used.

Grammar Results and Slot Processors

Grammars return their results in slots, which are essentially a set of name-value pairs. These arereturned from the SAB adaptor layer as a Java Map of Strings.

To convert these Maps into a more useful object representation, SAB uses Slot Processors. Theseare two-way converters between a Map of slots and a typed Object. The type of object depends onthe VoiceDialog in question. Whenever a VoiceDialog wraps a SingleResultQuestionDialog (as mostsimple types of questions do), it should specify a Slot Processor on the question. (For example,NumberDialog specifies a NumberSlotProcessor on its SingleResultQuestionDialog - it convertsbetween slots and Long objects).

Dynamic Grammars and the Grammar Model

Dynamic grammars are for use when the caller's response needs to be interpreted based on runtimeinformation. For example, in some components, the response to the first question is asked to restrictthe possibilities for a second question, to aid recognition. SAB provides an object-based abstractionfor building up these dynamic grammars, and a set of renderers for each Voice platform, e.g. Nuance,IBM, Speechworks.

The package com.fluencyvoice.runner.widget.voice.grammar contains the grammar abstractionclasses, including GrammarModel and GrammarRule. These allow a tree structure to be built up,specifying the rules or slots of the grammar.

Page 27: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 27 of 97

An example usage of this type of grammar is shown here:

GrammarModel model = new GrammarModel();

for(int i = 0 ; i < validAnswers.length; i ++){

HashMap slots = new HashMap(1);

slots.put(SLOT_NAME,validAnswers[i]);

model.addRule(new GrammarRule(validAnswers[i],slots));

}

questionDialog.setQuestionGrammar(

new DynamicGrammar(ruleName,model,new String[]{SLOT_NAME},getId()));

This creates a new GrammarModel, adds a list of valid answers to that grammar, then wraps it with aDynamicGrammar and sets it on a question dialog component.

Building a Voice Dialog and its Wrapper

An example consisting of step-by-step instructions for building voice dialogs and their wrappers isgiven below where a sample VoiceDialog is created, and a DialogStep wrapper is developed aroundit. The example shown is a variant of the NumberDialog, since this is a fairly straightforwardcomponent that exhibits many common pieces of functionality.

Note that this is by no means the simplest component that could be developed - many standard "question" typecomponents would be more straightforward. In particular, the NumberDialog wraps two sub-dialogs, whereasmany will need to wrap only one.

Creating a VoiceDialog

The NumberDialog collects a number from the caller and performs any confirmation anddisambiguation as required, based on the confidence thresholds set. To do this, it uses theSingleResultQuestionDialog, which it wraps and delegates to. The NumberDialog also allows theapplication developer to specify a range of numbers that are acceptable. If the user states a numberoutside that range, he will be informed that his entry is not a valid one (by a nested MessageDialog),and re-prompted. (The grammar used by the component shown here can recognize 1 to 1,000,000.Any numbers outside that range will not be recognized at all, and will give a Not Recognizedresponse).

The NumberDialog class extends AbstractVoiceDialog, as described in the “VoiceDialogImplementation and Lifecycle” section of this document. The first step in the implementation is todeclare the requests it will handle and the responses it will issue:

/**

* Returns all the DialogRequest classes this will handle. This covers just

* the single StartRequest.

*/

protected Class[] doGetDialogRequests() {

return new Class[] { StartRequest.class };

}

Page 28: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 28 of 97

/**

* Returns all the DialogResponse classes that this will issue. This covers

* just two: either a number is gathered, or not.

*/

protected Class[] doGetDialogResponses() {

return new Class[] { NumberGatheredResponse.class, NotAnsweredResponse.class };

}

The actual classes are typically defined as inner classes in a VoiceDialog component:

/**

* Start request for this dialog.

*/

public static class StartRequest extends AbstractDialogRequest {

public StartRequest(Dialog source) {

super(source);

}

}

/**

* Indicates that a number has been successfully gathered from the

* user, and contains that number as a <code>Long</code>. Any confirmation

* or disambiguation, where required, will have been carried out.

*/

public static class NumberGatheredResponse extends AbstractDialogResponse {

// The result.

private Long result;

/**

* @param source The component issuing this response.

* @param result The result gathered by the component.

*/

public NumberGatheredResponse(Dialog source, Long result) {

super(source);

if (Assertions.ON) {

Assertions.assertTrue(result != null);

}

this.result = result;

Page 29: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 29 of 97

}

/**

* Returns the number gathered by this component.

* @return The response number as a Long, never null.

*/

public Long getNumber() {

return result;

}

}

/**

* Indicates that no answer could be gathered from the user. The

* component will have re-prompted the user according to the counts

* specified.

*/

public static class NotAnsweredResponse extends AbstractDialogResponse {

public NotAnsweredResponse(Dialog source) {

super(source);

}

}

This has defined what the very high-level runtime behavior of the VoiceDialog will be - it will take aStartRequest, and issue either a NumberGatheredResponse or a NotAnsweredResponse. (It mayalso issue a CallEndedResponse of some type - this is behavior specified by the baseAbstractVoiceDialog class, however, and may be completely ignored by this component'simplementation.)

Next, we declare various constants that will be required by the component - these define options forgrammars, and default values for the various properties:

private static final Logger LOGGER = LoggerFactory.getLogger(NumberDialog.class);

/**

* Default question prompt to use if none is specified.

*/

private static final DialogPrompt DEFAULT_QUESTION_PROMPT =

new BasicDialogPrompt("Please say a number");

/**

* Here we build up the default confirmation prompt to use - it is a

* concatenation of a prompt to say the dynamic numbers, and the

Page 30: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 30 of 97

* confirmation question.

*/

private static final ConcatenatedDialogPrompt DEFAULT_CONFIRMATION_PROMPT;

static {

DEFAULT_CONFIRMATION_PROMPT = new ConcatenatedDialogPrompt();

DEFAULT_CONFIRMATION_PROMPT.addPrompt(new NumberDialogPrompt());

DEFAULT_CONFIRMATION_PROMPT.addPrompt(new BasicDialogPrompt("is this correct?"));

}

/**

* Default maximum number the user can enter. 1 million - set to the maximum

* possible.

*/

private static final long DEFAULT_MAX_NUM_SIZE = 1000000L;

/**

* Default minimum number the user can enter. Zero - set to the minimum

* possible.

*/

private static final long DEFAULT_MIN_NUM_SIZE = 0L;

/**

* The default number of times the user is allowed to say a number outside

* of the limits. If this is exceeded, a NotAnsweredResponse will be issued.

*/

private static final int DEFAULT_MAX_INVALID_NUMBER_COUNT = 1;

/**

* The default NumberStyle to allow the user. This is mixed - both digit-by-

* digit and natural styles are allowed.

*/

private static final NumberStyle DEFAULT_NUMBER_STYLE = NumberStyle.MIXED;

/**

* The session key under which to store the count of how many times the user

* has said an invalid number. This will be stored in the request scope.

Page 31: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 31 of 97

*/

private static final String REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT =

"invalidNumberCount";

// The slot used by the grammar to return the number chosen.

private static final String SLOT_NAME = "choice";

// The paths to the various grammars that may be used.

private static final String SPELT_OUT_GRAMMAR_PATH =

"com/fluencyvoice/dialog/number/Number_SpeltOut";

private static final String NATURAL_GRAMMAR_PATH =

"com/fluencyvoice/dialog/number/Number_Natural";

private static final String MIXED_GRAMMAR_PATH =

"com/fluencyvoice/dialog/number/Number_Mixed";

// The names of the various rules of the grammars that may be used.

private static final String SPELT_OUT_RULE_NAME = "ZeroToAMillion_SpeltOut";

private static final String NATURAL_RULE_NAME = "ZeroToAMillion_Natural";

private static final String MIXED_RULE_NAME = "ZeroToAMillion_Mixed";

Instance variables are used to hold configuration properties, and references to sub-dialogs:

/**

* The sub-dialog component that asks the user the question (with any

* confirmation, disambiguation as required).

*/

private SingleResultQuestionDialog numQuestionDialog;

/**

* The sub-dialog component that plays a message to the user if they say a

* message that's out of the range acceptable.

*/

private MessageDialog outOfRangeMessageDialog;

/**

* The default prompt to play to the user if they say a number outside the

* allowed range. This is recreated when the max and min values are updated,

* as it says 'please say a number between X and Y'.

Page 32: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 32 of 97

* <p>

* See {@link #setupDefaultOutOfRangePrompt()}.

*/

private DialogPrompt defaultOutOfRangePrompt;

/**

* The minimum number that's acceptable from the user.

*/

private Long minNumberSize = new Long(0);

/**

* The maximum number that's acceptable from the user.

*/

private Long maxNumberSize = new Long(DEFAULT_MAX_NUM_SIZE);

/**

* The NumberStyle that defines the ways the user can enter their number.

*/

private NumberStyle numberStyle = null;

The constructor for the NumberDialog instantiates the sub-dialog components, and registers themwith the base class:

/**

* Constructor. This constructs the sub-dialog components to be used by this

* component, registers them, then sets up the default properties of this

* dialog.

*/

public NumberDialog() {

// Create sub-components

numQuestionDialog = new SingleResultQuestionDialog();

outOfRangeMessageDialog = new MessageDialog();

// Register sub-components

registerSubDialog(numQuestionDialog);

registerSubDialog(outOfRangeMessageDialog);

Page 33: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 33 of 97

// Setup the details of this dialog and the contained dialogs, as per defaults.

setupDefaultOutOfRangePrompt();

outOfRangeMessageDialog.setPrompt(defaultOutOfRangePrompt);

numQuestionDialog.setSlotProcessor(new NumberSlotProcessor());

numQuestionDialog.setQuestionPrompt(DEFAULT_QUESTION_PROMPT);

numQuestionDialog.setConfirmationPrompt(DEFAULT_CONFIRMATION_PROMPT);

resetGrammar();

}

Note that the constructor also sets up the default properties and configuration for the sub-dialogs, and anydynamically-created defaults for this component.

The doHandleRequest() method is implemented next. It is simple - after performing checks, itdelegates through to the nested SingleResultQuestionDialog to ask the question:

/**

* Handles the StartRequest for this dialog component. This performs some

* sanity checks on the configuration of the component's properties, then

* passes a request to the nested SingleResultQuestionDialog component, to

* ask the actual question.

*/

protected void doHandleRequest(DialogRequest request) {

// Check the request is of the correct type.

if (!(request instanceof StartRequest)) {

throw new IllegalArgumentException(

"Start with NumberDialog.StartRequest, but not a "

+ request.getClass());

}

// If assertions are enabled, check that the minimum value is actually under

// the maximum value.

if(Assertions.ON) {

Assertions.assertTrue(getActualMaxNumberSize() >= getActualMinNumberSize(),

"The Maximum allowed number (" + getActualMaxNumberSize()

+ ") is less than the Minimum allowed number ("

+ getActualMinNumberSize() + ")");

}

Page 34: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 34 of 97

// Pass a request to the nested question sub-dialog.

numQuestionDialog.handleRequest(new SingleResultQuestionDialog.StartRequest(this));

}

This is typical for the doHandleRequest() method, simply passing a request to a nested component.The doHandleResponse() method is more complex; it handles responses from both the wrappedSingleResultQuestionDialog and the MessageDialog. To work out what is currently happening in thecomponent, it's a simple matter of checking which sub-dialog is issuing the response.

/**

* Handles responses sent to this dialog from the nested sub-dialogs. These

* will either be from the SingleResultQuestionDialog responsible for asking

* the question, or from the MessageDialog responsible for saying the out-

* of-bounds message.

*/

protected void doHandleResponse(DialogResponse response) {

// If the response is from the SingleResultQuestionDialog to ask the number...

if (response.getSource().equals(numQuestionDialog)) {

// If it's an answer....

if (response instanceof SingleResultQuestionDialog.AnsweredResponse) {

SingleResultQuestionDialog.AnsweredResponse result =

(SingleResultQuestionDialog.AnsweredResponse) response;

if (Assertions.ON)

Assertions.assertTrue(result != null);

Long resultValue = (Long) result.getResult();

// Check that it's within the allowed range

if (resultValue.longValue() > getMaxNumberSize().longValue()

|| resultValue.longValue() < getMinNumberSize().longValue()) {

Integer counter = (Integer)

getRequestData(REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT);

int count = 0;

if (counter != null)

count = counter.intValue();

if ((count < DEFAULT_MAX_INVALID_NUMBER_COUNT)) {

count++;

Page 35: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 35 of 97

putRequestData(REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT,

new Integer(count));

outOfRangeMessageDialog.handleRequest(new MessageDialog.StartRequest(this));

}

else {

// Has said an out-of-bounds number too many times, return a

// not-answered response.

issueResponse(new NotAnsweredResponse(this));

}

}

else {

// Got a number - return it.

issueResponse(new NumberGatheredResponse(this, resultValue));

}

}

// If it's not been answered (this will happen after retries as necessary)...

else if (response instanceof SingleResultQuestionDialog.NotAnsweredResponse) {

// Issue the not-answered response from this dialog.

issueResponse(new NotAnsweredResponse(this));

}

}

// If the reponse is from the out-of-bounds MessageDialog, then just ask the question again.

else if (response.getSource().equals(outOfRangeMessageDialog)) {

// It only sends back CompletedResponses

if (response instanceof MessageDialog.CompletedResponse) {

// Re-ask the question

numQuestionDialog.handleRequest(new SingleResultQuestionDialog.StartRequest(this));

}

}

}

The NumberDialog now has all its core functionality implemented (apart from a couple of helpermethods, which will be included at the end of this section). Now its configuration properties areexposed. First are the simple properties that are being exposed from the wrapped sub-dialogs. Theyare just delegated through to the sub-dialog in question:

/**

* Set this to <tt>null</tt> to use the default value from the nested

Page 36: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 36 of 97

* SingleResultQuestionDialog.

*/

public void setNoMatchPrompt(DialogPrompt noMatchPrompt) {

numQuestionDialog.setNoMatchPrompt(noMatchPrompt);

}

/**

* @return prompt from underlying SingleResultQuestionDialog

*/

public DialogPrompt getNoMatchPrompt() {

return numQuestionDialog.getNoMatchPrompt();

}

Note that the standard semantics for the property getters and setters is that a "null" value means use the default- so setting a property to null means use the default (if any), and a returned null means the default will be used.If defaults are being defined in the component, the methods must check for that:

/**

* @param DialogPrompt prompt to say when number out of range or null for default

*/

public void setNumberOutOfRangePrompt(DialogPrompt outOfRangePrompt){

if (outOfRangePrompt == null) {

outOfRangeMessageDialog.setPrompt(defaultOutOfRangePrompt);

} else {

outOfRangeMessageDialog.setPrompt(outOfRangePrompt);

}

}

/**

* @return DialogPrompt prompt to say when number out of range, null for default

*/

public DialogPrompt getNumberOutOfRangePrompt() {

DialogPrompt result = outOfRangeMessageDialog.getPrompt();

if (result == defaultOutOfRangePrompt) {

return null;

} else {

return result;

}

Page 37: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 37 of 97

}

Properties that are held as member variables on the NumberDialog are exposed too, and setters maycontain any assertions required (actual user-friendly validation of values is done in the DialogStepwrapper, however):

/**

* Returns the maximum number to be accepted by the caller, or null if the

* default is to be used.

*/

public Long getMaxNumberSize() {

return maxNumberSize;

}

/**

* Sets the maximum number to be accepted from the caller; any numbers

* above this will be rejected, with a message informing the caller, and

* they will be re-prompted. Setting this to null will mean the default it

* used.

* <p>

* A call to this results in the default out-of-range prompt being reset.

*/

public void setMaxNumberSize(Long maxNumberSize) {

// Check it's not too big (we only support up to 1 million currently) or small

if(maxNumberSize!=null && maxNumberSize.longValue()>DEFAULT_MAX_NUM_SIZE) {

throw new IllegalArgumentException(

"The NumberDialog does not support numbers larger than 1 million.");

}

else if(maxNumberSize!=null && maxNumberSize.longValue()<0) {

throw new IllegalArgumentException(

"The NumberDialog only supports positive numbers.");

}

else {

this.maxNumberSize = maxNumberSize;

// Set up the out-of-range prompt again.

setupDefaultOutOfRangePrompt();

}

}

Page 38: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 38 of 97

See Appendices A, B and C for a listing for all the codes for all the components, including all theproperty methods. Once these are added, all that remains are the methods called in the above codeto set up the grammar and out-of-range prompt as per the configuration properties.

Note that many VoiceDialog implementations would not have such dependent data, and everything would besimply set up in the constructor or exposed as properties.

/**

* Creates the default out-of-range prompt to play to the user if they say

* an invalid number. The default prompt tells them what the minimum and

* maximum numbers are.

*/

private void setupDefaultOutOfRangePrompt() {

// A very simple TTS prompt by default.

defaultOutOfRangePrompt = new BasicDialogPrompt(

"Sorry, please say something between "

+ getActualMinNumberSize()

+ " and "

+ getActualMaxNumberSize());

}

/**

* Resets the grammar for the question to the currently set NumberStyle.

*/

private void resetGrammar() {

NumberStyle currentStyle = getActualNumberStyle();

if(NumberStyle.DIGIT_BY_DIGIT.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

SPELT_OUT_RULE_NAME,

SPELT_OUT_GRAMMAR_PATH,

new String[] { SLOT_NAME }));

}

else if (NumberStyle.NATURAL.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

NATURAL_RULE_NAME,

NATURAL_GRAMMAR_PATH,

Page 39: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 39 of 97

new String[] { SLOT_NAME }));

}

else if(NumberStyle.MIXED.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

MIXED_RULE_NAME,

MIXED_GRAMMAR_PATH,

new String[] { SLOT_NAME }));

}

else {

throw new IllegalStateException("Unknown NumberStyle set on the NumberDialog.");

}

}

When the implementation of the VoiceDialog is complete, you can wrap it in a DialogStep for use inthe Configurator.

Creating a DialogStep

To expose the NumberDialog to the SAB system, so that it may be used in applications alongsideother components and configured in the Configurator, a DialogStep must be created. This class,called NumberDialogStep, will be built according to the steps outlined in the “DialogStepImplementation and Lifecycle” section of this document.

The step allows the Configurator user to set the range of numbers to allow the caller to enter, variousprompts for the different possible circumstances, and a folder variable for the caller's answer.

First, a number of constants and instance variables are defined for use in the step class:

// Descriptions of this step.

private static final String STEP_NAME = "Number Dialog Step";

private static final String STEP_DESCRIPTION =

"Gathers a number from the user, handling confirmation and disambiguation.";

private static final String STEP_CATEGORY = "Sample Steps";

private static final String STEP_IMAGE = "images/nouns/noun_number.png";

// Pages for the configuration properties

private static final String PROMPTS_PAGE = "Prompts";

private static final String NUMBER_OUT_OF_RANGE_PAGE = "Number Out of Range";

private static final String CHOICES_AND_RESULT_PAGE = "Choices and Result";

// Prompt specifying the question to ask the user.

private static final String QUESTION_TO_ASK_PROMPT_NAME = "QuestionToAskPrompt";

Page 40: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 40 of 97

private static final String QUESTION_TO_ASK_PROMPT_DESCRIPTION = "The question toask";

private String questionTTS;

private WavDBODataType questionWav;

// Prompt specifying that the number spoken is out of the allowed range.

private static final String NUMBER_OUT_OF_RANGE_PROMPT_NAME ="NumberOutOfRangePrompt";

private static final String NUMBER_OUT_OF_RANGE_PROMPT_DESCRIPTION = "Number outof range prompt";

private String invalidNumberTTS;

private WavDBODataType invalidNumberWav;

// The lower bound of the allowed range of numbers.

private static final String RANGE_FROM_NAME = "RangeFrom";

private static final String RANGE_FROM_DESCRIPTION = "Allowed numbers range from";

private BigDecimal rangeFrom;

// The upper bound of the allowed range of numbers.

private static final String RANGE_TO_NAME = "RangeTo";

private static final String RANGE_TO_DESCRIPTION = "Allowed numbers range to";

private BigDecimal rangeTo;

// The variable in the folder to put the result into.

private static final String RESULT_REF_NAME = "ResultantVariable";

private static final String RESULT_REF_DESCRIPTION = "Variable for the Gathered Result";

private String resultantVar;

Using the constants defined, the Step Definition is defined next, along with the getStepDescription()method:

/**

* Creates the definition of this step and its configuration properties.

*/

public RepositoryComponent createStepTypeDefinition(Document jarFile)

throws BusinessRuleException {

// Overall step definition

RepositoryComponent stepDef =

Page 41: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 41 of 97

new RepositoryComponent(

STEP_NAME,

STEP_DESCRIPTION,

this.getClass(),

STEP_IMAGE,

jarFile,

STEP_CATEGORY,

6);

// Add the main question prompt

stepDef.addField(

QUESTION_TO_ASK_PROMPT_NAME,

QUESTION_TO_ASK_PROMPT_DESCRIPTION,

true, PromptDBODataType.class);

stepDef.getDboDefinition().getValueDefinition(QUESTION_TO_ASK_PROMPT_NAME)

.setPage(PROMPTS_PAGE);

// Add the confirmation prompt (as standard)

addConfirmation(stepDef);

// Add the additional no-input and no-match prompts (as standard)

addAdditionalPrompts(stepDef);

// Add the number out-of-range prompt

stepDef.addField(

NUMBER_OUT_OF_RANGE_PROMPT_NAME,

NUMBER_OUT_OF_RANGE_PROMPT_DESCRIPTION,

true, PromptDBODataType.class);

stepDef.getDboDefinition()

.getValueDefinition(NUMBER_OUT_OF_RANGE_PROMPT_NAME)

.setPage(NUMBER_OUT_OF_RANGE_PAGE);

// Add the number range lower and upper bounds

stepDef.getDboDefinition().addField(

RANGE_FROM_NAME,

RANGE_FROM_DESCRIPTION,

Page 42: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 42 of 97

true, false,

DBODataTypeFactory.DECIMAL_NUMBER_CLASS);

stepDef.getDboDefinition()

.getValueDefinition(RANGE_FROM_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

stepDef.getDboDefinition().addField(

RANGE_TO_NAME,

RANGE_TO_DESCRIPTION,

true, false,

DBODataTypeFactory.DECIMAL_NUMBER_CLASS);

stepDef.getDboDefinition()

.getValueDefinition(RANGE_TO_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

// Add the resultant variable reference

stepDef.getDboDefinition().addVariableField(

RESULT_REF_NAME,

RESULT_REF_DESCRIPTION,

true, false,

SchemaUtils.ALL_XSD_NUMBER_TYPES,

true, "xsd:decimal");

stepDef.getDboDefinition()

.getValueDefinition(RESULT_REF_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

return stepDef;

}

/**

* Returns a human-readable description of this step.

*/

public String getStepDescription() {

return STEP_DESCRIPTION;

}

The initStep() method is defined next, and works in a similar way to the doInit() method in Processand Rule components. Here the various configuration data of the step, stored as instance data in theNumberDialogStep class, is set based on the StepDefinition passed in.

Page 43: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 43 of 97

/**

* Initialises the Dialog Step, performing any configuration required and

* validating any input from the user.

*/

protected void initStep(StepDefinition def) throws StepInitializationException {

if (LOG.isDebugEnabled()) {

LOG.debug("Initializing Number Dialog step");

}

// Get values

PromptDBODataType prompt = getPrompt(QUESTION_TO_ASK_PROMPT_NAME);

if (prompt == null) {

try {

def.setValue(QUESTION_TO_ASK_PROMPT_NAME,

(((BasicDialogPrompt)

NumberDialog.DEFAULT_QUESTION_PROMPT).getScript()));

}

catch (BusinessRuleException e) {

throw new StepInitializationException("The question to ask is unassigned.");

}

}

questionTTS = prompt.getTTSPrompt();

questionWav = prompt;

prompt = getPrompt(NUMBER_OUT_OF_RANGE_PROMPT_NAME);

if(prompt != null){

numberOutOfRangeTTS = prompt.getTTSPrompt();

numberOutOfRangeWav = prompt;

}

rangeFrom = (BigDecimal) def.getValue(RANGE_FROM_NAME);

rangeTo = (BigDecimal) def.getValue(RANGE_TO_NAME);

resultantVar = (String) def.getValue(RESULT_REF_NAME);

if (resultantVar == null) {

Page 44: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 44 of 97

throw new StepInitializationException("The resultant variable must be selected");

}

}

This configures the Dialog Step in a similar way to that of Process Steps and introduces theNumberDialog that it wraps. The first method to define creates the NumberDialog instance, and will becalled at startup time:

/**

* Creates the wrapped NumberDialog object.

*/

protected Dialog createDialog() {

return new NumberDialog();

}

The next method configures that NumberDialog based on the configuration instance data in this Step.The properties on the NumberDialog are all set using the JavaBean property setter methods:

/**

* Configures the NumberDialog component, based on the configuration options

* stored as instance data in this NumberDialogStep.

*/

protected void configureDialog(Dialog dialog) {

// Cast the Dialog to NumberDialog.

NumberDialog numberDialog = (NumberDialog) dialog;

// Set up the main question prompt.

String wavUrl = null;

if(questionWav != null && questionWav.hasAudioFile()){

wavUrl = generateWAVDataURL(QUESTION_TO_ASK_PROMPT_NAME, 0);

}

if(questionTTS!= null || (questionWav != null && questionWav.hasAudioFile())){

numberDialog.setQuestionPrompt(new BasicDialogPrompt(questionTTS, wavUrl));

}

// Set up the confirmation prompt (using base class functionality).

numberDialog.setConfirmationPrompt(getConfirmationPrompt(new NumberDialogPrompt()));

// Set up the out-of-range prompt.

wavUrl = null;

Page 45: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 45 of 97

if(numberOutOfRangeWav != null && numberOutOfRangeWav.hasAudioFile()){

wavUrl = generateWAVDataURL(NUMBER_OUT_OF_RANGE_PROMPT_NAME , 0);

}

if (rangeFrom != null) {

numberDialog.setMinNumberSize(new Long(rangeFrom.longValue()));

}

if (rangeTo != null) {

numberDialog.setMaxNumberSize(new Long(rangeTo.longValue()));

}

if (numberOutOfRangeTTS != null ||

(numberOutOfRangeWav != null && numberOutOfRangeWav.hasAudioFile())) {

ConcatenatedDialogPrompt prompt = new ConcatenatedDialogPrompt();

prompt.addPrompt(

new BasicDialogPrompt(numberOutOfRangeTTS, wavUrl));

prompt.addPrompt(

new BasicDialogPrompt(" from " + numberDialog.getMinNumberSize()));

prompt.addPrompt(

new BasicDialogPrompt(" to " + numberDialog.getMaxNumberSize()));

numberDialog.setNumberOutOfRangePrompt(prompt);

}else{

numberDialog.setNumberOutOfRangePrompt(null);

}

// Set up no-input/no-match/etc.

numberDialog.setNoInputPrompt(getNoInputPrompt());

numberDialog.setNoMatchPrompt(getNoMatchPrompt());

numberDialog.setMaxReprompt(getMaxReprompts());

// Set up thresholds for acceptance, etc.

numberDialog.setAcceptanceConfidenceThreshold(getAcceptanceThreshold());

numberDialog.setAcceptanceConfidenceMargin(getAcceptanceMargin());

}

At this stage we have the component configured and ready for use. All that is left is to define runtimebehaviour - how the NumberDialog is started and what happens when it has finished. To start it, asimple StartRequest is passed in. No dynamic data is currently taken from the folder:

/**

Page 46: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 46 of 97

* Produces a new request to pass to the NumberDialog. Since it takes no

* dynamic data from the folder, a new NumberDialog.StartRequest is simply

* returned.

*/

protected DialogRequest convertToRequest(DialogFolder info) {

return new NumberDialog.StartRequest(this);

}

When the NumberDialog has issued a response, depending on the type of response, some data maybe inserted into the folder:

/**

* Receives a response given by the NumberDialog, and adds any result to the

* folder. A result is only given in the event that there is a

* NumberGatheredResponse issued by the NumberDialog, in which case the

* number is added to the folder at the location the Configurator user has

* specified.

*/

protected void addResultToFolderData(DialogFolder folder, DialogResponse response) {

if (response instanceof NumberDialog.NumberGatheredResponse) {

NumberDialog.NumberGatheredResponse answer =

(NumberDialog.NumberGatheredResponse) response;

if (LOG.isDebugEnabled()) {

LOG.debug("Answer : " + answer);

}

// Set the result in the folder.

try {

folder.setValueAt(resultantVar, answer.getNumber().toString());

}

catch (InvalidLocationException e) {

ExceptionService.of().handleException(e);

}

}

}

Implementing a full Dialog component is more time consuming than a Process Step or a Rule, but thatreflects the fact that Dialog components are generally more complex.

Page 47: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 47 of 97

VoiceDialog Properties

The Voice Dialog Properties expose finer settings like BargeIn, Endpointing, and Input ModesAllowed, to control the interaction. The SingleResultQuestionDialog supports these settings on allinteractions namely Main Question, Confirmation, NoInput, NoMatch and MisRecognition. On UI,these settings are exposed by VoicePropertiesDBODataType.

Testing Dialog Components

JUnit Test Cases written for Dialog Wrappers (Steps)/Process Steps

The purpose of writing test cases for Wrappers is to detect any step configuration errors during stepinitialization, and to check all folder variables required to run the step.

Currently, all the test cases for wrappers have pre-requisites attached to them.

To write a test case for wrappers, the following files need to be in place:

• A Simulator for the Dialog

• A Test Case for the Simulator

An AbstractDialogStepTestCase is written for the Wrapper Test Case. All test cases need toextend this abstract class to get the desired test functionality.

AbstractDialogStepTestCase has the following methods:

• execute (Dialog Simulator):

This method needs to be called to simulate the execution of the step and hence, requires a DialogSimulator.

• AddStepData:

Adds the data to the step, to configure any type of fields in the step.

• AddStepArrayTypeData:

Adds the data to the step, to configure array type fields in the step.

• AddFolderData

Adds the data to the folder that is required to execute the step.

• AddFolderElement

Adds the data to the folder for user-defined data type elements.

A wrapper test case that extends this abstract class must include a set of methods that would validatethe step when all mandatory fields are provided to it.

For each mandatory field, a test method must be written, where all the fields except the mandatoryfield should be provided. On execution of this test method, a Step Initialization Exception must bethrown (for example, if there are 4 valid mandatory fields, there must be 4 test methods to check theexistence of these fields).

In addition to that, there must be methods that test the functionality of the step along with the dialog.This is done by adding folder data to the step. In the case of erroneous folder inputs, you must checkfor a business process exception.

Page 48: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 48 of 97

All test cases follow the same pattern with varying inputs and step initialization settings.

Simulators for wrappers, test cases for the simulators and test cases for the wrappers are currentlybeing placed in the following package:

src_tests /test/com/netdecisions/components/dialog/nameofcomponent

An AbstractStepTestCase is written for the Process Step Test Case. All test cases need to extendthis abstract class to get the desired test functionality.

Most of the methods in this abstract test class are the same as the abstract dialog test class.

Besides the methods used for validating the steps for initialization and providing inputs to folder data,there is an important difference between writing a test case for a wrapper (i.e. step for the dialog) andfor a process step. The test cases for the wrappers check all responses from the dialog.

Test cases for the process steps are currently being placed in the following package:

src_tests /test/com/netdecisions/components/step/nameofcomponent.

Test the dialog flow

You need to test the dialog flow using the SAB tool.

Page 49: DD Avaya Component Developer Guide

Component Packaging and Deployment

Once your component has been written, it needs to be packaged so that it can be used within a SABapplication.

Packaging the Component In a Jar Archive

For components to be made available for use within the SAB Configurator they must be packagedwithin a standard JAR archive. It recognizes steps by the interface they implement and SAB does notrequire any special structure for the archive.

Several components may be packaged within the same archive. When the JAR file is imported, SABscans it, and any components within in it are recognized.

Example of an Ant Build Script

A standard approach to automating the build process is to use the Ant tool from Jakarta. A samplebuild script for building a component archive JAR is given below and it assumes the followingdirectory structure:

/SampleProject Project root directory

/java Location of build script

/dist Destination for distributable JAR file

/classes Destination for Class files

/lib Location of library JAR files

/src Location of source files

The Ant script contains targets to compile the classes ("compile") and to build the JAR archive ("jar"):

<?xml version="1.0" encoding="UTF-8"?>

<project basedir="." default="jar" name="sample-project">

<!-- Locations of various directories. -->

<property name="build.directory" value="classes"/>

<property name="source.directory" value="src"/>

<property name="lib.directory" value="lib/"/>

<!-- Definition of classpath. -->

<path id="classpath">

Page 50: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 50 of 97

<fileset dir="${lib.directory}"/>

</path>

<!-- Details for the JAR archive to build. -->

<property name="jar.directory" value="dist"/>

<property name="jar.name" value="${ant.project.name}.jar"/>

<property name="jar.basedirectory" value="${build.directory}"/>

<!-- Initialise the script. -->

<target name="init">

<tstamp/>

</target>

<!-- Perform a clean build of the classes. -->

<target name="compile" depends="init">

<delete dir="${build.directory}"/>

<mkdir dir="${build.directory}"/>

<javac destdir="${build.directory}" srcdir="${source.directory}">

<classpath refid="classpath"/>

</javac>

</target>

<!-- Build the JAR archive. -->

<target name="jar" depends="init, compile">

<mkdir dir="${jar.directory}"/>

<jar jarfile="${jar.directory}/${jar.name}" basedir="${jar.basedirectory}"/>

</target>

</project>

When the "jar" target is run in this script, a JAR archive called "sample-project.jar" is created in the"dist" directory.

Page 51: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 51 of 97

Using the Component in the Configurator

See the SAB Configurator User Guide for details on deploying the component in the Configurator.

Upgrading Configuration Data and Component Versions

During the lifecycle of a component, it is upgraded several times. During most of these upgrades, thefunctionality of the component changes, but the configuration remains the same. This kind of upgradeis handled by the platform and all you have to do is increment the version number. The platform evencopies data from the old version to the new version.

At times, the configuration of the component must be changed, like adding or removing configurationsettings. This is also handled by the platform. However, when you need to change variable names ortheir data types, the conversion between the old and the new values must be explicit. DialogStepimplements an interface called upgradable which defines a single method calledupgradeComponentData(DBODefinitionn data). You must override this method to define theconversion path.

Page 52: DD Avaya Component Developer Guide

Appendix A: Process Step Example and Code

Example Code for Process Component

The sample below illustrates the construction of a simple Process Step. This Step permits the SABConfigurator user to add two variables together or add a variable to a constant.

The Step involves taking an existing number type (xsd:decimal schema from the Folder), whichserves as a variable in this case, and adding either another xsd:decimal schema type from the Folder(again, another variable) or a constant defined by the Configurator user. The option of using either avariable from the Folder or a constant value defined by the user is determined by the use of a Booleantype (xsd:boolean).

Our class will extend AbstractStep (in package com.netdecisions.ccp.model.bp). The steps forproducing the concrete implementation class are given here. First, a number of constants andinstance variables are defined for use throughout the class:

Figure 8. Edit Step – Addition Step

// Descriptions of this step.

private static final String STEP_NAME = "Addition Step";

private static final String STEP_CATEGORY = "Sample Steps";

private static final String STEP_DESCRIPTION =

Page 53: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 53 of 97

"Takes two numbers from the folder, adds them, then puts the result in the folder.”

private static final String STEP_IMAGE = "images/nouns/noun_number.png";

// The first input reference for the calculation.

private static final String FIRST_INPUT_REF_NAME = "FirstInputRef”;

//This is the variable name used by the component.

private static final String FIRST_INPUT_REF_DESCRIPTION = "First Input Variable";

//This is the display label used on GUI.

private String firstInputRef;

// This is the actual value of the variable.

// The second input reference for the calculation.

private static final String SECOND_INPUT_REF_NAME = "SecondInputRef";

private static final String SECOND_INPUT_REF_DESCRIPTION = "Second Input Variable";

private String secondInputRef;

// The reference to put the result into.

private static final String RESULT_REF_NAME = "ResultRef";

private static final String RESULT_REF_DESCRIPTION = "Variable For the Result";

private String resultRef;

The constants here define the metadata for the component, and references to configuration propertiesit exposes. The instance variables will hold the actual values of the configuration properties, since theconfiguration applies to an instance of the Step. In normal circumstances, any variables defined at theinstance scope should not be updated at runtime.

The description of the Step Type Definition is given next:

/**

* Creates the definition of this step, and its configuration properties.

*/

public RepositoryComponent createStepTypeDefinition(Document jarFile)

throws BusinessRuleException {

// Overall step definition

RepositoryComponent stepDefinition =

new RepositoryComponent(

STEP_NAME,

Page 54: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 54 of 97

STEP_DESCRIPTION,

this.getClass(),

STEP_IMAGE,

jarFile,

STEP_CATEGORY,

3);

// Input variables

stepDefinition.addVariableField(

FIRST_INPUT_REF_NAME,

FIRST_INPUT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES);

stepDefinition.addVariableField(

SECOND_INPUT_REF_NAME,

SECOND_INPUT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES);

// Result variable – this has changed – RT to provide the changes

stepDefinition.addNewOrExistingVariableField(

RESULT_REF_NAME,

RESULT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES,

"xsd:" + SchemaUtils.XSD_DECIMAL);

return stepDefinition;

}

The first part of this method defines the generic metadata for the component. This covers thefollowing:

• the human-readable name for the step

Page 55: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 55 of 97

• a longer description of the functionality provided

• the class file providing the step (i.e. this class)

• an image file to provide a graphical representation of this step

• the JAR file containing this step (passed into the method)

• the category for the step. (It is a string that identifies the category of the component. For example,a project name, or a system component).

• The version number of the current component implementation – this should be updated as furtherreleases are made, and will be used by the Configurator to notify users that components havebeen upgraded. Failure to change will prevent importing.

The second part of this method specifies any variables that need to be exposed for configuration. Foreach, you can specify data such as:

• the name of the variable

• a human-readable description of it

• whether it is required

• whether it is an array or a single value

• the type of the variable

For variables referring to positions in the folder, the XSD Schema type of those variables allowed maybe specified, and depending on which add method you call, whether the variable is pre-existing(typically for input variables) or whether it is new (typically allowed for output variables). See handlingarrays, and complex data types from the folder for more details.

The jarFile parameter is the Document in which this component lives. This method is only calledon an import, and it identifies the source of this component. That JAR file will then be associated withthis component in the repository and when someone wishes to use the component, the platform willpull the class file from the Document object passed into this method.

The variables whose values will be added together are defined next. These variables take theirnames from the FIRST_INPUT_REF_NAME and SECOND_INPUT_REF_NAME constants.

Finally, the resultant variable is defined. This holds the result of the calculation that may be an existingfolder variable of XML session data or a new one defined by the Configurator user. It is declared inthe same way as the input variables, but additional arguments indicate it may be a new variable, andspecify the exact type.

The other metadata methods are defined next:

/**

* Returns a human-readable description of this step.

*/

public String getStepDescription() {

return STEP_DESCRIPTION;

}

/**

* Doesn't need any environment properties.

*/

Page 56: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 56 of 97

public String[] getRequiredEnvironmentProperties() {

return new String[0];

}

The next method to define is doInit():

/**

* Initialises the step, performing any configuration required and

* validating any properties specified by the user.

*/

protected void doInit(StepDefinition def)

throws StepInitializationException {

// Get the values for the variable references, from the step definition

firstInputRef = (String)def.getValue(FIRST_INPUT_REF_NAME);

secondInputRef = (String)def.getValue(SECOND_INPUT_REF_NAME);

resultRef = (String)def.getValue(RESULT_REF_NAME);

// Check that everything that is required has been specified.

if(firstInputRef==null) {

throw new StepInitializationException("First input variable must be specified.");

}

if(secondInputRef==null) {

throw new StepInitializationException("Second input variable must be specified.");

}

if(resultRef==null) {

throw new StepInitializationException("Result variable must be specified.");

}

}

The purpose of this method is to initialize the component with information provided by theConfigurator user. A part of the initialization is to verify as much of the component’s configuration aspossible. The platform uses this method to constantly validate the component as set up by the user,whenever it is changed. When the user attempts to validate the dialog flow, this method is invoked onall the Steps in the flow.

SAB takes the RepositoryComponent and creates a StepDefinition. It creates a user interface panelfor the RepositoryComponent, allowing the Configurator interface user to select and/or enter thevariables and values to customise the component for use in the dialog flow. As the user customizesthe component, the Configurator takes the submitted values and fills the StepDefinition asappropriate. In the doInit() method the component retrieves the parameters and validates them. Anycondition that does not satisfy the component should throw a StepInitializationException.

Page 57: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 57 of 97

Once the component is configured correctly, all that remains is to define the process() method thatactually performs the function of this step:

/**

* Performs the calculation.

*/

public void process(Folder folder, RunnableStepAssignment arg1)

throws BusinessProcessException {

// Extract the numbers from the folder

Double firstNum = new Double((String)folder.getElement(firstInputRef));

Double secondNum = new Double((String)folder.getElement(secondInputRef));

// Do the addition

Double result = new Double(firstNum.doubleValue() + secondNum.doubleValue());

// Set the result back in the folder

try {

folder.setValueAt(resultRef, result.toString());

}

catch(InvalidLocationException e) {

throw new BusinessProcessException("Unable to save result to folder.", e);

}

}

This method is called by the SAB runtime, made possible by multiple concurrent threads. An exampleis shown below:

• Fetch the input variables from the folder from locations specified by the component’s configurationusing XPath and XML.

• Perform the addition calculation, to produce a new variable.

• Set the resultant variable on the folder XML at an XPath location specified by the component’sconfiguration

If any problem occurs during the processing of this Step, a BusinessProcessException should bethrown with details (and possibly any causing exception).

Page 58: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 58 of 97

Process Component Example Code

package examples.fluencyvoice.process.addition;

import java.util.Hashtable;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

import com.netdecisions.agility.fw.kernel.common.BusinessRuleException;

import com.netdecisions.ccp.BusinessProcessException;

import com.netdecisions.ccp.StepInitializationException;

import com.netdecisions.ccp.dbo.bo.DBODefinition;

import com.netdecisions.ccp.model.bp.AbstractStep;

import com.netdecisions.ccp.model.bp.RunnableStepAssignment;

import com.netdecisions.ccp.model.bp.StepDefinition;

import com.netdecisions.ccp.model.bp.folder.Folder;

import com.netdecisions.ccp.model.bp.folder.InvalidLocationException;

import com.netdecisions.ccp.model.bp.folder.SchemaUtils;

import com.netdecisions.ccp.repository.bo.RepositoryComponent;

import com.netdecisions.ccp.repository.bo.document.Document;

/**

* A simple addition process step.

* <p>

* Takes two numeric values from the folder, adds them together, then puts the

* result back into the folder.

* <p>

* Allows the user to specify the location in the folder for the variables

* involved.

*/

public class AdditionStep extends AbstractStep {

// Descriptions of this step.

private static final String STEP_NAME = "Addition Step";

private static final String STEP_CATEGORY = "Sample Steps";

Page 59: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 59 of 97

private static final String STEP_DESCRIPTION = "Takes two numbers from the folder, adds themthen puts the result in the folder.";

private static final String STEP_IMAGE = "images/nouns/noun_number.png";

// The first input reference for the calculation.

private static final String FIRST_INPUT_REF_NAME = "FirstInputRef";

private static final String FIRST_INPUT_REF_DESCRIPTION = "First Input Variable";

private String firstInputRef;

// The second input reference for the calculation.

private static final String SECOND_INPUT_REF_NAME = "SecondInputRef";

private static final String SECOND_INPUT_REF_DESCRIPTION = "Second Input Variable";

private String secondInputRef;

// The refernce to put the result into.

private static final String RESULT_REF_NAME = "ResultRef";

private static final String RESULT_REF_DESCRIPTION = "Variable For the Result";

private String resultRef;

/**

* Constructor does nothing for this step.

*/

public AdditionStep() {

super();

}

/**

* Returns a human-readable description of this step.

*/

public String getStepDescription() {

return STEP_DESCRIPTION;

}

/**

* Creates the definition of this step, and its configuration properties.

*/

Page 60: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 60 of 97

public RepositoryComponent createStepTypeDefinition(Document jarFile)

throws BusinessRuleException {

// Overall step definition

RepositoryComponent stepDefinition =

new RepositoryComponent(

STEP_NAME,

STEP_DESCRIPTION,

this.getClass(),

STEP_IMAGE,

jarFile,

STEP_CATEGORY,

3);

// Input variables

stepDefinition.addVariableField(

FIRST_INPUT_REF_NAME,

FIRST_INPUT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES);

stepDefinition.addVariableField(

SECOND_INPUT_REF_NAME,

SECOND_INPUT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES);

// Result variable

stepDefinition.addNewOrExistingVariableField(

RESULT_REF_NAME,

RESULT_REF_DESCRIPTION,

true,

false,

SchemaUtils.ALL_XSD_NUMBER_TYPES,

"xsd:" + SchemaUtils.XSD_DECIMAL);

Page 61: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 61 of 97

return stepDefinition;

}

/**

* Doesn't need any environment properties.

*/

public String[] getRequiredEnvironmentProperties() {

return new String[0];

}

/**

* Initialises the step, performing any configuration required and

* validating any properties specified by the user.

*/

protected void doInit(StepDefinition def)

throws StepInitializationException {

// Get the values for the variable references, from the step definition

firstInputRef = (String)def.getValue(FIRST_INPUT_REF_NAME);

secondInputRef = (String)def.getValue(SECOND_INPUT_REF_NAME);

resultRef = (String)def.getValue(RESULT_REF_NAME);

// Check that everything that is required has been specified.

if(firstInputRef==null) {

throw new StepInitializationException("First input variable must be specified.");

}

if(secondInputRef==null) {

throw new StepInitializationException("Second input variable must be specified.");

}

if(resultRef==null) {

throw new StepInitializationException("Result variable must be specified.");

}

}

Page 62: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 62 of 97

/**

* Performs the calculation.

*/

public void process(Folder folder, RunnableStepAssignment arg1)

throws BusinessProcessException {

// Extract the numbers from the folder

Double firstNum = new Double((String)folder.getElement(firstInputRef));

Double secondNum = new Double((String)folder.getElement(secondInputRef));

// Do the addition

Double result = new Double(firstNum.doubleValue() + secondNum.doubleValue());

// Set the result back in the folder

try {

folder.setValueAt(resultRef, result.toString());

}

catch(InvalidLocationException e) {

throw new BusinessProcessException("Unable to save result to folder.", e);

}

}

}

Page 63: DD Avaya Component Developer Guide

Appendix B: Rule Component Example and Code

Rule Component Example

In this section, an example Rule will be constructed to illustrate the steps needed to build a Ruleclass. The rule will examine a text Folder variable specified by the Configurator user, and will evaluatewhether it contains a sub-string specified by the user. If it does contain the sub-string, it will returntrue, otherwise false.

Our class will extend AbstractRule (in package com.netdecisions.ccp.model.rule); the steps inproducing the concrete implementation class are given here. First, a number of constants andinstance variables are defined for use throughout the class:

// Descriptions of this rule.

private static final String RULE_NAME = "String Contains Rule";

private static final String RULE_CATEGORY = "Sample Steps";

private static final String RULE_DESCRIPTION =

"Sees if a string variable in the folder contains a specified sub-string.";

private static final String RULE_IMAGE = "images/rules/rule_boolean.gif";

// The folder variable reference for the comparison.

private static final String VARIABLE_REF_NAME = "Variable to Examine";

private static final String VARIABLE_REF_DESC = "Variable from the folder to be examined.";

private String variableRef;

// The substring value to look for.

private static final String SUB_STR_NAME = "Required substring";

private static final String SUB_STR_DESC = "The string you're looking for inside the variable.";

private String subStr;

As with the process step, the constants here define metadata for the component and references toconfiguration properties. The instance variables will hold the configuration of the rule instance.

The Rule Type Definition is given next:

/**

* Creates the definition of this rule, and its configuration properties.

*/

public RepositoryComponent createRuleTypeDefinition(Document jarFile)

throws BusinessRuleException {

Page 64: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 64 of 97

// Overall rule definition.

RepositoryComponent ruleTypeDefinition = new RepositoryComponent(

RULE_NAME,

RULE_DESCRIPTION,

this.getClass(),

RULE_IMAGE,

jarFile,

RULE_CATEGORY,

1);

// Variable from the folder for examination.

ruleTypeDefinition.addVariableField(

VARIABLE_REF_NAME,

VARIABLE_REF_DESC,

true,

false,

SchemaUtils.ALL_XSD_TYPES);

// Value of the substring to look for.

ruleTypeDefinition.addField(

SUB_STR_NAME,

SUB_STR_DESC,

true,

StringDBODataType.class);

return ruleTypeDefinition;

}

The code given here is essentially the same as for the Process Step. The metadata of the overallcomponent is populated in the first part of the method, and the two configuration properties are addedto the definition.

Note: The first of these is a Folder variable while the second is a static value field simply set directly by theConfigurator user.

You must specify the description methods next and define three of the Rules. There is a descriptionfor the Rule component, and one for each of the cases which would true and false when it isevaluated.

Page 65: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 65 of 97

/**

* Returns a human-readable description of this rule.

*/

public String getRuleDescription() {

return RULE_DESCRIPTION;

}

/**

* Returns a human-readable description of this rule when it is evaluated to

* false.

*/

public String getRuleDescriptionWhenFalse() {

return "Sub-string '" + subStr + "' isn't contained.";

}

/**

* Returns a human-readable description of this rule when it is evaluated to

* true.

*/

public String getRuleDescriptionWhenTrue() {

return "Sub-string '" + subStr + "' is contained.";

}

Note how the methods can be customised to return the current configuration settings as part of the descriptivetext.

The doInit() method is defined next, and it initialises the Rule with any configuration set by the user(and validates that configuration). This works exactly the same as for Process components.

/**

* Initialises the rule, performing any configuration required and

* validating any properties specified by the user.

*/

protected void doInit(RuleDefinition ruleDefinition)

throws RuleInitializationException {

// Setup the variable key into the folder.

Page 66: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 66 of 97

variableRef = (String)ruleDefinition.getValue(VARIABLE_REF_NAME);

if(variableRef==null) {

throw new RuleInitializationException("You must specify a variable in the folder forexamination.");

}

// Setup the constant value for comparison.

subStr = (String)ruleDefinition.getValue(SUB_STR_NAME);

if(subStr==null) {

throw new RuleInitializationException("You must specify the value for the requiredsubstring.");

}

}

With the Rule now equipped for configuration and initialization, the doEvaluate() method remains. Thisperforms the actual calculation for the rule, and will return a Boolean value depending on thatcalculation.

/**

* Evaluates the rule, returning true or false depending on the current

* conditions.

* <p>

* This will fetch the variable value from the folder, then return whether

* it contains the required substring.

*/

protected boolean doEvaluate(Folder folder, RunnableStepAssignment stepAssignment)

throws BusinessProcessException {

// Get the variable from the folder.

String variableVal = folder.getElement(variableRef).toString();

// Return whether it's equal to the value for comparison.

return (variableVal.indexOf(subStr)>=0);

}

The first line retrieves the String variable from the Folder and the second checks if it contains therequired sub-string. As with the Process Step’s process() method, exceptions occurring within thismethod should be caught and re-thrown as a BusinessProcessException.

Page 67: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 67 of 97

Rule Component Code

package examples.fluencyvoice.rule;

import com.netdecisions.agility.fw.kernel.common.BusinessRuleException;

import com.netdecisions.ccp.BusinessProcessException;

import com.netdecisions.ccp.RuleInitializationException;

import com.netdecisions.ccp.dbo.bo.StringDBODataType;

import com.netdecisions.ccp.model.bp.RunnableStepAssignment;

import com.netdecisions.ccp.model.bp.folder.Folder;

import com.netdecisions.ccp.model.bp.folder.SchemaUtils;

import com.netdecisions.ccp.model.rule.AbstractRule;

import com.netdecisions.ccp.model.rule.RuleDefinition;

import com.netdecisions.ccp.repository.bo.RepositoryComponent;

import com.netdecisions.ccp.repository.bo.document.Document;

/**

* A rule for determining whether a given string variable in the folder contains

* a specified substring.

* <p>

* Allows the user to specify the location in the folder for the variable, and

* the required substring.

*/

public class StringContainsRule1 extends AbstractRule {

// Descriptions of this rule.

private static final String RULE_NAME = "String Contains Rule";

private static final String RULE_CATEGORY = "Sample Steps";

private static final String RULE_DESCRIPTION = "Sees if a string variable in the folder contains aspecified sub-string.";

private static final String RULE_IMAGE = "images/rules/rule_boolean.gif";

// The folder variable reference for the comparison.

private static final String VARIABLE_REF_NAME = "Variable to Examine";

private static final String VARIABLE_REF_DESC = "Variable from the folder to be examined.";

private String variableRef;

Page 68: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 68 of 97

// The substring value to look for.

private static final String SUB_STR_NAME = "Required substring";

private static final String SUB_STR_DESC = "The string you're looking for inside the variable.";

private String subStr;

/**

* Creates the definition of this rule, and its configuration properties.

*/

public RepositoryComponent createRuleTypeDefinition(Document jarFile)

throws BusinessRuleException {

// Overall rule definition.

RepositoryComponent ruleTypeDefinition = new RepositoryComponent(

RULE_NAME,

RULE_DESCRIPTION,

this.getClass(),

RULE_IMAGE,

jarFile,

RULE_CATEGORY,

1);

// Variable from the folder for examination.

ruleTypeDefinition.addVariableField(

VARIABLE_REF_NAME,

VARIABLE_REF_DESC,

true,

false,

SchemaUtils.ALL_XSD_TYPES);

// Value of the substring to look for.

ruleTypeDefinition.addField(

SUB_STR_NAME,

SUB_STR_DESC,

true,

StringDBODataType.class);

Page 69: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 69 of 97

return ruleTypeDefinition;

}

/**

* Returns a human-readable description of this rule.

*/

public String getRuleDescription() {

return RULE_DESCRIPTION;

}

/**

* Returns a human-readable description of this rule when it is enaluated to

* false.

*/

public String getRuleDescriptionWhenFalse() {

return "Sub-string '" + subStr + "' isn't contained.";

}

/**

* Returns a human-readable description of this rule when it is enaluated to

* true.

*/

public String getRuleDescriptionWhenTrue() {

return "Sub-string '" + subStr + "' is contained.";

}

/**

* Initializes the rule, performing any configuration required and

* validating any properties specified by the user.

*/

protected void doInit(RuleDefinition ruleDefinition)

throws RuleInitializationException {

// Setup the variable key into the folder.

variableRef = (String)ruleDefinition.getValue(VARIABLE_REF_NAME);

Page 70: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 70 of 97

if(variableRef==null) {

throw new RuleInitializationException("You must specify a variable in the folder forexamination.");

}

// Setup the constant value for comparison.

subStr = (String)ruleDefinition.getValue(SUB_STR_NAME);

if(subStr==null) {

throw new RuleInitializationException("You must specify the value for the requiredsubstring.");

}

}

/**

* Evaluates the rule, returning true or false depending on the current

* conditions.

* <p>

* This will fetch the variable value from the folder, then return whether

* it contains the required substring.

*/

protected boolean doEvaluate(Folder folder, RunnableStepAssignment stepAssignment)

throws BusinessProcessException {

// Get the variable from the folder.

String variableVal = folder.getElement(variableRef).toString();

// Return whether it's equal to the value for comparison.

return (variableVal.indexOf(subStr)>=0);

}

}

Page 71: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 71 of 97

Appendix C: Dialog Component Example and Code

Dialog Component Example

For the NumberDialog:

package com.fluencyvoice.dialog.number;

import java.io.Serializable;

import java.util.HashMap;

import java.util.Map;

import com.fluencyvoice.runner.core.AbstractDialogRequest;

import com.fluencyvoice.runner.core.AbstractDialogResponse;

import com.fluencyvoice.runner.core.Dialog;

import com.fluencyvoice.runner.core.DialogRequest;

import com.fluencyvoice.runner.core.DialogResponse;

import com.fluencyvoice.runner.dialog.voice.AbstractVoiceDialog;

import com.fluencyvoice.runner.dialog.voice.basic.MessageDialog;

import com.fluencyvoice.runner.dialog.voice.basic.SingleResultQuestionDialog;

import com.fluencyvoice.runner.dialog.voice.basic.SlotProcessor;

import com.fluencyvoice.runner.dialog.voice.prompt.BasicDialogPrompt;

import com.fluencyvoice.runner.dialog.voice.prompt.ConcatenatedDialogPrompt;

import com.fluencyvoice.runner.dialog.voice.prompt.DialogPrompt;

import com.fluencyvoice.runner.logging.Logger;

import com.fluencyvoice.runner.logging.LoggerFactory;

import com.fluencyvoice.runner.util.Assertions;

import com.fluencyvoice.runner.widget.voice.grammar.PredefinedGrammar;

import com.fluencyvoice.runner.widget.voice.grammar.VoiceGrammar;

/**

* The NumberDialog allows the collection of a single number from the caller. It

* currently recognises integers between zero and 1 million, and allows the

* constraint of the acceptable range within this interval.

* <p>

* The dialog is started by passing in a {@link NumberDialog.StartRequest},

Page 72: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 72 of 97

* which takes no special arguments.

* <p>

* The following responses may be issued, in addition to any defined in the base

* class:

* <ul>

* <li>{@link NumberDialog.NumberGatheredResponse}

* <li>{@link NumberDialog.NotAnsweredResponse}

* </ul>

*

* @author Mehbooblal Mujawar (Original)

* @author $Author: gponma $

* @version $Revision: 1.32 $ $Date: 2003/03/04 12:51:34 $

*/

public class NumberDialog extends AbstractVoiceDialog {

private static final Logger LOGGER = LoggerFactory.getLogger(NumberDialog.class);

/**

* Default question prompt to use if none is specified.

*/

private static final DialogPrompt DEFAULT_QUESTION_PROMPT =

new BasicDialogPrompt("Please say a number");

/**

* Here we build up the default confirmation prompt to use - it is a

* concatenation of a prompt to say the dynamic numbers, and the

* confirmation question.

*/

private static final ConcatenatedDialogPrompt DEFAULT_CONFIRMATION_PROMPT;

static {

DEFAULT_CONFIRMATION_PROMPT = new ConcatenatedDialogPrompt();

DEFAULT_CONFIRMATION_PROMPT.addPrompt(new NumberDialogPrompt());

DEFAULT_CONFIRMATION_PROMPT.addPrompt(new BasicDialogPrompt("is this correct?"));

}

/**

Page 73: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 73 of 97

* Default maximum number the user can enter. 1 million - set to the maximum

* possible.

*/

private static final long DEFAULT_MAX_NUM_SIZE = 1000000L;

/**

* Default minimum number the user can enter. Zero - set to the minimum

* possible.

*/

private static final long DEFAULT_MIN_NUM_SIZE = 0L;

/**

* The default number of times the user is allowed to say a number outside

* of the limits. If this is exceeded, a NotAnsweredResponse will be issued.

*/

private static final int DEFAULT_MAX_INVALID_NUMBER_COUNT = 1;

/**

* The default NumberStyle to allow the user. This is mixed - both digit-by-

* digit and natural styles are allowed.

*/

private static final NumberStyle DEFAULT_NUMBER_STYLE = NumberStyle.MIXED;

/**

* The session key under which to store the count of how many times the user

* has said an invalid number. This will be stored in the request scope.

*/

private static final String REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT ="invalidNumberCount";

// The slot used by the grammar to return the number chosen.

private static final String SLOT_NAME = "choice";

// The paths to the various grammars that may be used.

private static final String SPELT_OUT_GRAMMAR_PATH ="com/fluencyvoice/dialog/number/Number_SpeltOut";

Page 74: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 74 of 97

private static final String NATURAL_GRAMMAR_PATH ="com/fluencyvoice/dialog/number/Number_Natural";

private static final String MIXED_GRAMMAR_PATH ="com/fluencyvoice/dialog/number/Number_Mixed";

// The names of the various rules of the grammars that may be used.

private static final String SPELT_OUT_RULE_NAME = "ZeroToAMillion_SpeltOut";

private static final String NATURAL_RULE_NAME = "ZeroToAMillion_Natural";

private static final String MIXED_RULE_NAME = "ZeroToAMillion_Mixed";

/**

* The sub-dialog component that asks the user the question (with any

* confirmation, disambiguation as required).

*/

private SingleResultQuestionDialog numQuestionDialog;

/**

* The sub-dialog component that plays a message to the user if they say a

* message that's out of the range acceptable.

*/

private MessageDialog outOfRangeMessageDialog;

/**

* The default prompt to play to the user if they say a number outside the

* allowed range. This is recreated when the max and min values are updated,

* as it says 'please say a number between X and Y'.

* <p>

* See {@link #setupDefaultOutOfRangePrompt()}.

*/

private DialogPrompt defaultOutOfRangePrompt;

/**

* The minimum number that's acceptable from the user.

*/

private Long minNumberSize = new Long(0);

/**

Page 75: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 75 of 97

* The maximum number that's acceptable from the user.

*/

private Long maxNumberSize = new Long(DEFAULT_MAX_NUM_SIZE);

/**

* The NumberStyle that defines the ways the user can enter their number.

*/

private NumberStyle numberStyle = null;

/**

* Constructor. This constructs the sub-dialog components to be used by this

* component, registers them, then sets up the default properties of this

* dialog.

*/

public NumberDialog() {

// Create sub-components

numQuestionDialog = new SingleResultQuestionDialog();

outOfRangeMessageDialog = new MessageDialog();

// Register sub-components

registerSubDialog(numQuestionDialog);

registerSubDialog(outOfRangeMessageDialog);

// Setup the details of this dialog and the contained dialogs, as per defaults.

setupDefaultOutOfRangePrompt();

outOfRangeMessageDialog.setPrompt(defaultOutOfRangePrompt);

numQuestionDialog.setSlotProcessor(new NumberSlotProcessor());

numQuestionDialog.setQuestionPrompt(DEFAULT_QUESTION_PROMPT);

numQuestionDialog.setConfirmationPrompt(DEFAULT_CONFIRMATION_PROMPT);

resetGrammar();

}

/**

* Handles the StartRequest for this dialog component. This performs some

Page 76: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 76 of 97

* sanity checks on the configuration of the component's properties, then

* passes a request to the nested SingleResultQuestionDialog component, to

* ask the actual question.

*/

protected void doHandleRequest(DialogRequest request) {

// Check the request is of the correct type.

if (!(request instanceof StartRequest)) {

throw new IllegalArgumentException(

"Start with NumberDialog.StartRequest, but not a "

+ request.getClass());

}

// If assertions are enabled, check that the minimum value is actually under

// the maximum value.

if(Assertions.ON) {

Assertions.assertTrue(getActualMaxNumberSize() >= getActualMinNumberSize(),

"The Maximum allowed number (" + getActualMaxNumberSize() +

") is less than the Minimum allowed number (" + getActualMinNumberSize() +")");

}

// Pass a request to the nested question sub-dialog.

numQuestionDialog.handleRequest(new SingleResultQuestionDialog.StartRequest(this));

}

/**

* Handles responses sent to this dialog from the nested sub-dialogs. These

* will either be from the SingleResultQuestionDialog responsible for asking

* the question, or from the MessageDialog responsible for saying the out-

* of-bounds message.

*/

protected void doHandleResponse(DialogResponse response) {

// If the response is from the SingleResultQuestionDialog to ask the number...

Page 77: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 77 of 97

if (response.getSource().equals(numQuestionDialog)) {

// If it's an answer....

if (response instanceof SingleResultQuestionDialog.AnsweredResponse) {

SingleResultQuestionDialog.AnsweredResponse result =(SingleResultQuestionDialog.AnsweredResponse) response;

if (Assertions.ON)

Assertions.assertTrue(result != null);

Long resultValue = (Long) result.getResult();

// Check that it's within the allowed range

if (resultValue.longValue() > getMaxNumberSize().longValue()

|| resultValue.longValue() < getMinNumberSize().longValue()) {

Integer counter = (Integer)getRequestData(REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT);

int count = 0;

if (counter != null)

count = counter.intValue();

if ((count < DEFAULT_MAX_INVALID_NUMBER_COUNT)) {

count++;

putRequestData(REQUEST_SCOPE_KEY_INVALID_NUMBER_COUNT, newInteger(count));

outOfRangeMessageDialog.handleRequest(new MessageDialog.StartRequest(this));

}

else {

// Has said an out-of-bounds number too many times, return a not-answered response.

issueResponse(new NotAnsweredResponse(this));

}

}

else {

// Got a number - return it.

issueResponse(new NumberGatheredResponse(this, resultValue));

}

}

// If it's not been answered (this will happen after retries as necessary)...

else if (response instanceof SingleResultQuestionDialog.NotAnsweredResponse) {

// Issue the not-answered response from this dialog.

issueResponse(new NotAnsweredResponse(this));

Page 78: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 78 of 97

}

}

// If the reponse is from the out-of-bounds MessageDialog, then just ask the question again.

else if (response.getSource().equals(outOfRangeMessageDialog)) {

// It only sends back CompletedResponses

if (response instanceof MessageDialog.CompletedResponse) {

// Re-ask the question

numQuestionDialog.handleRequest(new SingleResultQuestionDialog.StartRequest(this));

}

}

}

/**

* Creates the default out-of-range prompt to play to the user if they say

* an invalid number. The default prompt tells them what the minimum and

* maximum numbers are.

*/

private void setupDefaultOutOfRangePrompt() {

// A very simple TTS prompt by default.

defaultOutOfRangePrompt = new BasicDialogPrompt(

"Sorry, please say something between "

+ getActualMinNumberSize()

+ " and "

+ getActualMaxNumberSize());

}

/**

* Resets the grammar for the question to the currently set NumberStyle.

*/

private void resetGrammar() {

NumberStyle currentStyle = getActualNumberStyle();

if(NumberStyle.DIGIT_BY_DIGIT.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

SPELT_OUT_RULE_NAME,

Page 79: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 79 of 97

SPELT_OUT_GRAMMAR_PATH,

new String[] { SLOT_NAME }));

}

else if (NumberStyle.NATURAL.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

NATURAL_RULE_NAME,

NATURAL_GRAMMAR_PATH,

new String[] { SLOT_NAME }));

}

else if(NumberStyle.MIXED.equals(currentStyle)) {

numQuestionDialog.setQuestionGrammar(

new PredefinedGrammar(

MIXED_RULE_NAME,

MIXED_GRAMMAR_PATH,

new String[] { SLOT_NAME }));

}

else {

throw new IllegalStateException("Unknown NumberStyle set on the NumberDialog.");

}

}

/**

* Returns all the DialogRequest classes this will handle. This covers just

* the single StartRequest.

*/

protected Class[] doGetDialogRequests() {

return new Class[] { StartRequest.class };

}

/**

* Returns all the DialogResponse classes that this will issue. This covers

* just two: either a number is gathered, or not.

*/

protected Class[] doGetDialogResponses() {

return new Class[] { NumberGatheredResponse.class, NotAnsweredResponse.class };

Page 80: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 80 of 97

}

/**

* Sets the prompt to use for the question.

* <p>

* Set this to <tt>null</tt> to use the default value ({@link

* #DEFAULT_QUESTION_PROMPT}).

*/

public void setQuestionPrompt(DialogPrompt questionPrompt) {

if (questionPrompt == null) {

numQuestionDialog.setQuestionPrompt(DEFAULT_QUESTION_PROMPT);

}

else {

numQuestionDialog.setQuestionPrompt(questionPrompt);

}

}

/**

* Returns the prompt to use for the question, or <tt>null</tt> to use the

* default value ({@link #DEFAULT_QUESTION_PROMPT}).

*/

public DialogPrompt getQuestionPrompt() {

DialogPrompt result = numQuestionDialog.getQuestionPrompt();

if (result == DEFAULT_QUESTION_PROMPT) {

return null;

} else {

return result;

}

}

/**

* Sets the maximum number of reprompts to allow before a {@link

* #NotAnsweredResponse} is issued.

* <p>

* Set this to <tt>null</tt> to use the default value from the nested

* SingleResultQuestionDialog.

Page 81: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 81 of 97

*/

public void setMaxReprompt(Integer maxReprompt) {

numQuestionDialog.setMaxReprompt(maxReprompt);

}

/**

* @param DialogPrompt prompt to say when number out of range or null for default

*/

public void setNumberOutOfRangePrompt(DialogPrompt outOfRangePrompt){

if (outOfRangePrompt == null) {

outOfRangeMessageDialog.setPrompt(defaultOutOfRangePrompt);

} else {

outOfRangeMessageDialog.setPrompt(outOfRangePrompt);

}

}

/**

* @return DialogPrompt prompt to say when number out of range, null for default

*/

public DialogPrompt getNumberOutOfRangePrompt() {

DialogPrompt result = outOfRangeMessageDialog.getPrompt();

if (result == defaultOutOfRangePrompt) {

return null;

} else {

return result;

}

}

/**

* @param DialogPrompt as confirmationPrompt

*/

public void setConfirmationPrompt(DialogPrompt confirmPrompt) {

if (confirmPrompt != null) {

numQuestionDialog.setConfirmationPrompt(confirmPrompt);

}

else{

Page 82: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 82 of 97

numQuestionDialog.setConfirmationPrompt(DEFAULT_CONFIRMATION_PROMPT);

}

}

/**

* @return DialogPrompt prompt to use for confirmation, null for default

*/

public DialogPrompt getConfirmationPrompt() {

DialogPrompt result = numQuestionDialog.getConfirmationPrompt();

if (result == DEFAULT_CONFIRMATION_PROMPT) {

return null;

} else {

return result;

}

}

/**

* @return MaxReprompt from underlying SingleResultQuestionDialog

*/

public Integer getMaxReprompt() {

return numQuestionDialog.getMaxReprompt();

}

/**

* Set this to <tt>null</tt> to use the default value.

*/

public void setAcceptanceConfidenceThreshold(Float acceptanceConfidenceThreshold) {

numQuestionDialog.setAcceptanceConfidenceThreshold(acceptanceConfidenceThreshold);

}

/**

* @return Float from underlying SingleResultQuestionDialog

*/

public Float getAcceptanceConfidenceThreshold() {

return numQuestionDialog.getAcceptanceConfidenceThreshold();

}

Page 83: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 83 of 97

/**

* Set this to <tt>null</tt> to use the default value.

*/

public void setAcceptanceConfidenceMargin(Float acceptanceConfidenceMargin) {

numQuestionDialog.setAcceptanceConfidenceMargin(acceptanceConfidenceMargin);

}

/**

* @return Float from underlying SingleResultQuestionDialog

*/

public Float getAcceptanceConfidenceMargin() {

return numQuestionDialog.getAcceptanceConfidenceMargin();

}

/**

* Set this to <tt>null</tt> to use the default value from underlying SingleResultQuestionDialog

*/

public void setMaxDisambiguation(Integer maxDisambiguateListSize) {

numQuestionDialog.setMaxDisambiguation(maxDisambiguateListSize);

}

/**

* @return max disambiguation attempts from underlying SingleResultQuestionDialog

*/

public Integer getMaxDisambiguation() {

return numQuestionDialog.getMaxDisambiguation();

}

/**

* Set this to <tt>null</tt> to use the default value from the nested

* SingleResultQuestionDialog.

*/

public void setNoMatchPrompt(DialogPrompt noMatchPrompt) {

numQuestionDialog.setNoMatchPrompt(noMatchPrompt);

}

Page 84: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 84 of 97

/**

* @return prompt from underlying SingleResultQuestionDialog

*/

public DialogPrompt getNoMatchPrompt() {

return numQuestionDialog.getNoMatchPrompt();

}

/**

* <p> Set this to <tt>null</tt> to use the default value from the nested

* SingleResultQuestionDialog.

*/

public void setNoInputPrompt(DialogPrompt noInputPrompt) {

numQuestionDialog.setNoInputPrompt(noInputPrompt);

}

/**

* @return DialogPrompt from underlying SingleResultQuestionDialog

*/

public DialogPrompt getNoInputPrompt() {

return numQuestionDialog.getNoInputPrompt();

}

/**

* Start request for this dialog.

*/

public static class StartRequest extends AbstractDialogRequest {

public StartRequest(Dialog source) {

super(source);

}

}

/**

* Indicates that a number has been successfully gathered from the

* user, and contains that number as a <code>Long</code>. Any confirmation

* or disambiguation, where required, will have been carried out.

Page 85: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 85 of 97

*/

public static class NumberGatheredResponse extends AbstractDialogResponse {

// The result.

private Long result;

/**

* @param source The component issuing this response.

* @param result The result gathered by the component.

*/

public NumberGatheredResponse(Dialog source, Long result) {

super(source);

if (Assertions.ON) {

Assertions.assertTrue(result != null);

}

this.result = result;

}

/**

* Returns the number gathered by this component.

* @return The response number as a Long, never null.

*/

public Long getNumber() {

return result;

}

}

/**

* Indicates that no answer could be gathered from the user. The

* component will have reprompted the user according to the counts

* specified.

*/

public static class NotAnsweredResponse extends AbstractDialogResponse {

public NotAnsweredResponse(Dialog source) {

super(source);

}

}

/**

Page 86: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 86 of 97

* converts slots (which must contain a 'number' slot) into

*/

public static class NumberSlotProcessor implements SlotProcessor {

/**

* Returns a {@link Number} object with the name from the slot 'number'.

*/

public Serializable convertSlotDataToObject(Map slotData) {

if (slotData.containsKey(SLOT_NAME)) {

return new Long((String) slotData.get(SLOT_NAME));

} else {

if (slotData.keySet().size() < 1) {

throw new IllegalArgumentException("Slot did not contain anykeys - expected 'number'");

} else {

throw new IllegalArgumentException("Given slot did not containthe key 'number'");

}

}

}

/**

* @seecom.fluencyvoice.runner.dialog.voice.basic.simulator.SlotCreator#createSlots(Serializable)

*/

public Map convertObjectToSlotData(Serializable processedObject) {

String strValue = (String) processedObject;

Map map = new HashMap();

map.put(NumberDialog.SLOT_NAME, strValue);

return map;

}

}

/**

* Returns the minNumberSize.

* @return Long

*/

public Long getMinNumberSize() {

Page 87: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 87 of 97

return minNumberSize;

}

private long getActualMinNumberSize() {

if (minNumberSize == null) {

return DEFAULT_MIN_NUM_SIZE;

}

else {

return minNumberSize.longValue();

}

}

/**

* Sets the minNumberSize.

* @param minNumberSize The minNumberSize to set

*/

public void setMinNumberSize(Long minNumberSize) {

if ((minNumberSize != null) && (minNumberSize.longValue() < 0)) {

throw new IllegalArgumentException("The NumberDialog only supports positive numbers.");

}

else {

this.minNumberSize = minNumberSize;

setupDefaultOutOfRangePrompt();

}

}

/**

* Returns the maximum number to be accepted by the caller, or null if the

* default is to be used.

*/

public Long getMaxNumberSize() {

return maxNumberSize;

}

/**

* Sets the maximum number to be accepted from the caller; any numbers

Page 88: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 88 of 97

* above this will be rejected, with a message informing the caller, and

* they will be re-prompted. Setting this to null will mean the default it

* used.

* <p>

* A call to this results in the default out-of-range prompt being reset.

*/

public void setMaxNumberSize(Long maxNumberSize) {

// Check it's not too big (we only support up to 1 million currently) or small

if(maxNumberSize!=null && maxNumberSize.longValue()>DEFAULT_MAX_NUM_SIZE) {

throw new IllegalArgumentException("The NumberDialog does not support numbers largerthan 1 million.");

}

else if(maxNumberSize!=null && maxNumberSize.longValue()<0) {

throw new IllegalArgumentException("The NumberDialog only supports positive numbers.");

}

else {

this.maxNumberSize = maxNumberSize;

// Set up the out-of-range prompt again.

setupDefaultOutOfRangePrompt();

}

}

private long getActualMaxNumberSize() {

if (maxNumberSize == null) {

return DEFAULT_MAX_NUM_SIZE;

}

else {

return maxNumberSize.longValue();

}

}

public void setNumberStyle(NumberStyle style) {

this.numberStyle = style;

}

public NumberStyle getNumberStyle() {

Page 89: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 89 of 97

return numberStyle;

}

public NumberStyle getActualNumberStyle() {

if(numberStyle==null) {

return DEFAULT_NUMBER_STYLE;

}

else {

return numberStyle;

}

}

}

For the NumberDialogStep:

package com.netdecisions.components.dialog.ccp.number;

import java.math.BigDecimal;

import com.fluencyvoice.dialog.number.NumberDialog;

import com.fluencyvoice.runner.core.Dialog;

import com.fluencyvoice.runner.core.DialogRequest;

import com.fluencyvoice.runner.core.DialogResponse;

import com.fluencyvoice.runner.dialog.voice.prompt.BasicDialogPrompt;

import com.fluencyvoice.runner.dialog.voice.prompt.ConcatenatedDialogPrompt;

import com.netdecisions.agility.fw.kernel.common.BusinessRuleException;

import com.netdecisions.agility.fw.services.global.exceptionhandling.ExceptionService;

import com.netdecisions.agility.util.log.Log;

import com.netdecisions.ccp.StepInitializationException;

import com.netdecisions.ccp.dbo.bo.PromptDBODataType;

import com.netdecisions.ccp.dbo.bo.WavDBODataType;

import com.netdecisions.ccp.dbo.util.DBODataTypeFactory;

import com.netdecisions.ccp.model.bp.StepDefinition;

import com.netdecisions.ccp.model.bp.folder.InvalidLocationException;

import com.netdecisions.ccp.model.bp.folder.SchemaUtils;

import com.netdecisions.ccp.model.vr.DialogStep;

import com.netdecisions.ccp.model.vr.bo.DialogFolder;

Page 90: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 90 of 97

import com.netdecisions.ccp.repository.bo.RepositoryComponent;

import com.netdecisions.ccp.repository.bo.document.Document;

/**

* Wrapper step for the NumberDialog; allows the collection of a number from the

* caller.

* <p>

* Allows the Configurator user to specify prompts, a valid number range, and

* where in the folder to put the collected number.

*/

public class NumberDialogStep extends DialogStep {

private static final Log LOG = Log.getLog(NumberDialogStep.class);

// Descriptions of this step.

private static final String STEP_NAME = "Number Dialog Step";

private static final String STEP_DESCRIPTION =

"Gathers a number from the user, handling confirmation and disambiguation.";

private static final String STEP_CATEGORY = "Sample Steps";

private static final String STEP_IMAGE = "images/nouns/noun_number.png";

// Pages for the configuration properties

private static final String PROMPTS_PAGE = "Prompts";

private static final String NUMBER_OUT_OF_RANGE_PAGE = "Number Out of Range";

private static final String CHOICES_AND_RESULT_PAGE = "Choices and Result";

// Prompt specifying the question to ask the user.

private static final String QUESTION_TO_ASK_PROMPT_NAME = "QuestionToAskPrompt";

private static final String QUESTION_TO_ASK_PROMPT_DESCRIPTION = "The question toask";

private String questionTTS;

private WavDBODataType questionWav;

// Prompt specifying that the number spoken is out of the allowed range.

private static final String NUMBER_OUT_OF_RANGE_PROMPT_NAME ="NumberOutOfRangePrompt";

Page 91: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 91 of 97

private static final String NUMBER_OUT_OF_RANGE_PROMPT_DESCRIPTION = "Number outof range prompt";

private String numberOutOfRangeTTS;

private WavDBODataType numberOutOfRangeWav;

// The lower bound of the allowed range of numbers.

private static final String RANGE_FROM_NAME = "RangeFrom";

private static final String RANGE_FROM_DESCRIPTION = "Allowed numbers range from";

private BigDecimal rangeFrom;

// The upper bound of the allowed range of numbers.

private static final String RANGE_TO_NAME = "RangeTo";

private static final String RANGE_TO_DESCRIPTION = "Allowed numbers range to";

private BigDecimal rangeTo;

// The variable in the folder to put the result into.

private static final String RESULT_REF_NAME = "ResultantVariable";

private static final String RESULT_REF_DESCRIPTION = "Variable for the Gathered Result";

private String resultantVar;

/**

* Creates the definition of this step and its configuration properties.

*/

public RepositoryComponent createStepTypeDefinition(Document jarFile)

throws BusinessRuleException {

// Overall step definition

RepositoryComponent stepDef =

new RepositoryComponent(

STEP_NAME,

STEP_DESCRIPTION,

this.getClass(),

STEP_IMAGE,

jarFile,

STEP_CATEGORY,

Page 92: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 92 of 97

6);

// Add the main question prompt

stepDef.addField(

QUESTION_TO_ASK_PROMPT_NAME,

QUESTION_TO_ASK_PROMPT_DESCRIPTION,

true, PromptDBODataType.class);

stepDef.getDboDefinition().getValueDefinition(QUESTION_TO_ASK_PROMPT_NAME)

.setPage(PROMPTS_PAGE);

// Add the confirmation prompt (as standard)

addConfirmation(stepDef);

// Add the additional no-input and no-match prompts (as standard)

addAdditionalPrompts(stepDef);

// Add the number out-of-range prompt

stepDef.addField(

NUMBER_OUT_OF_RANGE_PROMPT_NAME,

NUMBER_OUT_OF_RANGE_PROMPT_DESCRIPTION,

true, PromptDBODataType.class);

stepDef.getDboDefinition()

.getValueDefinition(NUMBER_OUT_OF_RANGE_PROMPT_NAME)

.setPage(NUMBER_OUT_OF_RANGE_PAGE);

// Add the number range lower and upper bounds

stepDef.getDboDefinition().addField(

RANGE_FROM_NAME,

RANGE_FROM_DESCRIPTION,

true, false,

DBODataTypeFactory.DECIMAL_NUMBER_CLASS);

stepDef.getDboDefinition()

.getValueDefinition(RANGE_FROM_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

stepDef.getDboDefinition().addField(

RANGE_TO_NAME,

Page 93: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 93 of 97

RANGE_TO_DESCRIPTION,

true, false,

DBODataTypeFactory.DECIMAL_NUMBER_CLASS);

stepDef.getDboDefinition()

.getValueDefinition(RANGE_TO_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

// Add the resultant variable reference

stepDef.getDboDefinition().addVariableField(

RESULT_REF_NAME,

RESULT_REF_DESCRIPTION,

true, false,

SchemaUtils.ALL_XSD_NUMBER_TYPES,

true, "xsd:decimal");

stepDef.getDboDefinition()

.getValueDefinition(RESULT_REF_NAME)

.setPage(CHOICES_AND_RESULT_PAGE);

return stepDef;

}

/**

* Returns a human-readable description of this step.

*/

public String getStepDescription() {

return STEP_DESCRIPTION;

}

/**

* Initialises the Dialog Step, performing any configuration required and

* validating any input from the user.

*/

protected void initStep(StepDefinition def) throws StepInitializationException {

if (LOG.isDebugEnabled()) {

Page 94: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 94 of 97

LOG.debug("Initializing Number Dialog step");

}

// Get values

PromptDBODataType prompt = getPrompt(QUESTION_TO_ASK_PROMPT_NAME);

if (prompt == null) {

try {

def.setValue(QUESTION_TO_ASK_PROMPT_NAME,

(((BasicDialogPrompt)

NumberDialog.DEFAULT_QUESTION_PROMPT).getScript()));

}

catch (BusinessRuleException e) {

throw new StepInitializationException("The question to ask is unassigned.");

}

}

questionTTS = prompt.getTTSPrompt();

questionWav = prompt;

prompt = getPrompt(NUMBER_OUT_OF_RANGE_PROMPT_NAME);

if(prompt != null){

numberOutOfRangeTTS = prompt.getTTSPrompt();

numberOutOfRangeWav = prompt;

}

rangeFrom = (BigDecimal) def.getValue(RANGE_FROM_NAME);

rangeTo = (BigDecimal) def.getValue(RANGE_TO_NAME);

resultantVar = (String) def.getValue(RESULT_REF_NAME);

if (resultantVar == null) {

throw new StepInitializationException("The resultant variable must be selected");

}

if (LOG.isDebugEnabled()) {

LOG.debug(QUESTION_TO_ASK_PROMPT_NAME + ": " + questionTTS);

LOG.debug(QUESTION_TO_ASK_PROMPT_NAME + ": " + questionWav);

Page 95: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 95 of 97

LOG.debug(NUMBER_OUT_OF_RANGE_PROMPT_NAME + ": " +numberOutOfRangeTTS);

LOG.debug(NUMBER_OUT_OF_RANGE_PROMPT_NAME + ": " +numberOutOfRangeWav);

LOG.debug(RANGE_FROM_NAME + ": " + rangeFrom);

LOG.debug(RANGE_TO_NAME + ": " + rangeTo);

LOG.debug(RESULT_REF_NAME + ": " + resultantVar);

}

}

/**

* Creates the wrapped NumberDialog object.

*/

protected Dialog createDialog() {

return new NumberDialog();

}

/**

* Configures the NumberDialog component, based on the configuration options

* stored as instance data in this NumberDialogStep.

*/

protected void configureDialog(Dialog dialog) {

// Cast the Dialog to NumberDialog.

NumberDialog numberDialog = (NumberDialog) dialog;

// Set up the main question prompt.

String wavUrl = null;

if(questionWav != null && questionWav.hasAudioFile()){

wavUrl = generateWAVDataURL(QUESTION_TO_ASK_PROMPT_NAME, 0);

}

if(questionTTS!= null || (questionWav != null && questionWav.hasAudioFile())){

numberDialog.setQuestionPrompt(new BasicDialogPrompt(questionTTS, wavUrl));

}

// Set up the confirmation prompt (using base class functionality).

Page 96: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 96 of 97

numberDialog.setConfirmationPrompt(getConfirmationPrompt(new NumberDialogPrompt()));

// Set up the out-of-range prompt.

wavUrl = null;

if(numberOutOfRangeWav != null && numberOutOfRangeWav.hasAudioFile()){

wavUrl = generateWAVDataURL(NUMBER_OUT_OF_RANGE_PROMPT_NAME , 0);

}

if (rangeFrom != null) {

numberDialog.setMinNumberSize(new Long(rangeFrom.longValue()));

}

if (rangeTo != null) {

numberDialog.setMaxNumberSize(new Long(rangeTo.longValue()));

}

if (numberOutOfRangeTTS != null || (numberOutOfRangeWav != null &&numberOutOfRangeWav.hasAudioFile())) {

ConcatenatedDialogPrompt prompt = new ConcatenatedDialogPrompt();

prompt.addPrompt(new BasicDialogPrompt(numberOutOfRangeTTS, wavUrl));

prompt.addPrompt(new BasicDialogPrompt(" from " + numberDialog.getMinNumberSize()));

prompt.addPrompt(new BasicDialogPrompt(" to " + numberDialog.getMaxNumberSize()));

numberDialog.setNumberOutOfRangePrompt(prompt);

}else{

numberDialog.setNumberOutOfRangePrompt(null);

}

// Set up no-input/no-match/etc.

numberDialog.setNoInputPrompt(getNoInputPrompt());

numberDialog.setNoMatchPrompt(getNoMatchPrompt());

numberDialog.setMaxReprompt(getMaxReprompts());

// Set up thresholds for acceptance, etc.

numberDialog.setAcceptanceConfidenceThreshold(getAcceptanceThreshold());

numberDialog.setAcceptanceConfidenceMargin(getAcceptanceMargin());

}

/**

Page 97: DD Avaya Component Developer Guide

Speech Applications Builder Component Developer’s Guide • May 15, 2004 • page 97 of 97

* Produces a new request to pass to the NumberDialog. Since it takes no

* dynamic data from the folder, a new NumberDialog.StartRequest is simply

* returned.

*/

protected DialogRequest convertToRequest(DialogFolder info) {

return new NumberDialog.StartRequest(this);

}

/**

* Receives a response given by the NumberDialog, and adds any result to the

* folder. A result is only given in the event that there's a

* NumberGatheredResponse issued by the NumberDialog, in which case the

* number is added to the folder at the location the Configurator user has

* specified.

*/

protected void addResultToFolderData(DialogFolder folder, DialogResponse response) {

if (response instanceof NumberDialog.NumberGatheredResponse) {

NumberDialog.NumberGatheredResponse answer =

(NumberDialog.NumberGatheredResponse) response;

if (LOG.isDebugEnabled()) {

LOG.debug("Answer : " + answer);

}

// Set the result in the folder.

try {

folder.setValueAt(resultantVar, answer.getNumber().toString());

}

catch (InvalidLocationException e) {

ExceptionService.of().handleException(e);

}

}

}

}