KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > jsp > decorator > CmsHtmlDecorator


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/jsp/decorator/CmsHtmlDecorator.java,v $
3  * Date : $Date: 2006/09/19 14:29:08 $
4  * Version: $Revision: 1.3 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (C) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.jsp.decorator;
33
34 import org.opencms.file.CmsObject;
35 import org.opencms.main.CmsLog;
36 import org.opencms.util.CmsHtmlParser;
37 import org.opencms.util.CmsStringUtil;
38
39 import java.util.ArrayList JavaDoc;
40 import java.util.List JavaDoc;
41
42 import org.apache.commons.logging.Log;
43
44 import org.htmlparser.Text;
45 import org.htmlparser.util.Translate;
46
47 /**
48  * The CmsHtmlDecorator is the main object for processing the text decorations.<p>
49  *
50  * It uses the information of a <code>{@link CmsDecoratorConfiguration}</code> to process the
51  * text decorations.
52  *
53  * @author Michael Emmerich
54  *
55  * @version $Revision: 1.3 $
56  *
57  * @since 6.1.3
58  */

59 public class CmsHtmlDecorator extends CmsHtmlParser {
60
61     /** Delimiters for string seperation. */
62     private static final String JavaDoc[] DELIMITERS = {
63         " ",
64         ",",
65         ".",
66         ";",
67         ":",
68         "!",
69         "(",
70         ")",
71         "'",
72         "?",
73         "\"",
74         "&nbsp;",
75         "&quot;",
76         "\r\n",
77         "\n"};
78
79     /** Delimiters for second level string seperation. */
80     private static final String JavaDoc[] DELIMITERS_SECOND_LEVEL = {"-", "@", "/", ".", ","};
81
82     /** Steps for forward lookup in workd list. */
83     private static final int FORWARD_LOOKUP = 5;
84
85     /** The log object for this class. */
86     private static final Log LOG = CmsLog.getLog(CmsHtmlDecorator.class);
87
88     /** Non translators, strings starting with those values must not be translated. */
89     private static final String JavaDoc[] NON_TRANSLATORS = {"&nbsp;", "&quot;"};
90
91     /** The decoration configuration.<p> */
92     CmsDecoratorConfiguration m_config;
93
94     /** Decoration bundle to be used by the decorator. */
95     CmsDecorationBundle m_decorations;
96
97     /**
98      * Constructor, creates a new CmsHtmlDecorator with a given configuration.<p>
99      *
100      * @param config the configuration to be used
101      */

102     public CmsHtmlDecorator(CmsDecoratorConfiguration config) {
103
104         m_config = config;
105         m_decorations = config.getDecorations();
106         m_result = new StringBuffer JavaDoc(512);
107         m_echo = true;
108     }
109
110     /**
111      * Constructor, creates a new, empty CmsHtmlDecorator.<p>
112      *
113      * @param cms the CmsObject
114      */

115     public CmsHtmlDecorator(CmsObject cms) {
116
117         m_config = new CmsDecoratorConfiguration(cms);
118         m_decorations = m_config.getDecorations();
119         m_result = new StringBuffer JavaDoc(512);
120         m_echo = true;
121
122     }
123
124     /**
125      * Splits a String into substrings along the provided delimiter list and returns
126      * the result as a List of Substrings.<p>
127      *
128      * @param source the String to split
129      * @param delimiters the delimiters to split at
130      * @param trim flag to indicate if leading and trailing whitespaces should be omitted
131      * @param includeDelimiters flag to indicate if the delimiters should be included as well
132      *
133      * @return the List of splitted Substrings
134      */

135     public static List JavaDoc splitAsList(String JavaDoc source, String JavaDoc[] delimiters, boolean trim, boolean includeDelimiters) {
136
137         List JavaDoc result = new ArrayList JavaDoc();
138         String JavaDoc delimiter = new String JavaDoc();
139         int i = 0;
140         int l = source.length();
141         int n = -1;
142         int max = Integer.MAX_VALUE;
143
144         // find the next delimiter
145
for (int j = 0; j < delimiters.length; j++) {
146             if (source.indexOf(delimiters[j]) > -1) {
147                 if (source.indexOf(delimiters[j]) < max) {
148                     max = source.indexOf(delimiters[j]);
149                     n = source.indexOf(delimiters[j]);
150                     delimiter = delimiters[j];
151                 }
152             }
153         }
154
155         while (n != -1) {
156             // zero - length items are not seen as tokens at start or end
157
if ((i < n) || (i > 0) && (i < l)) {
158                 result.add(trim ? source.substring(i, n).trim() : source.substring(i, n));
159                 // add the delimiter to the list as well
160
if (includeDelimiters && n + delimiter.length() <= l) {
161                     result.add(source.substring(n, n + delimiter.length()));
162                 }
163             } else {
164                 // add the delimiter to the list as well
165
if (includeDelimiters && source.startsWith(delimiter)) {
166                     result.add(delimiter);
167                 }
168             }
169             i = n + delimiter.length();
170
171             // find the next delimiter
172
max = Integer.MAX_VALUE;
173             n = -1;
174             for (int j = 0; j < delimiters.length; j++) {
175                 if (source.indexOf(delimiters[j], i) > -1) {
176                     if (source.indexOf(delimiters[j], i) < max) {
177                         max = source.indexOf(delimiters[j], i);
178                         n = source.indexOf(delimiters[j], i);
179                         delimiter = delimiters[j];
180                     }
181                 }
182             }
183
184         }
185         // is there a non - empty String to cut from the tail?
186
if (n < 0) {
187             n = source.length();
188         }
189         if (i < n) {
190             result.add(trim ? source.substring(i).trim() : source.substring(i));
191         }
192         return result;
193     }
194
195     /**
196      * Appends a text decoration to the output.<p>
197      *
198      * A lookup is made to find a text decoration for each word in the given text.
199      * If a text decoration is found, the word will be decorated and added to the output.
200      * If no text decoration is found, the word alone will be added to the output.
201      *
202      * @param text the text to add a text decoration for
203      * @param delimiters delimiters for text seperation
204      * @param recursive flag for recusrive search
205      */

206     private void appendText(String JavaDoc text, String JavaDoc[] delimiters, boolean recursive) {
207
208         if (LOG.isDebugEnabled()) {
209             LOG.debug(Messages.get().getBundle().key(Messages.LOG_HTML_DECORATOR_APPEND_TEXT_2, m_config, text));
210         }
211
212         if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
213
214             // split the input into single words
215
List JavaDoc wordList = splitAsList(text, delimiters, false, true);
216
217             for (int i = 0; i < wordList.size(); i++) {
218                 String JavaDoc word = (String JavaDoc)wordList.get(i);
219
220                 if (LOG.isDebugEnabled()) {
221                     LOG.debug(Messages.get().getBundle().key(
222                         Messages.LOG_HTML_DECORATOR_PROCESS_WORD_2,
223                         word,
224                         new Boolean JavaDoc(mustDecode(word, wordList, i))));
225                 }
226
227                 // test if the word must be decoded
228
if (mustDecode(word, wordList, i)) {
229                     word = Translate.decode(word);
230                     if (LOG.isDebugEnabled()) {
231                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_HTML_DECORATOR_DECODED_WORD_1, word));
232                     }
233                 }
234
235                 // test if the word is no delimiter
236
// try to get a decoration if it is not
237
CmsDecorationObject decObj = null;
238                 if (!hasDelimiter(word, delimiters)) {
239                     decObj = (CmsDecorationObject)m_decorations.get(word);
240                 }
241
242                 if (LOG.isDebugEnabled()) {
243                     LOG.debug(Messages.get().getBundle().key(
244                         Messages.LOG_HTML_DECORATOR_DECORATION_FOUND_2,
245                         decObj,
246                         word));
247                 }
248
249                 // if there is a decoration obejct for this word, we must do the decoration
250
// if not, we must test if the word itself consits of several parts diveded by
251
// second level delimiters
252
if (decObj == null) {
253                     if (hasDelimiter(word, DELIMITERS_SECOND_LEVEL) && recursive) {
254                         // add the following symbol if possbile to allow the second level decoration
255
// test to make a forward lookup as well
256
String JavaDoc secondLevel = word;
257                         if (i < wordList.size() - 1) {
258                             if (!((String JavaDoc)wordList.get(i + 1)).equals(" ")) {
259                                 secondLevel = word + (String JavaDoc)wordList.get(i + 1);
260                                 i++;
261                             }
262                         }
263                         appendText(secondLevel, DELIMITERS_SECOND_LEVEL, false);
264                     } else {
265                         // make a forward lookup to the next elements of the word list to check
266
// if the combination of word and delimiter can be found as a decoration key
267
// an example would be "Dr." wich must be decorated with "Doctor"
268
StringBuffer JavaDoc decKey = new StringBuffer JavaDoc();
269                         decKey.append(word);
270                         // calculate how much forward looking must be made
271
int forwardLookup = wordList.size() - i - 1;
272                         if (forwardLookup > FORWARD_LOOKUP) {
273                             forwardLookup = FORWARD_LOOKUP;
274                         }
275                         if (i < wordList.size() - forwardLookup) {
276                             for (int j = 1; j <= forwardLookup; j++) {
277                                 decKey.append(wordList.get(i + j));
278                                 decObj = (CmsDecorationObject)m_decorations.get(decKey.toString());
279                                 if (LOG.isDebugEnabled()) {
280                                     LOG.debug(Messages.get().getBundle().key(
281                                         Messages.LOG_HTML_DECORATOR_DECORATION_FOUND_FWL_3,
282                                         decObj,
283                                         word,
284                                         new Integer JavaDoc(j)));
285                                 }
286                                 if (decObj != null) {
287                                     if (LOG.isDebugEnabled()) {
288                                         LOG.debug(Messages.get().getBundle().key(
289                                             Messages.LOG_HTML_DECORATOR_DECORATION_APPEND_DECORATION_1,
290                                             decObj.getContentDecoration(m_config)));
291                                     }
292                                     // decorate the current word with the following delimiter
293
m_result.append(decObj.getContentDecoration(m_config));
294                                     // important, we must skip the next element of the list
295
i += j;
296                                     break;
297                                 }
298                             }
299                         }
300                         if (decObj == null) {
301                             if (LOG.isDebugEnabled()) {
302                                 LOG.debug(Messages.get().getBundle().key(
303                                     Messages.LOG_HTML_DECORATOR_DECORATION_APPEND_WORD_1,
304                                     word));
305                             }
306                             // no decoration was found, use the word alone
307
m_result.append(word);
308                         }
309                     }
310                 } else {
311                     if (LOG.isDebugEnabled()) {
312                         LOG.debug(Messages.get().getBundle().key(
313                             Messages.LOG_HTML_DECORATOR_DECORATION_APPEND_DECORATION_1,
314                             decObj.getContentDecoration(m_config)));
315                     }
316                     // decorate the current word
317
m_result.append(decObj.getContentDecoration(m_config));
318                 }
319             }
320         } else {
321             if (LOG.isDebugEnabled()) {
322                 LOG.debug(Messages.get().getBundle().key(
323                     Messages.LOG_HTML_DECORATOR_DECORATION_APPEND_ORIGINALTEXT_1,
324                     text));
325             }
326             m_result.append(text);
327         }
328     }
329
330     /**
331      * Processes a HTML string and adds text decorations according to the decoration configuration.<p>
332      *
333      * @param html a string holding the HTML code that should be added with text decorations
334      * @param encoding the encoding to be used
335      * @return a HTML string with the decorations added.
336      * @throws Exception if something goes wrong
337      */

