Esempio n. 1
0
 private TocItemInfo ResolveItem(TocItemInfo wrapper, Stack<FileAndType> stack)
 {
     using (new LoggerFileScope(wrapper.File.File))
     {
         return ResolveItemCore(wrapper, stack);
     }
 }
Esempio n. 2
0
 private TocItemInfo ResolveItem(TocItemInfo wrapper, Stack <FileAndType> stack, bool isRoot = true)
 {
     using (new LoggerFileScope(wrapper.File.File))
     {
         return(ResolveItemCore(wrapper, stack, isRoot));
     }
 }
Esempio n. 3
0
        private TocItemViewModel GetReferencedToc(FileAndType tocFile, Stack <FileAndType> stack)
        {
            if (_collection.TryGetValue(tocFile.FullPath, out TocItemInfo referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel))
            {
                referencedTocFileModel = ResolveItem(referencedTocFileModel, stack);
                referencedTocFileModel.IsReferenceToc = true;
                return(referencedTocFileModel.Content);
            }
            else
            {
                // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally
                TocItemViewModel referencedTocItemViewModel;
                try
                {
                    referencedTocItemViewModel = TocHelper.LoadSingleToc(tocFile.FullPath);
                }
                catch (FileNotFoundException)
                {
                    Logger.LogError($"Referenced TOC file {tocFile.FullPath} does not exist.", code: WarningCodes.Build.InvalidTocInclude);
                    return(null);
                }

                referencedTocFileModel = new TocItemInfo(tocFile, referencedTocItemViewModel);

                referencedTocFileModel         = ResolveItem(referencedTocFileModel, stack);
                _notInProjectTocCache[tocFile] = referencedTocFileModel;
                return(referencedTocFileModel.Content);
            }
        }
Esempio n. 4
0
        private TocItemViewModel GetReferencedToc(FileAndType tocFile, Stack <FileAndType> stack)
        {
            if (_collection.TryGetValue(tocFile.FullPath, out TocItemInfo referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel))
            {
                referencedTocFileModel = ResolveItem(referencedTocFileModel, stack);
                referencedTocFileModel.IsReferenceToc = true;
                return(referencedTocFileModel.Content);
            }
            else
            {
                // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally
                referencedTocFileModel = new TocItemInfo(tocFile, TocHelper.LoadSingleToc(tocFile.FullPath));

                referencedTocFileModel         = ResolveItem(referencedTocFileModel, stack);
                _notInProjectTocCache[tocFile] = referencedTocFileModel;
                return(referencedTocFileModel.Content);
            }
        }
