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