1 package build.tools.pandocfilter; 2 3 import build.tools.pandocfilter.json.JSON; 4 import build.tools.pandocfilter.json.JSONArray; 5 import build.tools.pandocfilter.json.JSONObject; 6 import build.tools.pandocfilter.json.JSONString; 7 import build.tools.pandocfilter.json.JSONValue; 8 9 import java.io.BufferedReader; 10 import java.io.FileNotFoundException; 11 import java.io.FileReader; 12 import java.io.InputStreamReader; 13 import java.util.Map; 14 15 public class PandocFilter { 16 /** 17 * Traverse a tree of pandoc format objects, calling callback on each 18 * element, and replacing it if callback returns a new object. 19 * <p> 20 * Inspired by the walk method in 21 * https://github.com/jgm/pandocfilters/blob/master/pandocfilters.py 22 */ traverse(JSONValue obj, Callback callback, boolean deep)23 public JSONValue traverse(JSONValue obj, Callback callback, boolean deep) { 24 if (obj instanceof JSONArray) { 25 JSONArray array = (JSONArray) obj; 26 27 JSONArray processed_array = new JSONArray(); 28 for (JSONValue elem : array) { 29 if (elem instanceof JSONObject && elem.contains("t")) { 30 JSONValue replacement = callback.invoke(elem.get("t").asString(), elem.contains("c") ? elem.get("c") : new JSONArray()); 31 if (replacement == null) { 32 // no replacement object returned, use original 33 processed_array.add(traverse(elem, callback, deep)); 34 } else if (replacement instanceof JSONArray) { 35 // array of objects returned, splice all elements into array 36 JSONArray replacement_array = (JSONArray) replacement; 37 for (JSONValue repl_elem : replacement_array) { 38 processed_array.add(traverse(repl_elem, callback, deep)); 39 } 40 } else { 41 // replacement object given, traverse it 42 processed_array.add(traverse(replacement, callback, deep)); 43 } 44 } else { 45 processed_array.add(traverse(elem, callback, deep)); 46 } 47 } 48 return processed_array; 49 } else if (obj instanceof JSONObject) { 50 if (deep && obj.contains("t")) { 51 JSONValue replacement = callback.invoke(obj.get("t").asString(), obj.contains("c") ? obj.get("c") : new JSONArray()); 52 if (replacement != null) { 53 return replacement; 54 } 55 } JSONObject obj_obj = (JSONObject) obj; 56 var processed_obj = new JSONObject(); 57 for (String key : obj_obj.keys()) { 58 processed_obj.put(key, traverse(obj_obj.get(key), callback, deep)); 59 } 60 return processed_obj; 61 } else { 62 return obj; 63 } 64 } 65 createPandocNode(String type, JSONValue content)66 public JSONValue createPandocNode(String type, JSONValue content) { 67 if (content == null) { 68 return new JSONObject(Map.of( 69 "t", new JSONString(type))); 70 } else { 71 return new JSONObject(Map.of( 72 "t", new JSONString(type), 73 "c", content)); 74 } 75 } 76 createPandocNode(String type)77 public JSONValue createPandocNode(String type) { 78 return createPandocNode(type, null); 79 } 80 81 /* 82 * Helper constructors to create pandoc format objects 83 */ createSpace()84 public JSONValue createSpace() { 85 return createPandocNode("Space"); 86 } 87 createStr(String string)88 public JSONValue createStr(String string) { 89 return createPandocNode("Str", new JSONString(string)); 90 } 91 loadJson(String[] args)92 public static JSONValue loadJson(String[] args) throws FileNotFoundException { 93 StringBuffer input = new StringBuffer(); 94 InputStreamReader reader; 95 if (args.length > 0) 96 reader = new FileReader(args[0]); 97 else { 98 reader = new InputStreamReader(System.in); 99 } 100 new BufferedReader(reader).lines().forEach(line -> input.append(line)); 101 102 return JSON.parse(input.toString()); 103 } 104 105 public interface Callback { invoke(String type, JSONValue value)106 JSONValue invoke(String type, JSONValue value); 107 } 108 } 109