// Parse the chapters in the document, recursively private void ParseBookMapChapters() { List <DitaElement> chapters = RootMap.RootElement.FindChildren(_chapterElements); foreach (DitaElement chapter in chapters) { // What is the href to the chapter? string chapterHref = chapter.Attributes?["href"]; // Try to find this file DitaFile linkedFile = Collection.GetFileByName(chapterHref); List <DitaCollectionLinkJson> children = ParseChaptersFromFile(linkedFile); Chapters.AddRange(children); if (linkedFile is DitaFileTopicAbstract && children.Count > 0) { children[0].Children.AddRange(ParseRefs(chapter.FindChildren(_refElements))); } //if (chapter.Type == "appendices" && children.Count > 0) //{ //} } }
// Rename the files in the collection to match their title (instead of their given file names) public void RenameFiles() { List <string> fileNames = new List <string>(); // Generate a new name for each file, based on it's title foreach (DitaFile file in Files) { string newFileName = DitaFile.TitleToFileName(file.Title, Path.GetExtension(file.FileName)); if (!string.IsNullOrWhiteSpace(newFileName)) { if (fileNames.Contains(newFileName)) { string newFileNameBase = newFileName; int counter = 1; while (fileNames.Contains(newFileName)) { newFileName = Path.ChangeExtension($"{Path.GetFileNameWithoutExtension(newFileNameBase)}_{counter}", Path.GetExtension(newFileNameBase)); counter++; } } fileNames.Add(newFileName); file.NewFileName = newFileName; Trace.TraceInformation($"Renaming {file.FileName} to {newFileName}"); } } // Update references from old to new file names UpdateRenamedFileReferences(); }
// Load a single file private DitaFile LoadFile(string filePath) { // Look for known extensions // Is this an image? if (Path.HasExtension(filePath)) { string extension = Path.GetExtension(filePath)?.ToLower(); if (DitaFileImage.Extensions.Contains(extension)) { DitaFileImage image = new DitaFileImage(filePath); Trace.TraceInformation($"{Path.GetFileName(filePath)} is a {typeof(DitaFileImage)}"); return(image); } } // Try to load the given file try { // Try to load as an XML document XmlDocument xmlDocument = DitaFile.LoadAndCheckType(filePath, out Type fileType); // Create a new object of the correct type if (DitaFile.DitaFileTypeCreation.ContainsKey(fileType)) { return(DitaFile.DitaFileTypeCreation[fileType](xmlDocument, filePath)); } } catch (Exception ex) { Trace.TraceWarning($"Unable to load {filePath} as XML: {ex}"); } throw new Exception($"{filePath} is an unknown file type."); }
// Loads all of the DITA files and supports from the given directory private void LoadDirectory(string input) { // Get a list of all the files in the directory string[] files = Directory.GetFiles(input); if (files.Length > 0) { Trace.TraceInformation($"Checking {files.Length} files..."); foreach (string file in files) { try { DitaFile ditaFile = LoadFile(file); Files.Add(ditaFile); } catch { Trace.TraceWarning($"Unable to load file {file}"); } } Trace.TraceInformation($"Found {FileCount} valid DITA files."); Trace.TraceInformation($"- {GetBookMaps().Count} bookmaps."); Trace.TraceInformation($"- {GetMaps().Count} maps."); Trace.TraceInformation($"- {GetTopics().Count} topics."); Trace.TraceInformation($"- {GetImages().Count} images."); } else { Trace.TraceWarning($"No files found in directory {input}"); } }
// Construct from a single topic public DitaPageJson(DitaFile file, DitaCollection collection) { Collection = collection; // Get the title of the page Title = DitaFile.FixSpecialCharacters(file.Title); // Create the file name FileName = file.NewFileName ?? file.FileName; FileName = Path.ChangeExtension(FileName, ".json"); OriginalFileName = file.FileName; // Find the body element string bodyElementName = null; if (DitaFile.DitaFileBodyElement.ContainsKey(file.GetType())) { bodyElementName = DitaFile.DitaFileBodyElement[file.GetType()](); } if (!string.IsNullOrEmpty(bodyElementName)) { DitaElement bodyElement = file.RootElement.FindOnlyChild(bodyElementName); if (bodyElement != null) { Sections = new List <DitaPageSectionJson>(); // Convert the body to html DitaElementToHtmlConverter htmlConverter = new DitaElementToHtmlConverter(collection); htmlConverter.Convert(bodyElement, Sections, file.FileName, out string bodyHtml); BodyHtml = bodyHtml; // Convert the body to text DitaElementToTextConverter textConverter = new DitaElementToTextConverter(); textConverter.Convert(bodyElement, out string bodyText); BodyText = bodyText; } else { Trace.TraceWarning($"Body element not found in {file.FileName}."); } } else { Trace.TraceWarning($"No body element identified in {file.FileName}."); } IsEmpty = string.IsNullOrEmpty(BodyText) || string.IsNullOrEmpty(Title); }
// Parse chapter structure from a dita file private List <DitaCollectionLinkJson> ParseChaptersFromFile(DitaFile linkedFile, string navTitle = null) { Trace.TraceInformation($"Converting {linkedFile}"); // What type of file is this? switch (linkedFile) { case DitaFileBookMap bookMap: // This should never happen throw new Exception($"Found bookmap {linkedFile} nested in bookmap."); case DitaFileMap map: return(ParseChaptersFromMap(map)); case DitaFileTopicAbstract topic: return(ParseChaptersFromTopic(topic, navTitle)); } return(null); }
// Construct a collection from a Dita bookmap in a Dita collection public DitaCollectionJson(DitaCollection collection, DitaFile rootMap) { // Store the construction properties Collection = collection; RootMap = rootMap; // Initialize the properties BookTitle = new Dictionary <string, string>(); BookMeta = new Dictionary <string, string>(); Chapters = new List <DitaCollectionLinkJson>(); Pages = new List <DitaPageJson>(); // Create the output object if (RootMap is DitaFileBookMap) { ParseBookMap(); } else if (RootMap is DitaFileMap) { ParseMap(); } }
// Try to find the root map file private void FindRootMap(string rootMapFile) { // If no file name specified, try to find a bookmap if (string.IsNullOrEmpty(rootMapFile)) { List <DitaFileBookMap> bookMaps = Collection.GetBookMaps(); if (bookMaps.Count == 1) { RootMap = bookMaps[0]; return; } throw new Exception($"Expecting exactly 1 bookmap, but found {bookMaps.Count}"); } // Is there a bookmap or map with the given name? DitaFile rootFile = Collection.GetFileByName(rootMapFile); if (rootFile == null) { throw new Exception($"Specified root map file {rootMapFile} was not found in collection."); } switch (rootFile) { case DitaFileBookMap bookMap: RootMap = bookMap; break; case DitaFileMap map: RootMap = map; break; default: throw new Exception($"{rootMapFile} must be a map or bookmap."); } }
// Resolves conrefs in this file private void ResolveConRefs(DitaElement parentElement, DitaCollection collection) { if (parentElement != null) { bool updated = false; // Does this element have a conref? string conref = parentElement.AttributeValueOrDefault("conref", String.Empty); if (!string.IsNullOrEmpty(conref)) { // We expect the conref to be in the form of filename.xml#fileid/elementid Regex conRefRegex = new Regex("^(.*)#(.*)/(.*)$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); MatchCollection conRefMatchCollection = conRefRegex.Matches(conref); if (conRefMatchCollection?.Count > 0 && (conRefMatchCollection[0].Groups.Count == 4)) { // Extract the components of the conref string refFileName = conRefMatchCollection[0].Groups[1].Value; string refFileId = conRefMatchCollection[0].Groups[2].Value; string refElementId = conRefMatchCollection[0].Groups[3].Value; if (Path.GetFileNameWithoutExtension(refFileName) != refFileId) { Trace.TraceWarning($"conref file name '{refFileName}' is not equal to file id '{refFileId}'."); } // Try to find the file that this conref refers to DitaFile refFile = collection.GetFileByName(refFileName); if (refFile != null) { // Does the references element exist in this file if (refFile.ElementsById.ContainsKey(refElementId)) { DitaElement refElement = refFile.ElementsById[refElementId]; // Copy the refernce element parentElement.Copy(refElement); updated = true; } else { Trace.TraceWarning($"Element '{refElementId}' not found in file '{refFileName}'."); } } else { Trace.TraceWarning($"Can't find file '{refFileName}' referenced in file '{FileName}'."); } } else { Trace.TraceWarning($"conref {conref} not in expected format."); } } // Update child references if (!updated && parentElement.Children != null) { foreach (DitaElement childElement in parentElement.Children) { ResolveConRefs(childElement, collection); } } } }
// Returns the relative or absolute url from a Dita XREF for use in an html A tag private string UrlFromXref(DitaElement xrefElement, out string title) { // What is the scope string scope = xrefElement.AttributeValueOrDefault("scope", null); string format = xrefElement.AttributeValueOrDefault("format", null); string href = xrefElement.AttributeValueOrDefault("href", null); title = null; if (scope == "external") { return(href); } if (!string.IsNullOrEmpty(href)) { string result = null; if (href[0] == '#') { // Link to the same page if (href.Contains('/')) { string[] anchorSplit = href.Split('/'); if (anchorSplit.Length > 1) { result = $"#{anchorSplit[1]}"; } } else { result = href.Substring(1); } } else if (href.ToLowerInvariant().StartsWith("http")) { result = href; } else { // Split by hash, if any string[] hashSplit = href.Split('#'); // Try to find the topic it is linking to DitaFile referenceFile = Collection?.GetFileByName(hashSplit[0]); if (referenceFile != null) { result = $"%DOCUMENT_ROOT%/{Path.GetFileNameWithoutExtension(referenceFile.NewFileName)}"; if (hashSplit.Length > 1) { result += $"#{hashSplit[1]}"; } title = referenceFile.Title; } else { Trace.TraceError($"Xref refers to unknown local file {hashSplit[0]} in {FileName}"); } } if (!string.IsNullOrEmpty(result)) { return(result); } else { return("#"); } } Trace.TraceWarning($"Unknown xref scope={scope}, format={format}, href={href} in {FileName}"); return("#"); }
private List <DitaCollectionLinkJson> ParseRefs(List <DitaElement> topicRefElements) { List <DitaCollectionLinkJson> chapters = new List <DitaCollectionLinkJson>(); if (topicRefElements?.Count > 0) { foreach (DitaElement topicRefElement in topicRefElements) { // Try to find the linked file string topicRefHref = topicRefElement.AttributeValueOrDefault("href", ""); string topicRefKeyRef = topicRefElement.AttributeValueOrDefault("keyref", ""); string topicRefNavTitle = topicRefElement?.FindOnlyChild("topicmeta")?.FindOnlyChild("navtitle")?.ToString(); // If there is no navtitle, check the topicmeta if (string.IsNullOrWhiteSpace(topicRefNavTitle)) { topicRefNavTitle = topicRefElement.AttributeValueOrDefault("navtitle", ""); } // Is this an external link? if (topicRefElement.AttributeValueOrDefault("scope", "") == "external") { DitaCollectionLinkJson externalLink = new DitaCollectionLinkJson { FileName = topicRefHref, Title = topicRefNavTitle, IsExternal = true }; chapters.Add(externalLink); } else { // Local scope DitaFile linkedFile = null; if (!string.IsNullOrWhiteSpace(topicRefHref)) { linkedFile = Collection.GetFileByName(topicRefHref); } // If no href, try to find by keyref if (linkedFile == null && !string.IsNullOrWhiteSpace(topicRefKeyRef)) { linkedFile = Collection.GetFileByKey(topicRefKeyRef); } if (linkedFile != null) { if (string.IsNullOrWhiteSpace(linkedFile.Title)) { linkedFile.Title = topicRefNavTitle; } else if (string.IsNullOrWhiteSpace(topicRefNavTitle)) { topicRefNavTitle = linkedFile.Title; } // Add references from the linked files List <DitaCollectionLinkJson> newChapters = ParseChaptersFromFile(linkedFile, topicRefNavTitle); if (newChapters != null && newChapters.Count > 0) { // Are there child chapters? List <DitaCollectionLinkJson> childChapters = ParseRefs(topicRefElement.FindChildren(_refElements)); if (newChapters.Count > 1 && childChapters.Count > 0) { // This should never happen throw new Exception("Found multiple children in a map and topic refs."); } if (childChapters != null && childChapters.Count > 0) { newChapters[0]?.Children?.AddRange(childChapters); } chapters.AddRange(newChapters); } } else { Trace.TraceWarning($"Reference with missing href/keyref: {topicRefElement}"); } } } } return(chapters); }