/// <summary> /// Expands a node into child nodes. /// </summary> /// <param name="node">The node to expand</param> /// <param name="model">The ODCM model</param> /// <returns>The child nodes if the node can be expanded, otherwise an empty list.</returns> private static IEnumerable <OdcmNode> CreateChildNodes(this OdcmNode node, OdcmModel model) { if (node == null) { throw new ArgumentNullException(nameof(node)); } if (model == null) { throw new ArgumentNullException(nameof(model)); } // Identify the kind of ODCM element this node represents and expand it if we need to OdcmProperty obj = node.OdcmProperty; IEnumerable <OdcmProperty> childObjects = obj.Type.GetChildObjects(model); // Don't allow loops in the list of ODCM nodes ICollection <string> parents = new HashSet <string>(); OdcmNode currentNode = node; while (currentNode != null) { parents.Add(currentNode.OdcmProperty.CanonicalName()); currentNode = currentNode.Parent; } return(childObjects // Filter out the children we've already seen so we can avoid loops .Where(child => !parents.Contains(child.CanonicalName())) // Add the child nodes to the current node .Select(child => node.CreateChildNode(child))); }
/// <summary> /// Converts the structure of an ODCM model into a tree and returns all of the nodes in the tree. /// The first node returned is guaranteed to be the root of the tree, however the order of the remaining nodes is undetermined. /// </summary> /// <param name="obj">The ODCM model to convert</param> /// <returns>The root node of the created tree.</returns> public static IEnumerable <OdcmNode> ConvertToOdcmNodes(this OdcmModel model) { if (model == null) { throw new ArgumentNullException(nameof(model)); } // Create a stack to allow us to traverse the model Stack <OdcmNode> unvisited = new Stack <OdcmNode>(); // The EntityContainer in the model represents all of the data exposed through the OData service OdcmClass entityContainer = model.EntityContainer; // Evaluate the EntityContainer's children foreach (OdcmProperty prop in entityContainer.GetChildObjects(model)) { // Mark the EntityContainer's properties as "to be expanded" if it is not a "$ref" property unvisited.Push(new OdcmNode(prop)); } // Continue adding to the tree until there are no more nodes to expand while (unvisited.Any()) { // Get the next node to expand OdcmNode currentNode = unvisited.Pop(); // Return the visited node so it can be processed immediately yield return(currentNode); // Mark the child nodes as "to be expanded" if we haven't hit the maximum traversal depth and it is not a reference property int currentDepth = new ODataRoute(currentNode).Segments.Count; if (currentDepth <= MaxTraversalDepth && !currentNode.OdcmProperty.IsReference(currentNode.Parent?.OdcmProperty)) { // Expand the node IEnumerable <OdcmNode> childNodes = currentNode.CreateChildNodes(model); // Mark the child nodes as "to be expanded" foreach (OdcmNode childNode in childNodes) { unvisited.Push(childNode); } } } }