private void ProcessIncludes(XmlNode contextElement, SageContext context) { var state = new IncludeProcessingState(context); if (!string.IsNullOrWhiteSpace(this.BaseURI)) { var cacheEntry = state.VisitedNodes[this.BaseURI] = new Dictionary<string, XmlNode>(); cacheEntry["/"] = this.DocumentElement; } this.ProcessIncludes(contextElement, state); }
private void ProcessIncludes(XmlNode contentElement, IncludeProcessingState state) { var nm = XmlNamespaces.Manager.MergeWith(new XmlNamespaceManager(contentElement.OwnerDocument.NameTable)); var includeElements = contentElement.SelectNodes(".//sage:include[not(ancestor::sage:literal)]", XmlNamespaces.Manager); bool debugMode = false; if (state.Context != null) debugMode = state.Context.ProjectConfiguration.IsDebugEnabled; XmlDocument elementDocument = contentElement.OwnerDocument; foreach (XmlElement includingElement in includeElements) { XmlNode fallbackNode = includingElement.SelectSingleNode("sage:fallback", XmlNamespaces.Manager); XmlNode includedNode = this.ProcessInclude(nm, includingElement, state); XmlNode elementParent = includingElement.ParentNode; if (state.IsError) { if (state.IncludeStack.Count == 0) { log.ErrorFormat(state.ErrorMessage); if (debugMode) { var debugElement = this.CreateErrorElement(state.ErrorMessage); elementParent.ReplaceChild(elementDocument.ImportNode(debugElement, true), includingElement); } } else elementParent.RemoveChild(includingElement); } else { if (includedNode == null && fallbackNode != null) { var fragment = elementDocument.CreateDocumentFragment(); foreach (XmlNode childNode in fallbackNode.ChildNodes) fragment.AppendChild(childNode.CloneNode(true)); this.ProcessIncludes(fragment, state); includedNode = fragment; } if (includedNode != null) { elementParent.ReplaceChild(elementDocument.ImportNode(includedNode, true), includingElement); } else { var includeId = this.GetIncludeId(includingElement); var errorMessage = string.Format("NOT FOUND: {0}", includeId); log.ErrorFormat(errorMessage); if (debugMode) { var debugElement = this.CreateErrorElement(errorMessage); elementParent.ReplaceChild(elementDocument.ImportNode(debugElement, true), includingElement); } else elementParent.RemoveChild(includingElement); } } } }
private XmlNode ProcessInclude(XmlNamespaceManager nm, XmlElement element, IncludeProcessingState state) { XmlNode includedNode = null; string href = element.GetAttribute("href"); string xpath = Kelp.Util.GetParameterValue(element.GetAttribute("xpath"), "/"); if (string.IsNullOrWhiteSpace(href) && string.IsNullOrWhiteSpace(xpath)) { state.ErrorMessage = Messages.IncludeElementMissingRequiredAttributes; return null; } var includeId = this.GetIncludeId(element); if (state.IncludeStack.Count >= MaxIncludeDepth) { state.ErrorMessage = string.Format(Messages.MaxIncludeDepthExceeded, includeId, MaxIncludeDepth); return null; } string parse = Kelp.Util.GetParameterValue(element.GetAttribute("parse"), "xml", "^text|xml$"); string encoding = Kelp.Util.GetParameterValue(element.GetAttribute("encoding"), "ascii", "^utf-8|ascii$"); var directory = string.Empty; var includePath = href; if (!string.IsNullOrWhiteSpace(this.BaseURI)) { if (string.IsNullOrWhiteSpace(href)) includePath = this.BaseURI; directory = Path.GetDirectoryName(this.BaseURI); } if (UrlResolver.GetScheme(includePath) == "file") { if (includePath.StartsWith("~/")) includePath = state.Context.Path.Resolve(includePath); if (!Path.IsPathRooted(includePath) && !string.IsNullOrWhiteSpace(directory)) includePath = Path.Combine(directory, includePath); } IDictionary<string, XmlNode> cacheEntry; if (state.VisitedNodes.ContainsKey(includePath)) { cacheEntry = state.VisitedNodes[includePath]; if (cacheEntry.ContainsKey(xpath)) { if (state.IncludeStack.Contains(includeId)) { state.ErrorMessage = string.Format(Messages.IncludeRecursionError, includeId); return null; } } } else { cacheEntry = state.VisitedNodes[includePath] = new Dictionary<string, XmlNode>(); } if (cacheEntry.ContainsKey(xpath)) { includedNode = cacheEntry[xpath]; } else { try { if (string.IsNullOrWhiteSpace(href)) { includedNode = this.ResolveIntraDocumentInclude(nm, parse, element, xpath); if (includedNode.Contains(element)) { // intra-document circular-reference inclusion state.ErrorMessage = string.Format(Messages.IncludeRecursionErrorInternal, includeId); return null; } } else { includedNode = this.ResolveExtraDocumentInclude(nm, parse, includePath, xpath, encoding, state.Context); } if (includedNode != null) { var xmlElement = includedNode as XmlElement; if (xmlElement != null) { state.IncludeStack.Add(includeId); this.ProcessIncludes(xmlElement, state); state.IncludeStack.RemoveAt(state.IncludeStack.Count - 1); } } } catch (Exception ex) { string target = string.IsNullOrWhiteSpace(href) ? xpath : href; state.ErrorMessage = string.Format("Error fetching '{0}': {1}", target, ex.Message); } } return includedNode; }