View Javadoc

1   package com.atlassian.templaterenderer;
2   
3   import com.atlassian.templaterenderer.annotations.HtmlSafe;
4   
5   import java.io.IOException;
6   import java.io.StringWriter;
7   import java.io.Writer;
8   
9   /**
10   * Escaping that was ripped from Apache Commons-Lang StringUtils class, modified to escape the '<' character as a
11   * unicode string as described in AG-1005.
12   */
13  public class JavaScriptEscaper {
14      /**
15       * <p>
16       * Escapes the characters in a <code>String</code> using JavaScript String
17       * rules.
18       * </p>
19       * <p>
20       * Escapes any values it finds into their JavaScript String literal form. Deals
21       * correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)
22       * </p>
23       *
24       * <p>
25       * So a tab becomes the characters <code>'\\'</code> and <code>'t'</code>.
26       * </p>
27       *
28       * <p>
29       * The only difference between Java strings and JavaScript strings is that
30       * in JavaScript, a single quote must be escaped.
31       * </p>
32       *
33       * <p>
34       * Example:
35       *
36       * <pre>
37       * input string: He didn't say, "Stop!"
38       * output string: He didn\'t say, \"Stop!\"
39       * </pre>
40       *
41       * @param str String to escape values in, may be null
42       * @return String with escaped values, <code>null</code> if null string
43       * input
44       */
45      @HtmlSafe
46      public static String escape(String str) {
47          return escapeJavaStyleString(str, true);
48      }
49  
50      /**
51       * <p>
52       * Escapes the characters in a <code>String</code> using JavaScript String
53       * rules to a <code>Writer</code>.
54       * </p>
55       *
56       * <p>
57       * A <code>null</code> string input has no effect.
58       * </p>
59       *
60       * @param out Writer to write escaped string into
61       * @param str String to escape values in, may be null
62       * @throws IllegalArgumentException if the Writer is <code>null</code>
63       * @throws IOException              if error occurs on underlying Writer
64       * @see #escape(java.lang.String)
65       **/
66      @HtmlSafe
67      public static void escape(Writer out, String str) throws IOException {
68          escapeJavaStyleString(out, str, true);
69      }
70  
71      /**
72       * <p>
73       * Worker method for the {@link #escape(String)} method.
74       * </p>
75       *
76       * @param str                String to escape values in, may be null
77       * @param escapeSingleQuotes escapes single quotes if <code>true</code>
78       * @return the escaped string
79       */
80      private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes) {
81          if (str == null) {
82              return null;
83          }
84          try {
85              StringWriter writer = new StringWriter(str.length() * 2);
86              escapeJavaStyleString(writer, str, escapeSingleQuotes);
87              return writer.toString();
88          } catch (IOException ioe) {
89              // this should never ever happen while writing to a StringWriter
90              ioe.printStackTrace();
91              return null;
92          }
93      }
94  
95      /**
96       * <p>
97       * Worker method for the {@link #escape(String)} method.
98       * </p>
99       *
100      * @param out               write to receieve the escaped string
101      * @param str               String to escape values in, may be null
102      * @param escapeSingleQuote escapes single quotes if <code>true</code>
103      * @throws IOException if an IOException occurs
104      */
105     private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote) throws IOException {
106         if (out == null) {
107             throw new IllegalArgumentException("The Writer must not be null");
108         }
109         if (str == null) {
110             return;
111         }
112         int sz;
113         sz = str.length();
114         for (int i = 0; i < sz; i++) {
115             char ch = str.charAt(i);
116 
117             // handle unicode
118             if (ch > 0xfff) {
119                 out.write("\\u" + hex(ch));
120             } else if (ch > 0xff) {
121                 out.write("\\u0" + hex(ch));
122             } else if (ch > 0x7f) {
123                 out.write("\\u00" + hex(ch));
124             } else if (ch < 32) {
125                 switch (ch) {
126                     case '\b':
127                         out.write('\\');
128                         out.write('b');
129                         break;
130                     case '\n':
131                         out.write('\\');
132                         out.write('n');
133                         break;
134                     case '\t':
135                         out.write('\\');
136                         out.write('t');
137                         break;
138                     case '\f':
139                         out.write('\\');
140                         out.write('f');
141                         break;
142                     case '\r':
143                         out.write('\\');
144                         out.write('r');
145                         break;
146                     default:
147                         if (ch > 0xf) {
148                             out.write("\\u00" + hex(ch));
149                         } else {
150                             out.write("\\u000" + hex(ch));
151                         }
152                         break;
153                 }
154             } else {
155                 switch (ch) {
156                     case '\'':
157                         if (escapeSingleQuote) {
158                             out.write('\\');
159                         }
160                         out.write('\'');
161                         break;
162                     case '"':
163                         out.write('\\');
164                         out.write('"');
165                         break;
166                     case '\\':
167                         out.write('\\');
168                         out.write('\\');
169                         break;
170                     case '/':
171                         out.write('\\');
172                         out.write('/');
173                         break;
174                     case '<':
175                         out.write("\\u003c");
176                         break;
177                     default:
178                         out.write(ch);
179                         break;
180                 }
181             }
182         }
183     }
184 
185     /**
186      * <p>
187      * Returns an upper case hexadecimal <code>String</code> for the given
188      * character.
189      * </p>
190      *
191      * @param ch The character to convert.
192      * @return An upper case hexadecimal <code>String</code>
193      */
194     public static String hex(char ch) {
195         return Integer.toHexString(ch).toUpperCase();
196     }
197 }
198