View Javadoc
1   package com.atlassian.refapp.sal.pluginsettings;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   /**
7    * This class helps substitute characters that are not supported in xml (see all the chars defined in escapeString variable) to escaped format.
8    * Its implementation is based on $FISHEYE_TRUNK/src/java/com/cenqua/fisheye/util/XmlUtils.java.
9    *
10   * @since 2.7.0
11   */
12  public class XmlUtils {
13  
14      private static final char ESCAPE_SYM1 = '!';
15      private static final char ESCAPE_SYM2 = '@';
16      private static final char ESCAPE_SYM3 = '#';
17      private static final char ESCAPE_SYM4 = '$';
18      private static final Map<Character, String> charToCode;
19  
20      static {
21          final String escapeString = "\u0000\u0001\u0002\u0003\u0004\u0005" +
22                  "\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012" +
23                  "\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C" +
24                  "\u001D\u001E\u001F\uFFFE\uFFFF";
25  
26          charToCode = new HashMap<Character, String>();
27  
28          for (int i = 0; i < escapeString.length(); i++) {
29              final char c = escapeString.charAt(i);
30              charToCode.put(c, "" + ESCAPE_SYM1 + ESCAPE_SYM2
31                      + ESCAPE_SYM3 + ESCAPE_SYM4
32                      + String.format("%04X", (int) c));
33          }
34      }
35  
36      // Not for instantiation.
37      private XmlUtils() {
38      }
39  
40      /**
41       * Replaces all characters that are illegal in XML with a custom-made unicode escape sequence.
42       * escape sequence '!@#$[0-9][0-9][0-9][0-9]'. When <code>null</code> is
43       * passed into this method, <code>null</code> is returned.
44       */
45      public static String escape(String string) {
46          if (string == null) {
47              return null;
48          }
49  
50          // working buffer.
51          StringBuilder copy = new StringBuilder();
52  
53          // just consider every char for a chance of escaping.
54          for (int i = 0; i < string.length(); i++) {
55              copy.append(escapeChar(string.charAt(i)));
56          }
57          return copy.toString();
58      }
59  
60      private static String escapeChar(char c) {
61          String escaped = charToCode.get(c);
62          // escape the char if its escape sequence is defined.
63          return escaped == null ? String.valueOf(c) : escaped;
64      }
65  
66      /**
67       * Substitutes all occurances of '!@#$[0-9][0-9][0-9][0-9]' with their
68       * corresponding character codes. When <code>null</code> is passed into this
69       * method, <code>null</code> is returned.
70       */
71      public static String unescape(String string) {
72  
73          if (string == null) {
74              return null;
75  
76          } else {
77  
78              // the conversion buffer.
79              final StringBuilder copy = new StringBuilder();
80  
81              for (int i = 0; i < string.length(); i++) {
82                  char c = string.charAt(i);
83                  // if there is a possibility that this is an escape sequence (subject to the parsability of the code part).
84                  if (i + 7 < string.length()
85                          && c == ESCAPE_SYM1
86                          && string.charAt(i + 1) == ESCAPE_SYM2
87                          && string.charAt(i + 2) == ESCAPE_SYM3
88                          && string.charAt(i + 3) == ESCAPE_SYM4) {
89                      // extract the number code
90                      String value = string.substring(i + 4, i + 8);
91                      try {
92                          // if this is parsable fine, we just convert it to the original.
93                          int charCode = Integer.parseInt(value, 16);
94                          copy.append((char) charCode);
95                          i += 7;
96                      } catch (NumberFormatException nfe) {
97                          // if it cannot be parsed to base 16, then this is not an escape sequence
98                          // we simply append the char being read to the buffer.
99                          copy.append(c);
100                     }
101                 }
102                 // otherwise, just append the char we just read to the buffer.
103                 else {
104                     copy.append(c);
105                 }
106             }
107 
108             return copy.toString();
109         }
110     }
111 }
112