|
| 1 | +package og; |
| 2 | + |
| 3 | +import com.fasterxml.jackson.databind.JsonNode; |
| 4 | +import com.fasterxml.jackson.databind.ObjectMapper; |
| 5 | +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; |
| 6 | + |
| 7 | +import java.io.IOException; |
| 8 | +import java.io.UncheckedIOException; |
| 9 | +import java.nio.file.Files; |
| 10 | +import java.nio.file.Path; |
| 11 | +import java.util.ArrayList; |
| 12 | +import java.util.LinkedHashMap; |
| 13 | +import java.util.Map; |
| 14 | +import java.util.SequencedMap; |
| 15 | + |
| 16 | +/** Loads content JSON/YAML files and category properties. */ |
| 17 | +public final class ContentLoader { |
| 18 | + |
| 19 | + static final String CONTENT_DIR = "content"; |
| 20 | + static final String CATEGORIES_FILE = "html-generators/categories.properties"; |
| 21 | + |
| 22 | + static final ObjectMapper JSON_MAPPER = new ObjectMapper(); |
| 23 | + static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); |
| 24 | + static final Map<String, ObjectMapper> MAPPERS = Map.of( |
| 25 | + "json", JSON_MAPPER, "yaml", YAML_MAPPER, "yml", YAML_MAPPER |
| 26 | + ); |
| 27 | + |
| 28 | + public static final SequencedMap<String, String> CATEGORY_DISPLAY = loadProperties(CATEGORIES_FILE); |
| 29 | + |
| 30 | + public record Snippet(JsonNode node) { |
| 31 | + public String get(String f) { return node.get(f).asText(); } |
| 32 | + public String slug() { return get("slug"); } |
| 33 | + public String category() { return get("category"); } |
| 34 | + public String title() { return get("title"); } |
| 35 | + public String jdkVersion() { return get("jdkVersion"); } |
| 36 | + public String oldCode() { return get("oldCode"); } |
| 37 | + public String modernCode() { return get("modernCode"); } |
| 38 | + public String oldApproach() { return get("oldApproach"); } |
| 39 | + public String modernApproach() { return get("modernApproach"); } |
| 40 | + public String oldLabel() { return get("oldLabel"); } |
| 41 | + public String modernLabel() { return get("modernLabel"); } |
| 42 | + public String key() { return category() + "/" + slug(); } |
| 43 | + public String catDisplay() { return CATEGORY_DISPLAY.get(category()); } |
| 44 | + } |
| 45 | + |
| 46 | + public static SequencedMap<String, Snippet> loadAllSnippets() throws IOException { |
| 47 | + var snippets = new LinkedHashMap<String, Snippet>(); |
| 48 | + for (var cat : CATEGORY_DISPLAY.sequencedKeySet()) { |
| 49 | + var catDir = Path.of(CONTENT_DIR, cat); |
| 50 | + if (!Files.isDirectory(catDir)) continue; |
| 51 | + var sorted = new ArrayList<Path>(); |
| 52 | + for (var ext : MAPPERS.keySet()) { |
| 53 | + try (var stream = Files.newDirectoryStream(catDir, "*." + ext)) { |
| 54 | + stream.forEach(sorted::add); |
| 55 | + } |
| 56 | + } |
| 57 | + sorted.sort(Path::compareTo); |
| 58 | + for (var path : sorted) { |
| 59 | + var ext = path.getFileName().toString(); |
| 60 | + ext = ext.substring(ext.lastIndexOf('.') + 1); |
| 61 | + var snippet = new Snippet(MAPPERS.get(ext).readTree(Files.readString(path))); |
| 62 | + snippets.put(snippet.key(), snippet); |
| 63 | + } |
| 64 | + } |
| 65 | + return snippets; |
| 66 | + } |
| 67 | + |
| 68 | + public static SequencedMap<String, String> loadProperties(String file) { |
| 69 | + try { |
| 70 | + var map = new LinkedHashMap<String, String>(); |
| 71 | + for (var line : Files.readAllLines(Path.of(file))) { |
| 72 | + line = line.strip(); |
| 73 | + if (line.isEmpty() || line.startsWith("#")) continue; |
| 74 | + var idx = line.indexOf('='); |
| 75 | + if (idx > 0) map.put(line.substring(0, idx).strip(), line.substring(idx + 1).strip()); |
| 76 | + } |
| 77 | + return map; |
| 78 | + } catch (IOException e) { throw new UncheckedIOException(e); } |
| 79 | + } |
| 80 | + |
| 81 | + public static String xmlEscape(String s) { |
| 82 | + return s == null ? "" |
| 83 | + : s.replace("&", "&").replace("<", "<").replace(">", ">") |
| 84 | + .replace("\"", """).replace("'", "'"); |
| 85 | + } |
| 86 | + |
| 87 | + private ContentLoader() {} |
| 88 | +} |
0 commit comments