コード例 #1
0
        private protected virtual void UpdateInterfaceType(Type nodeType)
        {
            if (InterfaceType == Type.Missing)
            {
                string[] Split    = CollectionName.Split('.');
                Type     BaseType = nodeType;

                for (int i = 0; i < Split.Length; i++)
                {
                    string PropertyName = Split[i];
                    Type   ChildNodeType;

                    if (i + 1 < Split.Length)
                    {
                        bool IsValidProperty = NodeTreeHelperChild.IsChildNodeProperty(BaseType, PropertyName, out ChildNodeType) || NodeTreeHelperOptional.IsOptionalChildNodeProperty(BaseType, PropertyName, out ChildNodeType);
                        Debug.Assert(IsValidProperty);

                        BaseType = ChildNodeType;
                    }
                    else
                    {
                        Type ChildInterfaceType;
                        bool IsValidProperty = NodeTreeHelperBlockList.IsBlockListProperty(BaseType, PropertyName, /*out ChildInterfaceType,*/ out /*ChildNodeType*/ ChildInterfaceType) || NodeTreeHelperList.IsNodeListProperty(BaseType, PropertyName, out ChildInterfaceType);
                        Debug.Assert(IsValidProperty);

                        BaseType = ChildInterfaceType;
                    }
                }

                InterfaceType = BaseType;
            }
        }
コード例 #2
0
        private protected virtual bool IsNodeTreeValid(Node node)
        {
            Type           ChildNodeType;
            IList <string> PropertyNames = NodeTreeHelper.EnumChildNodeProperties(node);
            bool           IsValid       = true;

            foreach (string PropertyName in PropertyNames)
            {
                if (NodeTreeHelperChild.IsChildNodeProperty(node, PropertyName, out ChildNodeType))
                {
                    IsValid &= InvariantFailed(IsNodeTreeChildNodeValid(node, PropertyName));
                }
                else if (NodeTreeHelperOptional.IsOptionalChildNodeProperty(node, PropertyName, out ChildNodeType))
                {
                    IsValid &= InvariantFailed(IsNodeTreeOptionalNodeValid(node, PropertyName));
                }
                else if (NodeTreeHelperList.IsNodeListProperty(node, PropertyName, out ChildNodeType))
                {
                    IsValid &= InvariantFailed(IsNodeTreeListValid(node, PropertyName));
                }
                else if (NodeTreeHelperBlockList.IsBlockListProperty(node, PropertyName, /*out Type ChildInterfaceType,*/ out ChildNodeType))
                {
                    IsValid &= InvariantFailed(IsNodeTreeBlockListValid(node, PropertyName));
                }
            }

            return(IsValid);
        }
コード例 #3
0
        /// <summary>
        /// Checks if the template associated to the <paramref name="propertyName"/> property of the <paramref name="stateView"/> state is complex.
        /// </summary>
        /// <param name="stateView">The state view for the node with property <paramref name="propertyName"/>.</param>
        /// <param name="propertyName">Name of the property pointing to the template to check.</param>
        public virtual bool IsTemplateComplex(IFocusNodeStateView stateView, string propertyName)
        {
            IFocusNodeState State = stateView.State;

            Debug.Assert(State.InnerTable.ContainsKey(propertyName));

            IFocusPlaceholderInner ParentInner = State.InnerTable[propertyName] as IFocusPlaceholderInner;

            Debug.Assert(ParentInner != null);

            NodeTreeHelperChild.GetChildNode(stateView.State.Node, propertyName, out Node ChildNode);
            Debug.Assert(ChildNode != null);

            Type NodeType = Type.FromGetType(ChildNode);

            //Type InterfaceType = NodeTreeHelper.NodeTypeToInterfaceType(NodeType);
            //Debug.Assert(TemplateSet.NodeTemplateTable.ContainsKey(InterfaceType));
            Debug.Assert(TemplateSet.NodeTemplateTable.ContainsKey(NodeType));

            IFocusNodeTemplate ParentTemplate = TemplateSet.NodeTemplateTable[NodeType] as IFocusNodeTemplate;

            Debug.Assert(ParentTemplate != null);

            return(ParentTemplate.IsComplex);
        }
