diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/ParserContext.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/ParserContext.cs index 79e3d3358ef..7659848d3c7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/ParserContext.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/ParserContext.cs @@ -198,13 +198,14 @@ internal ParserContext(ParserContext parserContext) /// internal Dictionary InitBracketCharacterCacheForType(Type type) { - if (!MasterBracketCharacterCache.ContainsKey(type)) + if (!MasterBracketCharacterCache.TryGetValue(type, out Dictionary map)) { - Dictionary map = BuildBracketCharacterCacheForType(type); + map = BuildBracketCharacterCacheForType(type); + MasterBracketCharacterCache.Add(type, map); } - return MasterBracketCharacterCache[type]; + return map; } /// @@ -846,9 +847,9 @@ internal Freezable TryGetFreezable(string value) /// Looks up all properties via reflection on the given type, and scans through the attributes on all of them /// to build a cache of properties which have MarkupExtensionBracketCharactersAttribute set on them. /// - private Dictionary BuildBracketCharacterCacheForType(Type extensionType) + private static Dictionary BuildBracketCharacterCacheForType(Type extensionType) { - Dictionary cache = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary cache = new(StringComparer.OrdinalIgnoreCase); PropertyInfo[] propertyInfo = extensionType.GetProperties(BindingFlags.Public | BindingFlags.Instance); Type constructorArgumentType = null; Type markupExtensionBracketCharacterType = null; @@ -859,6 +860,7 @@ private Dictionary BuildBracketCharacterCacheF string constructorArgumentName = null; IList customAttributes = CustomAttributeData.GetCustomAttributes(property); SpecialBracketCharacters bracketCharacters = null; + foreach (CustomAttributeData attributeData in customAttributes) { Type attributeType = attributeData.AttributeType; @@ -877,10 +879,7 @@ private Dictionary BuildBracketCharacterCacheF } else if (attributeType.IsAssignableFrom(markupExtensionBracketCharacterType)) { - if (bracketCharacters == null) - { - bracketCharacters = new SpecialBracketCharacters(); - } + bracketCharacters ??= new SpecialBracketCharacters(); bracketCharacters.AddBracketCharacters((char)attributeData.ConstructorArguments[0].Value, (char)attributeData.ConstructorArguments[1].Value); } diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/Xaml/Parser/SpecialBracketCharacters.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/Xaml/Parser/SpecialBracketCharacters.cs index 091d63efb7a..94d7977f279 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/Xaml/Parser/SpecialBracketCharacters.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/Xaml/Parser/SpecialBracketCharacters.cs @@ -7,10 +7,11 @@ // Description: Data model for the Bracket characters specified on a Markup Extension property. using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; using System.Text; +using System.Buffers; +using System.Windows.Markup; +using System.ComponentModel; +using System.Collections.Generic; namespace MS.Internal.Xaml.Parser { @@ -18,12 +19,18 @@ namespace MS.Internal.Xaml.Parser /// Class that provides helper functions for the parser/Xaml Reader /// to process Bracket Characters specified on a Markup Extension Property /// - internal class SpecialBracketCharacters : ISupportInitialize + internal sealed class SpecialBracketCharacters : ISupportInitialize { +#if !NETFX + /// + /// Stores characters that cannot be specified in . + /// + private static readonly SearchValues s_restrictedCharSet = SearchValues.Create('=', ',', '\'', '"', '{', '}', '\\'); + + private bool _initializing; + private string _startChars; private string _endChars; - private readonly static ISet _restrictedCharSet = new SortedSet((new char[] { '=', ',', '\'', '"', '{', '}', '\\' })); - private bool _initializing; private StringBuilder _startCharactersStringBuilder; private StringBuilder _endCharactersStringBuilder; @@ -32,10 +39,11 @@ internal SpecialBracketCharacters() BeginInit(); } - internal SpecialBracketCharacters(IReadOnlyDictionary attributeList) + internal SpecialBracketCharacters(IReadOnlyDictionary attributeList) { BeginInit(); - if (attributeList != null && attributeList.Count > 0) + + if (attributeList?.Count > 0) { Tokenize(attributeList); } @@ -43,30 +51,146 @@ internal SpecialBracketCharacters(IReadOnlyDictionary attributeList) internal void AddBracketCharacters(char openingBracket, char closingBracket) { - if (_initializing) + if (!_initializing) + throw new InvalidOperationException(); + + _startCharactersStringBuilder.Append(openingBracket); + _endCharactersStringBuilder.Append(closingBracket); + } + + private void Tokenize(IReadOnlyDictionary attributeList) + { + if (!_initializing) + throw new InvalidOperationException(); + + foreach (char openingBracket in attributeList.Keys) { - _startCharactersStringBuilder.Append(openingBracket); - _endCharactersStringBuilder.Append(closingBracket); + char closingBracket = attributeList[openingBracket]; + + if (IsValidBracketCharacter(openingBracket, closingBracket)) + { + _startCharactersStringBuilder.Append(openingBracket); + _endCharactersStringBuilder.Append(closingBracket); + } } - else - { + } + + private static bool IsValidBracketCharacter(char openingBracket, char closingBracket) + { + if (openingBracket == closingBracket) + throw new InvalidOperationException("Opening bracket character cannot be the same as closing bracket character."); + else if (char.IsLetterOrDigit(openingBracket) || char.IsLetterOrDigit(closingBracket) || char.IsWhiteSpace(openingBracket) || char.IsWhiteSpace(closingBracket)) + throw new InvalidOperationException("Bracket characters cannot be alpha-numeric or whitespace."); + else if (s_restrictedCharSet.Contains(openingBracket) || s_restrictedCharSet.Contains(closingBracket)) + throw new InvalidOperationException("Bracket characters cannot be one of the following: '=' , ',', '\'', '\"', '{ ', ' }', '\\'"); + + return true; + } + + internal bool IsSpecialCharacter(char ch) + { + return _startChars.Contains(ch) || _endChars.Contains(ch); + } + + internal bool StartsEscapeSequence(char ch) + { + return _startChars.Contains(ch); + } + + internal bool EndsEscapeSequence(char ch) + { + return _endChars.Contains(ch); + } + + internal bool Match(char start, char end) + { + return _endChars.IndexOf(end, StringComparison.Ordinal) == _startChars.IndexOf(start, StringComparison.Ordinal); + } + + internal string StartBracketCharacters + { + get { return _startChars; } + } + + internal string EndBracketCharacters + { + get { return _endChars; } + } + + public void BeginInit() + { + if (_initializing) throw new InvalidOperationException(); + + _initializing = true; + _startCharactersStringBuilder = new StringBuilder(); + _endCharactersStringBuilder = new StringBuilder(); + } + + public void EndInit() + { + if (!_initializing) + throw new InvalidOperationException(); + + _startChars = _startCharactersStringBuilder.ToString(); + _endChars = _endCharactersStringBuilder.ToString(); + + // Clear the references, otherwise we could consider this a memory leak + _startCharactersStringBuilder = null; + _endCharactersStringBuilder = null; + + _initializing = false; + } +#else + /// + /// Stores characters that cannot be specified in . + /// + private static readonly SortedSet s_restrictedCharSet = new(new char[] { '=', ',', '\'', '"', '{', '}', '\\' }); + + private bool _initializing; + + private string _startChars; + private string _endChars; + private StringBuilder _startCharactersStringBuilder; + private StringBuilder _endCharactersStringBuilder; + + internal SpecialBracketCharacters() + { + BeginInit(); + } + + internal SpecialBracketCharacters(IReadOnlyDictionary attributeList) + { + BeginInit(); + + if (attributeList?.Count > 0) + { + Tokenize(attributeList); } } - private void Tokenize(IReadOnlyDictionary attributeList) + internal void AddBracketCharacters(char openingBracket, char closingBracket) { - if (_initializing) + if (!_initializing) + throw new InvalidOperationException(); + + _startCharactersStringBuilder.Append(openingBracket); + _endCharactersStringBuilder.Append(closingBracket); + } + + private void Tokenize(IReadOnlyDictionary attributeList) + { + if (!_initializing) + throw new InvalidOperationException(); + + foreach (char openingBracket in attributeList.Keys) { - foreach (char openingBracket in attributeList.Keys) + char closingBracket = attributeList[openingBracket]; + + if (IsValidBracketCharacter(openingBracket, closingBracket)) { - char closingBracket = attributeList[openingBracket]; - string errorMessage = string.Empty; - if (IsValidBracketCharacter(openingBracket, closingBracket)) - { - _startCharactersStringBuilder.Append(openingBracket); - _endCharactersStringBuilder.Append(closingBracket); - } + _startCharactersStringBuilder.Append(openingBracket); + _endCharactersStringBuilder.Append(closingBracket); } } } @@ -74,21 +198,13 @@ private void Tokenize(IReadOnlyDictionary attributeList) private bool IsValidBracketCharacter(char openingBracket, char closingBracket) { if (openingBracket == closingBracket) - { throw new InvalidOperationException("Opening bracket character cannot be the same as closing bracket character."); - } else if (char.IsLetterOrDigit(openingBracket) || char.IsLetterOrDigit(closingBracket) || char.IsWhiteSpace(openingBracket) || char.IsWhiteSpace(closingBracket)) - { throw new InvalidOperationException("Bracket characters cannot be alpha-numeric or whitespace."); - } - else if (_restrictedCharSet.Contains(openingBracket) || _restrictedCharSet.Contains(closingBracket)) - { + else if (s_restrictedCharSet.Contains(openingBracket) || s_restrictedCharSet.Contains(closingBracket)) throw new InvalidOperationException("Bracket characters cannot be one of the following: '=' , ',', '\'', '\"', '{ ', ' }', '\\'"); - } - else - { - return true; - } + + return true; } internal bool IsSpecialCharacter(char ch) @@ -123,6 +239,9 @@ internal string EndBracketCharacters public void BeginInit() { + if (_initializing) + throw new InvalidOperationException(); + _initializing = true; _startCharactersStringBuilder = new StringBuilder(); _endCharactersStringBuilder = new StringBuilder(); @@ -130,9 +249,18 @@ public void BeginInit() public void EndInit() { + if (!_initializing) + throw new InvalidOperationException(); + _startChars = _startCharactersStringBuilder.ToString(); _endChars = _endCharactersStringBuilder.ToString(); + + // Clear the references, otherwise we could consider this a memory leak + _startCharactersStringBuilder = null; + _endCharactersStringBuilder = null; + _initializing = false; } +#endif } } diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs index 9f27c75a174..73a86e09858 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs @@ -564,25 +564,22 @@ internal Dictionary InitBracketCharacterCacheF /// Looks up all properties via reflection on the given type, and scans through the attributes on all of them /// to build a cache of properties which have MarkupExtensionBracketCharactersAttribute set on them. /// - private Dictionary BuildBracketCharacterCacheForType(XamlType type) + private static Dictionary BuildBracketCharacterCacheForType(XamlType type) { - Dictionary map = new Dictionary(StringComparer.OrdinalIgnoreCase); - ICollection members = type.GetAllMembers(); - foreach (XamlMember member in members) - { - string constructorArgumentName = member.ConstructorArgument; - string propertyName = member.Name; - IReadOnlyDictionary markupExtensionBracketCharactersList = member.MarkupExtensionBracketCharacters; - SpecialBracketCharacters splBracketCharacters = markupExtensionBracketCharactersList != null && markupExtensionBracketCharactersList.Count > 0 - ? new SpecialBracketCharacters(markupExtensionBracketCharactersList) - : null; - if (splBracketCharacters != null) + Dictionary map = new(StringComparer.OrdinalIgnoreCase); + + foreach (XamlMember member in type.GetAllMembers()) + { + if (member.MarkupExtensionBracketCharacters?.Count > 0) { - splBracketCharacters.EndInit(); - map.Add(propertyName, splBracketCharacters); - if (!string.IsNullOrEmpty(constructorArgumentName)) + SpecialBracketCharacters specialBracketChars = new(member.MarkupExtensionBracketCharacters); + specialBracketChars.EndInit(); + + map.Add(member.Name, specialBracketChars); + + if (!string.IsNullOrEmpty(member.ConstructorArgument)) { - map.Add(constructorArgumentName, splBracketCharacters); + map.Add(member.ConstructorArgument, specialBracketChars); } } }