Esempio n. 5
0
        private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack <FileAndType> stack, bool isRoot)
        {
            if (wrapper.IsResolved)
            {
                return(wrapper);
            }

            var file = wrapper.File;

            if (stack.Contains(file))
            {
                throw new DocumentException($"Circular reference to {file.FullPath} is found in {stack.Peek().FullPath}");
            }

            var item = wrapper.Content;

            // HomepageUid and Uid is deprecated, unified to TopicUid
            if (string.IsNullOrEmpty(item.TopicUid))
            {
                if (!string.IsNullOrEmpty(item.Uid))
                {
                    item.TopicUid = item.Uid;
                    item.Uid      = null;
                }
                else if (!string.IsNullOrEmpty(item.HomepageUid))
                {
                    item.TopicUid = item.HomepageUid;
                    Logger.LogWarning($"HomepageUid is deprecated in TOC. Please use topicUid to specify uid {item.Homepage}");
                    item.HomepageUid = null;
                }
            }
            // Homepage is deprecated, unified to TopicHref
            if (!string.IsNullOrEmpty(item.Homepage))
            {
                if (string.IsNullOrEmpty(item.TopicHref))
                {
                    item.TopicHref = item.Homepage;
                }
                else
                {
                    Logger.LogWarning($"Homepage is deprecated in TOC. Homepage {item.Homepage} is overwritten with topicHref {item.TopicHref}");
                }
            }
            // validate href
            ValidateHref(item);

            // validate if name is missing
            if (!isRoot && string.IsNullOrEmpty(item.Name) && string.IsNullOrEmpty(item.TopicUid))
            {
                Logger.LogWarning(
                    $"TOC item ({item.ToString()}) with empty name found. Missing a name?",
                    code: WarningCodes.Build.EmptyTocItemName);
            }

            // TocHref supports 2 forms: absolute path and local toc file.
            // When TocHref is set, using TocHref as Href in output, and using Href as Homepage in output
            var tocHrefType = Utility.GetHrefType(item.TocHref);

            // check whether toc exists
            TocItemInfo tocFileModel = null;

            if (!string.IsNullOrEmpty(item.TocHref) && (tocHrefType == HrefType.MarkdownTocFile || tocHrefType == HrefType.YamlTocFile))
            {
                var tocFilePath = (RelativePath)file.File + (RelativePath)item.TocHref;
                var tocFile     = file.ChangeFile(tocFilePath);
                if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                {
                    var message = $"Unable to find {item.TocHref}. Make sure the file is included in config file docfx.json!";
                    Logger.LogWarning(message);
                }
            }

            if (!string.IsNullOrEmpty(item.TocHref))
            {
                if (!string.IsNullOrEmpty(item.Homepage))
                {
                    throw new DocumentException(
                              $"TopicHref should be used to specify the homepage for {item.TocHref} when tocHref is used.");
                }
                if (tocHrefType == HrefType.RelativeFile || tocHrefType == HrefType.RelativeFolder)
                {
                    throw new DocumentException($"TocHref {item.TocHref} only supports absolute path or local toc file.");
                }
            }

            var hrefType = Utility.GetHrefType(item.Href);

            switch (hrefType)
            {
            case HrefType.AbsolutePath:
            case HrefType.RelativeFile:
                if (item.Items != null && item.Items.Count > 0)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack, false).Content;
                    }
                    if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                    {
                        var defaultItem = GetDefaultHomepageItem(item);
                        if (defaultItem != null)
                        {
                            item.AggregatedHref = defaultItem.TopicHref;
                            item.AggregatedUid  = defaultItem.TopicUid;
                        }
                    }
                }
                if (string.IsNullOrEmpty(item.TopicHref))
                {
                    // Get homepage from TocHref if href/topicHref is null or empty
                    if (string.IsNullOrEmpty(item.Href) && string.IsNullOrEmpty(item.TopicUid) && tocFileModel != null)
                    {
                        stack.Push(file);
                        var resolved = ResolveItem(tocFileModel, stack).Content;
                        stack.Pop();
                        item.Href     = resolved.TopicHref ?? resolved.AggregatedHref;
                        item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                    }
                    // Use TopicHref in output model
                    item.TopicHref = item.Href;
                }
                break;

            case HrefType.RelativeFolder:
            {
                if (tocFileModel != null)
                {
                    Logger.LogWarning($"Href {item.Href} is overwritten by tocHref {item.TocHref}");
                }
                else
                {
                    var relativeFolder = (RelativePath)file.File + (RelativePath)item.Href;
                    var tocFilePath    = relativeFolder + (RelativePath)Constants.TableOfContents.YamlTocFileName;

                    var tocFile = file.ChangeFile(tocFilePath);

                    // First, try finding toc.yml under the relative folder
                    // Second, try finding toc.md under the relative folder
                    if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                    {
                        tocFilePath = relativeFolder + (RelativePath)Constants.TableOfContents.MarkdownTocFileName;
                        tocFile     = file.ChangeFile(tocFilePath);
                        if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                        {
                            var message =
                                $"Unable to find either {Constants.TableOfContents.YamlTocFileName} or {Constants.TableOfContents.MarkdownTocFileName} inside {item.Href}. Make sure the file is included in config file docfx.json!";
                            Logger.LogWarning(message);
                            break;
                        }
                    }

                    item.TocHref = tocFilePath - (RelativePath)file.File;
                }

                // Get homepage from TocHref if TopicHref/TopicUid is not specified
                if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                {
                    stack.Push(file);
                    var resolved = ResolveItem(tocFileModel, stack).Content;
                    stack.Pop();
                    item.Href     = item.TopicHref = resolved.TopicHref ?? resolved.AggregatedHref;
                    item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                }
                else
                {
                    item.Href = item.TopicHref;
                }

                if (item.Items != null)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack, false).Content;
                    }
                }
            }
            break;

            case HrefType.MarkdownTocFile:
            case HrefType.YamlTocFile:
            {
                var href        = (RelativePath)item.Href;
                var tocFilePath = (RelativePath)file.File + href;
                var tocFile     = file.ChangeFile(tocFilePath);
                stack.Push(file);
                var referencedToc = GetReferencedToc(tocFile, stack);
                stack.Pop();
                // For referenced toc, content from referenced toc is expanded as the items of current toc item,
                // Href is reset to the homepage of current toc item
                item.Href = item.TopicHref;
                var referencedTocClone = referencedToc.Items?.Clone();

                // For [reference](a/toc.md), and toc.md contains not-exist.md, the included not-exist.md should be resolved to a/not-exist.md
                item.Items = UpdateOriginalHref(referencedTocClone, href);
            }
            break;

            default:
                break;
            }

            var relativeToFile = (RelativePath)file.File;

            item.OriginalHref      = item.Href;
            item.OriginalTocHref   = item.TocHref;
            item.OriginalTopicHref = item.TopicHref;
            item.OriginalHomepage  = item.Homepage;
            item.Href      = NormalizeHref(item.Href, relativeToFile);
            item.TocHref   = NormalizeHref(item.TocHref, relativeToFile);
            item.TopicHref = NormalizeHref(item.TopicHref, relativeToFile);
            item.Homepage  = NormalizeHref(item.Homepage, relativeToFile);

            wrapper.IsResolved = true;

            // for backward compatibility
            if (item.Href == null && item.Homepage == null)
            {
                item.Href     = item.TocHref;
                item.Homepage = item.TopicHref;
            }

            return(wrapper);
        }
