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.helpers;
18  
19  
20  /**
21   * Utility class for transforming strings.
22   */
23  public final class Transform {
24  
25      private static final String CDATA_START = "<![CDATA[";
26      private static final String CDATA_END = "]]>";
27      private static final String CDATA_PSEUDO_END = "]]&gt;";
28      private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START;
29      private static final int CDATA_END_LEN = CDATA_END.length();
30  
31      private Transform() {
32      }
33  
34      /**
35       * This method takes a string which may contain HTML tags (ie,
36       * &lt;b&gt;, &lt;table&gt;, etc) and replaces any
37       * '<',  '>' , '&' or '"'
38       * characters with respective predefined entity references.
39       *
40       * @param input The text to be converted.
41       * @return The input string with the special characters replaced.
42       */
43      public static String escapeHtmlTags(final String input) {
44          //Check if the string is null, zero length or devoid of special characters
45          // if so, return what was sent in.
46  
47          if (Strings.isEmpty(input)
48              || (input.indexOf('"') == -1 &&
49              input.indexOf('&') == -1 &&
50              input.indexOf('<') == -1 &&
51              input.indexOf('>') == -1)) {
52              return input;
53          }
54  
55          //Use a StringBuilder in lieu of String concatenation -- it is
56          //much more efficient this way.
57  
58          final StringBuilder buf = new StringBuilder(input.length() + 6);
59          char ch = ' ';
60  
61          final int len = input.length();
62          for (int i = 0; i < len; i++) {
63              ch = input.charAt(i);
64              if (ch > '>') {
65                  buf.append(ch);
66              } else if (ch == '<') {
67                  buf.append("&lt;");
68              } else if (ch == '>') {
69                  buf.append("&gt;");
70              } else if (ch == '&') {
71                  buf.append("&amp;");
72              } else if (ch == '"') {
73                  buf.append("&quot;");
74              } else {
75                  buf.append(ch);
76              }
77          }
78          return buf.toString();
79      }
80  
81      /**
82       * Ensures that embedded CDEnd strings (]]>) are handled properly
83       * within message, NDC and throwable tag text.
84       *
85       * @param buf StringBuilder holding the XML data to this point.  The
86       *            initial CDStart (<![CDATA[) and final CDEnd (]]>) of the CDATA
87       *            section are the responsibility of the calling method.
88       * @param str The String that is inserted into an existing CDATA Section within buf.
89       */
90      public static void appendEscapingCDATA(final StringBuilder buf, final String str) {
91          if (str != null) {
92              int end = str.indexOf(CDATA_END);
93              if (end < 0) {
94                  buf.append(str);
95              } else {
96                  int start = 0;
97                  while (end > -1) {
98                      buf.append(str.substring(start, end));
99                      buf.append(CDATA_EMBEDED_END);
100                     start = end + CDATA_END_LEN;
101                     if (start < str.length()) {
102                         end = str.indexOf(CDATA_END, start);
103                     } else {
104                         return;
105                     }
106                 }
107                 buf.append(str.substring(start));
108             }
109         }
110     }
111 
112     /**
113      * This method takes a string which may contain JSON reserved chars and 
114      * escapes them.
115      *
116      * @param input The text to be converted.
117      * @return The input string with the special characters replaced.
118      */
119     public static String escapeJsonControlCharacters(final String input) {
120         // Check if the string is null, zero length or devoid of special characters
121         // if so, return what was sent in.
122     
123         // TODO: escaped Unicode chars.
124         
125         if (Strings.isEmpty(input)
126             || (input.indexOf('"') == -1 &&
127             input.indexOf('\\') == -1 &&
128             input.indexOf('/') == -1 &&
129             input.indexOf('\b') == -1 &&
130             input.indexOf('\f') == -1 &&
131             input.indexOf('\n') == -1 &&
132             input.indexOf('\r') == -1 && 
133             input.indexOf('\t') == -1)) {
134             return input;
135         }
136     
137         final StringBuilder buf = new StringBuilder(input.length() + 6);
138         
139         final int len = input.length();
140         for (int i = 0; i < len; i++) {
141             final char ch = input.charAt(i);
142             final String escBs = "\\";
143             switch (ch) {
144             case '"':
145                 buf.append(escBs);
146                 buf.append(ch);                
147                 break;
148             case '\\':
149                 buf.append(escBs);
150                 buf.append(ch);                
151                 break;
152             case '/':
153                 buf.append(escBs);
154                 buf.append(ch);                
155                 break;
156             case '\b':
157                 buf.append(escBs);
158                 buf.append('b');                
159                 break;
160             case '\f':
161                 buf.append(escBs);
162                 buf.append('f');                
163                 break;
164             case '\n':
165                 buf.append(escBs);
166                 buf.append('n');                
167                 break;
168             case '\r':
169                 buf.append(escBs);
170                 buf.append('r');                
171                 break;
172             case '\t':
173                 buf.append(escBs);
174                 buf.append('t');                
175                 break;
176             default: 
177                 buf.append(ch);                
178             } 
179         }
180         return buf.toString();
181     }
182 }