|
// 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.
/***************************************************************************\
*
* Purpose: Class for compiling Xaml.
*
\***************************************************************************/
using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Globalization;
using MS.Utility;
using System.Runtime.InteropServices;
using MS.Internal;
// Disabling 1634 and 1691:
// In order to avoid generating warnings about unknown message numbers and
// unknown pragmas when compiling C# source code with the C# compiler,
// you need to disable warnings 1634 and 1691. (Presharp Documentation)
#pragma warning disable 1634, 1691
#if PBTCOMPILER
namespace MS.Internal.Markup
#else
using System.Windows;
namespace System.Windows.Markup
#endif
{
#region enums
/// <summary>
/// Parser modes. indicates if the Xaml should be parsed sync or async.
/// currently public so test can set these values.
/// </summary>
internal enum XamlParseMode
{
/// <summary>
/// Not initialized
/// </summary>
Uninitialized,
/// <summary>
/// Sync
/// </summary>
Synchronous,
/// <summary>
/// Async
/// </summary>
Asynchronous,
}
#endregion enums
/// <summary>
/// XamlParser class. This class is used internally
/// </summary>
internal class XamlParser
{
#if PBTCOMPILER
#region Constructors
/// <summary>
/// Constructor that takes a stream and creates an XmlCompatibilityReader on it.
/// </summary>
public XamlParser(
ParserContext parserContext,
BamlRecordWriter bamlWriter,
Stream xamlStream,
bool multipleRoots) : this(parserContext, bamlWriter,
new XmlTextReader(xamlStream,
multipleRoots ? XmlNodeType.Element : XmlNodeType.Document,
(XmlParserContext)parserContext) )
{
}
protected XamlParser(
ParserContext parserContext,
BamlRecordWriter bamlWriter,
XmlTextReader textReader) : this(parserContext, bamlWriter)
{
// When the XML 1.0 specification was authored, security was not a top concern, and as a result DTDs have the
// unfortunate capability of severe Denial of Service (DoS) attacks, typically through the use of an internal
// entity expansion technique. In System.Xml V2.0, in order to provide protection against DTD DoS attacks there
// is the capability of turning off DTD parsing through the use of the ProhibitDtd property.
#pragma warning disable 0618
// CS0618: A class member was marked with the Obsolete attribute, such that a warning
// will be issued when the class member is referenced.
textReader.ProhibitDtd = true;
#pragma warning enable 0618
XmlCompatibilityReader xcr = new XmlCompatibilityReader(textReader,
new IsXmlNamespaceSupportedCallback(IsXmlNamespaceSupported),
_predefinedNamespaces );
TokenReader = new XamlReaderHelper(this,parserContext,xcr);
}
protected XamlParser(
ParserContext parserContext,
BamlRecordWriter bamlWriter)
{
_parserContext = parserContext;
_bamlWriter = bamlWriter;
}
// Default constructor to aid in subclassing
protected XamlParser()
{
}
#endregion Constructors
#region PublicMethods
/// <summary>
/// Main method to Parse the XAML.
/// When in synchronous mode the entire file is parsed before
/// this method returns.
/// In asynchronous mode at least the root tag is parsed. This
/// is necessary for the way binders currently work.
/// </summary>
public void Parse()
{
// if parseMode hasn't been set then set it now to synchronous.
if (XamlParseMode == XamlParseMode.Uninitialized)
{
XamlParseMode = XamlParseMode.Synchronous;
}
_Parse();
}
/// <summary>
/// Main function called by the XamlCompiler on a Compile.
/// If singleRecordMode is true the Read returns after each item read.
/// </summary>
/// <returns>True if more nodes to read</returns>
// !! Review - now that have separate out into XamlReaderHelper
// review if still need all the virtuals or the caller of the TokenReader
// can dispatch as they feel appropriate.
public bool ReadXaml(bool singleRecordMode)
{
XamlNode xamlNode = null;
bool cleanup = !singleRecordMode;
bool done = false;
try // What do we do with Exceptions we catch on this thread?.
{
while (TokenReader.Read(ref xamlNode))
{
SetParserAction(xamlNode);
if (ParserAction == ParserAction.Normal)
{
// If processing of the xaml node determines that we are done,
// then stop now. This can happen if ProcessXamlNode is
// overridden (such as when processing styles) and the parsed
// block is finished, but the overall file is not. In that
// case exit this parser, but leave everything intact.
ProcessXamlNode(xamlNode, ref cleanup, ref done);
if (done)
{
break;
}
// return here in single record mode since we don't want
// to set the parser loop to Done.
if (singleRecordMode)
{
return true;
}
}
}
}
catch (Exception e)
{
if (CriticalExceptions.IsCriticalException(e))
{
throw;
}
else
{
// Don't treat an AssemblyVersion parsing error as a XamlParseException.
// Throw it back to the task execution.
if(e is AssemblyVersionParseException)
{
throw;
}
if (e is XamlParseException)
{
throw;
}
// If the exception was a XamlParse exception on the other
// side of a Reflection Invoke, then just pull up the Parse exception.
if (e is TargetInvocationException && e.InnerException is XamlParseException)
{
throw e.InnerException;
}
int lineNumber = 0;
int linePosition = 0;
string newMessage = null;
if (e is XmlException)
{
XmlException xmlEx = (XmlException)e;
lineNumber = xmlEx.LineNumber;
linePosition = xmlEx.LinePosition;
newMessage = xmlEx.Message;
}
else
{
// generic exception, If have a xamlNode then use the
// nodes values for the line Numbers.
if (null != xamlNode)
{
lineNumber = xamlNode.LineNumber;
linePosition = xamlNode.LinePosition;
}
newMessage = $"{e.Message} {SR.Format(SR.ParserLineAndOffset,
lineNumber.ToString(CultureInfo.CurrentCulture),
linePosition.ToString(CultureInfo.CurrentCulture))}";
}
XamlParseException parseException = new XamlParseException(newMessage, lineNumber, linePosition, e);
ParseError(parseException);
// Recurse on error
cleanup = true;
throw parseException;
}
}
finally
{
// Perform cleanup only if we encountered a non-recoverable error, or
// we're finished reading the stream (EndDocument reached in single
// record mode, or end of stream reached in multi-record mode)
if (cleanup)
{
// Close the reader, which will close the underlying stream.
TokenReader.Close();
}
}
return false;
}
/// <summary>
/// Big switch to handle all the records.
/// </summary>
/// <param name="xamlNode"> Node received from TokenReader to process</param>
/// <param name="cleanup"> True if end of stream reached and document is
/// totally finished and should be closed </param>
/// <param name="done"> True if done processing and want to exit. Doesn't
/// necessarily mean document is finished (see cleanup) </param>
internal virtual void ProcessXamlNode(
XamlNode xamlNode,
ref bool cleanup,
ref bool done)
{
switch(xamlNode.TokenType)
{
case XamlNodeType.DocumentStart:
XamlDocumentStartNode xamlDocumentStartNode =
(XamlDocumentStartNode) xamlNode;
WriteDocumentStart(xamlDocumentStartNode);
break;
case XamlNodeType.DocumentEnd:
XamlDocumentEndNode xamlEndDocumentNode =
(XamlDocumentEndNode) xamlNode;
cleanup = true;
done = true;
WriteDocumentEnd(xamlEndDocumentNode);
break;
case XamlNodeType.ElementStart:
XamlElementStartNode xamlElementNode =
(XamlElementStartNode) xamlNode;
WriteElementStart(xamlElementNode);
break;
case XamlNodeType.ElementEnd:
XamlElementEndNode xamlEndElementNode =
(XamlElementEndNode) xamlNode;
WriteElementEnd(xamlEndElementNode);
break;
case XamlNodeType.UnknownTagStart:
XamlUnknownTagStartNode xamlUnknownTagStartNode =
(XamlUnknownTagStartNode) xamlNode;
WriteUnknownTagStart(xamlUnknownTagStartNode);
break;
case XamlNodeType.UnknownTagEnd:
XamlUnknownTagEndNode xamlUnknownTagEndNode =
(XamlUnknownTagEndNode) xamlNode;
WriteUnknownTagEnd(xamlUnknownTagEndNode);
break;
case XamlNodeType.XmlnsProperty:
XamlXmlnsPropertyNode xamlXmlnsPropertyNode =
(XamlXmlnsPropertyNode) xamlNode;
WriteNamespacePrefix(xamlXmlnsPropertyNode);
break;
case XamlNodeType.Property:
XamlPropertyNode xamlPropertyNode =
(XamlPropertyNode) xamlNode;
if (xamlPropertyNode.AttributeUsage == BamlAttributeUsage.RuntimeName)
{
_parserContext.XamlTypeMapper.ValidateNames(
xamlPropertyNode.Value,
xamlPropertyNode.LineNumber,
xamlPropertyNode.LinePosition);
}
WriteProperty(xamlPropertyNode);
break;
case XamlNodeType.PropertyWithExtension:
XamlPropertyWithExtensionNode xamlPropertyWithExtensionNode =
(XamlPropertyWithExtensionNode)xamlNode;
WritePropertyWithExtension(xamlPropertyWithExtensionNode);
break;
case XamlNodeType.PropertyWithType:
XamlPropertyWithTypeNode xamlPropertyWithTypeNode =
(XamlPropertyWithTypeNode) xamlNode;
WritePropertyWithType(xamlPropertyWithTypeNode);
break;
case XamlNodeType.UnknownAttribute:
XamlUnknownAttributeNode xamlUnknownAttributeNode =
(XamlUnknownAttributeNode) xamlNode;
WriteUnknownAttribute(xamlUnknownAttributeNode);
break;
case XamlNodeType.PropertyComplexStart:
XamlPropertyComplexStartNode xamlPropertyComplexStartNode =
(XamlPropertyComplexStartNode) xamlNode;
WritePropertyComplexStart(xamlPropertyComplexStartNode);
break;
case XamlNodeType.PropertyComplexEnd:
XamlPropertyComplexEndNode xamlPropertyComplexEndNode =
(XamlPropertyComplexEndNode) xamlNode;
WritePropertyComplexEnd(xamlPropertyComplexEndNode);
break;
case XamlNodeType.LiteralContent:
XamlLiteralContentNode xamlLiteralContentNode =
(XamlLiteralContentNode) xamlNode;
WriteLiteralContent(xamlLiteralContentNode);
break;
case XamlNodeType.Text:
XamlTextNode xamlTextNode =
(XamlTextNode) xamlNode;
WriteText(xamlTextNode);
break;
case XamlNodeType.ClrEvent:
XamlClrEventNode xamlClrEventNode =
(XamlClrEventNode) xamlNode;
WriteClrEvent(xamlClrEventNode);
break;
case XamlNodeType.PropertyArrayStart:
XamlPropertyArrayStartNode xamlPropertyArrayStartNode =
(XamlPropertyArrayStartNode) xamlNode;
WritePropertyArrayStart(xamlPropertyArrayStartNode);
break;
case XamlNodeType.PropertyArrayEnd:
XamlPropertyArrayEndNode xamlPropertyArrayEndNode =
(XamlPropertyArrayEndNode) xamlNode;
WritePropertyArrayEnd(xamlPropertyArrayEndNode);
break;
case XamlNodeType.PropertyIListStart:
XamlPropertyIListStartNode xamlPropertyIListStartNode =
(XamlPropertyIListStartNode) xamlNode;
WritePropertyIListStart(xamlPropertyIListStartNode);
break;
case XamlNodeType.PropertyIListEnd:
XamlPropertyIListEndNode xamlPropertyIListEndNode =
(XamlPropertyIListEndNode) xamlNode;
WritePropertyIListEnd(xamlPropertyIListEndNode);
break;
case XamlNodeType.PropertyIDictionaryStart:
XamlPropertyIDictionaryStartNode xamlPropertyIDictionaryStartNode =
(XamlPropertyIDictionaryStartNode) xamlNode;
WritePropertyIDictionaryStart(xamlPropertyIDictionaryStartNode);
break;
case XamlNodeType.PropertyIDictionaryEnd:
XamlPropertyIDictionaryEndNode xamlPropertyIDictionaryEndNode =
(XamlPropertyIDictionaryEndNode) xamlNode;
WritePropertyIDictionaryEnd(xamlPropertyIDictionaryEndNode);
break;
case XamlNodeType.DefTag:
XamlDefTagNode xamlDefTagNode =
(XamlDefTagNode) xamlNode;
WriteDefTag(xamlDefTagNode);
break;
case XamlNodeType.DefKeyTypeAttribute:
XamlDefAttributeKeyTypeNode xamlDefAttributeKeyTypeNode =
(XamlDefAttributeKeyTypeNode) xamlNode;
WriteDefAttributeKeyType(xamlDefAttributeKeyTypeNode);
break;
case XamlNodeType.DefAttribute:
XamlDefAttributeNode xamlDefAttributeNode =
(XamlDefAttributeNode) xamlNode;
if (xamlDefAttributeNode.AttributeUsage == BamlAttributeUsage.RuntimeName)
{
_parserContext.XamlTypeMapper.ValidateNames(
xamlDefAttributeNode.Value,
xamlDefAttributeNode.LineNumber,
xamlDefAttributeNode.LinePosition);
}
WriteDefAttributeCore(xamlDefAttributeNode);
break;
case XamlNodeType.PresentationOptionsAttribute:
XamlPresentationOptionsAttributeNode xamlPresentationOptionsAttributeNode =
(XamlPresentationOptionsAttributeNode) xamlNode;
WritePresentationOptionsAttribute(xamlPresentationOptionsAttributeNode);
break;
case XamlNodeType.PIMapping:
XamlPIMappingNode xamlPIMappingNode =
(XamlPIMappingNode) xamlNode;
WritePIMapping(xamlPIMappingNode);
break;
// The following tokens that are used primarily by the markup compiler
case XamlNodeType.EndAttributes:
XamlEndAttributesNode xamlEndAttributesNode =
(XamlEndAttributesNode) xamlNode;
// if first tag and haven't alredy set the ParseMode
// set it to synchronous.
if (0 == xamlEndAttributesNode.Depth)
{
if (XamlParseMode == XamlParseMode.Uninitialized)
{
XamlParseMode = XamlParseMode.Synchronous;
}
}
WriteEndAttributes(xamlEndAttributesNode);
break;
case XamlNodeType.KeyElementStart:
XamlKeyElementStartNode xamlKeyElementStartNode =
(XamlKeyElementStartNode) xamlNode;
WriteKeyElementStart(xamlKeyElementStartNode);
break;
case XamlNodeType.KeyElementEnd:
XamlKeyElementEndNode xamlKeyElementEndNode =
(XamlKeyElementEndNode) xamlNode;
WriteKeyElementEnd(xamlKeyElementEndNode);
break;
case XamlNodeType.ConstructorParametersEnd:
XamlConstructorParametersEndNode xamlConstructorParametersEndNode =
(XamlConstructorParametersEndNode) xamlNode;
WriteConstructorParametersEnd(xamlConstructorParametersEndNode);
break;
case XamlNodeType.ConstructorParametersStart:
XamlConstructorParametersStartNode xamlConstructorParametersStartNode =
(XamlConstructorParametersStartNode) xamlNode;
WriteConstructorParametersStart(xamlConstructorParametersStartNode);
break;
case XamlNodeType.ContentProperty:
XamlContentPropertyNode xamlContentPropertyNode =
(XamlContentPropertyNode)xamlNode;
WriteContentProperty(xamlContentPropertyNode);
break;
case XamlNodeType.ConstructorParameterType:
XamlConstructorParameterTypeNode xamlConstructorParameterTypeNode =
(XamlConstructorParameterTypeNode)xamlNode;
WriteConstructorParameterType(xamlConstructorParameterTypeNode);
break;
default:
Debug.Assert(false,"Unknown Xaml Token.");
break;
}
}
#endregion // PublicMethods
#region Virtuals
/// <summary>
/// Called when parsing begins
/// </summary>
public virtual void WriteDocumentStart(XamlDocumentStartNode XamlDocumentStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDocumentStart(XamlDocumentStartNode);
}
}
/// <summary>
/// Called when parsing ends
/// </summary>
public virtual void WriteDocumentEnd(XamlDocumentEndNode xamlEndDocumentNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDocumentEnd(xamlEndDocumentNode);
}
}
/// <summary>
/// Write Start of an Element, which is a tag of the form /<Classname />
/// </summary>
public virtual void WriteElementStart(XamlElementStartNode xamlElementStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteElementStart(xamlElementStartNode);
}
}
/// <summary>
/// Write start of an unknown tag
/// </summary>
public virtual void WriteUnknownTagStart(XamlUnknownTagStartNode xamlUnknownTagStartNode)
{
// The default action for unknown tags is throw an exception.
ThrowException(nameof(SR.ParserUnknownTag),
xamlUnknownTagStartNode.Value,
xamlUnknownTagStartNode.XmlNamespace,
xamlUnknownTagStartNode.LineNumber,
xamlUnknownTagStartNode.LinePosition);
}
/// <summary>
/// Write end of an unknown tag
/// </summary>
public virtual void WriteUnknownTagEnd(XamlUnknownTagEndNode xamlUnknownTagEndNode)
{
// The default action for unknown tags is throw an exception. This should never
// get here unless there is a coding error, since it would first hit
// WriteUnknownTagStart
ThrowException(nameof(SR.ParserUnknownTag),
"???",
xamlUnknownTagEndNode.LineNumber,
xamlUnknownTagEndNode.LinePosition);
}
/// <summary>
/// Write End Element
/// </summary>
public virtual void WriteElementEnd(XamlElementEndNode xamlElementEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteElementEnd(xamlElementEndNode);
}
}
/// <summary>
/// Called when parsing hits literal content.
/// </summary>
public virtual void WriteLiteralContent(XamlLiteralContentNode xamlLiteralContentNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteLiteralContent(xamlLiteralContentNode);
}
}
/// <summary>
/// Write Start Complex Property, where the tag is of the
/// form /<Classname.PropertyName/>
/// </summary>
public virtual void WritePropertyComplexStart(
XamlPropertyComplexStartNode xamlPropertyComplexStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyComplexStart(xamlPropertyComplexStartNode);
}
}
/// <summary>
/// Write End Complex Property
/// </summary>
public virtual void WritePropertyComplexEnd(
XamlPropertyComplexEndNode xamlPropertyComplexEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyComplexEnd(xamlPropertyComplexEndNode);
}
}
/// <summary>
/// Write Start element for a dictionary key section.
/// </summary>
public virtual void WriteKeyElementStart(
XamlElementStartNode xamlKeyElementStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteKeyElementStart(xamlKeyElementStartNode);
}
}
/// <summary>
/// Write End element for a dictionary key section
/// </summary>
public virtual void WriteKeyElementEnd(
XamlElementEndNode xamlKeyElementEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteKeyElementEnd(xamlKeyElementEndNode);
}
}
/// <summary>
/// Write unknown attribute
/// </summary>
public virtual void WriteUnknownAttribute(XamlUnknownAttributeNode xamlUnknownAttributeNode)
{
// The default action for unknown attributes is throw an exception.
ThrowException(nameof(SR.ParserUnknownAttribute),
xamlUnknownAttributeNode.Name,
xamlUnknownAttributeNode.XmlNamespace,
xamlUnknownAttributeNode.LineNumber,
xamlUnknownAttributeNode.LinePosition);
}
/// <summary>
/// Write a Property, which has the form in markup of property="value".
/// </summary>
/// <remarks>
/// Note that for DependencyProperties, the assemblyName, TypeFullName, PropIdName
/// refer to DependencyProperty field
/// that the property was found on. This may be different from the ownerType
/// of the propId if the property was registered as an alias so if the
/// callback is persisting we want to persist the information that the propId
/// was found on in case the alias is a private or internal field.
/// </remarks>
public virtual void WriteProperty(XamlPropertyNode xamlPropertyNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteProperty(xamlPropertyNode);
}
}
internal void WriteBaseProperty(XamlPropertyNode xamlPropertyNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.BaseWriteProperty(xamlPropertyNode);
}
}
/// <summary>
/// Write a Property, which has the form in markup of property="value".
/// </summary>
/// <remarks>
/// Note that for DependencyProperties, the assemblyName, TypeFullName, PropIdName
/// refer to DependencyProperty field
/// that the property was found on. This may be different from the ownerType
/// of the propId if the property was registered as an alias so if the
/// callback is persisting we want to persist the information that the propId
/// was found on in case the alias is a private or internal field.
/// </remarks>
public virtual void WritePropertyWithType(XamlPropertyWithTypeNode xamlPropertyNode)
{
if (BamlRecordWriter != null)
{
if (xamlPropertyNode.ValueElementType == null)
{
ThrowException(nameof(SR.ParserNoType),
xamlPropertyNode.ValueTypeFullName,
xamlPropertyNode.LineNumber,
xamlPropertyNode.LinePosition);
}
BamlRecordWriter.WritePropertyWithType(xamlPropertyNode);
}
}
public virtual void WritePropertyWithExtension(XamlPropertyWithExtensionNode xamlPropertyWithExtensionNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyWithExtension(xamlPropertyWithExtensionNode);
}
}
/// <summary>
/// Write out Text, currently don't keep track if originally CData or Text
/// </summary>
public virtual void WriteText(XamlTextNode xamlTextNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteText(xamlTextNode);
}
}
/// <summary>
/// Write a new namespacePrefix to NamespaceURI map
/// </summary>
public virtual void WriteNamespacePrefix(XamlXmlnsPropertyNode xamlXmlnsPropertyNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteNamespacePrefix(xamlXmlnsPropertyNode);
}
}
/// <summary>
/// Xml - Clr namespace mapping
/// </summary>
public virtual void WritePIMapping(XamlPIMappingNode xamlPIMappingNode)
{
// The only case when the assembly name can be empty is when there is a local assembly
// specified in the Mapping PI, but the compiler extension should have resolved it by
// now. So if we are still seeing an empty string here that means we in the pure xaml
// parsing scenario and should throw.
if (xamlPIMappingNode.AssemblyName.Length == 0)
{
ThrowException(nameof(SR.ParserMapPIMissingKey), xamlPIMappingNode.LineNumber, xamlPIMappingNode.LinePosition);
}
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePIMapping(xamlPIMappingNode);
}
}
/// <summary>
/// Write out the Clr event.
/// </summary>
public virtual void WriteClrEvent(XamlClrEventNode xamlClrEventNode)
{
// Parser currently doesn't support hooking up Events directly from
// XAML so throw an exception. In the compile case this method
// is overriden.
if (null == ParserHooks)
{
ThrowException(nameof(SR.ParserNoEvents),
xamlClrEventNode.LineNumber,
xamlClrEventNode.LinePosition);
}
else if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteClrEvent(xamlClrEventNode);
}
}
/// <summary>
/// Write Property Array Start
/// </summary>
public virtual void WritePropertyArrayStart(XamlPropertyArrayStartNode xamlPropertyArrayStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyArrayStart(xamlPropertyArrayStartNode);
}
}
/// <summary>
/// Write Property Array End
/// </summary>
public virtual void WritePropertyArrayEnd(XamlPropertyArrayEndNode xamlPropertyArrayEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyArrayEnd(xamlPropertyArrayEndNode);
}
}
/// <summary>
/// Write Property IList Start
/// </summary>
public virtual void WritePropertyIListStart(XamlPropertyIListStartNode xamlPropertyIListStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyIListStart(xamlPropertyIListStartNode);
}
}
/// <summary>
/// Write Property IList End
/// </summary>
public virtual void WritePropertyIListEnd(XamlPropertyIListEndNode xamlPropertyIListEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyIListEnd(xamlPropertyIListEndNode);
}
}
/// <summary>
/// Write Property IDictionary Start
/// </summary>
public virtual void WritePropertyIDictionaryStart(XamlPropertyIDictionaryStartNode xamlPropertyIDictionaryStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyIDictionaryStart(xamlPropertyIDictionaryStartNode);
}
}
/// <summary>
/// Write Property IDictionary End
/// </summary>
public virtual void WritePropertyIDictionaryEnd(XamlPropertyIDictionaryEndNode xamlPropertyIDictionaryEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePropertyIDictionaryEnd(xamlPropertyIDictionaryEndNode);
}
}
/// <summary>
/// WriteEndAttributes occurs after the last attribute (property, complex property or
/// def record) is written. Note that if there are none of the above, then WriteEndAttributes
/// is not called for a normal start tag.
/// </summary>
public virtual void WriteEndAttributes(XamlEndAttributesNode xamlEndAttributesNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteEndAttributes(xamlEndAttributesNode);
}
}
/// <summary>
/// WriteDefTag occurs when a x:Whatever tag is encountered.
/// Superclasses must interprete this since the base class doesn't understand
/// any of them.
/// </summary>
public virtual void WriteDefTag(XamlDefTagNode xamlDefTagNode)
{
ThrowException(nameof(SR.ParserDefTag),
xamlDefTagNode.Value,
xamlDefTagNode.LineNumber,
xamlDefTagNode.LinePosition);
}
/// <summary>
/// Write out a key to a dictionary that has been resolved at compile or parse
/// time to a Type object.
/// </summary>
public virtual void WriteDefAttributeKeyType(XamlDefAttributeKeyTypeNode xamlDefNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDefAttributeKeyType(xamlDefNode);
}
}
/// <summary>
/// WriteDefAttribute when attributes of the form x:Whatever are encountered
/// </summary>
public virtual void WriteDefAttribute(XamlDefAttributeNode xamlDefAttributeNode)
{
string attributeValue = xamlDefAttributeNode.Value;
// There are several known def attributes, and these are checked for
// correctness by running the known type converters.
switch(xamlDefAttributeNode.Name)
{
case XamlReaderHelper.DefinitionSynchronousMode:
if (BamlRecordWriter != null)
{
if (xamlDefAttributeNode.Value == "Async")
{
ThrowException(nameof(SR.ParserNoBamlAsync), "Async",
xamlDefAttributeNode.LineNumber,
xamlDefAttributeNode.LinePosition);
}
}
break;
case XamlReaderHelper.DefinitionAsyncRecords:
// Update the AsyncRecords and don't store this as a def attribute
ThrowException(nameof(SR.ParserNoBamlAsync), xamlDefAttributeNode.Name,
xamlDefAttributeNode.LineNumber,
xamlDefAttributeNode.LinePosition);
break;
case XamlReaderHelper.DefinitionShared:
Boolean.Parse(attributeValue); // For validation only.
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDefAttribute(xamlDefAttributeNode);
}
break;
case XamlReaderHelper.DefinitionUid:
case XamlReaderHelper.DefinitionRuntimeName:
//Error if x:Uid or x:Name are markup extensions
if (MarkupExtensionParser.LooksLikeAMarkupExtension(attributeValue))
{
string message = SR.Format(SR.ParserBadUidOrNameME, attributeValue);
message += " ";
message += SR.Format(SR.ParserLineAndOffset,
xamlDefAttributeNode.LineNumber.ToString(CultureInfo.CurrentCulture),
xamlDefAttributeNode.LinePosition.ToString(CultureInfo.CurrentCulture));
XamlParseException parseException = new XamlParseException(message,
xamlDefAttributeNode.LineNumber, xamlDefAttributeNode.LinePosition);
throw parseException;
}
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDefAttribute(xamlDefAttributeNode);
}
break;
case XamlReaderHelper.DefinitionName:
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteDefAttribute(xamlDefAttributeNode);
}
break;
default:
string errorID;
errorID = nameof(SR.ParserUnknownDefAttribute);
ThrowException(errorID,
xamlDefAttributeNode.Name,
xamlDefAttributeNode.LineNumber,
xamlDefAttributeNode.LinePosition);
break;
}
}
/// <summary>
/// Write attributes of the form PresentationOptions:Whatever to Baml
/// </summary>
public virtual void WritePresentationOptionsAttribute(XamlPresentationOptionsAttributeNode xamlPresentationOptionsAttributeNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WritePresentationOptionsAttribute(xamlPresentationOptionsAttributeNode);
}
}
/// <summary>
/// Write the start of a constructor parameter section
/// </summary>
public virtual void WriteConstructorParametersStart(XamlConstructorParametersStartNode xamlConstructorParametersStartNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteConstructorParametersStart(xamlConstructorParametersStartNode);
}
}
public virtual void WriteContentProperty(XamlContentPropertyNode xamlContentPropertyNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteContentProperty(xamlContentPropertyNode);
}
}
/// <summary>
/// Write the constructor parameter record where the parameter is a compile time
/// resolved Type object.
/// </summary>
public virtual void WriteConstructorParameterType(
XamlConstructorParameterTypeNode xamlConstructorParameterTypeNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteConstructorParameterType(xamlConstructorParameterTypeNode);
}
}
/// <summary>
/// Write the end of a constructor parameter section
/// </summary>
public virtual void WriteConstructorParametersEnd(XamlConstructorParametersEndNode xamlConstructorParametersEndNode)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteConstructorParametersEnd(xamlConstructorParametersEndNode);
}
}
/// <summary>
/// Can be used by ac to return the type of the element if a class attribute is
/// present. If want to override default Type because of class= attribute can do so here.
/// Should leave the XmlReader positioned at the Element so if read attributes
/// to determine type need to call XmlReader.MoveToElement()
/// </summary>
public virtual bool GetElementType(
XmlReader reader,
string localName,
string namespaceUri,
ref string assemblyName,
ref string typeFullName,
ref Type baseType,
ref Type serializerType)
{
bool result = false;
assemblyName = string.Empty;
typeFullName = string.Empty;
serializerType = null;
baseType = null;
// if no namespaceURI or local name don't bother
if (null == namespaceUri || null == localName)
{
return false;
}
TypeAndSerializer typeAndSerializer =
XamlTypeMapper.GetTypeAndSerializer(namespaceUri, localName, null);
if (typeAndSerializer != null &&
typeAndSerializer.ObjectType != null)
{
serializerType = typeAndSerializer.SerializerType;
baseType = typeAndSerializer.ObjectType;
typeFullName = baseType.FullName;
assemblyName = baseType.Assembly.FullName;
result = true;
Debug.Assert(null != assemblyName, "assembly name returned from GetBaseElement is null");
Debug.Assert(null != typeFullName, "Type name returned from GetBaseElement is null");
}
return result;
}
#endregion Virtuals
/// <summary>
/// Write a Connector Id for compiler.
/// </summary>
protected internal void WriteConnectionId(Int32 connectionId)
{
if (BamlRecordWriter != null)
{
BamlRecordWriter.WriteConnectionId(connectionId);
}
}
/// <summary>
/// A def attribute was encountered. Perform synchonous mode checking
/// prior to calling the virtual that may be overridden.
/// </summary>
void WriteDefAttributeCore(XamlDefAttributeNode xamlDefAttributeNode)
{
string attributeValue = xamlDefAttributeNode.Value;
switch(xamlDefAttributeNode.Name)
{
case XamlReaderHelper.DefinitionSynchronousMode:
XamlParseMode documentParseMode = XamlParseMode.Synchronous;
if (attributeValue.Equals("Async"))
{
documentParseMode = XamlParseMode.Asynchronous;
}
else if (attributeValue.Equals("Sync"))
{
documentParseMode = XamlParseMode.Synchronous;
}
else
{
ThrowException(nameof(SR.ParserBadSyncMode),
xamlDefAttributeNode.LineNumber,
xamlDefAttributeNode.LinePosition );
}
// if we haven't initialized the the parseMode yet set it
if (XamlParseMode == XamlParseMode.Uninitialized)
{
XamlParseMode = documentParseMode;
}
break;
default:
break;
}
WriteDefAttribute(xamlDefAttributeNode);
}
#region Methods
// virtuals to override the default implementation. used by the compiler
// for internal virtuals review why not public as the others?
// Used when an exception is thrown.The default action is to shutdown the parser
// and throw the exception.
internal virtual void ParseError(XamlParseException e)
{
}
/// <summary>
/// Called when the parse was cancelled by the user.
/// </summary>
internal virtual void ParseCancelled()
{
}
/// <summary>
/// called when the parse has been completed successfully.
/// </summary>
internal virtual void ParseCompleted()
{
}
/// <summary>
/// Default parsing is to synchronously read all the xaml nodes
/// until done.
/// </summary>
internal virtual void _Parse()
{
ReadXaml(false /* want to parse the entire thing */);
}
/// <summary>
/// If there are ParserHooks, call it with the current xamlNode and perform
/// as directed by the callback.
/// </summary>
private void SetParserAction(XamlNode xamlNode)
{
// if no ParserHooks then process as normal
if (null == ParserHooks)
{
ParserAction = ParserAction.Normal;
return;
}
// if ParserHooks want to skip the current node and its children,
// check for end of scope where it asked to be skipped.
if (ParserAction == ParserAction.Skip)
{
if (xamlNode.Depth <= SkipActionDepthCount &&
xamlNode.TokenType == SkipActionToken)
{
// We found the end token at the correct depth. Reset the depth count
// so that in the next call we won't skip calling the ParserHooks. Don't
// reset the ParserAction since we want to skip this end token.
SkipActionDepthCount = -1;
SkipActionToken = XamlNodeType.Unknown;
return;
}
else if (SkipActionDepthCount >= 0)
{
return;
}
}
// If we get to here, the ParserHooks want to be called.
ParserAction = ParserHooks.LoadNode(xamlNode);
// if the ParserHooks want to skip the current node and its children then
// set the callback depth so that we'll know when to start processing again.
if (ParserAction == ParserAction.Skip)
{
// For tokens with no scope (eg = attributes), don't set the depth so
// that we will only skip once
Debug.Assert(SkipActionDepthCount == -1);
int tokenIndex = ((IList)XamlNode.ScopeStartTokens).IndexOf(xamlNode.TokenType);
if (tokenIndex != -1)
{
SkipActionDepthCount = xamlNode.Depth;
SkipActionToken = XamlNode.ScopeEndTokens[tokenIndex];
}
}
}
// Return true if the passed namespace is known, meaning that it maps
// to a set of assemblies and clr namespaces
internal bool IsXmlNamespaceSupported(string xmlNamespace, out string newXmlNamespace)
{
newXmlNamespace = null;
if (xmlNamespace.StartsWith(XamlReaderHelper.MappingProtocol, StringComparison.Ordinal))
{
return true;
}
else if (xmlNamespace == XamlReaderHelper.PresentationOptionsNamespaceURI)
{
// PresentationOptions is expected to be marked as 'ignorable' in most Xaml
// so that other Xaml parsers don't have to interpret it, but this parser
// does handle it to support it's Freeze attribute.
return true;
}
else
{
return XamlTypeMapper.IsXmlNamespaceKnown(xmlNamespace, out newXmlNamespace) ||
TokenReader.IsXmlDataIsland();
}
}
#endregion Methods
#region Properties
/// <summary>
/// TokenReader that is being used.
/// </summary>
internal XamlReaderHelper TokenReader
{
get { return _xamlTokenReader; }
set { _xamlTokenReader = value; }
}
/// <summary>
/// ParserHooks implementation that any parse time callbacks
/// should be called on.
/// </summary>
internal ParserHooks ParserHooks
{
get { return _parserHooks; }
set { _parserHooks = value; }
}
// Set the depth count for how deep we are within
// a ParserAction.Skip reference.
int SkipActionDepthCount
{
get { return _skipActionDepthCount; }
set { _skipActionDepthCount = value; }
}
// Set and get the token to watch for when skipping a
// section of a xaml file
XamlNodeType SkipActionToken
{
get { return _skipActionToken; }
set { _skipActionToken = value; }
}
// set the operation mode of the parser as determined
// by attached ParserHooks
ParserAction ParserAction
{
get { return _parserAction; }
set { _parserAction = value; }
}
/// <summary>
/// Instance of the XamlTypeMapper
/// </summary>
internal XamlTypeMapper XamlTypeMapper
{
get { return _parserContext.XamlTypeMapper; }
}
/// <summary>
/// Instance of the BamlMapTable
/// </summary>
internal BamlMapTable MapTable
{
get { return _parserContext.MapTable; }
}
/// <summary>
/// BamlRecordWriter being used by the Parser
/// </summary>
public BamlRecordWriter BamlRecordWriter
{
get { return _bamlWriter; }
set
{
Debug.Assert(null == _bamlWriter || null == value, "XamlParser already had a bamlWriter");
_bamlWriter = value;
}
}
/// <summary>
/// ParseMode the Parser is in.
/// </summary>
internal XamlParseMode XamlParseMode
{
get { return _xamlParseMode; }
set { _xamlParseMode = value; }
}
/// <summary>
/// Parser context
/// </summary>
internal ParserContext ParserContext
{
get { return _parserContext; }
set { _parserContext = value; }
}
internal virtual bool CanResolveLocalAssemblies()
{
return false;
}
// Used to determine if strict or loose parsing rules should be enforced. The TokenReader
// does some validations that are difficult for the XamlParser to do and in strict parsing
// mode the TokenReader should throw exceptions of standard Xaml rules are violated.
internal virtual bool StrictParsing
{
get { return true; }
}
#endregion Properties
#region Data
// private Data
XamlReaderHelper _xamlTokenReader;
ParserContext _parserContext;
XamlParseMode _xamlParseMode;
BamlRecordWriter _bamlWriter;
// ParserHooks related
ParserHooks _parserHooks;
ParserAction _parserAction = ParserAction.Normal;
int _skipActionDepthCount = -1; // skip mode depth count.
XamlNodeType _skipActionToken = XamlNodeType.Unknown;
static private string [] _predefinedNamespaces = new string [3] {
XamlReaderHelper.DefinitionNamespaceURI,
XamlReaderHelper.DefaultNamespaceURI,
XamlReaderHelper.DefinitionMetroNamespaceURI
};
#endregion Data
#endif
// helper method called to throw an exception.
internal static void ThrowException(string id, int lineNumber, int linePosition)
{
string message = SR.GetResourceString(id);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
// helper method called to throw an exception.
internal static void ThrowException(string id, string value, int lineNumber, int linePosition)
{
string message = SR.Format(SR.GetResourceString(id), value);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
// helper method called to throw an exception.
internal static void ThrowException(string id, string value1, string value2, int lineNumber, int linePosition)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
internal static void ThrowException(string id, string value1, string value2, string value3, int lineNumber, int linePosition)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2, value3);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
internal static void ThrowException(string id, string value1, string value2, string value3, string value4, int lineNumber, int linePosition)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2, value3, value4);
ThrowExceptionWithLine(message, lineNumber, linePosition);
}
private static void ThrowExceptionWithLine(string message, int lineNumber, int linePosition)
{
message += " ";
message += SR.Format(SR.ParserLineAndOffset,
lineNumber.ToString(CultureInfo.CurrentCulture),
linePosition.ToString(CultureInfo.CurrentCulture));
XamlParseException parseException = new XamlParseException(message,
lineNumber, linePosition);
throw parseException;
}
}
}
|