[Xaml] Create value from positional argument text of markup extension (#8980)

The behavior introduced with this change conforms to the description of
[MS-XAML-2009] page 80 and 81.
fixes #6905
This commit is contained in:
Akihiko Odaki 2020-01-03 04:07:49 +09:00 коммит произвёл Samantha Houts
Родитель 65c6860fc3
Коммит fee5faaca8
5 изменённых файлов: 54 добавлений и 48 удалений

Просмотреть файл

@ -143,11 +143,11 @@ namespace Xamarin.Forms.Build.Tasks
}
else
{
char next;
string piece;
while ((piece = GetNextPiece(serviceProvider, ref remaining, out next)) != null)
Property parsed;
do
{
var parsed = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
parsed = ParseProperty(serviceProvider, ref remaining);
XmlName childname;
if (parsed.name == null)
@ -177,6 +177,7 @@ namespace Xamarin.Forms.Build.Tasks
childnodes.Add((childname, childnode));
}
}
while (!parsed.last);
}
//The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.

Просмотреть файл

@ -12,6 +12,8 @@ namespace Xamarin.Forms.Xaml.UnitTests
{
IXamlTypeResolver typeResolver;
public static readonly string Foo = "Foo";
class MockElementNode : IElementNode, IValueNode, IXmlLineInfo
{
public bool HasLineInfo () { return false; }
@ -93,11 +95,10 @@ namespace Xamarin.Forms.Xaml.UnitTests
Assert.AreEqual (Binding.SelfPath, ((Binding)binding).Path);
}
[Test]
public void BindingWithImplicitPath ()
[TestCase("{Binding Foo}")]
[TestCase("{Binding {x:Static local:MarkupExpressionParserTests.Foo}}")]
public void BindingWithImplicitPath (string bindingString)
{
var bindingString = "{Binding Foo}";
var binding = (new MarkupExtensionParser ()).ParseExpression (ref bindingString, new Internals.XamlServiceProvider (null, null) {
IXamlTypeResolver = typeResolver,
});
@ -377,6 +378,8 @@ namespace Xamarin.Forms.Xaml.UnitTests
[TestCase("{Binding")]
[TestCase("{Binding 'Foo}")]
[TestCase("{Binding Foo, Converter={StaticResource Bar}")]
[TestCase("{Binding Foo, Converter={StaticResource Bar}?}")]
public void InvalidExpressions (string expression)
{
var serviceProvider = new Internals.XamlServiceProvider (null, null);

Просмотреть файл

@ -131,10 +131,9 @@ namespace Xamarin.Forms.Xaml
remaining = remaining.Substring(1);
}
else {
char next;
string piece;
while ((piece = GetNextPiece(serviceProvider, ref remaining, out next)) != null) {
var parsed = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
Property parsed;
do {
parsed = ParseProperty(serviceProvider, ref remaining);
XmlName childname;
if (parsed.name == null) {
@ -160,6 +159,7 @@ namespace Xamarin.Forms.Xaml
childnodes.Add((childname, childnode));
}
}
while (!parsed.last);
}

Просмотреть файл

@ -40,6 +40,7 @@ namespace Xamarin.Forms.Xaml
{
protected struct Property
{
public bool last;
public string name;
public string strValue;
public object value;
@ -119,50 +120,56 @@ namespace Xamarin.Forms.Xaml
return true;
}
protected Property ParseProperty(string prop, IServiceProvider serviceProvider, ref string remaining, bool isImplicit)
protected Property ParseProperty(IServiceProvider serviceProvider, ref string remaining)
{
char next;
object value = null;
string str_value;
string name;
if (isImplicit)
{
return new Property { name = null, strValue = prop, value = null };
}
remaining = remaining.TrimStart();
if (remaining.StartsWith("{", StringComparison.Ordinal))
{
value = ParseExpression(ref remaining, serviceProvider);
if (remaining[0] == '{')
return ParsePropertyExpression(null, serviceProvider, ref remaining);
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
if (next == '=') {
remaining = remaining.TrimStart();
if (remaining[0] == '{')
return ParsePropertyExpression(str_value, serviceProvider, ref remaining);
if (remaining.Length > 0 && remaining[0] == ',')
remaining = remaining.Substring(1);
else if (remaining.Length > 0 && remaining[0] == '}')
remaining = remaining.Substring(1);
str_value = value as string;
name = str_value;
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
}
else {
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
if (str_value == null) {
throw new XamlParseException($"No value found for property '{prop}' in markup expression", serviceProvider);
}
name = null;
}
return new Property { name = prop, strValue = str_value, value = value };
return new Property { last = next == '}', name = name, strValue = str_value, value = value };
}
protected string GetNextPiece(IServiceProvider serviceProvider, ref string remaining, out char next)
private Property ParsePropertyExpression(string prop, IServiceProvider serviceProvider, ref string remaining)
{
bool last;
var value = ParseExpression(ref remaining, serviceProvider);
remaining = remaining.TrimStart();
if (remaining.Length <= 0)
throw new XamlParseException("Unexpected end of markup expression", serviceProvider);
if (remaining[0] == ',')
last = false;
else if (remaining[0] == '}')
last = true;
else
throw new XamlParseException("Unexpected character following value string", serviceProvider);
remaining = remaining.Substring(1);
return new Property { last = last, name = prop, strValue = value as string, value = value };
}
private string GetNextPiece(IServiceProvider serviceProvider, ref string remaining, out char next)
{
bool inString = false;
int end = 0;
char stringTerminator = '\0';
remaining = remaining.TrimStart();
if (remaining.Length == 0)
{
next = Char.MaxValue;
return null;
}
var piece = new StringBuilder();
// If we're inside a quoted string we append all chars to our piece until we hit the ending quote.
@ -203,14 +210,8 @@ namespace Xamarin.Forms.Xaml
if (inString && end == remaining.Length)
throw new XamlParseException("Unterminated quoted string", serviceProvider);
if (end == remaining.Length && !remaining.EndsWith("}", StringComparison.Ordinal))
throw new XamlParseException("Expression did not end with '}'", serviceProvider);
if (end == 0)
{
next = Char.MaxValue;
return null;
}
throw new XamlParseException("Empty value string in markup expression", serviceProvider);
next = remaining[end];
remaining = remaining.Substring(end + 1);

Просмотреть файл

@ -42,11 +42,12 @@ namespace Xamarin.Forms.Xaml
if (remaining == "}")
return markupExtension.ProvideValue(serviceProvider);
string piece;
while ((piece = GetNextPiece(serviceProvider, ref remaining, out char next)) != null) {
var value = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
Property value;
do {
value = ParseProperty(serviceProvider, ref remaining);
SetPropertyValue(value.name, value.strValue, value.value, serviceProvider);
}
while (!value.last);
return markupExtension.ProvideValue(serviceProvider);
}