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     */
017    package org.apache.logging.log4j.core.util;
018    
019    import org.apache.logging.log4j.util.Strings;
020    
021    
022    /**
023     * Utility class for transforming strings.
024     */
025    public final class Transform {
026    
027        private static final String CDATA_START = "<![CDATA[";
028        private static final String CDATA_END = "]]>";
029        private static final String CDATA_PSEUDO_END = "]]&gt;";
030        private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START;
031        private static final int CDATA_END_LEN = CDATA_END.length();
032    
033        private Transform() {
034        }
035    
036        /**
037         * This method takes a string which may contain HTML tags (ie,
038         * &lt;b&gt;, &lt;table&gt;, etc) and replaces any
039         * '&lt;',  '&gt;' , '&amp;' or '&quot;'
040         * characters with respective predefined entity references.
041         *
042         * @param input The text to be converted.
043         * @return The input string with the special characters replaced.
044         */
045        public static String escapeHtmlTags(final String input) {
046            //Check if the string is null, zero length or devoid of special characters
047            // if so, return what was sent in.
048    
049            if (Strings.isEmpty(input)
050                || (input.indexOf('"') == -1 &&
051                input.indexOf('&') == -1 &&
052                input.indexOf('<') == -1 &&
053                input.indexOf('>') == -1)) {
054                return input;
055            }
056    
057            //Use a StringBuilder in lieu of String concatenation -- it is
058            //much more efficient this way.
059    
060            final StringBuilder buf = new StringBuilder(input.length() + 6);
061            char ch = ' ';
062    
063            final int len = input.length();
064            for (int i = 0; i < len; i++) {
065                ch = input.charAt(i);
066                if (ch > '>') {
067                    buf.append(ch);
068                } else if (ch == '<') {
069                    buf.append("&lt;");
070                } else if (ch == '>') {
071                    buf.append("&gt;");
072                } else if (ch == '&') {
073                    buf.append("&amp;");
074                } else if (ch == '"') {
075                    buf.append("&quot;");
076                } else {
077                    buf.append(ch);
078                }
079            }
080            return buf.toString();
081        }
082    
083        /**
084         * Ensures that embedded CDEnd strings (]]&gt;) are handled properly
085         * within message, NDC and throwable tag text.
086         *
087         * @param buf StringBuilder holding the XML data to this point.  The
088         *            initial CDStart (&lt;![CDATA[) and final CDEnd (]]&gt;) of the CDATA
089         *            section are the responsibility of the calling method.
090         * @param str The String that is inserted into an existing CDATA Section within buf.
091         */
092        public static void appendEscapingCData(final StringBuilder buf, final String str) {
093            if (str != null) {
094                int end = str.indexOf(CDATA_END);
095                if (end < 0) {
096                    buf.append(str);
097                } else {
098                    int start = 0;
099                    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    }