CanvasGeometry path parse error now more descriptive in the Sample App.

This commit is contained in:
Ratish Philip 2021-01-22 08:22:40 -08:00
Родитель 7d756c54c4
Коммит e618fc74cc
7 изменённых файлов: 160 добавлений и 34 удалений

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

@ -85,13 +85,14 @@
HorizontalAlignment="Left"
Click="{x:Bind OnClearCanvas}"
Content="Clear" />
<Button Grid.Row="3"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Right"
Click="{x:Bind OnParseData}"
Content="Parse" />
<TextBlock x:Name="ParseError"
Grid.Row="3"
Margin="150,3,10,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontSize="16"
Foreground="Red"
TextAlignment="Left" />
<Pivot x:Name="RootPivot"
Grid.Row="4">
<PivotItem Foreground="Black"

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

@ -6,15 +6,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows.System;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.Toolkit.Uwp.UI.Extensions;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry;
using Windows.System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Microsoft.Toolkit.Uwp.UI.Extensions;
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
@ -78,6 +78,10 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
"135.021,11.529 135.313,10.749 135.898,10.158 C 136.482,9.567 137.210,9.272 138.080,9.272 C 138.938,9.272 139.662,9.570 140.253,10.167 C 140.844,10.764 141.139,11.516 141.139,12.424 " +
"C 141.139,12.611 141.108,13.021 141.046,13.655 Z";
private const string ParseError1 = "Parameter \"(pathData matches.Count == 0)\" must be false, was true: ";
private const string ParseError2 = "Parameter \"(pathData matches.Count > 1)\" must be false, was true: ";
private const string ParseError3 = "PATH_ERR003";
private DispatcherQueueTimer _typeTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
private List<Color> _colors;
@ -158,7 +162,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
_selectionChanged = false;
}
private void OnParseData(object sender, RoutedEventArgs e)
private void ParseData()
{
_data = InputData.Text;
_isParsing = true;
@ -170,6 +174,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
if (string.IsNullOrWhiteSpace(_data))
{
ParseError.Text = string.Empty;
CommandsList.Text = string.Empty;
return;
}
@ -178,6 +183,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
_logger?.Clear();
CommandsList.Text = string.Empty;
ParseError.Text = string.Empty;
try
{
@ -192,9 +198,38 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
args.DrawingSession.FillGeometry(geometry, _fillColor);
args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
}
catch (ArgumentException argEx)
{
var message = argEx.Message;
var errorCode = message.Substring(0, 11);
if (message.StartsWith(ParseError1))
{
ParseError.Text = "Parse Error: No matching data!";
}
else if (message.StartsWith(ParseError2))
{
ParseError.Text = "Parse Error: Multiple FillRule elements present in Path Data!";
}
else if (message.StartsWith(ParseError3))
{
var tokens = message.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 3)
{
ParseError.Text = $"Parse Error at {tokens[1]}. Cannot parse '{tokens[2]}'.";
}
}
else
{
ParseError.Text = "Parsing error! Invalid input data!";
}
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
CommandsList.Text = ParseError.Text;
}
catch (Exception)
{
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
ParseError.Text = "Parsing error! Invalid input data!";
CommandsList.Text = "Parsing error! Invalid input data!";
}
}
@ -247,7 +282,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
private void OnClearCanvas(object sender, RoutedEventArgs e)
{
InputData.Text = string.Empty;
OnParseData(this, null);
ParseData();
}
private void OnShowRoundedStarSample(object sender, RoutedEventArgs e)
@ -277,12 +312,8 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
public void OnInputTextChanged(object sender, RoutedEventArgs e)
{
_typeTimer.Debounce(
() =>
{
// Only executes this code after 0.3 seconds have elapsed since last trigger.
this.OnParseData(null, null);
}, TimeSpan.FromSeconds(0.3));
// Call the ParseData method only after 0.3 seconds have elapsed since last trigger.
_typeTimer.Debounce(ParseData, TimeSpan.FromSeconds(0.3));
}
}
}

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

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Toolkit.Diagnostics;
@ -25,10 +26,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
var matches = RegexFactory.CanvasBrushRegex.Matches(brushData);
// If no match is found or no captures in the match, then it means that the brush data is invalid.
Guard.IsFalse(matches.Count == 0, "(brushData matches.Count == 0)", $"Invalid Brush data! No matching brush data found!\nBrush Data: {brushData}");
Guard.IsFalse(matches.Count == 0, "(brushData matches.Count == 0)", $"BRUSH_ERR001:Invalid Brush data! No matching brush data found!\nBrush Data: {brushData}");
// If the match contains more than one captures, it means that there are multiple brushes present in the brush data. There should be only one brush defined in the brush data.
Guard.IsFalse(matches.Count > 1, "(brushData matches.Count > 1)", "Multiple Brushes defined in Brush Data! " +
Guard.IsFalse(matches.Count > 1, "(brushData matches.Count > 1)", "BRUSH_ERR002:Multiple Brushes defined in Brush Data! " +
"There should be only one Brush definition within the Brush Data. " +
"You can either remove Brush definitions or split the Brush Data " +
"into multiple Brush Data and call the CanvasPathGeometry.CreateBrush() method on each of them." +
@ -66,7 +67,26 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
// Perform validation to check if there are any invalid characters in the brush data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(brushData, string.Empty).Length;
var postValidationCount = brushElement.ValidationCount;
Guard.IsTrue(preValidationCount == postValidationCount, nameof(brushData), $"Brush data contains invalid characters!\nBrush Data: {brushData}");
// If there are invalid characters, extract them and add them to the ArgumentException message
if (preValidationCount != postValidationCount)
{
var parseIndex = 0;
if (!string.IsNullOrWhiteSpace(brushElement.Data))
{
parseIndex = brushData.IndexOf(brushElement.Data, parseIndex, StringComparison.Ordinal);
}
var errorString = brushData.Substring(parseIndex);
if (errorString.Length > 30)
{
errorString = $"{errorString.Substring(0, 30)}...";
}
errorString = $"BRUSH_ERR003:Brush data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
ThrowHelper.ThrowArgumentException(errorString);
}
return brushElement;
}

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

@ -33,10 +33,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
var matches = RegexFactory.CanvasGeometryRegex.Matches(pathData);
// If no match is found or no captures in the match, then it means // that the path data is invalid.
Guard.IsFalse(matches.Count == 0, "(pathData matches.Count == 0)", $"Invalid Path data! No matching path data found!\nPath Data: {pathData}");
Guard.IsFalse(matches.Count == 0, "(pathData matches.Count == 0)", $"PATH_ERR000:Invalid Path data! No matching path data found!\nPath Data: {pathData}");
// If the match contains more than one captures, it means that there are multiple FillRuleElements present in the path data. There can be only one FillRuleElement in the path data (at the beginning).
Guard.IsFalse(matches.Count > 1, "(pathData matches.Count > 1)", "Multiple FillRule elements present in Path Data! " +
Guard.IsFalse(matches.Count > 1, "(pathData matches.Count > 1)", "PATH_ERR001:Multiple FillRule elements present in Path Data!\n" +
"There should be only one FillRule within the Path Data. " +
"You can either remove additional FillRule elements or split the Path Data " +
"into multiple Path Data and call the CanvasPathGeometry.CreateGeometry() method on each of them." +
@ -80,16 +80,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
pathFigures.Insert(0, PathElementFactory.CreateDefaultPathElement(PathFigureType.FillRule));
}
}
else
{
return null;
}
// Perform validation to check if there are any invalid characters in the path data that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(pathData, string.Empty).Length;
var postValidationCount = pathFigures.Sum(x => x.ValidationCount);
Guard.IsTrue(preValidationCount == postValidationCount, nameof(pathData), $"Path data contains invalid characters!\nPath Data: {pathData}");
if (pathFigures.Count == 0)
// If there are invalid characters, extract them and add them to the ArgumentException message
if (preValidationCount != postValidationCount)
{
return null;
var parseIndex = 0;
foreach (var figure in pathFigures)
{
parseIndex = pathData.IndexOf(figure.Data, parseIndex, StringComparison.Ordinal) + figure.Data.Length;
}
var errorString = pathData.Substring(parseIndex);
if (errorString.Length > 30)
{
errorString = $"{errorString.Substring(0, 30)}...";
}
errorString = $"PATH_ERR003:Path data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
ThrowHelper.ThrowArgumentException(errorString);
}
ICanvasPathElement lastElement = null;

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

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Graphics.Canvas;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
@ -25,12 +26,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
// If no match is found or no captures in the match, then it means
// that the stroke data is invalid.
Guard.IsFalse(matches.Count == 0, "(strokeData matches.Count == 0)", $"Invalid Stroke data! No matching CanvasStroke found!\nStroke Data: {strokeData}");
Guard.IsFalse(matches.Count == 0, "(strokeData matches.Count == 0)", $"STROKE_ERR001:Invalid Stroke data! No matching CanvasStroke found!\nStroke Data: {strokeData}");
// If the match contains more than one captures, it means that there
// are multiple CanvasStrokes present in the stroke data. There should
// be only one CanvasStroke defined in the stroke data.
Guard.IsFalse(matches.Count > 1, "(strokeData matches.Count > 1)", "Multiple CanvasStrokes defined in Stroke Data! " +
Guard.IsFalse(matches.Count > 1, "(strokeData matches.Count > 1)", "STROKE_ERR002:Multiple CanvasStrokes defined in Stroke Data! " +
"There should be only one CanvasStroke definition within the Stroke Data. " +
"You can either remove CanvasStroke definitions or split the Stroke Data " +
"into multiple Stroke Data and call the CanvasPathGeometry.CreateStroke() method on each of them." +
@ -44,7 +45,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
var preValidationCount = RegexFactory.ValidationRegex.Replace(strokeData, string.Empty).Length;
var postValidationCount = strokeElement.ValidationCount;
Guard.IsTrue(preValidationCount == postValidationCount, nameof(strokeData), $"Stroke data contains invalid characters!\nStroke Data: {strokeData}");
// If there are invalid characters, extract them and add them to the ArgumentException message
if (preValidationCount != postValidationCount)
{
var parseIndex = 0;
if (!string.IsNullOrWhiteSpace(strokeElement.Data))
{
parseIndex = strokeData.IndexOf(strokeElement.Data, parseIndex, StringComparison.Ordinal);
}
var errorString = strokeData.Substring(parseIndex);
if (errorString.Length > 30)
{
errorString = $"{errorString.Substring(0, 30)}...";
}
errorString = $"STROKE_ERR003:Stroke data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
ThrowHelper.ThrowArgumentException(errorString);
}
return strokeElement;
}

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

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Diagnostics;
@ -25,12 +26,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
var matches = RegexFactory.CanvasStrokeStyleRegex.Matches(styleData);
// If no match is found or no captures in the match, then it means that the style data is invalid.
Guard.IsFalse(matches.Count == 0, "(styleData matches.Count == 0)", $"Invalid CanvasStrokeStyle data! No matching CanvasStrokeStyle found!\nCanvasStrokeStyle Data: {styleData}");
Guard.IsFalse(matches.Count == 0, "(styleData matches.Count == 0)", $"STYLE_ERR001:Invalid CanvasStrokeStyle data! No matching CanvasStrokeStyle found!\nCanvasStrokeStyle Data: {styleData}");
// If the match contains more than one captures, it means that there
// are multiple CanvasStrokeStyles present in the CanvasStrokeStyle data. There should
// be only one CanvasStrokeStyle defined in the CanvasStrokeStyle data.
Guard.IsFalse(matches.Count > 1, "(styleData matches.Count > 1)", "Multiple CanvasStrokeStyles defined in CanvasStrokeStyle Data! " +
Guard.IsFalse(matches.Count > 1, "(styleData matches.Count > 1)", "STYLE_ERR002:Multiple CanvasStrokeStyles defined in CanvasStrokeStyle Data! " +
"There should be only one CanvasStrokeStyle definition within the CanvasStrokeStyle Data. " +
"You can either remove CanvasStrokeStyle definitions or split the CanvasStrokeStyle Data " +
"into multiple CanvasStrokeStyle Data and call the CanvasPathGeometry.CreateStrokeStyle() method on each of them." +
@ -44,7 +45,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
var preValidationCount = RegexFactory.ValidationRegex.Replace(styleData, string.Empty).Length;
var postValidationCount = styleElement.ValidationCount;
Guard.IsTrue(preValidationCount == postValidationCount, nameof(styleData), $"CanvasStrokeStyle data contains invalid characters!\nCanvasStrokeStyle Data: {styleData}");
// If there are invalid characters, extract them and add them to the ArgumentException message
if (preValidationCount != postValidationCount)
{
var parseIndex = 0;
if (!string.IsNullOrWhiteSpace(styleElement.Data))
{
parseIndex = styleData.IndexOf(styleElement.Data, parseIndex, StringComparison.Ordinal);
}
var errorString = styleData.Substring(parseIndex);
if (errorString.Length > 30)
{
errorString = $"{errorString.Substring(0, 30)}...";
}
errorString = $"STYLE_ERR003:Style data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
ThrowHelper.ThrowArgumentException(errorString);
}
return styleElement.Style;
}

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

@ -28,12 +28,31 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers
internal static Color Parse(string colorString)
{
var match = RegexFactory.ColorRegex.Match(colorString);
Guard.IsTrue(match.Success, nameof(colorString), "Invalid value provided in colorString! No matching color found in the colorString.");
Guard.IsTrue(match.Success, nameof(colorString), "COLOR_ERR001:Invalid value provided in Color Data! No matching color found in the Color Data.");
// Perform validation to check if there are any invalid characters in the colorString that were not captured
var preValidationCount = RegexFactory.ValidationRegex.Replace(colorString, string.Empty).Length;
var postValidationCount = RegexFactory.ValidationRegex.Replace(match.Value, string.Empty).Length;
Guard.IsTrue(preValidationCount == postValidationCount, nameof(colorString), $"colorString contains invalid characters!\ncolorString: {colorString}");
// If there are invalid characters, extract them and add them to the ArgumentException message
if (preValidationCount != postValidationCount)
{
var parseIndex = 0;
if (!string.IsNullOrWhiteSpace(match.Value))
{
parseIndex = colorString.IndexOf(match.Value, parseIndex, StringComparison.Ordinal);
}
var errorString = colorString.Substring(parseIndex);
if (errorString.Length > 30)
{
errorString = $"{errorString.Substring(0, 30)}...";
}
errorString = $"COLOR_ERR003:Color data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
ThrowHelper.ThrowArgumentException(errorString);
}
return Parse(match);
}