Esempio n. 6
0
        private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack<FileAndType> stack)
        {
            if (wrapper.IsResolved)
            {
                return wrapper;
            }

            var file = wrapper.File;
            if (stack.Contains(file))
            {
                throw new DocumentException($"Circular reference to {file.FullPath} is found in {stack.Peek().FullPath}");
            }

            var item = wrapper.Content;

            // HomepageUid and Uid is deprecated, unified to TopicUid
            if (string.IsNullOrEmpty(item.TopicUid))
            {
                if (!string.IsNullOrEmpty(item.Uid))
                {
                    item.TopicUid = item.Uid;
                    item.Uid = null;
                }
                else if (!string.IsNullOrEmpty(item.HomepageUid))
                {
                    item.TopicUid = item.HomepageUid;
                    Logger.LogWarning($"HomepageUid is deprecated in TOC. Please use topicUid to specify uid {item.Homepage}");
                    item.HomepageUid = null;
                }
            }
            // Homepage is deprecated, unified to TopicHref
            if (!string.IsNullOrEmpty(item.Homepage))
            {
                if (string.IsNullOrEmpty(item.TopicHref))
                {
                    item.TopicHref = item.Homepage;
                }
                else
                {
                    Logger.LogWarning($"Homepage is deprecated in TOC. Homepage {item.Homepage} is overwritten with topicHref {item.TopicHref}");
                }
            }
            // TocHref supports 2 forms: absolute path and local toc file.
            // When TocHref is set, using TocHref as Href in output, and using Href as Homepage in output
            var tocHrefType = Utility.GetHrefType(item.TocHref);

            // check whether toc exists
            TocItemInfo tocFileModel = null;
            if (!string.IsNullOrEmpty(item.TocHref) && (tocHrefType == HrefType.MarkdownTocFile || tocHrefType == HrefType.YamlTocFile))
            {
                var tocFilePath = (TypeForwardedToRelativePath)file.File + (TypeForwardedToRelativePath)item.TocHref;
                var tocFile = file.ChangeFile(tocFilePath);
                if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                {
                    var message = $"Unable to find {item.TocHref}. Make sure the file is included in config file docfx.json!";
                    Logger.LogWarning(message);
                }
            }

            if (!string.IsNullOrEmpty(item.TocHref))
            {
                if (!string.IsNullOrEmpty(item.Homepage))
                {
                    throw new DocumentException(
                        $"TopicHref should be used to specify the homepage for {item.TocHref} when tocHref is used.");
                }
                if (tocHrefType == HrefType.RelativeFile || tocHrefType == HrefType.RelativeFolder)
                {
                    throw new DocumentException($"TocHref {item.TocHref} only supports absolute path or local toc file.");
                }
            }

            var hrefType = Utility.GetHrefType(item.Href);
            switch (hrefType)
            {
                case HrefType.AbsolutePath:
                case HrefType.RelativeFile:
                    if (item.Items != null && item.Items.Count > 0)
                    {
                        for (int i = 0; i < item.Items.Count; i++)
                        {
                            item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                        }
                        if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                        {
                            var defaultItem = GetDefaultHomepageItem(item);
                            if (defaultItem != null)
                            {
                                item.AggregatedHref = defaultItem.TopicHref;
                                item.AggregatedUid = defaultItem.TopicUid;
                            }
                        }
                    }
                    if (string.IsNullOrEmpty(item.TopicHref))
                    {
                        // Get homepage from TocHref if href/topicHref is null or empty
                        if (string.IsNullOrEmpty(item.Href) && string.IsNullOrEmpty(item.TopicUid) && tocFileModel != null)
                        {
                            stack.Push(file);
                            var resolved = ResolveItem(tocFileModel, stack).Content;
                            stack.Pop();
                            item.Href = resolved.TopicHref ?? resolved.AggregatedHref;
                            item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                        }
                        // Use TopicHref in output model
                        item.TopicHref = item.Href;
                    }
                    break;
                case HrefType.RelativeFolder:
                    {
                        if (tocFileModel != null)
                        {
                            Logger.LogWarning($"Href {item.Href} is overwritten by tocHref {item.TocHref}");
                        }
                        else
                        {
                            var relativeFolder = (TypeForwardedToRelativePath)file.File + (TypeForwardedToRelativePath)item.Href;
                            var tocFilePath = relativeFolder + (TypeForwardedToRelativePath)Constants.YamlTocFileName;

                            var tocFile = file.ChangeFile(tocFilePath);

                            // First, try finding toc.yml under the relative folder
                            // Second, try finding toc.md under the relative folder
                            if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                            {
                                tocFilePath = relativeFolder + (TypeForwardedToRelativePath)Constants.MarkdownTocFileName;
                                tocFile = file.ChangeFile(tocFilePath);
                                if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel))
                                {
                                    var message =
                                        $"Unable to find either {Constants.YamlTocFileName} or {Constants.MarkdownTocFileName} inside {item.Href}. Make sure the file is included in config file docfx.json!";
                                    Logger.LogWarning(message);
                                    break;
                                }
                            }

                            item.TocHref = tocFilePath - (TypeForwardedToRelativePath)file.File;
                        }

                        // Get homepage from TocHref if TopicHref/TopicUid is not specified
                        if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                        {
                            stack.Push(file);
                            var resolved = ResolveItem(tocFileModel, stack).Content;
                            stack.Pop();
                            item.Href = item.TopicHref = resolved.TopicHref ?? resolved.AggregatedHref;
                            item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                        }
                        else
                        {
                            item.Href = item.TopicHref;
                        }

                        if (item.Items != null)
                        {
                            for (int i = 0; i < item.Items.Count; i++)
                            {
                                item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                            }
                        }
                    }
                    break;
                case HrefType.MarkdownTocFile:
                case HrefType.YamlTocFile:
                    {
                        var tocFilePath = (TypeForwardedToRelativePath)file.File + (TypeForwardedToRelativePath)item.Href;
                        var tocFile = file.ChangeFile(tocFilePath);
                        TocItemInfo referencedTocFileModel;
                        TocItemViewModel referencedToc;
                        stack.Push(file);
                        if (_collection.TryGetValue(tocFile.FullPath, out referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel))
                        {
                            referencedTocFileModel = ResolveItem(referencedTocFileModel, stack);
                            referencedTocFileModel.IsReferenceToc = true;
                            referencedToc = referencedTocFileModel.Content;
                        }
                        else
                        {
                            // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally
                            referencedTocFileModel = new TocItemInfo(tocFile, new TocItemViewModel
                            {
                                Items = Utility.LoadSingleToc(tocFile.FullPath)
                            });

                            referencedTocFileModel = ResolveItem(referencedTocFileModel, stack);
                            referencedToc = referencedTocFileModel.Content;
                            _notInProjectTocCache[tocFile] = referencedTocFileModel;
                        }
                        stack.Pop();
                        // For referenced toc, content from referenced toc is expanded as the items of current toc item,
                        // Href is reset to the homepage of current toc item
                        item.Href = item.TopicHref;
                        item.Items = referencedToc.Items;
                    }
                    break;
                default:
                    break;
            }

            var relativeToFile = (TypeForwardedToRelativePath)file.File;

            item.OriginalHref = item.Href;
            item.OriginalTocHref = item.TocHref;
            item.OriginalTopicHref = item.TopicHref;
            item.OriginalHomepage = item.Homepage;
            item.Href = NormalizeHref(item.Href, relativeToFile);
            item.TocHref = NormalizeHref(item.TocHref, relativeToFile);
            item.TopicHref = NormalizeHref(item.TopicHref, relativeToFile);
            item.Homepage = NormalizeHref(item.Homepage, relativeToFile);

            wrapper.IsResolved = true;

            // for backward compatibility
            if (item.Href == null && item.Homepage == null)
            {
                item.Href = item.TocHref;
                item.Homepage = item.TopicHref;
            }

            return wrapper;
        }
