|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Xaml.Schema;
namespace MS.Internal.Xaml.Parser
{
class GenericTypeNameParser
{
[Serializable]
class TypeNameParserException : Exception
{
public TypeNameParserException(string message)
: base(message)
{
}
#pragma warning disable SYSLIB0051 // Type or member is obsolete
protected TypeNameParserException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{
}
#pragma warning restore SYSLIB0051 // Type or member is obsolete
}
private GenericTypeNameScanner _scanner;
private string _inputText;
private Func<string, string> _prefixResolver;
Stack<TypeNameFrame> _stack;
public GenericTypeNameParser(Func<string, string> prefixResolver)
{
_prefixResolver = prefixResolver;
}
public static XamlTypeName ParseIfTrivalName(string text, Func<string, string> prefixResolver, out string error)
{
int parenIdx = text.IndexOf('(');
int bracketIdx = text.IndexOf('[');
if (parenIdx != -1 || bracketIdx != -1)
{
error = string.Empty;
return null;
}
string prefix;
string simpleName;
error = string.Empty;
if (!XamlQualifiedName.Parse(text, out prefix, out simpleName))
{
error = SR.Format(SR.InvalidTypeString, text);
return null;
}
string ns = prefixResolver(prefix);
if (String.IsNullOrEmpty(ns))
{
error = SR.Format(SR.PrefixNotFound, prefix);
return null;
}
XamlTypeName xamlTypeName = new XamlTypeName(ns, simpleName);
return xamlTypeName;
}
public XamlTypeName ParseName(string text, out string error)
{
error = string.Empty;
_scanner = new GenericTypeNameScanner(text);
_inputText = text;
StartStack();
try
{
_scanner.Read();
P_XamlTypeName();
if (_scanner.Token != GenericTypeNameScannerToken.NONE)
{
ThrowOnBadInput();
}
}
catch (TypeNameParserException ex)
{
error = ex.Message;
}
XamlTypeName typeName = null;
if (String.IsNullOrEmpty(error))
{
typeName = CollectNameFromStack();
}
return typeName;
}
public IList<XamlTypeName> ParseList(string text, out string error)
{
_scanner = new GenericTypeNameScanner(text);
_inputText = text;
StartStack();
error = string.Empty;
try
{
_scanner.Read();
P_XamlTypeNameList();
if (_scanner.Token != GenericTypeNameScannerToken.NONE)
{
ThrowOnBadInput();
}
}
catch (TypeNameParserException ex)
{
error = ex.Message;
}
IList<XamlTypeName> typeNameList = null;
if (String.IsNullOrEmpty(error))
{
typeNameList = CollectNameListFromStack();
}
return typeNameList;
}
// XamlTypeName ::= SimpleTypeName TypeParameters? Subscript*
// SimpleTypeName ::= (Prefix ‘:’)? TypeName
// TypeParameters ::= ‘(‘ XamlTypeNameList ‘)’
// XamlTypeNameList ::= XamlTypeName NameListExt*
// NameListExt ::= ‘,’ XamlTypeName
// Subscript ::= ‘[’ ‘,’* ‘]’
// XamlTypeName ::= SimpleTypeName TypeParameters? Subscript*
//
private void P_XamlTypeName()
{
// Required
if (_scanner.Token != GenericTypeNameScannerToken.NAME)
{
ThrowOnBadInput();
}
P_SimpleTypeName();
// Optional
if (_scanner.Token == GenericTypeNameScannerToken.OPEN)
{
P_TypeParameters();
}
// Optional
if (_scanner.Token == GenericTypeNameScannerToken.SUBSCRIPT)
{
P_RepeatingSubscript();
}
Callout_EndOfType();
}
// SimpleTypeName ::= (Prefix ‘:’)? TypeName
//
private void P_SimpleTypeName()
{
// caller checks this.
Debug.Assert(_scanner.Token == GenericTypeNameScannerToken.NAME);
string prefix = string.Empty;
string name = _scanner.MultiCharTokenText;
_scanner.Read();
// Colon is optional.
if (_scanner.Token == GenericTypeNameScannerToken.COLON)
{
prefix = name;
_scanner.Read();
// IF there was a colon then there must be a name following.
if (_scanner.Token != GenericTypeNameScannerToken.NAME)
{
ThrowOnBadInput();
}
name = _scanner.MultiCharTokenText;
_scanner.Read();
}
Callout_FoundName(prefix, name);
}
// TypeParameters ::= ‘(‘ XamlTypeNameList ‘)’
//
private void P_TypeParameters()
{
// Required
// caller checks this.
Debug.Assert(_scanner.Token == GenericTypeNameScannerToken.OPEN);
_scanner.Read();
P_XamlTypeNameList();
// Required
if (_scanner.Token != GenericTypeNameScannerToken.CLOSE)
{
ThrowOnBadInput();
}
_scanner.Read();
}
// XamlTypeNameList ::= XamlTypeName NameListExt*
//
private void P_XamlTypeNameList()
{
P_XamlTypeName();
// optional zero or more.
while (_scanner.Token == GenericTypeNameScannerToken.COMMA)
{
P_NameListExt();
}
}
// NameListExt ::= ‘,’ XamlTypeName
//
private void P_NameListExt()
{
// Caller checked this.
Debug.Assert(_scanner.Token == GenericTypeNameScannerToken.COMMA);
_scanner.Read();
P_XamlTypeName();
}
// Subscript ::= ‘[’ ‘,’* ‘]’
//
private void P_RepeatingSubscript()
{
// caller checks this.
Debug.Assert(_scanner.Token == GenericTypeNameScannerToken.SUBSCRIPT);
do
{
Callout_Subscript(_scanner.MultiCharTokenText);
_scanner.Read();
}
while (_scanner.Token == GenericTypeNameScannerToken.SUBSCRIPT);
}
private void ThrowOnBadInput()
{
throw new TypeNameParserException(SR.Format(SR.InvalidCharInTypeName, _scanner.ErrorCurrentChar, _inputText));
}
private void StartStack()
{
_stack = new Stack<TypeNameFrame>();
TypeNameFrame frame;
frame = new TypeNameFrame();
_stack.Push(frame);
}
void Callout_FoundName(string prefix, string name)
{
TypeNameFrame frame = new TypeNameFrame();
frame.Name = name;
string ns = _prefixResolver(prefix);
frame.Namespace = ns ?? throw new TypeNameParserException(SR.Format(SR.PrefixNotFound, prefix));
_stack.Push(frame);
}
void Callout_EndOfType()
{
TypeNameFrame frame = _stack.Pop();
XamlTypeName typeName = new XamlTypeName(frame.Namespace, frame.Name, frame.TypeArgs);
frame = _stack.Peek();
if (frame.TypeArgs == null)
{
frame.AllocateTypeArgs();
}
frame.TypeArgs.Add(typeName);
}
void Callout_Subscript(string subscript)
{
TypeNameFrame frame = _stack.Peek();
frame.Name += subscript;
}
private XamlTypeName CollectNameFromStack()
{
if (_stack.Count != 1)
{
throw new TypeNameParserException(SR.Format(SR.InvalidTypeString, _inputText));
}
TypeNameFrame frame = _stack.Peek();
if (frame.TypeArgs.Count != 1)
{
throw new TypeNameParserException(SR.Format(SR.InvalidTypeString, _inputText));
}
XamlTypeName xamlTypeName = frame.TypeArgs[0];
return xamlTypeName;
}
private IList<XamlTypeName> CollectNameListFromStack()
{
if (_stack.Count != 1)
{
throw new TypeNameParserException(SR.Format(SR.InvalidTypeString, _inputText));
}
TypeNameFrame frame = _stack.Peek();
List<XamlTypeName> xamlTypeNameList = frame.TypeArgs;
return xamlTypeNameList;
}
}
class TypeNameFrame
{
List<XamlTypeName> _typeArgs;
public string Namespace { get; set; }
public string Name { get; set; }
public List<XamlTypeName> TypeArgs { get { return _typeArgs; } }
public void AllocateTypeArgs()
{
_typeArgs = new List<XamlTypeName>();
}
}
}
|