misc. maintenance
[mir.git] / source / mir / misc / StringUtil.java
1 /*
2  * Copyright (C) 2001, 2002  The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with the com.oreilly.servlet library, any library
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24  * the above that use the same license as the above), and distribute linked
25  * combinations including the two.  You must obey the GNU General Public
26  * License in all respects for all of the code used other than the above
27  * mentioned libraries.  If you modify this file, you may extend this exception
28  * to your version of the file, but you are not obligated to do so.  If you do
29  * not wish to do so, delete this exception statement from your version.
30  */
31
32 package  mir.misc;
33
34 import  java.io.*;
35 import  java.lang.*;
36 import  java.util.*;
37 import  java.text.NumberFormat;
38 import  gnu.regexp.*;
39
40 /**
41  * Statische Hilfsmethoden zur Stringbehandlung
42  *
43  * @version $Id: StringUtil.java,v 1.29 2002/12/02 12:33:23 zapata Exp $
44  * @author mir-coders group
45  *
46  */
47 public final class StringUtil {
48
49   private static RE   re_newline2br, re_brbr2p, re_mail, re_url, re_tags;
50
51   private StringUtil() { }  // this avoids contruction
52
53   static {
54     try {
55       //precompile regex
56       re_newline2br = new RE("(\r?\n){1}");
57       re_brbr2p     = new RE("(<br>\r?\n<br>){1,}");
58       re_mail       = new RE("([a-zA-Z0-9_.-]+)@([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_.-]+)");
59       re_url        = new RE("((https://)|(http://)|(ftp://)){1}([a-zA-Z0-9_-]+).([a-zA-Z0-9_.:-]+)/?([^ \t\r\n<>\\)\\]]+[^ \t\r\n.,<>\\)\\]])");
60       re_tags       = new RE("<[^>]*>",RE.REG_ICASE);
61     }
62     catch (REException e){
63       System.err.println("FATAL: StringUtil: could not precompile REGEX: "+e.toString());
64     }
65   }
66
67   /**
68    * Formats a number with the specified minimum and maximum number of digits.
69    **/
70   public static synchronized String zeroPaddingNumber(long value, int minDigits,
71       int maxDigits)
72   {
73     NumberFormat numberFormat = NumberFormat.getInstance();
74     numberFormat.setMinimumIntegerDigits(minDigits);
75     numberFormat.setMaximumIntegerDigits(maxDigits);
76     return numberFormat.format(value);
77   }
78
79   /**
80    * Wandelt Datum in einen 8-ziffrigen String um (yyyymmdd)
81    * @param theDate
82    * @return 8-ziffriger String (yyyymmdd)
83    */
84
85   public static final String date2webdbDate (GregorianCalendar theDate) {
86     StringBuffer webdbDate = new StringBuffer();
87     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
88     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
89     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
90     return  webdbDate.toString();
91   }
92
93   /**
94    * Wandelt Calendar in einen 12-ziffrigen String um (yyyymmddhhmm)
95    * @param theDate
96    * @return 12-ziffriger String (yyyymmdd)
97    */
98
99   public static final String date2webdbDateTime (GregorianCalendar theDate) {
100     StringBuffer webdbDate = new StringBuffer();
101     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
102     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
103     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
104     webdbDate.append(pad2(theDate.get(Calendar.HOUR)));
105     webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
106     return  webdbDate.toString();
107   }
108
109   /**
110    * Return a http://www.w3.org/TR/NOTE-datetime formatted date (yyyy-mm-ddThh:mm:ssTZ)
111    * @param theDate
112    * @return w3approved datetime
113    */
114
115   public static final String date2w3DateTime (GregorianCalendar theDate) {
116     StringBuffer webdbDate = new StringBuffer();
117     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
118     webdbDate.append("-");
119     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
120     webdbDate.append("-");
121     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
122     webdbDate.append("T");
123     webdbDate.append(pad2(theDate.get(Calendar.HOUR)));
124     webdbDate.append(":");
125     webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
126     webdbDate.append(":");
127     webdbDate.append(pad2(theDate.get(Calendar.SECOND)));
128     //assumes you are an hour-multiple away from UTC....
129     int offset=(theDate.get(Calendar.ZONE_OFFSET)/(60*60*1000));
130     if (offset < 0){
131       webdbDate.append("-");
132     }
133     else{
134       webdbDate.append("+");
135     }
136     webdbDate.append(pad2(Math.abs(offset)));
137     webdbDate.append(":00");
138     return  webdbDate.toString();
139   }
140
141   /**
142    * wandelt Calendar in dd.mm.yyyy / hh.mm um
143    * @param theDate
144    * @return String mit (dd.mm.yyyy / hh.mm um)
145    */
146   public static String date2readableDateTime (GregorianCalendar theDate) {
147     String readable = "";
148     int hour;
149     readable += pad2(theDate.get(Calendar.DATE));
150     readable += "." + pad2(theDate.get(Calendar.MONTH) + 1);
151     readable += "." + String.valueOf(theDate.get(Calendar.YEAR));
152     hour = theDate.get(Calendar.HOUR);
153     if (theDate.get(Calendar.AM_PM) == Calendar.PM)
154       hour += 12;
155     readable += " / " + pad2(hour);
156     readable += ":" + pad2(theDate.get(Calendar.MINUTE));
157     return  readable;
158   }
159
160   /**
161   *  deleteForbiddenTags
162   *  this method deletes all <script>, <body> and <head>-tags
163   */
164   public static final String deleteForbiddenTags(String haystack) {
165     try {
166       RE regex = new RE("<[ \t\r\n](.*?)script(.*?)/script(.*?)>",RE.REG_ICASE);
167       haystack = regex.substituteAll(haystack,"");
168       regex = new RE("<head>(.*?)</head>");
169       haystack = regex.substituteAll(haystack,"");
170       regex = new RE("<[ \t\r\n/]*body(.*?)>");
171       haystack = regex.substituteAll(haystack,"");
172       return haystack;
173     } catch(REException ex){
174       return null;
175     }
176   }
177
178   /**
179    * wandelt eine Datum in einen 8-buchstabigen String, der durch <code>/</code>
180    * getrennt ist.
181    *
182    * @param webdbDate
183    * @return String mit <code>/yyyy/mm/dd</code>
184    */
185   public static final String webdbDate2path (String webdbDate) {
186     StringBuffer path = new StringBuffer();
187     path.append("/").append(webdbDate.substring(0, 4));
188     path.append("/").append(webdbDate.substring(4, 6));
189     path.append("/");
190     //who did this?
191     //path.append("/").append(webdbDate.substring(6, 8));
192     return  path.toString();
193   }
194
195   /**
196    * converts string from format: yyyy-mm-dd__hh:mm:ss.d
197    * to dd.mm.yyyy hh:mm
198    */
199   public static String dateToReadableDate(String date) {
200     StringBuffer returnDate = new StringBuffer();
201     if (date!=null) {
202
203       returnDate.append(date.substring(8,10)).append('.');
204       returnDate.append(date.substring(5,7)).append('.');
205       returnDate.append(date.substring(0,4)).append(' ');
206       returnDate.append(date.substring(11,16));
207     }
208     return returnDate.toString();
209   }
210
211   /**
212    * converts string from format: yyyy-mm-dd__hh:mm:ss.d
213    * to yyyy
214    */
215   public static String dateToYear (String date) {
216     StringBuffer returnDate = new StringBuffer();
217     if (date!=null) {
218
219       returnDate.append(date.substring(0,4));
220     }
221     return returnDate.toString();
222   }
223
224   /**
225    * converts string from format: yyyy-mm-dd__hh:mm:ss.d
226    * to [m]m
227    */
228   public static String dateToMonth (String date) {
229     StringBuffer returnDate = new StringBuffer();
230     if (date!=null) {
231       if (!date.substring(5,6).equalsIgnoreCase("0")) returnDate.append(date.substring(5,7));
232       else returnDate.append(date.substring(6,7));
233     }
234     return returnDate.toString();
235   }
236
237   /**
238    * converts string from format: yyyy-mm-dd__hh:mm:ss.d
239    * to [d]d
240    */
241   public static String dateToDayOfMonth (String date) {
242     StringBuffer returnDate = new StringBuffer();
243     if (date!=null) {
244       if (!date.substring(8,9).equalsIgnoreCase("0")) returnDate.append(date.substring(8,10));
245       else returnDate.append(date.substring(9,10));
246     }
247     return returnDate.toString();
248   }
249
250   /**
251    * converts string from format: yyyy-mm-dd__hh:mm:ss.d
252    * to hh:mm
253    */
254   public static String dateToTime (String date) {
255     StringBuffer returnDate = new StringBuffer();
256     if (date!=null) {
257       returnDate.append(date.substring(11,16));
258     }
259     return returnDate.toString();
260   }
261
262   /**
263    * Splits the provided CSV text into a list. stolen wholesale from
264    * from Jakarta Turbine StrinUtils.java -mh
265    *
266    * @param text      The CSV list of values to split apart.
267    * @param separator The separator character.
268    * @return          The list of values.
269    */
270   public static String[] split(String text, String separator)
271   {
272     StringTokenizer st = new StringTokenizer(text, separator);
273     String[] values = new String[st.countTokens()];
274     int pos = 0;
275     while (st.hasMoreTokens())
276     {
277       values[pos++] = st.nextToken();
278     }
279     return values;
280   }
281
282   /**
283    * Joins the elements of the provided array into a single string
284    * containing a list of CSV elements. Stolen wholesale from Jakarta
285    * Turbine StringUtils.java. -mh
286    *
287    * @param list      The list of values to join together.
288    * @param separator The separator character.
289    * @return          The CSV text.
290    */
291   public static String join(String[] list, String separator)
292   {
293     StringBuffer csv = new StringBuffer();
294     for (int i = 0; i < list.length; i++)
295     {
296       if (i > 0)
297       {
298       csv.append(separator);
299     }
300     csv.append(list[i]);
301     }
302     return csv.toString();
303   }
304
305
306   /**
307    * schließt einen String in Anführungsszeichen ein, falls er Leerzeichen o.ä. enthält
308    *
309    * @return gequoteter String
310    */
311   public static String quoteIfNecessary(String s) {
312     for (int i = 0; i < s.length(); i++)
313       if (!(Character.isLetterOrDigit(s.charAt(i)) || s.charAt(i) == '.'))
314         return quote(s, '"');
315     return s;
316   }
317
318   /**
319    * schließt <code>s</code> in <code>'</code> ein und setzt Backslashes vor
320    * "gefährliche" Zeichen innerhalb des Strings
321    * Quotes special SQL-characters in <code>s</code>
322    *
323    * @return geqoteter String
324    */
325   public static String quote(String s)
326   {
327     //String s2 = quote(s, '\'');
328     //Quickhack ÊÊ Ê Ê Ê Ê Ê Ê
329     //Because of '?-Bug in Postgresql-JDBC-Driver
330     StringBuffer temp = new StringBuffer();
331     for(int i=0;i<s.length();i++){
332       if(s.charAt(i)=='\''){
333         temp.append("&#39;");
334       } else {
335         temp.append(s.charAt(i));
336       }
337     }
338     String s2 = temp.toString();
339     //end Quickhack
340
341     s2 = quote(s2, '\"');
342     return s2;
343   }
344
345   /**
346    * schließt <code>s</code> in <code>'</code> ein und setzt Backslashes vor
347    * "gefährliche" Zeichen innerhalb des Strings
348    *
349    * @param s String, der gequoted werden soll
350    * @param quoteChar zu quotendes Zeichen
351    * @return gequoteter String
352    */
353   public static String quote(String s, char quoteChar)
354   {
355     StringBuffer buf = new StringBuffer(s.length());
356     int pos = 0;
357     while (pos < s.length()) {
358       int i = s.indexOf(quoteChar, pos);
359       if (i < 0) i = s.length();
360       buf.append(s.substring(pos, i));
361       pos = i;
362       if (pos < s.length()) {
363         buf.append('\\');
364         buf.append(quoteChar);
365         pos++;
366       }
367     }
368     return buf.toString();
369   }
370
371   /**
372    * replaces dangerous characters in <code>s</code>
373    *
374    */
375
376   public static String unquote(String s)
377   {
378     char quoteChar='\'';
379     StringBuffer buf = new StringBuffer(s.length());
380     int pos = 0;
381     String searchString = "\\"+quoteChar;
382     while (pos < s.length()) {
383       int i = s.indexOf(searchString, pos);
384       if (i < 0) i = s.length();
385       buf.append(s.substring(pos, i));
386       pos = i+1;
387     }
388     return buf.toString();
389   }
390
391   /**
392    * Wandelet String in byte[] um.
393    * @param s
394    * @return byte[] des String
395    */
396
397   public static byte[] stringToBytes(String s) {
398     String crlf = System.getProperty("line.separator");
399     if (!crlf.equals("\n"))
400       s = replace(s, "\n", crlf);
401     // byte[] buf = new byte[s.length()];
402     byte[] buf = s.getBytes();
403     return buf;
404   }
405
406   /**
407    * Ersetzt in String <code>s</code> das <code>pattern</code> durch <code>substitute</code>
408    * @param s
409    * @param pattern
410    * @param substitute
411    * @return String mit den Ersetzungen
412    */
413   public static String replace(String s, String pattern, String substitute) {
414     int i = 0, pLen = pattern.length(), sLen = substitute.length();
415     StringBuffer buf = new StringBuffer(s.length());
416     while (true) {
417       int j = s.indexOf(pattern, i);
418       if (j < 0) {
419         buf.append(s.substring(i));
420         break;
421       } else {
422         buf.append(s.substring(i, j));
423         buf.append(substitute);
424         i = j+pLen;
425       }
426     }
427     return buf.toString();
428   }
429
430   /**
431    * Ersetzt in String <code>s</code> das Regexp <code>pattern</code> durch <code>substitute</code>
432    * @param s
433    * @param pattern
434    * @param substitute
435    * @return String mit den Ersetzungen
436    */
437   public static String regexpReplace(String haystack, String pattern, String substitute) {
438     try {
439       RE regex = new RE(pattern);
440       return regex.substituteAll(haystack,substitute);
441     } catch(REException ex){
442       return null;
443     }
444   }
445
446
447
448
449   /**
450    * Fügt einen Separator an den Pfad an
451    * @param path
452    * @return Pfad mit Separator am Ende
453    */
454   public static final String addSeparator (String path) {
455     return  path.length() == 0 || path.endsWith(File.separator) ? path : path
456                           + File.separatorChar;
457   }
458
459   /**
460    * Fügt ein <code>/</code> ans ende des Strings and
461    * @param path
462    * @return Pfad mit <code>/</code> am Ende
463    */
464   public static final String addSlash (String path) {
465     return  path.length() == 0 || path.endsWith("/") ? path : path + '/';
466   }
467
468   /**
469    * Löscht <code>/</code> am Ende des Strings, falls vorhanden
470    * @param path
471    * @return String ohne <code>/</code> am Ende
472    */
473   public static final String removeSlash (String path) {
474     return  path.length() > 1 && path.endsWith("/") ? path.substring(0, path.length()
475         - 1) : path;
476   }
477
478   /**
479    * Checks to see if the path is absolute by looking for a leading file
480    * separater
481    * @param path
482    * @return
483    */
484   public static boolean isAbsolutePath (String path) {
485     return  path.startsWith(File.separator);
486   }
487
488   /**
489    * Löscht Slash am Anfang des Strings
490    * @param path
491    * @return
492    */
493   public static String removeFirstSlash (String path) {
494     return  path.startsWith("/") ? path.substring(1) : path;
495   }
496
497   /**
498    * formatiert eine Zahl (0-99) zweistellig (z.B. 5 -> 05)
499    * @return zwistellige Zahl
500    */
501   public static String pad2 (int number) {
502     return  number < 10 ? "0" + number : String.valueOf(number);
503   }
504
505   /**
506    * formatiert eine Zahl (0-999) dreistellig (z.B. 7 -> 007)
507    *
508    * @return 3-stellige Zahl
509    */
510   public static String pad3 (int number) {
511     return  number < 10 ? "00" + number : number < 100 ? "0" + number : String.valueOf(number);
512   }
513
514   /**
515    * Konvertiert Unix-Linefeeds in Win-Linefeeds
516    * @param s
517    * @return Konvertierter String
518    */
519   public static String unixLineFeedsToWin(String s) {
520     int i = -1;
521     while (true) {
522       i = s.indexOf('\n', i+1);
523       if (i < 0) break;
524       if ((i == 0 || s.charAt(i-1) != '\r') &&
525           (i == s.length()-1 || s.charAt(i+1) != '\r')) {
526         s = s.substring(0, i)+'\r'+s.substring(i);
527         i++;
528       }
529     }
530     return s;
531   }
532
533   public static String replaceQuot(String s) {
534     StringBuffer buffer = new StringBuffer();
535     for(int j = 0; j < s.length();j++){
536       if(s.charAt(j)=='&'){
537         if(s.indexOf( "&quot;",j) == j) {
538           buffer.append( "\"" );
539           j += 5;
540         }//if
541       } else {
542         buffer.append(s.charAt(j));
543       }//else
544     }//for
545     return buffer.toString();
546   }
547
548  /**
549   * schnellere Variante der String.toLowerCase()-Routine
550   *
551   * @return String in Kleinbuchsten
552   */
553   public static String toLowerCase(String s) {
554     int l = s.length();
555     char[] a = new char[l];
556     for (int i = 0; i < l; i++)
557       a[i] = Character.toLowerCase(s.charAt(i));
558     return new String(a);
559   }
560
561   /**
562    * Findet <code>element</code> im String-Array <code>array</code>
563    * @param array
564    * @param element
565    * @return Fundstelle als int oder -1
566    */
567   public static int indexOf(String[] array, String element) {
568     if (array != null)
569       for (int i = 0; i < array.length; i++)
570         if (array[i].equals(element))
571           return i;
572     return -1;
573   }
574
575   /**
576    * Testet auf Vorkommen von <code>element</code> in <code>array</code>
577    * @param array String-Array
578    * @param element
579    * @return true wenn <code>element</code> vorkommt, sonst false
580    */
581   public static boolean contains(String[] array, String element) {
582     return indexOf(array, element) >= 0;
583   }
584
585   /**
586    * Ermittelt CRC-Prüfsumme von String <code>s</code>
587    * @param s
588    * @return CRC-Prüfsumme
589    */
590   public static int getCRC(String s) {
591     int h = 0;
592     char val[] = s.toCharArray();
593     int len = val.length;
594
595     for (int i = 0 ; i < len; i++) {
596       h &= 0x7fffffff;
597       h = (((h >> 30) | (h << 1)) ^ (val[i]+i));
598     }
599
600     return (h << 8) | (len & 0xff);
601   }
602
603   /**
604    * Liefert Default-Wert def zurück, wenn String <code>s</code>
605    * kein Integer ist.
606    *
607    * @param s
608    * @param def
609    * @return geparster int aus s oder def
610    */
611   public static int parseInt(String s, int def) {
612     if (s == null) return def;
613     try {
614       return Integer.parseInt(s);
615     } catch (NumberFormatException e) {
616       return def;
617     }
618   }
619
620   /**
621    * Liefert Defaultwert def zurück, wenn s nicht zu einem float geparsed werden kann.
622    * @param s
623    * @param def
624    * @return geparster float oder def
625    */
626   public static float parseFloat(String s, float def) {
627     if (s == null) return def;
628     try {
629       return new Float(s).floatValue();
630     } catch (NumberFormatException e) {
631       return def;
632     }
633   }
634
635   /**
636    * Findet Ende eines Satzes in String <code>text</code>
637    * @param text
638    * @param startIndex
639    * @return index des Satzendes, oder -1
640    */
641   public static int findEndOfSentence(String text, int startIndex) {
642     while (true) {
643       int i = text.indexOf('.', startIndex);
644       if (i < 0) return -1;
645       if (i > 0 && !Character.isDigit(text.charAt(i-1)) &&
646           (i+1 >= text.length()
647           || text.charAt(i+1) == ' '
648           || text.charAt(i+1) == '\n'
649           || text.charAt(i+1) == '\t'))
650         return i+1;
651       startIndex = i+1;
652     }
653   }
654
655   /**
656    * Findet Wortende in String <code>text</code> ab <code>startIndex</code>
657    * @param text
658    * @param startIndex
659    * @return Index des Wortendes, oder -1
660    */
661   public static int findEndOfWord(String text, int startIndex) {
662     int i = text.indexOf(' ', startIndex),
663     j = text.indexOf('\n', startIndex);
664     if (i < 0) i = text.length();
665     if (j < 0) j = text.length();
666     return Math.min(i, j);
667   }
668
669
670   /**
671    *  convertNewline2P ist eine regex-routine zum umwandeln von 2 oder mehr newlines (\n)
672    *  in den html-tag <p>
673    *  nur sinnvoll, wenn text nicht im html-format eingegeben
674    */
675   public static String convertNewline2P(String haystack) {
676     return re_brbr2p.substituteAll(haystack,"\n</p><p>");
677   }
678
679   /**
680    *  convertNewline2Break ist eine regex-routine zum umwandeln von 1 newline (\n)
681    *  in den html-tag <br>
682    *  nur sinnvoll, wenn text nicht im html-format eingegeben
683    */
684   public static String convertNewline2Break(String haystack) {
685     return re_newline2br.substituteAll(haystack,"$0<br />");
686   }
687
688   /**
689    *  createMailLinks wandelt text im email-adressenformat
690    *  in einen klickbaren link um
691    *  nur sinnvoll, wenn text nicht im html-format eingegeben
692    */
693   public static String createMailLinks(String haystack) {
694     return re_mail.substituteAll(haystack,"<a href=\"mailto:$0\">$0</a>");
695   }
696
697
698   /**
699    *  createMailLinks wandelt text im email-adressenformat
700    *  in einen klickbaren link um
701    *  nur sinnvoll, wenn text nicht im html-format eingegeben
702    */
703   public static String createMailLinks(String haystack, String imageRoot, String mailImage) {
704     return re_mail.substituteAll(haystack,"<img src=\""+imageRoot+"/"+mailImage+"\" border=\"0\"/>&#160;<a href=\"mailto:$0\">$0</a>");
705   }
706
707
708   /**
709    *  createURLLinks wandelt text im url-format
710    *  in einen klickbaren link um
711    *  nur sinnvoll, wenn text nicht im html-format eingegeben
712    */
713   public static String createURLLinks(String haystack) {
714     return re_url.substituteAll(haystack,"<a href=\"$0\">$0</a>");
715   }
716
717   /**
718    * this routine takes text in url format and makes
719    * a clickaeble "<href>" link removing any "illegal" html tags
720    * @param haystack, the url
721    * @param title, the href link text
722    * @param imagRoot, the place to find icons
723    * @param extImage, the url of the icon to show next to the link
724    * @return a String containing the url
725    */
726   public static String createURLLinks(String haystack, String title, String imageRoot,String extImage) {
727     if (title == null) {
728       return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">$0</a>");
729     } else {
730       title = removeHTMLTags(title);
731       return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">"+title+"</a>");
732     }
733   }
734
735   /**
736    * this routine takes text in url format and makes
737    * a clickaeble "<href>" link removing any "illegal" html tags
738    * @param haystack, the url
739    * @param imageRoot, the place to find icons
740    * @param extImage, the url of the icon to show next to the link
741    * @param intImage, unused
742    * @return a String containing the url
743    */
744   public static String createURLLinks(String haystack, String title, String imageRoot,String extImage,String intImage) {
745     return createURLLinks(haystack, title, imageRoot, extImage);
746   }
747
748   /**
749    * this method deletes all html tags
750    */
751   public static final String removeHTMLTags(String haystack){
752     return re_tags.substituteAll(haystack,"");
753   }
754
755   /**
756    * this method deletes all but the approved tags html tags
757    * it also deletes approved tags which contain malicious-looking attributes and doesn't work at all
758    */
759   public static String approveHTMLTags(String haystack){
760     try {
761       String approvedTags="a|img|h1|h2|h3|h4|h5|h6|br|b|i|strong|p";
762       String badAttributes="onAbort|onBlur|onChange|onClick|onDblClick|onDragDrop|onError|onFocus|onKeyDown|onKeyPress|onKeyUp|onLoad|onMouseDown|onMouseMove|onMouseOut|onMouseOver|onMouseUp|onMove|onReset|onResize|onSelect|onSubmit|onUnload";
763       String approvedProtocols="rtsp|http|ftp|https|freenet|mailto";
764
765       // kill all the bad tags that have attributes
766       String s = "<\\s*/?\\s*(?!(("+approvedTags+")\\s))\\w+\\s[^>]*>";
767       RE regex = new RE(s,RE.REG_ICASE);
768       haystack = regex.substituteAll(haystack,"");
769
770       // kill all the bad tags that are attributeless
771       regex = new RE("<\\s*/?\\s*(?!(("+approvedTags+")\\s*>))\\w+\\s*>",RE.REG_ICASE);
772       haystack = regex.substituteAll(haystack,"");
773
774       // kill all the tags which have a javascript attribute like onLoad
775       regex = new RE("<[^>]*("+badAttributes+")[^>]*>",RE.REG_ICASE);
776       haystack = regex.substituteAll(haystack,"");
777
778       // kill all the tags which include a url to an unacceptable protocol
779       regex = new RE("<\\s*a\\s+[^>]*href=(?!(\'|\")?("+approvedProtocols+"))[^>]*>",RE.REG_ICASE);
780       haystack = regex.substituteAll(haystack,"");
781
782       return haystack;
783     } catch(REException ex){
784       ex.printStackTrace();
785       return null;
786     }
787   }
788
789
790   /**
791    *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
792    *  htmlcodierten string auf und returnt einen htmlcodierten String
793    */
794   public static String createHTML(String content){
795     content=convertNewline2Break(content);
796     content=convertNewline2P(content);
797     content=createMailLinks(content);
798     content=createURLLinks(content);
799     return content;
800   }
801
802
803   /**
804    *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
805    *  htmlcodierten string auf und returnt einen htmlcodierten String
806    */
807   public static String createHTML(String content,String producerDocRoot,String mailImage,String extImage,String intImage){
808     content=convertNewline2Break(content);
809     content=convertNewline2P(content);
810     content=createMailLinks(content,producerDocRoot,mailImage);
811     content=createURLLinks(content,null,producerDocRoot,extImage,intImage);
812     return content;
813   }
814
815 }
816