/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.helpers; import java.util.regex.Pattern; /** * Utility class for transforming strings. * * @author Ceki Gülcü * @author Michael A. McAngus */ public class Transform { private static final String CDATA_START = "<![CDATA["; private static final String CDATA_END = "]]>"; private static final String CDATA_PSEUDO_END = "]]>"; private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START; private static final int CDATA_END_LEN = CDATA_END.length(); private static final Pattern UNSAFE_XML_CHARS = Pattern.compile("[\u0000-\u0008\u000b\u000c\u000e-\u001f<>&'\"]"); /** * This method takes a string which may contain HTML tags (ie, <b>, * <table>, etc) and replaces any '<','>' ... characters with * respective predefined entity references. * * @param input * The text to be converted. */ public static String escapeTags(final String input) { if (input == null || input.length() == 0 || !UNSAFE_XML_CHARS.matcher(input).find()) { return input; } StringBuffer buf = new StringBuffer(input); return escapeTags(buf); } /** * This method takes a StringBuilder which may contain HTML tags (ie, <b>, * <table>, etc) and replaces any '<' and '>' characters with * respective predefined entity references. * @param buf StringBuffer to transform * @return */ public static String escapeTags(final StringBuffer buf) { for (int i = 0; i < buf.length(); i++) { char ch = buf.charAt(i); switch (ch) { case '\t': case '\n': case '\r': // These characters are below '\u0020' but are allowed: break; case '&': buf.replace(i, i + 1, "&"); break; case '<': buf.replace(i, i + 1, "<"); break; case '>': buf.replace(i, i + 1, ">"); break; case '"': buf.replace(i, i + 1, """); break; case '\'': buf.replace(i, i + 1, "'"); break; default: if (ch < '\u0020') { // These characters are not allowed, // replace them with "Object replacement character": buf.replace(i, i + 1, "\uFFFD"); } break; } } return buf.toString(); } /** * Ensures that embedded CDEnd strings (]]>) are handled properly within * message, NDC and throwable tag text. * * @param output * Writer. The initial CDSutart (<![CDATA[) and final CDEnd (]]>) of * the CDATA section are the responsibility of the calling method. * * @param str * The String that is inserted into an existing CDATA Section. */ public static void appendEscapingCDATA(StringBuilder output, String str) { if (str == null) { return; } int end = str.indexOf(CDATA_END); if (end < 0) { output.append(str); return; } int start = 0; while (end > -1) { output.append(str.substring(start, end)); output.append(CDATA_EMBEDED_END); start = end + CDATA_END_LEN; if (start < str.length()) { end = str.indexOf(CDATA_END, start); } else { return; } } output.append(str.substring(start)); } }