Field.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Linq;
38 using System.Text.RegularExpressions;
39 using System.Threading.Tasks;
40 
41 namespace Microsoft.Bot.Builder.FormFlow.Advanced
42 {
43  #region Documentation
44  #endregion
52  public delegate Task<bool> DefineAsyncDelegate<T>(T state, Field<T> field)
53  where T : class;
54 
62  public delegate NextStep NextDelegate<T>(object value, T state)
63  where T : class;
64 
67  public class Field<T> : IField<T>
68  where T : class
69  {
73  public Field(string name, FieldRole role)
74  {
75  _name = name;
76  _role = role;
77  _min = -double.MaxValue;
78  _max = double.MaxValue;
79  _limited = false;
80  }
81 
82  #region IField
83 
84  public string Name { get { return _name; } }
85 
86  public virtual IForm<T> Form
87  {
88  get { return this._form; }
89  set
90  {
91  _form = value;
92  foreach (var template in _form.Configuration.Templates)
93  {
94  if (!_templates.ContainsKey(template.Usage))
95  {
96  AddTemplate(template);
97  }
98  }
99  if (_define == null)
100  {
101  DefinePrompt();
102  DefineRecognizer();
103  }
104  }
105  }
106 
107  #region IFieldState
108  public virtual object GetValue(T state)
109  {
110  throw new NotImplementedException();
111  }
112 
113  public virtual void SetValue(T state, object value)
114  {
115  throw new NotImplementedException();
116  }
117 
118  public virtual bool IsUnknown(T state)
119  {
120  throw new NotImplementedException();
121  }
122 
123  public virtual void SetUnknown(T state)
124  {
125  throw new NotImplementedException();
126  }
127 
128  public virtual Type Type
129  {
130  get { return _type; }
131  }
132 
133  public virtual bool Optional
134  {
135  get
136  {
137  return _optional;
138  }
139  }
140 
141  public virtual bool IsNullable
142  {
143  get
144  {
145  return _isNullable;
146  }
147  }
148 
149  public virtual bool Limits(out double min, out double max)
150  {
151  min = _min;
152  max = _max;
153  return _limited;
154  }
155 
156  public virtual string Pattern
157  {
158  get { return _pattern; }
159  }
160 
161  public virtual IEnumerable<string> Dependencies
162  {
163  get
164  {
165  return _dependencies;
166  }
167  }
168  #endregion
169 
170  #region IFieldDescription
171  public virtual FieldRole Role
172  {
173  get
174  {
175  return _role;
176  }
177  }
178 
179  public virtual DescribeAttribute FieldDescription
180  {
181  get
182  {
183  return _description;
184  }
185  }
186 
187  public virtual IEnumerable<string> FieldTerms
188  {
189  get
190  {
191  return _terms.Alternatives;
192  }
193  }
194 
195  public virtual IEnumerable<string> Terms(object value)
196  {
197  return _valueTerms[value].Alternatives;
198  }
199 
200  public virtual DescribeAttribute ValueDescription(object value)
201  {
202  return _valueDescriptions[value];
203  }
204 
205  public virtual IEnumerable<DescribeAttribute> ValueDescriptions
206  {
207  get
208  {
209  return _valueDescriptions.Values;
210  }
211  }
212 
213  public virtual IEnumerable<object> Values
214  {
215  get
216  {
217  return _valueDescriptions.Keys;
218  }
219  }
220 
221  public virtual bool AllowsMultiple
222  {
223  get
224  {
225  return _allowsMultiple;
226  }
227  }
228 
229  public virtual bool AllowDefault
230  {
231  get
232  {
233  return _promptDefinition.AllowDefault != BoolDefault.False;
234  }
235  }
236 
237  public bool AllowNumbers
238  {
239  get
240  {
241  return _promptDefinition.AllowNumbers;
242  }
243  }
244  #endregion
245 
246  #region IFieldResources
247 
248  public virtual void SaveResources()
249  {
250  var localizer = _form.Resources;
251  if (_description.IsLocalizable)
252  {
253  localizer.Add(_name + nameof(_description), _description.Description);
254  localizer.Add(_name + nameof(_description.Image), _description.Image);
255  localizer.Add(_name + nameof(_description.Title), _description.Title);
256  localizer.Add(_name + nameof(_description.SubTitle), _description.SubTitle);
257  localizer.Add(_name + nameof(_description.Message), _description.Message);
258  }
259  if (_terms.IsLocalizable)
260  {
261  localizer.Add(_name + nameof(_terms), _terms.Alternatives);
262  }
263  localizer.Add(_name + nameof(_valueDescriptions), _valueDescriptions);
264  localizer.Add(_name + nameof(_valueTerms), _valueTerms);
265  if (_promptDefinition != null && _promptDefinition.IsLocalizable)
266  {
267  localizer.Add(_name + nameof(_promptDefinition), _promptDefinition.Patterns);
268  }
269  localizer.Add(_name, _templates);
270  }
271 
272  public virtual void Localize()
273  {
274  var localizer = _form.Resources;
275  string strValue;
276  string[] terms;
277  if (localizer.Lookup(_name + nameof(_description), out strValue))
278  {
279  _description.Description = strValue;
280  }
281  if (localizer.Lookup(_name + nameof(_description.Image), out strValue))
282  {
283  _description.Image = strValue;
284  }
285  if (localizer.Lookup(_name + nameof(_description.Title), out strValue))
286  {
287  _description.Title = strValue;
288  }
289  if (localizer.Lookup(_name + nameof(_description.SubTitle), out strValue))
290  {
291  _description.SubTitle = strValue;
292  }
293  if (localizer.Lookup(_name + nameof(_description.Message), out strValue))
294  {
295  _description.Message = strValue;
296  }
297  if (localizer.LookupValues(_name + nameof(_terms), out terms))
298  {
299  _terms = new TermsAttribute(terms);
300  }
301  localizer.LookupDictionary(_name + nameof(_valueDescriptions), _valueDescriptions);
302  localizer.LookupDictionary(_name + nameof(_valueTerms), _valueTerms);
303  string[] patterns;
304  if (localizer.LookupValues(_name + nameof(_promptDefinition), out patterns))
305  {
306  _promptDefinition.Patterns = patterns;
307  }
308  localizer.LookupTemplates(_name, _templates);
309  if (!_promptSet)
310  {
311  _promptDefinition = null;
312  }
313  _prompt = null;
314  _recognizer = null;
315  if (_define == null)
316  {
317  DefinePrompt();
318  DefineRecognizer();
319  }
320  }
321 
322  #endregion
323 
324  #region IFieldPrompt
325 
326  public virtual bool Active(T state)
327  {
328  return _condition(state);
329  }
330 
332  {
333  TemplateAttribute template;
334  _templates.TryGetValue(usage, out template);
335  if (template != null)
336  {
337  template.ApplyDefaults(_form.Configuration.DefaultPrompt);
338  }
339  return template;
340  }
341 
342  public virtual IPrompt<T> Prompt
343  {
344  get
345  {
346  return _prompt;
347  }
348  }
349 
350  public async virtual Task<bool> DefineAsync(T state)
351  {
352  bool result = true;
353  if (_define != null)
354  {
355  if (!_promptSet)
356  {
357  _promptDefinition = null;
358  }
359  _recognizer = null;
360  _help = null;
361  _prompt = null;
362  result = await _define(state, this);
363  DefinePrompt();
364  DefineRecognizer();
365  }
366  return result;
367  }
368 
369  public async virtual Task<ValidateResult> ValidateAsync(T state, object value)
370  {
371  return await _validate(state, value);
372  }
373 
374  public virtual IPrompt<T> Help
375  {
376  get
377  {
378  return _help;
379  }
380  }
381 
382  public virtual NextStep Next(object value, T state)
383  {
384  return _next(value, state);
385  }
386 
387  #endregion
388  #endregion
389 
390  #region Publics
391  public Field<T> SetFieldDescription(string description)
395  {
396  _description = new DescribeAttribute(description);
397  return this;
398  }
399 
406  {
407  _description = description;
408  return this;
409  }
410 
414  public Field<T> SetFieldTerms(params string[] terms)
415  {
416  _terms = new TermsAttribute(terms);
417  return this;
418  }
419 
426  public Field<T> AddDescription(object value, string description, string image = null, string message = null)
427  {
428  _valueDescriptions[value] = new DescribeAttribute(description, image, message);
429  return this;
430  }
431 
436  public Field<T> AddDescription(object value, DescribeAttribute description)
437  {
438  _valueDescriptions[value] = description;
439  return this;
440  }
441 
446  public Field<T> AddTerms(object value, params string[] terms)
447  {
448  _valueTerms[value] = new TermsAttribute(terms);
449  return this;
450  }
451 
456  public Field<T> AddTerms(object value, TermsAttribute terms)
457  {
458  _valueTerms[value] = terms;
459  return this;
460  }
461 
465  public Field<T> RemoveValue(object value)
466  {
467  _valueDescriptions.Remove(value);
468  _valueTerms.Remove(value);
469  return this;
470  }
471 
475  {
476  _valueDescriptions.Clear();
477  _valueTerms.Clear();
478  return this;
479  }
480 
484  public Field<T> SetType(Type type)
485  {
486  _type = type;
487  return this;
488  }
489 
493  public Field<T> SetOptional(bool optional = true)
494  {
495  _optional = optional;
496  return this;
497  }
498 
499  #region Documentation
500  #endregion
504  public Field<T> SetAllowsMultiple(bool multiple = true)
505  {
506  _allowsMultiple = multiple;
507  return this;
508  }
509 
513  public Field<T> SetIsNullable(bool nullable = true)
514  {
515  _isNullable = nullable;
516  return this;
517  }
518 
519  #region Documentation
520  #endregion
524  public Field<T> SetActive(ActiveDelegate<T> condition)
525  {
526  if (condition != null) _condition = condition;
527  return this;
528  }
529 
530  #region Documentation
531  #endregion
537  public Field<T> SetDefine(DefineAsyncDelegate<T> definition)
538  {
539  if (definition != null) _define = definition;
540  return this;
541  }
542 
547  {
548  _promptDefinition = prompt;
549  _promptSet = true;
550  return this;
551  }
552 
561  {
562  _recognizer = recognizer;
563  _buildPrompts = true;
564  return this;
565  }
566 
571  {
572  AddTemplate(template);
573  return this;
574  }
575 
579  public Field<T> SetValidate(ValidateAsyncDelegate<T> validate)
580  {
581  if (validate != null) _validate = validate;
582  return this;
583  }
584 
589  public Field<T> SetLimits(double min, double max)
590  {
591  SetLimits(min, max, true);
592  return this;
593  }
594 
600  public Field<T> SetPattern(string pattern)
601  {
602  _pattern = pattern;
603  var regex = new Regex(pattern, RegexOptions.Compiled);
604  _validate = async (T state, object value) =>
605  {
606  var result = new ValidateResult { Value = value };
607  if (value == null)
608  {
609  result.IsValid = _optional;
610  }
611  else
612  {
613  var match = regex.Match((string)value);
614  result.IsValid = match.Success;
615  }
616  if (!result.IsValid)
617  {
618  result.Feedback = new Prompter<T>(Template(TemplateUsage.NotUnderstood), _form, null).Prompt(state, this, value).Prompt;
619  }
620  return result;
621  };
622  return this;
623  }
624 
625  #region Documentation
626  #endregion
630  public Field<T> SetDependencies(params string[] dependencies)
631  {
632  _dependencies = dependencies;
633  return this;
634  }
635 
640  public Field<T> SetNext(NextDelegate<T> next)
641  {
642  _next = next;
643  return this;
644  }
645 
646  #endregion
647 
648  #region Internals
649  protected void DefinePrompt()
650  {
651  if (_promptDefinition == null)
652  {
653  TemplateUsage usage = TemplateUsage.None;
654  if (_type == null || _type.IsEnum)
655  {
656  usage = _allowsMultiple ? TemplateUsage.EnumSelectMany : TemplateUsage.EnumSelectOne;
657  }
658  else if (_type == typeof(string))
659  {
660  usage = TemplateUsage.String;
661  }
662  else if (_type.IsIntegral())
663  {
664  usage = TemplateUsage.Integer;
665  }
666  else if (_type == typeof(bool))
667  {
668  usage = TemplateUsage.Bool;
669  }
670  else if (_type.IsDouble())
671  {
672  usage = TemplateUsage.Double;
673  }
674  else if (_type == typeof(DateTime))
675  {
676  usage = TemplateUsage.DateTime;
677  }
678  else
679  {
680  throw new ArgumentException($"{_name} is not a type FormFlow understands.");
681  }
682  if (usage != TemplateUsage.None)
683  {
684  _promptDefinition = new PromptAttribute(Template(usage));
685  }
686  _promptSet = false;
687  }
688  _promptDefinition.ApplyDefaults(_form.Configuration.DefaultPrompt);
689  }
690 
691  protected void DefineRecognizer()
692  {
693  if (_recognizer == null)
694  {
695  if (_type == null || _type.IsEnum)
696  {
697  _recognizer = new RecognizeEnumeration<T>(this);
698  }
699  else if (_type == typeof(bool))
700  {
701  _recognizer = new RecognizeBool<T>(this);
702  }
703  else if (_type == typeof(string))
704  {
705  _recognizer = new RecognizeString<T>(this);
706  }
707  else if (_type.IsIntegral())
708  {
709  _recognizer = new RecognizeNumber<T>(this);
710  }
711  else if (_type.IsDouble())
712  {
713  _recognizer = new RecognizeDouble<T>(this);
714  }
715  else if (_type == typeof(DateTime))
716  {
717  _recognizer = new RecognizeDateTime<T>(this);
718  }
719  else if (_type.IsIEnumerable())
720  {
721  var elt = _type.GetGenericElementType();
722  if (elt.IsEnum)
723  {
724  _recognizer = new RecognizeEnumeration<T>(this);
725  }
726  }
727  _buildPrompts = true;
728  }
729  if (_buildPrompts)
730  {
731  var template = Template(TemplateUsage.Help);
732  _help = new Prompter<T>(template, _form, _recognizer);
733  var prompt = _promptDefinition;
734  _prompt = new Prompter<T>(_promptDefinition, _form, _recognizer);
735  _buildPrompts = false;
736  }
737  }
738 
739  protected void SetLimits(double min, double max, bool limited)
740  {
741  _min = min;
742  _max = max;
743  _limited = limited;
744  }
745 
746  protected void AddTemplate(TemplateAttribute template)
747  {
748  _templates[template.Usage] = template;
749  }
750 
751  protected IForm<T> _form;
752  protected string _name;
753  protected FieldRole _role;
754  protected ActiveDelegate<T> _condition = new ActiveDelegate<T>((state) => true);
755  protected DefineAsyncDelegate<T> _define = null;
756  protected ValidateAsyncDelegate<T> _validate = new ValidateAsyncDelegate<T>(async (state, value) => new ValidateResult { IsValid = true, Value = value });
757  protected NextDelegate<T> _next = new NextDelegate<T>((value, state) => new NextStep());
758  protected double _min, _max;
759  protected bool _limited;
760  protected string _pattern;
761  protected string[] _dependencies = Array.Empty<string>();
762  protected bool _allowsMultiple;
763  protected Type _type;
764  protected bool _optional;
765  protected bool _isNullable;
766  protected bool _keepZero;
767  protected DescribeAttribute _description = new DescribeAttribute(null);
768  protected TermsAttribute _terms = new TermsAttribute();
769  protected Dictionary<object, DescribeAttribute> _valueDescriptions = new Dictionary<object, DescribeAttribute>();
770  protected Dictionary<object, TermsAttribute> _valueTerms = new Dictionary<object, TermsAttribute>();
771  protected Dictionary<TemplateUsage, TemplateAttribute> _templates = new Dictionary<TemplateUsage, TemplateAttribute>();
772  protected bool _promptSet;
774  protected bool _buildPrompts = true;
776  protected IPrompt<T> _help;
777  protected IPrompt<T> _prompt;
778  #endregion
779  }
780 
785  public class Fields<T> : IFields<T>
786  where T : class
787  {
788  public IField<T> Field(string name)
789  {
790  IField<T> field;
791  _fields.TryGetValue(name, out field);
792  return field;
793  }
794 
795  public void Add(IField<T> field)
796  {
797  _fields[field.Name] = field;
798  }
799 
800  public IEnumerator<IField<T>> GetEnumerator()
801  {
802  return (from entry in _fields select entry.Value).GetEnumerator();
803  }
804 
805  IEnumerator IEnumerable.GetEnumerator()
806  {
807  return (from entry in _fields select entry.Value).GetEnumerator();
808  }
809 
811  protected Dictionary<string, IField<T>> _fields = new Dictionary<string, IField<T>>();
812  }
813 }
Field< T > SetDefine(DefineAsyncDelegate< T > definition)
Define a delegate for dynamically defining field.
Definition: Field.cs:537
Field< T > SetRecognizer(IRecognize< T > recognizer)
Sets the recognizer for the field.
Definition: Field.cs:560
virtual bool Limits(out double min, out double max)
Limits of numeric values.
Definition: Field.cs:149
Field< T > SetValidate(ValidateAsyncDelegate< T > validate)
Set the field validation.
Definition: Field.cs:579
FieldRole
The role the field plays in a form.
Definition: IField.cs:129
Field< T > SetLimits(double min, double max)
Set numeric limits.
Definition: Field.cs:589
Define a template for generating strings.
Definition: Attributes.cs:571
Dictionary of all fields indexed by name.
Definition: Field.cs:785
A prompt and recognizer packaged together.
Definition: IPrompt.cs:330
Field< T > AddDescription(object value, string description, string image=null, string message=null)
Adds a description for a value.
Definition: Field.cs:426
Field< T > SetIsNullable(bool nullable=true)
Set whether or not field is nullable.
Definition: Field.cs:513
void ApplyDefaults(TemplateBaseAttribute defaultTemplate)
Any default values in this template will be overridden by the supplied defaultTemplate ...
Definition: Attributes.cs:779
void SetLimits(double min, double max, bool limited)
Definition: Field.cs:739
virtual NextStep Next(object value, T state)
Next step to execute.
Definition: Field.cs:382
Define the prompt used when asking about a field.
Definition: Attributes.cs:294
virtual TemplateAttribute Template(TemplateUsage usage)
Return a template for building a prompt.
Definition: Field.cs:331
virtual void Localize()
Loads any string resources from the form localizer.
Definition: Field.cs:272
Interface for recognizers that look for matches in user input.
Definition: IRecognize.cs:159
TemplateUsage
All of the built-in templates.
Definition: Attributes.cs:321
Field(string name, FieldRole role)
Construct field.
Definition: Field.cs:73
What to display when asked for help.
Interface to track all of the fields in a form.
Definition: IField.cs:427
Field< T > SetType(Type type)
Sets the type of the underlying field state.
Definition: Field.cs:484
Interface for all the information about a specific field.
Definition: IField.cs:404
Field< T > RemoveValues()
Removes all values and their associated descriptions and terms.
Definition: Field.cs:474
Field< T > SetPrompt(PromptAttribute prompt)
Sets the field prompt.
Definition: Field.cs:546
virtual void SetValue(T state, object value)
Set this field value in form state.
Definition: Field.cs:113
Attribute to override the default terms used to match a field, property or enum value to user input...
Definition: Attributes.cs:119
Field< T > AddTerms(object value, TermsAttribute terms)
Adds terms for a value.
Definition: Field.cs:456
Interface for a prompt and its associated recognizer.
Definition: IPrompt.cs:56
BoolDefault
Three state boolean value.
Definition: Attributes.cs:240
IEnumerator< IField< T > > GetEnumerator()
Definition: Field.cs:800
FormPrompt Prompt(T state, IField< T > field, params object[] args)
Return prompt to send to user.
Definition: IPrompt.cs:357
IField< T > Field(string name)
Return a specific field or null if not present.
Definition: Field.cs:788
virtual DescribeAttribute ValueDescription(object value)
Return the description of a specific value.
Definition: Field.cs:200
Field< T > AddTerms(object value, params string[] terms)
Adds terms for a value.
Definition: Field.cs:446
Field< T > SetPattern(string pattern)
Regular expression for validating strings.
Definition: Field.cs:600
Form definition interface.
Definition: IForm.cs:47
virtual void SaveResources()
Adds any string resources to form localizer.
Definition: Field.cs:248
Field< T > ReplaceTemplate(TemplateAttribute template)
Replace a template in the field.
Definition: Field.cs:570
void AddTemplate(TemplateAttribute template)
Definition: Field.cs:746
Field is used to get a value to set in the form state.
virtual bool Active(T state)
Test to see if field is currently active based on the current state.
Definition: Field.cs:326
Encapsulates the result of a ValidateAsyncDelegate<T>
Definition: IFormDialog.cs:79
virtual async Task< bool > DefineAsync(T state)
Build the prompt and recognizer for dynamically defined fields.
Definition: Field.cs:350
Field< T > SetActive(ActiveDelegate< T > condition)
Define a delegate for checking state to see if field applies.
Definition: Field.cs:524
Base class with declarative implementation of IField.
Definition: Field.cs:67
Field< T > SetNext(NextDelegate< T > next)
Delegate for deciding on the next form step to execute.
Definition: Field.cs:640
Field< T > SetFieldDescription(DescribeAttribute description)
Set the full field description.
Definition: Field.cs:405
Field< T > SetOptional(bool optional=true)
Set whether or not a field is optional.
Definition: Field.cs:493
virtual bool IsUnknown(T state)
Test to see if the field value form state has a value.
Definition: Field.cs:118
string Name
Name of this field.
Definition: IField.cs:415
Field< T > SetDependencies(params string[] dependencies)
Define the fields this field depends on.
Definition: Field.cs:630
virtual IEnumerable< string > Terms(object value)
Given a value return terms that can be used in a dialog to match the object.
Definition: Field.cs:195
virtual object GetValue(T state)
Get this field value from form state.
Definition: Field.cs:108
virtual void SetUnknown(T state)
Set this field value in form state to unknown.
Definition: Field.cs:123
Field< T > RemoveValue(object value)
Removes the description and terms associated with a value.
Definition: Field.cs:465
Field< T > SetFieldTerms(params string[] terms)
Set the terms associated with the field.
Definition: Field.cs:414
Field< T > SetAllowsMultiple(bool multiple=true)
Sets whether or not multiple values are allowed.
Definition: Field.cs:504
virtual async Task< ValidateResult > ValidateAsync(T state, object value)
Validate value to be set on state and return feedback if not valid.
Definition: Field.cs:369
Field< T > AddDescription(object value, DescribeAttribute description)
Adds a full description for a value.
Definition: Field.cs:436
Attribute to override the default description of a field, property or enum value. ...
Definition: Attributes.cs:62