View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.util;
18  
19  import org.apache.logging.log4j.util.Strings;
20  
21  
22  /**
23   * Utility class for transforming strings.
24   */
25  public final class Transform {
26  
27      private static final String CDATA_START = "<![CDATA[";
28      private static final String CDATA_END = "]]>";
29      private static final String CDATA_PSEUDO_END = "]]&gt;";
30      private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START;
31      private static final int CDATA_END_LEN = CDATA_END.length();
32  
33      private Transform() {
34      }
35  
36      /**
37       * This method takes a string which may contain HTML tags (ie,
38       * &lt;b&gt;, &lt;table&gt;, etc) and replaces any
39       * '&lt;',  '&gt;' , '&amp;' or '&quot;'
40       * characters with respective predefined entity references.
41       *
42       * @param input The text to be converted.
43       * @return The input string with the special characters replaced.
44       */
45      public static String escapeHtmlTags(final String input) {
46          // Check if the string is null, zero length or devoid of special characters
47          // if so, return what was sent in.
48  
49          if (Strings.isEmpty(input)
50              || (input.indexOf('"') == -1 &&
51              input.indexOf('&') == -1 &&
52              input.indexOf('<') == -1 &&
53              input.indexOf('>') == -1)) {
54              return input;
55          }
56  
57          //Use a StringBuilder in lieu of String concatenation -- it is
58          //much more efficient this way.
59  
60          final StringBuilder buf = new StringBuilder(input.length() + 6);
61  
62          final int len = input.length();
63          for (int i = 0; i < len; i++) {
64              final char ch = input.charAt(i);
65              if (ch > '>') {
66                  buf.append(ch);
67              } else if (ch == '<') {
68                  buf.append("&lt;");
69              } else if (ch == '>') {
70                  buf.append("&gt;");
71              } else if (ch == '&') {
72                  buf.append("&amp;");
73              } else if (ch == '"') {
74                  buf.append("&quot;");
75              } else {
76                  buf.append(ch);
77              }
78          }
79          return buf.toString();
80      }
81  
82      /**
83       * Ensures that embedded CDEnd strings (]]&gt;) are handled properly
84       * within message, NDC and throwable tag text.
85       *
86       * @param buf StringBuilder holding the XML data to this point.  The
87       *            initial CDStart (&lt;![CDATA[) and final CDEnd (]]&gt;) of the CDATA
88       *            section are the responsibility of the calling method.
89       * @param str The String that is inserted into an existing CDATA Section within buf.
90       */
91      public static void appendEscapingCData(final StringBuilder buf, final String str) {
92          if (str != null) {
93              int end = str.indexOf(CDATA_END);
94              if (end < 0) {
95                  buf.append(str);
96              } else {
97                  int start = 0;
98                  while (end > -1) {
99                      buf.append(str.substring(start, end));
100                     buf.append(CDATA_EMBEDED_END);
101                     start = end + CDATA_END_LEN;
102                     if (start < str.length()) {
103                         end = str.indexOf(CDATA_END, start);
104                     } else {
105                         return;
106                     }
107                 }
108                 buf.append(str.substring(start));
109             }
110         }
111     }
112 
113     /**
114      * This method takes a string which may contain JSON reserved chars and
115      * escapes them.
116      *
117      * @param input The text to be converted.
118      * @return The input string with the special characters replaced.
119      */
120     public static String escapeJsonControlCharacters(final String input) {
121         // Check if the string is null, zero length or devoid of special characters
122         // if so, return what was sent in.
123 
124         // TODO: escaped Unicode chars.
125 
126         if (Strings.isEmpty(input)
127             || (input.indexOf('"') == -1 &&
128             input.indexOf('\\') == -1 &&
129             input.indexOf('/') == -1 &&
130             input.indexOf('\b') == -1 &&
131             input.indexOf('\f') == -1 &&
132             input.indexOf('\n') == -1 &&
133             input.indexOf('\r') == -1 &&
134             input.indexOf('\t') == -1)) {
135             return input;
136         }
137 
138         final StringBuilder buf = new StringBuilder(input.length() + 6);
139 
140         final int len = input.length();
141         for (int i = 0; i < len; i++) {
142             final char ch = input.charAt(i);
143             final String escBs = "\\";
144             switch (ch) {
145             case '"':
146                 buf.append(escBs);
147                 buf.append(ch);
148                 break;
149             case '\\':
150                 buf.append(escBs);
151                 buf.append(ch);
152                 break;
153             case '/':
154                 buf.append(escBs);
155                 buf.append(ch);
156                 break;
157             case '\b':
158                 buf.append(escBs);
159                 buf.append('b');
160                 break;
161             case '\f':
162                 buf.append(escBs);
163                 buf.append('f');
164                 break;
165             case '\n':
166                 buf.append(escBs);
167                 buf.append('n');
168                 break;
169             case '\r':
170                 buf.append(escBs);
171                 buf.append('r');
172                 break;
173             case '\t':
174                 buf.append(escBs);
175                 buf.append('t');
176                 break;
177             default:
178                 buf.append(ch);
179             }
180         }
181         return buf.toString();
182     }
183 }