private static IEnumerable <OpenXmlElement> ValidatingTraverse(OpenXmlElement inElement, MCContext mcContext, FileFormatVersions version) { var stack = new Stack <OpenXmlElement>(); stack.Push(inElement); while (stack.Count > 0) { var element = stack.Pop(); if (element is null) { mcContext.PopMCAttributes2(); continue; } // 1. Skip elements in ProcessContent. // 2. Select correct Choice / Fallback // Need bookkeep MC context // Need to collect MC context from ancestor // bookkeep MC context, // MC Spec: Compatibility-rule attributes shall affect the element to which they 1 are attached, including the element’s other attributes and contents. mcContext.PushMCAttributes2(element.MCAttributes, element.LookupNamespace); stack.Push(null); if (element.IsStrongTypedElement()) { // only call validate on elements that defined in the format. if (version.AtLeast(element.InitialVersion)) { yield return(element); } else if (mcContext.IsProcessContent(element)) { // do not validate this element. } foreach (var child in element.ChildElements) { stack.Push(child); } } else if (element.IsUnknown()) { // TODO: Issue: all types are weak types now, need to change the Framework to load strong typed elements!!! if (mcContext.IsProcessContent(element)) { // do validating on children elements. foreach (var child in element.ChildElements) { stack.Push(child); } } } else if (element.IsAlternateContent()) { yield return(element); var selectedContent = mcContext.GetContentFromACBlock((AlternateContent)element, version); if (selectedContent is not null) { foreach (var child in selectedContent.ChildElements) { stack.Push(child); } } } else if (element.IsMiscNode()) { // non-element node // just skip } else { Debug.Assert(element is AlternateContentChoice || element is AlternateContentFallback); Debug.Assert(element.Parent is not null && element.Parent is AlternateContent); // should not be here, otherwise, wrong case ( the parent is not AlternateContent). } } }
private static OpenXmlElement GetChildMc(this OpenXmlElement parent, OpenXmlElement child, MCContext mcContext, FileFormatVersions format) { // Use stack to cache the next siblings in different levels. Stack <OpenXmlElement> nextSiblings = new Stack <OpenXmlElement>(); while (child != null) { var acb = child as AlternateContent; if (acb == null && child.IsInVersion(format)) { return(child); } else { mcContext.PushMCAttributes2(child.MCAttributes, child.LookupNamespace); if (acb != null) { nextSiblings.Push(child.GetNextNonMiscElementSibling()); var select = mcContext.GetContentFromACBlock(acb, format); if (select != null) { child = select.GetFirstNonMiscElementChild(); } else { // The ACB has no children elements. // case like: <acb/> <acb><choice/><fallback/></acb> child = null; } } else { // Ignorable element, skip it if (mcContext.IsIgnorableNs(child.NamespaceUri)) { // Any element marked with ProcessContent should be an Ignorable Element if (mcContext.IsProcessContent(child)) { nextSiblings.Push(child.GetNextNonMiscElementSibling()); // child = child.GetFirstNonMiscElementChild(); } else { child = child.GetNextNonMiscElementSibling(); } } else { mcContext.PopMCAttributes2(); return(child); } } mcContext.PopMCAttributes2(); } while (child == null && nextSiblings.Count > 0) { child = nextSiblings.Pop(); } } // child is null. return(child); }