001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.helpers; 018 019 020/** 021 * Utility class for transforming strings. 022 */ 023public final class Transform { 024 025 private static final String CDATA_START = "<![CDATA["; 026 private static final String CDATA_END = "]]>"; 027 private static final String CDATA_PSEUDO_END = "]]>"; 028 private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START; 029 private static final int CDATA_END_LEN = CDATA_END.length(); 030 031 private Transform() { 032 } 033 034 /** 035 * This method takes a string which may contain HTML tags (ie, 036 * <b>, <table>, etc) and replaces any 037 * '<', '>' , '&' or '"' 038 * characters with respective predefined entity references. 039 * 040 * @param input The text to be converted. 041 * @return The input string with the special characters replaced. 042 */ 043 public static String escapeHtmlTags(final String input) { 044 //Check if the string is null, zero length or devoid of special characters 045 // if so, return what was sent in. 046 047 if (Strings.isEmpty(input) 048 || (input.indexOf('"') == -1 && 049 input.indexOf('&') == -1 && 050 input.indexOf('<') == -1 && 051 input.indexOf('>') == -1)) { 052 return input; 053 } 054 055 //Use a StringBuilder in lieu of String concatenation -- it is 056 //much more efficient this way. 057 058 final StringBuilder buf = new StringBuilder(input.length() + 6); 059 char ch = ' '; 060 061 final int len = input.length(); 062 for (int i = 0; i < len; i++) { 063 ch = input.charAt(i); 064 if (ch > '>') { 065 buf.append(ch); 066 } else if (ch == '<') { 067 buf.append("<"); 068 } else if (ch == '>') { 069 buf.append(">"); 070 } else if (ch == '&') { 071 buf.append("&"); 072 } else if (ch == '"') { 073 buf.append("""); 074 } else { 075 buf.append(ch); 076 } 077 } 078 return buf.toString(); 079 } 080 081 /** 082 * Ensures that embedded CDEnd strings (]]>) are handled properly 083 * within message, NDC and throwable tag text. 084 * 085 * @param buf StringBuilder holding the XML data to this point. The 086 * initial CDStart (<![CDATA[) and final CDEnd (]]>) of the CDATA 087 * section are the responsibility of the calling method. 088 * @param str The String that is inserted into an existing CDATA Section within buf. 089 */ 090 public static void appendEscapingCDATA(final StringBuilder buf, final String str) { 091 if (str != null) { 092 int end = str.indexOf(CDATA_END); 093 if (end < 0) { 094 buf.append(str); 095 } else { 096 int start = 0; 097 while (end > -1) { 098 buf.append(str.substring(start, end)); 099 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}