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
018package org.apache.log4j.helpers;
019
020/**
021   Utility class for transforming strings.
022
023   @author Ceki Gülcü
024   @author Michael A. McAngus 
025 */
026public class Transform {
027
028   private static final String CDATA_START  = "<![CDATA[";
029   private static final String CDATA_END    = "]]>";
030   private static final String CDATA_PSEUDO_END = "]]&gt;";
031   private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START;
032   private static final int CDATA_END_LEN = CDATA_END.length();
033
034  /**
035   * This method takes a string which may contain HTML tags (ie,
036   * &lt;b&gt;, &lt;table&gt;, 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  static public String escapeTags(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(input == null
048       || input.length() == 0
049       || (input.indexOf('"') == -1 &&
050           input.indexOf('&') == -1 &&
051           input.indexOf('<') == -1 &&
052           input.indexOf('>') == -1)) {
053      return input;
054    }
055
056    //Use a StringBuffer in lieu of String concatenation -- it is
057    //much more efficient this way.
058
059    StringBuffer buf = new StringBuffer(input.length() + 6);
060    char ch = ' ';
061
062    int len = input.length();
063    for(int i=0; i < len; i++) {
064      ch = input.charAt(i);
065      if (ch > '>') {
066          buf.append(ch);
067      } else if(ch == '<') {
068              buf.append("&lt;");
069      } else if(ch == '>') {
070              buf.append("&gt;");
071      } else if(ch == '&') {
072              buf.append("&amp;");
073      } else if(ch == '"') {
074              buf.append("&quot;");
075      } else {
076              buf.append(ch);
077      }
078    }
079    return buf.toString();
080  }
081
082  /**
083  * Ensures that embeded CDEnd strings (]]>) are handled properly
084  * within message, NDC and throwable tag text.
085  *
086  * @param buf StringBuffer holding the XML data to this point.  The
087  * initial CDStart (<![CDATA[) and final CDEnd (]]>) of the CDATA
088  * section are the responsibility of the calling method.
089  * @param str The String that is inserted into an existing CDATA Section within buf.  
090  * */
091  static public void appendEscapingCDATA(final StringBuffer buf,
092                                         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}