Esempio n. 7
0
        private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack <FileAndType> stack)
        {
            if (wrapper.IsResolved)
            {
                return(wrapper);
            }

            var file = wrapper.File;

            if (stack.Contains(file))
            {
                throw new DocumentException($"Circular reference to {file.FullPath} is found in {stack.Peek().FullPath}");
            }

            var item = wrapper.Content;

            // HomepageUid and Uid is deprecated, unified to TopicUid
            if (string.IsNullOrEmpty(item.TopicUid))
            {
                if (!string.IsNullOrEmpty(item.Uid))
                {
                    item.TopicUid = item.Uid;
                    item.Uid      = null;
                }
                else if (!string.IsNullOrEmpty(item.HomepageUid))
                {
                    item.TopicUid = item.HomepageUid;
                    Logger.LogWarning($"HomepageUid is deprecated in TOC. Please use topicUid to specify uid {item.Homepage}");
                    item.HomepageUid = null;
                }
            }
            // Homepage is deprecated, unified to TopicHref
            if (!string.IsNullOrEmpty(item.Homepage))
            {
                if (string.IsNullOrEmpty(item.TopicHref))
                {
                    item.TopicHref = item.Homepage;
                }
                else
                {
                    Logger.LogWarning($"Homepage is deprecated in TOC. Homepage {item.Homepage} is overwritten with topicHref {item.TopicHref}");
                }
            }
            // TocHref supports 2 forms: absolute path and local toc file.
            // When TocHref is set, using TocHref as Href in output, and using Href as Homepage in output
            var tocHrefType = Utility.GetHrefType(item.TocHref);

            // check whether toc exists
            TocItemInfo tocFileModel = null;

            if (!string.IsNullOrEmpty(item.TocHref) && (tocHrefType == HrefType.MarkdownTocFile || tocHrefType == HrefType.YamlTocFile))
            {
                var tocFilePath = (RelativePath)file.File + (RelativePath)item.TocHref;
                var tocFile     = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                if (!_collection.TryGetValue(tocFile, out tocFileModel))
                {
                    var message = $"Unable to find {item.TocHref}. Make sure the file is included in config file docfx.json!";
                    Logger.LogWarning(message);
                }
            }

            if (!string.IsNullOrEmpty(item.TocHref))
            {
                if (!string.IsNullOrEmpty(item.Homepage))
                {
                    throw new DocumentException(
                              $"TopicHref should be used to specify the homepage for {item.TocHref} when tocHref is used.");
                }
                if (tocHrefType == HrefType.RelativeFile || tocHrefType == HrefType.RelativeFolder)
                {
                    throw new DocumentException($"TocHref {item.TocHref} only supports absolute path or local toc file.");
                }
            }

            var hrefType = Utility.GetHrefType(item.Href);

            switch (hrefType)
            {
            case HrefType.AbsolutePath:
            case HrefType.RelativeFile:
                if (item.Items != null && item.Items.Count > 0)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                    }
                    if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                    {
                        var defaultItem = GetDefaultHomepageItem(item);
                        if (defaultItem != null)
                        {
                            item.AggregatedHref = defaultItem.TopicHref;
                            item.AggregatedUid  = defaultItem.TopicUid;
                        }
                    }
                }
                if (string.IsNullOrEmpty(item.TopicHref))
                {
                    // Get homepage from TocHref if href/topicHref is null or empty
                    if (string.IsNullOrEmpty(item.Href) && string.IsNullOrEmpty(item.TopicUid) && tocFileModel != null)
                    {
                        stack.Push(file);
                        var resolved = ResolveItem(tocFileModel, stack).Content;
                        stack.Pop();
                        item.Href     = resolved.TopicHref ?? resolved.AggregatedHref;
                        item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                    }
                    // Use TopicHref in output model
                    item.TopicHref = item.Href;
                }
                break;

            case HrefType.RelativeFolder:
            {
                if (tocFileModel != null)
                {
                    Logger.LogWarning($"Href {item.Href} is overwritten by tocHref {item.TocHref}");
                }
                else
                {
                    var relativeFolder = (RelativePath)file.File + (RelativePath)item.Href;
                    var tocFilePath    = relativeFolder + (RelativePath)Constants.YamlTocFileName;

                    var tocFile = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);

                    // First, try finding toc.yml under the relative folder
                    // Second, try finding toc.md under the relative folder
                    if (!_collection.TryGetValue(tocFile, out tocFileModel))
                    {
                        tocFilePath = relativeFolder + (RelativePath)Constants.MarkdownTocFileName;
                        tocFile     = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                        if (!_collection.TryGetValue(tocFile, out tocFileModel))
                        {
                            var message =
                                $"Unable to find either {Constants.YamlTocFileName} or {Constants.MarkdownTocFileName} inside {item.Href}. Make sure the file is included in config file docfx.json!";
                            Logger.LogWarning(message);
                            break;
                        }
                    }

                    item.TocHref = tocFilePath - (RelativePath)file.File;
                }

                // Get homepage from TocHref if TopicHref/TopicUid is not specified
                if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid))
                {
                    stack.Push(file);
                    var resolved = ResolveItem(tocFileModel, stack).Content;
                    stack.Pop();
                    item.Href     = item.TopicHref = resolved.TopicHref ?? resolved.AggregatedHref;
                    item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid;
                }
                else
                {
                    item.Href = item.TopicHref;
                }

                if (item.Items != null)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                    }
                }
            }
            break;

            case HrefType.MarkdownTocFile:
            case HrefType.YamlTocFile:
            {
                var              tocFilePath = (RelativePath)file.File + (RelativePath)item.Href;
                var              tocFile     = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                TocItemInfo      referencedTocFileModel;
                TocItemViewModel referencedToc;
                stack.Push(file);
                if (_collection.TryGetValue(tocFile, out referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel))
                {
                    referencedTocFileModel = ResolveItem(referencedTocFileModel, stack);
                    referencedTocFileModel.IsReferenceToc = true;
                    referencedToc = referencedTocFileModel.Content;
                }
                else
                {
                    // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally
                    referencedTocFileModel = new TocItemInfo(tocFile, new TocItemViewModel
                        {
                            Items = Utility.LoadSingleToc(tocFile.FullPath)
                        });

                    referencedTocFileModel         = ResolveItem(referencedTocFileModel, stack);
                    referencedToc                  = referencedTocFileModel.Content;
                    _notInProjectTocCache[tocFile] = referencedTocFileModel;
                }
                stack.Pop();
                // For referenced toc, content from referenced toc is expanded as the items of current toc item,
                // Href is reset to the homepage of current toc item
                item.Href  = item.TopicHref;
                item.Items = referencedToc.Items;
            }
            break;

            default:
                break;
            }

            var relativeToFile = (RelativePath)file.File;

            item.OriginalHref      = item.Href;
            item.OriginalTocHref   = item.TocHref;
            item.OriginalTopicHref = item.TopicHref;
            item.OriginalHomepage  = item.Homepage;
            item.Href      = NormalizeHref(item.Href, relativeToFile);
            item.TocHref   = NormalizeHref(item.TocHref, relativeToFile);
            item.TopicHref = NormalizeHref(item.TopicHref, relativeToFile);
            item.Homepage  = NormalizeHref(item.Homepage, relativeToFile);

            wrapper.IsResolved = true;

            // for backward compatibility
            if (item.Href == null && item.Homepage == null)
            {
                item.Href     = item.TocHref;
                item.Homepage = item.TopicHref;
            }

            return(wrapper);
        }