338     public String JavaDoc doDecoration(String JavaDoc html, String JavaDoc encoding) throws Exception JavaDoc {
339
340         return process(html, encoding);
341     }
342
343     /**
344      * Checks if a word contains a given delimiter.<p>
345      *
346      * @param word the word to test
347      * @param delimiters array of delimiter strings
348      * @return true if the word contains the delimiter, false otherwiese
349      */

350     private boolean hasDelimiter(String JavaDoc word, String JavaDoc[] delimiters) {
351
352         boolean delim = false;
353         for (int i = 0; i < delimiters.length; i++) {
354             if (word.indexOf(delimiters[i]) > -1) {
355                 delim = true;
356                 break;
357             }
358         }
359         return delim;
360     }
361
362     /**
363      * Checks if a word must be decoded.<p>
364      *
365      * The given word is compated to a negative list of words which must not be decoded.
366      * @param word the word to test
367      * @return true if the word must be decoded, false otherweise
368      */

369     private boolean mustDecode(String JavaDoc word, List JavaDoc wordList, int count) {
370
371         boolean decode = true;
372         String JavaDoc nextWord = null;
373
374         if (count < wordList.size() - 1) {
375             nextWord = (String JavaDoc)wordList.get(count + 1);
376         }
377         // test if the current word contains a "&" and the following with a ";"
378
// if so, we must not decode the word
379
if (nextWord != null && word.indexOf("&") > -1 && nextWord.startsWith(";")) {
380             return false;
381         } else {
382             // now scheck if the word matches one of the non decoder tokens
383
for (int i = 0; i < NON_TRANSLATORS.length; i++) {
384                 if (word.startsWith(NON_TRANSLATORS[i])) {
385                     decode = false;
386                     break;
387                 }
388             }
389         }
390         return decode;
391     }
392
393     /**
394      * Resets the first occurance flags of all decoration objects.<p>
395      *
396      * This is nescessary if decoration objects should be used for processing more than once. *
397      */

398     public void resetDecorationDefinitions() {
399
400         m_config.resetMarkedDecorations();
401     }
402
403     /**
404      * @see org.htmlparser.visitors.NodeVisitor#visitStringNode(org.htmlparser.Text)
405      */

406     public void visitStringNode(Text text) {
407
408         appendText(text.toPlainTextString(), DELIMITERS, true);
409     }
410
411 }
412
Popular Tags