JAL-3130 adapted getdown src. attempt 2. first attempt failed due to cp'ed .git files
[jalview.git] / getdown / src / getdown / core / src / main / java / com / threerings / getdown / util / MessageUtil.java
1 //
2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
5
6 package com.threerings.getdown.util;
7
8 public class MessageUtil {
9
10     /**
11      * Returns whether or not the provided string is tainted. See {@link #taint}. Null strings
12      * are considered untainted.
13      */
14     public static boolean isTainted (String text)
15     {
16         return text != null && text.startsWith(TAINT_CHAR);
17     }
18
19     /**
20      * Call this to "taint" any string that has been entered by an entity outside the application
21      * so that the translation code knows not to attempt to translate this string when doing
22      * recursive translations.
23      */
24     public static String taint (Object text)
25     {
26         return TAINT_CHAR + text;
27     }
28
29     /**
30      * Removes the tainting character added to a string by {@link #taint}. If the provided string
31      * is not tainted, this silently returns the originally provided string.
32      */
33     public static String untaint (String text)
34     {
35         return isTainted(text) ? text.substring(TAINT_CHAR.length()) : text;
36     }
37
38     /**
39      * Composes a message key with an array of arguments. The message can subsequently be
40      * decomposed and translated without prior knowledge of how many arguments were provided.
41      */
42     public static String compose (String key, Object... args)
43     {
44         StringBuilder buf = new StringBuilder();
45         buf.append(key);
46         buf.append('|');
47         for (int i = 0; i < args.length; i++) {
48             if (i > 0) {
49                 buf.append('|');
50             }
51             // escape the string while adding to the buffer
52             String arg = (args[i] == null) ? "" : String.valueOf(args[i]);
53             int alength = arg.length();
54             for (int p = 0; p < alength; p++) {
55                 char ch = arg.charAt(p);
56                 if (ch == '|') {
57                     buf.append("\\!");
58                 } else if (ch == '\\') {
59                     buf.append("\\\\");
60                 } else {
61                     buf.append(ch);
62                 }
63             }
64         }
65         return buf.toString();
66     }
67
68     /**
69      * Compose a message with String args. This is just a convenience so callers do not have to
70      * cast their String[] to an Object[].
71      */
72     public static String compose (String key, String... args)
73     {
74         return compose(key, (Object[]) args);
75     }
76
77     /**
78      * A convenience method for calling {@link #compose(String,Object[])} with an array of
79      * arguments that will be automatically tainted (see {@link #taint}).
80      */
81     public static String tcompose (String key, Object... args)
82     {
83         int acount = args.length;
84         String[] targs = new String[acount];
85         for (int ii = 0; ii < acount; ii++) {
86             targs[ii] = taint(args[ii]);
87         }
88         return compose(key, (Object[]) targs);
89     }
90
91     /**
92      * A convenience method for calling {@link #compose(String,String[])} with an array of argument
93      * that will be automatically tainted.
94      */
95     public static String tcompose (String key, String... args)
96     {
97         for (int ii = 0, nn = args.length; ii < nn; ii++) {
98             args[ii] = taint(args[ii]);
99         }
100         return compose(key, args);
101     }
102
103     /**
104      * Used to escape single quotes so that they are not interpreted by <code>MessageFormat</code>.
105      * As we assume all single quotes are to be escaped, we cannot use the characters
106      * <code>{</code> and <code>}</code> in our translation strings, but this is a small price to
107      * pay to have to differentiate between messages that will and won't eventually be parsed by a
108      * <code>MessageFormat</code> instance.
109      */
110     public static String escape (String message)
111     {
112         return message.replace("'", "''");
113     }
114
115     /**
116      * Unescapes characters that are escaped in a call to compose.
117      */
118     public static String unescape (String value)
119     {
120         int bsidx = value.indexOf('\\');
121         if (bsidx == -1) {
122             return value;
123         }
124
125         StringBuilder buf = new StringBuilder();
126         int vlength = value.length();
127         for (int ii = 0; ii < vlength; ii++) {
128             char ch = value.charAt(ii);
129             if (ch != '\\' || ii == vlength-1) {
130                 buf.append(ch);
131             } else {
132                 // look at the next character
133                 ch = value.charAt(++ii);
134                 buf.append((ch == '!') ? '|' : ch);
135             }
136         }
137
138         return buf.toString();
139     }
140
141     /** Text prefixed by this character will be considered tainted when doing recursive
142      * translations and won't be translated. */
143     protected static final String TAINT_CHAR = "~";
144 }