1 package club.wpia.gigi.output.template;
3 import java.io.PrintWriter;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.HashMap;
7 import java.util.LinkedList;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern;
13 import club.wpia.gigi.Gigi;
14 import club.wpia.gigi.localisation.Language;
15 import club.wpia.gigi.util.HTMLEncoder;
18 * A pattern that is to be translated before variables are inserted.
20 public final class SprintfCommand implements Translatable {
23 * The pattern to fill. Containing placeholders of pattern
24 * {@link #placeholder}. This is the string that will be translated.
26 private final String pattern;
29 * A regex that matches the replacement patterns like "{0}" or "{1}".
31 private static final Pattern placeholder = Pattern.compile("\\{([0-9]+)\\}");
34 * The values describing what to put into the {@link #placeholder}s of
37 private final String[] replacements;
40 * Regex for detecting processing instructions in a in-template
43 private static final Pattern processingInstruction = Pattern.compile("(?:(\\$!?\\{[a-zA-Z0-9_-]+)\\})|(?:(!'[^{}'\\$]*)')|(?:(!\\([^{})\\$]*)\\))");
46 * Creates a new SprintfCommand based on its pre-parsed contents. This is
47 * the variant that the data is stored internally in this class. So the
48 * <code>pattern</code> has numbers as placeholders and the replacement list
49 * contains the instructions on what to put in there (without closing
53 * a string with <code>{0},{1},..</code> as placeholders.
55 * instructions for what data to put into the placeholders:
56 * <code>${var</code>, <code>$!{var</code>, <code>!'plain</code>,
57 * <code>!(/link</code>.
59 public SprintfCommand(String pattern, List<String> replacements) {
60 this.pattern = pattern;
61 this.replacements = replacements.toArray(new String[replacements.size()]);
65 * Creates a new SprintfCommand that is parsed as from template source. This
66 * version is internally used to create {@link SprintfCommand}s from the
67 * literals specified in {@link Template}s.
70 * the part from the template that is to be parsed.
72 protected SprintfCommand(String content) {
73 StringBuffer pattern = new StringBuffer();
74 List<String> replacements = new LinkedList<String>();
76 Matcher m = processingInstruction.matcher(content);
79 pattern.append(content.substring(last, m.start()));
81 if ((group = m.group(1)) != null) {
82 replacements.add(group);
83 } else if ((group = m.group(2)) != null) {
84 replacements.add(group);
85 } else if ((group = m.group(3)) != null) {
86 replacements.add(group);
88 throw new Error("Regex is broken??");
91 pattern.append("{" + (counter++) + "}");
93 pattern.append(content.substring(last));
94 this.pattern = pattern.toString();
95 this.replacements = replacements.toArray(new String[replacements.size()]);
99 public void output(PrintWriter out, Language l, Map<String, Object> externalVariables) {
100 String parts = l.getTranslation(pattern);
101 Matcher m = placeholder.matcher(parts);
104 out.print(escape(externalVariables, parts.substring(pos, m.start())));
105 String replacement = replacements[Integer.parseInt(m.group(1))];
106 if (replacement.startsWith("$!")) {
107 Template.outputVar(out, l, externalVariables, replacement.substring(3), true);
108 } else if (replacement.startsWith("!'")) {
109 out.print(replacement.substring(2));
110 } else if (replacement.startsWith("!(")) {
111 String host = (String) externalVariables.get(Gigi.LINK_HOST);
113 throw new Error("Unconfigured link-host while interpreting link-syntax.");
115 if (replacement.charAt(2) != '/') {
116 throw new Error("Need an absolute link for the link service.");
118 String link = "//" + host + replacement.substring(2);
119 out.print("<a href='" + HTMLEncoder.encodeHTML(link) + "'>");
120 } else if (replacement.startsWith("$")) {
121 Template.outputVar(out, l, externalVariables, replacement.substring(2), false);
123 throw new Error("Processing error in template.");
128 out.print(escape(externalVariables, parts.substring(pos)));
131 private String escape(Map<String, Object> vars, String target) {
132 if (vars.containsKey(OUT_KEY_PLAIN)) {
135 return HTMLEncoder.encodeHTML(target);
139 public void addTranslations(Collection<String> s) {
144 * Creates a simple {@link SprintfCommand} wrapped in a {@link Scope} to fit
145 * in now constant variables into this template.
148 * the message (to be translated) with <code>{0},{1},...</code>
151 * the contents of the variables to put into the placeholders.
152 * @return the constructed {@link Outputable}.
154 public static Outputable createSimple(String msg, Object... vars) {
155 HashMap<String, Object> scope = new HashMap<>();
156 String[] store = new String[vars.length];
157 for (int i = 0; i < vars.length; i++) {
158 scope.put("autoVar" + i, vars[i]);
159 store[i] = "${autoVar" + i;
161 return new Scope(new SprintfCommand(msg, Arrays.asList(store)), scope);