Esempio n. 8
0
        private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack <FileAndType> stack)
        {
            if (wrapper.IsResolved)
            {
                return(wrapper);
            }

            var file = wrapper.File;

            if (stack.Contains(file))
            {
                throw new DocumentException($"Circular reference to {file.FullPath} is found in {stack.Peek().FullPath}");
            }

            var item     = wrapper.Content;
            var hrefType = Utility.GetHrefType(item.Href);

            switch (hrefType)
            {
            case HrefType.AbsolutePath:
            case HrefType.RelativeFile:
                if (item.Items != null && item.Items.Count > 0)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                    }
                    if (string.IsNullOrEmpty(item.Homepage) && string.IsNullOrEmpty(item.HomepageUid))
                    {
                        var defaultItem = GetDefaultHomepageItem(item);
                        if (defaultItem != null)
                        {
                            item.Homepage    = defaultItem.Href;
                            item.HomepageUid = defaultItem.Uid;
                        }
                    }
                }

                break;

            case HrefType.RelativeFolder:
            {
                var relativeFolder = (RelativePath)file.File + (RelativePath)item.Href;
                var tocFilePath    = relativeFolder + (RelativePath)Constants.YamlTocFileName;

                var         tocFile = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                TocItemInfo tocFileModel;

                // First, try finding toc.yml under the relative folder
                // Second, try finding toc.md under the relative folder
                if (!_collection.TryGetValue(tocFile, out tocFileModel))
                {
                    tocFilePath = relativeFolder + (RelativePath)Constants.MarkdownTocFileName;
                    tocFile     = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                    if (!_collection.TryGetValue(tocFile, out tocFileModel))
                    {
                        var message = $"Unable to find either {Constants.YamlTocFileName} or {Constants.MarkdownTocFileName} inside {item.Href}. Make sure the file is included in config file docfx.json!";
                        Logger.LogWarning(message);
                        break;
                    }
                }

                item.TocHref = tocFilePath - (RelativePath)file.File;

                // Get homepage from the referenced toc
                if (string.IsNullOrEmpty(item.Homepage) && string.IsNullOrEmpty(item.HomepageUid))
                {
                    stack.Push(file);
                    var resolved = ResolveItem(tocFileModel, stack).Content;
                    stack.Pop();
                    item.Href = resolved.Homepage;
                    item.Uid  = resolved.HomepageUid;
                }
                else
                {
                    // Set homepage to href
                    item.Href = item.Homepage;
                    item.Uid  = item.HomepageUid;
                }

                if (item.Items != null)
                {
                    for (int i = 0; i < item.Items.Count; i++)
                    {
                        item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content;
                    }
                }
            }
            break;

            case HrefType.MarkdownTocFile:
            case HrefType.YamlTocFile:
            {
                var              tocFilePath = (RelativePath)file.File + (RelativePath)item.Href;
                var              tocFile     = new FileAndType(file.BaseDir, tocFilePath, file.Type, file.PathRewriter);
                TocItemInfo      tocFileModel;
                TocItemViewModel referencedToc;
                stack.Push(file);
                if (_collection.TryGetValue(tocFile, out tocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out tocFileModel))
                {
                    tocFileModel = ResolveItem(tocFileModel, stack);
                    tocFileModel.IsReferenceToc = true;
                    referencedToc = tocFileModel.Content;
                }
                else
                {
                    // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally
                    tocFileModel = new TocItemInfo(tocFile, new TocItemViewModel
                        {
                            Items = Utility.LoadSingleToc(tocFile.FullPath)
                        });

                    tocFileModel  = ResolveItem(tocFileModel, stack);
                    referencedToc = tocFileModel.Content;
                    _notInProjectTocCache[tocFile] = tocFileModel;
                }
                stack.Pop();
                // For referenced toc, content from referenced toc is expanded as the items of current toc item,
                // Href is reset to the homepage of current toc item
                item.Href  = item.Homepage;
                item.Uid   = item.HomepageUid;
                item.Items = referencedToc.Items;
            }
            break;

            default:
                break;
            }

            var relativeToFile = (RelativePath)file.File;

            item.Href          = NormalizeHref(item.Href, relativeToFile);
            item.TocHref       = NormalizeHref(item.TocHref, relativeToFile);
            item.Homepage      = NormalizeHref(item.Homepage, relativeToFile);
            wrapper.IsResolved = true;
            return(wrapper);
        }