/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Markup / BamlRecordReader.cs / 7 / BamlRecordReader.cs
/****************************************************************************\
*
* File: BamlRecordReader.cs
*
* Purpose: Main class to handle reading a Baml file
*
* History:
* 6/06/01: rogerg Created
* 5/28/03: [....] Ported to wcp
* 10/6/03: [....] Reorganized to remove DP - clr distinction
*
* Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Globalization;
using MS.Utility;
using MS.Internal;
using System.Runtime.InteropServices;
using MS.Internal.Utility;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Documents;
using System.Windows.Controls;
// 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
namespace System.Windows.Markup
{
// Unlike the tokenizer and the writer, the reader knows the difference between CLR
// object and DependencyObjects. This is done because DependencyObjects support a
// superset of CLR functionality, such as the ability to quickly set a property
// using DependencyObject.SetValue(DependencyProperty, object)
internal enum ReaderFlags : ushort
{
// Context types
Unknown = 0x0000,
DependencyObject = 0x1000,
ClrObject = 0x2000,
PropertyComplexClr = 0x3000,
PropertyComplexDP = 0x4000,
PropertyArray = 0x5000,
PropertyIList = 0x6000,
PropertyIDictionary = 0x7000,
PropertyIAddChild = 0x8000,
RealizeDeferContent = 0x9000,
ConstructorParams = 0xA000,
ContextTypeMask = 0xF000,
StyleObject = 0x0100,
FrameworkTemplateObject = 0x0200,
TableTemplateObject = 0x0400,
SingletonConstructorParam = 0x0800,
// Element flags
NeedToAddToTree = 0x0001, // Need to add to element tree, but haven't yet
AddedToTree = 0x0002, // Has already been added to element tree, so don't do it again
InjectedElement = 0x0004, // Context was an injected element, so skip over it
CollectionHolder = 0x0008,
IDictionary = 0x0010,
IList = 0x0020,
ArrayExt = 0x0040,
IAddChild = 0x0080,
}
///
/// reads BAML from a Stream
/// This is an internal class
///
//
// This code should always be transparent. Meaning you should never add
// SecurityCritical to this section of the code.
//
internal class BamlRecordReader
{
#region Constructor
///
/// Eventually should need xamltypemapper when finish compiler integration
/// and namespaceMaps are written to Baml.
/// summary>
internal BamlRecordReader(
Stream bamlStream,
ParserContext parserContext)
: this(bamlStream,parserContext,true)
{
XamlParseMode = XamlParseMode.Synchronous;
}
internal BamlRecordReader(
Stream bamlStream,
ParserContext parserContext,
object root)
{
Debug.Assert(null != bamlStream);
Debug.Assert(null != parserContext && null != parserContext.XamlTypeMapper);
ParserContext = parserContext;
_rootElement = root;
_bamlAsForest = (root != null);
if (_bamlAsForest)
{
ParserContext.RootElement = _rootElement;
}
_rootList = new ArrayList(1);
BamlStream = bamlStream;
}
///
/// BamlRecordReader constructor
///
/// The input BAML stream
/// The parser context
/// Ensure parser context has same XamlTypeMapper and
/// map table as this reader
internal BamlRecordReader(
Stream bamlStream,
ParserContext parserContext,
bool loadMapper)
{
Debug.Assert(null != parserContext && null != parserContext.XamlTypeMapper);
ParserContext = parserContext;
_rootList = new ArrayList(1);
BamlStream = bamlStream;
if (loadMapper)
{
ParserContext.XamlTypeMapper = XamlTypeMapper;
}
}
///
/// Default internal constructor
///
protected internal BamlRecordReader()
{
}
#endregion Constructor
#region Methods
///
/// Set up the XamlTypeMapper and baml map table prior to starting to read.
///
internal void Initialize()
{
MapTable.Initialize();
XamlTypeMapper.Initialize();
ParserContext.Initialize();
}
///
/// Array of root objects contained in the baml stream. This is added to
/// as the baml is loaded.
///
internal ArrayList RootList
{
get { return _rootList; }
set { _rootList = value; }
}
///
/// True if tree is to be built strictly (well, more or less strictly)
/// top down.
///
internal bool BuildTopDown
{
get { return _buildTopDown; }
set { _buildTopDown = value; }
}
internal int BytesAvailible
{
get
{
Stream stream = BinaryReader.BaseStream;
return (int)(stream.Length - stream.Position);
}
}
///
/// Read a BamlRecord from the underlying binary stream and return it.
/// If we're at the end of the stream, return null.
/// summary>
internal BamlRecord GetNextRecord()
{
BamlRecord bamlRecord = null;
if (null == PreParsedRecordsStart)
{
Stream stream = BinaryReader.BaseStream;
// need to read differently based on the binaryReady
// Todo: should cache this or pass as a flag to the BamlRecordReader
// in the Read operation.
// If we have a ReaderStream we know we are coming from XAML (check if can read full record)
// When reading BAML we don't get a ReaderStream so:
// network - should check for number of bits downloaded but currently
// no way to get this information. For now read sync.
// NOTE: This has to be addressed if we want async download
// of BAML.
// local file - in memory or something, so else read the record sync.
if (null != XamlReaderStream)
{
long currentPosition = stream.Position;
long bytesAvailable = stream.Length - currentPosition;
// Make sure there is room for the record type in the stream.
if (BamlRecord.RecordTypeFieldLength > bytesAvailable)
{
return null;
}
BamlRecordType recordType = (BamlRecordType)BinaryReader.ReadByte();
// We've read the record type, so decrement available bytes.
bytesAvailable -= BamlRecord.RecordTypeFieldLength;
// call GetNextRecord passing in the record type. If this returns null,
// then the complete record was not yet available and could not be
// read.
bamlRecord = ReadNextRecordWithDebugExtension(bytesAvailable, recordType);
if (bamlRecord == null)
{
#if DEBUG
// this case can happen if doing BAML Async and entire record hasn't
// been downloaded.
Debug.Assert(false == XamlReaderStream.IsWriteComplete,
"not enough bytes for RecordSize but write is complete");
#endif
stream.Seek(currentPosition,SeekOrigin.Begin);
return null;
}
// tell stream we are done with these file bits.
XamlReaderStream.ReaderDoneWithFileUpToPosition(stream.Position -1);
}
else
{
// default to reading a single record synchronous. Don't attempt
// to read if its already at the end of the stream.
bool keepOnReading = true;
while (keepOnReading)
{
if (BinaryReader.BaseStream.Length >
BinaryReader.BaseStream.Position)
{
// If we are supposed to skip info records, then just advance the stream
// for info records and continue until we get a non-info record, or we
// run out of stream data to read.
BamlRecordType recordType = (BamlRecordType)BinaryReader.ReadByte();
bamlRecord = ReadNextRecordWithDebugExtension(Int64.MaxValue, recordType);
keepOnReading = false;
}
else
{
keepOnReading = false;
}
}
}
}
else if (PreParsedCurrentRecord != null) // If the preparsed list has not reached its end
{
bamlRecord = PreParsedCurrentRecord; // return the record pointed to index
PreParsedCurrentRecord = PreParsedCurrentRecord.Next;
// if the next record is a debug record then process it and advance over it.
// The Debug record extension record is process BEFORE the current record because
// it is debug information regarding the current record.
if (BamlRecordHelper.HasDebugExtensionRecord(ParserContext.IsDebugBamlStream, bamlRecord))
{
ProcessDebugBamlRecord(PreParsedCurrentRecord);
PreParsedCurrentRecord = PreParsedCurrentRecord.Next;
}
}
return bamlRecord;
}
internal BamlRecord ReadNextRecordWithDebugExtension(
long bytesAvailable,
BamlRecordType recordType)
{
BamlRecord bamlRecord = BamlRecordManager.ReadNextRecord(BinaryReader, bytesAvailable, recordType);
if (IsDebugBamlStream)
{
if (BamlRecordHelper.DoesRecordTypeHaveDebugExtension(bamlRecord.RecordType))
{
BamlRecord debugExtensionRecord = ReadDebugExtensionRecord();
bamlRecord.Next = debugExtensionRecord;
}
}
return bamlRecord;
}
internal BamlRecord ReadDebugExtensionRecord()
{
Stream stream = BinaryReader.BaseStream;
long bytesAvailable = stream.Length - stream.Position;
if(bytesAvailable == 0)
return null;
BamlRecordType nextRecordType = (BamlRecordType)BinaryReader.ReadByte();
if (BamlRecordHelper.IsDebugBamlRecordType(nextRecordType))
{
BamlRecord debugBamlRecord = BamlRecordManager.ReadNextRecord(BinaryReader, bytesAvailable, nextRecordType);
ProcessDebugBamlRecord(debugBamlRecord);
return debugBamlRecord;
}
else
{
// if it wasn't a debug record then backup.
stream.Seek( -1, SeekOrigin.Current);
return null;
}
}
internal void ProcessDebugBamlRecord(BamlRecord bamlRecord)
{
if(bamlRecord.RecordType == BamlRecordType.LineNumberAndPosition)
{
BamlLineAndPositionRecord bamlLineAndPositionRecord = (BamlLineAndPositionRecord)bamlRecord;
LineNumber = (int)bamlLineAndPositionRecord.LineNumber;
LinePosition = (int)bamlLineAndPositionRecord.LinePosition;
}
else
{
Debug.Assert(bamlRecord.RecordType == BamlRecordType.LinePosition);
BamlLinePositionRecord bamlLinePositionRecord = (BamlLinePositionRecord)bamlRecord;
LinePosition = (int)bamlLinePositionRecord.LinePosition;
}
}
///
/// Gets the type of the record at the current position of the reader.
///
internal BamlRecordType GetNextRecordType()
{
BamlRecordType bamlRecordType;
if (null == PreParsedRecordsStart)
{
bamlRecordType = (BamlRecordType)BinaryReader.PeekChar();
}
else
{
bamlRecordType = PreParsedCurrentRecord.RecordType;
}
return bamlRecordType;
}
///
/// Close the underlying baml stream
///
internal void Close()
{
if (BamlStream != null)
{
BamlStream.Close();
}
EndOfDocument = true;
}
///
/// Read the Baml and buld a Tree.
///
internal bool Read(bool singleRecord)
{
BamlRecord bamlRecord = null;
bool moreData = true;
// loop through the records until the end building the Tree.
while ( (true == moreData)
&& null != (bamlRecord = GetNextRecord()))
{
moreData = ReadRecord(bamlRecord);
// if singleRecordMode then break
if (singleRecord)
{
break;
}
}
// if next bamlRecord read comes back null
// then moreData is false
if (null == bamlRecord)
{
moreData = false;
}
// return true for more data meaning it is worth calling
// read again if in Single Record mode. May or may not
// really be another record.
return moreData;
}
///
/// Synchronous read from Baml
///
internal bool Read()
{
return Read(false); // not in single record mode.
}
///
/// Synchronous read callback that passes line information from original xaml file.
/// This line information is used when reporting errors. Make certain that the
/// parser context line numbers are correct, since this is passed to subparsers and
/// serializers and they may wish to report line information also.
///
internal bool Read(
BamlRecord bamlRecord,
int lineNumber,
int linePosition)
{
LineNumber = lineNumber;
LinePosition = linePosition;
return ReadRecord(bamlRecord);
}
internal void ReadVersionHeader()
{
BamlVersionHeader version = new BamlVersionHeader();
version.LoadVersion(BinaryReader);
}
///
/// Read the Baml starting at the current location until the end of this
/// element scope has been reached. Return the object parsed.
///
///
/// Note that the first record read MUST be a BamlElementStartRecord, and
/// reading will continue until the matching BamlElementEndRecord is reached.
///
/// The dictionaryKey property, if set, indicates that this element is in a
/// dictionary, and this was the key used to identify it. This is used to
/// provide better exception messages for deferred instantiation from a dictionary.
///
internal object ReadElement(Int64 startPosition,
XamlObjectIds contextXamlObjectIds,
object dictionaryKey )
{
BamlRecord bamlRecord = null;
bool moreData = true;
BinaryReader.BaseStream.Position = startPosition;
int elementDepth = 0;
object data = null;
bool isKeySetInContext = false;
// Push a special context onto the stack that is used as a placeholder
// for surrounding context information about this element.
PushContext(ReaderFlags.RealizeDeferContent, null, null, 0);
CurrentContext.ElementNameOrPropertyName = contextXamlObjectIds.Name;
CurrentContext.Uid = contextXamlObjectIds.Uid;
CurrentContext.Key = dictionaryKey;
#if DEBUG
int stackDepth = ReaderContextStack.Count;
#endif
// Loop through the records until the matching end record is reached.
while ( moreData
&& null != (bamlRecord = GetNextRecord()))
{
// Count start and end records and stop when we've reached the end
// record associated with the first start record. Note that
// serializers handle the end record, so don't increment the element
// depth for types that have serializers (such as styles).
BamlElementStartRecord startRecord = bamlRecord as BamlElementStartRecord;
if (startRecord != null)
{
if (!MapTable.HasSerializerForTypeId(startRecord.TypeId))
{
elementDepth++;
}
}
else if (bamlRecord is BamlElementEndRecord)
{
elementDepth--;
}
moreData = ReadRecord(bamlRecord);
// If we got a key from the caller, it indicates that this element is being
// defer-loaded from a dictionary, and this was the element's key. Set it into
// the context, as would happen in the non-deferred case, so that it is available to
// make a good exception message.
if( !isKeySetInContext )
{
CurrentContext.Key = dictionaryKey;
isKeySetInContext = true;
}
// if singleRecordMode then break
if (elementDepth == 0)
{
break;
}
}
// Get the element out of the context, then restore it
// to null (as it was from the PushContext above).
data = CurrentContext.ObjectData;
CurrentContext.ObjectData = null;
#if DEBUG // ifdef's around Debug.Assert are necessary because stackDepth is only DEBUG defined
Debug.Assert( stackDepth == ReaderContextStack.Count );
#endif
PopContext();
MapTable.ClearConverterCache();
return data;
}
protected virtual void ReadConnectionId(BamlConnectionIdRecord bamlConnectionIdRecord)
{
// Hookup any IDs or events that correspond to this connectionId on the component
if (_componentConnector != null)
{
object target = GetCurrentObjectData();
Debug.Assert(bamlConnectionIdRecord.ConnectionId > 0);
_componentConnector.Connect(bamlConnectionIdRecord.ConnectionId, target);
}
}
void ReadDocumentStartRecord(BamlDocumentStartRecord documentStartRecord)
{
IsDebugBamlStream = documentStartRecord.DebugBaml;
}
void ReadDocumentEndRecord()
{
Debug.Assert(0 == ReaderContextStack.Count); // if not zero we missed an EndElement
SetPropertyValueToParent(false /*fromStartTag*/);
ParserContext.RootElement = null;
MapTable.ClearConverterCache();
EndOfDocument = true;
}
// Read a single record and process it. Return false if there are no
// more records to process.
internal virtual bool ReadRecord(BamlRecord bamlRecord)
{
bool moreData = true;
#if !STRESS
try
{
#endif
switch (bamlRecord.RecordType)
{
case BamlRecordType.DocumentStart:
ReadDocumentStartRecord((BamlDocumentStartRecord)bamlRecord);
break;
case BamlRecordType.DocumentEnd:
ReadDocumentEndRecord();
moreData = false;
break;
case BamlRecordType.XmlnsProperty:
ReadXmlnsPropertyRecord((BamlXmlnsPropertyRecord)bamlRecord);
break;
case BamlRecordType.PIMapping:
{
// If this mapping has not already been set up, then set it now
BamlPIMappingRecord piMappingRecord = (BamlPIMappingRecord)bamlRecord;
if (!XamlTypeMapper.PITable.Contains(piMappingRecord.XmlNamespace))
{
BamlAssemblyInfoRecord assemblyInfo = MapTable.GetAssemblyInfoFromId(piMappingRecord.AssemblyId);
// Add information to the MappingPI hashtable
ClrNamespaceAssemblyPair mapping = new ClrNamespaceAssemblyPair(
piMappingRecord.ClrNamespace,
assemblyInfo.AssemblyFullName);
XamlTypeMapper.PITable.Add(piMappingRecord.XmlNamespace, mapping);
}
break;
}
case BamlRecordType.AssemblyInfo:
MapTable.LoadAssemblyInfoRecord((BamlAssemblyInfoRecord)bamlRecord);
break;
case BamlRecordType.TypeInfo:
case BamlRecordType.TypeSerializerInfo:
MapTable.LoadTypeInfoRecord((BamlTypeInfoRecord)bamlRecord);
break;
case BamlRecordType.AttributeInfo:
MapTable.LoadAttributeInfoRecord((BamlAttributeInfoRecord)bamlRecord);
break;
case BamlRecordType.StringInfo:
MapTable.LoadStringInfoRecord((BamlStringInfoRecord)bamlRecord);
break;
case BamlRecordType.LiteralContent:
ReadLiteralContentRecord((BamlLiteralContentRecord)bamlRecord);
break;
case BamlRecordType.ElementStart:
case BamlRecordType.StaticResourceStart:
if (((BamlElementStartRecord)bamlRecord).IsInjected)
{
CurrentContext.SetFlag(ReaderFlags.InjectedElement);
}
else
{
ReadElementStartRecord((BamlElementStartRecord)bamlRecord);
}
break;
case BamlRecordType.NamedElementStart:
// This is only used by template code, and only as a temporary record, so should never occur here.
// See comment on BamlNamedElementStartRecord
Debug.Assert(false);
break;
case BamlRecordType.ConnectionId:
ReadConnectionId((BamlConnectionIdRecord)bamlRecord);
break;
case BamlRecordType.ElementEnd:
case BamlRecordType.StaticResourceEnd:
if (CurrentContext.CheckFlag(ReaderFlags.InjectedElement))
{
CurrentContext.ClearFlag(ReaderFlags.InjectedElement);
}
else
{
ReadElementEndRecord(false);
}
break;
case BamlRecordType.PropertyComplexStart:
ReadPropertyComplexStartRecord((BamlPropertyComplexStartRecord)bamlRecord);
break;
case BamlRecordType.PropertyComplexEnd:
ReadPropertyComplexEndRecord();
break;
case BamlRecordType.Property:
ReadPropertyRecord((BamlPropertyRecord)bamlRecord);
break;
case BamlRecordType.PropertyStringReference:
ReadPropertyStringRecord((BamlPropertyStringReferenceRecord)bamlRecord);
break;
case BamlRecordType.PropertyTypeReference:
ReadPropertyTypeRecord((BamlPropertyTypeReferenceRecord)bamlRecord);
break;
case BamlRecordType.PropertyWithExtension:
ReadPropertyWithExtensionRecord((BamlPropertyWithExtensionRecord)bamlRecord);
break;
case BamlRecordType.PropertyWithConverter:
ReadPropertyConverterRecord((BamlPropertyWithConverterRecord)bamlRecord);
break;
case BamlRecordType.PropertyCustom:
ReadPropertyCustomRecord((BamlPropertyCustomRecord)bamlRecord);
break;
case BamlRecordType.PropertyArrayStart:
ReadPropertyArrayStartRecord((BamlPropertyArrayStartRecord)bamlRecord);
break;
case BamlRecordType.PropertyArrayEnd:
ReadPropertyArrayEndRecord();
break;
case BamlRecordType.PropertyIListStart:
ReadPropertyIListStartRecord((BamlPropertyIListStartRecord)bamlRecord);
break;
case BamlRecordType.PropertyIListEnd:
ReadPropertyIListEndRecord();
break;
case BamlRecordType.PropertyIDictionaryStart:
ReadPropertyIDictionaryStartRecord((BamlPropertyIDictionaryStartRecord)bamlRecord);
break;
case BamlRecordType.PropertyIDictionaryEnd:
ReadPropertyIDictionaryEndRecord();
break;
case BamlRecordType.DefAttribute:
ReadDefAttributeRecord((BamlDefAttributeRecord)bamlRecord);
break;
case BamlRecordType.DefAttributeKeyType:
ReadDefAttributeKeyTypeRecord((BamlDefAttributeKeyTypeRecord)bamlRecord);
break;
case BamlRecordType.PresentationOptionsAttribute:
ReadPresentationOptionsAttributeRecord((BamlPresentationOptionsAttributeRecord)bamlRecord);
break;
case BamlRecordType.RoutedEvent:
{
Debug.Assert(ReaderFlags.DependencyObject == CurrentContext.ContextType);
DependencyObject currentParent = GetCurrentObjectData() as DependencyObject;
BamlRoutedEventRecord bamlRoutedEventRecord = (BamlRoutedEventRecord)bamlRecord;
//
ThrowException(SRID.ParserBamlEvent, bamlRoutedEventRecord.Value);
}
break;
case BamlRecordType.Text:
case BamlRecordType.TextWithId:
case BamlRecordType.TextWithConverter:
ReadTextRecord((BamlTextRecord)bamlRecord);
break;
case BamlRecordType.DeferableContentStart:
ReadDeferableContentStart((BamlDeferableContentStartRecord)bamlRecord);
break;
case BamlRecordType.KeyElementStart:
ReadKeyElementStartRecord((BamlKeyElementStartRecord)bamlRecord);
break;
case BamlRecordType.KeyElementEnd:
ReadKeyElementEndRecord();
break;
case BamlRecordType.ConstructorParametersStart:
ReadConstructorParametersStartRecord();
break;
case BamlRecordType.ConstructorParametersEnd:
ReadConstructorParametersEndRecord();
break;
case BamlRecordType.ConstructorParameterType:
ReadConstructorParameterTypeRecord((BamlConstructorParameterTypeRecord)bamlRecord);
break;
case BamlRecordType.ContentProperty:
ReadContentPropertyRecord((BamlContentPropertyRecord)bamlRecord);
break;
case BamlRecordType.StaticResourceId:
ReadStaticResourceIdRecord((BamlStaticResourceIdRecord)bamlRecord);
break;
case BamlRecordType.PropertyWithStaticResourceId:
ReadPropertyWithStaticResourceIdRecord((BamlPropertyWithStaticResourceIdRecord)bamlRecord);
break;
case BamlRecordType.LineNumberAndPosition: // Should be skipped in ReadNextRecordWithDebugExtension.
case BamlRecordType.LinePosition: // Should be skipped in ReadNextRecordWithDebugExtension.
default:
ThrowException(SRID.ParserUnknownBaml, ((int)bamlRecord.RecordType).ToString(CultureInfo.CurrentCulture));
break;
}
#if !STRESS
}
catch (Exception e)
{
// Don't wrap critical exceptions or already-wrapped exceptions.
if (CriticalExceptions.IsCriticalException(e) || e is XamlParseException )
{
throw;
}
XamlParseException.ThrowException( ParserContext,
LineNumber,
LinePosition,
String.Empty /*message*/,
e );
}
#endif
return moreData;
}
// Read an XML namespace definition record. This contains a mapping from
// an xml namespace to a prefix used within the scope of the current element.
protected virtual void ReadXmlnsPropertyRecord(BamlXmlnsPropertyRecord xmlnsRecord)
{
Debug.Assert(ReaderFlags.DependencyObject == CurrentContext.ContextType ||
ReaderFlags.ClrObject == CurrentContext.ContextType ||
ReaderFlags.PropertyComplexClr == CurrentContext.ContextType ||
ReaderFlags.PropertyComplexDP == CurrentContext.ContextType);
// Accept name space directives for DependencyObjects and clr objects only
if (ReaderFlags.DependencyObject == CurrentContext.ContextType ||
ReaderFlags.ClrObject == CurrentContext.ContextType ||
ReaderFlags.PropertyComplexClr == CurrentContext.ContextType ||
ReaderFlags.PropertyComplexDP == CurrentContext.ContextType)
{
XmlnsDictionary[xmlnsRecord.Prefix] = xmlnsRecord.XmlNamespace;
XamlTypeMapper.SetUriToAssemblyNameMapping(xmlnsRecord.XmlNamespace, xmlnsRecord.AssemblyIds);
// Making this call will populate XamlTypeMapper.NamespaceMapHashList
// with appropriate values for this namespace URI.
// We do this here because we need to save this data now to make it
// available later for XamlTypeMapper.GetTypeFromName() which gets it
// as value of XmlAttributeProperties.XmlNamespaceMapsProperty.
// XamlTypeMapper.GetNamespaceMapEntries(xmlnsRecord);
if (ReaderFlags.DependencyObject == CurrentContext.ContextType)
{
SetXmlnsOnCurrentObject(xmlnsRecord);
}
}
}
// Determine the element type for a baml start record, and either create a new
// element or mark it for delay creation later. Update the ReaderFlags to
// indicate the type of element created.
private void GetElementAndFlags(
BamlElementStartRecord bamlElementStartRecord,
out object element,
out ReaderFlags flags,
out Type delayCreatedType,
out short delayCreatedTypeId)
{
short typeId = bamlElementStartRecord.TypeId;
Type elementType = MapTable.GetTypeFromId(typeId);
element = null;
delayCreatedType = null;
delayCreatedTypeId = 0;
flags = ReaderFlags.Unknown;
if (null != elementType)
{
if (bamlElementStartRecord.CreateUsingTypeConverter ||
typeof(MarkupExtension).IsAssignableFrom(elementType))
{
// CreateUsingTypeConverter means we've decided
// (in XamlRecordReader) that we do not wish to
// create an instance at this time, go to the delay-
// creation state.
// MarkupExtensions will be created later after we have
// parsed the constructor parameters.
delayCreatedType = elementType;
delayCreatedTypeId = typeId;
}
else
{
// Create an instance of the object specified by the
// BeginElement, throw if the creation fails.
element = CreateInstanceFromType(elementType, typeId, false);
if (element == null)
{
ThrowException(SRID.ParserNoElementCreate2, elementType.FullName);
}
}
flags = GetFlagsFromType(elementType);
}
}
// Sets the ReaderFlags based on whether the elementType passed in is one of the special
// collection types and whether the type is a DependencyObject or not.
protected ReaderFlags GetFlagsFromType(Type elementType)
{
ReaderFlags flags = (typeof(DependencyObject).IsAssignableFrom(elementType) ? ReaderFlags.DependencyObject :
ReaderFlags.ClrObject);
if (typeof(IDictionary).IsAssignableFrom(elementType))
{
flags |= ReaderFlags.IDictionary;
}
else if (typeof(IList).IsAssignableFrom(elementType))
{
flags |= ReaderFlags.IList;
}
else if (typeof(ArrayExtension).IsAssignableFrom(elementType))
{
flags |= ReaderFlags.ArrayExt;
}
else if (BamlRecordManager.TreatAsIAddChild(elementType))
{
flags |= ReaderFlags.IAddChild;
}
return flags;
}
// Modify the passed flags to set the NeedToAddToTree flag based on the current
// context. This is needed by subparsers in some cases.
internal static void CheckForTreeAdd(ref ReaderFlags flags, ReaderContextStackData context)
{
// An element doesn't need to be added to a tree if in the context of constructor
// paramters or deferred content
if (context == null ||
(context.ContextType != ReaderFlags.ConstructorParams &&
context.ContextType != ReaderFlags.RealizeDeferContent))
{
flags |= ReaderFlags.NeedToAddToTree;
}
}
//+-----------------------------------------------------------------------------------------------------------------------
//
// SetDependencyValue
//
// We call this routine to set a DP value onto a DO, but it's virtual so that custom baml record readers
// can do their own thing. This was added so that templates could set unshareable template child property
// values into per-FE state.
//
//+-----------------------------------------------------------------------------------------------------------------------
internal void SetDependencyValue(DependencyObject dependencyObject, DependencyProperty dependencyProperty, object value)
{
// We don't need to get the metadata if we aren't skipping journaled properties
FrameworkPropertyMetadata metadata = ParserContext != null && ParserContext.SkipJournaledProperties ?
dependencyProperty.GetMetadata(dependencyObject.DependencyObjectType) as FrameworkPropertyMetadata
: null;
// If the metadata is not null here, we are skipping journaled properties (if the metadata requires it)
// NOTE: we do not journal expression. So even when the property is journalable but the value is expression,
// we still want to set the value from parser. See corresponding code for avoiding saving expression in DataStream.SaveSubStreams.
// Please see Windows OS bug # 1852349 for details.
if ((metadata == null) || (!metadata.Journal) || (value is Expression))
{
SetDependencyValueCore(dependencyObject, dependencyProperty, value);
}
}
internal virtual void SetDependencyValueCore(DependencyObject dependencyObject, DependencyProperty dependencyProperty, object value)
{
// By default, we just set the DP on the DO.
dependencyObject.SetValue(dependencyProperty, value);
}
//+-----------------------------------------------------------------------------------------------------------------------
//
// ProvideValueFromMarkupExtension
//
// Given a MarkupExtension, call its ProvideValue, passing it the right IServiceProvider.
//
//+------------------------------------------------------------------------------------------------------------------------
object ProvideValueFromMarkupExtension(MarkupExtension markupExtension, object obj, object member)
{
object returnValue = null;
// Get the IServiceProvider
ProvideValueServiceProvider serviceProvider = ParserContext.ProvideValueProvider;
// Let it know the target object & property
serviceProvider.SetData(obj, member);
try
{
returnValue = markupExtension.ProvideValue(serviceProvider);
if( TraceMarkup.IsEnabled )
{
TraceMarkup.TraceActivityItem(
TraceMarkup.ProvideValue,
markupExtension,
obj,
member,
returnValue );
}
}
finally
{
serviceProvider.ClearData();
}
return returnValue;
}
// Read the start of an element. This involves creating a new object, and storing it
// for later addition to the tree or setting as a property. The base method does
// not check for a serializer.
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
internal void BaseReadElementStartRecord(
BamlElementStartRecord bamlElementRecord)
{
object element = null;
Type delayCreatedType = null;
short delayCreatedTypeId = 0;
ReaderFlags flags = ReaderFlags.Unknown;
ReaderContextStackData currentContext = CurrentContext;
if (_bamlAsForest && currentContext == null)
{
Debug.Assert(_rootElement != null);
element = _rootElement;
flags = GetFlagsFromType(element.GetType());
}
else
{
if (null != currentContext &&
(ReaderFlags.PropertyComplexClr == currentContext.ContextType ||
ReaderFlags.PropertyComplexDP == currentContext.ContextType) &&
null == currentContext.ExpectedType)
{
string propName = GetPropNameFrom(currentContext.ObjectData);
ThrowException(SRID.ParserNoComplexMulti, propName);
}
// If this is the very top element as indicated by there not being a
// parent context, then we have to add this element to the rootlist
// by calling SetPropertyValueToParent. For all other cases we don't want to
// call this here since we may be building a subtree bottom up and want
// to defer addition of elements. This fixes bug 943189 - Async parsing broken
if (null == ParentContext)
{
SetPropertyValueToParent(true);
}
// Get an instance of the element, if it is to be created now. Also set
// the flags to indicate what the element is and how to treat it.
GetElementAndFlags(bamlElementRecord, out element, out flags,
out delayCreatedType, out delayCreatedTypeId);
}
Stream bamlStream = BamlStream;
if (!_bamlAsForest &&
currentContext == null &&
element != null &&
bamlStream != null &&
!(bamlStream is ReaderStream) &&
StreamPosition == StreamLength)
{
// We are here because the root element was loaded from this baml stream
// and not passed to us as is the case with the app loader\navigation engine,
// and we are already at the end of the stream while processing this start
// element record which would be the case when we have an element that has already
// been instantiated by an inner LoadBaml call for that element with the same
// Uri\ResourceLocator. So in this case we need to simulate a DocumentEnd to
// cleanup properly and add to the RootList so that the caller of the outer
// LoadBaml can access the element created by the inner LoadBaml.
Debug.Assert(null == ParentContext);
ReadDocumentEndRecord();
if (RootList.Count == 0)
{
RootList.Add(element);
}
// Set a flag to prevent the TreeBuilder from clobbering the
// XamlTypeMapper's xmlns hashtable on the root element as this
// would have already been set by the inner Baml loading Context.
IsRootAlreadyLoaded = true;
}
else
{
// If we have a real element at this point, instead of some object that will be
// delay created later, check for various interfaces that are called upon
// element creation.
if (element != null)
{
// If this is an element start record that carries its name also, then register the name now also.
string elementName = null;
if (bamlElementRecord is BamlNamedElementStartRecord)
{
BamlNamedElementStartRecord bamlNamedElementStartRecord = bamlElementRecord as BamlNamedElementStartRecord;
elementName = bamlNamedElementStartRecord.RuntimeName;
}
ElementInitialize(element, elementName);
}
// Remember the object that was just created. It will be added when
// the end tag is reached.
CheckForTreeAdd(ref flags, currentContext);
PushContext(flags, element, delayCreatedType, delayCreatedTypeId, bamlElementRecord.CreateUsingTypeConverter);
// Add just constructed element to the tree if it is a UIElement. This
// builds the tree in a top-down fashion for objects that make up the majority
// of the logical tree. Other objects, such as Freezables, are added bottom-up
// to aid in having all properties set prior to adding them to the tree.
// See PS workitem #19080
if (BuildTopDown &&
element != null &&
((element is UIElement) ||
(element is ContentElement) ||
(element is UIElement3D)))
{
SetPropertyValueToParent(true);
}
else if (CurrentContext.CheckFlag(ReaderFlags.IDictionary))
{
// AddElement to Tree checks this, but if that wasn't called, then we need
// to check for an explicit tag after a dictionary property.
bool isMarkupExtension = false;
if (CheckExplicitCollectionTag(ref isMarkupExtension))
{
// if the tag is an explicit element under a collection property, we're done
CurrentContext.MarkAddedToTree();
// ResourceDictionary objects must be loaded top-down
if (element is ResourceDictionary)
{
SetCollectionPropertyValue(ParentContext);
}
}
}
}
}
// Read the start of an element. This involves creating a new object, and storing it
// for later addition to the tree or setting as a property.
// Return true if a serializer has been spun off to parse the next element, or
// false if it was handled by the BamlRecordReader itself. If a serializer has
// been spun off, then the end record that matches this start record will NOT
// get to this instance of the BamlRecordReader.
//[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
protected virtual bool ReadElementStartRecord(BamlElementStartRecord bamlElementRecord)
{
// Check if the type of this element has a serializer associated with it. If so then
// we have to create a serializer and pass off processing to it. Otherwise, continue
// with default processing.
bool hasSerializer = MapTable.HasSerializerForTypeId(bamlElementRecord.TypeId);
if (hasSerializer)
{
bool tracingEnabled = EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose);
if (tracingEnabled)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.PARSEREADERCREATEINSTANCEGUID), MS.Utility.EventType.StartEvent);
}
BamlTypeInfoRecord typeInfo = MapTable.GetTypeInfoFromId(bamlElementRecord.TypeId);
XamlSerializer serializer = CreateSerializer((BamlTypeInfoWithSerializerRecord)typeInfo);
if (ParserContext.RootElement == null)
{
ParserContext.RootElement = _rootElement;
}
if (ParserContext.StyleConnector == null)
{
ParserContext.StyleConnector = _rootElement as IStyleConnector;
}
// PreParsedIndex is updated by Serializer after its TreeBuilder.Parse.
serializer.ConvertBamlToObject(this, bamlElementRecord, ParserContext);
if (tracingEnabled)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.PARSEREADERCREATEINSTANCEGUID), MS.Utility.EventType.EndEvent);
}
return true;
}
BaseReadElementStartRecord(bamlElementRecord);
return false;
}
// The end of an element has been reached. What to do depends on the current
// context, since the object may be added as a child to another object, placed in
// in a dictionary, added to a list, set as a property, etc...
protected internal virtual void ReadElementEndRecord(bool fromNestedBamlRecordReader)
{
if (null == CurrentContext ||
(ReaderFlags.DependencyObject != CurrentContext.ContextType &&
ReaderFlags.ClrObject != CurrentContext.ContextType))
{
ThrowException(SRID.ParserUnexpectedEndEle);
}
object currentElement = GetCurrentObjectData();
// Make sure we have called EndInit on the currentElement before it is set
// as a property value on the parent. This is because if we encounter an
// exception during the call to EndInit we want to be able to use the
// Fallback provided by the parent for the given property. So in order to
// set the right property value on the parent we should have called EndInit
// prior to the call to SetPropertyValueToParent. Example
//
//
//
//
//
//
//
// Calling EndInit on BitmapImage throws and exception and then we need to
// use the Fallback provided by Image for the Source property.
ElementEndInit(ref currentElement);
// Check if the element on the stack needs to be added to the tree as a child
// If not, then continue and see if the element should be added to a list, dictionary,
// array, set as a property value, etc.
SetPropertyValueToParent(false /*fromStartTag*/);
Debug.Assert(!CurrentContext.CheckFlag(ReaderFlags.NeedToAddToTree), "Failed to add Element to tree before popping stack");
ReaderFlags flags = CurrentContext.ContextFlags;
FreezeIfRequired(currentElement);
// pop the stack
PopContext();
// Deferred content and constructor parameters aren't handled in SetPropertyValueToParent
if ((flags & (ReaderFlags.AddedToTree)) == 0 &&
CurrentContext != null)
{
Debug.Assert( CurrentContext.ContextType != ReaderFlags.PropertyComplexClr );
Debug.Assert( CurrentContext.ContextType != ReaderFlags.PropertyComplexDP );
switch (CurrentContext.ContextType)
{
case ReaderFlags.RealizeDeferContent:
// Pass the object up
CurrentContext.ObjectData = currentElement;
break;
case ReaderFlags.ConstructorParams:
SetConstructorParameter(currentElement);
break;
}
}
}
// Read the start of an element that is the first
// object in a key in a resource dictionary.
internal virtual void ReadKeyElementStartRecord(
BamlKeyElementStartRecord bamlElementRecord)
{
Type elementType = MapTable.GetTypeFromId(bamlElementRecord.TypeId);
ReaderFlags flags = (elementType.IsAssignableFrom(typeof(DependencyObject)) ?
ReaderFlags.DependencyObject :
ReaderFlags.ClrObject) |
ReaderFlags.NeedToAddToTree;
// Remember the object that was just created. It will be used when
// the end tag is reached and the key is complete
PushContext(flags, null, elementType, bamlElementRecord.TypeId);
}
// The end of an element that represents a key in an IDictionary has
// been reached. Search for the dictionary holder in the reader stack
// and set the key value to the current object on the top of the
// reader stack.
internal virtual void ReadKeyElementEndRecord()
{
object key = ProvideValueFromMarkupExtension((MarkupExtension)GetCurrentObjectData(),
ParentObjectData, null /*member*/);
SetKeyOnContext(key, XamlReaderHelper.DefinitionName, ParentContext, GrandParentContext);
PopContext();
}
// The constructor parameter with a single Type object has been read in. Get the
// type associated with this record and add it to the constructor parameter list.
internal virtual void ReadConstructorParameterTypeRecord(
BamlConstructorParameterTypeRecord constructorParameterType)
{
Debug.Assert(CurrentContext.ContextType == ReaderFlags.ConstructorParams);
SetConstructorParameter(MapTable.GetTypeFromId(constructorParameterType.TypeId));
}
// Read the content property record and set the ContentProperty in the context.
internal virtual void ReadContentPropertyRecord(
BamlContentPropertyRecord bamlContentPropertyRecord)
{
object contentProperty = null;
short attributeId = bamlContentPropertyRecord.AttributeId;
// Try KnownTypes Optimization: When the property is known use generated code for accessing it
object parent = GetCurrentObjectData();
if (parent != null)
{
short elementId = BamlMapTable.GetKnownTypeIdFromType(parent.GetType());
if (elementId < 0)
{
contentProperty = KnownTypes.GetCollectionForCPA(parent, (KnownElements)(-elementId));
}
}
if (contentProperty == null)
{
PropertyDefinition propertyDefinition = new PropertyDefinition(this, attributeId, parent is DependencyObject);
// Try DependencyProperty Optimization: When a DP exists for the property, use it for accessing the property
if (propertyDefinition.DependencyProperty != null)
{
Debug.Assert(parent is DependencyObject);
if (typeof(IList).IsAssignableFrom(propertyDefinition.PropertyType))
{
contentProperty = ((DependencyObject)parent).GetValue(propertyDefinition.DependencyProperty) as IList;
// We assume that the contentProperty will become a collection now.
// However, in case when the DP is implemented incorrectly it may return null even if the clr property has non-null value.
// So leaving contentProperty==null will drive us to general clr case below, which must do better job.
}
else
{
// When the property is not a IList store a DP itself as a contentProperty for assigning a simple value
contentProperty = propertyDefinition.DependencyProperty;
}
}
if (contentProperty == null)
{
// We consider only PropertyInfo case here.
// We do not consider AttachedPropertySetter in this case because content property cannot be attached.
if (propertyDefinition.PropertyInfo != null)
{
// Try to treat the content property as IList
if (propertyDefinition.IsInternal)
{
contentProperty = XamlTypeMapper.GetInternalPropertyValue(ParserContext,
ParserContext.RootElement,
propertyDefinition.PropertyInfo,
parent) as IList;
// if Content Property does not support IList, then see if it is
// accessible\allowed as a regular setter.
//
if (contentProperty == null)
{
bool isPublicProperty;
bool allowProtected =
(ParserContext.RootElement is IComponentConnector) &&
(ParserContext.RootElement == parent);
if (!XamlTypeMapper.IsAllowedPropertySet(propertyDefinition.PropertyInfo, allowProtected, out isPublicProperty))
{
ThrowException(SRID.ParserCantSetContentProperty, propertyDefinition.Name, propertyDefinition.PropertyInfo.ReflectedType.Name);
}
}
}
else
{
contentProperty = propertyDefinition.PropertyInfo.GetValue(
parent,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy,
null, null, XamlReaderHelper.EnglishUSCulture)
as IList;
}
if (contentProperty == null)
{
// The property returned null, try setting it directly.
contentProperty = propertyDefinition.PropertyInfo;
}
}
}
}
if (contentProperty == null)
{
ThrowException(SRID.ParserCantGetDPOrPi, GetPropertyNameFromAttributeId(attributeId));
}
CurrentContext.ContentProperty = contentProperty;
}
// The Start of the constructor parameter section has been reached. Prepare to
// store all the top level objects that follow up to the ConstructorParametersEnd
// record is reached.
internal virtual void ReadConstructorParametersStartRecord()
{
PushContext(ReaderFlags.ConstructorParams, null, null, 0);
}
// The end of the constructor parameter section has been reached. Create an
// instance of the object after finding the appropriate constructor and converting
// all of the objects held on the stack.
internal virtual void ReadConstructorParametersEndRecord()
{
Type elementType = ParentContext.ExpectedType;
short positiveElementTypeId = (short)-ParentContext.ExpectedTypeId;
object param = null;
ArrayList paramList = null;
int paramCount;
object instance = null;
bool foundInstance = false;
if( TraceMarkup.IsEnabled )
{
TraceMarkup.Trace( TraceEventType.Start,
TraceMarkup.CreateMarkupExtension,
elementType );
}
if (CurrentContext.CheckFlag(ReaderFlags.SingletonConstructorParam))
{
param = CurrentContext.ObjectData;
paramCount = 1;
// Fast code path for [static/dynamic] resource extensions &
// Type/Static/TemplateBinding extensions
switch (positiveElementTypeId)
{
case (short)KnownElements.TypeExtension:
// Note that this assumes that TypeExtension has a
// constructor with one param of type Type or String.
Type t = param as Type;
if (t != null)
{
instance = new TypeExtension(t);
}
else
{
Debug.Assert(param is String);
instance = new TypeExtension((String)param);
}
foundInstance = true;
break;
case (short)KnownElements.StaticResourceExtension:
// Note that this assumes that StaticResourceExtension has a
// constructor with one param of type object.
instance = new StaticResourceExtension(param);
foundInstance = true;
break;
case (short)KnownElements.DynamicResourceExtension:
// Note that this assumes that DynamicResourceExtension has a
// constructor with one param of type object.
instance = new DynamicResourceExtension(param);
foundInstance = true;
break;
case (short)KnownElements.StaticExtension:
// Note that this assumes that StaticExtension has a default
// constructor and one public property of type string and one
// internal property of type object for optimized member info.
instance = new StaticExtension((string)param);
foundInstance = true;
break;
case (short)KnownElements.TemplateBindingExtension:
// Note that this assumes that TemplateBindingExtension has a
// constructor with one param of type DependencyProperty. If a
// string is passed in due to there being other attributes like
// converter being set, then that needs to be converted now first.
DependencyProperty dp = param as DependencyProperty;
if (dp == null)
{
string paramString = param as string;
Type ownerType = ParserContext.TargetType;
Debug.Assert(paramString != null);
dp = XamlTypeMapper.ParsePropertyName(ParserContext,
paramString.Trim(),
ref ownerType);
if (dp == null)
{
ThrowException(SRID.ParserNoDPOnOwner, paramString, ownerType.FullName);
}
}
instance = new TemplateBindingExtension(dp);
foundInstance = true;
break;
}
}
else
{
paramList = (ArrayList)CurrentContext.ObjectData;
paramCount = paramList.Count;
}
if (!foundInstance)
{
// Find the constructor based on the number of parameters stored in paramList
XamlTypeMapper.ConstructorData data = XamlTypeMapper.GetConstructors(elementType);
ConstructorInfo[] infos = data.Constructors;
for (int i=0; i staticResourceValuesList;
BaseReadDeferableContentStart(bamlRecord, out defKeyList, out staticResourceValuesList);
// If we don't own the stream, someone might close it when the parse is complete,
// so copy the defer load contents into a buffer. If we own the
// stream, then we'll assume the owner will keep it open.
long endOfKeysPosition = stream.Position;
Int32 valuesSize = (Int32)(bamlRecord.ContentSize - endOfKeysPosition + startPosition);
if (!ParserContext.OwnsBamlStream)
{
byte[] buffer = new byte[valuesSize];
if (valuesSize > 0)
{
MS.Internal.IO.Packaging.PackagingUtilities.ReliableRead(
BinaryReader, buffer, 0, valuesSize);
}
dictionary.SetDeferableContent(buffer,
ParserContext, _rootElement, defKeyList, staticResourceValuesList);
}
else
{
dictionary.SetDeferableContent(_bamlStream,
endOfKeysPosition, valuesSize,
ParserContext, _rootElement, defKeyList, staticResourceValuesList);
_bamlStream.Seek(valuesSize, SeekOrigin.Current);
}
}
}
internal void BaseReadDeferableContentStart(
BamlDeferableContentStartRecord bamlRecord,
out ArrayList defKeyList,
out List