File Coverage

blib/lib/VoiceXML/Client/Item/Field.pm
Criterion Covered Total %
statement 33 179 18.4
branch 4 78 5.1
condition 4 36 11.1
subroutine 6 16 37.5
pod 1 11 9.0
total 48 320 15.0


line stmt bran cond sub pod time code
1             package VoiceXML::Client::Item::Field;
2              
3              
4 3     3   16 use strict;
  3         5  
  3         94  
5              
6              
7 3     3   15 use base qw (VoiceXML::Client::Item);
  3         5  
  3         203  
8 3     3   18 use VoiceXML::Client::Util;
  3         5  
  3         55  
9 3     3   14 use VoiceXML::Client::Item::Prompt;
  3         5  
  3         123  
10              
11             =head1 COPYRIGHT AND LICENSE
12              
13            
14             Copyright (C) 2007,2008 by Pat Deegan.
15             All rights reserved
16             http://voicexml.psychogenic.com
17              
18             This library is released under the terms of the GNU GPL version 3, making it available only for
19             free programs ("free" here being used in the sense of the GPL, see http://www.gnu.org for more details).
20             Anyone wishing to use this library within a proprietary or otherwise non-GPLed program MUST contact psychogenic.com to
21             acquire a distinct license for their application. This approach encourages the use of free software
22             while allowing for proprietary solutions that support further development.
23              
24              
25             This file is part of VoiceXML::Client.
26              
27            
28            
29             VoiceXML::Client is free software: you can redistribute it and/or modify
30             it under the terms of the GNU General Public License as published by
31             the Free Software Foundation, either version 3 of the License, or
32             (at your option) any later version.
33              
34             VoiceXML::Client is distributed in the hope that it will be useful,
35             but WITHOUT ANY WARRANTY; without even the implied warranty of
36             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37             GNU General Public License for more details.
38              
39             You should have received a copy of the GNU General Public License
40             along with VoiceXML::Client. If not, see .
41              
42              
43             =cut
44              
45              
46              
47              
48 3         6162 use vars qw{
49             $VERSION
50             $DefaultNumDigits
51 3     3   14 };
  3         4  
52              
53             $VERSION = $VoiceXML::Client::Item::VERSION;
54              
55             $DefaultNumDigits = 1;
56              
57              
58              
59              
60             sub init {
61 1     1 1 3 my $self = shift;
62            
63 1 50       12 unless (defined $self->{'name'})
64             {
65 1   33     7 $self->{'name'} = $self->{'XMLElement'}->attribute('name') || int(rand(999999)) . time();
66            
67             }
68            
69 1   50     52 $self->{'type'} = $self->{'XMLElement'}->attribute('type') || 'digits';
70            
71 1 50       18 if ($self->{'type'} eq 'digits')
72             {
73 1   33     5 $self->{'numdigits'} = $self->{'XMLElement'}->attribute('numdigits') || $DefaultNumDigits;
74            
75 1 50 33     24 if ($self->{'numdigits'} && $self->{'numdigits'} >= 1)
76             {
77 1         5 $self->{'inputmode'} = $VoiceXML::Client::DeviceHandle::InputMode{'FIXEDDIGIT'};
78 1 50       4 VoiceXML::Client::Util::log_msg("Setting input mode to FIXED ($self->{'numdigits'}) for field $self->{'name'}")
79             if ($VoiceXML::Client::Debug);
80             } else {
81 0         0 $self->{'inputmode'} = $VoiceXML::Client::DeviceHandle::InputMode{'MULTIDIGIT'};
82 0 0       0 VoiceXML::Client::Util::log_msg("Setting input mode to MULTIDIGIT for field $self->{'name'}")
83             if ($VoiceXML::Client::Debug);
84             }
85            
86             } else {
87 0         0 $self->{'numdigits'} = '';
88 0         0 $self->{'inputmode'} = $VoiceXML::Client::DeviceHandle::InputMode{'MULTIDIGIT'};
89            
90 0 0       0 VoiceXML::Client::Util::log_msg("Defaulting to MULTIDIGIT mode for field $self->{'name'}")
91             if ($VoiceXML::Client::Debug);
92             }
93            
94            
95 1         5 VoiceXML::Client::Item::Util->declareVariable($self, $self->{'name'});
96            
97 1         4 $self->{'noinput'} = [];
98 1         4 $self->{'nomatch'} = [];
99 1         3 $self->{'timeswithoutinput'} = 0;
100 1         3 $self->{'timesnomatchinput'} = 0;
101 1         10 $self->{'lastinputstate'} = '';
102 1         3 $self->{'infilledelement'} = 0;
103            
104 1         11 $self->{'currentinputtimeout'} = $VoiceXML::Client::Item::Prompt::DefaultTimeoutSeconds;
105            
106 1         3 return 1;
107            
108            
109             }
110              
111             sub inputReceivedInterrupt {
112 0     0 0   my $self = shift;
113 0           my $handle = shift;
114 0           my $input = shift;
115            
116 0           $handle->deleteInputReceivedCallback('fieldinput_' . $self->{'name'});
117              
118 0 0         if ($self->{'infilledelement'})
119             {
120            
121 0           $self->abortProcessing(0) ;
122             } else {
123            
124 0           $self->abortProcessing(1) ;
125             }
126            
127            
128 0           return;
129             }
130            
131             sub execute {
132 0     0 0   my $self = shift;
133 0           my $handle = shift;
134 0           my $optParams = shift;
135            
136 0           $self->clearValue();
137 0           $handle->registerInputReceivedCallback('fieldinput_' . $self->{'name'}, $self, 'inputReceivedInterrupt');
138            
139 0           $handle->inputMode($self->{'inputmode'}, $self->{'numdigits'});
140            
141            
142            
143 0           for (my $i=0; $i < @{$self->{'children'}}; $i++)
  0            
144             {
145 0   0       my $aChild = $self->{'children'}->[$i] || next;
146            
147 0           my $cType = $aChild->getType();
148            
149 0 0         if ($cType eq 'VoiceXML::Client::Item::NoInput')
    0          
150             {
151 0           push @{$self->{'noinput'}}, $aChild;
  0            
152             } elsif ($cType eq 'VoiceXML::Client::Item::NoMatch')
153             {
154            
155 0           push @{$self->{'nomatch'}}, $aChild;
  0            
156             }
157             }
158            
159            
160 0           my $fillFound = 0;
161 0           while (my $curChild = $self->getCurrentChild())
162             {
163            
164 0           my $cType = $curChild->getType();
165 0 0         if ($cType eq 'VoiceXML::Client::Item::Filled')
166             {
167 0           $self->{'infilledelement'} = 1;
168             } else {
169 0           $self->{'infilledelement'} = 0;
170             }
171            
172 0 0         if ($self->haveUserInput($handle, $optParams))
173             {
174             # the user has barged in... fugget about it--this is a barge in... we don't want to do
175             # anything except deal with the filled element if present and collect any remaining input
176            
177            
178 0           $self->abortProcessing(0);
179            
180 0 0         VoiceXML::Client::Util::log_msg("Field -- detected user input...") if ($VoiceXML::Client::Debug > 1);
181            
182            
183            
184 0 0         unless ($self->{'infilledelement'})
185             {
186 0 0         VoiceXML::Client::Util::log_msg("skipping $cType item") if ($VoiceXML::Client::Debug > 1);
187 0           $self->proceedToNextChild();
188 0           next;
189             }
190 0 0         VoiceXML::Client::Util::log_msg("Dealing with the item") if ($VoiceXML::Client::Debug > 1);
191             }
192            
193              
194 0 0         if ($curChild->can('timeoutSeconds'))
195             {
196 0   0       $self->{'currentinputtimeout'} =
197             $curChild->timeoutSeconds()
198             || $VoiceXML::Client::Item::Prompt::DefaultTimeoutSeconds;
199             }
200            
201 0 0 0       if ($self->{'infilledelement'})
    0          
202             {
203             # we are in a element...
204 0           $fillFound = 1;
205            
206 0           $self->abortProcessing(0);
207             # we might just be done here... let's see
208            
209 0 0         unless ($self->getUserInput($handle, $optParams))
210             {
211 0           return $self->refetchUserInput($handle, $optParams);
212             }
213            
214             } elsif ($cType eq 'VoiceXML::Client::Item::NoInput' || $cType eq 'VoiceXML::Client::Item::NoMatch')
215             {
216             # skip these...
217 0           $self->proceedToNextChild();
218 0           next;
219             }
220            
221            
222 0           my $rv = $curChild->execute($handle, $optParams) ;
223            
224 0 0         return $rv if ($rv != $VoiceXML::Client::Flow::Directive{'CONTINUE'});
225            
226 0           $self->proceedToNextChild();
227             }
228            
229 0 0         unless ($fillFound)
230             {
231             # no filled element... need some input
232 0           $self->abortProcessing(0);
233 0 0         unless ($self->getUserInput($handle, $optParams))
234             {
235 0           return $self->refetchUserInput($handle, $optParams);
236             }
237             }
238            
239 0           return $VoiceXML::Client::Flow::Directive{'CONTINUE'};
240            
241             }
242              
243             sub reset {
244 0     0 0   my $self = shift;
245 0           my $vxmlDoc = $self->getParentVXMLDocument();
246 0           $vxmlDoc->clearGlobalVar($self->{'name'});
247 0           $self->SUPER::reset();
248             }
249              
250             sub getBestNoInputItem {
251 0     0 0   my $self = shift;
252 0           my $handle = shift;
253 0           my $optParams = shift;
254            
255            
256 0           my $lastCountMatch = 0;
257              
258 0           my $noInputMatch = $self->{'noinput'}->[0];
259 0           my $idx = 1;
260 0           while ($idx < scalar @{$self->{'noinput'}})
  0            
261             {
262 0           my $niItem = $self->{'noinput'}->[$idx];
263 0 0 0       if ($niItem
      0        
264             && $niItem->{'count'} <= $self->{'timeswithoutinput'}
265             && $niItem->{'count'} > $lastCountMatch)
266             {
267 0           $lastCountMatch = $niItem->{'count'};
268 0           $noInputMatch = $niItem;
269             }
270            
271 0           $idx++;
272             }
273            
274 0           return $noInputMatch;
275             }
276              
277              
278             sub getBestNoMatchItem {
279 0     0 0   my $self = shift;
280 0           my $handle = shift;
281 0           my $optParams = shift;
282            
283            
284 0           my $lastCountMatch = 0;
285              
286 0           my $nomatchMatch = $self->{'nomatch'}->[0];
287 0           my $idx = 1;
288 0           while ($idx < scalar @{$self->{'nomatch'}})
  0            
289             {
290 0           my $niItem = $self->{'nomatch'}->[$idx];
291 0 0 0       if ($niItem
      0        
292             && $niItem->{'count'} <= $self->{'timesnomatchinput'}
293             && $niItem->{'count'} > $lastCountMatch)
294             {
295 0           $lastCountMatch = $niItem->{'count'};
296 0           $nomatchMatch = $niItem;
297             }
298            
299 0           $idx++;
300             }
301            
302 0           return $nomatchMatch;
303             }
304            
305              
306             sub refetchUserInput {
307 0     0 0   my $self = shift;
308 0           my $handle = shift;
309 0           my $optParams = shift;
310              
311              
312            
313 0 0         if ($self->{'lastinputstate'} eq 'none')
    0          
314             {
315 0 0         if (scalar @{$self->{'noinput'}})
  0            
316             {
317 0           my $noInputMatch = $self->getBestNoInputItem($handle, $optParams);
318            
319 0 0         if ($noInputMatch)
320             {
321 0           return $noInputMatch->execute($handle, $optParams);
322             }
323             }
324             } elsif ($self->{'lastinputstate'} eq 'invalid')
325             {
326            
327 0           $self->reset();
328 0           my $noMatchInputItem = $self->getBestNoMatchItem();
329            
330 0 0         if ($noMatchInputItem)
331             {
332 0           return $noMatchInputItem->execute($handle, $optParams);
333             }
334             }
335            
336            
337 0           $self->{'parent'}->reset();
338 0           return $self->{'parent'}->execute($handle, $optParams);
339             }
340              
341             sub haveUserInput {
342 0     0 0   my $self = shift;
343 0   0       my $handle = shift || return undef;
344 0           my $optParams = shift;
345            
346            
347 0           return $handle->pendingInputLength();
348             }
349              
350              
351             sub getUserInput {
352 0     0 0   my $self = shift;
353 0           my $handle = shift;
354 0           my $optParams = shift;
355            
356            
357              
358 0           my $number = $handle->readnum(undef, $self->{'currentinputtimeout'}, 1, $self->{'numdigits'});
359 0 0         if ($VoiceXML::Client::Debug > 1)
360             {
361 0 0         my $gotnum = (defined $number) ? $number : '';
362 0           VoiceXML::Client::Util::log_msg("Field::getUserInput got '$gotnum' from handle");
363             }
364            
365 0 0 0       if (defined $number && length($number))
366             {
367             # got some input...
368 0           $self->{'timeswithoutinput'} = 0;
369            
370 0 0         if (scalar @{$self->{'nomatch'}})
  0            
371             {
372             # we have some nomatch fields... better check if this input is valid
373             # according to our grammmars...
374            
375 0           my $parentForm = $self->getParentForm();
376            
377 0 0         if ($parentForm->inputValidAccordingToGrammar($number))
378             {
379             # huzzah!
380 0           $self->value($number);
381            
382 0           return 1;
383             }
384            
385             # the input came in, but was invalid and we have nomatch set...
386            
387 0 0         VoiceXML::Client::Util::log_msg("Field user input considered invalid") if ($VoiceXML::Client::Debug);
388 0           $self->{'lastinputstate'} = 'invalid';
389 0           $self->{'timesnomatchinput'}++;
390 0           return 0;
391            
392             } else {
393            
394             # no nomatches set... so no checking, just set the value
395             # whatever it may be...
396            
397 0           $self->value($number);
398            
399 0 0         VoiceXML::Client::Util::log_msg("Setting user input to '$number'") if ($VoiceXML::Client::Debug);
400            
401 0           return 1;
402             }
403             }
404            
405 0 0         VoiceXML::Client::Util::log_msg("Field: no user input received") if ($VoiceXML::Client::Debug);
406            
407 0           $self->{'timeswithoutinput'}++;
408 0           $self->{'lastinputstate'} = 'none';
409            
410 0           return 0;
411             }
412              
413             sub value {
414 0     0 0   my $self = shift;
415 0           my $setTo = shift;
416            
417 0           my $vxmlDoc = $self->getParentVXMLDocument();
418            
419            
420            
421 0 0         if (defined $setTo)
422             {
423            
424 0           $self->{'lastinputstate'} = 'valid';
425            
426 0 0         VoiceXML::Client::Util::log_msg("Field setting variable " . $self->{'name'} . " to $setTo")
427             if ($VoiceXML::Client::Debug);
428            
429 0           $vxmlDoc->globalVar($self->{'name'}, $setTo);
430             }
431            
432 0           return $vxmlDoc->globalVar($self->{'name'});
433             }
434              
435             sub clearValue {
436 0     0 0   my $self = shift;
437            
438 0           VoiceXML::Client::Item::Util->resetVariable($self, $self->{'name'});
439            
440             }
441            
442            
443              
444            
445            
446              
447              
448             1;