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