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          char ch = ' ';
62  
63          final int len = input.length();
64          for (int i = 0; i < len; i++) {
65              ch = input.charAt(i);
66              if (ch > '>') {
67                  buf.append(ch);
68              } else if (ch == '<') {
69                  buf.append("&lt;");
70              } else if (ch == '>') {
71                  buf.append("&gt;");
72              } else if (ch == '&') {
73                  buf.append("&amp;");
74              } else if (ch == '"') {
75                  buf.append("&quot;");
76              } else {
77                  buf.append(ch);
78              }
79          }
80          return buf.toString();
81      }
82  
83      /**
84       * Ensures that embedded CDEnd strings (]]&gt;) are handled properly
85       * within message, NDC and throwable tag text.
86       *
87       * @param buf StringBuilder holding the XML data to this point.  The
88       *            initial CDStart (&lt;![CDATA[) and final CDEnd (]]&gt;) of the CDATA
89       *            section are the responsibility of the calling method.
90       * @param str The String that is inserted into an existing CDATA Section within buf.
91       */
92      public static void appendEscapingCData(final StringBuilder buf, final String str) {
93          if (str != null) {
94              int end = str.indexOf(CDATA_END);
95              if (end < 0) {
96                  buf.append(str);
97              } else {
98                  int start = 0;
99                  while (end > -1) {
100                     buf.append(str.substring(start, end));
101                     buf.append(CDATA_EMBEDED_END);
102                     start = end + CDATA_END_LEN;
103                     if (start < str.length()) {
104                         end = str.indexOf(CDATA_END, start);
105                     } else {
106                         return;
107                     }
108                 }
109                 buf.append(str.substring(start));
110             }
111         }
112     }
113 
114     /**
115      * This method takes a string which may contain JSON reserved chars and
116      * escapes them.
117      *
118      * @param input The text to be converted.
119      * @return The input string with the special characters replaced.
120      */
121     public static String escapeJsonControlCharacters(final String input) {
122         // Check if the string is null, zero length or devoid of special characters
123         // if so, return what was sent in.
124 
125         // TODO: escaped Unicode chars.
126 
127         if (Strings.isEmpty(input)
128             || (input.indexOf('"') == -1 &&
129             input.indexOf('\\') == -1 &&
130             input.indexOf('/') == -1 &&
131             input.indexOf('\b') == -1 &&
132             input.indexOf('\f') == -1 &&
133             input.indexOf('\n') == -1 &&
134             input.indexOf('\r') == -1 &&
135             input.indexOf('\t') == -1)) {
136             return input;
137         }
138 
139         final StringBuilder buf = new StringBuilder(input.length() + 6);
140 
141         final int len = input.length();
142         for (int i = 0; i < len; i++) {
143             final char ch = input.charAt(i);
144             final String escBs = "\\";
145             switch (ch) {
146             case '"':
147                 buf.append(escBs);
148                 buf.append(ch);
149                 break;
150             case '\\':
151                 buf.append(escBs);
152                 buf.append(ch);
153                 break;
154             case '/':
155                 buf.append(escBs);
156                 buf.append(ch);
157                 break;
158             case '\b':
159                 buf.append(escBs);
160                 buf.append('b');
161                 break;
162             case '\f':
163                 buf.append(escBs);
164                 buf.append('f');
165                 break;
166             case '\n':
167                 buf.append(escBs);
168                 buf.append('n');
169                 break;
170             case '\r':
171                 buf.append(escBs);
172                 buf.append('r');
173                 break;
174             case '\t':
175                 buf.append(escBs);
176                 buf.append('t');
177                 break;
178             default:
179                 buf.append(ch);
180             }
181         }
182         return buf.toString();
183     }
184 }