private void AddDatabaseTargets(string file, ReferenceLinkType type) { try { XPathDocument document = new XPathDocument(file); // This will only load into the memory... TargetCollectionXmlUtilities.AddTargets(_msdnStorage, document.CreateNavigator(), type); } catch (XmlSchemaException e) { WriteMessage(MessageLevel.Error, String.Format( "The reference targets file '{0}' is not valid. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); } catch (XmlException e) { WriteMessage(MessageLevel.Error, String.Format( "The reference targets file '{0}' is not well-formed XML. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); } catch (IOException e) { WriteMessage(MessageLevel.Error, String.Format( "An access error occurred while opening the reference targets file '{0}'. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); } }
private void AddTargets(string file, ReferenceLinkType type) { XPathDocument document = new XPathDocument(file); // This will only load into the memory... TargetCollectionXmlUtilities.AddTargets(_storage, document.CreateNavigator(), type); }
public TargetCollections(MemoryTargetStorage local, DatabaseTargetStorage msdn, ReferenceLinkType localLink, ReferenceLinkType msdnLink) { _localLink = localLink; _msdnLink = msdnLink; _localStorage = local; _msdnStorage = msdn; }
protected virtual void OnReadXml(XmlReader reader) { // This reads only the target node... if (!String.Equals(reader.Name, "Target", StringComparison.OrdinalIgnoreCase)) { return; } id = reader.GetAttribute("id"); container = reader.GetAttribute("container"); file = reader.GetAttribute("file"); string typeText = reader.GetAttribute("type"); if (!String.IsNullOrEmpty(typeText)) { switch (typeText.ToLower()) { case "none": type = ReferenceLinkType.None; break; case "self": type = ReferenceLinkType.Self; break; case "local": type = ReferenceLinkType.Local; break; case "index": type = ReferenceLinkType.Index; break; case "localorindex": type = ReferenceLinkType.LocalOrIndex; break; case "msdn": type = ReferenceLinkType.Msdn; break; case "id": type = ReferenceLinkType.Id; break; } } }
private static EnumerationTarget CreateEnumerationTarget(XPathNavigator api, ReferenceLinkType linkType) { EnumerationTarget enumeration = new EnumerationTarget(); string typeId = (string)api.Evaluate(topicIdExpression); string file = (string)api.Evaluate(topicFileExpression); // Create tar List <MemberTarget> members = new List <MemberTarget>(); XPathNodeIterator elementNodes = api.Select("elements/element"); foreach (XPathNavigator elementNode in elementNodes) { string memberId = elementNode.GetAttribute("api", String.Empty); // try to get name from attribute on element node string memberName = elementNode.GetAttribute("name", String.Empty); if (String.IsNullOrEmpty(memberName)) { // if we can't do that, try to get the name by searching the file for the <api> element of that member XPathNavigator memberApi = api.SelectSingleNode(String.Format( "following-sibling::api[@id='{0}']", memberId)); if (memberApi != null) { memberName = (string)memberApi.Evaluate(apiNameExpression); } else { // if all else fails, get the name by parsing the identifier string arguments; string type; ReferenceTextUtilities.DecomposeMemberIdentifier(memberId, out type, out memberName, out arguments); } } MemberTarget member = new MemberTarget(); member.id = memberId; // get Id from element member.file = file; // get file from type file member.type = linkType; member.name = memberName; // get name from element member.containingType = new SimpleTypeReference(typeId); // get containing type from this type members.Add(member); } enumeration.elements = members; return(enumeration); }
private void AddTargets(string directory, string filePattern, bool recurse, ReferenceLinkType type) { string[] files = Directory.GetFiles(directory, filePattern); foreach (string file in files) { AddTargets(file, type); } if (recurse) { string[] subdirectories = Directory.GetDirectories(directory); foreach (string subdirectory in subdirectories) { AddTargets(subdirectory, filePattern, recurse, type); } } }
/// <summary> /// This attempts to retrieve the item with the specified key, returning it along with the reference /// link type with which it is associated. /// </summary> /// <param name="key">The item key to look up</param> /// <param name="value">On return, this will contain the item value if found</param> /// <param name="linkType">On return, this will contain the link type of the item if found</param> /// <returns>True if the item was found, false if not</returns> public bool TryGetValue(string key, out Target value, out ReferenceLinkType linkType) { value = null; linkType = ReferenceLinkType.None; foreach (var kp in targetDictionaries) { if (kp.Value.TryGetValue(key, out value)) { linkType = kp.Key; return(true); } } return(false); }
// Target factory methods private static Target CreateTarget(XPathNavigator topic, ReferenceLinkType type) { if (topic == null) { throw new ArgumentNullException("topic"); } bool isApiTarget = (bool)topic.Evaluate("boolean(apidata)"); Target target; if (isApiTarget) { target = CreateApiTarget(topic, type); } else { target = new Target(); } if (target == null) { throw new XmlSchemaValidationException(String.Format( "The target file '{0}' is not valid.", topic.BaseURI)); } target.id = (string)topic.Evaluate(topicIdExpression); if (String.IsNullOrEmpty(target.id)) { throw new XmlSchemaValidationException( String.Format("The target file '{0}' is not valid.", topic.BaseURI)); } target.container = (string)topic.Evaluate(topicContainerExpression); target.file = (string)topic.Evaluate(topicFileExpression); if (String.IsNullOrEmpty(target.file)) { throw new XmlSchemaValidationException(String.Format( "The target file '{0}' is not valid.", topic.BaseURI)); } target.type = type; return(target); }
private static Target CreateApiTarget(XPathNavigator api, ReferenceLinkType linkType) { string subGroup = (string)api.Evaluate(apiGroupExpression); if (subGroup == "namespace") { return(CreateNamespaceTarget(api)); } else if (subGroup == "type") { return(CreateTypeTarget(api, linkType)); } else if (subGroup == "member") { return(CreateMemberTarget(api)); } else { return(null); } }
private void AddTargets(string directory, string filePattern, bool recurse, ReferenceLinkType type) { // add the specified targets from the directory WriteMessage(MessageLevel.Info, String.Format( "Searching directory '{0}' for targets files of the form '{1}'.", directory, filePattern)); string[] files = Directory.GetFiles(directory, filePattern); foreach (string file in files) { AddTargets(file, type); } if (recurse) { string[] subdirectories = Directory.GetDirectories(directory); foreach (string subdirectory in subdirectories) { AddTargets(subdirectory, filePattern, recurse, type); } } }
private static TypeTarget CreateTypeTarget(XPathNavigator api, ReferenceLinkType linkType) { string subgroup = (string)api.Evaluate(apiSubgroupExpression); TypeTarget target; if (subgroup == "enumeration") { target = CreateEnumerationTarget(api, linkType); } else { target = new TypeTarget(); } target.name = (string)api.Evaluate(apiNameExpression); // containing namespace XPathNavigator namespaceNode = api.SelectSingleNode(apiContainingNamespaceExpression); target.containingNamespace = CreateNamespaceReference(namespaceNode); // containing type, if any XPathNavigator typeNode = api.SelectSingleNode(apiContainingTypeExpression); if (typeNode == null) { target.containingType = null; } else { target.containingType = CreateSimpleTypeReference(typeNode); } // templates target.templates = GetTemplateNames(api); return(target); }
public TargetCollections() { _localLink = ReferenceLinkType.None; _msdnLink = ReferenceLinkType.None; }
/// <summary> /// Add a target type dictionary to the collection /// </summary> /// <param name="type">The reference link type to use for targets in the given dictionary</param> /// <param name="dictionary">The target dictionary to add</param> public void Add(ReferenceLinkType type, TargetDictionary dictionary) { // Dictionaries are stored in reverse order to allow entries from later dictionaries to override // one from earlier dictionaries targetDictionaries.Insert(0, new KeyValuePair <ReferenceLinkType, TargetDictionary>(type, dictionary)); }
public TargetCollection GetCollection(XPathNavigator configuration, out ReferenceLinkType localLink, out ReferenceLinkType msdnLink) { localLink = ReferenceLinkType.None; msdnLink = ReferenceLinkType.None; XPathNodeIterator targetsNodes = configuration.Select("targets"); if (targetsNodes == null || targetsNodes.Count == 0) { return(new TargetCollections()); } foreach (XPathNavigator targetsNode in targetsNodes) { // get target type string typeValue = targetsNode.GetAttribute("type", String.Empty); if (String.IsNullOrEmpty(typeValue)) { WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute that specifies which type of links to create."); } ReferenceLinkType linkType = ReferenceLinkType.None; try { linkType = (ReferenceLinkType)Enum.Parse(typeof(ReferenceLinkType), typeValue, true); } catch (ArgumentException) { WriteMessage(MessageLevel.Error, String.Format( "'{0}' is not a supported reference link type.", typeValue)); } // get base directory string baseValue = targetsNode.GetAttribute("base", String.Empty); // get file pattern string filesValue = targetsNode.GetAttribute("files", String.Empty); if (String.IsNullOrEmpty(filesValue)) { WriteMessage(MessageLevel.Error, "Each targets element must have a files attribute specifying which target files to load."); } // determine whether to search recursively bool recurse = false; string recurseValue = targetsNode.GetAttribute("recurse", String.Empty); if (!String.IsNullOrEmpty(recurseValue)) { if (String.Compare(recurseValue, Boolean.TrueString, true) == 0) { recurse = true; } else if (String.Compare(recurseValue, Boolean.FalseString, true) == 0) { recurse = false; } else { WriteMessage(MessageLevel.Error, String.Format( "On the targets element, recurse='{0}' is not an allowed value.", recurseValue)); } } // turn baseValue and filesValue into directoryPath and filePattern string fullPath; if (String.IsNullOrEmpty(baseValue)) { fullPath = filesValue; } else { fullPath = Path.Combine(baseValue, filesValue); } fullPath = Environment.ExpandEnvironmentVariables(fullPath); string directoryPath = Path.GetDirectoryName(fullPath); if (String.IsNullOrEmpty(directoryPath)) { directoryPath = Environment.CurrentDirectory; } string filePattern = Path.GetFileName(fullPath); bool isSystem = false; // verify that directory exists if (!Directory.Exists(directoryPath)) { WriteMessage(MessageLevel.Error, String.Format( "The targets directory '{0}' does not exist.", directoryPath)); } else { if (directoryPath.EndsWith(@"Sandcastle\Data\Reflection", StringComparison.OrdinalIgnoreCase)) { isSystem = true; if (msdnLink == ReferenceLinkType.None) { msdnLink = linkType; } } else { if (localLink == ReferenceLinkType.None) { localLink = linkType; } } } if (!_isDataLoaded) { if (isSystem) { if (!_msdnStorage.Exists) { AddTargets(directoryPath, filePattern, recurse, linkType); } } else { AddTargets(directoryPath, filePattern, recurse, linkType); } } } if (!_isDataLoaded) { _isDataLoaded = true; } return(new TargetCollections(_localStorage, _msdnStorage, localLink, msdnLink)); }
protected TargetStorage(ReferenceLinkType linkType) { _linkType = linkType; }
public TargetCollection GetCollection(XPathNavigator configuration, out ReferenceLinkType localLink, out ReferenceLinkType msdnLink) { localLink = ReferenceLinkType.None; msdnLink = ReferenceLinkType.None; XPathNodeIterator targetsNodes = configuration.Select("targets"); if (targetsNodes == null || targetsNodes.Count == 0) { return(new TargetCollections()); } foreach (XPathNavigator targetsNode in targetsNodes) { // get target type string typeValue = targetsNode.GetAttribute("type", String.Empty); if (String.IsNullOrEmpty(typeValue)) { WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute that specifies which type of links to create."); } ReferenceLinkType linkType = ReferenceLinkType.None; try { linkType = (ReferenceLinkType)Enum.Parse(typeof(ReferenceLinkType), typeValue, true); } catch (ArgumentException) { WriteMessage(MessageLevel.Error, String.Format( "'{0}' is not a supported reference link type.", typeValue)); } // get base directory string baseValue = targetsNode.GetAttribute("base", String.Empty); // get file pattern string filesValue = targetsNode.GetAttribute("files", String.Empty); if (String.IsNullOrEmpty(filesValue)) { WriteMessage(MessageLevel.Error, "Each targets element must have a files attribute specifying which target files to load."); } // determine whether to search recursively bool recurse = false; string recurseValue = targetsNode.GetAttribute("recurse", String.Empty); if (!String.IsNullOrEmpty(recurseValue)) { if (String.Compare(recurseValue, Boolean.TrueString, true) == 0) { recurse = true; } else if (String.Compare(recurseValue, Boolean.FalseString, true) == 0) { recurse = false; } else { WriteMessage(MessageLevel.Error, String.Format( "On the targets element, recurse='{0}' is not an allowed value.", recurseValue)); } } // turn baseValue and filesValue into directoryPath and filePattern string fullPath; if (String.IsNullOrEmpty(baseValue)) { fullPath = filesValue; } else { fullPath = Path.Combine(baseValue, filesValue); } fullPath = Environment.ExpandEnvironmentVariables(fullPath); string directoryPath = Path.GetDirectoryName(fullPath); if (String.IsNullOrEmpty(directoryPath)) { directoryPath = Environment.CurrentDirectory; } string filePattern = Path.GetFileName(fullPath); bool isSystem = false; DataSource dataSource = null; // Verify that directory exists... if (!Directory.Exists(directoryPath)) { WriteMessage(MessageLevel.Error, String.Format( "The targets directory '{0}' does not exist.", directoryPath)); } else { bool systemIsFound = false; string systemValue = targetsNode.GetAttribute("system", String.Empty); if (!String.IsNullOrEmpty(systemValue)) { if (String.Compare(systemValue, Boolean.TrueString, true) == 0) { isSystem = true; systemIsFound = true; if (msdnLink == ReferenceLinkType.None) { msdnLink = linkType; } } else if (String.Compare(systemValue, Boolean.FalseString, true) == 0) { isSystem = false; systemIsFound = true; if (localLink == ReferenceLinkType.None && linkType != ReferenceLinkType.Msdn) { localLink = linkType; } } else { WriteMessage(MessageLevel.Error, String.Format( "On the targets element, system='{0}' is not an allowed value.", systemValue)); } } // Retrieve the data source, if any... XPathNavigator nodeDataSource = targetsNode.SelectSingleNode("source"); if (nodeDataSource != null) { dataSource = new DataSource(false, nodeDataSource); if (dataSource.IsValid) { // Currently, database is supported for systems only... if (!dataSource.IsSystem && !dataSource.IsDatabase) { dataSource = null; } else { if (String.IsNullOrEmpty(systemValue)) { isSystem = dataSource.IsSystem; systemIsFound = true; } } } else { dataSource = null; } } if (String.IsNullOrEmpty(systemValue) && !systemIsFound) { // Try doing it generally... if (directoryPath.IndexOf(@"Sandcastle\Data\Reflection", StringComparison.OrdinalIgnoreCase) >= 0) { isSystem = true; if (msdnLink == ReferenceLinkType.None) { msdnLink = linkType; } } else { if (localLink == ReferenceLinkType.None && linkType != ReferenceLinkType.Msdn) { localLink = linkType; } } } } // If the database is not yet loaded, we load all... if (!_isDataLoaded) { if (isSystem || (dataSource != null && dataSource.IsSystem)) { if (dataSource != null && dataSource.Exists) { if (_msdnStorage.IsInitialize) { DatabaseTargetStorageEx databaseStorage = new DatabaseTargetStorageEx(true, false, dataSource.OutputDir); _msdnStorage.AddStorage(databaseStorage); } else { _msdnStorage.Initialize(dataSource.OutputDir, false); } } else { if (!_msdnStorage.Exists) { if (!_msdnStorage.IsInitialize) { if (Directory.Exists(directoryPath)) { _msdnStorage.Initialize(directoryPath, false); } } this.AddDatabaseTargets(directoryPath, filePattern, recurse, linkType); } } } else { if (linkType == ReferenceLinkType.Msdn) { if (!_msdnStorage.IsInitialize) { if (Directory.Exists(directoryPath)) { _msdnStorage.Initialize(directoryPath, false); } } this.AddDatabaseTargets(directoryPath, filePattern, recurse, linkType); } else { this.AddTargets(directoryPath, filePattern, recurse, linkType); } } } } if (!_isDataLoaded) { _isDataLoaded = true; } return(new TargetCollections(_localStorage, _msdnStorage, localLink, msdnLink)); }
// super factory method public static void AddTargets(TargetStorage storage, XPathNavigator topicsNode, ReferenceLinkType type) { XPathNodeIterator topicNodes = topicsNode.Select("/*/apis/api[not(topicdata/@notopic)]"); foreach (XPathNavigator topicNode in topicNodes) { Target target = CreateTarget(topicNode, type); if (target != null) { target.Add(storage); } } }
private void ProcessReferenceLink(XmlDocument document, string key) { XPathNodeIterator linkIterator = document.CreateNavigator().Select( referenceLinkExpression); if (linkIterator == null || linkIterator.Count == 0) { return; } XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(linkIterator); foreach (XPathNavigator linkNode in linkNodes) { // extract link information ReferenceLinkInfo link = ReferenceLinkInfo.Create(linkNode); if (link == null) { this.WriteMessage(MessageLevel.Warn, "Invalid referenceLink element."); #if DEBUG this.WriteMessage(MessageLevel.Warn, linkNode.OuterXml); #endif } else { // determine target, link type, and display options string targetId = link.Target; ReferenceLinkDisplayOptions options = link.DisplayOptions; ReferenceLinkType type = ReferenceLinkType.None; Target target = _targets[targetId]; if (target == null) { if (_hasTopicLinks && _targetController[targetId] != null) { this.ProcessConceptualLink(linkNode, key); // delete the original tag linkNode.DeleteSelf(); continue; } else { // no such target known; set link type to none and warn type = ReferenceLinkType.None; this.WriteMessage(MessageLevel.Warn, String.Format( "Unknown reference link target '{0}'.", targetId)); } } else { // if overload is preferred and found, change targetId and make link options hide parameters if (link.PreferOverload) { bool isConversionOperator = false; TargetType targetType = target.TargetType; MemberTarget member = null; if (targetType == TargetType.Method) { MethodTarget method = (MethodTarget)target; isConversionOperator = method.ConversionOperator; member = method; // a method is a member... } else if (targetType == TargetType.Member || targetType == TargetType.Constructor || targetType == TargetType.Procedure || targetType == TargetType.Event || targetType == TargetType.Property) { member = (MemberTarget)target; } // if conversion operator is found, always link to individual topic. if ((member != null) && (!String.IsNullOrEmpty(member.OverloadId)) && !isConversionOperator) { Target overloadTarget = _targets[member.OverloadId]; if (overloadTarget != null) { target = overloadTarget; targetId = overloadTarget.Id; } } // if individual conversion operator is found, always display parameters. if (isConversionOperator && member != null && (!string.IsNullOrEmpty(member.OverloadId))) { options = options | ReferenceLinkDisplayOptions.ShowParameters; } else { options = options & ~ReferenceLinkDisplayOptions.ShowParameters; } } // get stored link type type = _targets.RecentLinkTypeIsMsdn ? _targets.RecentLinkType : target.LinkType; // if link type is local or index, determine which if (type == ReferenceLinkType.LocalOrIndex) { if ((key != null) && _targets.Contains(key) && (target.Container == _targets[key].Container)) { type = ReferenceLinkType.Local; } else { type = ReferenceLinkType.Index; } } } // links to this page are not live if (targetId == key) { type = ReferenceLinkType.Self; } else if ((target != null) && (key != null) && _targets.Contains(key) && (target.File == _targets[key].File)) { type = ReferenceLinkType.Self; } // get msdn endpoint, if needed string msdnUrl = null; if (type == ReferenceLinkType.Msdn) { if ((_msdnResolver == null) || (_msdnResolver.IsDisabled)) { // no msdn resolver } else { msdnUrl = _msdnResolver[targetId]; if (String.IsNullOrEmpty(msdnUrl)) { WriteMessage(MessageLevel.Warn, String.Format( "MSDN URL not found for target '{0}'.", targetId)); } } if (String.IsNullOrEmpty(msdnUrl)) { type = ReferenceLinkType.None; } } // write opening link tag and target info XmlWriter writer = linkNode.InsertAfter(); switch (type) { case ReferenceLinkType.None: writer.WriteStartElement("span"); writer.WriteAttributeString("class", "nolink"); break; case ReferenceLinkType.Self: writer.WriteStartElement("span"); writer.WriteAttributeString("class", "selflink"); break; case ReferenceLinkType.Local: // format link with prefix and/or postfix string href = String.Format(_hrefFormat, target.File); // make link relative, if we have a baseUrl if (_baseUrl != null) { href = BuildComponentUtilities.GetRelativePath(href, BuildComponentUtilities.EvalXPathExpr(document, _baseUrl, "key", key)); } writer.WriteStartElement("a"); writer.WriteAttributeString("href", href); break; case ReferenceLinkType.Index: writer.WriteStartElement("mshelp", "link", "http://msdn.microsoft.com/mshelp"); writer.WriteAttributeString("keywords", targetId); writer.WriteAttributeString("tabindex", "0"); break; case ReferenceLinkType.Msdn: writer.WriteStartElement("a"); writer.WriteAttributeString("href", msdnUrl); writer.WriteAttributeString("target", _linkTarget); break; case ReferenceLinkType.Id: string xhelp = String.Format("ms-xhelp://?Id={0}", targetId); xhelp = xhelp.Replace("#", "%23"); writer.WriteStartElement("a"); writer.WriteAttributeString("href", xhelp); break; } // write the link text if (String.IsNullOrEmpty(link.DisplayTarget)) { if (link.Contents == null) { if (target != null) { _linkResolver.WriteTarget(target, options, writer); } else { Reference reference = ReferenceTextUtilities.CreateReference(targetId); if (reference.ReferenceType == ReferenceType.Invalid) { WriteMessage(MessageLevel.Warn, String.Format( "Invalid reference link target '{0}'.", targetId)); } _linkResolver.WriteReference(reference, options, writer); } } else { // write contents to writer link.Contents.WriteSubtree(writer); } } else { if ((String.Compare(link.DisplayTarget, "content", true) == 0) && (link.Contents != null)) { // Use the contents as an XML representation of the display target Reference reference = TargetCollectionXmlUtilities.CreateReference(link.Contents); _linkResolver.WriteReference(reference, options, writer); } if ((String.Compare(link.DisplayTarget, "format", true) == 0) && (link.Contents != null)) { // Use the contents as a format string for the display target string format = link.Contents.OuterXml; string input = null; StringWriter textStore = new StringWriter(); try { XmlWriter xmlStore = XmlWriter.Create(textStore, _writerSettings); try { if (target != null) { _linkResolver.WriteTarget(target, options, xmlStore); } else { Reference reference = ReferenceTextUtilities.CreateReference(targetId); _linkResolver.WriteReference(reference, options, xmlStore); } } finally { xmlStore.Close(); } input = textStore.ToString(); } finally { textStore.Close(); } string output = String.Format(format, input); XmlDocumentFragment fragment = document.CreateDocumentFragment(); fragment.InnerXml = output; fragment.WriteTo(writer); //writer.WriteRaw(output); } else if ((String.Compare(link.DisplayTarget, "extension", true) == 0) && (link.Contents != null)) { Reference extMethodReference = TargetCollectionXmlUtilities.CreateExtensionMethodReference(link.Contents); _linkResolver.WriteReference(extMethodReference, options, writer); } else { // Use the display target value as a CER for the display target ReferenceTextUtilities.SetGenericContext(key); Reference reference = ReferenceTextUtilities.CreateReference(link.DisplayTarget); _linkResolver.WriteReference(reference, options, writer); } } // write the closing link tag writer.WriteEndElement(); writer.Close(); } // delete the original tag linkNode.DeleteSelf(); } }
/// <inheritdoc /> public override void Apply(XmlDocument document, string key) { Target target = null, keyTarget; string msdnUrl = null; foreach (XPathNavigator linkNode in document.CreateNavigator().Select(referenceLinkExpression).ToArray()) { // Extract link information ReferenceLinkInfo link = new ReferenceLinkInfo(linkNode); // Determine target, link type, and display options string targetId = link.Target; DisplayOptions options = link.DisplayOptions; ReferenceLinkType type = ReferenceLinkType.None; if (String.IsNullOrWhiteSpace(targetId)) { this.WriteMessage(key, MessageLevel.Warn, "The target attribute is missing or has no " + "value. You have most likely omitted a cref attribute or left it blank on an XML " + "comments element such as see, seealso, or exception."); continue; } bool targetFound = targets.TryGetValue(targetId, out target, out type); // If it's an overload ID that wasn't found, it's possible that the overloads got excluded. // As such, see if we can find a match for a method using the same ID regardless of any // parameters. if (!targetFound && targetId.StartsWith("Overload:", StringComparison.Ordinal) || targetId.StartsWith("O:", StringComparison.Ordinal)) { string methodTargetId = "M:" + targetId.Substring(targetId.IndexOf(':') + 1); methodTargetId = targets.Keys.FirstOrDefault(k => k.StartsWith(methodTargetId)); if (methodTargetId != null) { targetId = methodTargetId; targetFound = targets.TryGetValue(targetId, out target, out type); options |= DisplayOptions.ShowParameters; // Don't use the content as it may not be appropriate for the method. The default is // "{0} Overload" which no longer applies. Instead we'll show the method name and // its parameters. if (link.DisplayTarget.Equals("format", StringComparison.OrdinalIgnoreCase)) { link.DisplayTarget = null; link.Contents = null; } } } // If not found and it starts with "System." or "Microsoft." we'll go with the assumption that // it's part of a Microsoft class library that is not part of the core framework but does have // documentation available on MSDN. Worst case it doesn't and we get an unresolved link warning // instead of an unknown reference target warning. if (!targetFound && ((targetId.Length > 9 && targetId.Substring(2).StartsWith("System.", StringComparison.Ordinal)) || (targetId.Length > 12 && targetId.Substring(2).StartsWith( "Microsoft.", StringComparison.Ordinal)))) { // Use the same link type as a core framework class targetFound = targets.TryGetValue("T:System.Object", out target, out type); // We don't have a target in this case so links to overloads pages won't work. Also note // that the link text will be generated from the ID which shouldn't make much of a difference // in most cases. If either case is an issue, the Additional Reference Links SHFB plug-in // can be used to generate valid link target data. target = null; } if (!targetFound) { // If not being rendered as a link, don't report a warning if (link.RenderAsLink && targetId != key) { this.WriteMessage(key, MessageLevel.Warn, "Unknown reference link target '{0}'.", targetId); } // !EFW - Turn off the Show Parameters option for unresolved elements except methods. If // not, it outputs an empty "()" after the member name which looks odd. if (targetId[0] != 'M') { options &= ~DisplayOptions.ShowParameters; } } else { // If overload is preferred and found, change targetId and make link options hide parameters if (link.PreferOverload && target != null) { bool isConversionOperator = false; MethodTarget method = target as MethodTarget; if (method != null) { isConversionOperator = method.IsConversionOperator; } MemberTarget member = target as MemberTarget; // If conversion operator is found, always link to individual topic if (member != null && !String.IsNullOrEmpty(member.OverloadId) && !isConversionOperator) { Target overloadTarget = targets[member.OverloadId]; if (overloadTarget != null) { target = overloadTarget; targetId = overloadTarget.Id; } } // If individual conversion operator is found, always display parameters if (isConversionOperator && member != null && !String.IsNullOrEmpty(member.OverloadId)) { options = options | DisplayOptions.ShowParameters; } else { options = options & ~DisplayOptions.ShowParameters; } } } // Suppress the link if so requested. Links to this page are not live. if (!link.RenderAsLink) { type = ReferenceLinkType.None; } else if (targetId == key) { type = ReferenceLinkType.Self; } else if (target != null && targets.TryGetValue(key, out keyTarget) && target.File == keyTarget.File) { type = ReferenceLinkType.Self; } // !EFW - Redirect enumeration fields to the containing enumerated type so that we // get a valid link target. Enum fields don't have a topic to themselves. if (type != ReferenceLinkType.None && type != ReferenceLinkType.Self && type != ReferenceLinkType.Local && targetId.StartsWith("F:", StringComparison.OrdinalIgnoreCase)) { MemberTarget member = target as MemberTarget; if (member != null) { SimpleTypeReference typeRef = member.ContainingType as SimpleTypeReference; if (typeRef != null && targets[typeRef.Id] is EnumerationTarget) { targetId = typeRef.Id; } } } // Get MSDN endpoint if needed if (type == ReferenceLinkType.Msdn) { if (msdnResolver != null && !msdnResolver.IsDisabled) { msdnUrl = msdnResolver.GetMsdnUrl(targetId); if (String.IsNullOrEmpty(msdnUrl)) { // If the web service failed, report the reason if (msdnResolver.IsDisabled) { this.WriteMessage(key, MessageLevel.Warn, "MSDN web service failed. No " + "further look ups will be performed for this build.\r\nReason: {0}", msdnResolver.DisabledReason); } else { this.WriteMessage(key, MessageLevel.Warn, "MSDN URL not found for target '{0}'.", targetId); } type = ReferenceLinkType.None; } } else { type = ReferenceLinkType.None; } } // Write opening link tag and target info XmlWriter writer = linkNode.InsertAfter(); switch (type) { case ReferenceLinkType.None: writer.WriteStartElement("span"); // If the link was intentionally suppressed, write it out as an identifier (i.e. links // in the syntax section). if (link.RenderAsLink) { writer.WriteAttributeString("class", "nolink"); } else { writer.WriteAttributeString("class", "identifier"); } break; case ReferenceLinkType.Self: writer.WriteStartElement("span"); writer.WriteAttributeString("class", "selflink"); break; case ReferenceLinkType.Local: // Format link with prefix and/or postfix string href = String.Format(CultureInfo.InvariantCulture, hrefFormat, target.File); // Make link relative, if we have a baseUrl if (baseUrl != null) { href = href.GetRelativePath(document.EvalXPathExpr(baseUrl, "key", key)); } writer.WriteStartElement("a"); writer.WriteAttributeString("href", href); break; case ReferenceLinkType.Msdn: writer.WriteStartElement("a"); writer.WriteAttributeString("href", msdnUrl); writer.WriteAttributeString("target", linkTarget); break; case ReferenceLinkType.Id: writer.WriteStartElement("a"); writer.WriteAttributeString("href", ("ms-xhelp:///?Id=" + targetId).Replace("#", "%23")); break; } // Write the link text if (String.IsNullOrEmpty(link.DisplayTarget)) { if (link.Contents == null) { if (target != null) { resolver.WriteTarget(target, options, writer); } else { Reference reference = TextReferenceUtilities.CreateReference(targetId); if (reference is InvalidReference) { this.WriteMessage(key, MessageLevel.Warn, "Invalid reference link target '{0}'.", targetId); } resolver.WriteReference(reference, options, writer); } } else { do { link.Contents.WriteSubtree(writer); } while(link.Contents.MoveToNext()); } } else { if (link.DisplayTarget.Equals("content", StringComparison.OrdinalIgnoreCase) && link.Contents != null) { // Use the contents as an XML representation of the display target Reference reference = XmlTargetDictionaryUtilities.CreateReference(link.Contents); resolver.WriteReference(reference, options, writer); } if (link.DisplayTarget.Equals("format", StringComparison.OrdinalIgnoreCase) && link.Contents != null) { // Use the contents as a format string for the display target string format = link.Contents.OuterXml; string input = null; using (StringWriter textStore = new StringWriter(CultureInfo.InvariantCulture)) { XmlWriterSettings settings = new XmlWriterSettings(); settings.ConformanceLevel = ConformanceLevel.Fragment; using (XmlWriter xmlStore = XmlWriter.Create(textStore, settings)) { if (target != null) { resolver.WriteTarget(target, options, xmlStore); } else { Reference reference = TextReferenceUtilities.CreateReference(targetId); resolver.WriteReference(reference, options, xmlStore); } } input = textStore.ToString(); } string output = String.Format(CultureInfo.InvariantCulture, format, input); XmlDocumentFragment fragment = document.CreateDocumentFragment(); fragment.InnerXml = output; fragment.WriteTo(writer); } else if (link.DisplayTarget.Equals("extension", StringComparison.OrdinalIgnoreCase) && link.Contents != null) { Reference extMethodReference = XmlTargetDictionaryUtilities.CreateExtensionMethodReference(link.Contents); resolver.WriteReference(extMethodReference, options, writer); } else { // Use the display target value as a CER for the display target TextReferenceUtilities.SetGenericContext(key); Reference reference = TextReferenceUtilities.CreateReference(link.DisplayTarget); resolver.WriteReference(reference, options, writer); } } // Write the closing link tag writer.WriteEndElement(); writer.Close(); // Delete the original tag linkNode.DeleteSelf(); } }
private void ParseReferenceConfiguration(XPathNavigator configuration) { TargetController controller = TargetController.Controller; Debug.Assert(controller != null); controller.Initialize(this); ReferenceLinkType localLink = ReferenceLinkType.None; ReferenceLinkType msdnLink = ReferenceLinkType.None; _targets = controller.GetCollection(configuration, out localLink, out msdnLink); Debug.Assert(_targets != null); _linkResolver = new ReferenceLinkTextResolver(_targets); if (msdnLink == ReferenceLinkType.Msdn && _msdnResolver == null) { XPathNavigator linkResolverNode = configuration.SelectSingleNode("linkResolver"); if (linkResolverNode != null) { WriteMessage(MessageLevel.Info, "Creating cached MSDN URL resolver."); _msdnResolver = TargetMsdnController.GetController(linkResolverNode); } else { WriteMessage(MessageLevel.Info, "Creating MSDN URL resolver."); _msdnResolver = TargetMsdnController.GetController(); } } XPathNavigator optionsNode = configuration.SelectSingleNode("options"); if (optionsNode == null) { optionsNode = configuration; } // base-url is an xpath expression applied against the current document to pick up the save location of the // document. If specified, local links will be made relative to the base-url. string baseUrlValue = optionsNode.GetAttribute("base-url", String.Empty); if (!String.IsNullOrEmpty(baseUrlValue)) { _baseUrl = XPathExpression.Compile(baseUrlValue); } // url-format is a string format that is used to format the value of local href attributes. The default is // "{0}.htm" for backwards compatibility. string hrefFormatValue = optionsNode.GetAttribute("href-format", String.Empty); if (!String.IsNullOrEmpty(hrefFormatValue)) { _hrefFormat = hrefFormatValue; } // the container XPath can be replaced; this is useful string containerValue = optionsNode.GetAttribute("container", String.Empty); if (!String.IsNullOrEmpty(containerValue)) { TargetCollectionXmlUtilities.ContainerExpression = containerValue; } string localeValue = optionsNode.GetAttribute("locale", String.Empty); if (!String.IsNullOrEmpty(localeValue) && _msdnResolver != null) { _msdnResolver.Locale = localeValue; } string versionValue = optionsNode.GetAttribute("version", String.Empty); if (!String.IsNullOrEmpty(versionValue) && _msdnResolver != null) { _msdnResolver.Version = versionValue; } string mvcVersionValue = optionsNode.GetAttribute("mvcVersion", String.Empty); if (!String.IsNullOrEmpty(mvcVersionValue) && _msdnResolver != null) { _msdnResolver.MvcVersion = mvcVersionValue; } string targetValue = optionsNode.GetAttribute("linkTarget", String.Empty); if (!String.IsNullOrEmpty(targetValue)) { _linkTarget = targetValue; } this.WriteMessage(MessageLevel.Info, String.Format( "Loaded {0} reference targets.", _targets.Count)); _writerSettings = new XmlWriterSettings(); _writerSettings.ConformanceLevel = ConformanceLevel.Fragment; _hasMsdnStorage = controller.HasMsdnStorage; }