/// <summary>
        /// Recursively updates a hierarchy with the nodes that already exist in an instance.
        /// </summary>
        /// <param name="instanceId">The instance identifier.</param>
        /// <param name="hierarchy">The hierarchy.</param>
        private void UpdateInstanceHierarchyWithInstance(
            ExpandedNodeId instanceId,
            InstanceDeclarationHierarchy hierarchy)
        {
            // the instance must be local to the address space.
            ILocalNode instance = m_nodes.Find(instanceId) as ILocalNode;

            if (instance == null)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadNodeIdUnknown,
                          "The node is not in the local address space.\r\nNodeId = {0}",
                          instanceId);
            }

            // must be an object, variable or method.
            if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadNodeClassInvalid,
                          "The instance node is not any Object, Variable or Method.\r\nNodeId = {0}\r\nNodeClass = {1}",
                          instanceId,
                          instance.NodeClass);
            }

            // find the root of the hierarchy.
            HierarchyBrowsePath root = null;

            if (!hierarchy.BrowsePaths.TryGetValue("/", out root))
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadUnexpectedError,
                          "The hierarchy does not have a root.\r\nHierarchy = {0}",
                          hierarchy);
            }

            // the instance type definition must match the hierarchy.
            if (instance.TypeDefinitionId != root.DeclarationId)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadTypeDefinitionInvalid,
                          "The instance node is not an instance of a {0} type.\r\nNodeId = {1}\r\nTypeDefinition = {2}",
                          root.DeclarationId,
                          instanceId,
                          instance.TypeDefinitionId);
            }

            // set the instance id for the root.
            root.InstanceId = instance.NodeId;

            // follow children.
            foreach (IReference reference in instance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree))
            {
                UpdateInstanceHierarchyWithInstance(root, reference.TargetId, hierarchy);
            }
        }
        /// <summary>
        /// Recursively collects the nodes within a type hierarchy.
        /// </summary>
        private void GetInstanceHierarchyForType(
            HierarchyBrowsePath parent,
            ExpandedNodeId instanceId,
            InstanceDeclarationHierarchy hierarchy)
        {
            // the instance must be local to the address space.
            ILocalNode instance = m_nodes.Find(instanceId) as ILocalNode;

            if (instance == null)
            {
                return;
            }

            // must be an object, variable or method.
            if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0)
            {
                return;
            }

            // get the naming rule.
            NamingRule namingRule = GetNamingRule(instance.ModellingRule);

            // only include instances with unique browse names in the hierarchy.
            if (namingRule != NamingRule.Unique && namingRule != NamingRule.UniqueOptional)
            {
                return;
            }

            // construct the browse path that identifies the node.
            string browsePath = null;

            if (parent.BrowsePath == "/")
            {
                browsePath = Utils.Format("/{0}", instance.BrowseName);
            }
            else
            {
                browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName);
            }

            // check if the browse path already exists in the hierarchy.
            HierarchyBrowsePath child = null;

            if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child))
            {
                child = new HierarchyBrowsePath();

                child.BrowsePath    = browsePath;
                child.DeclarationId = instance.NodeId;
                child.InstanceId    = null;
                child.IsModelParent = false;
                child.IsOptional    = namingRule != NamingRule.Unique;

                // add new browse path to hierarchy.
                hierarchy.BrowsePaths.Add(browsePath, child);
            }

            // override any declaration specified in a supertype.
            child.DeclarationId = instance.NodeId;

            // check if node has been processed via another path.
            HierarchyBrowsePath alternatePath = null;

            if (hierarchy.Declarations.TryGetValue(instance.NodeId, out alternatePath))
            {
                // keep the model parent path as the primary path.
                if (!alternatePath.IsModelParent && child.IsModelParent)
                {
                    hierarchy.Declarations[instance.NodeId] = child;
                }

                // nothing more to do since node has been processed once.
                return;
            }

            // save child.
            hierarchy.Declarations.Add(instance.NodeId, child);



            // follow children.
            foreach (IReference reference in instance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree))
            {
                GetInstanceHierarchyForType(child, reference.TargetId, hierarchy);
            }
        }
        /// <summary>
        /// Adds or updates a reference declaration.
        /// </summary>
        private void UpdateReferenceDeclaration(
            HierarchyBrowsePath source,
            IReference reference,
            InstanceDeclarationHierarchy hierarchy)
        {
            // ignore inverse references.
            if (reference.IsInverse)
            {
                return;
            }

            // ignore subtype references.
            if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasSubtype))
            {
                return;
            }

            // find the target browse path.
            HierarchyBrowsePath targetBrowsePath = null;

            if (!reference.TargetId.IsAbsolute)
            {
                if (!hierarchy.Declarations.TryGetValue((NodeId)reference.TargetId, out targetBrowsePath))
                {
                    targetBrowsePath = null;
                }
            }

            // ignore hierarchical references with targets that are outside the hierarchy.
            if (targetBrowsePath == null)
            {
                if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HierarchicalReferences))
                {
                    return;
                }
            }

            // type definition references are never relative to the hierarchy.
            if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasTypeDefinition))
            {
                targetBrowsePath = null;
            }

            // update existing references.
            int count = hierarchy.References.Count;

            for (int ii = 0; ii < count; ii++)
            {
                HierarchyReference existingReference = hierarchy.References[ii];

                // match source browse path.
                if (existingReference.SourceBrowsePath != source.BrowsePath)
                {
                    continue;
                }

                // allow subtypes of the reference type.
                if (!m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, existingReference.ReferenceTypeId))
                {
                    continue;
                }

                // match target browse path for internal references.
                if (targetBrowsePath != null)
                {
                    if (existingReference.TargetBrowsePath != targetBrowsePath.BrowsePath)
                    {
                        continue;
                    }
                }
                else
                {
                    // check for external reference types where exactly one reference is permitted.
                    bool singletonReferenceType = false;

                    if (reference.ReferenceTypeId.IdType == IdType.Numeric && reference.ReferenceTypeId.NamespaceIndex == 0)
                    {
                        switch ((uint)reference.ReferenceTypeId.Identifier)
                        {
                        case ReferenceTypes.HasTypeDefinition:
                        {
                            singletonReferenceType = true;
                            break;
                        }

                        case ReferenceTypes.HasModellingRule:
                        {
                            singletonReferenceType = true;
                            break;
                        }

                        default:
                        {
                            break;
                        }
                        }
                    }

                    // add new reference if multiples permitted.
                    if (!singletonReferenceType)
                    {
                        break;
                    }
                }

                // update existing reference.
                existingReference.SourceDeclarationId = source.DeclarationId;
                existingReference.SourceInstanceId    = null;
                existingReference.ReferenceTypeId     = reference.ReferenceTypeId;
                existingReference.TargetBrowsePath    = (targetBrowsePath != null)?targetBrowsePath.BrowsePath:null;
                existingReference.TargetDeclarationId = reference.TargetId;
                existingReference.TargetInstanceId    = null;

                // only one reference with the same source/reference type/target allowed.
                return;
            }

            // create a new reference.
            HierarchyReference newReference = new HierarchyReference();

            newReference.SourceBrowsePath    = source.BrowsePath;
            newReference.SourceDeclarationId = source.DeclarationId;
            newReference.SourceInstanceId    = null;
            newReference.ReferenceTypeId     = reference.ReferenceTypeId;
            newReference.TargetBrowsePath    = (targetBrowsePath != null)?targetBrowsePath.BrowsePath:null;
            newReference.TargetDeclarationId = reference.TargetId;
            newReference.TargetInstanceId    = null;

            hierarchy.References.Add(newReference);
        }
        /// <summary>
        /// Adds or updates the references
        /// </summary>
        private void InstantiateReferences(
            HierarchyBrowsePath source,
            InstanceDeclarationHierarchy hierarchy)
        {
            // don't add references to nodes that are not owned.
            if (!source.IsModelParent)
            {
                return;
            }

            // get the instance.
            ILocalNode instance = hierarchy.Instances[(NodeId)source.InstanceId] as ILocalNode;

            if (instance == null)
            {
                return;
            }

            // find references for the source.
            foreach (HierarchyReference reference in hierarchy.References)
            {
                // match source browse path.
                if (reference.SourceBrowsePath != source.BrowsePath)
                {
                    continue;
                }

                // check if the target is in the hierarchy.
                HierarchyBrowsePath targetBrowsePath = null;

                // check declaration.
                if (!reference.TargetDeclarationId.IsAbsolute)
                {
                    if (!hierarchy.Declarations.TryGetValue((NodeId)reference.TargetDeclarationId, out targetBrowsePath))
                    {
                        targetBrowsePath = null;
                    }
                }

                // check browse path.
                if (targetBrowsePath == null && reference.TargetBrowsePath != null)
                {
                    if (!hierarchy.BrowsePaths.TryGetValue(reference.TargetBrowsePath, out targetBrowsePath))
                    {
                        targetBrowsePath = null;
                    }
                }

                // type definition references are never relative to the hierarchy.
                if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasTypeDefinition))
                {
                    targetBrowsePath = null;
                }

                // target outside hierarchy.
                if (targetBrowsePath == null)
                {
                    if (reference.TargetDeclarationId != null)
                    {
                        // check for existing type definition.
                        if (reference.ReferenceTypeId == ReferenceTypeIds.HasTypeDefinition)
                        {
                            IList <IReference> existingReferences = instance.References.Find(reference.ReferenceTypeId, false, true, m_nodes.TypeTree);

                            if (existingReferences.Count > 0)
                            {
                                if (m_nodes.TypeTree.IsTypeOf(existingReferences[0].TargetId, reference.TargetDeclarationId))
                                {
                                    continue;
                                }

                                instance.References.RemoveAll(ReferenceTypeIds.HasTypeDefinition, false);
                            }
                        }

                        instance.References.Add(reference.ReferenceTypeId, false, reference.TargetDeclarationId);
                    }

                    continue;
                }

                // target inside hierarchy.
                if (targetBrowsePath.InstanceId != null)
                {
                    instance.References.Add(reference.ReferenceTypeId, false, targetBrowsePath.InstanceId);
                }
            }
        }
        /// <summary>
        /// Recursively builds the full inhierited type hierarchy starting with the top-level type.
        /// </summary>
        private void GetInstanceHierarchyForType(ExpandedNodeId typeId, InstanceDeclarationHierarchy hierarchy)
        {
            // the type must be local to the address space.
            ILocalNode type = m_nodes.Find(typeId) as ILocalNode;

            if (type == null)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadNodeIdUnknown,
                          "The type is not in the local address space.\r\nNodeId = {0}",
                          typeId);
            }

            // must be an object or variable type.
            if ((type.NodeClass & (NodeClass.ObjectType | NodeClass.VariableType)) == 0)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadNodeClassInvalid,
                          "The type node is not an ObjectType or VariableType.\r\nNodeId = {0}\r\nNodeClass = {1}",
                          typeId,
                          type.NodeClass);
            }

            // find hierarchy in supertypes first.
            foreach (IReference reference in type.References.Find(ReferenceTypeIds.HasSubtype, true, false, null))
            {
                GetInstanceHierarchyForType(reference.TargetId, hierarchy);
            }

            string browsePath = "/";

            // check if the browse path already exists in the hierarchy.
            HierarchyBrowsePath parent = null;

            if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out parent))
            {
                parent = new HierarchyBrowsePath();

                parent.BrowsePath    = browsePath;
                parent.DeclarationId = type.NodeId;
                parent.InstanceId    = null;
                parent.IsModelParent = true;
                parent.IsOptional    = false;

                // add new browse path to hierarchy.
                hierarchy.BrowsePaths.Add(browsePath, parent);
            }

            // override any declaration specified in a supertype.
            parent.DeclarationId = type.NodeId;
            hierarchy.Declarations[type.NodeId] = parent;

            // follow hierarchial references to nodes with a naming rule of unique or unique optional.
            foreach (IReference reference in type.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree))
            {
                GetInstanceHierarchyForType(parent, reference.TargetId, hierarchy);
            }

            // update references defined in the type.
            foreach (HierarchyBrowsePath declaration in hierarchy.Declarations.Values)
            {
                // the declaration must be local to the address space.
                ILocalNode declarationNode = m_nodes.Find(declaration.DeclarationId) as ILocalNode;

                if (declarationNode == null)
                {
                    continue;
                }

                // process all references.
                foreach (IReference reference in declarationNode.References)
                {
                    UpdateReferenceDeclaration(declaration, reference, hierarchy);
                }
            }
        }
        /// <summary>
        /// Recursively collects the nodes within a type hierarchy.
        /// </summary>
        private void UpdateInstanceHierarchyWithInstance(
            HierarchyBrowsePath parent,
            ExpandedNodeId instanceId,
            InstanceDeclarationHierarchy hierarchy)
        {
            INode instance = m_nodes.Find(instanceId) as INode;

            // ignore instances not in the address space.
            if (instance == null)
            {
                return;
            }

            // must be an object, variable or method.
            if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0)
            {
                return;
            }

            // construct the browse path that identifies the node.
            string browsePath = null;

            if (parent.BrowsePath == "/")
            {
                browsePath = Utils.Format("/{0}", instance.BrowseName);
            }
            else
            {
                browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName);
            }

            // check if the browse path exists in the hierarchy.
            HierarchyBrowsePath child = null;

            if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child))
            {
                return;
            }

            // update the instance.
            child.InstanceId = instance.NodeId;

            // check if already followed.
            if (hierarchy.Instances.ContainsKey((NodeId)instance.NodeId))
            {
                return;
            }

            // save child.
            hierarchy.Instances.Add((NodeId)instance.NodeId, instance);

            // check for local node.
            ILocalNode localInstance = instance as ILocalNode;

            if (localInstance == null)
            {
                return;
            }

            // follow children.
            foreach (IReference reference in localInstance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree))
            {
                UpdateInstanceHierarchyWithInstance(child, reference.TargetId, hierarchy);
            }
        }