tiny fixes here and there
[mir.git] / source / mir / misc / StringUtil.java
1 /*
2  * Copyright (C) 2005 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  any library licensed under the Apache Software License.
22  * You must obey the GNU General Public License in all respects for all of the code used
23  * other than the above mentioned libraries.  If you modify this file, you may extend this
24  * exception to your version of the file, but you are not obligated to do so.
25  * If you do not wish to do so, delete this exception statement from your version.
26  */
27 package  mir.misc;
28
29 import gnu.regexp.RE;
30 import gnu.regexp.REException;
31
32 import java.text.NumberFormat;
33 import java.util.Calendar;
34 import java.util.Date;
35 import java.util.GregorianCalendar;
36 import java.util.TimeZone;
37
38 /**
39  */
40 public final class StringUtil {
41
42   private static TimeZone UTC = TimeZone.getTimeZone("UTC");
43   private static RE   re_newline2br, re_brbr2p, re_mail, re_url, re_tags,
44                       re_tables, re_forbiddenTags;
45
46   private StringUtil() { }  // this avoids contruction
47
48   static {
49     try {
50       //precompile regex
51       re_newline2br = new RE("(\r?\n){1}");
52       re_brbr2p     = new RE("(<br>\r?\n<br>){1,}");
53       re_mail       = new RE("\\b([a-zA-Z0-9_.-]+)@([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_.-]+)\\b");
54       re_url        = new RE("((https://)|(http://)|(ftp://)){1}([a-zA-Z0-9_-]+).([a-zA-Z0-9_.:-]+)/?([^ \t\r\n<>\\)\\]]+[^ \t\r\n.,<>\\)\\]])");
55       re_tags       = new RE("<[^>]*>",RE.REG_ICASE);
56       re_tables = new RE("<[ \t\r\n/]*(table|td|tr)[ \t\r\n]*>",RE.REG_ICASE);
57       re_forbiddenTags = new RE("<[ \t\r\n/]*(html|meta|body|head|script)[ \t\r\n]*>",RE.REG_ICASE);
58     }
59     catch (REException e){
60       System.err.println("FATAL: StringUtil: could not precompile REGEX: "+e.toString());
61     }
62   }
63
64   /**
65    * Formats a number with the specified minimum and maximum number of digits.
66    **/
67   public static synchronized String zeroPaddingNumber(long value, int minDigits,
68       int maxDigits)
69   {
70     NumberFormat numberFormat = NumberFormat.getInstance();
71     numberFormat.setMinimumIntegerDigits(minDigits);
72     numberFormat.setMaximumIntegerDigits(maxDigits);
73     return numberFormat.format(value);
74   }
75
76   /**
77    * Wandelt Datum in einen 8-ziffrigen String um (yyyymmdd)
78    * @param theDate
79    * @return 8-ziffriger String (yyyymmdd)
80    */
81
82   public static final String date2webdbDate (GregorianCalendar theDate) {
83     StringBuffer webdbDate = new StringBuffer();
84     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
85     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
86     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
87
88     return  webdbDate.toString();
89   }
90
91   /**
92    * Wandelt Calendar in einen 12-ziffrigen String um (yyyymmddhhmm)
93    * @param theDate
94    * @return 12-ziffriger String (yyyymmdd)
95    */
96
97   public static final String date2webdbDateTime (GregorianCalendar theDate) {
98     StringBuffer webdbDate = new StringBuffer();
99     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
100     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
101     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
102     webdbDate.append(pad2(theDate.get(Calendar.HOUR)));
103     webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
104
105     return  webdbDate.toString();
106   }
107
108   /**
109    * Return a http://www.w3.org/TR/NOTE-datetime formatted date (yyyy-mm-ddThh:mm:ssTZ)
110    * @param theDate
111    * @return w3approved datetime
112    */
113
114   public static final String date2w3DateTime (GregorianCalendar theDate) {
115     StringBuffer webdbDate = new StringBuffer();
116     webdbDate.append(String.valueOf(theDate.get(Calendar.YEAR)));
117     webdbDate.append("-");
118     webdbDate.append(pad2(theDate.get(Calendar.MONTH) + 1));
119     webdbDate.append("-");
120     webdbDate.append(pad2(theDate.get(Calendar.DATE)));
121     webdbDate.append("T");
122     webdbDate.append(pad2(theDate.get(Calendar.HOUR_OF_DAY)));
123     webdbDate.append(":");
124     webdbDate.append(pad2(theDate.get(Calendar.MINUTE)));
125     webdbDate.append(":");
126     webdbDate.append(pad2(theDate.get(Calendar.SECOND)));
127     //assumes you are an hour-multiple away from UTC....
128     int offset=(theDate.get(Calendar.ZONE_OFFSET)/(60*60*1000));
129     if (offset < 0){
130       webdbDate.append("-");
131     }
132     else{
133       webdbDate.append("+");
134     }
135     webdbDate.append(pad2(Math.abs(offset)));
136     webdbDate.append(":00");
137     return  webdbDate.toString();
138   }
139
140   /**
141    * wandelt Calendar in dd.mm.yyyy / hh.mm um
142    * @param theDate
143    * @return String mit (dd.mm.yyyy / hh.mm um)
144    */
145   public static String date2readableDateTime (GregorianCalendar theDate) {
146     String readable = "";
147     int hour;
148     readable += pad2(theDate.get(Calendar.DATE));
149     readable += "." + pad2(theDate.get(Calendar.MONTH) + 1);
150     readable += "." + String.valueOf(theDate.get(Calendar.YEAR));
151     hour = theDate.get(Calendar.HOUR);
152     if (theDate.get(Calendar.AM_PM) == Calendar.PM)
153       hour += 12;
154     readable += " / " + pad2(hour);
155     readable += ":" + pad2(theDate.get(Calendar.MINUTE));
156     return  readable;
157   }
158
159   /**
160   *  deleteForbiddenTags
161   *  this method deletes all <script>, <body> and <head>-tags
162   */
163   public static final String deleteForbiddenTags(String haystack) {
164     return re_forbiddenTags.substituteAll(haystack,"");
165   }
166
167   /**
168    *  deleteHTMLTableTags
169    *  this method deletes all <table>, <tr> and <td>-tags
170    */
171   public static final String deleteHTMLTableTags(String haystack) {
172     return re_tables.substituteAll(haystack,"");
173   }
174
175   /**
176    * wandelt eine Datum in einen 8-buchstabigen String, der durch <code>/</code>
177    * getrennt ist.
178    *
179    * @param webdbDate
180    * @return String mit <code>/yyyy/mm/dd</code>
181    */
182   public static final String webdbDate2path (String webdbDate) {
183     StringBuffer path = new StringBuffer();
184     path.append("/").append(webdbDate.substring(0, 4));
185     path.append("/").append(webdbDate.substring(4, 6));
186     path.append("/");
187     //who did this?
188     //path.append("/").append(webdbDate.substring(6, 8));
189     return  path.toString();
190   }
191
192   /**
193    * Replaces in <code>haystack</code> matching <code>pattern</code> by <code>substitute</code>
194    * @param haystack
195    * @param pattern
196    * @param substitute
197    * @return String with replacements.
198    */
199   public static String regexpReplace(String haystack, String pattern, String substitute) {
200     try {
201       RE regex = new RE(pattern);
202       return regex.substituteAll(haystack,substitute);
203     } catch(REException ex){
204       return null;
205     }
206   }
207
208   /**
209    * L?scht <code>/</code> am Ende des Strings, falls vorhanden
210    * @param path
211    * @return String ohne <code>/</code> am Ende
212    */
213   public static final String removeSlash (String path) {
214     return  path.length() > 1 && path.endsWith("/") ? path.substring(0, path.length()
215         - 1) : path;
216   }
217
218   /**
219    * formatiert eine Zahl (0-99) zweistellig (z.B. 5 -> 05)
220    * @return zwistellige Zahl
221    */
222   public static String pad2 (int number) {
223     return  number < 10 ? "0" + number : String.valueOf(number);
224   }
225
226   /**
227    * formatiert eine Zahl (0-999) dreistellig (z.B. 7 -> 007)
228    *
229    * @return 3-stellige Zahl
230    */
231   public static String pad3 (int number) {
232     return  number < 10 ? "00" + number : number < 100 ? "0" + number : String.valueOf(number);
233   }
234
235   /**
236    * Liefert Default-Wert def zur?ck, wenn String <code>s</code>
237    * kein Integer ist.
238    *
239    * @param s
240    * @param def
241    * @return geparster int aus s oder def
242    */
243   public static int parseInt(String s, int def) {
244     if (s == null) return def;
245     try {
246       return Integer.parseInt(s);
247     } catch (NumberFormatException e) {
248       return def;
249     }
250   }
251
252
253   /**
254    *  convertNewline2P ist eine regex-routine zum umwandeln von 2 oder mehr newlines (\n)
255    *  in den html-tag <p>
256    *  nur sinnvoll, wenn text nicht im html-format eingegeben
257    */
258   private static String convertNewline2P(String haystack) {
259     return re_brbr2p.substituteAll(haystack,"\n</p><p>");
260   }
261
262   /**
263    *  convertNewline2Break ist eine regex-routine zum umwandeln von 1 newline (\n)
264    *  in den html-tag <br>
265    *  nur sinnvoll, wenn text nicht im html-format eingegeben
266    */
267   private static String convertNewline2Break(String haystack) {
268     return re_newline2br.substituteAll(haystack,"$0<br />");
269   }
270
271   /**
272    *  createMailLinks wandelt text im email-adressenformat
273    *  in einen klickbaren link um
274    *  nur sinnvoll, wenn text nicht im html-format eingegeben
275    */
276   private static String createMailLinks(String haystack) {
277     return re_mail.substituteAll(haystack,"<a href=\"mailto:$0\">$0</a>");
278   }
279
280
281   /**
282    *  createMailLinks wandelt text im email-adressenformat
283    *  in einen klickbaren link um
284    *  nur sinnvoll, wenn text nicht im html-format eingegeben
285    */
286   private static String createMailLinks(String haystack, String imageRoot, String mailImage) {
287     return re_mail.substituteAll(haystack,"<img src=\""+imageRoot+"/"+mailImage+"\" border=\"0\"/>&#160;<a href=\"mailto:$0\">$0</a>");
288   }
289
290
291   /**
292    *  createURLLinks wandelt text im url-format
293    *  in einen klickbaren link um
294    *  nur sinnvoll, wenn text nicht im html-format eingegeben
295    */
296   private static String createURLLinks(String haystack) {
297     return re_url.substituteAll(haystack,"<a href=\"$0\">$0</a>");
298   }
299
300   /**
301    * this routine takes text in url format and makes
302    * a clickaeble "<href>" link removing any "illegal" html tags
303    * @param haystack  the url
304    * @param title  the href link text
305    * @param imageRoot  the place to find icons
306    * @param extImage  the url of the icon to show next to the link
307    * @return a String containing the url
308    */
309   private static String createURLLinks(String haystack, String title, String imageRoot,String extImage) {
310     if (title == null) {
311       return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">$0</a>");
312     }
313                 title = removeHTMLTags(title);
314                 return re_url.substituteAll(haystack,"<img src=\""+imageRoot+"/"+extImage+"\" border=\"0\"/>&#160;<a href=\"$0\">"+title+"</a>");
315   }
316
317   /**
318    * this routine takes text in url format and makes
319    * a clickaeble "<href>" link removing any "illegal" html tags
320    * @param haystack the url
321    * @param imageRoot the place to find icons
322    * @param extImage the url of the icon to show next to the link
323    * @param intImage unused
324    * @return a String containing the url
325    */
326   private static String createURLLinks(String haystack, String title, String imageRoot,String extImage,String intImage) {
327     return createURLLinks(haystack, title, imageRoot, extImage);
328   }
329
330   /**
331    * this method deletes all html tags
332    */
333   public static String removeHTMLTags(String haystack){
334     return re_tags.substituteAll(haystack,"");
335   }
336
337
338   /**
339    *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
340    *  htmlcodierten string auf und returnt einen htmlcodierten String
341    */
342   public static String createHTML(String content){
343     content=convertNewline2Break(content);
344     content=convertNewline2P(content);
345     content=createMailLinks(content);
346     content=createURLLinks(content);
347     return content;
348   }
349
350
351   /**
352    *  createHTML ruft alle regex-methoden zum unwandeln eines nicht
353    *  htmlcodierten string auf und returnt einen htmlcodierten String
354    */
355   public static String createHTML(String content,String producerDocRoot,String mailImage,String extImage,String intImage){
356     content=convertNewline2Break(content);
357     content=convertNewline2P(content);
358     content=createMailLinks(content, producerDocRoot,mailImage);
359     content=createURLLinks(content, null, producerDocRoot, extImage, intImage);
360
361     return content;
362   }
363
364   /**
365    * Converts mir's horrible internal date format (yyyy-MM-dd HH:mm:ss+zz) into a java Date
366    *
367    * @param anInternalDate
368    */
369   public static Date convertMirInternalDateToDate(String anInternalDate) {
370     Calendar calendar = new GregorianCalendar();
371
372     int year;
373     int month;
374     int day;
375     int hours;
376     int minutes;
377     int seconds;
378     int timezoneOffset;
379
380     year = Integer.parseInt(anInternalDate.substring(0,4));
381     month = Integer.parseInt(anInternalDate.substring(5,7));
382     day = Integer.parseInt(anInternalDate.substring(8,10));
383     hours = Integer.parseInt(anInternalDate.substring(11,13));
384     minutes = Integer.parseInt(anInternalDate.substring(14,16));
385     seconds = Integer.parseInt(anInternalDate.substring(17,19));
386
387     timezoneOffset = Integer.parseInt(anInternalDate.substring(20,22));
388     if (anInternalDate.charAt(19) == '-') {
389       timezoneOffset = -timezoneOffset;
390     }
391
392     calendar.setTimeZone(UTC);
393     calendar.set(year, month-1, day, hours, minutes, seconds);
394     calendar.add(Calendar.HOUR, -timezoneOffset);
395
396     return calendar.getTime();
397   }
398
399 }
400