gt:                         StringBuffer decKey = new StringBuffer();
context: /*
   * File   : $Source: /usr/local/cvs/opencms/src/org/opencms/jsp/decorator/CmsHtmlDecorator.java,v $
   * Date   : $Date: 2008-03-14 14:29:56 $
   * Version: $Revision: 1.8 $
   *
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software GmbH, please see the
   * company website: http://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: http://www.opencms.org
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.jsp.decorator;
  
  import org.opencms.file.CmsObject;
  import org.opencms.main.CmsException;
  import org.opencms.main.CmsLog;
  import org.opencms.util.CmsHtmlParser;
  import org.opencms.util.CmsStringUtil;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.apache.commons.logging.Log;
  
  import org.htmlparser.Tag;
  import org.htmlparser.Text;
  import org.htmlparser.util.Translate;
  
  /**
   * The CmsHtmlDecorator is the main object for processing the text decorations.<p>
   * 
   * It uses the information of a <code>{@link CmsDecoratorConfiguration}</code> to process the
   * text decorations.
   *
   * @author Michael Emmerich  
   * 
   * @version $Revision: 1.8 $ 
   * 
   * @since 6.1.3 
   */
  public class CmsHtmlDecorator extends CmsHtmlParser {
  
      /** Delimiters for string seperation. */
      private static final String[] DELIMITERS = {
          " ",
          ",",
          ".",
          ";",
          ":",
          "!",
          "(",
          ")",
          "'",
          "?",
          "\"",
          "&nbsp;",
          "&quot;",
          "\r\n",
          "\n"};
  
      /** Delimiters for second level string seperation. */
      private static final String[] DELIMITERS_SECOND_LEVEL = {"-", "@", "/", ".", ","};
  
      /** Steps for forward lookup in workd list. */
      private static final int FORWARD_LOOKUP = 7;
  
      /** The log object for this class. */
      private static final Log LOG = CmsLog.getLog(CmsHtmlDecorator.class);
  
      /** Non translators, strings starting with those values must not be translated. */
      private static final String[] NON_TRANSLATORS = {"&nbsp;", "&quot;"};
  
      /** The decoration configuration.<p> */
      I_CmsDecoratorConfiguration m_config;
  
      /** Decoration bundle to be used by the decorator. */
      CmsDecorationBundle m_decorations;
  
      /** decorate flag. */
      private boolean m_decorate;
  
      /** the CmsObject. */
      private CmsObject m_cms;
  
      /**
       * Constructor, creates a new, empty CmsHtmlDecorator.<p>
       * 
       * @param cms the CmsObject
       * @throws CmsException if something goes wrong
       */
      public CmsHtmlDecorator(CmsObject cms)
      throws CmsException {
  
          m_config = new CmsDecoratorConfiguration(cms);
          m_decorations = m_config.getDecorations();
          m_result = new StringBuffer(512);
          m_echo = true;
          m_decorate = true;
  
      }
  
      /**
       * Constructor, creates a new CmsHtmlDecorator with a given configuration.<p>
       * 
       * @param cms the CmsObject
       * @param config the configuration to be used
       * 
       */
      public CmsHtmlDecorator(CmsObject cms, I_CmsDecoratorConfiguration config) {
  
          m_config = config;
          m_decorations = config.getDecorations();
          m_result = new StringBuffer(512);
          m_echo = true;
          m_decorate = true;
          m_cms = cms;
      }
  
      /**
       * Splits a String into substrings along the provided delimiter list and returns
       * the result as a List of Substrings.<p>
       *
       * @param source the String to split
       * @param delimiters the delimiters to split at
       * @param trim flag to indicate if leading and trailing whitespaces should be omitted
       * @param includeDelimiters flag to indicate if the delimiters should be included as well
       *
       * @return the List of splitted Substrings
       */
      public static List splitAsList(String source, String[] delimiters, boolean trim, boolean includeDelimiters) {
  
          List result = new ArrayList();
          String delimiter = "";
          int i = 0;
          int l = source.length();
          int n = -1;
          int max = Integer.MAX_VALUE;
  
          // find the next delimiter
          for (int j = 0; j < delimiters.length; j++) {
              if (source.indexOf(delimiters[j]) > -1) {
                  if (source.indexOf(delimiters[j]) < max) {
                      max = source.indexOf(delimiters[j]);
                      n = source.indexOf(delimiters[j]);
                      delimiter = delimiters[j];
                  }
              }
          }
  
          while (n != -1) {
              // zero - length items are not seen as tokens at start or end
              if ((i < n) || (i > 0) && (i < l)) {
                  result.add(trim ? source.substring(i, n).trim() : source.substring(i, n));
                  // add the delimiter to the list as well
                  if (includeDelimiters && n + delimiter.length() <= l) {
                      result.add(source.substring(n, n + delimiter.length()));
                  }
              } else {
                  // add the delimiter to the list as well
                  if (includeDelimiters && source.startsWith(delimiter)) {
                      result.add(delimiter);
                  }
              }
              i = n + delimiter.length();
  
              // find the next delimiter
              max = Integer.MAX_VALUE;
              n = -1;
              for (int j = 0; j < delimiters.length; j++) {
                  if (source.indexOf(delimiters[j], i) > -1) {
                      if (source.indexOf(delimiters[j], i) < max) {
                          max = source.indexOf(delimiters[j], i);
                          n = source.indexOf(delimiters[j], i);
                          delimiter = delimiters[j];
                      }
                  }
              }
  
          }
          // is there a non - empty String to cut from the tail? 
          if (n < 0) {
              n = source.length();
          }
          if (i < n) {
              result.add(trim ? source.substring(i).trim() : source.substring(i));
          }
          return result;
      }
  
      /**
       * Processes a HTML string and adds text decorations according to the decoration configuration.<p>
       * 
       * @param html a string holding the HTML code that should be added with text decorations
       * @param encoding the encoding to be used
       * @return a HTML string with the decorations added.
       * @throws Exception if something goes wrong
       */
      public String doDecoration(String html, String encoding) throws Exception {
  
          return process(html, encoding);
      }
  
      /**
       * Resets the first occurance flags of all decoration objects.<p>
       * 
       * This is nescessary if decoration objects should be used for processing more than once.     *
       */
      public void resetDecorationDefinitions() {
  
          m_config.resetMarkedDecorations();
      }
  
      /**
       * @see org.htmlparser.visitors.NodeVisitor#visitStringNode(org.htmlparser.Text)
       */
      public void visitStringNode(Text text) {
  
          appendText(text.toPlainTextString(), DELIMITERS, true);
      }
  
      /**
       * @see org.htmlparser.visitors.NodeVisitor#visitTag(org.htmlparser.Tag)
       */
      public void visitTag(Tag tag) {
  
          super.visitTag(tag);
          // get the tagname
          String tagname = tag.getTagName();
          // this is one of the tags that should not allow decoation
          if (m_config.isExcluded(tagname)) {
              m_decorate = false;
          } else {
              m_decorate = true;
          }
      }
  
      /**
       * Appends a text decoration to the output.<p>
       * 
       * A lookup is made to find a text decoration for each word in the given text.
       * If a text decoration is found, the word will be decorated and added to the output.
       * If no text decoration is found, the word alone will be added to the output.
       * 
       * @param text the text to add a text decoration for
       * @param delimiters delimiters for text seperation
       * @param recursive flag for recusrive search
       */
      private void appendText(String text, String[] delimiters, boolean recursive) {
  
          if (LOG.isDebugEnabled()) {
              LOG.debug(Messages.get().getBundle().key(Messages.LOG_HTML_DECORATOR_APPEND_TEXT_2, m_config, text));
          }
  
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(text) && m_decorate) {
  
              // split the input into single words
              List wordList = splitAsList(text, delimiters, false, true);
  
              for (int i = 0; i < wordList.size(); i++) {
                  String word = (String)wordList.get(i);
  
                  if (LOG.isDebugEnabled()) {
                      LOG.debug(Messages.get().getBundle().key(
                          Messages.LOG_HTML_DECORATOR_PROCESS_WORD_2,
                          word,
                          Boolean.valueOf(mustDecode(word, wordList, i))));
                  }
  
                  // test if the word must be decoded
                  if (mustDecode(word, wordList, i)) {
                      word = Translate.decode(word);
                      if (LOG.isDebugEnabled()) {
                          LOG.debug(Messages.get().getBundle().key(Messages.LOG_HTML_DECORATOR_DECODED_WORD_1, word));
                      }
                  }
  
                  // test if the word is no delimiter
                  // try to get a decoration if it is not
                  CmsDecorationObject decObj = null;
                  if (!hasDelimiter(word, delimiters)) {
                      decObj = (CmsDecorationObject)m_decorations.get(word);
                  }
  
                  if (LOG.isDebugEnabled()) {
                      LOG.debug(Messages.get().getBundle().key(
                          Messages.LOG_HTML_DECORATOR_DECORATION_FOUND_2,
                          decObj,
                          word));
                  }
  
                  // if there is a decoration obejct for this word, we must do the decoration
                  // if not, we must test if the word itself consits of several parts diveded by
                  // second level delimiters
                  if (decObj == null) {
                      if (hasDelimiter(word, DELIMITERS_SECOND_LEVEL) && recursive) {
                          // add the following symbol if possbile to allow the second level decoration
                          // test to make a forward lookup as well
                          String secondLevel = word;
                          if (i < wordList.size() - 1) {
                              if (!((String)wordList.get(i + 1)).equals(" ")) {
                                  secondLevel = word + (String)wordList.get(i + 1);
                                  i++;
                              }
                          }
                          appendText(secondLevel, DELIMITERS_SECOND_LEVEL, false);
                      } else {
                          // make a forward lookup to the next elements of the word list to check
                          // if the combination of word and delimiter can be found as a decoration key
                          // an example would be "Dr." wich must be decorated with "Doctor"
  