|
// 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.Reflection;
using System.Xaml;
using System.Xaml.Schema;
using MS.Internal.Xaml.Runtime;
using XAML3 = System.Windows.Markup;
namespace MS.Internal.Xaml.Context
{
internal class ObjectWriterContext : XamlContext
{
private XamlContextStack<ObjectWriterFrame> _stack;
private object _rootInstance;
ServiceProviderContext _serviceProviderContext;
XamlRuntime _runtime;
int _savedDepth; // The depth of the "saved" part this context is based on.
bool _nameResolutionComplete;
XamlObjectWriterSettings _settings;
List<NameScopeInitializationCompleteSubscriber> _nameScopeInitializationCompleteSubscribers;
public ObjectWriterContext(XamlSavedContext savedContext,
XamlObjectWriterSettings settings, XAML3.INameScope rootNameScope, XamlRuntime runtime)
: base(savedContext.SchemaContext)
{
_stack = new XamlContextStack<ObjectWriterFrame>(savedContext.Stack, false);
if (settings is not null)
{
_settings = settings.StripDelegates();
}
_runtime = runtime;
BaseUri = savedContext.BaseUri;
// If the bottom of the stack is a (no XamlType) Value (reparse) then back-up onto it.
// Otherwise add a blank frame to isolate template use from the saved context.
switch(savedContext.SaveContextType)
{
case SavedContextType.Template:
// Templates always need a root namescope, to isolate them from the rest of the doc
XAML3.INameScopeDictionary rootNameScopeDictionary = null;
if (rootNameScope is null)
{
rootNameScopeDictionary = new NameScope();
}
else
{
rootNameScopeDictionary = rootNameScope as XAML3.INameScopeDictionary;
if (rootNameScopeDictionary is null)
{
rootNameScopeDictionary = new NameScopeDictionary(rootNameScope);
}
}
// Push an extra frame to ensure that the template NameScope is
// not part of the saved context. Otherwise, the namescope
// will hold things alive as long as the template is alive
_stack.PushScope();
_savedDepth = _stack.Depth;
_stack.CurrentFrame.NameScopeDictionary = rootNameScopeDictionary;
_stack.PushScope();
break;
case SavedContextType.ReparseValue:
case SavedContextType.ReparseMarkupExtension:
Debug.Assert(rootNameScope is null, "Cannot pass a new namescope in to a reparse context");
_savedDepth = _stack.Depth - 1;
break;
}
}
public ObjectWriterContext(XamlSchemaContext schemaContext,
XamlObjectWriterSettings settings, XAML3.INameScope rootNameScope, XamlRuntime runtime)
: base(schemaContext)
{
_stack = new XamlContextStack<ObjectWriterFrame>(() => new ObjectWriterFrame());
XAML3.INameScopeDictionary rootNameScopeDictionary = null;
if (rootNameScope is null)
{
rootNameScopeDictionary = new NameScope();
}
else
{
rootNameScopeDictionary = rootNameScope as XAML3.INameScopeDictionary;
if (rootNameScopeDictionary is null)
{
rootNameScopeDictionary = new NameScopeDictionary(rootNameScope);
}
}
_stack.CurrentFrame.NameScopeDictionary = rootNameScopeDictionary;
_stack.PushScope(); // put a blank sentinal frame on the stack.
if (settings is not null)
{
_settings = settings.StripDelegates();
}
_runtime = runtime;
_savedDepth = 0;
}
public override Assembly LocalAssembly
{
get
{
Assembly result = base.LocalAssembly;
if (result is null && _settings is not null && _settings.AccessLevel is not null)
{
result = Assembly.Load(_settings.AccessLevel.AssemblyAccessToAssemblyName);
base.LocalAssembly = result;
}
return result;
}
protected set { base.LocalAssembly = value; }
}
internal ICheckIfInitialized IsInitializedCallback { get; set; }
internal bool NameResolutionComplete
{
get { return _nameResolutionComplete; }
set
{
// Resolution should never become incomplete once it's complete
Debug.Assert(!_nameResolutionComplete || value);
_nameResolutionComplete = value;
}
}
internal XamlRuntime Runtime
{
get
{
return _runtime;
}
}
#region ServiceProvider Interfaces
// This class doesn't implement the IServiceProvider. That is done
// with worker classes ValueConverterContext or MarkupConverterContext.
// The worker class implements IServiceProvider but uses the real
// context for the implementation of the actual services.
internal Type ServiceProvider_Resolve(string qName)
{
// As soon as we have the necessary setting on ObjectWriter, we need to start passing
// the local assembly into the context; currently, this will only return publics.
XamlType xamlType = ServiceProvider_ResolveXamlType(qName);
if (xamlType is null || xamlType.UnderlyingType is null)
{
XamlTypeName name = XamlTypeName.Parse(qName, _serviceProviderContext);
xamlType = GetXamlType(name, true, true);
throw new XamlParseException(SR.Format(SR.TypeNotFound, xamlType.GetQualifiedName()));
}
return xamlType.UnderlyingType;
}
internal XamlType ServiceProvider_ResolveXamlType(string qName)
{
return ResolveXamlType(qName, true);
}
internal AmbientPropertyValue ServiceProvider_GetFirstAmbientValue(IEnumerable<XamlType> ceilingTypes, XamlMember[] properties)
{
List<AmbientPropertyValue> valueList = FindAmbientValues(ceilingTypes, /*searchLiveStackOnly*/false, /*types*/null, properties, true);
return (valueList.Count == 0) ? null : valueList[0];
}
internal object ServiceProvider_GetFirstAmbientValue(XamlType[] types)
{
List<object> valueList = FindAmbientValues(types, true);
return (valueList.Count == 0) ? null : valueList[0];
}
internal IEnumerable<AmbientPropertyValue> ServiceProvider_GetAllAmbientValues(IEnumerable<XamlType> ceilingTypes, XamlMember[] properties)
{
List<AmbientPropertyValue> valueList = FindAmbientValues(ceilingTypes, /*searchLiveStackOnly*/false, /*types*/null, properties, /*stopAfterFirst*/ false);
return valueList;
}
internal IEnumerable<object> ServiceProvider_GetAllAmbientValues(XamlType[] types)
{
List<object> valueList = FindAmbientValues(types, false);
return valueList;
}
internal IEnumerable<AmbientPropertyValue> ServiceProvider_GetAllAmbientValues(IEnumerable<XamlType> ceilingTypes, bool searchLiveStackOnly, IEnumerable<XamlType> types, XamlMember[] properties)
{
List<AmbientPropertyValue> valueList = FindAmbientValues(ceilingTypes, searchLiveStackOnly, types, properties, false);
return valueList;
}
internal XamlObjectWriterSettings ServiceProvider_GetSettings()
{
if (_settings is null)
{
_settings = new XamlObjectWriterSettings();
}
return _settings;
}
#endregion
// ----- abstracts overriden from XamlContext.
public override void AddNamespacePrefix(String prefix, string xamlNS)
{
_stack.CurrentFrame.AddNamespace(prefix, xamlNS);
}
public override string FindNamespaceByPrefix(string prefix)
{
string xamlNs;
ObjectWriterFrame frame = _stack.CurrentFrame;
while (frame.Depth > 0)
{
if (frame.TryGetNamespaceByPrefix(prefix, out xamlNs))
{
return xamlNs;
}
frame = (ObjectWriterFrame)frame.Previous;
}
return null;
}
public override IEnumerable<NamespaceDeclaration> GetNamespacePrefixes()
{
ObjectWriterFrame frame = _stack.CurrentFrame;
HashSet<string> keys = new HashSet<string>();
while (frame.Depth > 0)
{
if (frame._namespaces is not null)
{
foreach (NamespaceDeclaration namespaceDeclaration in frame.GetNamespacePrefixes())
{
if (keys.Add(namespaceDeclaration.Prefix))
{
yield return namespaceDeclaration;
}
}
}
frame = (ObjectWriterFrame)frame.Previous;
}
}
// Method to easily add the lineinfo to a Xaml Exception.
public XamlException WithLineInfo(XamlException ex)
{
ex.SetLineInfo(LineNumber, LinePosition);
return ex;
}
// ----- methods to support the Service Providers
internal ServiceProviderContext ServiceProviderContext
{
get
{
if (_serviceProviderContext is null)
{
_serviceProviderContext = new ServiceProviderContext(this);
}
return _serviceProviderContext;
}
}
internal XamlType GetDestinationType()
{
ObjectWriterFrame frame = _stack.CurrentFrame;
if (frame is null)
{
return null;
}
if (frame.Instance is not null && frame.XamlType is null)
{
//
// Text/TypeConverter, we need to go up a frame
frame = frame.Previous as ObjectWriterFrame;
}
if (frame.Member == XamlLanguage.Initialization)
{
return frame.XamlType;
}
return frame.Member.Type;
}
private List<AmbientPropertyValue> FindAmbientValues(IEnumerable<XamlType> ceilingTypesEnumerable,
bool searchLiveStackOnly,
IEnumerable<XamlType> types,
XamlMember[] properties,
bool stopAfterFirst)
{
foreach (XamlMember xamlMember in properties)
{
if (!xamlMember.IsAmbient)
{
throw new ArgumentException(SR.Format(SR.NotAmbientProperty, xamlMember.DeclaringType.Name, xamlMember.Name), nameof(properties));
}
}
List<XamlType> ceilingTypes = ceilingTypesEnumerable is not null ? new List<XamlType>(ceilingTypesEnumerable) : null;
List<AmbientPropertyValue> retList = new List<AmbientPropertyValue>();
// Start the search for ambient properties and types starting with the parent frame.
ObjectWriterFrame frame = _stack.PreviousFrame;
ObjectWriterFrame lowerFrame = _stack.CurrentFrame;
while (frame.Depth >= 1)
{
if (searchLiveStackOnly && frame.Depth <= SavedDepth)
{
break;
}
object inst = frame.Instance;
if (types is not null)
{
foreach (XamlType type in types)
{
if (frame.XamlType is not null && frame.XamlType.CanAssignTo(type))
{
if (inst is not null)
{
AmbientPropertyValue apValue = new AmbientPropertyValue(null, inst);
retList.Add(apValue);
}
}
}
}
if (properties is not null)
{
foreach (XamlMember prop in properties)
{
bool returnAmbientValue = false;
object value = null;
if (frame.XamlType is not null && frame.XamlType.CanAssignTo(prop.DeclaringType))
{
if (inst is not null)
{
// If we are searching from inside the target Ambient property,
// (like StaticResource inside a ResourceDictionary)
// and the object is bottom-up, then it won't be assigned to
// the object but will only exist on the parse stack.
// If it is top-down it will be attached to the instance already
// and the normal path will serve.
if (prop == frame.Member && lowerFrame.Instance is not null
&& lowerFrame.XamlType is not null && !lowerFrame.XamlType.IsUsableDuringInitialization)
{
// One last thing to check: If the object we are inside is a ME
// then we are inside a call to ProvideValue and we don't want to
// return a reference to ourselves to ourselves.
if (!(lowerFrame.Instance is XAML3.MarkupExtension))
{
returnAmbientValue = true;
value = lowerFrame.Instance;
}
}
else
{ // The Ambient Property is either Fully build or not set.
// FIRST: Ask the object (via IQueryAmbient interface) if it has a value for this property.
// This is usefull to prevent needless creation of empty lazy properties.
var ambientCtrl = inst as XAML3.IQueryAmbient;
// If there is no ambientControl or if ambientControl says YES, then get the property value.
if (ambientCtrl is null || ambientCtrl.IsAmbientPropertyAvailable(prop.Name))
{
returnAmbientValue = true;
value = _runtime.GetValue(inst, prop);
}
}
}
if (returnAmbientValue)
{
AmbientPropertyValue apValue = new AmbientPropertyValue(prop, value);
retList.Add(apValue);
}
}
}
}
if (stopAfterFirst && retList.Count > 0)
{
break;
}
if (ceilingTypes is not null)
{
if (ceilingTypes.Contains(frame.XamlType))
{
break;
}
}
lowerFrame = frame;
frame = (ObjectWriterFrame)frame.Previous;
Debug.Assert(frame is not null);
}
return retList;
}
private List<object> FindAmbientValues(XamlType[] types, bool stopAfterFirst)
{
foreach (XamlType xamlType in types)
{
if (!xamlType.IsAmbient)
{
throw new ArgumentException(SR.Format(SR.NotAmbientType, xamlType.Name), nameof(types));
}
}
List<object> retList = new List<object>();
// Start the search for ambient properties with the parent frame.
ObjectWriterFrame frame = _stack.PreviousFrame;
while (frame.Depth >= 1)
{
foreach (XamlType type in types)
{
object inst = frame.Instance;
if (frame.XamlType is not null && frame.XamlType.CanAssignTo(type))
{
if (inst is not null)
{
retList.Add(inst);
if (stopAfterFirst)
{
return retList;
}
}
}
}
frame = (ObjectWriterFrame)frame.Previous;
Debug.Assert(frame is not null);
}
return retList;
}
// ----- new public methods.
public void PushScope()
{
_stack.PushScope();
}
// Don't call this - serious workaround
public void LiftScope()
{
_stack.Depth--;
}
// Don't call this - serious workaround
public void UnLiftScope()
{
_stack.Depth++;
}
public void PopScope()
{
_stack.PopScope();
}
/// <summary>
/// Total depth of the stack SavedDepth+LiveDepth
/// </summary>
public int Depth
{
get { return _stack.Depth; }
}
/// <summary>
/// The Depth of the Saved (template) part.
/// </summary>
public int SavedDepth
{
get { return _savedDepth; }
}
/// <summary>
/// The Depth of the Stack above the Saved (template) part
/// </summary>
public int LiveDepth
{
get { return Depth - SavedDepth; }
}
public XamlType CurrentType
{
get { return _stack.CurrentFrame.XamlType; }
set { _stack.CurrentFrame.XamlType = value; }
}
public XamlType ParentType
{
get { return _stack.PreviousFrame.XamlType; }
}
public XamlType GrandParentType
{
get { return (_stack.PreviousPreviousFrame is not null) ? _stack.PreviousPreviousFrame.XamlType : null; }
}
public XamlMember CurrentProperty
{
get { return _stack.CurrentFrame.Member; }
set { _stack.CurrentFrame.Member = value; }
}
public XamlMember ParentProperty
{
get { return _stack.PreviousFrame.Member; }
}
public XamlMember GrandParentProperty
{
get { return _stack.PreviousPreviousFrame.Member; }
}
public object CurrentInstance
{
get { return _stack.CurrentFrame.Instance; }
set { _stack.CurrentFrame.Instance = value; }
}
public object ParentInstance
{
get { return _stack.PreviousFrame.Instance; }
}
public object GrandParentInstance
{
get { return (_stack.PreviousPreviousFrame is not null) ? _stack.PreviousPreviousFrame.Instance : null; }
}
public object CurrentCollection
{
get { return _stack.CurrentFrame.Collection; }
set { _stack.CurrentFrame.Collection = value; }
}
public object ParentCollection
{
get { return _stack.PreviousFrame.Collection; }
}
public bool CurrentWasAssignedAtCreation
{
get { return _stack.CurrentFrame.WasAssignedAtCreation; }
set { _stack.CurrentFrame.WasAssignedAtCreation = value; }
}
public bool CurrentIsObjectFromMember
{
get { return _stack.CurrentFrame.IsObjectFromMember; }
set { _stack.CurrentFrame.IsObjectFromMember = value; }
}
public bool ParentIsObjectFromMember
{
get { return _stack.PreviousFrame.IsObjectFromMember; }
}
public bool GrandParentIsObjectFromMember
{
get { return (_stack.PreviousPreviousFrame is not null) ? _stack.PreviousPreviousFrame.IsObjectFromMember : false; }
}
public bool CurrentIsPropertyValueSet
{
// get { return _stack.CurrentFrame.IsPropertyValueSet; } Currently Unused (FxCop)
set { _stack.CurrentFrame.IsPropertyValueSet = value; }
}
public bool ParentIsPropertyValueSet
{
get { return _stack.PreviousFrame.IsPropertyValueSet; }
set { _stack.PreviousFrame.IsPropertyValueSet = value; }
}
public bool CurrentIsTypeConvertedObject
{
get { return _stack.CurrentFrame.IsTypeConvertedObject; }
set { _stack.CurrentFrame.IsTypeConvertedObject = value; }
}
public Dictionary<XamlMember, object> CurrentPreconstructionPropertyValues
{
get { return _stack.CurrentFrame.PreconstructionPropertyValues; }
}
public bool CurrentHasPreconstructionPropertyValuesDictionary
{
get { return _stack.CurrentFrame.HasPreconstructionPropertyValuesDictionary; }
}
public Dictionary<XamlMember, object> ParentPreconstructionPropertyValues
{
get { return _stack.PreviousFrame.PreconstructionPropertyValues; }
}
public HashSet<XamlMember> CurrentAssignedProperties
{
get { return _stack.CurrentFrame.AssignedProperties; }
}
public HashSet<XamlMember> ParentAssignedProperties
{
get { return _stack.PreviousFrame.AssignedProperties; }
}
public string CurrentInstanceRegisteredName
{
get { return _stack.CurrentFrame.InstanceRegisteredName; }
set { _stack.CurrentFrame.InstanceRegisteredName = value; }
}
public string ParentInstanceRegisteredName
{
get { return _stack.PreviousFrame.InstanceRegisteredName; }
set { _stack.PreviousFrame.InstanceRegisteredName = value; }
}
public Uri BaseUri { get; set; }
public int LineNumber { get; set; }
public int LinePosition { get; set; }
// Used only for BeginInitHandler, in place of BaseUri.
public Uri SourceBamlUri
{
get { return _settings is not null ? _settings.SourceBamlUri : null; }
}
// This specifically stores the start line number for a start object for consistency
public int LineNumber_StartObject { get; set; }
// This specifically stores the start line position for a start object for consistency
public int LinePosition_StartObject { get; set; }
public XAML3.INameScopeDictionary CurrentNameScope
{
get
{
return LookupNameScopeDictionary((ObjectWriterFrame)_stack.CurrentFrame);
}
}
public XAML3.INameScopeDictionary ParentNameScope
{
get
{
return LookupNameScopeDictionary((ObjectWriterFrame)_stack.PreviousFrame);
}
}
public XAML3.INameScopeDictionary GrandParentNameScope
{
get
{
return LookupNameScopeDictionary((ObjectWriterFrame)_stack.PreviousPreviousFrame);
}
}
public XAML3.INameScopeDictionary RootNameScope
{
get
{
ObjectWriterFrame rootFrame = _stack.GetFrame(SavedDepth + 1);
return LookupNameScopeDictionary(rootFrame);
}
}
/// <summary>
/// From x:Arguments or ME positional syntax.
/// </summary>
public object[] CurrentCtorArgs
{
get { return _stack.CurrentFrame.PositionalCtorArgs; }
set { _stack.CurrentFrame.PositionalCtorArgs = value; }
}
public object CurrentKey
{
get { return _stack.CurrentFrame.Key; }
}
public bool CurrentIsKeySet
{
get { return _stack.CurrentFrame.IsKeySet; }
}
public object ParentKey
{
get { return _stack.PreviousFrame.Key; }
set
{
_stack.PreviousFrame.Key = value;
}
}
public bool CurrentKeyIsUnconverted
{
get { return _stack.CurrentFrame.KeyIsUnconverted; }
set { _stack.CurrentFrame.KeyIsUnconverted = value; }
}
public bool ParentKeyIsUnconverted
{
// Write-only property: Getter was dead code, so removed per FxCop; add back if needed.
set { _stack.PreviousFrame.KeyIsUnconverted = value; }
}
public bool ParentShouldConvertChildKeys
{
get { return _stack.PreviousFrame.ShouldConvertChildKeys; }
set { _stack.PreviousPreviousFrame.ShouldConvertChildKeys = value; }
}
public bool GrandParentShouldConvertChildKeys
{
get { return _stack.PreviousPreviousFrame.ShouldConvertChildKeys; }
set { _stack.PreviousPreviousFrame.ShouldConvertChildKeys = value; }
}
public bool ParentShouldNotConvertChildKeys
{
get { return _stack.PreviousFrame.ShouldNotConvertChildKeys; }
set { _stack.PreviousPreviousFrame.ShouldNotConvertChildKeys = value; }
}
public bool GrandParentShouldNotConvertChildKeys
{
get { return _stack.PreviousPreviousFrame.ShouldNotConvertChildKeys; }
}
public object RootInstance
{
get
{
//evaluate if _rootInstance should just always look at _rootFrame.Instance instead of caching an instance
if (_rootInstance is null)
{
ObjectWriterFrame rootFrame = GetTopFrame();
_rootInstance = rootFrame.Instance;
}
return _rootInstance;
}
}
// Consider replacing GetTopFrame with _rootFrame, _liveRootFrame
private ObjectWriterFrame GetTopFrame()
{
if (_stack.Depth == 0)
{
return null;
}
XamlFrame frame = _stack.CurrentFrame;
while (frame.Depth > 1)
{
frame = frame.Previous;
}
return (ObjectWriterFrame)frame;
}
private XAML3.INameScopeDictionary LookupNameScopeDictionary(ObjectWriterFrame frame)
{
if (frame.NameScopeDictionary is null)
{
if (frame.XamlType is not null && frame.XamlType.IsNameScope)
{
frame.NameScopeDictionary = frame.Instance as XAML3.INameScopeDictionary ?? new NameScopeDictionary(frame.Instance as XAML3.INameScope);
}
if (frame.NameScopeDictionary is null)
{
if (frame.Depth == 1)
{
frame.NameScopeDictionary = HuntAroundForARootNameScope(frame);
}
else if (frame.Depth > 1)
{
if (frame.Depth == SavedDepth + 1 &&
_settings is not null && !_settings.RegisterNamesOnExternalNamescope)
{
frame.NameScopeDictionary = new NameScope();
}
else
{
var parentFrame = (ObjectWriterFrame)frame.Previous;
frame.NameScopeDictionary = LookupNameScopeDictionary(parentFrame);
}
}
}
}
// We are sure to find a name scope at the root (at least).
Debug.Assert(frame.NameScopeDictionary is not null || frame.Depth == 0);
return frame.NameScopeDictionary;
}
public IEnumerable<XAML3.INameScopeDictionary> StackWalkOfNameScopes
{
get
{
var frame = (ObjectWriterFrame)_stack.CurrentFrame;
XAML3.INameScopeDictionary previousNameScopeDictionary = null;
XAML3.INameScopeDictionary nameScopeDictionary = null;
while (frame.Depth > 0)
{
nameScopeDictionary = LookupNameScopeDictionary(frame);
Debug.Assert(nameScopeDictionary is not null);
if (frame.NameScopeDictionary != previousNameScopeDictionary)
{
previousNameScopeDictionary = nameScopeDictionary;
yield return nameScopeDictionary;
}
frame = (ObjectWriterFrame)frame.Previous;
}
// return the provided root namescope if it's different from the document root namescope
if (frame.NameScopeDictionary is not null && frame.NameScopeDictionary != previousNameScopeDictionary)
{
yield return frame.NameScopeDictionary;
}
}
}
public bool IsOnTheLiveStack(object instance)
{
var frame = (ObjectWriterFrame)_stack.CurrentFrame;
while (frame.Depth > SavedDepth)
{
if (instance == frame.Instance)
{
return true;
}
frame = (ObjectWriterFrame)frame.Previous;
}
return false;
}
private XAML3.INameScopeDictionary HuntAroundForARootNameScope(ObjectWriterFrame rootFrame)
{
Debug.Assert(rootFrame.Depth == 1);
object inst = rootFrame.Instance;
if (inst is null && rootFrame.XamlType.IsNameScope)
{
throw new InvalidOperationException(SR.NameScopeOnRootInstance);
}
XAML3.INameScopeDictionary nameScopeDictionary = null;
nameScopeDictionary = inst as XAML3.INameScopeDictionary;
if (nameScopeDictionary is null)
{
XAML3.INameScope nameScope = inst as XAML3.INameScope;
if (nameScope is not null)
{
nameScopeDictionary = new NameScopeDictionary(nameScope);
}
}
// If the root instance isn't a name scope
// then perhaps it designated a property as the name scope.
if (nameScopeDictionary is null)
{
XamlType xamlType = rootFrame.XamlType;
if (xamlType.UnderlyingType is not null)
{
// Get the Name Scope Property (from attribute on the class)
XamlMember nameScopeProperty = TypeReflector.LookupNameScopeProperty(xamlType);
if (nameScopeProperty is not null)
{
// Read the value of the property. If it is an object we are good.
// if it is null create a stock name scope dictionary object and assign it back.
XAML3.INameScope nameScope = (XAML3.INameScope)_runtime.GetValue(inst, nameScopeProperty, false);
if (nameScope is null)
{
nameScopeDictionary = new NameScope();
_runtime.SetValue(inst, nameScopeProperty, nameScopeDictionary);
}
else
{
nameScopeDictionary = nameScope as XAML3.INameScopeDictionary;
if (nameScopeDictionary is null)
{
nameScopeDictionary = new NameScopeDictionary(nameScope);
}
}
}
}
}
if (nameScopeDictionary is null && _settings is not null
&& _settings.RegisterNamesOnExternalNamescope)
{
ObjectWriterFrame frameZero = (ObjectWriterFrame)rootFrame.Previous;
nameScopeDictionary = frameZero.NameScopeDictionary;
}
// Otherwise we still need a namescope at the root of the parse
// for our own usage. For IXamlNameResolver() to use.
if (nameScopeDictionary is null)
{
nameScopeDictionary = new NameScope();
}
rootFrame.NameScopeDictionary = nameScopeDictionary;
return nameScopeDictionary;
}
public XamlSavedContext GetSavedContext(SavedContextType savedContextType)
{
// Ensure that we have a root namescope before cloning the stack
ObjectWriterFrame topFrame = GetTopFrame();
if (topFrame.NameScopeDictionary is null)
{
topFrame.NameScopeDictionary = LookupNameScopeDictionary(topFrame);
}
// Clone the stack
var newStack = new XamlContextStack<ObjectWriterFrame>(_stack, true);
XamlSavedContext savedContext = new XamlSavedContext(savedContextType, this, newStack);
return savedContext;
}
public object ResolveName(string name, out bool isFullyInitialized)
{
isFullyInitialized = false;
object value = null;
foreach (XAML3.INameScope nameScope in StackWalkOfNameScopes)
{
object obj = nameScope.FindName(name);
if (obj is not null)
{
if (IsInitializedCallback is not null)
{
isFullyInitialized = IsInitializedCallback.IsFullyInitialized(obj);
}
if (NameResolutionComplete || isFullyInitialized || IsInitializedCallback is null)
{
value = obj;
}
break;
}
}
return value;
}
public IEnumerable<KeyValuePair<string, object>> GetAllNamesAndValuesInScope()
{
List<KeyValuePair<string, object>> allNamesAndValues = new List<KeyValuePair<string, object>>();
//
// This could be optimized further by enumerating the collection of namescopes, getting the NamesAndValues
// from each, calculating the total size required, pre-allocating the final collection, and then
// inserting the names and values from each name scope into it.
// However unless we have a lot of namescopes in the graph, which doesn't seem likely, this seems like overkill
foreach (XAML3.INameScopeDictionary nameScopeDictionary in StackWalkOfNameScopes)
{
foreach (KeyValuePair<string, object> nameValuePair in nameScopeDictionary)
{
if (allNamesAndValues.Exists(pair => pair.Key == nameValuePair.Key))
{
continue;
}
allNamesAndValues.Add(nameValuePair);
}
}
return allNamesAndValues;
}
internal void AddNameScopeInitializationCompleteSubscriber(EventHandler handler)
{
if (_nameScopeInitializationCompleteSubscribers is null)
{
_nameScopeInitializationCompleteSubscribers = new List<NameScopeInitializationCompleteSubscriber>();
}
var subscriber = new NameScopeInitializationCompleteSubscriber { Handler = handler };
subscriber.NameScopeDictionaryList.AddRange(StackWalkOfNameScopes);
_nameScopeInitializationCompleteSubscribers.Add(subscriber);
}
internal void RemoveNameScopeInitializationCompleteSubscriber(EventHandler handler)
{
var subscriber = _nameScopeInitializationCompleteSubscribers.Find(o => o.Handler == handler);
if (subscriber is not null)
{
_nameScopeInitializationCompleteSubscribers.Remove(subscriber);
}
}
internal void RaiseNameScopeInitializationCompleteEvent()
{
if (_nameScopeInitializationCompleteSubscribers is not null)
{
EventArgs e = new EventArgs();
foreach (var subscriber in _nameScopeInitializationCompleteSubscribers)
{
var resolver = new StackWalkNameResolver(subscriber.NameScopeDictionaryList);
subscriber.Handler(resolver, e);
}
}
}
internal class NameScopeInitializationCompleteSubscriber
{
List<XAML3.INameScopeDictionary> _nameScopeDictionaryList = new List<XAML3.INameScopeDictionary>();
public EventHandler Handler
{
get; set;
}
public List<XAML3.INameScopeDictionary> NameScopeDictionaryList
{
get { return _nameScopeDictionaryList; }
}
}
private class StackWalkNameResolver : IXamlNameResolver
{
List<XAML3.INameScopeDictionary> _nameScopeDictionaryList;
public StackWalkNameResolver(List<XAML3.INameScopeDictionary> nameScopeDictionaryList)
{
_nameScopeDictionaryList = nameScopeDictionaryList;
}
public bool IsFixupTokenAvailable
{
get
{
return false;
}
}
public object GetFixupToken(IEnumerable<string> name)
{
return null;
}
public object GetFixupToken(IEnumerable<string> name, bool canAssignDirectly)
{
return null;
}
public event EventHandler OnNameScopeInitializationComplete
{
// at this point all name scopes have been completed, and we will
// not raise any event for subscriptions that come after this.
add
{
}
remove
{
}
}
public object Resolve(string name)
{
object value = null;
foreach (XAML3.INameScopeDictionary nameScope in _nameScopeDictionaryList)
{
object obj = nameScope.FindName(name);
if (obj is not null)
{
value = obj;
break;
}
}
return value;
}
public object Resolve(string name, out bool isFullyInitialized)
{
// This resolver is only used after the parse is complete, including completing
// name references. So all objects are fully initialized.
object result = Resolve(name);
isFullyInitialized = (result is not null);
return result;
}
public IEnumerable<KeyValuePair<string, object>> GetAllNamesAndValuesInScope()
{
List<KeyValuePair<string, object>> allNamesAndValues = new List<KeyValuePair<string, object>>();
foreach (XAML3.INameScopeDictionary nameScopeDictionary in _nameScopeDictionaryList)
{
foreach (KeyValuePair<string, object> nameValuePair in nameScopeDictionary)
{
if (allNamesAndValues.Exists(pair => pair.Key == nameValuePair.Key))
{
continue;
}
allNamesAndValues.Add(nameValuePair);
}
}
return allNamesAndValues;
}
}
}
}
|