upd: document variables in SprintfCommand more clearly
authorFelix Dörre <felix@dogcraft.de>
Sat, 18 Feb 2017 00:39:57 +0000 (01:39 +0100)
committerFelix Dörre <felix@dogcraft.de>
Mon, 20 Feb 2017 23:54:50 +0000 (00:54 +0100)
Change-Id: I4227c3f38cf811c5efddf0e5ff31775df16fe861

src/club/wpia/gigi/output/template/SprintfCommand.java
src/club/wpia/gigi/pages/Verify.java
src/club/wpia/gigi/util/AuthorizationContext.java

index 015fe6b..d313df8 100644 (file)
@@ -19,93 +19,113 @@ import club.wpia.gigi.util.HTMLEncoder;
  */
 public final class SprintfCommand implements Translatable {
 
-    private final String text;
+    /**
+     * The pattern to fill. Containing placeholders of pattern
+     * {@link #placeholder}. This is the string that will be translated.
+     */
+    private final String pattern;
 
-    private final String[] store;
+    /**
+     * A regex that matches the replacement patterns like "{0}" or "{1}".
+     */
+    private static final Pattern placeholder = Pattern.compile("\\{([0-9]+)\\}");
+
+    /**
+     * The values describing what to put into the {@link #placeholder}s of
+     * {@link #pattern}.
+     */
+    private final String[] replacements;
 
     /**
-     * Creates a new SprintfCommand based on its pre-parsed contents.
+     * Regex for detecting processing instructions in a in-template
+     * SprintfCommand.
+     */
+    private static final Pattern processingInstruction = Pattern.compile("(?:(\\$!?\\{[a-zA-Z0-9_-]+)\\})|(?:(!'[^{}'\\$]*)')|(?:(!\\([^{})\\$]*)\\))");
+
+    /**
+     * Creates a new SprintfCommand based on its pre-parsed contents. This is
+     * the variant that the data is stored internally in this class. So the
+     * <code>pattern</code> has numbers as placeholders and the replacement list
+     * contains the instructions on what to put in there (without closing
+     * brackets, etc.).
      * 
-     * @param text
+     * @param pattern
      *            a string with <code>{0},{1},..</code> as placeholders.
-     * @param store
-     *            the data to put into the placeholders: ${var}, $!{var},
-     *            !'plain', !(/link).
+     * @param replacements
+     *            instructions for what data to put into the placeholders:
+     *            <code>${var</code>, <code>$!{var</code>, <code>!'plain</code>,
+     *            <code>!(/link</code>.
      */
-    public SprintfCommand(String text, List<String> store) {
-        this.text = text;
-        this.store = store.toArray(new String[store.size()]);
+    public SprintfCommand(String pattern, List<String> replacements) {
+        this.pattern = pattern;
+        this.replacements = replacements.toArray(new String[replacements.size()]);
     }
 
-    private static final String VARIABLE = "\\$!?\\{[a-zA-Z0-9_-]+\\}";
-
-    private static final Pattern processingInstruction = Pattern.compile("(" + VARIABLE + ")|(?:(!'[^{}'\\$]*)')|(?:(!\\([^{})\\$]*)\\))");
-
     /**
-     * Creates a new SprintfCommand that is parsed as from template source.
+     * Creates a new SprintfCommand that is parsed as from template source. This
+     * version is internally used to create {@link SprintfCommand}s from the
+     * literals specified in {@link Template}s.
      * 
      * @param content
      *            the part from the template that is to be parsed.
      */
     protected SprintfCommand(String content) {
-        StringBuffer raw = new StringBuffer();
-        List<String> var = new LinkedList<String>();
+        StringBuffer pattern = new StringBuffer();
+        List<String> replacements = new LinkedList<String>();
         int counter = 0;
         Matcher m = processingInstruction.matcher(content);
         int last = 0;
         while (m.find()) {
-            raw.append(content.substring(last, m.start()));
+            pattern.append(content.substring(last, m.start()));
             String group = null;
             if ((group = m.group(1)) != null) {
-                var.add(group);
+                replacements.add(group);
             } else if ((group = m.group(2)) != null) {
-                var.add(group);
+                replacements.add(group);
             } else if ((group = m.group(3)) != null) {
-                var.add(group);
+                replacements.add(group);
             } else {
                 throw new Error("Regex is broken??");
             }
             last = m.end();
-            raw.append("{" + (counter++) + "}");
+            pattern.append("{" + (counter++) + "}");
         }
-        raw.append(content.substring(last));
-        text = raw.toString();
-        store = var.toArray(new String[var.size()]);
+        pattern.append(content.substring(last));
+        this.pattern = pattern.toString();
+        this.replacements = replacements.toArray(new String[replacements.size()]);
     }
 
-    private static final Pattern replacant = Pattern.compile("\\{([0-9]+)\\}");
-
     @Override
-    public void output(PrintWriter out, Language l, Map<String, Object> vars) {
-        String parts = l.getTranslation(text);
-        Matcher m = replacant.matcher(parts);
+    public void output(PrintWriter out, Language l, Map<String, Object> externalVariables) {
+        String parts = l.getTranslation(pattern);
+        Matcher m = placeholder.matcher(parts);
         int pos = 0;
         while (m.find()) {
-            out.print(escape(vars, parts.substring(pos, m.start())));
-            String var = store[Integer.parseInt(m.group(1))];
-            if (var.startsWith("$!")) {
-                Template.outputVar(out, l, vars, var.substring(3, var.length() - 1), true);
-            } else if (var.startsWith("!'")) {
-                out.print(var.substring(2));
-            } else if (var.startsWith("!(")) {
-                String host = (String) vars.get(Gigi.LINK_HOST);
+            out.print(escape(externalVariables, parts.substring(pos, m.start())));
+            String replacement = replacements[Integer.parseInt(m.group(1))];
+            if (replacement.startsWith("$!")) {
+                Template.outputVar(out, l, externalVariables, replacement.substring(3), true);
+            } else if (replacement.startsWith("!'")) {
+                out.print(replacement.substring(2));
+            } else if (replacement.startsWith("!(")) {
+                String host = (String) externalVariables.get(Gigi.LINK_HOST);
                 if (host == null) {
                     throw new Error("Unconfigured link-host while interpreting link-syntax.");
                 }
-                if (var.charAt(2) != '/') {
+                if (replacement.charAt(2) != '/') {
                     throw new Error("Need an absolute link for the link service.");
                 }
-                String link = "//" + host + var.substring(2);
+                String link = "//" + host + replacement.substring(2);
                 out.print("<a href='" + HTMLEncoder.encodeHTML(link) + "'>");
-            } else if (var.startsWith("$")) {
-                Template.outputVar(out, l, vars, var.substring(2, var.length() - 1), false);
+            } else if (replacement.startsWith("$")) {
+                Template.outputVar(out, l, externalVariables, replacement.substring(2), false);
             } else {
                 throw new Error("Processing error in template.");
             }
             pos = m.end();
 
         }
-        out.print(escape(vars, parts.substring(pos)));
+        out.print(escape(externalVariables, parts.substring(pos)));
     }
 
     private String escape(Map<String, Object> vars, String target) {
@@ -117,7 +137,7 @@ public final class SprintfCommand implements Translatable {
 
     @Override
     public void addTranslations(Collection<String> s) {
-        s.add(text);
+        s.add(pattern);
     }
 
     /**
@@ -128,7 +148,7 @@ public final class SprintfCommand implements Translatable {
      *            the message (to be translated) with <code>{0},{1},...</code>
      *            as placeholders.
      * @param vars
-     *            the variables to put into the placeholders.
+     *            the contents of the variables to put into the placeholders.
      * @return the constructed {@link Outputable}.
      */
     public static Outputable createSimple(String msg, Object... vars) {
@@ -136,7 +156,7 @@ public final class SprintfCommand implements Translatable {
         String[] store = new String[vars.length];
         for (int i = 0; i < vars.length; i++) {
             scope.put("autoVar" + i, vars[i]);
-            store[i] = "${autoVar" + i + "}";
+            store[i] = "${autoVar" + i;
         }
         return new Scope(new SprintfCommand(msg, Arrays.asList(store)), scope);
     }
index 930b5ef..da43ab9 100644 (file)
@@ -20,9 +20,9 @@ import club.wpia.gigi.output.template.SprintfCommand;
 
 public class Verify extends Page {
 
-    private static final SprintfCommand emailAddressVerified = new SprintfCommand("Email address {0} verified", Arrays.asList("${subject}"));
+    private static final SprintfCommand emailAddressVerified = new SprintfCommand("Email address {0} verified", Arrays.asList("${subject"));
 
-    private static final SprintfCommand domainVerified = new SprintfCommand("Domain {0} verified", Arrays.asList("${subject}"));
+    private static final SprintfCommand domainVerified = new SprintfCommand("Domain {0} verified", Arrays.asList("${subject"));
 
     private class VerificationForm extends Form {
 
index fdc9187..84f10f7 100644 (file)
@@ -65,9 +65,9 @@ public class AuthorizationContext implements Outputable, Serializable {
         return getSupporterTicketId() != null && isInGroup(Group.SUPPORTER);
     }
 
-    private static final SprintfCommand sp = new SprintfCommand("Logged in as {0} via {1}.", Arrays.asList("${username}", "${loginMethod}"));
+    private static final SprintfCommand sp = new SprintfCommand("Logged in as {0} via {1}.", Arrays.asList("${username", "${loginMethod"));
 
-    private static final SprintfCommand inner = new SprintfCommand("{0} (on behalf of {1})", Arrays.asList("${user}", "${target}"));
+    private static final SprintfCommand inner = new SprintfCommand("{0} (on behalf of {1})", Arrays.asList("${user", "${target"));
 
     @Override
     public void output(PrintWriter out, Language l, Map<String, Object> vars) {