コード例 #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ReadOnlyBrowsingPlaceholderNodeIndex"/> class.
        /// </summary>
        /// <param name="parentNode">Node containing the indexed node.</param>
        /// <param name="node">The indexed node.</param>
        /// <param name="propertyName">Property in <paramref name="parentNode"/> corresponding to the indexed node.</param>
        public ReadOnlyBrowsingPlaceholderNodeIndex(Node parentNode, Node node, string propertyName)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));
            Debug.Assert(NodeTreeHelperChild.IsChildNode(parentNode, propertyName, node));

            Node         = node;
            PropertyName = propertyName;
        }
コード例 #5
0
        /// <summary>
        /// Checks that a frame visibility is correctly constructed.
        /// </summary>
        /// <param name="nodeType">Type of the node this frame visibility can describe.</param>
        public override bool IsValid(Type nodeType)
        {
            bool IsValid = true;

            IsValid &= !string.IsNullOrEmpty(PropertyName);
            IsValid &= NodeTreeHelperChild.IsChildNodeProperty(nodeType, PropertyName, out Type ChildNodeType);

            return(IsValid);
        }
コード例 #6
0
        private protected virtual bool IsNodeTreeChildNodeValid(Node node, string propertyName)
        {
            NodeTreeHelperChild.GetChildNode(node, propertyName, out Node ChildNode);
            Debug.Assert(ChildNode != null);

            bool IsValid = InvariantFailed(IsNodeTreeValid(ChildNode));

            return(IsValid);
        }
コード例 #7
0
        /// <inheritdoc/>
        public override void CloneChildren(Node parentNode)
        {
            // Clone the child recursively.
            Node ChildNodeClone = ChildState.CloneNode();

            Debug.Assert(ChildNodeClone != null);

            // Set the clone in the parent.
            NodeTreeHelperChild.SetChildNode(parentNode, PropertyName, ChildNodeClone);
        }
コード例 #8
0
        /// <summary>
        /// Checks that a frame is correctly constructed.
        /// </summary>
        /// <param name="nodeType">Type of the node this frame can describe.</param>
        /// <param name="nodeTemplateTable">Table of templates with all frames.</param>
        /// <param name="commentFrameCount">Number of comment frames found so far.</param>
        public override bool IsValid(Type nodeType, IFrameTemplateReadOnlyDictionary nodeTemplateTable, ref int commentFrameCount)
        {
            bool IsValid = true;

            IsValid &= base.IsValid(nodeType, nodeTemplateTable, ref commentFrameCount);
            IsValid &= NodeTreeHelperChild.IsChildNodeProperty(nodeType, PropertyName, out Type ChildNodeType);

            Debug.Assert(IsValid);
            return(IsValid);
        }
