1 /**
2 * Logback: the reliable, generic, fast and flexible logging framework.
3 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4 *
5 * This program and the accompanying materials are dual-licensed under
6 * either the terms of the Eclipse Public License v1.0 as published by
7 * the Eclipse Foundation
8 *
9 * or (per the licensee's choosing)
10 *
11 * under the terms of the GNU Lesser General Public License version 2.1
12 * as published by the Free Software Foundation.
13 */
14 package ch.qos.logback.core.helpers;
15
16 import java.util.regex.Pattern;
17
18 /**
19 * Utility class for transforming strings.
20 *
21 * @author Ceki Gülcü
22 * @author Michael A. McAngus
23 */
24 public class Transform {
25 private static final String CDATA_START = "<![CDATA[";
26 private static final String CDATA_END = "]]>";
27 private static final String CDATA_PSEUDO_END = "]]>";
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 private static final Pattern UNSAFE_XML_CHARS = Pattern.compile("[\u0000-\u0008\u000b\u000c\u000e-\u001f<>&'\"]");
31
32 /**
33 * This method takes a string which may contain HTML tags (ie, <b>,
34 * <table>, etc) and replaces any '<','>' ... characters with
35 * respective predefined entity references.
36 *
37 * @param input The text to be converted.
38 */
39 public static String escapeTags(final String input) {
40 if (input == null || input.length() == 0 || !UNSAFE_XML_CHARS.matcher(input).find()) {
41 return input;
42 }
43 StringBuffer buf = new StringBuffer(input);
44 return escapeTags(buf);
45 }
46
47 /**
48 * This method takes a StringBuilder which may contain HTML tags (ie, <b>,
49 * <table>, etc) and replaces any '<' and '>' characters with
50 * respective predefined entity references.
51 *
52 * @param buf StringBuffer to transform
53 * @return
54 */
55 public static String escapeTags(final StringBuffer buf) {
56 for (int i = 0; i < buf.length(); i++) {
57 char ch = buf.charAt(i);
58 switch (ch) {
59 case '\t':
60 case '\n':
61 case '\r':
62 // These characters are below '\u0020' but are allowed:
63 break;
64 case '&':
65 buf.replace(i, i + 1, "&");
66 break;
67 case '<':
68 buf.replace(i, i + 1, "<");
69 break;
70 case '>':
71 buf.replace(i, i + 1, ">");
72 break;
73 case '"':
74 buf.replace(i, i + 1, """);
75 break;
76 case '\'':
77 buf.replace(i, i + 1, "'");
78 break;
79 default:
80 if (ch < '\u0020') {
81 // These characters are not allowed,
82 // replace them with "Object replacement character":
83 buf.replace(i, i + 1, "\uFFFD");
84 }
85 break;
86 }
87 }
88 return buf.toString();
89 }
90
91 /**
92 * Ensures that embedded CDEnd strings (]]>) are handled properly within
93 * message, NDC and throwable tag text.
94 *
95 * @param output Writer. The initial CDStart (<![CDATA[) and final CDEnd
96 * (]]>) of the CDATA section are the responsibility of the
97 * calling method.
98 *
99 * @param str The String that is inserted into an existing CDATA Section.
100 */
101 public static void appendEscapingCDATA(StringBuilder output, String str) {
102 if (str == null) {
103 return;
104 }
105
106 int end = str.indexOf(CDATA_END);
107
108 if (end < 0) {
109 output.append(str);
110
111 return;
112 }
113
114 int start = 0;
115
116 while (end > -1) {
117 output.append(str.substring(start, end));
118 output.append(CDATA_EMBEDED_END);
119 start = end + CDATA_END_LEN;
120
121 if (start < str.length()) {
122 end = str.indexOf(CDATA_END, start);
123 } else {
124 return;
125 }
126 }
127
128 output.append(str.substring(start));
129 }
130 }