fix up the deleteForbiddenTags and table tags regexp's so they handle multiline and...
[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.23.2.5 2002/12/13 05:55:40 mh Exp $
44  * @author rk, mir-coders group
45  *
46  */
47
48 public final class StringUtil {
49
50         private static RE   re_newline2br, re_brbr2p, re_mail, re_url, re_tags,
51                       re_tables, re_forbiddenTags;
52
53         private StringUtil() { }  // this avoids contruction
54
55         static {
56                 try {
57                         //precompile regex
58                         re_newline2br = new RE("(\r?\n){1}");
59                         re_brbr2p     = new RE("(<br>\r?\n<br>){1,}");
60       re_mail       = new RE("([a-zA-Z0-9_.-]+)@([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_.-]+)");
61                         re_url        = new RE("((https://)|(http://)|(ftp://)){1}([a-zA-Z0-9_-]+).([a-zA-Z0-9_.:-]+)/?([^ \t\r\n<>\\)\\]]+[^ \t\r\n.,<>\\)\\]])");
62                         re_tags       = new RE("<[^>]*>",RE.REG_ICASE);
63                         re_tables = new RE("<[ \t\r\n/]*(table|td|tr)[ \t\r\n]*>",RE.REG_ICASE);
64                         re_forbiddenTags = new RE("<[ \t\r\n/]*(body|head|script)[ \t\r\n]*>",RE.REG_ICASE);
65                 }
66                 catch (REException e){
67                         System.err.println("FATAL: StringUtil: could not precompile REGEX: "+e.toString());
68                 }
69         }
70
71   /**
72    * Formats a number with the specified minimum and maximum number of digits.
73    **/
74   public static synchronized String zeroPaddingNumber(long value, int minDigits,
75                                                       int maxDigits)
76   {
77     NumberFormat numberFormat = NumberFormat.getInstance();
78     numberFormat.setMinimumIntegerDigits(minDigits);
79     numberFormat.setMaximumIntegerDigits(maxDigits);
80     return numberFormat.format(value);
81   }
82
83         /**
84          * Wandelt Datum in einen 8-ziffrigen String um (yyyymmdd)
85          * @param theDate
86          * @return 8-ziffriger String (yyyymmdd)
87          */
88
89         public static final String date2webdbDate (GregorianCalendar theDate) {
90                 StringBuffer webdbDate = new StringBuffer();
91                 webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
92                 webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
93                 webdbDate.append(pad2(theDate.get(Calendar.DATE)));
94                 return  webdbDate.toString();
95         }
96
97         /**
98          * Wandelt Calendar in einen 12-ziffrigen String um (yyyymmddhhmm)
99          * @param theDate
100          * @return 12-ziffriger String (yyyymmdd)
101          */
102
103         public static final String date2webdbDateTime (GregorianCalendar theDate) {
104                 StringBuffer webdbDate = new StringBuffer();
105                 webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
106                 webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
107                 webdbDate.append(pad2(theDate.get(Calendar.DATE)));
108                 webdbDate.append(pad2(theDate.get(Calendar.HOUR)));
109                 webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
110                 return  webdbDate.toString();
111         }
112
113         /**
114          * Return a http://www.w3.org/TR/NOTE-datetime formatted date (yyyy-mm-ddThh:mm:ssTZ)
115          * @param theDate
116          * @return w3approved datetime
117          */
118
119         public static final String date2w3DateTime (GregorianCalendar theDate) {
120                 StringBuffer webdbDate = new StringBuffer();
121                 webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
122                 webdbDate.append("-");
123                 webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
124                 webdbDate.append("-");
125                 webdbDate.append(pad2(theDate.get(Calendar.DATE)));
126                 webdbDate.append("T");
127                 webdbDate.append(pad2(theDate.get(Calendar.HOUR)));
128                 webdbDate.append(":");
129                 webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
130                 webdbDate.append(":");
131                 webdbDate.append(pad2(theDate.get(Calendar.SECOND)));
132                 //assumes you are an hour-multiple away from UTC....
133                 int offset=(theDate.get(Calendar.ZONE_OFFSET)/(60*60*1000));
134                 if (offset < 0){
135                 webdbDate.append("-");
136                 }
137                 else{
138                 webdbDate.append("+");
139                 }
140                 webdbDate.append(pad2(Math.abs(offset)));
141                 webdbDate.append(":00");
142                 return  webdbDate.toString();
143         }
144
145         /**
146          * wandelt Calendar in dd.mm.yyyy / hh.mm um
147          * @param theDate
148          * @return String mit (dd.mm.yyyy / hh.mm um)
149          */
150         public static String date2readableDateTime (GregorianCalendar theDate) {
151                 String readable = "";
152                 int hour;
153                 readable += pad2(theDate.get(Calendar.DATE));
154                 readable += "." + pad2(theDate.get(Calendar.MONTH) + 1);
155                 readable += "." + String.valueOf(theDate.get(Calendar.YEAR));
156                 hour = theDate.get(Calendar.HOUR);
157                 if (theDate.get(Calendar.AM_PM) == Calendar.PM)
158                         hour += 12;
159                 readable += " / " + pad2(hour);
160                 readable += ":" + pad2(theDate.get(Calendar.MINUTE));
161                 return  readable;
162         }
163
164         /**
165          * wandelt eine Datum in einen 8-buchstabigen String, der durch <code>/</code>
166          * getrennt ist.
167          *
168          * @param webdbDate
169          * @return String mit <code>/yyyy/mm/dd</code>
170          */
171         public static final String webdbDate2path (String webdbDate) {
172                 StringBuffer path = new StringBuffer();
173                 path.append("/").append(webdbDate.substring(0, 4));
174                 path.append("/").append(webdbDate.substring(4, 6));
175                 path.append("/");
176                 //who did this?
177                 //path.append("/").append(webdbDate.substring(6, 8));
178                 return  path.toString();
179         }
180
181         /**
182          * wandelt Calendar in dd.mm.yyyy um
183          *
184          * @param theDate
185          * @return String mit  <code>yyyy.mm.dd</code>
186          */
187         public static final String webdbDate2readableDate (String webdbDate) {
188                 String date = "";
189                 date += webdbDate.substring(0, 4);
190                 date += "-" + webdbDate.substring(5, 7);
191                 date += "-"+webdbDate.substring(8, 10);
192                 return  date;
193         }
194
195
196         /**
197          * converts string from format: yyyy-mm-dd__hh:mm:ss.d
198          * to dd.mm.yyyy hh:mm
199          */
200         public static String dateToReadableDate(String date) {
201                 StringBuffer returnDate = new StringBuffer();
202                 if (date!=null) {
203
204                         returnDate.append(date.substring(8,10)).append('.');
205                         returnDate.append(date.substring(5,7)).append('.');
206                         returnDate.append(date.substring(0,4)).append(' ');
207                         returnDate.append(date.substring(11,16));
208                 }
209                 return returnDate.toString();
210         }
211         
212   /**
213          * converts string from format: yyyy-mm-dd__hh:mm:ss.dddddd+TZ
214          * to yyyy-mm-ddThh:mm:ss+TZ:00 (w3 format for Dublin Core)
215          */
216         public static String webdbdateToDCDate(String date) {
217                 StringBuffer returnDate = new StringBuffer();
218                 if (date!=null) {
219       returnDate.append(date.substring(0,10));
220       returnDate.append("T");
221       returnDate.append(date.substring(11,19));
222       //String tzInfo=date.substring(26,29);
223       //if (tzInfo.equals("+00")){
224       //UTC gets a special code in w3 dates
225       //    returnDate.append("Z");
226       //}
227       //else{
228       //need to see what a newfoundland postgres
229       //timestamp looks like before making this robust
230       //    returnDate.append(tzInfo);
231       //    returnDate.append(":00");
232       //}
233
234                 }
235                 return returnDate.toString();
236         }
237
238
239         /**
240          * converts string from format: yyyy-mm-dd__hh:mm:ss.d
241          * to yyyy
242          */
243         public static String dateToYear (String date) {
244                 StringBuffer returnDate = new StringBuffer();
245                 if (date!=null) {
246
247                         returnDate.append(date.substring(0,4));
248                 }
249                 return returnDate.toString();
250         }
251
252         /**
253          * converts string from format: yyyy-mm-dd__hh:mm:ss.d
254          * to [m]m
255          */
256         public static String dateToMonth (String date) {
257                 StringBuffer returnDate = new StringBuffer();
258                 if (date!=null) {
259                         if (!date.substring(5,6).equalsIgnoreCase("0")) returnDate.append(date.substring(5,7));
260                         else returnDate.append(date.substring(6,7));
261                 }
262                 return returnDate.toString();
263         }
264
265         /**
266          * converts string from format: yyyy-mm-dd__hh:mm:ss.d
267          * to [d]d
268          */
269         public static String dateToDayOfMonth (String date) {
270                 StringBuffer returnDate = new StringBuffer();
271                 if (date!=null) {
272                         if (!date.substring(8,9).equalsIgnoreCase("0")) returnDate.append(date.substring(8,10));
273                         else returnDate.append(date.substring(9,10));
274                 }
275                 return returnDate.toString();
276         }
277
278         /**
279          * converts string from format: yyyy-mm-dd__hh:mm:ss.d
280          * to hh:mm
281          */
282         public static String dateToTime (String date) {
283                 StringBuffer returnDate = new StringBuffer();
284                 if (date!=null) {
285                         returnDate.append(date.substring(11,16));
286                 }
287                 return returnDate.toString();
288         }
289
290     /**
291      * Splits the provided CSV text into a list. stolen wholesale from
292      * from Jakarta Turbine StrinUtils.java -mh
293      *
294      * @param text      The CSV list of values to split apart.
295      * @param separator The separator character.
296      * @return          The list of values.
297      */
298     public static String[] split(String text, String separator)
299     {
300         StringTokenizer st = new StringTokenizer(text, separator);
301         String[] values = new String[st.countTokens()];
302         int pos = 0;
303         while (st.hasMoreTokens())
304         {
305             values[pos++] = st.nextToken();
306         }
307         return values;
308     }
309
310     /**
311      * Joins the elements of the provided array into a single string
312      * containing a list of CSV elements. Stolen wholesale from Jakarta
313      * Turbine StringUtils.java. -mh
314      *
315      * @param list      The list of values to join together.
316      * @param separator The separator character.
317      * @return          The CSV text.
318      */
319     public static String join(String[] list, String separator)
320     {
321         StringBuffer csv = new StringBuffer();
322         for (int i = 0; i < list.length; i++)
323         {
324             if (i > 0)
325             {
326                 csv.append(separator);
327             }
328             csv.append(list[i]);
329         }
330         return csv.toString();
331     }
332
333
334         /**
335          * schließt einen String in Anführungsszeichen ein, falls er Leerzeichen o.ä. enthält
336          *
337          * @return gequoteter String
338          */
339          public static String quoteIfNecessary(String s) {
340                 for (int i = 0; i < s.length(); i++)
341                         if (!(Character.isLetterOrDigit(s.charAt(i)) || s.charAt(i) == '.'))
342                                 return quote(s, '"');
343                 return s;
344         }
345
346          /**
347          * schließt <code>s</code> in <code>'</code> ein und setzt Backslashes vor
348          * "gefährliche" Zeichen innerhalb des Strings
349          * Quotes special SQL-characters in <code>s</code>
350          *
351          * @return geqoteter String
352          */
353         public static String quote(String s)
354         {
355                 //String s2 = quote(s, '\'');
356                 //Quickhack     ÃŠÃŠ ÃŠ ÃŠ ÃŠ ÃŠ ÃŠ ÃŠ
357                 //Because of '?-Bug in Postgresql-JDBC-Driver
358                 StringBuffer temp = new StringBuffer();
359                 for(int i=0;i<s.length();i++){
360                         if(s.charAt(i)=='\''){
361                                 temp.append("&#39;");
362                         } else {
363                                 temp.append(s.charAt(i));
364                         }
365                 }
366                 String s2 = temp.toString();
367                 //end Quickhack
368                 
369                 s2 = quote(s2, '\"');
370                 return s2;
371         }
372
373         /**
374          * schließt <code>s</code> in <code>'</code> ein und setzt Backslashes vor
375          * "gefährliche" Zeichen innerhalb des Strings
376          *
377          * @param s String, der gequoted werden soll
378          * @param quoteChar zu quotendes Zeichen
379          * @return gequoteter String
380          */
381         public static String quote(String s, char quoteChar)
382         {
383                 StringBuffer buf = new StringBuffer(s.length());
384                 int pos = 0;
385                 while (pos < s.length()) {
386                         int i = s.indexOf(quoteChar, pos);
387                         if (i < 0) i = s.length();
388                         buf.append(s.substring(pos, i));
389                         pos = i;
390                         if (pos < s.length()) {
391                                 buf.append('\\');
392                                 buf.append(quoteChar);
393                                 pos++;
394                         }
395                 }
396                 return buf.toString();
397         }
398
399         /**
400          * replaces dangerous characters in <code>s</code>
401          *
402          */
403
404         public static String unquote(String s)
405         {
406                 char quoteChar='\'';
407                 StringBuffer buf = new StringBuffer(s.length());
408                 int pos = 0;
409                 String searchString = "\\"+quoteChar;
410                 while (pos < s.length()) {
411                         int i = s.indexOf(searchString, pos);
412                         if (i < 0) i = s.length();
413                         buf.append(s.substring(pos, i));
414                         pos = i+1;
415                 }
416                 return buf.toString();
417         }
418
419   /*
420    * replaces characters that cannot appera in HTML with escaped equivalents.
421    */
422   public static String encodeHTML(String aText) {
423     final char[] CHARACTERS_TO_ESCAPE = { '&', '<', '>', '"', '\'' };
424     final String[] ESCAPE_CODES = { "&amp;", "&lt;", "&gt;", "&quot;", "&apos;" };
425
426     int position, nextPosition;
427     int i;
428     StringBuffer result = new StringBuffer();
429
430     position=0;
431
432     do {
433       nextPosition = indexOfCharacters(aText, CHARACTERS_TO_ESCAPE, position);
434
435       if (nextPosition<0)
436         nextPosition = aText.length();
437
438       result.append(aText.substring(position, nextPosition));
439
440       if (nextPosition<aText.length())
441         for (i=0; i<CHARACTERS_TO_ESCAPE.length; i++) {
442           if (CHARACTERS_TO_ESCAPE[i] == aText.charAt(nextPosition)) {
443             result.append(ESCAPE_CODES[i]);
444             break;
445           }
446         }
447       position=nextPosition+1;
448     }
449     while (nextPosition<aText.length()) ;
450
451     return result.toString();
452   }
453
454         /**
455          * Wandelet String in byte[] um.
456          * @param s
457          * @return byte[] des String
458          */
459
460         public static byte[] stringToBytes(String s) {
461                 String crlf = System.getProperty("line.separator");
462                 if (!crlf.equals("\n"))
463                         s = replace(s, "\n", crlf);
464                 // byte[] buf = new byte[s.length()];
465                 byte[] buf = s.getBytes();
466                 return buf;
467         }
468
469                 /**
470          * Ersetzt in String <code>s</code> das <code>pattern</code> durch <code>substitute</code>
471          * @param s
472          * @param pattern
473          * @param substitute
474          * @return String mit den Ersetzungen
475          */
476         public static String replace(String s, String pattern, String substitute) {
477                 int i = 0, pLen = pattern.length(), sLen = substitute.length();
478                 StringBuffer buf = new StringBuffer(s.length());
479                 while (true) {
480                         int j = s.indexOf(pattern, i);
481                         if (j < 0) {
482                                 buf.append(s.substring(i));
483                                 break;
484                         } else {
485                                 buf.append(s.substring(i, j));
486                                 buf.append(substitute);
487                                 i = j+pLen;
488                         }
489                 }
490                 return buf.toString();
491         }
492
493         /**
494          * Ersetzt in String <code>s</code> das Regexp <code>pattern</code> durch <code>substitute</code>
495          * @param s
496          * @param pattern
497          * @param substitute
498          * @return String mit den Ersetzungen
499          */
500         public static String regexpReplace(String haystack, String pattern, String substitute) {
501                 try {
502                         RE regex = new RE(pattern);
503                         return regex.substituteAll(haystack,substitute);
504                 } catch(REException ex){
505                         return null;
506                 }
507         }
508
509
510
511
512         /**
513          * Fügt einen Separator an den Pfad an
514          * @param path
515          * @return Pfad mit Separator am Ende
516          */
517         public static final String addSeparator (String path) {
518                 return  path.length() == 0 || path.endsWith(File.separator) ? path : path
519                                 + File.separatorChar;
520         }
521
522         /**
523          * Fügt ein <code>/</code> ans ende des Strings and
524          * @param path
525          * @return Pfad mit <code>/</code> am Ende
526          */
527         public static final String addSlash (String path) {
528                 return  path.length() == 0 || path.endsWith("/") ? path : path + '/';
529         }
530
531         /**
532          * Löscht <code>/</code> am Ende des Strings, falls vorhanden
533          * @param path
534          * @return String ohne <code>/</code> am Ende
535          */
536         public static final String removeSlash (String path) {
537                 return  path.length() > 1 && path.endsWith("/") ? path.substring(0, path.length()
538                                 - 1) : path;
539         }
540
541         /**
542          * Checks to see if the path is absolute by looking for a leading file
543          * separater
544          * @param path
545          * @return
546          */
547         public static boolean isAbsolutePath (String path) {
548                 return  path.startsWith(File.separator);
549         }
550
551         /**
552          * Löscht Slash am Anfang des Strings
553          * @param path
554          * @return
555          */
556         public static String removeFirstSlash (String path) {
557                 return  path.startsWith("/") ? path.substring(1) : path;
558         }
559
560         /**
561          * formatiert eine Zahl (0-99) zweistellig (z.B. 5 -> 05)
562          * @return zwistellige Zahl
563          */
564         public static String pad2 (int number) {
565                 return  number < 10 ? "0" + number : String.valueOf(number);
566         }
567
568         /**
569          * formatiert eine Zahl (0-999) dreistellig (z.B. 7 -> 007)
570          *
571          * @return 3-stellige Zahl
572          */
573         public static String pad3 (int number) {
574                 return  number < 10 ? "00" + number : number < 100 ? "0" + number : String.valueOf(number);
575         }
576
577         /**
578          * Konvertiert Unix-Linefeeds in Win-Linefeeds
579          * @param s
580          * @return Konvertierter String
581          */
582         public static String unixLineFeedsToWin(String s) {
583                 int i = -1;
584                 while (true) {
585                         i = s.indexOf('\n', i+1);
586                         if (i < 0) break;
587                         if ((i == 0 || s.charAt(i-1) != '\r') &&
588                                 (i == s.length()-1 || s.charAt(i+1) != '\r')) {
589                                 s = s.substring(0, i)+'\r'+s.substring(i);
590                                 i++;
591                         }
592                 }
593                 return s;
594         }
595
596
597         /**
598          * verwandelt einen String in eine gültige Url, konvertiert Sonderzeichen
599          * und Spaces werden zu Underscores
600          *
601          * @return gültige Url
602          */
603         public static String convert2url(String s) {
604                 s = toLowerCase(s);
605                 StringBuffer buf = new StringBuffer();
606                 for(int i = 0; i < s.length(); i++ ) {
607                                 switch( s.charAt( i ) ) {
608                                 case 'ö':
609                         buf.append( "oe" ); break;
610                                 case 'ä':
611                         buf.append( "ae" ); break;
612                                 case 'ü':
613                         buf.append( "ue" ); break;
614                                 case 'ã':
615                         buf.append( "a" ); break;
616                                 case '´':
617                                 case '.':
618                         buf.append( "_" ); break;
619                                 case ' ':
620                         if( buf.charAt( buf.length() - 1 ) != '_' ) {
621                                         buf.append( "_" );
622                         }
623                         break;
624                                 default:
625                         buf.append( s.charAt( i ) );
626                                 }
627                 }
628                 return buf.toString();
629         }
630
631         public static String replaceQuot(String s) {
632                 StringBuffer buffer = new StringBuffer();
633                 for(int j = 0; j < s.length();j++){
634                         if(s.charAt(j)=='&'){
635                                 if(s.indexOf( "&quot;",j) == j) {
636                                         buffer.append( "\"" );
637                                         j += 5;
638                                 }//if
639                         } else {
640                                 buffer.append(s.charAt(j));
641                         }//else
642                 }//for
643                 return buffer.toString();
644         }
645
646         /**
647          * schnellere Variante der String.toLowerCase()-Routine
648          *
649          * @return String in Kleinbuchsten
650          */
651         public static String toLowerCase(String s) {
652                 int l = s.length();
653                 char[] a = new char[l];
654                 for (int i = 0; i < l; i++)
655                         a[i] = Character.toLowerCase(s.charAt(i));
656                 return new String(a);
657         }
658
659          /**
660          * Finds <code>element</code> in String-Array <code>array</code>
661          * @param array
662          * @param element
663          * @return Position where the element was found or -1
664          */
665         public static int indexOf(String[] array, String element) {
666                 if (array != null)
667                         for (int i = 0; i < array.length; i++)
668                                 if (array[i].equals(element))
669                                         return i;
670                 return -1;
671         }
672
673          /**
674    * Finds position of first in <code>aCharacters</code> array in String
675    * <code>aString</code> starting from position <code>aFrom</code>
676    *
677          * @param aString
678          * @param aCharacters
679    * @param aFrom
680          * @return Position where the element was found or -1
681          */
682   public static int indexOfCharacters(String aString, char[] aCharacters, int aFrom) {
683     int i;
684     int result=-1;
685     int position;
686
687     for (i=0; i<aCharacters.length ; i++) {
688       position = aString.indexOf(aCharacters[i], aFrom);
689
690       if (position != -1 && ( result == -1 || position < result )) {
691         result = position;
692       }
693     }
694
695     return result;
696   }
697
698
699         /**
700          * Testet auf Vorkommen von <code>element</code> in <code>array</code>
701          * @param array String-Array
702          * @param element
703          * @return true wenn <code>element</code> vorkommt, sonst false
704          */
705         public static boolean contains(String[] array, String element) {
706                 return indexOf(array, element) >= 0;
707         }
708
709                 /**
710          * Ermittelt CRC-Prüfsumme von String <code>s</code>
711          * @param s
712          * @return CRC-Prüfsumme
713          */
714         public static int getCRC(String s) {
715                 int h = 0;
716                 char val[] = s.toCharArray();
717                 int len = val.length;
718
719                 for (int i = 0 ; i < len; i++) {
720                         h &= 0x7fffffff;
721                         h = (((h >> 30) | (h << 1)) ^ (val[i]+i));
722                 }
723
724                 return (h << 8) | (len & 0xff);
725         }
726
727                 /**
728          * Liefert Default-Wert def zurück, wenn String <code>s</code>
729          * kein Integer ist.
730          *
731          * @param s
732          * @param def
733          * @return geparster int aus s oder def
734          */
735         public static int parseInt(String s, int def) {
736                 if (s == null) return def;
737                 try {
738                         return Integer.parseInt(s);
739                 } catch (NumberFormatException e) {
740                         return def;
741                 }
742         }
743
744   public static String interpretAsString(Object aValue) throws Exception {
745     if (aValue instanceof String)
746       return (String) aValue;
747
748     if (aValue instanceof Integer)
749       return ((Integer) aValue).toString();
750
751     if (aValue == null)
752       return "";
753
754     throw new Exception("String expected, "+aValue+" found");
755   }
756
757
758
759         /**
760          * Liefert Defaultwert def zurück, wenn s nicht zu einem float geparsed werden kann.
761          * @param s
762          * @param def
763          * @return geparster float oder def
764          */
765         public static float parseFloat(String s, float def) {
766                 if (s == null) return def;
767                 try {
768                         return new Float(s).floatValue();
769                 } catch (NumberFormatException e) {
770                         return def;
771                 }
772         }
773
774                 /**
775          * Findet Ende eines Satzes in String <code>text</code>
776          * @param text
777          * @param startIndex
778          * @return index des Satzendes, oder -1
779          */
780         public static int findEndOfSentence(String text, int startIndex) {
781                  while (true) {
782                          int i = text.indexOf('.', startIndex);
783                          if (i < 0) return -1;
784                          if (i > 0 && !Character.isDigit(text.charAt(i-1)) &&
785                                         (i+1 >= text.length()
786                                         || text.charAt(i+1) == ' '
787                                         || text.charAt(i+1) == '\n'
788                                         || text.charAt(i+1) == '\t'))
789                                         return i+1;
790                          startIndex = i+1;
791                  }
792         }
793
794                 /**
795          * Findet Wortende in String <code>text</code> ab <code>startIndex</code>
796          * @param text
797          * @param startIndex
798          * @return Index des Wortendes, oder -1
799          */
800         public static int findEndOfWord(String text, int startIndex) {
801                 int i = text.indexOf(' ', startIndex),
802                         j = text.indexOf('\n', startIndex);
803                 if (i < 0) i = text.length();
804                 if (j < 0) j = text.length();
805                 return Math.min(i, j);
806         }
807
808
809         /**
810          *  convertNewline2P ist eine regex-routine zum umwandeln von 2 oder mehr newlines (\n)
811          *  in den html-tag <p>
812          *  nur sinnvoll, wenn text nicht im html-format eingegeben
813          */
814         public static String convertNewline2P(String haystack) {
815                         return re_brbr2p.substituteAll(haystack,"\n</p><p>");
816         }
817
818         /**
819          *  convertNewline2Break ist eine regex-routine zum umwandeln von 1 newline (\n)
820          *  in den html-tag <br>
821          *  nur sinnvoll, wenn text nicht im html-format eingegeben
822          */
823         public static String convertNewline2Break(String haystack) {
824                 return re_newline2br.substituteAll(haystack,"$0<br />");
825         }
826
827         /**
828          *  createMailLinks wandelt text im email-adressenformat
829          *  in einen klickbaren link um
830          *  nur sinnvoll, wenn text nicht im html-format eingegeben
831          */
832         public static String createMailLinks(String haystack) {
833                         return re_mail.substituteAll(haystack,"<a href=\"mailto:$0\">$0</a>");
834         }
835
836
837         /**
838          *  createMailLinks wandelt text im email-adressenformat
839          *  in einen klickbaren link um
840          *  nur sinnvoll, wenn text nicht im html-format eingegeben
841          */
842         public static String createMailLinks(String haystack, String imageRoot, String mailImage) {
843                 return re_mail.substituteAll(haystack,"<img src=\""+imageRoot+"/"+mailImage+"\" border=\"0\"/>&#160;<a href=\"mailto:$0\">$0</a>");
844         }
845
846
847         /**
848          *  createURLLinks wandelt text im url-format
849          *  in einen klickbaren link um
850          *  nur sinnvoll, wenn text nicht im html-format eingegeben
851          */
852         public static String createURLLinks(String haystack) {
853                 return re_url.substituteAll(haystack,"<a href=\"$0\">$0</a>");
854         }
855
856         /**
857          * this routine takes text in url format and makes
858          * a clickaeble "<href>" link removing any "illegal" html tags
859          * @param haystack, the url
860          * @param title, the href link text
861          * @param imagRoot, the place to find icons
862          * @param extImage, the url of the icon to show next to the link
863          * @return a String containing the url
864          */
865         public static String createURLLinks(String haystack, String title, String imageRoot,String extImage) {
866                 if (title == null) {
867                         return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">$0</a>");
868                 } else {
869                         title = removeHTMLTags(title);
870                         return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">"+title+"</a>");
871                 }
872         }
873
874         /**
875          * this routine takes text in url format and makes
876          * a clickaeble "<href>" link removing any "illegal" html tags
877          * @param haystack, the url
878          * @param imageRoot, the place to find icons
879          * @param extImage, the url of the icon to show next to the link
880          * @param intImage, unused
881          * @return a String containing the url
882          */
883         public static String createURLLinks(String haystack, String title, String imageRoot,String extImage,String intImage) {
884                 return createURLLinks(haystack, title, imageRoot, extImage);
885         }
886
887          /**
888          *  deleteForbiddenTags
889          *  this method deletes all <script>, <body> and <head>-tags
890          */
891         public static final String deleteForbiddenTags(String haystack) {
892     return re_forbiddenTags.substituteAll(haystack,"");
893         }
894
895          /**
896          *  deleteHTMLTableTags
897          *  this method deletes all <table>, <tr> and <td>-tags
898          */
899         public static final String deleteHTMLTableTags(String haystack) {
900     return re_tables.substituteAll(haystack,"");
901         }
902
903         /**
904          * this method deletes all html tags
905          */
906         public static final String removeHTMLTags(String haystack){
907                         return re_tags.substituteAll(haystack,"");
908         }
909
910
911         /**
912          * this method deletes all but the approved tags html tags
913          * it also deletes approved tags which contain malicious-looking attributes and doesn't work at all
914          */
915         public static String approveHTMLTags(String haystack){
916                 try {
917                         String approvedTags="a|img|h1|h2|h3|h4|h5|h6|br|b|i|strong|p";
918                         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";
919                         String approvedProtocols="rtsp|http|ftp|https|freenet|mailto";
920
921                         // kill all the bad tags that have attributes
922                         String s = "<\\s*/?\\s*(?!(("+approvedTags+")\\s))\\w+\\s[^>]*>";
923                         RE regex = new RE(s,RE.REG_ICASE);
924                         haystack = regex.substituteAll(haystack,"");
925
926                         // kill all the bad tags that are attributeless
927                         regex = new RE("<\\s*/?\\s*(?!(("+approvedTags+")\\s*>))\\w+\\s*>",RE.REG_ICASE);
928                         haystack = regex.substituteAll(haystack,"");
929
930                         // kill all the tags which have a javascript attribute like onLoad
931                         regex = new RE("<[^>]*("+badAttributes+")[^>]*>",RE.REG_ICASE);
932                         haystack = regex.substituteAll(haystack,"");
933
934                         // kill all the tags which include a url to an unacceptable protocol
935                         regex = new RE("<\\s*a\\s+[^>]*href=(?!(\'|\")?("+approvedProtocols+"))[^>]*>",RE.REG_ICASE);
936                         haystack = regex.substituteAll(haystack,"");
937
938                         return haystack;
939                 } catch(REException ex){
940                         ex.printStackTrace();
941                         return null;
942                 }
943         }
944
945
946         /**
947          *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
948          *  htmlcodierten string auf und returnt einen htmlcodierten String
949          */
950         public static String createHTML(String content){
951                 content=convertNewline2Break(content);
952                 content=convertNewline2P(content);
953                 content=createMailLinks(content);
954                 content=createURLLinks(content);
955                 return content;
956         }
957
958
959         /**
960          *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
961          *  htmlcodierten string auf und returnt einen htmlcodierten String
962          */
963         public static String createHTML(String content,String producerDocRoot,String mailImage,String extImage,String intImage){
964                 content=convertNewline2Break(content);
965                 content=convertNewline2P(content);
966                 content=createMailLinks(content,producerDocRoot,mailImage);
967                 content=createURLLinks(content,null,producerDocRoot,extImage,intImage);
968                 return content;
969         }
970
971 }
972