Add validation of patterns.
Fix bug where numeric prompts did not get min/max.
This commit is contained in:
Родитель
1877bc7203
Коммит
1e1f4b3146
|
@ -280,9 +280,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
/// All possible templates.
|
||||
/// </summary>
|
||||
/// <returns>The possible templates.</returns>
|
||||
public string[] Patterns()
|
||||
public string[] Patterns
|
||||
{
|
||||
return _patterns;
|
||||
get
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -56,8 +56,8 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
: base(Guid.NewGuid().ToString(), FieldRole.Confirm, form)
|
||||
{
|
||||
this
|
||||
.Description(_name)
|
||||
.Prompt(prompt)
|
||||
.SetFieldDescription(_name)
|
||||
.SetPrompt(prompt)
|
||||
.AddDescription(true, "Yes")
|
||||
.AddTerms(true, new string[] { "yes", "y", "sure", "ok" })
|
||||
.AddDescription(false, "No")
|
||||
|
@ -74,11 +74,14 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return null;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Dependencies()
|
||||
{
|
||||
return _dependencies;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Dependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
#region IFieldPrompt
|
||||
public override bool Active(T state)
|
||||
{
|
||||
|
|
|
@ -96,14 +96,20 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual bool Optional()
|
||||
public virtual bool Optional
|
||||
{
|
||||
return _optional;
|
||||
get
|
||||
{
|
||||
return _optional;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsNullable()
|
||||
public virtual bool IsNullable
|
||||
{
|
||||
return _isNullable;
|
||||
get
|
||||
{
|
||||
return _isNullable;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Limits(out double min, out double max)
|
||||
|
@ -113,26 +119,36 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return _limited;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> Dependencies()
|
||||
public virtual IEnumerable<string> Dependencies
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IFieldDescription
|
||||
public virtual FieldRole Role()
|
||||
{
|
||||
return _role;
|
||||
get
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Description()
|
||||
public virtual FieldRole Role
|
||||
{
|
||||
return _description;
|
||||
get
|
||||
{
|
||||
return _role;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> Terms()
|
||||
public virtual string FieldDescription
|
||||
{
|
||||
return _terms;
|
||||
get
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> FieldTerms
|
||||
{
|
||||
get
|
||||
{
|
||||
return _terms;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> Terms(object value)
|
||||
|
@ -145,30 +161,45 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return _valueDescriptions[value];
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> ValueDescriptions()
|
||||
public virtual IEnumerable<string> ValueDescriptions
|
||||
{
|
||||
return (from entry in _valueDescriptions select entry.Value);
|
||||
get
|
||||
{
|
||||
return (from entry in _valueDescriptions select entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> Values()
|
||||
public virtual IEnumerable<object> Values
|
||||
{
|
||||
return (from entry in _valueDescriptions select entry.Key);
|
||||
get
|
||||
{
|
||||
return (from entry in _valueDescriptions select entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool AllowsMultiple()
|
||||
public virtual bool AllowsMultiple
|
||||
{
|
||||
return _allowsMultiple;
|
||||
get
|
||||
{
|
||||
return _allowsMultiple;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool AllowDefault()
|
||||
public virtual bool AllowDefault
|
||||
{
|
||||
return _promptDefinition.AllowDefault != BoolDefault.False;
|
||||
get
|
||||
{
|
||||
return _promptDefinition.AllowDefault != BoolDefault.False;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowNumbers()
|
||||
public bool AllowNumbers
|
||||
{
|
||||
_promptDefinition.ApplyDefaults(_form.Configuration.DefaultPrompt);
|
||||
return _promptDefinition.AllowNumbers;
|
||||
get
|
||||
{
|
||||
_promptDefinition.ApplyDefaults(_form.Configuration.DefaultPrompt);
|
||||
return _promptDefinition.AllowNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -222,7 +253,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary>Set the field description. </summary>
|
||||
/// <param name="description">Field description. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Description(string description)
|
||||
public Field<T> SetFieldDescription(string description)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_description = description;
|
||||
|
@ -232,7 +263,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Set the help associated with a field. </summary>
|
||||
/// <param name="help"> Help string. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Help(Prompt help)
|
||||
public Field<T> SetHelp(Prompt help)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_help = help;
|
||||
|
@ -242,7 +273,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Set the terms associated with the field. </summary>
|
||||
/// <param name="terms"> The terms. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Terms(IEnumerable<string> terms)
|
||||
public Field<T> SetFieldTerms(IEnumerable<string> terms)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_terms = terms.ToArray();
|
||||
|
@ -285,7 +316,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Set whether or not a field is optional. </summary>
|
||||
/// <param name="optional"> True if field is optional. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Optional(bool optional = true)
|
||||
public Field<T> SetOptional(bool optional = true)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_optional = optional;
|
||||
|
@ -295,7 +326,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Set whether or not field is nullable. </summary>
|
||||
/// <param name="nullable"> True if field is nullable. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> IsNullable(bool nullable = true)
|
||||
public Field<T> SetIsNullable(bool nullable = true)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_isNullable = nullable;
|
||||
|
@ -305,7 +336,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Sets the field prompt. </summary>
|
||||
/// <param name="prompt"> The prompt. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Prompt(Prompt prompt)
|
||||
public Field<T> SetPrompt(Prompt prompt)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_promptDefinition = prompt;
|
||||
|
@ -315,17 +346,17 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Sets the recognizer for the field. </summary>
|
||||
/// <param name="recognizer"> The recognizer for the field. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Recognizer(IRecognize<T> recognizer)
|
||||
public Field<T> SetRecognizer(IRecognize<T> recognizer)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_recognizer = recognizer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary> Add a template to the field. </summary>
|
||||
/// <summary> Replace a template in the field. </summary>
|
||||
/// <param name="template"> The template. </param>
|
||||
/// <returns> A Field<T> </returns>
|
||||
public Field<T> Template(Template template)
|
||||
public Field<T> ReplaceTemplate(Template template)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
AddTemplate(template);
|
||||
|
@ -335,7 +366,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <summary> Set the field validation. </summary>
|
||||
/// <param name="validate"> The validator. </param>
|
||||
/// <returns> An IField<T> </returns>
|
||||
public IField<T> Validate(ValidateDelegate<T> validate)
|
||||
public IField<T> SetValidation(ValidateDelegate<T> validate)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
_validate = validate;
|
||||
|
@ -346,7 +377,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <param name="min"> The minimum. </param>
|
||||
/// <param name="max"> The maximum. </param>
|
||||
/// <returns> An IField<T> </returns>
|
||||
public IField<T> Numeric(double min, double max)
|
||||
public IField<T> SetLimits(double min, double max)
|
||||
{
|
||||
UpdateAnnotations();
|
||||
SetLimits(min, max, true);
|
||||
|
|
|
@ -1,289 +1,366 @@
|
|||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
public sealed class FormBuilder<T> : IFormBuilder<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly Form<T> _form;
|
||||
|
||||
/// <summary>
|
||||
/// Construct the form builder.
|
||||
/// </summary>
|
||||
/// <param name="ignoreAnnotations">True if you want to ignore any annotations on classes when doing reflection.</param>
|
||||
public FormBuilder(bool ignoreAnnotations = false)
|
||||
{
|
||||
_form = new Form<T>(ignoreAnnotations);
|
||||
}
|
||||
|
||||
public IForm<T> Build()
|
||||
{
|
||||
if (!_form._steps.Any((step) => step.Type == StepType.Field))
|
||||
{
|
||||
var paths = new List<string>();
|
||||
FormBuilder<T>.FieldPaths(typeof(T), "", paths);
|
||||
IFormBuilder<T> builder = this;
|
||||
foreach (var path in paths)
|
||||
{
|
||||
builder.Field(new FieldReflector<T>(path, _form));
|
||||
}
|
||||
builder.Confirm("Is this your selection?\n{*}");
|
||||
}
|
||||
|
||||
return this._form;
|
||||
}
|
||||
|
||||
public FormConfiguration Configuration { get { return _form._configuration; } }
|
||||
|
||||
public IFormBuilder<T> Message(string message, ConditionalDelegate<T> condition = null)
|
||||
{
|
||||
_form._steps.Add(new MessageStep<T>(new Prompt(message), condition, _form));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Message(Prompt prompt, ConditionalDelegate<T> condition = null)
|
||||
{
|
||||
_form._steps.Add(new MessageStep<T>(prompt, condition, _form));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.Validate(validate);
|
||||
}
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, string prompt, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.Validate(validate);
|
||||
}
|
||||
field.Prompt(new Prompt(prompt));
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, Prompt prompt, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.Validate(validate);
|
||||
}
|
||||
field.Prompt(prompt);
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(IField<T> field)
|
||||
{
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> AddRemainingFields(IEnumerable<string> exclude = null)
|
||||
{
|
||||
var exclusions = (exclude == null ? new string[0] : exclude.ToArray());
|
||||
var paths = new List<string>();
|
||||
FieldPaths(typeof(T), "", paths);
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!exclusions.Contains(path))
|
||||
{
|
||||
IField<T> field = _form._fields.Field(path);
|
||||
if (field == null)
|
||||
{
|
||||
AddField(new FieldReflector<T>(path, _form));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(string prompt, ConditionalDelegate<T> condition = null, IEnumerable<string> dependencies = null)
|
||||
{
|
||||
IFormBuilder<T> builder = this;
|
||||
return builder.Confirm(new Prompt(prompt) { ChoiceFormat = "{1}", AllowDefault = BoolDefault.False }, condition, dependencies);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(Prompt prompt, ConditionalDelegate<T> condition = null, IEnumerable<string> dependencies = null)
|
||||
{
|
||||
if (condition == null) condition = state => true;
|
||||
if (dependencies == null)
|
||||
{
|
||||
// Default next steps go from previous field ignoring confirmations back to next confirmation
|
||||
// Last field before confirmation
|
||||
var end = _form._steps.Count();
|
||||
while (end > 0)
|
||||
{
|
||||
if (_form._steps[end - 1].Type == StepType.Field)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--end;
|
||||
}
|
||||
|
||||
var start = end;
|
||||
while (start > 0)
|
||||
{
|
||||
if (_form._steps[start - 1].Type == StepType.Confirm)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--start;
|
||||
}
|
||||
var fields = new List<string>();
|
||||
for (var i = start; i < end; ++i)
|
||||
{
|
||||
if (_form._steps[i].Type == StepType.Field)
|
||||
{
|
||||
fields.Add(_form._steps[i].Name);
|
||||
}
|
||||
}
|
||||
dependencies = fields;
|
||||
}
|
||||
var confirmation = new Confirmation<T>(prompt, condition, dependencies, _form);
|
||||
_form._fields.Add(confirmation);
|
||||
_form._steps.Add(new ConfirmStep<T>(confirmation));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(IFieldPrompt<T> prompt)
|
||||
{
|
||||
// TODO: Need to fill this in
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> OnCompletionAsync(CompletionDelegate<T> callback)
|
||||
{
|
||||
_form._completion = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
private IFormBuilder<T> AddField(IField<T> field)
|
||||
{
|
||||
_form._fields.Add(field);
|
||||
var step = new FieldStep<T>(field.Name, _form);
|
||||
var stepIndex = this._form._steps.FindIndex(s => s.Name == field.Name);
|
||||
if (stepIndex >= 0)
|
||||
{
|
||||
_form._steps[stepIndex] = step;
|
||||
}
|
||||
else
|
||||
{
|
||||
_form._steps.Add(step);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
internal static void FieldPaths(Type type, string path, List<string> paths)
|
||||
{
|
||||
var newPath = (path == "" ? path : path + ".");
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
TypePaths(field.FieldType, newPath + field.Name, paths);
|
||||
}
|
||||
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (property.CanRead && property.CanWrite)
|
||||
{
|
||||
TypePaths(property.PropertyType, newPath + property.Name, paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void TypePaths(Type type, string path, List<string> paths)
|
||||
{
|
||||
if (type.IsClass)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsIEnumerable())
|
||||
{
|
||||
var elt = type.GetGenericElementType();
|
||||
if (elt.IsEnum)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: What to do about enumerations of things other than enums?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldPaths(type, path, paths);
|
||||
}
|
||||
}
|
||||
else if (type.IsEnum)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsIntegral())
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsDouble())
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsNullable() && type.IsValueType)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
public sealed class FormBuilder<T> : IFormBuilder<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly Form<T> _form;
|
||||
|
||||
/// <summary>
|
||||
/// Construct the form builder.
|
||||
/// </summary>
|
||||
/// <param name="ignoreAnnotations">True if you want to ignore any annotations on classes when doing reflection.</param>
|
||||
public FormBuilder(bool ignoreAnnotations = false)
|
||||
{
|
||||
_form = new Form<T>(ignoreAnnotations);
|
||||
}
|
||||
|
||||
public IForm<T> Build()
|
||||
{
|
||||
if (!_form._steps.Any((step) => step.Type == StepType.Field))
|
||||
{
|
||||
var paths = new List<string>();
|
||||
FormBuilder<T>.FieldPaths(typeof(T), "", paths);
|
||||
IFormBuilder<T> builder = this;
|
||||
foreach (var path in paths)
|
||||
{
|
||||
builder.Field(new FieldReflector<T>(path, _form));
|
||||
}
|
||||
builder.Confirm("Is this your selection?\n{*}");
|
||||
}
|
||||
Validate();
|
||||
return this._form;
|
||||
}
|
||||
|
||||
public FormConfiguration Configuration { get { return _form._configuration; } }
|
||||
|
||||
public IFormBuilder<T> Message(string message, ConditionalDelegate<T> condition = null)
|
||||
{
|
||||
_form._steps.Add(new MessageStep<T>(new Prompt(message), condition, _form));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Message(Prompt prompt, ConditionalDelegate<T> condition = null)
|
||||
{
|
||||
_form._steps.Add(new MessageStep<T>(prompt, condition, _form));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.SetValidation(validate);
|
||||
}
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, string prompt, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.SetValidation(validate);
|
||||
}
|
||||
field.SetPrompt(new Prompt(prompt));
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(string name, Prompt prompt, ConditionalDelegate<T> condition = null, ValidateDelegate<T> validate = null)
|
||||
{
|
||||
var field = (condition == null ? new FieldReflector<T>(name, _form) : new Conditional<T>(name, _form, condition));
|
||||
if (validate != null)
|
||||
{
|
||||
field.SetValidation(validate);
|
||||
}
|
||||
field.SetPrompt(prompt);
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Field(IField<T> field)
|
||||
{
|
||||
return AddField(field);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> AddRemainingFields(IEnumerable<string> exclude = null)
|
||||
{
|
||||
var exclusions = (exclude == null ? new string[0] : exclude.ToArray());
|
||||
var paths = new List<string>();
|
||||
FieldPaths(typeof(T), "", paths);
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!exclusions.Contains(path))
|
||||
{
|
||||
IField<T> field = _form._fields.Field(path);
|
||||
if (field == null)
|
||||
{
|
||||
AddField(new FieldReflector<T>(path, _form));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(string prompt, ConditionalDelegate<T> condition = null, IEnumerable<string> dependencies = null)
|
||||
{
|
||||
IFormBuilder<T> builder = this;
|
||||
return builder.Confirm(new Prompt(prompt) { ChoiceFormat = "{1}", AllowDefault = BoolDefault.False }, condition, dependencies);
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(Prompt prompt, ConditionalDelegate<T> condition = null, IEnumerable<string> dependencies = null)
|
||||
{
|
||||
if (condition == null) condition = state => true;
|
||||
if (dependencies == null)
|
||||
{
|
||||
// Default next steps go from previous field ignoring confirmations back to next confirmation
|
||||
// Last field before confirmation
|
||||
var end = _form._steps.Count();
|
||||
while (end > 0)
|
||||
{
|
||||
if (_form._steps[end - 1].Type == StepType.Field)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--end;
|
||||
}
|
||||
|
||||
var start = end;
|
||||
while (start > 0)
|
||||
{
|
||||
if (_form._steps[start - 1].Type == StepType.Confirm)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--start;
|
||||
}
|
||||
var fields = new List<string>();
|
||||
for (var i = start; i < end; ++i)
|
||||
{
|
||||
if (_form._steps[i].Type == StepType.Field)
|
||||
{
|
||||
fields.Add(_form._steps[i].Name);
|
||||
}
|
||||
}
|
||||
dependencies = fields;
|
||||
}
|
||||
var confirmation = new Confirmation<T>(prompt, condition, dependencies, _form);
|
||||
_form._fields.Add(confirmation);
|
||||
_form._steps.Add(new ConfirmStep<T>(confirmation));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> Confirm(IFieldPrompt<T> prompt)
|
||||
{
|
||||
// TODO: Need to fill this in
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFormBuilder<T> OnCompletionAsync(CompletionDelegate<T> callback)
|
||||
{
|
||||
_form._completion = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
private IFormBuilder<T> AddField(IField<T> field)
|
||||
{
|
||||
_form._fields.Add(field);
|
||||
var step = new FieldStep<T>(field.Name, _form);
|
||||
var stepIndex = this._form._steps.FindIndex(s => s.Name == field.Name);
|
||||
if (stepIndex >= 0)
|
||||
{
|
||||
_form._steps[stepIndex] = step;
|
||||
}
|
||||
else
|
||||
{
|
||||
_form._steps.Add(step);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private Dictionary<TemplateUsage, int> _templateArgs = new Dictionary<TemplateUsage, int>
|
||||
{
|
||||
{TemplateUsage.Bool, 0 },
|
||||
{ TemplateUsage.BoolHelp, 1},
|
||||
{ TemplateUsage.Clarify, 1},
|
||||
{ TemplateUsage.CurrentChoice, 0},
|
||||
{ TemplateUsage.DateTime, 0},
|
||||
{ TemplateUsage.DateTimeHelp, 2},
|
||||
{ TemplateUsage.Double, 2},
|
||||
{ TemplateUsage.DoubleHelp, 4},
|
||||
{ TemplateUsage.EnumManyNumberHelp, 3},
|
||||
{ TemplateUsage.EnumOneNumberHelp, 3},
|
||||
{ TemplateUsage.EnumManyWordHelp, 3},
|
||||
{ TemplateUsage.EnumOneWordHelp, 3},
|
||||
{ TemplateUsage.EnumSelectOne, 0},
|
||||
{ TemplateUsage.EnumSelectMany, 0},
|
||||
{ TemplateUsage.Feedback, 1},
|
||||
{ TemplateUsage.Help, 2},
|
||||
{ TemplateUsage.HelpClarify, 2},
|
||||
{ TemplateUsage.HelpConfirm, 2},
|
||||
{ TemplateUsage.HelpNavigation, 2},
|
||||
{ TemplateUsage.Integer, 2},
|
||||
{ TemplateUsage.IntegerHelp, 4},
|
||||
{ TemplateUsage.Navigation, 0},
|
||||
{ TemplateUsage.NavigationCommandHelp, 1},
|
||||
{ TemplateUsage.NavigationFormat, 0},
|
||||
{ TemplateUsage.NavigationHelp, 2},
|
||||
{ TemplateUsage.NoPreference, 0},
|
||||
{ TemplateUsage.NotUnderstood, 1},
|
||||
{ TemplateUsage.StatusFormat, 0},
|
||||
{ TemplateUsage.String, 0},
|
||||
{ TemplateUsage.StringHelp, 2},
|
||||
{ TemplateUsage.Unspecified, 0},
|
||||
};
|
||||
|
||||
private int TemplateArgs(TemplateUsage usage)
|
||||
{
|
||||
int args;
|
||||
if (!_templateArgs.TryGetValue(usage, out args))
|
||||
{
|
||||
throw new ArgumentException("Missing template usage for validation");
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
foreach (var step in _form._steps)
|
||||
{
|
||||
// Validate prompt
|
||||
var annotation = step.Annotation;
|
||||
var name = step.Type == StepType.Field ? step.Name : "";
|
||||
foreach (var pattern in annotation.Patterns)
|
||||
{
|
||||
ValidatePattern(pattern, name, 5);
|
||||
}
|
||||
if (step.Type != StepType.Message)
|
||||
{
|
||||
foreach (TemplateUsage usage in Enum.GetValues(typeof(TemplateUsage)))
|
||||
{
|
||||
foreach (var pattern in step.Field.Template(usage).Patterns)
|
||||
{
|
||||
ValidatePattern(pattern, name, TemplateArgs(usage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ValidatePattern(_form.Configuration.DefaultPrompt.ChoiceFormat, "", 2);
|
||||
}
|
||||
|
||||
private void ValidatePattern(string pattern, string pathName, int maxArgs)
|
||||
{
|
||||
if (!Prompter<T>.ValidatePattern(_form, pattern, pathName, maxArgs))
|
||||
{
|
||||
throw new ArgumentException(string.Format("Illegal pattern: \"{0}\"", pattern));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void FieldPaths(Type type, string path, List<string> paths)
|
||||
{
|
||||
var newPath = (path == "" ? path : path + ".");
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
TypePaths(field.FieldType, newPath + field.Name, paths);
|
||||
}
|
||||
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (property.CanRead && property.CanWrite)
|
||||
{
|
||||
TypePaths(property.PropertyType, newPath + property.Name, paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void TypePaths(Type type, string path, List<string> paths)
|
||||
{
|
||||
if (type.IsClass)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsIEnumerable())
|
||||
{
|
||||
var elt = type.GetGenericElementType();
|
||||
if (elt.IsEnum)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: What to do about enumerations of things other than enums?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldPaths(type, path, paths);
|
||||
}
|
||||
}
|
||||
else if (type.IsEnum)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsIntegral())
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsDouble())
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type.IsNullable() && type.IsValueType)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -474,7 +474,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
if (step.Type == StepType.Confirm)
|
||||
{
|
||||
// Ensure all dependencies have values
|
||||
foreach (var dependency in step.Dependencies())
|
||||
foreach (var dependency in step.Dependencies)
|
||||
{
|
||||
var dstep = _form.Step(dependency);
|
||||
var dstepi = _form.StepIndex(dstep);
|
||||
|
@ -563,8 +563,8 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var navigation = new Prompter<T>(field.Template(TemplateUsage.NavigationCommandHelp), _form, null);
|
||||
var active = (from istep in _form.Steps
|
||||
where istep.Type == StepType.Field && istep.Active(state)
|
||||
select istep.Field.Description());
|
||||
var activeList = Language.BuildList(active, navigation.Annotation().Separator, navigation.Annotation().LastSeparator);
|
||||
select istep.Field.FieldDescription);
|
||||
var activeList = Language.BuildList(active, navigation.Annotation.Separator, navigation.Annotation.LastSeparator);
|
||||
builder.Append("* ");
|
||||
builder.Append(navigation.Prompt(state, "", activeList));
|
||||
feedback = step.Help(state, form, builder.ToString());
|
||||
|
|
|
@ -1,393 +1,393 @@
|
|||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that defines basic access to a field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The form state that is read or written to.</typeparam>
|
||||
public interface IFieldState<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get this field value from form state.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to get field value from.</param>
|
||||
/// <returns>Current value found in state.</returns>
|
||||
object GetValue(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Set this field value in form state.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to set field value in.</param>
|
||||
/// <param name="value">New value.</param>
|
||||
void SetValue(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if the field value form state has a value.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to check.</param>
|
||||
/// <returns>True if value is unknown.</returns>
|
||||
/// <remarks>
|
||||
/// For value types (numbers, bools, date time) a value is unknown only if the field is nullable and it is null.
|
||||
/// For enum based values (both simple and enumerated) they can also be nullable or the 0 enum value if not nullable.
|
||||
/// For non value types like string the test is to see if the field is actually null.
|
||||
/// </remarks>
|
||||
bool IsUnknown(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Set this field value in form state to unknown.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state with field value to set to unknown.</param>
|
||||
/// <remarks>
|
||||
/// For value types (numbers, bools, date time) the value is set to null if nullable.
|
||||
/// For enum types it is set to null if nullable or 0 if not.
|
||||
/// For non value types like string set the value to null.
|
||||
/// </remarks>
|
||||
void SetUnknown(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if field is optional which means that an unknown value is legal.
|
||||
/// </summary>
|
||||
/// <returns>True if field is optional.</returns>
|
||||
bool Optional();
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if field is nullable.
|
||||
/// </summary>
|
||||
/// <returns>True if field is nullable.</returns>
|
||||
bool IsNullable();
|
||||
|
||||
/// <summary>
|
||||
/// Limits of numeric values.
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum possible value.</param>
|
||||
/// <param name="max">Maximum possible value.</param>
|
||||
/// <returns>True if limits limit the underlying data type.</returns>
|
||||
/// <remarks>
|
||||
/// This reflects the result of setting <see cref="Numeric" limits on the possible values./></remarks>
|
||||
bool Limits(out double min, out double max);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the other fields this one depends on.
|
||||
/// </summary>
|
||||
/// <returns>List of field names this one depends on.</returns>
|
||||
/// <remarks>This is mainly useful for <see cref="Confirmation"/> fields.</remarks>
|
||||
IEnumerable<string> Dependencies();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The role the field plays in a form.
|
||||
/// </summary>
|
||||
public enum FieldRole {
|
||||
/// <summary>
|
||||
/// Field is used to get a value to set in the form state.
|
||||
/// </summary>
|
||||
/// <remarks>This is the kind of field generated by <see cref="IForm<T>.Field"/>.</remarks>
|
||||
Value,
|
||||
|
||||
/// <summary>
|
||||
/// Field is used to confirm some settings during the dialog.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the kind of field generated by <see cref="IForm<T>.Confirm"/>.
|
||||
/// </remarks>
|
||||
Confirm };
|
||||
|
||||
/// <summary>
|
||||
/// Describe the information displayed about a field and its values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Throughout this class Description refers to the name of a field or a value
|
||||
/// whereas "terms" tell what people can type to match the field or terms in it.
|
||||
/// When generating terms it is a good idea to include anything that might be reasonable
|
||||
/// for someone to type. The form dialog itself will help clarify any ambiguity. One
|
||||
/// way to do this is to use <see cref="Terms.MatchPhrase"/> which ensures that <see cref="Language.GenerateTerms"/>
|
||||
/// is called on your base terms.
|
||||
/// </remarks>
|
||||
public interface IFieldDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Role field plays in a form.
|
||||
/// </summary>
|
||||
/// <returns>Role field plays in form.</returns>
|
||||
FieldRole Role();
|
||||
|
||||
/// <summary>
|
||||
/// Decription of the field itself.
|
||||
/// </summary>
|
||||
/// <returns>Field description.</returns>
|
||||
/// <remarks>
|
||||
/// This is the value that will be generated in \ref patterns by {&} or choices. {||}.
|
||||
/// </remarks>
|
||||
string Description();
|
||||
|
||||
/// <summary>
|
||||
/// Terms for matching this field.
|
||||
/// </summary>
|
||||
/// <returns>List of term regex for matching the field name.</returns>
|
||||
IEnumerable<string> Terms();
|
||||
|
||||
/// <summary>
|
||||
/// Return the string describing a specific value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value being described.</param>
|
||||
/// <returns>String describing value.</returns>
|
||||
string ValueDescription(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return all possible value descriptions in order to support enumeration.
|
||||
/// </summary>
|
||||
/// <returns>All possible value descriptions.</returns>
|
||||
IEnumerable<string> ValueDescriptions();
|
||||
|
||||
/// <summary>
|
||||
/// Given a value return terms that can be used in a dialog to match the object.
|
||||
/// </summary>
|
||||
/// <param name="value">Value that would result from a match.</param>
|
||||
/// <returns>Enumeration of regex.</returns>
|
||||
IEnumerable<string> Terms(object value);
|
||||
|
||||
/// <summary>
|
||||
/// All possible values or null if it is a data type like number.
|
||||
/// </summary>
|
||||
/// <returns>All possible values.</returns>
|
||||
IEnumerable<object> Values();
|
||||
|
||||
/// <summary>
|
||||
/// Are multiple matches allowed.
|
||||
/// </summary>
|
||||
/// <returns>True if more than one value is allowed.</returns>
|
||||
/// <remarks>This is true is you have a list of enumerated values.</remarks>
|
||||
bool AllowsMultiple();
|
||||
|
||||
/// <summary>
|
||||
/// Allow the default value as an option.
|
||||
/// </summary>
|
||||
/// <returns>True if default values are allowed.</returns>
|
||||
bool AllowDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Allow user input to match numbers shown with enumerated choices.
|
||||
/// </summary>
|
||||
/// <returns>True if numbers are allowed as input.</returns>
|
||||
bool AllowNumbers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction for next step.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As each step in a form completes, the step can determine the next step to take.
|
||||
/// Usually this is just to move onto the next active, uncompleted step, but you can
|
||||
/// also move back or present a list of choices to the user.
|
||||
/// A step is active if its <see cref="ConditionDelegate"/> returns true on the current state.
|
||||
/// A step is ready if it has not already been successfully completed.
|
||||
/// </remarks>
|
||||
public enum StepDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// The form is complete and <see cref="IForm<T>.OnCompletion"/> should be called.
|
||||
/// </summary>
|
||||
Complete,
|
||||
|
||||
/// <summary>
|
||||
/// Move to a named step. If there is more than one name, the user will be asked to choose.
|
||||
/// </summary>
|
||||
Named,
|
||||
|
||||
/// <summary>
|
||||
/// Move to the next step that is Active() and uncompleted.
|
||||
/// </summary>
|
||||
Next,
|
||||
|
||||
/// <summary>
|
||||
/// Move to the previously executed step.
|
||||
/// </summary>
|
||||
Previous,
|
||||
|
||||
/// <summary>
|
||||
/// Quit the form and return failure to the parent dialog.
|
||||
/// </summary>
|
||||
Quit,
|
||||
|
||||
/// <summary>
|
||||
/// Reset the form to start over.
|
||||
/// </summary>
|
||||
Reset
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Next step to take.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class NextStep
|
||||
{
|
||||
/// <summary>
|
||||
/// By default move on to the next active, uncompleted step.
|
||||
/// </summary>
|
||||
public NextStep()
|
||||
{
|
||||
Direction = StepDirection.Next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move as specified in direction.
|
||||
/// </summary>
|
||||
/// <param name="direction">What step to do next.</param>
|
||||
public NextStep(StepDirection direction)
|
||||
{
|
||||
Direction = direction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ask the user which of the fields to move to next.
|
||||
/// </summary>
|
||||
/// <param name="names">Enumeration of possible next steps.</param>
|
||||
public NextStep(IEnumerable<string> names)
|
||||
{
|
||||
Direction = StepDirection.Named;
|
||||
Names = names.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction for next step.
|
||||
/// </summary>
|
||||
public StepDirection Direction;
|
||||
|
||||
/// <summary>
|
||||
/// If this is a named step, one or more named steps to move to. If there are more than one, the user will choose.
|
||||
/// </summary>
|
||||
public string[] Names;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This provides control information about a field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Form state that is being completed.</typeparam>
|
||||
public interface IFieldPrompt<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Test to see if field is currently active based on the current state.
|
||||
/// </summary>
|
||||
/// <returns>True if field is active.</returns>
|
||||
/// <remarks>
|
||||
/// One way to control this is to supply a <see cref="ConditionalDelegate"/> to the
|
||||
/// <see cref="IForm<T>.Field"/> or <see cref="IForm<T>.Confirm"/> steps.
|
||||
/// </remarks>
|
||||
bool Active(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Return a template for building a prompt.
|
||||
/// </summary>
|
||||
/// <param name="usage">Kind of template we are looking for.</param>
|
||||
/// <returns>NULL if no template, otherwise a template annotation.</returns>
|
||||
Template Template(TemplateUsage usage);
|
||||
|
||||
/// <summary>
|
||||
/// Return the prompt associated with a field.
|
||||
/// </summary>
|
||||
/// <returns>A prompt and recognizer packaged together.</returns>
|
||||
IPrompt<T> Prompt();
|
||||
|
||||
/// <summary>
|
||||
/// Validate value to be set on state and return feedback if not valid.
|
||||
/// </summary>
|
||||
/// <param name="state">State before setting value.</param>
|
||||
/// <param name="value">Value to be set in field.</param>
|
||||
/// <returns>Null if OK, otherwise feedback on what should change.</returns>
|
||||
/// <remarks>
|
||||
/// One way to control this is to supply a <see cref="ValidationDelegate"/> to the
|
||||
/// <see cref="IForm<T>.Field"/> or <see cref="IForm<T>.Confirm"/> steps.
|
||||
/// </remarks>
|
||||
Task<string> ValidateAsync(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return the help description for this field.
|
||||
/// </summary>
|
||||
/// <returns>The prompt to use for generating help.</returns>
|
||||
/// <remarks>
|
||||
/// Help is a mixture of field specific help, what a recognizer understands and available commands.
|
||||
/// </remarks>
|
||||
IPrompt<T> Help();
|
||||
|
||||
/// <summary>
|
||||
/// Next step to execute.
|
||||
/// </summary>
|
||||
/// <param name="value">Value in response to prompt.</param>
|
||||
/// <param name="state">Current form state.</param>
|
||||
/// <returns>Next step to execute.</returns>
|
||||
NextStep Next(object value, T state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for all the information about a specific field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Form state interface applies to.</typeparam>
|
||||
public interface IField<T> : IFieldState<T>, IFieldDescription, IFieldPrompt<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this field.
|
||||
/// </summary>
|
||||
/// <returns>Name of this field.</returns>
|
||||
/// <remarks>
|
||||
/// For a value field this is the path in the form state that leads to the value being filled in.
|
||||
/// For a confirm field this is a randomly generated name.
|
||||
/// </remarks>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Form that owns this field
|
||||
/// </summary>
|
||||
IForm<T> Form { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface to track all of the fields in a form.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IFields<T> : IEnumerable<IField<T>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a specific field or null if not present.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of field to find.</param>
|
||||
/// <returns>Field description for name or null.</returns>
|
||||
IField<T> Field(string name);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that defines basic access to a field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The form state that is read or written to.</typeparam>
|
||||
public interface IFieldState<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get this field value from form state.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to get field value from.</param>
|
||||
/// <returns>Current value found in state.</returns>
|
||||
object GetValue(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Set this field value in form state.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to set field value in.</param>
|
||||
/// <param name="value">New value.</param>
|
||||
void SetValue(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if the field value form state has a value.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state to check.</param>
|
||||
/// <returns>True if value is unknown.</returns>
|
||||
/// <remarks>
|
||||
/// For value types (numbers, bools, date time) a value is unknown only if the field is nullable and it is null.
|
||||
/// For enum based values (both simple and enumerated) they can also be nullable or the 0 enum value if not nullable.
|
||||
/// For non value types like string the test is to see if the field is actually null.
|
||||
/// </remarks>
|
||||
bool IsUnknown(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Set this field value in form state to unknown.
|
||||
/// </summary>
|
||||
/// <param name="state">Form state with field value to set to unknown.</param>
|
||||
/// <remarks>
|
||||
/// For value types (numbers, bools, date time) the value is set to null if nullable.
|
||||
/// For enum types it is set to null if nullable or 0 if not.
|
||||
/// For non value types like string set the value to null.
|
||||
/// </remarks>
|
||||
void SetUnknown(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if field is optional which means that an unknown value is legal.
|
||||
/// </summary>
|
||||
/// <returns>True if field is optional.</returns>
|
||||
bool Optional { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Test to see if field is nullable.
|
||||
/// </summary>
|
||||
/// <returns>True if field is nullable.</returns>
|
||||
bool IsNullable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Limits of numeric values.
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum possible value.</param>
|
||||
/// <param name="max">Maximum possible value.</param>
|
||||
/// <returns>True if limits limit the underlying data type.</returns>
|
||||
/// <remarks>
|
||||
/// This reflects the result of setting <see cref="Numeric" limits on the possible values./></remarks>
|
||||
bool Limits(out double min, out double max);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the other fields this one depends on.
|
||||
/// </summary>
|
||||
/// <returns>List of field names this one depends on.</returns>
|
||||
/// <remarks>This is mainly useful for <see cref="Confirmation"/> fields.</remarks>
|
||||
IEnumerable<string> Dependencies { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The role the field plays in a form.
|
||||
/// </summary>
|
||||
public enum FieldRole {
|
||||
/// <summary>
|
||||
/// Field is used to get a value to set in the form state.
|
||||
/// </summary>
|
||||
/// <remarks>This is the kind of field generated by <see cref="IForm<T>.Field"/>.</remarks>
|
||||
Value,
|
||||
|
||||
/// <summary>
|
||||
/// Field is used to confirm some settings during the dialog.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the kind of field generated by <see cref="IForm<T>.Confirm"/>.
|
||||
/// </remarks>
|
||||
Confirm };
|
||||
|
||||
/// <summary>
|
||||
/// Describe the information displayed about a field and its values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Throughout this class Description refers to the name of a field or a value
|
||||
/// whereas "terms" tell what people can type to match the field or terms in it.
|
||||
/// When generating terms it is a good idea to include anything that might be reasonable
|
||||
/// for someone to type. The form dialog itself will help clarify any ambiguity. One
|
||||
/// way to do this is to use <see cref="Terms.MatchPhrase"/> which ensures that <see cref="Language.GenerateTerms"/>
|
||||
/// is called on your base terms.
|
||||
/// </remarks>
|
||||
public interface IFieldDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Role field plays in a form.
|
||||
/// </summary>
|
||||
/// <returns>Role field plays in form.</returns>
|
||||
FieldRole Role { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Decription of the field itself.
|
||||
/// </summary>
|
||||
/// <returns>Field description.</returns>
|
||||
/// <remarks>
|
||||
/// This is the value that will be generated in \ref patterns by {&} or choices. {||}.
|
||||
/// </remarks>
|
||||
string FieldDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Terms for matching this field.
|
||||
/// </summary>
|
||||
/// <returns>List of term regex for matching the field name.</returns>
|
||||
IEnumerable<string> FieldTerms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the string describing a specific value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value being described.</param>
|
||||
/// <returns>String describing value.</returns>
|
||||
string ValueDescription(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return all possible value descriptions in order to support enumeration.
|
||||
/// </summary>
|
||||
/// <returns>All possible value descriptions.</returns>
|
||||
IEnumerable<string> ValueDescriptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Given a value return terms that can be used in a dialog to match the object.
|
||||
/// </summary>
|
||||
/// <param name="value">Value that would result from a match.</param>
|
||||
/// <returns>Enumeration of regex.</returns>
|
||||
IEnumerable<string> Terms(object value);
|
||||
|
||||
/// <summary>
|
||||
/// All possible values or null if it is a data type like number.
|
||||
/// </summary>
|
||||
/// <returns>All possible values.</returns>
|
||||
IEnumerable<object> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Are multiple matches allowed.
|
||||
/// </summary>
|
||||
/// <returns>True if more than one value is allowed.</returns>
|
||||
/// <remarks>This is true is you have a list of enumerated values.</remarks>
|
||||
bool AllowsMultiple { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow the default value as an option.
|
||||
/// </summary>
|
||||
/// <returns>True if default values are allowed.</returns>
|
||||
bool AllowDefault { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow user input to match numbers shown with enumerated choices.
|
||||
/// </summary>
|
||||
/// <returns>True if numbers are allowed as input.</returns>
|
||||
bool AllowNumbers { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction for next step.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As each step in a form completes, the step can determine the next step to take.
|
||||
/// Usually this is just to move onto the next active, uncompleted step, but you can
|
||||
/// also move back or present a list of choices to the user.
|
||||
/// A step is active if its <see cref="ConditionDelegate"/> returns true on the current state.
|
||||
/// A step is ready if it has not already been successfully completed.
|
||||
/// </remarks>
|
||||
public enum StepDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// The form is complete and <see cref="IForm<T>.OnCompletion"/> should be called.
|
||||
/// </summary>
|
||||
Complete,
|
||||
|
||||
/// <summary>
|
||||
/// Move to a named step. If there is more than one name, the user will be asked to choose.
|
||||
/// </summary>
|
||||
Named,
|
||||
|
||||
/// <summary>
|
||||
/// Move to the next step that is Active() and uncompleted.
|
||||
/// </summary>
|
||||
Next,
|
||||
|
||||
/// <summary>
|
||||
/// Move to the previously executed step.
|
||||
/// </summary>
|
||||
Previous,
|
||||
|
||||
/// <summary>
|
||||
/// Quit the form and return failure to the parent dialog.
|
||||
/// </summary>
|
||||
Quit,
|
||||
|
||||
/// <summary>
|
||||
/// Reset the form to start over.
|
||||
/// </summary>
|
||||
Reset
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Next step to take.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class NextStep
|
||||
{
|
||||
/// <summary>
|
||||
/// By default move on to the next active, uncompleted step.
|
||||
/// </summary>
|
||||
public NextStep()
|
||||
{
|
||||
Direction = StepDirection.Next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move as specified in direction.
|
||||
/// </summary>
|
||||
/// <param name="direction">What step to do next.</param>
|
||||
public NextStep(StepDirection direction)
|
||||
{
|
||||
Direction = direction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ask the user which of the fields to move to next.
|
||||
/// </summary>
|
||||
/// <param name="names">Enumeration of possible next steps.</param>
|
||||
public NextStep(IEnumerable<string> names)
|
||||
{
|
||||
Direction = StepDirection.Named;
|
||||
Names = names.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction for next step.
|
||||
/// </summary>
|
||||
public StepDirection Direction;
|
||||
|
||||
/// <summary>
|
||||
/// If this is a named step, one or more named steps to move to. If there are more than one, the user will choose.
|
||||
/// </summary>
|
||||
public string[] Names;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This provides control information about a field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Form state that is being completed.</typeparam>
|
||||
public interface IFieldPrompt<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Test to see if field is currently active based on the current state.
|
||||
/// </summary>
|
||||
/// <returns>True if field is active.</returns>
|
||||
/// <remarks>
|
||||
/// One way to control this is to supply a <see cref="ConditionalDelegate"/> to the
|
||||
/// <see cref="IForm<T>.Field"/> or <see cref="IForm<T>.Confirm"/> steps.
|
||||
/// </remarks>
|
||||
bool Active(T state);
|
||||
|
||||
/// <summary>
|
||||
/// Return a template for building a prompt.
|
||||
/// </summary>
|
||||
/// <param name="usage">Kind of template we are looking for.</param>
|
||||
/// <returns>NULL if no template, otherwise a template annotation.</returns>
|
||||
Template Template(TemplateUsage usage);
|
||||
|
||||
/// <summary>
|
||||
/// Return the prompt associated with a field.
|
||||
/// </summary>
|
||||
/// <returns>A prompt and recognizer packaged together.</returns>
|
||||
IPrompt<T> Prompt();
|
||||
|
||||
/// <summary>
|
||||
/// Validate value to be set on state and return feedback if not valid.
|
||||
/// </summary>
|
||||
/// <param name="state">State before setting value.</param>
|
||||
/// <param name="value">Value to be set in field.</param>
|
||||
/// <returns>Null if OK, otherwise feedback on what should change.</returns>
|
||||
/// <remarks>
|
||||
/// One way to control this is to supply a <see cref="ValidationDelegate"/> to the
|
||||
/// <see cref="IForm<T>.Field"/> or <see cref="IForm<T>.Confirm"/> steps.
|
||||
/// </remarks>
|
||||
Task<string> ValidateAsync(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return the help description for this field.
|
||||
/// </summary>
|
||||
/// <returns>The prompt to use for generating help.</returns>
|
||||
/// <remarks>
|
||||
/// Help is a mixture of field specific help, what a recognizer understands and available commands.
|
||||
/// </remarks>
|
||||
IPrompt<T> Help();
|
||||
|
||||
/// <summary>
|
||||
/// Next step to execute.
|
||||
/// </summary>
|
||||
/// <param name="value">Value in response to prompt.</param>
|
||||
/// <param name="state">Current form state.</param>
|
||||
/// <returns>Next step to execute.</returns>
|
||||
NextStep Next(object value, T state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for all the information about a specific field.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Form state interface applies to.</typeparam>
|
||||
public interface IField<T> : IFieldState<T>, IFieldDescription, IFieldPrompt<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this field.
|
||||
/// </summary>
|
||||
/// <returns>Name of this field.</returns>
|
||||
/// <remarks>
|
||||
/// For a value field this is the path in the form state that leads to the value being filled in.
|
||||
/// For a confirm field this is a randomly generated name.
|
||||
/// </remarks>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Form that owns this field
|
||||
/// </summary>
|
||||
IForm<T> Form { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface to track all of the fields in a form.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IFields<T> : IEnumerable<IField<T>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a specific field or null if not present.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of field to find.</param>
|
||||
/// <returns>Field description for name or null.</returns>
|
||||
IField<T> Field(string name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
internal static IRecognize<T> BuildCommandRecognizer<T>(this IForm<T> form) where T : class
|
||||
{
|
||||
var field = new Field<T>("__commands__", FieldRole.Value, form);
|
||||
field.Prompt(new Prompt(""));
|
||||
field.SetPrompt(new Prompt(""));
|
||||
foreach (var entry in form.Configuration.Commands)
|
||||
{
|
||||
field.AddDescription(entry.Key, entry.Value.Description);
|
||||
|
@ -88,10 +88,10 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
foreach (var nav in form.Fields)
|
||||
{
|
||||
var fterms = nav.Terms();
|
||||
var fterms = nav.FieldTerms;
|
||||
if (fterms != null)
|
||||
{
|
||||
field.AddDescription(nav.Name, nav.Description());
|
||||
field.AddDescription(nav.Name, nav.FieldDescription);
|
||||
field.AddTerms(nav.Name, fterms.ToArray());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
|
@ -185,5 +186,169 @@ namespace Microsoft.Bot.Builder.Form
|
|||
/// to the parent dialog.
|
||||
/// </remarks>
|
||||
IFormBuilder<T> OnCompletionAsync(CompletionDelegate<T> callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default values for the form.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These defaults can all be overriden when you create a form and before you add steps.
|
||||
/// </remarks>
|
||||
public class FormConfiguration
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Default prompt and template format settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When you specify a <see cref="Prompt"/> or <see cref="Template"/>, any format
|
||||
/// value you do not specify will come from this default.
|
||||
/// </remarks>
|
||||
public Prompt DefaultPrompt = new Prompt("")
|
||||
{
|
||||
AllowDefault = BoolDefault.True,
|
||||
ChoiceStyle = ChoiceStyleOptions.Auto,
|
||||
FieldCase = CaseNormalization.Lower,
|
||||
Feedback = FeedbackOptions.Auto,
|
||||
ChoiceFormat = "{0}. {1}",
|
||||
LastSeparator = ", and ",
|
||||
Separator = ", ",
|
||||
ValueCase = CaseNormalization.InitialUpper
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of strings for interpreting a user response as setting an optional field to be unspecified.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The first string is also used to describe not having a preference for an optional field.
|
||||
/// </remarks>
|
||||
public string[] NoPreference = new string[] { "No Preference", "no", "none", "I don'?t care" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of strings for interpreting a user response as asking for the current value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The first value is also used to describe the option of keeping the current value.
|
||||
/// </remarks>
|
||||
public string[] CurrentChoice = new string[] { "Current Choice", "current" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of values for a "yes" response for boolean fields or confirmations.
|
||||
/// </summary>
|
||||
public string[] Yes = new string[] { "Yes", "yes", "y", "sure", "ok" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of values for a "no" response for boolean fields or confirmations.
|
||||
/// </summary>
|
||||
public string[] No = new string[] { "No", "n" };
|
||||
|
||||
/// <summary>
|
||||
/// Default templates to use if not override on the class or field level.
|
||||
/// </summary>
|
||||
public List<Template> Templates = new List<Template>
|
||||
{
|
||||
new Template(TemplateUsage.Bool, "Would you like a {&}? {||}"),
|
||||
new Template(TemplateUsage.BoolHelp, "Please enter 'yes' or 'no'{?, {0}}."),
|
||||
|
||||
// {0} is term being clarified
|
||||
new Template(TemplateUsage.Clarify, "By \"{0}\" {&} did you mean {||}"),
|
||||
|
||||
new Template(TemplateUsage.CurrentChoice, "(current choice: {})"),
|
||||
|
||||
new Template(TemplateUsage.DateTime, "Please enter a date and time for {&} {||}"),
|
||||
// {0} is current choice, {1} is no preference
|
||||
new Template(TemplateUsage.DateTimeHelp, "Please enter a date or time expression like 'Monday' or 'July 3rd'{?, {0}}{?, {1}}."),
|
||||
|
||||
// {0} is min and {1} is max.
|
||||
new Template(TemplateUsage.Double, "Please enter a number {?between {0:F1} and {1:F1}} for {&} {||}") { ChoiceFormat = "{1}" },
|
||||
|
||||
// {0} is current choice, {1} is no preference
|
||||
// {2} is min and {3} is max
|
||||
new Template(TemplateUsage.DoubleHelp, "Please enter a number{? between {2:F1} and {3:F1}}{?, {0}}{?, {1}}."),
|
||||
|
||||
// {0} is min, {1} is max and {2} are enumerated descriptions
|
||||
new Template(TemplateUsage.EnumManyNumberHelp, "You can enter one or more numbers {0}-{1} or words from the descriptions. ({2})"),
|
||||
new Template(TemplateUsage.EnumOneNumberHelp, "You can enter a number {0}-{1} or words from the descriptions. ({2})"),
|
||||
|
||||
// {2} are the words people can type
|
||||
new Template(TemplateUsage.EnumManyWordHelp, "You can enter in one or more selections from the descriptions. ({2})"),
|
||||
new Template(TemplateUsage.EnumOneWordHelp, "You can enter in any words from the descriptions. ({2})"),
|
||||
|
||||
new Template(TemplateUsage.EnumSelectOne, "Please select a {&} {||}"),
|
||||
new Template(TemplateUsage.EnumSelectMany, "Please select one or more {&} {||}"),
|
||||
|
||||
// {0} is the not understood term
|
||||
new Template(TemplateUsage.Feedback, "For {&} I understood {}. {?\"{0}\" is not an option.}"),
|
||||
|
||||
// For {0} is recognizer help and {1} is command help.
|
||||
new Template(TemplateUsage.Help, "You are filling in the {&} field. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpClarify, "You are clarifying a {&} value. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpConfirm, "Please answer the question. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpNavigation, "Choose what field to change. Possible responses:\n{0}\n{1}"),
|
||||
|
||||
// {0} is min and {1} is max if present
|
||||
new Template(TemplateUsage.Integer, "Please enter a number{? between {0} and {1}} for {&} {||}") { ChoiceFormat = "{1}" },
|
||||
// {0} is current choice, {1} is no preference
|
||||
// {2} is min and {3} is max
|
||||
new Template(TemplateUsage.IntegerHelp, "You can enter a number{? between {2} and {3}}{?, {0}}{?, {1}}."),
|
||||
|
||||
new Template(TemplateUsage.Navigation, "What do you want to change? {||}") { FieldCase = CaseNormalization.None },
|
||||
// {0} is list of field names.
|
||||
new Template(TemplateUsage.NavigationCommandHelp, "You can switch to another field by entering its name. ({0})."),
|
||||
new Template(TemplateUsage.NavigationFormat, "{&}({})") {FieldCase = CaseNormalization.None },
|
||||
// {0} is min, {1} is max
|
||||
new Template(TemplateUsage.NavigationHelp, "Choose {?a number from {0}-{1}, or} a field name."),
|
||||
|
||||
new Template(TemplateUsage.NoPreference, "No Preference"),
|
||||
|
||||
// {0} is the term that is not understood
|
||||
new Template(TemplateUsage.NotUnderstood, @"""{0}"" is not a {&} option."),
|
||||
|
||||
new Template(TemplateUsage.StatusFormat, "{&}: {}") {FieldCase = CaseNormalization.None },
|
||||
|
||||
new Template(TemplateUsage.String, "Please enter {&} {||}") { ChoiceFormat = "{1}" },
|
||||
|
||||
// {0} is current choice, {1} is no preference
|
||||
new Template(TemplateUsage.StringHelp, "You can enter anything{?, {0}}{?, {1}}."),
|
||||
|
||||
new Template(TemplateUsage.Unspecified, "Unspecified")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Definitions of the built-in commands.
|
||||
/// </summary>
|
||||
public Dictionary<FormCommand, CommandDescription> Commands = new Dictionary<FormCommand, CommandDescription>()
|
||||
{
|
||||
{FormCommand.Backup, new CommandDescription("Backup", new string[] {"backup", "go back", "back" },
|
||||
"Back: Go back to the previous question.") },
|
||||
{FormCommand.Help, new CommandDescription("Help", new string[] { "help", "choices", @"\?" },
|
||||
"Help: Show the kinds of responses you can enter.") },
|
||||
{FormCommand.Quit, new CommandDescription("Quit", new string[] { "quit", "stop", "finish", "goodbye", "good bye"},
|
||||
"Quit: Quit the form without completing it.") },
|
||||
{FormCommand.Reset, new CommandDescription("Start over", new string[] { "start over", "reset", "clear" },
|
||||
"Reset: Start over filling in the form. (With defaults of your previous entries.)" ) },
|
||||
{FormCommand.Status, new CommandDescription("status", new string[] {"status", "progress", "so far" },
|
||||
"Status: Show your progress in filling in the form so far.") }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Look up a particular template.
|
||||
/// </summary>
|
||||
/// <param name="usage">Desired template.</param>
|
||||
/// <returns>Matching template.</returns>
|
||||
public Template Template(TemplateUsage usage)
|
||||
{
|
||||
Template result = null;
|
||||
foreach (var template in Templates)
|
||||
{
|
||||
if (template.Usage == usage)
|
||||
{
|
||||
result = template;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Debug.Assert(result != null);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -153,166 +153,5 @@ namespace Microsoft.Bot.Builder.Form
|
|||
Help = help;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default values for the form.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These defaults can all be overriden when you create a form and before you add steps.
|
||||
/// </remarks>
|
||||
public class FormConfiguration
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Default prompt and template format settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When you specify a <see cref="Prompt"/> or <see cref="Template"/>, any format
|
||||
/// value you do not specify will come from this default.
|
||||
/// </remarks>
|
||||
public Prompt DefaultPrompt = new Prompt("")
|
||||
{
|
||||
AllowDefault = BoolDefault.True,
|
||||
ChoiceStyle = ChoiceStyleOptions.Auto,
|
||||
FieldCase = CaseNormalization.Lower,
|
||||
Feedback = FeedbackOptions.Auto,
|
||||
ChoiceFormat = "{0}. {1}",
|
||||
LastSeparator = ", and ",
|
||||
Separator = ", ",
|
||||
ValueCase = CaseNormalization.InitialUpper
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of strings for interpreting a user response as setting an optional field to be unspecified.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The first string is also used to describe not having a preference for an optional field.
|
||||
/// </remarks>
|
||||
public string[] NoPreference = new string[] { "No Preference", "no", "none", "I don'?t care" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of strings for interpreting a user response as asking for the current value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The first value is also used to describe the option of keeping the current value.
|
||||
/// </remarks>
|
||||
public string[] CurrentChoice = new string[] { "Current Choice", "current" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of values for a "yes" response for boolean fields or confirmations.
|
||||
/// </summary>
|
||||
public string[] Yes = new string[] { "Yes", "yes", "y", "sure", "ok" };
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of values for a "no" response for boolean fields or confirmations.
|
||||
/// </summary>
|
||||
public string[] No = new string[] { "No", "n" };
|
||||
|
||||
/// <summary>
|
||||
/// Default templates to use if not override on the class or field level.
|
||||
/// </summary>
|
||||
public List<Template> Templates = new List<Template>
|
||||
{
|
||||
new Template(TemplateUsage.Bool, "Would you like a {&}? {||}"),
|
||||
new Template(TemplateUsage.BoolHelp, "Please enter 'yes' or 'no'{?, {0}}."),
|
||||
|
||||
// {0} is term being clarified
|
||||
new Template(TemplateUsage.Clarify, "By \"{0}\" {&} did you mean {||}"),
|
||||
new Template(TemplateUsage.CurrentChoice, "(current choice: {})"),
|
||||
|
||||
new Template(TemplateUsage.DateTime, "Please enter a date and time for {&} {||}"),
|
||||
// {0} is current choice, {1} is no preference
|
||||
new Template(TemplateUsage.DateTimeHelp, "Please enter a date or time expression like 'Monday' or 'July 3rd'{?, {0}}{?, {1}}."),
|
||||
|
||||
new Template(TemplateUsage.Double, "Please enter a number for {&} {||}") { ChoiceFormat = "{1}" },
|
||||
|
||||
// {0} is current choice, {1} is no preference
|
||||
// {2} is min and {3} is max
|
||||
new Template(TemplateUsage.DoubleHelp, "Please enter a number{? between {2:F1} and {3:F1}}{?, {0}}{?, {1}}."),
|
||||
|
||||
// {0} is min, {1} is max and {2} are enumerated descriptions
|
||||
new Template(TemplateUsage.EnumOneNumberHelp, "You can enter a number {0}-{1} or words from the descriptions. ({2})"),
|
||||
new Template(TemplateUsage.EnumManyNumberHelp, "You can enter one or more numbers {0}-{1} or words from the descriptions. ({2})"),
|
||||
|
||||
// {0} are the words people can type
|
||||
new Template(TemplateUsage.EnumOneWordHelp, "You can enter in any words from the descriptions. ({2})"),
|
||||
new Template(TemplateUsage.EnumManyWordHelp, "You can enter in one or more selections from the descriptions. ({2})"),
|
||||
|
||||
new Template(TemplateUsage.EnumSelectOne, "Please select a {&} {||}"),
|
||||
new Template(TemplateUsage.EnumSelectMany, "Please select one or more {&} {||}"),
|
||||
|
||||
// {0} is the not understood term
|
||||
new Template(TemplateUsage.Feedback, "For {&} I understood {}. {?\"{0}\" is not an option.}"),
|
||||
|
||||
// For {0} is recognizer help and {1} is command help.
|
||||
new Template(TemplateUsage.Help, "You are filling in the {&} field. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpConfirm, "Please answer the question. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpClarify, "You are clarifying a {&} value. Possible responses:\n{0}\n{1}"),
|
||||
new Template(TemplateUsage.HelpNavigation, "Choose what field to change. Possible responses:\n{0}\n{1}"),
|
||||
|
||||
// {0} is min and {1} is max if present
|
||||
new Template(TemplateUsage.Integer, "Please enter a number{? between {0} and {1}} for {&} {||}") { ChoiceFormat = "{1}" },
|
||||
// {0} is current choice, {1} is no preference
|
||||
// {2} is min and {3} is max
|
||||
new Template(TemplateUsage.IntegerHelp, "You can enter a number{? between {2} and {3}}{?, {0}}{?, {1}}."),
|
||||
|
||||
new Template(TemplateUsage.Navigation, "What do you want to change? {||}") { FieldCase = CaseNormalization.None },
|
||||
// {0} is list of field names.
|
||||
new Template(TemplateUsage.NavigationCommandHelp, "You can switch to another field by entering its name. ({0})."),
|
||||
new Template(TemplateUsage.NavigationFormat, "{&}({})") {FieldCase = CaseNormalization.None },
|
||||
new Template(TemplateUsage.NavigationHelp, "Choose {?a number from {0}-{1}, or} a field name."),
|
||||
|
||||
new Template(TemplateUsage.NoPreference, "No Preference"),
|
||||
|
||||
// {0} is the term that is not understood
|
||||
new Template(TemplateUsage.NotUnderstood, @"""{0}"" is not a {&} option."),
|
||||
|
||||
new Template(TemplateUsage.StatusFormat, "{&}: {}") {FieldCase = CaseNormalization.None },
|
||||
|
||||
new Template(TemplateUsage.String, "Please enter {&} {||}") { ChoiceFormat = "{1}" },
|
||||
|
||||
// {0} is current choice, {1} is no preference
|
||||
new Template(TemplateUsage.StringHelp, "You can enter anything{?, {0}}{?, {1}}."),
|
||||
|
||||
new Template(TemplateUsage.Unspecified, "Unspecified")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Definitions of the built-in commands.
|
||||
/// </summary>
|
||||
public Dictionary<FormCommand, CommandDescription> Commands = new Dictionary<FormCommand, CommandDescription>()
|
||||
{
|
||||
{FormCommand.Backup, new CommandDescription("Backup", new string[] {"backup", "go back", "back" },
|
||||
"Back: Go back to the previous question.") },
|
||||
{FormCommand.Help, new CommandDescription("Help", new string[] { "help", "choices", @"\?" },
|
||||
"Help: Show the kinds of responses you can enter.") },
|
||||
{FormCommand.Quit, new CommandDescription("Quit", new string[] { "quit", "stop", "finish", "goodbye", "good bye"},
|
||||
"Quit: Quit the form without completing it.") },
|
||||
{FormCommand.Reset, new CommandDescription("Start over", new string[] { "start over", "reset", "clear" },
|
||||
"Reset: Start over filling in the form. (With defaults of your previous entries.)" ) },
|
||||
{FormCommand.Status, new CommandDescription("status", new string[] {"status", "progress", "so far" },
|
||||
"Status: Show your progress in filling in the form so far.") }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Look up a particular template.
|
||||
/// </summary>
|
||||
/// <param name="usage">Desired template.</param>
|
||||
/// <returns>Matching template.</returns>
|
||||
public Template Template(TemplateUsage usage)
|
||||
{
|
||||
Template result = null;
|
||||
foreach(var template in Templates)
|
||||
{
|
||||
if (template.Usage == usage)
|
||||
{
|
||||
result = template;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Debug.Assert(result != null);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// Description of the prompt and how to generate it.
|
||||
/// </summary>
|
||||
/// <returns>Attribute describing how to generate prompt.</returns>
|
||||
TemplateBase Annotation();
|
||||
TemplateBase Annotation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return string to send to user.
|
||||
|
@ -87,9 +87,12 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
_recognizer = recognizer;
|
||||
}
|
||||
|
||||
public TemplateBase Annotation()
|
||||
public TemplateBase Annotation
|
||||
{
|
||||
return _annotation;
|
||||
get
|
||||
{
|
||||
return _annotation;
|
||||
}
|
||||
}
|
||||
|
||||
public string Prompt(T state, string pathName, params object[] args)
|
||||
|
@ -100,7 +103,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
{
|
||||
var field = _form.Fields.Field(pathName);
|
||||
currentChoice = field.Template(TemplateUsage.CurrentChoice).Pattern();
|
||||
if (field.Optional())
|
||||
if (field.Optional)
|
||||
{
|
||||
noValue = field.Template(TemplateUsage.NoPreference).Pattern();
|
||||
}
|
||||
|
@ -113,6 +116,62 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return (response == null ? "" : _spacesPunc.Replace(_spaces.Replace(Language.ANormalization(response), "$1 "), "$1"));
|
||||
}
|
||||
|
||||
public static bool ValidatePattern(IForm<T> form, string pattern, string pathName, int argLimit = 0)
|
||||
{
|
||||
bool ok = true;
|
||||
var fields = form.Fields;
|
||||
foreach (Match match in _args.Matches(pattern))
|
||||
{
|
||||
var expr = match.Groups[1].Value.Trim();
|
||||
int numeric;
|
||||
if (expr == "||")
|
||||
{
|
||||
ok = true;
|
||||
}
|
||||
else if (expr.StartsWith("&"))
|
||||
{
|
||||
var name = expr.Substring(1);
|
||||
if (name == "") name = pathName;
|
||||
ok = (name == "" || fields.Field(name) != null);
|
||||
}
|
||||
else if (expr.StartsWith("?"))
|
||||
{
|
||||
ok = ValidatePattern(form, expr.Substring(1), pathName, argLimit);
|
||||
}
|
||||
else if (expr.StartsWith("["))
|
||||
{
|
||||
if (expr.EndsWith("]"))
|
||||
{
|
||||
ok = ValidatePattern(form, expr.Substring(1, expr.Length - 2), pathName, argLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else if (expr.StartsWith("*"))
|
||||
{
|
||||
ok = (expr == "*" || expr == "*filled");
|
||||
}
|
||||
else if (TryParseFormat(expr, out numeric))
|
||||
{
|
||||
ok = numeric <= argLimit - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var formatArgs = expr.Split(':');
|
||||
var name = formatArgs[0];
|
||||
if (name == "") name = pathName;
|
||||
ok = (name == "" || fields.Field(name) != null);
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private string ExpandTemplate(string template, string currentChoice, string noValue, T state, string pathName, object[] args)
|
||||
{
|
||||
bool foundUnspecified = false;
|
||||
|
@ -126,15 +185,10 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
var substitute = "";
|
||||
if (expr.StartsWith("&"))
|
||||
{
|
||||
var spec = expr.Substring(1).Split(':');
|
||||
var name = spec[0];
|
||||
if (name == "")
|
||||
{
|
||||
// Use default pathname
|
||||
name = pathName;
|
||||
}
|
||||
var name = expr.Substring(1);
|
||||
if (name == "") name = pathName;
|
||||
var pathField = _form.Fields.Field(name);
|
||||
substitute = Normalize(pathField == null ? pathName : pathField.Description(), _annotation.FieldCase);
|
||||
substitute = Normalize(pathField == null ? pathName : pathField.FieldDescription, _annotation.FieldCase);
|
||||
}
|
||||
else if (expr == "||")
|
||||
{
|
||||
|
@ -143,7 +197,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
var values = _recognizer.ValueDescriptions();
|
||||
if (_annotation.AllowDefault != BoolDefault.False)
|
||||
{
|
||||
if (!field.Optional())
|
||||
if (!field.Optional)
|
||||
{
|
||||
if (!field.IsUnknown(state))
|
||||
{
|
||||
|
@ -218,7 +272,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
{
|
||||
builder.Append("\n");
|
||||
}
|
||||
foreach (var entry in (from step in _form.Fields where (!filled || !step.IsUnknown(state)) && step.Role() == FieldRole.Value && step.Active(state) select step))
|
||||
foreach (var entry in (from step in _form.Fields where (!filled || !step.IsUnknown(state)) && step.Role== FieldRole.Value && step.Active(state) select step))
|
||||
{
|
||||
builder.Append("* ").AppendLine(format.Prompt(state, entry.Name));
|
||||
}
|
||||
|
@ -328,7 +382,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return (foundUnspecified ? null : response.Append(template.Substring(last, template.Length - last)).ToString());
|
||||
}
|
||||
|
||||
private bool TryParseFormat(string format, out int number)
|
||||
private static bool TryParseFormat(string format, out int number)
|
||||
{
|
||||
var args = format.Split(':');
|
||||
return int.TryParse(args[0], out number);
|
||||
|
|
|
@ -1,200 +1,207 @@
|
|||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of special kinds of matches.
|
||||
/// </summary>
|
||||
public enum SpecialValues {
|
||||
/// <summary>
|
||||
/// Match corresponds to a field, not a specific value in the field.
|
||||
/// </summary>
|
||||
Field
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Describe a possible match in the user input.
|
||||
/// </summary>
|
||||
public class TermMatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a match.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of match in input string.</param>
|
||||
/// <param name="length">Length of match in input string.</param>
|
||||
/// <param name="confidence">Confidence of match, 0-1.0.</param>
|
||||
/// <param name="value">The underlying C# value for the match.</param>
|
||||
public TermMatch(int start, int length, double confidence, object value)
|
||||
{
|
||||
Start = start;
|
||||
Length = length;
|
||||
Confidence = confidence;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start of match in input string.
|
||||
/// </summary>
|
||||
public readonly int Start;
|
||||
|
||||
/// <summary>
|
||||
/// End of match in input string.
|
||||
/// </summary>
|
||||
public int End { get { return Start + Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Length of match in input string.
|
||||
/// </summary>
|
||||
public readonly int Length;
|
||||
|
||||
/// <summary>
|
||||
/// Confidence of match, 0-1.0.
|
||||
/// </summary>
|
||||
public readonly double Confidence;
|
||||
|
||||
/// <summary>
|
||||
/// Underlying C# value.
|
||||
/// </summary>
|
||||
public readonly object Value;
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this covers the same span as match.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if both cover the same span.</returns>
|
||||
public bool Same(TermMatch match)
|
||||
{
|
||||
return Start == match.Start && End == match.End;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this completely covers match.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if this covers all of match.</returns>
|
||||
public bool Covers(TermMatch match)
|
||||
{
|
||||
return Start <= match.Start && End >= match.End && (Start != match.Start || End != match.End);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this overlaps with match in input.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if the matches overlap in the input.</returns>
|
||||
public bool Overlaps(TermMatch match)
|
||||
{
|
||||
return (match.Start <= End && Start <= match.Start && End <= match.End) // tmtm
|
||||
|| (Start <= match.End && match.Start <= Start && match.End <= End) // mtmt
|
||||
|| (Start <= match.Start && End >= match.End) // tmmt
|
||||
|| (match.Start <= Start && match.End >= End) // mttm
|
||||
;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("TermMatch({0}, {1}, {2}-{3})", Value, Confidence, Start, Start + Length);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TermMatch && this == (TermMatch)obj;
|
||||
}
|
||||
|
||||
public static bool operator ==(TermMatch m1, TermMatch m2)
|
||||
{
|
||||
return ReferenceEquals(m1, m2) || (!ReferenceEquals(m1, null) && !ReferenceEquals(m2, null) && m1.Start == m2.Start && m1.Length == m2.Length && m1.Confidence == m2.Confidence && m1.Value == m2.Value);
|
||||
}
|
||||
|
||||
public static bool operator !=(TermMatch m1, TermMatch m2)
|
||||
{
|
||||
return !(m1 == m2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Start.GetHashCode() ^ Length.GetHashCode() ^ Confidence.GetHashCode() ^ Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for recognizers that look for matches in user input.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Underlying form state.</typeparam>
|
||||
public interface IRecognize<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Return all possible values or null if a primitive type.
|
||||
/// </summary>
|
||||
/// <returns>All possible values.</returns>
|
||||
IEnumerable<object> Values();
|
||||
|
||||
/// <summary>
|
||||
/// Return all possible value descriptions in order to support enumeration.
|
||||
/// </summary>
|
||||
/// <returns>All possible value descriptions.</returns>
|
||||
IEnumerable<string> ValueDescriptions();
|
||||
|
||||
/// <summary>
|
||||
/// Return the description of a specific value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to get description of.</param>
|
||||
/// <returns></returns>
|
||||
string ValueDescription(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return valid inputs to describe a particular value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value being checked.</param>
|
||||
/// <returns>Valid inputs for describing value.</returns>
|
||||
IEnumerable<string> ValidInputs(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return the help string describing what are valid inputs to the recognizer.
|
||||
/// </summary>
|
||||
/// <returns>Help on what the recognizer accepts.</returns>
|
||||
string Help(T state, object defaultValue = null);
|
||||
|
||||
/// <summary>
|
||||
/// Return the matches found in the input.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string being matched.</param>
|
||||
/// <param name="defaultValue">The default value or null if none.</param>
|
||||
/// <returns>Match records.</returns>
|
||||
IEnumerable<TermMatch> Matches(string input, object defaultValue = null);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of special kinds of matches.
|
||||
/// </summary>
|
||||
public enum SpecialValues {
|
||||
/// <summary>
|
||||
/// Match corresponds to a field, not a specific value in the field.
|
||||
/// </summary>
|
||||
Field
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Describe a possible match in the user input.
|
||||
/// </summary>
|
||||
public class TermMatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a match.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of match in input string.</param>
|
||||
/// <param name="length">Length of match in input string.</param>
|
||||
/// <param name="confidence">Confidence of match, 0-1.0.</param>
|
||||
/// <param name="value">The underlying C# value for the match.</param>
|
||||
public TermMatch(int start, int length, double confidence, object value)
|
||||
{
|
||||
Start = start;
|
||||
Length = length;
|
||||
Confidence = confidence;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start of match in input string.
|
||||
/// </summary>
|
||||
public readonly int Start;
|
||||
|
||||
/// <summary>
|
||||
/// End of match in input string.
|
||||
/// </summary>
|
||||
public int End { get { return Start + Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Length of match in input string.
|
||||
/// </summary>
|
||||
public readonly int Length;
|
||||
|
||||
/// <summary>
|
||||
/// Confidence of match, 0-1.0.
|
||||
/// </summary>
|
||||
public readonly double Confidence;
|
||||
|
||||
/// <summary>
|
||||
/// Underlying C# value.
|
||||
/// </summary>
|
||||
public readonly object Value;
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this covers the same span as match.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if both cover the same span.</returns>
|
||||
public bool Same(TermMatch match)
|
||||
{
|
||||
return Start == match.Start && End == match.End;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this completely covers match.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if this covers all of match.</returns>
|
||||
public bool Covers(TermMatch match)
|
||||
{
|
||||
return Start <= match.Start && End >= match.End && (Start != match.Start || End != match.End);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if this overlaps with match in input.
|
||||
/// </summary>
|
||||
/// <param name="match">TermMatch to compare.</param>
|
||||
/// <returns>True if the matches overlap in the input.</returns>
|
||||
public bool Overlaps(TermMatch match)
|
||||
{
|
||||
return (match.Start <= End && Start <= match.Start && End <= match.End) // tmtm
|
||||
|| (Start <= match.End && match.Start <= Start && match.End <= End) // mtmt
|
||||
|| (Start <= match.Start && End >= match.End) // tmmt
|
||||
|| (match.Start <= Start && match.End >= End) // mttm
|
||||
;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("TermMatch({0}, {1}, {2}-{3})", Value, Confidence, Start, Start + Length);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TermMatch && this == (TermMatch)obj;
|
||||
}
|
||||
|
||||
public static bool operator ==(TermMatch m1, TermMatch m2)
|
||||
{
|
||||
return ReferenceEquals(m1, m2) || (!ReferenceEquals(m1, null) && !ReferenceEquals(m2, null) && m1.Start == m2.Start && m1.Length == m2.Length && m1.Confidence == m2.Confidence && m1.Value == m2.Value);
|
||||
}
|
||||
|
||||
public static bool operator !=(TermMatch m1, TermMatch m2)
|
||||
{
|
||||
return !(m1 == m2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Start.GetHashCode() ^ Length.GetHashCode() ^ Confidence.GetHashCode() ^ Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for recognizers that look for matches in user input.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Underlying form state.</typeparam>
|
||||
public interface IRecognize<T>
|
||||
{
|
||||
#region Documentation
|
||||
/// <summary> Return the arguments to pass to the prompt. </summary>
|
||||
///<remarks>For example a numeric recognizer might pass min and max values.</remarks>
|
||||
/// <returns> An array of arguments.</returns>
|
||||
#endregion
|
||||
object[] PromptArgs();
|
||||
|
||||
/// <summary>
|
||||
/// Return all possible values or null if a primitive type.
|
||||
/// </summary>
|
||||
/// <returns>All possible values.</returns>
|
||||
IEnumerable<object> Values();
|
||||
|
||||
/// <summary>
|
||||
/// Return all possible value descriptions in order to support enumeration.
|
||||
/// </summary>
|
||||
/// <returns>All possible value descriptions.</returns>
|
||||
IEnumerable<string> ValueDescriptions();
|
||||
|
||||
/// <summary>
|
||||
/// Return the description of a specific value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to get description of.</param>
|
||||
/// <returns></returns>
|
||||
string ValueDescription(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return valid inputs to describe a particular value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value being checked.</param>
|
||||
/// <returns>Valid inputs for describing value.</returns>
|
||||
IEnumerable<string> ValidInputs(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return the help string describing what are valid inputs to the recognizer.
|
||||
/// </summary>
|
||||
/// <returns>Help on what the recognizer accepts.</returns>
|
||||
string Help(T state, object defaultValue = null);
|
||||
|
||||
/// <summary>
|
||||
/// Return the matches found in the input.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string being matched.</param>
|
||||
/// <param name="defaultValue">The default value or null if none.</param>
|
||||
/// <returns>Match records.</returns>
|
||||
IEnumerable<TermMatch> Matches(string input, object defaultValue = null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,83 +1,85 @@
|
|||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
internal enum StepPhase { Ready, Responding, Completed };
|
||||
internal enum StepType { Field, Confirm, Navigation, Message };
|
||||
|
||||
internal struct StepResult
|
||||
{
|
||||
internal StepResult(NextStep next, string feedback, string prompt)
|
||||
{
|
||||
this.Next = next;
|
||||
this.Feedback = feedback;
|
||||
this.Prompt = prompt;
|
||||
}
|
||||
|
||||
internal NextStep Next { get; set; }
|
||||
internal string Feedback { get; set; }
|
||||
internal string Prompt { get; set; }
|
||||
}
|
||||
|
||||
internal interface IStep<T>
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
StepType Type { get; }
|
||||
|
||||
IField<T> Field { get; }
|
||||
|
||||
bool Active(T state);
|
||||
|
||||
string Start(IDialogContext context, T state, FormState form);
|
||||
|
||||
IEnumerable<TermMatch> Match(IDialogContext context, T state, FormState form, string input, out string lastInput);
|
||||
|
||||
Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches);
|
||||
|
||||
string NotUnderstood(IDialogContext context, T state, FormState form, string input);
|
||||
|
||||
string Help(T state, FormState form, string commandHelp);
|
||||
|
||||
bool Back(IDialogContext context, T state, FormState form);
|
||||
|
||||
IEnumerable<string> Dependencies();
|
||||
}
|
||||
|
||||
}
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Microsoft Bot Framework: http://botframework.com
|
||||
//
|
||||
// Bot Builder SDK Github:
|
||||
// https://github.com/Microsoft/BotBuilder
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
//
|
||||
// MIT License:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
internal enum StepPhase { Ready, Responding, Completed };
|
||||
internal enum StepType { Field, Confirm, Navigation, Message };
|
||||
|
||||
internal struct StepResult
|
||||
{
|
||||
internal StepResult(NextStep next, string feedback, string prompt)
|
||||
{
|
||||
this.Next = next;
|
||||
this.Feedback = feedback;
|
||||
this.Prompt = prompt;
|
||||
}
|
||||
|
||||
internal NextStep Next { get; set; }
|
||||
internal string Feedback { get; set; }
|
||||
internal string Prompt { get; set; }
|
||||
}
|
||||
|
||||
internal interface IStep<T>
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
StepType Type { get; }
|
||||
|
||||
TemplateBase Annotation { get; }
|
||||
|
||||
IField<T> Field { get; }
|
||||
|
||||
bool Active(T state);
|
||||
|
||||
string Start(IDialogContext context, T state, FormState form);
|
||||
|
||||
IEnumerable<TermMatch> Match(IDialogContext context, T state, FormState form, string input, out string lastInput);
|
||||
|
||||
Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches);
|
||||
|
||||
string NotUnderstood(IDialogContext context, T state, FormState form, string input);
|
||||
|
||||
string Help(T state, FormState form, string commandHelp);
|
||||
|
||||
bool Back(IDialogContext context, T state, FormState form);
|
||||
|
||||
IEnumerable<string> Dependencies { get; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,21 +69,25 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
{
|
||||
var configuration = field.Form.Configuration;
|
||||
_form = field.Form;
|
||||
_allowNumbers = field.AllowNumbers();
|
||||
_description = field.Description();
|
||||
_terms = field.Terms().ToArray();
|
||||
_values = field.Values().ToArray();
|
||||
_valueDescriptions = field.ValueDescriptions().ToArray();
|
||||
_allowNumbers = field.AllowNumbers;
|
||||
_description = field.FieldDescription;
|
||||
_terms = field.FieldTerms.ToArray();
|
||||
_values = field.Values.ToArray();
|
||||
_valueDescriptions = field.ValueDescriptions.ToArray();
|
||||
_descriptionDelegate = (value) => field.ValueDescription(value);
|
||||
_termsDelegate = (value) => field.Terms(value);
|
||||
_helpFormat = field.Template(field.AllowNumbers()
|
||||
? (field.AllowsMultiple() ? TemplateUsage.EnumManyNumberHelp : TemplateUsage.EnumOneNumberHelp)
|
||||
: (field.AllowsMultiple() ? TemplateUsage.EnumManyWordHelp : TemplateUsage.EnumOneWordHelp));
|
||||
_noPreference = field.Optional() ? configuration.NoPreference.ToArray() : null;
|
||||
_helpFormat = field.Template(field.AllowNumbers ? (field.AllowsMultiple? TemplateUsage.EnumManyNumberHelp : TemplateUsage.EnumOneNumberHelp)
|
||||
: (field.AllowsMultiple? TemplateUsage.EnumManyWordHelp : TemplateUsage.EnumOneWordHelp));
|
||||
_noPreference = field.Optional? configuration.NoPreference.ToArray() : null;
|
||||
_currentChoice = configuration.CurrentChoice.FirstOrDefault();
|
||||
BuildPerValueMatcher(configuration.CurrentChoice);
|
||||
}
|
||||
|
||||
public object[] PromptArgs()
|
||||
{
|
||||
return new object[0];
|
||||
}
|
||||
|
||||
public IEnumerable<object> Values()
|
||||
{
|
||||
return _values;
|
||||
|
@ -356,9 +360,9 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
_field = field;
|
||||
_currentChoices = new HashSet<string>(from choice in field.Form.Configuration.CurrentChoice
|
||||
select choice.Trim().ToLower());
|
||||
if (field.Optional())
|
||||
if (field.Optional)
|
||||
{
|
||||
if (field.IsNullable())
|
||||
if (field.IsNullable)
|
||||
{
|
||||
_noPreference = new HashSet<string>(from choice in field.Form.Configuration.NoPreference
|
||||
select choice.Trim().ToLower());
|
||||
|
@ -370,6 +374,11 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
}
|
||||
}
|
||||
|
||||
public virtual object[] PromptArgs()
|
||||
{
|
||||
return new object[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract method for parsing input.
|
||||
/// </summary>
|
||||
|
@ -377,12 +386,6 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// <returns>TermMatch if input is a match.</returns>
|
||||
public abstract TermMatch Parse(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Match input with optional default value.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IEnumerable<TermMatch> Matches(string input, object defaultValue = null)
|
||||
{
|
||||
var matchValue = input.Trim().ToLower();
|
||||
|
@ -428,10 +431,10 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
protected List<object> HelpArgs(T state, object defaultValue)
|
||||
{
|
||||
var args = new List<object>();
|
||||
if (defaultValue != null || _field.Optional())
|
||||
if (defaultValue != null || _field.Optional)
|
||||
{
|
||||
args.Add(_field.Form.Configuration.CurrentChoice.First() + " or 'c'");
|
||||
if (_field.Optional())
|
||||
if (_field.Optional)
|
||||
{
|
||||
args.Add(_field.Form.Configuration.NoPreference.First());
|
||||
}
|
||||
|
@ -584,6 +587,11 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
_max = (long)max;
|
||||
}
|
||||
|
||||
public override object[] PromptArgs()
|
||||
{
|
||||
return _showLimits ? new object[] { _min, _max } : new object[] { null, null };
|
||||
}
|
||||
|
||||
public override string ValueDescription(object value)
|
||||
{
|
||||
return ((long)Convert.ChangeType(value, typeof(long))).ToString(_culture.NumberFormat);
|
||||
|
@ -646,6 +654,11 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
_showLimits = field.Limits(out _min, out _max);
|
||||
}
|
||||
|
||||
public override object[] PromptArgs()
|
||||
{
|
||||
return _showLimits ? new object[] { _min, _max } : new object[] { null, null };
|
||||
}
|
||||
|
||||
public override string ValueDescription(object value)
|
||||
{
|
||||
return ((double)Convert.ChangeType(value, typeof(double))).ToString(_culture.NumberFormat);
|
||||
|
|
|
@ -65,6 +65,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
|
||||
public TemplateBase Annotation
|
||||
{
|
||||
get { return _field.Prompt().Annotation; }
|
||||
}
|
||||
|
||||
public IField<T> Field
|
||||
{
|
||||
get
|
||||
|
@ -82,7 +87,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
{
|
||||
form.SetPhase(StepPhase.Responding);
|
||||
form.StepState = new FieldStepState(FieldStepStates.SentPrompt);
|
||||
return _field.Prompt().Prompt(state, _name);
|
||||
return _field.Prompt().Prompt(state, _name, _field.Prompt().Recognizer().PromptArgs());
|
||||
}
|
||||
|
||||
public IEnumerable<TermMatch> Match(IDialogContext context, T state, FormState form, string input, out string lastInput)
|
||||
|
@ -142,7 +147,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
// 2) Could be overlapping matches like "onion".
|
||||
// 3) Could be multiple matches where only one is expected.
|
||||
|
||||
if (!_field.AllowsMultiple())
|
||||
if (!_field.AllowsMultiple)
|
||||
{
|
||||
// Create a single group of all possibilities if only want one value
|
||||
var mergedGroup = groups.SelectMany((group) => group).ToList();
|
||||
|
@ -188,7 +193,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_field.AllowsMultiple())
|
||||
if (_field.AllowsMultiple)
|
||||
{
|
||||
response = settled;
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
|
@ -205,11 +210,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var unmatchedWords = string.Join(" ", unmatched);
|
||||
var nonNoise = Language.NonNoiseWords(Language.WordBreak(unmatchedWords)).ToArray();
|
||||
fieldState.Unmatched = null;
|
||||
if (_field.Prompt().Annotation().Feedback == FeedbackOptions.Always)
|
||||
if (_field.Prompt().Annotation.Feedback == FeedbackOptions.Always)
|
||||
{
|
||||
fieldState.Unmatched = string.Join(" ", nonNoise);
|
||||
}
|
||||
else if (_field.Prompt().Annotation().Feedback == FeedbackOptions.Auto
|
||||
else if (_field.Prompt().Annotation.Feedback == FeedbackOptions.Auto
|
||||
&& nonNoise.Length > 0
|
||||
&& unmatched.Count() > 0)
|
||||
{
|
||||
|
@ -234,7 +239,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
else
|
||||
{
|
||||
// No clarification left, so set the field
|
||||
if (_field.AllowsMultiple())
|
||||
if (_field.AllowsMultiple)
|
||||
{
|
||||
response = fieldState.Settled;
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
|
@ -291,7 +296,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
if (fieldState.State == FieldStepStates.SentClarify)
|
||||
{
|
||||
var desc = _field.Form.Fields.Field(_name);
|
||||
if (desc.AllowsMultiple())
|
||||
if (desc.AllowsMultiple)
|
||||
{
|
||||
desc.SetValue(state, fieldState.Settled);
|
||||
}
|
||||
|
@ -322,9 +327,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return "* " + template.Prompt(state, _name, "* " + template.Recognizer().Help(state, _field.GetValue(state)), commandHelp);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Dependencies()
|
||||
public IEnumerable<string> Dependencies
|
||||
{
|
||||
return new string[0];
|
||||
get
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
private IPrompt<T> Template(TemplateUsage usage, IRecognize<T> recognizer = null)
|
||||
|
@ -340,7 +348,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
{
|
||||
desc.SetUnknown(state);
|
||||
}
|
||||
else if (desc.AllowsMultiple())
|
||||
else if (desc.AllowsMultiple)
|
||||
{
|
||||
if (value is System.Collections.IEnumerable)
|
||||
{
|
||||
|
@ -388,9 +396,9 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var field = new Field<T>("__clarify__", FieldRole.Value, _field.Form);
|
||||
var template = _field.Template(TemplateUsage.Clarify);
|
||||
var helpTemplate = _field.Template(template.AllowNumbers ? TemplateUsage.EnumOneNumberHelp : TemplateUsage.EnumManyNumberHelp);
|
||||
field.Prompt(new Prompt(template));
|
||||
field.Template(_field.Template(TemplateUsage.Clarify));
|
||||
field.Template(helpTemplate);
|
||||
field.SetPrompt(new Prompt(template));
|
||||
field.ReplaceTemplate(_field.Template(TemplateUsage.Clarify));
|
||||
field.ReplaceTemplate(helpTemplate);
|
||||
foreach (var value in clarify.Values)
|
||||
{
|
||||
field.AddDescription(value, recognizer.ValueDescription(value));
|
||||
|
@ -472,6 +480,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
|
||||
public TemplateBase Annotation
|
||||
{
|
||||
get { return _field.Prompt().Annotation; }
|
||||
}
|
||||
|
||||
public string NotUnderstood(IDialogContext context, T state, FormState form, string input)
|
||||
{
|
||||
var template = _field.Template(TemplateUsage.NotUnderstood);
|
||||
|
@ -509,9 +522,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Dependencies()
|
||||
public IEnumerable<string> Dependencies
|
||||
{
|
||||
return _field.Dependencies();
|
||||
get
|
||||
{
|
||||
return _field.Dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IField<T> _field;
|
||||
|
@ -527,14 +543,14 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var field = _fields.Field(_name);
|
||||
var template = field.Template(TemplateUsage.Navigation);
|
||||
var recField = new Field<T>("__navigate__", FieldRole.Value, form)
|
||||
.Prompt(new Prompt(template));
|
||||
.SetPrompt(new Prompt(template));
|
||||
var fieldPrompt = field.Template(TemplateUsage.NavigationFormat);
|
||||
foreach (var value in formState.Next.Names)
|
||||
{
|
||||
var prompter = new Prompter<T>(fieldPrompt, form, _fields.Field(value as string).Prompt().Recognizer());
|
||||
recField
|
||||
.AddDescription(value, prompter.Prompt(state, value as string))
|
||||
.AddTerms(value, _fields.Field(value as string).Terms());
|
||||
.AddTerms(value, _fields.Field(value as string).FieldTerms);
|
||||
}
|
||||
var recognizer = new RecognizeEnumeration<T>(recField);
|
||||
_prompt = new Prompter<T>(template, form, recognizer);
|
||||
|
@ -573,6 +589,14 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
|
||||
public TemplateBase Annotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return _prompt.Annotation;
|
||||
}
|
||||
}
|
||||
|
||||
public string NotUnderstood(IDialogContext context, T state, FormState form, string input)
|
||||
{
|
||||
var field = _fields.Field(_name);
|
||||
|
@ -607,9 +631,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return "* " + prompt.Prompt(state, _name, "* " + recognizer.Help(state, null), commandHelp);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Dependencies()
|
||||
public IEnumerable<string> Dependencies
|
||||
{
|
||||
return new string[0];
|
||||
get
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
private string _name;
|
||||
|
@ -642,9 +669,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<string> Dependencies()
|
||||
public IEnumerable<string> Dependencies
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IField<T> Field
|
||||
|
@ -668,6 +698,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
|
||||
public TemplateBase Annotation
|
||||
{
|
||||
get { return _prompt.Annotation; }
|
||||
}
|
||||
|
||||
public string NotUnderstood(IDialogContext context, T state, FormState form, string input)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Bot.Builder;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Microsoft.Bot.Builder.Form;
|
||||
|
@ -140,8 +141,56 @@ namespace Microsoft.Bot.Builder.FormTest
|
|||
context.Call<T>(form, root.CallChild);
|
||||
}
|
||||
|
||||
public static void TestValidate()
|
||||
{
|
||||
try
|
||||
{
|
||||
var form = new FormBuilder<PizzaOrder>()
|
||||
.Message("{NotField}")
|
||||
.Build();
|
||||
Debug.Fail("Validation failed");
|
||||
}
|
||||
catch (ArgumentException )
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
var form = new FormBuilder<PizzaOrder>()
|
||||
.Message("[{NotField}]")
|
||||
.Build();
|
||||
Debug.Fail("Validation failed");
|
||||
}
|
||||
catch (ArgumentException )
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
var form = new FormBuilder<PizzaOrder>()
|
||||
.Message("{? {[{NotField}]}")
|
||||
.Build();
|
||||
Debug.Fail("Validation failed");
|
||||
}
|
||||
catch (ArgumentException )
|
||||
{
|
||||
}
|
||||
/*
|
||||
try
|
||||
{
|
||||
var form = new FormBuilder<PizzaOrder>();
|
||||
.Field(new FieldReflector<PizzaOrder>(nameof(PizzaOrder.Size))
|
||||
.ReplaceTemplate(new Template(TemplateUsage.Double, "{Notfield}"))
|
||||
.Build();
|
||||
Debug.Fail("Validation failed");
|
||||
}
|
||||
catch (ArgumentException exception)
|
||||
{
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// TestValidate();
|
||||
var choiceForm = FormDialog.FromType<Choices>();
|
||||
var callDebug = new CallDialog<Choices>(choiceForm, async (root, context, result) =>
|
||||
{
|
||||
|
@ -168,12 +217,12 @@ namespace Microsoft.Bot.Builder.FormTest
|
|||
Call(context, root, () => MakeForm(noNumbers: true));
|
||||
return;
|
||||
}
|
||||
case DebugOptions.NoAnnotations:
|
||||
case DebugOptions.NoAnnotations:
|
||||
{
|
||||
Call(context, root, () => MakeForm(noNumbers: true, ignoreAnnotations: true));
|
||||
return;
|
||||
}
|
||||
case DebugOptions.NoFieldOrder:
|
||||
case DebugOptions.NoFieldOrder:
|
||||
{
|
||||
Call(context, root, () => new FormBuilder<PizzaOrder>().Build());
|
||||
return;
|
||||
|
|
Загрузка…
Ссылка в новой задаче