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