View Javadoc
1   package com.atlassian.dbexporter.node.stax;
2   
3   import javax.xml.stream.XMLInputFactory;
4   import javax.xml.stream.XMLOutputFactory;
5   import java.text.DateFormat;
6   import java.text.SimpleDateFormat;
7   import java.util.Arrays;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.TimeZone;
11  
12  import static com.google.common.collect.Lists.newArrayList;
13  import static com.google.common.collect.Maps.newHashMap;
14  
15  final class StaxUtils {
16      private static final String WOODSTOX_INPUT_FACTORY = "com.ctc.wstx.stax.WstxInputFactory";
17      private static final String DEFAULT_INPUT_FACTORY = "com.sun.xml.internal.stream.XMLInputFactoryImpl";
18      private static final String WOODSTOX_OUTPUT_FACTORY = "com.ctc.wstx.stax.WstxOutputFactory";
19      private static final String DEFAULT_OUTPUT_FACTORY = "com.sun.xml.internal.stream.XMLOutputFactoryImpl";
20  
21      private static final char BACKSLASH = '\\';
22      private static final Map<Character, String> CHAR_TO_UNICODE;
23  
24      static {
25          final String escapeString = "\u0000\u0001\u0002\u0003\u0004\u0005" +
26                  "\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012" +
27                  "\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C" +
28                  "\u001D\u001E\u001F\uFFFE\uFFFF";
29  
30          CHAR_TO_UNICODE = newHashMap();
31  
32          for (int i = 0; i < escapeString.length(); i++) {
33              final char c = escapeString.charAt(i);
34              CHAR_TO_UNICODE.put(c, String.format("\\u%04X", (int) c));
35          }
36          CHAR_TO_UNICODE.put('\\', "\\\\");
37      }
38  
39      /**
40       * Replaces all characters that are illegal in XML with a Java-like unicode
41       * escape sequence '\\u[0-9][0-9][0-9][0-9]'. When <code>null</code> is
42       * passed into this method, <code>null</code> is returned.
43       *
44       * @see #unicodeDecode(String)
45       */
46      public static String unicodeEncode(String string) {
47  
48          if (string == null) {
49              return null;
50          } else {
51              final StringBuilder copy = new StringBuilder();
52              copy.setLength(0);
53              boolean copied = false;
54  
55              for (int i = 0; i < string.length(); i++) {
56  
57                  char c = string.charAt(i);
58                  String s = CHAR_TO_UNICODE.get(c);
59                  if (s != null && !copied) {
60                      copy.append(string.substring(0, i));
61                      copied = true;
62                  }
63                  if (copied) {
64                      if (s == null) {
65                          copy.append(c);
66                      } else {
67                          copy.append(s);
68                      }
69                  }
70              }
71              return copied ? copy.toString() : string;
72          }
73      }
74  
75      /**
76       * Substitutes all occurrences of '\\u[0-9][0-9][0-9][0-9]' with their
77       * corresponding character codes. When <code>null</code> is passed into this
78       * method, <code>null</code> is returned.
79       *
80       * @see #unicodeEncode(String)
81       */
82      public static String unicodeDecode(String string) {
83  
84          if (string == null) {
85              return null;
86          } else {
87              final StringBuilder copy = new StringBuilder();
88              copy.setLength(0);
89              boolean copied = false;
90  
91              for (int i = 0; i < string.length(); i++) {
92                  char c = string.charAt(i);
93                  if (c == BACKSLASH) {
94                      if (!copied) {
95                          copy.append(string.substring(0, i));
96                          copied = true;
97                      }
98                      if (string.charAt(++i) == BACKSLASH) {
99                          copy.append(BACKSLASH);
100                     } else {
101                         // must be a unicode string
102                         String value = string.substring(++i, i + 4);
103                         copy.append((char) Integer.parseInt(value, 16));
104                         i += 3;
105                     }
106                 } else if (copied) {
107                     copy.append(c);
108                 }
109             }
110             return copied ? copy.toString() : string;
111         }
112     }
113 
114     public static DateFormat newDateFormat() {
115         final DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
116         sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
117         return sdf;
118     }
119 
120     static XMLInputFactory newXmlInputFactory() {
121         return newInstance(XMLInputFactory.class, WOODSTOX_INPUT_FACTORY, DEFAULT_INPUT_FACTORY);
122     }
123 
124     static XMLOutputFactory newXmlOutputFactory() {
125         return newInstance(XMLOutputFactory.class, WOODSTOX_OUTPUT_FACTORY, DEFAULT_OUTPUT_FACTORY);
126     }
127 
128     private static <T> T newInstance(Class<T> type, String... classNames) {
129         final List<XmlFactoryException> exceptions = newArrayList();
130         for (String className : classNames) {
131             try {
132                 return newInstance(type, className);
133             } catch (XmlFactoryException e) {
134                 exceptions.add(e);
135             }
136         }
137         throw new XmlFactoryException("Could not instantiate any of " + Arrays.toString(classNames), exceptions.toArray(new Throwable[exceptions.size()]));
138     }
139 
140     private static <T> T newInstance(Class<T> type, String className) {
141         try {
142             return type.cast(StaxUtils.class.getClassLoader().loadClass(className).newInstance());
143         } catch (InstantiationException e) {
144             throw new XmlFactoryException("Could not instantiate " + className, e);
145         } catch (IllegalAccessException e) {
146             throw new XmlFactoryException("Could not instantiate " + className, e);
147         } catch (ClassNotFoundException e) {
148             throw new XmlFactoryException("Could not find class " + className, e);
149         }
150     }
151 }