Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,14 @@ internal ParserContext(ParserContext parserContext)
/// </summary>
internal Dictionary<string, SpecialBracketCharacters> InitBracketCharacterCacheForType(Type type)
{
if (!MasterBracketCharacterCache.ContainsKey(type))
if (!MasterBracketCharacterCache.TryGetValue(type, out Dictionary<string, SpecialBracketCharacters> map))
{
Dictionary<string, SpecialBracketCharacters> map = BuildBracketCharacterCacheForType(type);
map = BuildBracketCharacterCacheForType(type);

MasterBracketCharacterCache.Add(type, map);
}

return MasterBracketCharacterCache[type];
return map;
}

/// <summary>
Expand Down Expand Up @@ -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.
/// </summary>
private Dictionary<string, SpecialBracketCharacters> BuildBracketCharacterCacheForType(Type extensionType)
private static Dictionary<string, SpecialBracketCharacters> BuildBracketCharacterCacheForType(Type extensionType)
{
Dictionary<string, SpecialBracketCharacters> cache = new Dictionary<string, SpecialBracketCharacters>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, SpecialBracketCharacters> cache = new(StringComparer.OrdinalIgnoreCase);
PropertyInfo[] propertyInfo = extensionType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type constructorArgumentType = null;
Type markupExtensionBracketCharacterType = null;
Expand All @@ -859,6 +860,7 @@ private Dictionary<string, SpecialBracketCharacters> BuildBracketCharacterCacheF
string constructorArgumentName = null;
IList<CustomAttributeData> customAttributes = CustomAttributeData.GetCustomAttributes(property);
SpecialBracketCharacters bracketCharacters = null;

foreach (CustomAttributeData attributeData in customAttributes)
{
Type attributeType = attributeData.AttributeType;
Expand All @@ -877,10 +879,7 @@ private Dictionary<string, SpecialBracketCharacters> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@
// 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
{
/// <summary>
/// Class that provides helper functions for the parser/Xaml Reader
/// to process Bracket Characters specified on a Markup Extension Property
/// </summary>
internal class SpecialBracketCharacters : ISupportInitialize
internal sealed class SpecialBracketCharacters : ISupportInitialize
{
#if !NETFX
/// <summary>
/// Stores characters that cannot be specified in <see cref="MarkupExtensionBracketCharactersAttribute"/>.
/// </summary>
private static readonly SearchValues<char> s_restrictedCharSet = SearchValues.Create('=', ',', '\'', '"', '{', '}', '\\');

private bool _initializing;

private string _startChars;
private string _endChars;
private readonly static ISet<char> _restrictedCharSet = new SortedSet<char>((new char[] { '=', ',', '\'', '"', '{', '}', '\\' }));
private bool _initializing;
private StringBuilder _startCharactersStringBuilder;
private StringBuilder _endCharactersStringBuilder;

Expand All @@ -32,63 +39,172 @@ internal SpecialBracketCharacters()
BeginInit();
}

internal SpecialBracketCharacters(IReadOnlyDictionary<char,char> attributeList)
internal SpecialBracketCharacters(IReadOnlyDictionary<char, char> attributeList)
{
BeginInit();
if (attributeList != null && attributeList.Count > 0)

if (attributeList?.Count > 0)
{
Tokenize(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<char, char> 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
/// <summary>
/// Stores characters that cannot be specified in <see cref="MarkupExtensionBracketCharactersAttribute"/>.
/// </summary>
private static readonly SortedSet<char> 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<char, char> attributeList)
{
BeginInit();

if (attributeList?.Count > 0)
{
Tokenize(attributeList);
}
}

private void Tokenize(IReadOnlyDictionary<char,char> 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<char, char> 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);
}
}
}

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)
Expand Down Expand Up @@ -123,16 +239,28 @@ internal string EndBracketCharacters

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;
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -564,25 +564,22 @@ internal Dictionary<string, SpecialBracketCharacters> 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.
/// </summary>
private Dictionary<string, SpecialBracketCharacters> BuildBracketCharacterCacheForType(XamlType type)
private static Dictionary<string, SpecialBracketCharacters> BuildBracketCharacterCacheForType(XamlType type)
{
Dictionary<string, SpecialBracketCharacters> map = new Dictionary<string, SpecialBracketCharacters>(StringComparer.OrdinalIgnoreCase);
ICollection<XamlMember> members = type.GetAllMembers();
foreach (XamlMember member in members)
{
string constructorArgumentName = member.ConstructorArgument;
string propertyName = member.Name;
IReadOnlyDictionary<char,char> markupExtensionBracketCharactersList = member.MarkupExtensionBracketCharacters;
SpecialBracketCharacters splBracketCharacters = markupExtensionBracketCharactersList != null && markupExtensionBracketCharactersList.Count > 0
? new SpecialBracketCharacters(markupExtensionBracketCharactersList)
: null;
if (splBracketCharacters != null)
Dictionary<string, SpecialBracketCharacters> 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);
}
}
}
Expand Down