コード例 #9
0
        private protected virtual void SetNodeTypeToDefault(IFrameTemplateDictionary dictionary, Type nodeType)
        {
            Debug.Assert(dictionary.ContainsKey(nodeType));
            Debug.Assert(dictionary[nodeType] == null);

            FrameHorizontalPanelFrame RootFrame    = (FrameHorizontalPanelFrame)CreateHorizontalPanelFrame();
            FrameNodeTemplate         RootTemplate = (FrameNodeTemplate)CreateNodeTemplate();

            RootTemplate.NodeType = nodeType;
            RootTemplate.Root     = RootFrame;

            // Set the template, even if empty, in case the node recursively refers to itself (ex: expressions).
            dictionary[nodeType] = RootTemplate;

            RootFrame.Items.Add(CreateCommentFrame());

            Type           ChildNodeType;
            IList <string> Properties = NodeTreeHelper.EnumChildNodeProperties(nodeType);

            foreach (string PropertyName in Properties)
            {
                bool IsHandled = false;

                if (NodeTreeHelperChild.IsChildNodeProperty(nodeType, PropertyName, out ChildNodeType))
                {
                    FramePlaceholderFrame NewFrame = (FramePlaceholderFrame)CreatePlaceholderFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperOptional.IsOptionalChildNodeProperty(nodeType, PropertyName, out ChildNodeType))
                {
                    FrameOptionalFrame NewFrame = (FrameOptionalFrame)CreateOptionalFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperList.IsNodeListProperty(nodeType, PropertyName, out Type ListNodeType))
                {
                    FrameHorizontalListFrame NewFrame = (FrameHorizontalListFrame)CreateHorizontalListFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperBlockList.IsBlockListProperty(nodeType, PropertyName, out Type ChildInterfaceType, out Type ChildItemType))
                {
                    FrameHorizontalBlockListFrame NewFrame = (FrameHorizontalBlockListFrame)CreateHorizontalBlockListFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
コード例 #10
0
        /// <summary>
        /// Replaces a node.
        /// </summary>
        /// <param name="operation">Details of the operation performed.</param>
        public virtual void Replace(IWriteableReplaceOperation operation)
        {
            Node ParentNode = Owner.Node;

            IWriteableBrowsingPlaceholderNodeIndex OldBrowsingIndex = (IWriteableBrowsingPlaceholderNodeIndex)ChildState.ParentIndex;
            IWriteablePlaceholderNodeState         OldChildState    = (IWriteablePlaceholderNodeState)ChildState;
            Node OldNode = OldChildState.Node;

            NodeTreeHelperChild.SetChildNode(ParentNode, PropertyName, operation.NewNode);

            IWriteableBrowsingPlaceholderNodeIndex NewBrowsingIndex = CreateBrowsingNodeIndex(operation.NewNode);
            IWriteablePlaceholderNodeState         NewChildState    = (IWriteablePlaceholderNodeState)CreateNodeState(NewBrowsingIndex);

            SetChildState(NewChildState);

            operation.Update(OldBrowsingIndex, NewBrowsingIndex, OldNode, NewChildState);
        }
コード例 #11
0
        /// <summary>
        /// Checks that a frame selector is correctly constructed.
        /// </summary>
        /// <param name="nodeType">Type of the node this frame selector can describe.</param>
        /// <param name="nodeTemplateTable">Table of templates with all frames.</param>
        /// <param name="propertyName">The property for which frames can be selected.</param>
        public virtual bool IsValid(Type nodeType, IFocusTemplateReadOnlyDictionary nodeTemplateTable, string propertyName)
        {
            bool IsValid = true;

            IsValid &= SelectorType != null;
            IsValid &= !string.IsNullOrEmpty(SelectorName);

            Type ChildInterfaceType, ChildNodeType;

            IsValid &= NodeTreeHelperChild.IsChildNodeProperty(nodeType, propertyName, out ChildInterfaceType) ||
                       NodeTreeHelperOptional.IsOptionalChildNodeProperty(nodeType, propertyName, out ChildInterfaceType) ||
                       NodeTreeHelperList.IsNodeListProperty(nodeType, propertyName, out ChildInterfaceType) ||
                       NodeTreeHelperBlockList.IsBlockListProperty(nodeType, propertyName, out ChildInterfaceType, out ChildNodeType) ||
                       (NodeTreeHelper.IsBlockType(nodeType) && propertyName == nameof(BaseNode.IBlock.NodeList));

            IsValid &= nodeTemplateTable.ContainsKey(SelectorType);

            IFocusNodeTemplate Template = nodeTemplateTable[SelectorType] as IFocusNodeTemplate;

            Debug.Assert(Template != null);

            IFocusSelectionFrame AsSelectionFrame = Template.Root as IFocusSelectionFrame;

            IsValid &= AsSelectionFrame != null;

            if (IsValid)
            {
                IFocusSelectableFrame SelectedItem = null;
                foreach (IFocusSelectableFrame Item in AsSelectionFrame.Items)
                {
                    if (Item.Name == SelectorName)
                    {
                        SelectedItem = Item;
                        break;
                    }
                }

                IsValid &= SelectedItem != null;
            }

            Debug.Assert(IsValid);
            return(IsValid);
        }
コード例 #12
0
        private protected virtual void BrowseChildrenOfNode(IReadOnlyBrowseContext browseNodeContext, INode node)
        {
            IList <string> PropertyNames = NodeTreeHelper.EnumChildNodeProperties(node);

            foreach (string PropertyName in PropertyNames)
            {
                INode ChildNode;
                Type  ChildInterfaceType, ChildNodeType;
                IReadOnlyList <INode>          ChildNodeList;
                IReadOnlyList <INodeTreeBlock> ChildBlockList;
                bool IsHandled = false;

                if (NodeTreeHelperChild.IsChildNodeProperty(node, PropertyName, out ChildNodeType))
                {
                    NodeTreeHelperChild.GetChildNode(node, PropertyName, out ChildNode);
                    Debug.Assert(ChildNode != null);
                    IReadOnlyBrowsingPlaceholderNodeIndex ChildNodeIndex = CreateChildNodeIndex(browseNodeContext, node, PropertyName, ChildNode);

                    // Create a collection containing one index for this child node.
                    IReadOnlyIndexCollection IndexCollection = CreatePlaceholderIndexCollection(browseNodeContext, PropertyName, ChildNodeIndex);
                    browseNodeContext.AddIndexCollection(IndexCollection);

                    IsHandled = true;
                }
                else if (NodeTreeHelperOptional.IsOptionalChildNodeProperty(node, PropertyName, out ChildNodeType))
                {
                    IReadOnlyBrowsingOptionalNodeIndex OptionalNodeIndex = CreateOptionalNodeIndex(browseNodeContext, node, PropertyName);

                    // Create a collection containing one index for this optional node.
                    IReadOnlyIndexCollection IndexCollection = CreateOptionalIndexCollection(browseNodeContext, PropertyName, OptionalNodeIndex);
                    browseNodeContext.AddIndexCollection(IndexCollection);

                    IsHandled = true;
                }
                else if (NodeTreeHelperList.IsNodeListProperty(node, PropertyName, out ChildNodeType))
                {
                    NodeTreeHelperList.GetChildNodeList(node, PropertyName, out ChildNodeList);
                    Debug.Assert(ChildNodeList != null);

                    // Create a collection containing indexes for each children.
                    IReadOnlyIndexCollection IndexCollection = BrowseNodeList(browseNodeContext, node, PropertyName, ChildNodeList);
                    browseNodeContext.AddIndexCollection(IndexCollection);

                    IsHandled = true;
                }
                else if (NodeTreeHelperBlockList.IsBlockListProperty(node, PropertyName, out ChildInterfaceType, out ChildNodeType))
                {
                    NodeTreeHelperBlockList.GetChildBlockList(node, PropertyName, out ChildBlockList);
                    Debug.Assert(ChildBlockList != null);

                    // Create a collection containing indexes for each child blocks and their children.
                    IReadOnlyIndexCollection IndexCollection = BrowseNodeBlockList(browseNodeContext, node, PropertyName, ChildBlockList);
                    browseNodeContext.AddIndexCollection(IndexCollection);

                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsBooleanProperty(node, PropertyName))
                {
                    browseNodeContext.AddValueProperty(PropertyName, ValuePropertyType.Boolean);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsEnumProperty(node, PropertyName))
                {
                    browseNodeContext.AddValueProperty(PropertyName, ValuePropertyType.Enum);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsStringProperty(node, PropertyName))
                {
                    browseNodeContext.AddValueProperty(PropertyName, ValuePropertyType.String);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsGuidProperty(node, PropertyName))
                {
                    browseNodeContext.AddValueProperty(PropertyName, ValuePropertyType.Guid);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsDocumentProperty(node, PropertyName))
                {
                    IsHandled = true; // Ignore the doc node.
                }

                Debug.Assert(IsHandled);
            }
        }
コード例 #13
0
        private protected virtual void SetNodeTypeToDefault(FrameTemplateDictionary dictionary, Type nodeType)
        {
            Debug.Assert(dictionary.ContainsKey(nodeType));
            Debug.Assert(dictionary[nodeType] == null);

            FrameHorizontalPanelFrame RootFrame    = (FrameHorizontalPanelFrame)CreateHorizontalPanelFrame();
            FrameNodeTemplate         RootTemplate = (FrameNodeTemplate)CreateNodeTemplate();

            RootTemplate.NodeType = nodeType;
            RootTemplate.Root     = RootFrame;

            // Set the template, even if empty, in case the node recursively refers to itself (ex: expressions).
            dictionary[nodeType] = RootTemplate;

            RootFrame.Items.Add(CreateCommentFrame());

            Type           ChildNodeType;
            IList <string> Properties = NodeTreeHelper.EnumChildNodeProperties(nodeType);

            foreach (string PropertyName in Properties)
            {
                bool IsHandled = false;

                if (NodeTreeHelperChild.IsChildNodeProperty(nodeType, PropertyName, out ChildNodeType))
                {
                    FramePlaceholderFrame NewFrame = (FramePlaceholderFrame)CreatePlaceholderFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperOptional.IsOptionalChildNodeProperty(nodeType, PropertyName, out ChildNodeType))
                {
                    FrameOptionalFrame NewFrame = (FrameOptionalFrame)CreateOptionalFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperList.IsNodeListProperty(nodeType, PropertyName, out Type ListNodeType))
                {
                    FrameHorizontalListFrame NewFrame = (FrameHorizontalListFrame)CreateHorizontalListFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelperBlockList.IsBlockListProperty(nodeType, PropertyName, /*out Type ChildInterfaceType,*/ out Type ChildItemType))
                {
                    FrameHorizontalBlockListFrame NewFrame = (FrameHorizontalBlockListFrame)CreateHorizontalBlockListFrame();
                    NewFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsBooleanProperty(nodeType, PropertyName))
                {
                    FrameDiscreteFrame NewDiscreteFrame = (FrameDiscreteFrame)CreateDiscreteFrame();
                    NewDiscreteFrame.PropertyName = PropertyName;

                    FrameKeywordFrame KeywordFalseFrame = (FrameKeywordFrame)CreateKeywordFrame();
                    KeywordFalseFrame.Text = $"{PropertyName}=False";
                    NewDiscreteFrame.Items.Add(KeywordFalseFrame);

                    FrameKeywordFrame KeywordTrueFrame = (FrameKeywordFrame)CreateKeywordFrame();
                    KeywordTrueFrame.Text = $"{PropertyName}=True";
                    NewDiscreteFrame.Items.Add(KeywordTrueFrame);

                    RootFrame.Items.Add(NewDiscreteFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsEnumProperty(nodeType, PropertyName))
                {
                    NodeTreeHelper.GetEnumRange(nodeType, PropertyName, out int Min, out int Max);

                    FrameDiscreteFrame NewDiscreteFrame = (FrameDiscreteFrame)CreateDiscreteFrame();
                    NewDiscreteFrame.PropertyName = PropertyName;

                    for (int i = Min; i <= Max; i++)
                    {
                        FrameKeywordFrame KeywordFrame = (FrameKeywordFrame)CreateKeywordFrame();
                        KeywordFrame.Text = $"{PropertyName}={i}";
                        NewDiscreteFrame.Items.Add(KeywordFrame);
                    }

                    RootFrame.Items.Add(NewDiscreteFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsStringProperty(nodeType, PropertyName))
                {
                    FrameTextValueFrame NewDiscreteFrame = (FrameTextValueFrame)CreateTextValueFrame();
                    NewDiscreteFrame.PropertyName = PropertyName;
                    RootFrame.Items.Add(NewDiscreteFrame);
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsGuidProperty(nodeType, PropertyName))
                {
                    IsHandled = true;
                }
                else if (NodeTreeHelper.IsDocumentProperty(nodeType, PropertyName))
                {
                    IsHandled = true;
                }

                Debug.Assert(IsHandled);
            }

            RootFrame.UpdateParent(RootTemplate, GetRoot());
        }
コード例 #14
0
        /// <summary></summary>
        public bool ReplacePhase1Macro(BaseNode.INode node, BaseNode.INode parentNode, string propertyName, IWalkCallbacks <ReplacePhase1MacroContext> callbacks, ReplacePhase1MacroContext context)
        {
            bool Result = true;

            if (node is IClass AsClass)
            {
                context.CurrentClass = AsClass;
            }
            else if (node is ILibrary || node is IGlobalReplicate)
            {
                context.CurrentClass = null;
            }
            else if (node is IPreprocessorExpression AsPreprocessorExpression)
            {
                bool IsHandled = false;

                switch (AsPreprocessorExpression.Value)
                {
                case BaseNode.PreprocessorMacro.DateAndTime:
                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, CompilationDateTime);
                    IsHandled = true;
                    break;

                case BaseNode.PreprocessorMacro.CompilationDiscreteIdentifier:
                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, CompilationUID);
                    IsHandled = true;
                    break;

                case BaseNode.PreprocessorMacro.CompilerVersion:
                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, CompilerVersion);
                    IsHandled = true;
                    break;

                case BaseNode.PreprocessorMacro.ConformanceToStandard:
                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, ConformanceToStandard);
                    IsHandled = true;
                    break;

                case BaseNode.PreprocessorMacro.DiscreteClassIdentifier:
                    Debug.Assert(context.CurrentClass != null);

                    string GlassGuidDigits        = context.CurrentClass.ClassGuid.ToString("N", CultureInfo.InvariantCulture) + ":" + "H";
                    BaseNode.IIdentifier Operator = NodeHelper.CreateSimpleIdentifier("To UUID");
                    BaseNode.IManifestNumberExpression NumberExpression = NodeHelper.CreateSimpleManifestNumberExpression(GlassGuidDigits);
                    BaseNode.IUnaryOperatorExpression  Expression       = NodeHelper.CreateUnaryOperatorExpression(Operator, NumberExpression);
                    IExpression ReplacementNode = ToCompilerNode <BaseNode.IUnaryOperatorExpression, IUnaryOperatorExpression>(Expression);

                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, ReplacementNode);
                    IsHandled = true;
                    break;

                case BaseNode.PreprocessorMacro.Debugging:
                    NodeTreeHelperChild.SetChildNode(parentNode, propertyName, Debugging);
                    IsHandled = true;
                    break;

                // Processed in phase 2.
                case BaseNode.PreprocessorMacro.ClassPath:
                case BaseNode.PreprocessorMacro.Counter:
                case BaseNode.PreprocessorMacro.RandomInteger:
                    IsHandled = true;
                    break;
                }

                Debug.Assert(IsHandled);
            }

            return(Result);
        }
