package com.megatim.manuel.core.generate; import com.fasterxml.jackson.databind.ObjectMapper; import com.megatim.manuel.core.convert.ConversionResult; import com.megatim.manuel.core.convert.FrontMatter; import com.megatim.manuel.core.convert.Heading; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; /** * Écrit le sommaire de l'aide sous forme de {@code toc.json} dans le dossier de sortie, * rendant le dossier HTML autonome et lisible par le viewer JavaFX (remplace le .chm). */ public class HelpExporter { public static final String TOC_JSON = "toc.json"; public void writeToc(Path dir, String title, List results, String homePage) throws IOException { writeToc(dir, title, results, homePage, null, null); } public void writeToc(Path dir, String title, List results, String homePage, String project, String version) throws IOException { HelpManifest m = new HelpManifest(); m.project = project; m.version = version; m.title = title; // Page d'accueil = page de garde du 1er document m.home = results.isEmpty() ? homePage : results.get(0).htmlFileName; for (ConversionResult r : results) { HelpToc doc = new HelpToc(r.title, r.htmlFileName); // 1. Accueil -> page de garde du document doc.children.add(new HelpToc("Accueil (page de garde)", r.htmlFileName)); // 2. Préambule : regroupe Résumé, Mots clés, Historique, Diffusion (ordre canonique) HelpToc preambule = new HelpToc("Préambule", null); for (String key : FrontMatter.ENTRY_ORDER) { String wanted = FrontMatter.label(key); for (Heading lm : r.landmarks) { if (wanted.equals(lm.text)) { preambule.children.add(new HelpToc(lm.text, r.htmlFileName + "#" + lm.anchor)); break; } } } if (!preambule.children.isEmpty()) doc.children.add(preambule); // 3. Sommaire (chapitres), numéroté hiérarchiquement (1, 1.1, 1.1.1, 2…) List tree = buildHeadingTree(r); if (!tree.isEmpty()) { numberTree(tree, ""); HelpToc som = new HelpToc("Sommaire", null); som.children.addAll(tree); doc.children.add(som); } // 4. Illustrations (légendes) if (!r.figures.isEmpty()) { HelpToc figs = new HelpToc("Illustrations", null); for (Heading f : r.figures) { figs.children.add(new HelpToc(f.text, r.htmlFileName + "#" + f.anchor)); } doc.children.add(figs); } m.nodes.add(doc); } ObjectMapper mapper = new ObjectMapper(); String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(m); Files.write(dir.resolve(TOC_JSON), json.getBytes(StandardCharsets.UTF_8)); } /** Préfixe chaque titre du sommaire par sa numérotation hiérarchique (1, 1.1, 1.1.1, 2…). */ private void numberTree(List nodes, String prefix) { int i = 1; for (HelpToc n : nodes) { String num = prefix.isEmpty() ? String.valueOf(i) : prefix + "." + i; n.title = num + ". " + n.title; numberTree(n.children, num); i++; } } /** Convertit la liste plate de titres (avec niveaux) en arbre imbriqué. */ private List buildHeadingTree(ConversionResult r) { List roots = new ArrayList<>(); if (r.headings.isEmpty()) return roots; int min = Integer.MAX_VALUE; for (Heading h : r.headings) min = Math.min(min, Math.max(1, h.level)); Deque nodes = new ArrayDeque<>(); Deque levels = new ArrayDeque<>(); for (Heading h : r.headings) { int lvl = Math.max(1, h.level) - min + 1; HelpToc node = new HelpToc(h.text, r.htmlFileName + "#" + h.anchor); while (!levels.isEmpty() && levels.peek() >= lvl) { levels.pop(); nodes.pop(); } if (nodes.isEmpty()) roots.add(node); else nodes.peek().children.add(node); nodes.push(node); levels.push(lvl); } return roots; } }