コード例 #15
0
        static void BrowseNode(ReadOnlyController controller, IReadOnlyIndex index, Stats stats)
        {
            Assert.That(index != null, "ReadOnly #0");
            Assert.That(controller.Contains(index), "ReadOnly #1");
            IReadOnlyNodeState State = controller.IndexToState(index);

            Assert.That(State != null, "ReadOnly #2");
            Assert.That(State.ParentIndex == index, "ReadOnly #4");

            Node Node;

            if (State is IReadOnlyPlaceholderNodeState AsPlaceholderState)
            {
                Node = AsPlaceholderState.Node;
            }
            else if (State is IReadOnlyPatternState AsPatternState)
            {
                Node = AsPatternState.Node;
            }
            else if (State is IReadOnlySourceState AsSourceState)
            {
                Node = AsSourceState.Node;
            }
            else
            {
                Assert.That(State is IReadOnlyOptionalNodeState, "ReadOnly #5");
                IReadOnlyOptionalNodeState AsOptionalState = (IReadOnlyOptionalNodeState)State;
                IReadOnlyOptionalInner     ParentInner     = AsOptionalState.ParentInner;

                //Assert.That(ParentInner.IsAssigned, "ReadOnly #6");

                Node = AsOptionalState.Node;
            }

            stats.NodeCount++;

            Type           ChildNodeType;
            IList <string> PropertyNames = NodeTreeHelper.EnumChildNodeProperties(Node);

            foreach (string PropertyName in PropertyNames)
            {
                if (NodeTreeHelperChild.IsChildNodeProperty(Node, PropertyName, out ChildNodeType))
                {
                    stats.PlaceholderNodeCount++;

                    IReadOnlyPlaceholderInner Inner      = (IReadOnlyPlaceholderInner)State.PropertyToInner(PropertyName);
                    IReadOnlyNodeState        ChildState = Inner.ChildState;
                    IReadOnlyIndex            ChildIndex = ChildState.ParentIndex;
                    BrowseNode(controller, ChildIndex, stats);
                }

                else if (NodeTreeHelperOptional.IsOptionalChildNodeProperty(Node, PropertyName, out ChildNodeType))
                {
                    stats.OptionalNodeCount++;

                    NodeTreeHelperOptional.GetChildNode(Node, PropertyName, out bool IsAssigned, out Node ChildNode);
                    Debug.Assert(ChildNode is not null);
                    //if (IsAssigned)
                    {
                        if (IsAssigned)
                        {
                            stats.AssignedOptionalNodeCount++;
                        }

                        IReadOnlyOptionalInner Inner      = (IReadOnlyOptionalInner)State.PropertyToInner(PropertyName);
                        IReadOnlyNodeState     ChildState = Inner.ChildState;
                        IReadOnlyIndex         ChildIndex = ChildState.ParentIndex;
                        BrowseNode(controller, ChildIndex, stats);
                    }

                    /*else
                     *  stats.NodeCount++;*/
                }

                else if (NodeTreeHelperList.IsNodeListProperty(Node, PropertyName, out ChildNodeType))
                {
                    stats.ListCount++;

                    IReadOnlyListInner Inner = (IReadOnlyListInner)State.PropertyToInner(PropertyName);

                    for (int i = 0; i < Inner.StateList.Count; i++)
                    {
                        stats.PlaceholderNodeCount++;

                        IReadOnlyPlaceholderNodeState ChildState = Inner.StateList[i];
                        IReadOnlyIndex ChildIndex = ChildState.ParentIndex;
                        BrowseNode(controller, ChildIndex, stats);
                    }
                }

                else if (NodeTreeHelperBlockList.IsBlockListProperty(Node, PropertyName, /*out Type ChildInterfaceType,*/ out ChildNodeType))
                {
                    stats.BlockListCount++;

                    IReadOnlyBlockListInner Inner = (IReadOnlyBlockListInner)State.PropertyToInner(PropertyName);

                    for (int BlockIndex = 0; BlockIndex < Inner.BlockStateList.Count; BlockIndex++)
                    {
                        IReadOnlyBlockState BlockState = Inner.BlockStateList[BlockIndex];

                        stats.PlaceholderNodeCount++;
                        BrowseNode(controller, BlockState.PatternIndex, stats);

                        stats.PlaceholderNodeCount++;
                        BrowseNode(controller, BlockState.SourceIndex, stats);

                        for (int i = 0; i < BlockState.StateList.Count; i++)
                        {
                            stats.PlaceholderNodeCount++;

                            IReadOnlyPlaceholderNodeState ChildState = BlockState.StateList[i];
                            IReadOnlyIndex ChildIndex = ChildState.ParentIndex;
                            BrowseNode(controller, ChildIndex, stats);
                        }
                    }
                }

                else
                {
                    Type         NodeType = Type.FromGetType(Node);
                    PropertyInfo Info     = NodeType.GetProperty(PropertyName);

                    if (Info.PropertyType.IsTypeof <Document>())
                    {
                    }
                    else if (Info.PropertyType.IsTypeof <bool>())
                    {
                    }
                    else if (Info.PropertyType.IsEnum)
                    {
                    }
                    else if (Info.PropertyType.IsTypeof <string>())
                    {
                    }
                    else if (Info.PropertyType.IsTypeof <Guid>())
                    {
                    }
                    else
                    {
                        Assert.That(false, $"State Tree unexpected property: {Info.PropertyType.Name}");
                    }
                }
            }
        }