KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > meshcms > util > Utils


1 /*
2  * MeshCMS - A simple CMS based on SiteMesh
3  * Copyright (C) 2004-2007 Luciano Vernaschi
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * You can contact the author at http://www.cromoteca.com
20  * and at info@cromoteca.com
21  */

22
23 package org.meshcms.util;
24
25 import java.io.*;
26 import java.net.*;
27 import java.text.*;
28 import java.util.*;
29 import java.util.zip.*;
30
31 /**
32  * A collection of static utilities.
33  *
34  * @author Luciano Vernaschi
35  */

36 public final class Utils {
37   private Utils() {
38   }
39
40   /**
41    * A string of characters to be used for random strings.
42    */

43   public static final String JavaDoc VALID_CHARS = "abcdefghijkmnpqrstuvwxyz23456789";
44
45   /**
46    * Characters that should not be found in a file name.
47    */

48   public static final String JavaDoc INVALID_FILENAME_CHARS = "\"\\/:*?<>| ,\t\n\r";
49
50   /**
51    * Characters that are always acceptable in a filename.
52    */

53   public static final String JavaDoc VALID_FILENAME_CHARS =
54       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'._-";
55
56   /**
57    * A standard size for buffers.
58    */

59   public static final int BUFFER_SIZE = 2048;
60
61   /**
62    * The number of bytes in a kilobyte (2^10).
63    */

64   public static final int KBYTE = 1024;
65
66   /**
67    * The number of bytes in a megabyte (2^20).
68    */

69   public static final int MBYTE = KBYTE * KBYTE;
70
71   /**
72    * The number of bytes in a gigabyte (2^30).
73    */

74   public static final int GBYTE = MBYTE * KBYTE;
75
76   /**
77    * Repeatedly adds a character to the beginning of a number until the desired
78    * length has been reached. Tipically used to add spaces or zeros.
79    */

80   public static String JavaDoc addDigits(int num, char space, int len) {
81     return addDigits(Integer.toString(num), space, len);
82   }
83
84   /**
85    * Repeatedly adds a character to the beginning of a string until the desired
86    * length has been reached. Tipically used to add spaces or zeros.
87    */

88   public static String JavaDoc addDigits(String JavaDoc s, char space, int len) {
89     if (s == null) {
90       s = "";
91     }
92
93     while (s.length() < len) {
94       s = space + s;
95     }
96
97     return s;
98   }
99
100   /**
101    * Checks if an object is null or if its string representation is empty.
102    *
103    * @param s the String to be checked
104    * @return <code>true</code> if the String is null or empty, <code>false</code> otherwise.
105    */

106   public static boolean isNullOrEmpty(String JavaDoc s) {
107     return s == null || s.equals("");
108   }
109
110   /**
111    * Checks if a string is null, empty or made of whitespaces only.
112    *
113    * @param s the String to be checked
114    * @return <code>true</code> if the String is null or <i>whitespace</i>, <code>false</code> otherwise.
115    */

116   public static boolean isNullOrWhitespace(String JavaDoc s) {
117     if (s == null) {
118       return true;
119     }
120
121     for (int i = 0; i < s.length(); i++) {
122       if (!Character.isWhitespace(s.charAt(i))) {
123         return false;
124       }
125     }
126
127     return true;
128   }
129
130   /**
131    * Trim that handles <code>null</code> values too.
132    *
133    * @param s the String to be trimmed.
134    *
135    * @see String#trim()
136    *
137    * @return <code>s.trim()</code>, or null if <code>s</code> is null.
138    */

139   public static String JavaDoc trim(String JavaDoc s) {
140     return (s == null) ? null : s.trim();
141   }
142
143   /**
144    * Returns a non-null version of an object.
145    *
146    * @param s the String to be processed
147    *
148    * @return s.toString(), or the empty string if s is null
149    */

150   public static String JavaDoc noNull(String JavaDoc s) {
151     return s == null ? "" : s;
152   }
153
154   /**
155    * Returns a non-null version of a string.
156    *
157    * @param s the String to be processed
158    * @param def the default value, in case the String is null.
159    *
160    * @return s.toString(), or the default string provided if s is null
161    */

162   public static String JavaDoc noNull(String JavaDoc s, String JavaDoc def) {
163     return s == null ? def : s;
164   }
165
166   /**
167    * Compares two strings.&nbsp;<code>Null</code> values are accepted. If
168    * <code>ignoreCase</code> is true, case is ignored.
169    *
170    * @param s1 the String 1 to be compared to
171    * @param s2 the String 2
172    * @param ignoreCase flag
173    *
174    * @return true if both strings are null or if they are equal, false
175    * otherwise.
176    */

177   public static boolean compareStrings(String JavaDoc s1, String JavaDoc s2, boolean ignoreCase) {
178     if (s1 == null && s2 == null) {
179       return true;
180     }
181
182     if (s1 != null && s2 != null) {
183       return ignoreCase ? s1.equalsIgnoreCase(s2) : s1.equals(s2);
184     }
185
186     return false;
187   }
188
189   /**
190    * Checks if a string contains a value that is supposed to mean
191    * &quot;true&quot;.
192    *
193    * @param s the String to be checked
194    *
195    * @return true if the string is "true", "1", "yes", "ok", "checked",
196    * "selected" or "on" (case insensitive),
197    * false otherwise (null included)
198    */

199   public static boolean isTrue(String JavaDoc s) {
200     return isTrue(s, false);
201   }
202
203   public static boolean isTrue(String JavaDoc s, boolean checkFalse) {
204     boolean result = false;
205     
206     if (s == null) {
207       if (checkFalse) {
208         throw new IllegalArgumentException JavaDoc();
209       }
210     } else {
211       s = s.trim().toLowerCase();
212
213       if (s.equals("true") || s.equals("1") || s.equals("yes") ||
214           s.equals("ok") || s.equals("checked") || s.equals("selected") ||
215           s.equals("on")) {
216         result = true;
217       } else if (checkFalse) {
218         if (!(s.equals("false") || s.equals("0") || s.equals("no") ||
219             s.equals("off"))) {
220           throw new IllegalArgumentException JavaDoc();
221         }
222       }
223     }
224     
225     return result;
226   }
227
228   /**
229    * Shortens a string by cutting it and adding ellipses at the end. The string
230    * is returned unmodified if its length is less or equal than len. Handles <code>null</code> Strings too.
231    *
232    * @param s the source String to be shortened
233    * @param len the lenght
234    *
235    * @return the shortened String.
236    */

237   public static String JavaDoc limitedLength(String JavaDoc s, int len) {
238     String JavaDoc s1;
239
240     if (isNullOrEmpty(s)) {
241       s1 = "";
242     } else if (s.length() <= len) {
243       s1 = s;
244     } else if (len < 5) {
245       s1 = "...";
246     } else {
247       s1 = s.substring(0, len - 4) + " ...";
248     }
249
250     return s1;
251   }
252
253   /**
254    * Replaces ' with \' (useful for JavaScript).
255    *
256    * @param s the String to escape
257    *
258    * @return the escaped String
259    */

260   public static String JavaDoc escapeSingleQuotes(String JavaDoc s) {
261     return replace(s, '\'', "\\'");
262   }
263
264   /**
265    * Replaces all the occurrences of a character in a string.
266    *
267    * @param s the string that will be analyzed
268    * @param c the character to be replaced
269    * @param n the string used in place of the character
270    *
271    * @return the empty string if s is null; the modified string otherwise.
272    */

273   public static String JavaDoc replace(String JavaDoc s, char c, String JavaDoc n) {
274     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
275
276     for (int i = 0; i < s.length(); i++) {
277       if (s.charAt(i) == c) {
278         sb.append(n);
279       } else {
280         sb.append(s.charAt(i));
281       }
282     }
283
284     return sb.toString();
285   }
286
287   /**
288    * Replaces some characters with their HTML entities, like
289    * the method {@link #encodeHTML(String, boolean)} but without the ampersand.
290    *
291    * @see #encodeHTML(String, boolean)
292    *
293    * @param s the String to be encoded
294    *
295    * @return the HTML encoded String
296    */

297   public static String JavaDoc encodeHTML(String JavaDoc s) {
298     return encodeHTML(s, false);
299   }
300
301   /**
302    * Replaces some characters with their HTML entities.
303    * Only replaces
304    * <ul>
305    * <li>Quoutation: &quot;</li>
306    * <li>Apostrophe: &#39;</li>
307    * <li>Less Than: &lt;</li>
308    * <li>Greater Than: &gt;</li>
309    * </ul>
310    *
311    * @param s the String to be encoded
312    * @param encodeAmpersands if true replaces ampersand &amp; too
313    *
314    * @return the HTML encoded String
315    */

316   public static String JavaDoc encodeHTML(String JavaDoc s, boolean encodeAmpersands) {
317     if (isNullOrEmpty(s)) {
318       return "";
319     }
320
321     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(s.length());
322
323     for (int i = 0; i < s.length(); i++) {
324       char c = s.charAt(i);
325
326       switch (c) {
327         case '\"':
328           sb.append("&quot;");
329           break;
330         case '&':
331           if (encodeAmpersands) {
332             sb.append("&amp;");
333           } else {
334             sb.append(c);
335           }
336
337           break;
338         case '\'':
339           sb.append("&#39;");
340           break;
341         case '<':
342           sb.append("&lt;");
343           break;
344         case '>':
345           sb.append("&gt;");
346           break;
347         default:
348           sb.append(c);
349       }
350     }
351
352     return sb.toString();
353   }
354
355   /**
356    * Replaces some HTML entities with the corresponding characters. Only replaces
357    * &quot;, &amp;, &#39;, &lt; and &gt;. This is the reverse of method {@link #encodeHTML(String, boolean)}
358    *
359    * @param s the HTML String
360    *
361    * @return the decoded String
362    */

363   public static String JavaDoc decodeHTML(String JavaDoc s) {
364     if (isNullOrEmpty(s)) {
365       return "";
366     }
367
368     int sl = s.length();
369     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(sl);
370     int i = 0;
371     String JavaDoc s0;
372
373     while (i < sl - 3) {
374       s0 = s.substring(i, i + 4);
375
376       if (s0.equals("&gt;")) {
377         sb.append('>');
378         i += 4;
379       } else if (s0.equals("&lt;")) {
380         sb.append('<');
381         i += 4;
382       } else if (s0.equals("&amp")) {
383         if (i < sl - 4 && s.charAt(i + 4) == ';') {
384           sb.append('&');
385           i += 5;
386         }
387       } else if (s0.equals("&#39")) {
388         if (i < sl - 4 && s.charAt(i + 4) == ';') {
389           sb.append('\'');
390           i += 5;
391         }
392       } else if (s0.equals("&quo")) {
393         if (i < sl - 5 && s.charAt(i + 4) == 't' && s.charAt(i + 5) == ';') {
394           sb.append('\"');
395           i += 6;
396         }
397       } else {
398         sb.append(s.charAt(i++));
399       }
400     }
401
402     return sb.append(s.substring(i)).toString();
403   }
404
405   /**
406    * Strips the HTML tags from a string.
407    *
408    * @param s the HTML String to be processed
409    *
410    * @return the stripped String.
411    */

412   public static String JavaDoc stripHTMLTags(String JavaDoc s) {
413     return (s!=null) ? s.replaceAll("</?\\S+?[\\s\\S+]*?>", " ") : null;
414   }
415
416   /**
417    * Copies the content of a file to another file.
418    *
419    * @param file the file to be copied
420    * @param newFile the file to copy to
421    * @param overwrite if false, the file is not copied if newFile exists
422    * @param setLastModified if true, newFile gets the same date of file
423    *
424    * @return true if copied successfully, false otherwise
425    * @throws IOException if the content can't be copied due to an I/O error
426    */

427   public static boolean copyFile(File file, File newFile, boolean overwrite,
428                                  boolean setLastModified) throws IOException {
429     if (newFile.exists() && !overwrite) {
430       return false;
431     }
432
433     FileInputStream fis = null;
434
435     try {
436       fis = new FileInputStream(file);
437       copyStream(fis, new FileOutputStream(newFile), true);
438
439       if (setLastModified) {
440         newFile.setLastModified(file.lastModified());
441       }
442     } finally {
443       if (fis != null) {
444         fis.close();
445       }
446     }
447
448     return true;
449   }
450
451   /**
452    * Copies a directory with its contents. See {@link DirectoryCopier} for
453    * details on options.
454    */

455   public static boolean copyDirectory(File dir, File newDir,
456       boolean overwriteDir, boolean overwriteFiles, boolean setLastModified) {
457     DirectoryCopier dc = new DirectoryCopier(dir, newDir, overwriteDir,
458         overwriteFiles, setLastModified);
459     dc.process();
460     return dc.getResult();
461   }
462
463   /**
464    * Reads an <code>InputStream</code> and copy all data to an
465    * <code>OutputStream</code>.
466    *
467    * @param in the Input Stream
468    * @param out the Output Stream
469    * @param closeOut if true, out is closed when the copy has finished
470    *
471    * @throws IOException if an I/O error occurs.
472    */

473   public static void copyStream(InputStream in, OutputStream out,
474                                 boolean closeOut) throws IOException {
475     byte b[] = new byte[BUFFER_SIZE];
476     int n;
477
478     try {
479       while ((n = in.read(b)) != -1) {
480         out.write(b, 0, n);
481       }
482     } finally {
483       try {
484         in.close();
485       } finally {
486         if (closeOut) {
487           out.close();
488         }
489       }
490     }
491   }
492
493   /**
494    * Copies the Reader to the Writer until there are no data left.
495    *
496    * @param reader the Reader
497    * @param writer the Writer
498    * @param closeWriter if true, closes the Writer at the end
499    *
500    * @throws IOException if an I/O error occurs
501    */

502   public static void copyReaderToWriter(Reader reader, Writer writer,
503       boolean closeWriter) throws IOException {
504     char c[] = new char[BUFFER_SIZE];
505     int n;
506
507     while ((n = reader.read(c)) != -1) {
508       writer.write(c, 0, n);
509     }
510
511     reader.close();
512     writer.flush();
513
514     if (closeWriter) {
515       writer.close();
516     }
517   }
518
519   /**
520    * Writes the whole String to the File using a buffered FileWriter.
521    *
522    * @param file the destination file where to write
523    * @param s the String to be written.
524    *
525    * @throws IOException If an I/O error occurs
526    */

527   public static void writeFully(File file, String JavaDoc s) throws IOException {
528     Writer writer = new BufferedWriter(new FileWriter(file));
529     writer.write(s);
530     writer.close();
531   }
532
533   /**
534    * Writes the whole byte array to the File using a FileOutputStream.
535    *
536    * @param file the destination file where to write
537    * @param b byte array to be written
538    *
539    * @throws IOException If an I/O error occurs
540    */

541   public static void writeFully(File file, byte[] b) throws IOException {
542     FileOutputStream fos = new FileOutputStream(file);
543     fos.write(b);
544     fos.close();
545   }
546
547   /**
548    * Reads a file and puts all data into a String.
549    *
550    * @param file the file to read from.
551    *
552    * @return the content of the file as a string.
553    *
554    * @throws IOException If an I/O error occurs
555    */

556   public static String JavaDoc readFully(File file) throws IOException {
557     Reader reader = new BufferedReader(new FileReader(file));
558     String JavaDoc s = readFully(reader);
559     reader.close();
560     return s;
561   }
562
563   /**
564    * Reads from a Reader and puts all available data into a String.
565    *
566    * @param reader the reader to read from.
567    *
568    * @return the content of the <code>reader</code> as a string.
569    *
570    * @throws IOException If an I/O error occurs
571    */

572   public static String JavaDoc readFully(Reader reader) throws IOException {
573     CharArrayWriter caw = new CharArrayWriter();
574     char[] cbuf = new char[BUFFER_SIZE];
575     int n;
576
577     while ((n = reader.read(cbuf)) != -1) {
578       caw.write(cbuf, 0, n);
579     }
580
581     return caw.toString();
582   }
583
584     /**
585      * Reads all characters from a reader.
586      *
587      * @param reader the reader to read from
588      *
589      * @return an array of <code>Char</code>'s
590      *
591      * @throws IOException If an I/O error occurs
592      */

593   public static char[] readAllChars(Reader reader) throws IOException {
594     CharArrayWriter caw = new CharArrayWriter();
595     char[] cbuf = new char[BUFFER_SIZE];
596     int n;
597
598     while ((n = reader.read(cbuf)) != -1) {
599       caw.write(cbuf, 0, n);
600     }
601
602     return caw.toCharArray();
603   }
604
605   /**
606    * Reads from an InputStream and puts all available data into an array of
607    * bytes.
608    *
609    * @param in the Input Stream to read from
610    *
611    * @return an array of bytes
612    *
613    * @throws IOException If an I/O error occurs
614    */

615   public static byte[] readFully(InputStream in) throws IOException {
616     ByteArrayOutputStream baos = new ByteArrayOutputStream();
617     byte[] buf = new byte[BUFFER_SIZE];
618     int n;
619
620     while ((n = in.read(buf)) != -1) {
621       baos.write(buf, 0, n);
622     }
623
624     return baos.toByteArray();
625   }
626
627   /**
628    * Reads a file and put all lines into an array of Strings.
629    *
630    * @param file the file to read from
631    *
632    * @return an array of Strings representing the lines of text from the file.
633    *
634    * @throws IOException If an I/O error occurs
635    */

636   public static String JavaDoc[] readAllLines(File file)
637       throws FileNotFoundException, IOException {
638     BufferedReader reader = new BufferedReader(new FileReader(file));
639     List list = new ArrayList();
640     String JavaDoc line;
641
642     while ((line = reader.readLine()) != null) {
643       list.add(line);
644     }
645
646     reader.close();
647     return (String JavaDoc[]) list.toArray(new String JavaDoc[list.size()]);
648   }
649
650   /**
651    * A quick and dirty method to unzip an archive into a directory.
652    *
653    * @param zip source archive to be processed
654    * @param dir destination directory where to unzip the archive.
655    *
656    * @throws IOException If an I/O error occurs
657    */

658   public static void unzip(File zip, File dir) throws IOException {
659     dir.mkdirs();
660     InputStream in = new BufferedInputStream(new FileInputStream(zip));
661     ZipInputStream zin = new ZipInputStream(in);
662     ZipEntry e;
663
664     while ((e = zin.getNextEntry()) != null) {
665       File f = new File(dir, e.getName());
666
667       if (e.isDirectory()) {
668         f.mkdirs();
669       } else {
670         f.getParentFile().mkdirs();
671         FileOutputStream out = new FileOutputStream(f);
672         byte[] b = new byte[BUFFER_SIZE];
673         int len;
674
675         while ((len = zin.read(b)) != -1) {
676           out.write(b, 0, len);
677         }
678
679         out.close();
680       }
681     }
682
683     zin.close();
684   }
685
686   /**
687    * Creates a random string of a given length. Characters are picked from
688    * {@link #VALID_CHARS}.
689    */

690   public static String JavaDoc generateRandomString(int len) {
691     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(len);
692
693     for (int i = 0; i < len; i++) {
694       sb.append(Utils.VALID_CHARS.charAt((int) (Math.random() *
695                                                 Utils.VALID_CHARS.length())));
696     }
697
698     return sb.toString();
699   }
700
701   /**
702    * Creates a <code>String</code> containing the string representations of the
703    * objects in the array, separated by <code>sep</code>. It can be seen as
704    * somewhat opposite of <code>java.util.StringTokenizer</code>.
705    */

706   public static String JavaDoc generateList(Object JavaDoc[] list, String JavaDoc sep) {
707     if (list == null) {
708       return null;
709     }
710
711     if (list.length == 0) {
712       return "";
713     }
714
715     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
716     sb.append(list[0]);
717
718     for (int i = 1; i < list.length; i++) {
719       sb.append(sep).append(list[i]);
720     }
721
722     return sb.toString();
723   }
724
725   /**
726    * Appends all properties into a single string using the given separator.
727    */

728   public static String JavaDoc listProperties(Properties p, String JavaDoc sep) {
729     if (p == null) {
730       return null;
731     }
732
733     Enumeration names = p.propertyNames();
734     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
735
736     if (names.hasMoreElements()) {
737       String JavaDoc name = (String JavaDoc) names.nextElement();
738       sb.append(name).append('=').append(p.getProperty(name));
739     }
740
741     while (names.hasMoreElements()) {
742       String JavaDoc name = (String JavaDoc) names.nextElement();
743       sb.append(sep).append(name).append('=').append(p.getProperty(name));
744     }
745
746     return sb.toString();
747   }
748
749   /**
750    * Creates a <code>String</code> containing the string representations of the
751    * objects in the collection, separated by <code>sep</code>. It can be seen as
752    * somewhat opposite of <code>java.util.StringTokenizer</code>.
753    */

754   public static String JavaDoc generateList(Collection c, String JavaDoc sep) {
755     if (c == null) {
756       return null;
757     }
758
759     if (c.size() == 0) {
760       return "";
761     }
762
763     Iterator iter = c.iterator();
764     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
765
766     if (iter.hasNext()) {
767       sb.append(iter.next().toString());
768     }
769
770     while (iter.hasNext()) {
771       sb.append(sep).append(iter.next());
772     }
773
774     return sb.toString();
775   }
776
777   /**
778    * Creates a <code>String</code> containing the string representations of the
779    * objects in the enumeration, separated by <code>sep</code>. It can be seen as
780    * somewhat opposite of <code>java.util.StringTokenizer</code>.
781    */

782   public static String JavaDoc generateList(Enumeration e, String JavaDoc sep) {
783     if (e == null) {
784       return null;
785     }
786
787     if (!e.hasMoreElements()) {
788       return "";
789     }
790
791     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
792
793     if (e.hasMoreElements()) {
794       sb.append(e.nextElement().toString());
795     }
796
797     while (e.hasMoreElements()) {
798       sb.append(sep).append(e.nextElement());
799     }
800
801     return sb.toString();
802   }
803
804   /**
805    * Creates a <code>String</code> containing the string representations of the
806    * numbers in the array, separated by <code>sep</code>. It can be seen as
807    * somewhat opposite of <code>java.util.StringTokenizer</code>.
808    */

809   public static String JavaDoc generateList(int[] list, String JavaDoc sep) {
810     if (list == null) {
811       return null;
812     }
813
814     if (list.length == 0) {
815       return "";
816     }
817
818     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(Integer.toString(list[0]));
819
820     for (int i = 1; i < list.length; i++) {
821       sb.append(sep).append(list[i]);
822     }
823
824     return sb.toString();
825   }
826
827   /**
828    * @return the sum of the numbers stored in the array.
829    */

830   public static int sum(int[] ints) {
831     int s = 0;
832
833     for (int i = 0; i < ints.length; i++) {
834       s += ints[i];
835     }
836
837     return s;
838   }
839
840   /**
841    * @return a file name similar to <code>fileName</code>, but different from
842    * the names of the files in the directory.
843    */

844   public static String JavaDoc generateUniqueName(String JavaDoc fileName, File directory) {
845     if (directory.isDirectory()) {
846       return generateUniqueName(fileName, directory.list());
847     }
848
849     return null;
850   }
851
852   /**
853    * Returns a file name similar to <code>fileName</code>, but different from
854    * the strings in the <code>files</code> array.
855    *
856    * @return a file name in the 8+3 DOS format
857    */

858   public static String JavaDoc generateUniqueDosName(String JavaDoc fileName, String JavaDoc[] files) {
859     fileName = fileName.toLowerCase();
860     String JavaDoc ext = "";
861     int idx = fileName.lastIndexOf('.');
862
863     if (idx != -1) {
864       ext = fileName.substring(idx);
865
866       if (ext.length() > 4) {
867         ext = ext.substring(0, 4);
868       }
869
870       fileName = fileName.substring(0, idx);
871     }
872
873     String JavaDoc name = "";
874
875     for (int i = 0; i < fileName.length(); i++) {
876       char c = fileName.charAt(i);
877
878       if (Character.isLetterOrDigit(c)) {
879         name += c;
880
881         if (name.length() == 8) {
882           break;
883         }
884       }
885     }
886
887     if (name.length() == 0) {
888       name = "file";
889     }
890
891     if (searchString(files, name + ext, true) < 0) {
892       return name + ext;
893     }
894
895     int limit = 1;
896
897     for (int i = 1; i <= 8; i++) {
898       int first = limit;
899       limit *= 10;
900
901       String JavaDoc base = (name.length() <= 7 - i) ? name : name.substring(0, 7 - i);
902       base += "_";
903
904       for (int j = first; j < limit; j++) {
905         String JavaDoc temp = base + j + ext;
906
907         if (searchString(files, temp, true) < 0) {
908           return temp;
909         }
910       }
911     }
912
913     return null;
914   }
915
916   /**
917    * Generates a unique (but similar to the original) file name, based on an exclusion list.<p/>
918    * E.g. <code>product.html</code> would be <code>product1.html</code> if the
919    * exclusion list already contains <code>product.html</code>
920    *
921    * @param fileName the source file name
922    * @param files exlusion list of file names.
923    *
924    * @return a file name similar to <code>fileName</code>, but different from
925    * the strings in the <code>files</code> array.
926    */

927   public static String JavaDoc generateUniqueName(String JavaDoc fileName, String JavaDoc[] files) {
928     fileName = fileName.toLowerCase();
929     String JavaDoc ext = "";
930     int idx = fileName.lastIndexOf('.');
931
932     if (idx != -1) {
933       ext = fileName.substring(idx);
934       fileName = fileName.substring(0, idx);
935     }
936
937     if (searchString(files, fileName + ext, true) == -1) {
938       return fileName + ext;
939     }
940
941     int d = 0;
942
943     for (int i = fileName.length() - 1; i >= 0; i--) {
944       if (!Character.isDigit(fileName.charAt(i))) {
945         d = i + 1;
946         break;
947       }
948     }
949
950     int number = parseInt(fileName.substring(d), 1);
951     fileName = fileName.substring(0, d);
952     String JavaDoc temp;
953
954     do {
955       temp = fileName + ++number + ext;
956     } while (searchString(files, temp, true) != -1);
957
958     return temp;
959   }
960
961   /**
962    * Parses the string argument as an integer, but without returning
963    * exception. If that would be the case, the default value provided is
964    * returned instead.
965    *
966    * @param s the string to be converted to <code>int<
967    * @param def default value in case the string is not parasble.
968    *
969    * @return a <code>int</code> representation of a String, or a default value if the string
970    * can't be parsed.
971    */

972   public static int parseInt(String JavaDoc s, int def) {
973     try {
974       def = Integer.parseInt(s);
975     } catch (Exception JavaDoc ex) {}
976
977     return def;
978   }
979
980   /**
981    * Parses the string argument as an long, but without returning
982    * exception. If that would be the case, the default value provided is
983    * returned instead.
984    *
985    * @param s the string to be converted to <code>long</code>
986    * @param def default value in case the string is not parasble.
987    *
988    * @return a long representation of a String, or a default value if the string
989    * can't be parsed.
990    */

991   public static long parseLong(String JavaDoc s, long def) {
992     try {
993       def = Long.parseLong(s);
994     } catch (Exception JavaDoc ex) {}
995
996     return def;
997   }
998
999   /**
1000   * Returns the tokens of a string. The default delimiter characters of
1001   * <code>java.util.StringTokenizer</code> are used.
1002   * @see #tokenize(String, String)
1003   *
1004   * @param s the string to be tokenized
1005   *
1006   * @return an array of tokens
1007   */

1008  public static String JavaDoc[] tokenize(String JavaDoc s) {
1009    return tokenize(s, null);
1010  }
1011
1012  /**
1013   * Tokenizes a string with a given delimiter.
1014   *
1015   * @param s the String to tokenized
1016   * @param delim the delimiter
1017   *
1018   * @return an array of okens of a string using the specified delimiter characters.
1019   */

1020  public static String JavaDoc[] tokenize(String JavaDoc s, String JavaDoc delim) {
1021    if (s == null) {
1022      return null;
1023    }
1024
1025    StringTokenizer st;
1026
1027    if (isNullOrEmpty(delim)) {
1028      st = new StringTokenizer(s);
1029    } else {
1030      st = new StringTokenizer(s, delim);
1031    }
1032
1033    String JavaDoc[] res = new String JavaDoc[st.countTokens()];
1034
1035    for (int i = 0; i < res.length; i++) {
1036      res[i] = st.nextToken();
1037    }
1038
1039    return res;
1040  }
1041
1042  /**
1043   * Searches a string in an array of strings.
1044   *
1045   * @param array the array of strings
1046   * @param s the string to be searched
1047   * @param ignoreCase used to ask for a case insensitive search
1048   *
1049   * @return the index of the first occurrence, or -1 if not found
1050   */

1051  public static int searchString(String JavaDoc[] array, String JavaDoc s, boolean ignoreCase) {
1052    if (array == null || array.length == 0 || s == null) {
1053      return -1;
1054    }
1055
1056    if (ignoreCase) {
1057      for (int i = 0; i < array.length; i++) {
1058        if (s.equalsIgnoreCase(array[i])) {
1059          return i;
1060        }
1061      }
1062    } else {
1063      for (int i = 0; i < array.length; i++) {
1064        if (s.equals(array[i])) {
1065          return i;
1066        }
1067      }
1068    }
1069
1070    return -1;
1071  }
1072
1073  /**
1074   * Tries to verify(validate) an e-mail address.
1075   *
1076   * @param email the Email address to verify
1077   *
1078   * @return <code>true</code> if it's a valid email, <code>false</code>otherwise.
1079   */

1080  public static boolean checkAddress(String JavaDoc email) {
1081    if (isNullOrEmpty(email) || email.indexOf(' ') >= 0) {
1082      return false;
1083    }
1084
1085    int dot = email.lastIndexOf('.');
1086    int at = email.lastIndexOf('@');
1087    return !(dot < 0 || at < 0 || at > dot);
1088  }
1089
1090  /**
1091   * Generates a random integer between 0 (included) and max (excluded).
1092   *
1093   * @param max the maximum value of the random generation domain.
1094   *
1095   * @return the randomly generated <code>int</code>
1096   */

1097  public static int getRandomInt(int max) {
1098    return (int) (Math.random() * max);
1099  }
1100
1101  /**
1102   * Picks a random element from the array and returns it.
1103   *
1104   * @param array the array of possible elements
1105   *
1106   * @return the randomly selected element from the given array.
1107   */

1108  public static Object JavaDoc getRandomElement(Object JavaDoc[] array) {
1109    if (array == null || array.length == 0) {
1110      return null;
1111    }
1112
1113    return array[getRandomInt(array.length)];
1114  }
1115
1116  /**
1117   * Returns the decimal part of a float, regardless of its sign.
1118   *
1119   * @param f the <code>float</code> to be processed.
1120   *
1121   * @return the decimal part of the given float.
1122   */

1123  public static float decimalPart(float f) {
1124    return f - (int) f;
1125  }
1126
1127  /**
1128   * Returns the decimal part of a double, regardless of its sign.
1129   *
1130   * @param d the <code>double</code> to be processed.
1131   *
1132   * @return the decimal part of the given double.
1133   */

1134  public static double decimalPart(double d) {
1135    return d - (long) d;
1136  }
1137
1138  /**
1139   * Returns the sign of an integer.
1140   *
1141   * @param n the <code>int</code> number who's sign is checked.
1142   *
1143   * @return -1 if negative, 0 if zero, 1 if positive
1144   */

1145  public static int sign(int n) {
1146    if (n < 0) {
1147      return -1;
1148    }
1149    if (n > 0) {
1150      return 1;
1151    }
1152    return 0;
1153  }
1154
1155  /**
1156   * Returns the closest number to n that does not exceed the interval between
1157   * min and max.
1158   *
1159   * @param min the minimum of the interval
1160   * @param max the maximum of the interval
1161   * @param n the number
1162   *
1163   * @return the closest number to <code>n</code> in the <code>[min...max]</code> interval
1164   */

1165  public static int constrain(int min, int max, int n) {
1166    if (n < min) {
1167      return min;
1168    }
1169    if (n > max) {
1170      return max;
1171    }
1172    return n;
1173  }
1174
1175  /**
1176   * Adds a string at the end of another string, but only if the latter doesn't
1177   * end with the former.
1178   *
1179   * @param base the source String
1180   * @param what suffix to append
1181   *
1182   * @return the processed String
1183
1184   */

1185  public static String JavaDoc addAtEnd(String JavaDoc base, String JavaDoc what) {
1186    if (base == null) {
1187      base = what;
1188    } else if (!base.endsWith(what)) {
1189      base += what;
1190    }
1191
1192    return base;
1193  }
1194
1195  /**
1196   * Removes a string at the end of another string, if latter ends with the
1197   * former.
1198   *
1199   * @param base the source String
1200   * @param what suffix to remove
1201   *
1202   * @return the processed String
1203   */

1204  public static String JavaDoc removeAtEnd(String JavaDoc base, String JavaDoc what) {
1205    if (base != null && base.endsWith(what)) {
1206      base = base.substring(0, base.length() - what.length());
1207    }
1208
1209    return base;
1210  }
1211
1212  /**
1213   * Adds a string at the beginning of another string, but only if the latter
1214   * doesn't begin with the former
1215   *
1216   * @param base the source String
1217   * @param what the prefix to add
1218   *
1219   * @return the processed String
1220   */

1221  public static String JavaDoc addAtBeginning(String JavaDoc base, String JavaDoc what) {
1222    if (base == null) {
1223      base = what;
1224    } else if (!base.startsWith(what)) {
1225      base = what + base;
1226    }
1227
1228    return base;
1229  }
1230
1231  /**
1232   * Removes a string at the beginning of another string, if the latter
1233   * begin with the former.
1234   *
1235   * @param base the source String
1236   * @param what prefix to remove
1237   *
1238   * @return the processed String
1239   */

1240  public static String JavaDoc removeAtBeginning(String JavaDoc base, String JavaDoc what) {
1241    if (base != null && base.startsWith(what)) {
1242      base = base.substring(what.length());
1243    }
1244
1245    return base;
1246  }
1247
1248  /**
1249   * Return a relative path from folder to file using the separator provided.
1250   * In general, using {@link Path} provides better management of relative
1251   * paths.
1252   *
1253   * @param folder the Folder
1254   * @param file the File
1255   * @param separator the File Separator
1256   *
1257   * @return the relative combined path
1258   */

1259  public static String JavaDoc getRelativePath(File folder, File file, String JavaDoc separator) {
1260    return getRelativePath(getFilePath(folder), getFilePath(file), separator);
1261  }
1262
1263  /**
1264   * Returns a relative path from folder to file using the separator provided.
1265   * In general, using {@link Path} provides better management of relative
1266   * paths.
1267   *
1268   * @param folder the Folder path
1269   * @param file the File path
1270   * @param separator the File separator
1271   *
1272   * @return the relative combined path
1273   */

1274  public static String JavaDoc getRelativePath(String JavaDoc folder, String JavaDoc file,
1275                                       String JavaDoc separator) {
1276    String JavaDoc[] a0 = Utils.tokenize(folder, "/\\");
1277    String JavaDoc[] a1 = Utils.tokenize(file, "/\\");
1278
1279    int i0 = 0;
1280    int i1 = 0;
1281
1282    String JavaDoc result = "";
1283
1284    while (i0 < a0.length && i1 < a1.length && a0[i0].equals(a1[i1])) {
1285      i0++;
1286      i1++;
1287    }
1288
1289    while (i0++ < a0.length) {
1290      result += ".." + separator;
1291    }
1292
1293    while (i1 < a1.length - 1) {
1294      result += a1[i1++] + separator;
1295    }
1296
1297    if (i1 == a1.length - 1) {
1298      result += a1[i1];
1299    }
1300
1301    return result;
1302  }
1303
1304  /**
1305   * Adds the file path to the folder path. As an example,
1306   * <code>getCombinedPath("home/user/docs", "../myfile.txt", "/")</code>
1307   * returns "home/user/myfile.txt".
1308   * In general, using {@link Path} provides better management of paths.
1309   *
1310   * @param folder the Folder path
1311   * @param file the File path
1312   * @param separator the File separator
1313   *
1314   * @return the resulting combined path
1315   */

1316  public static String JavaDoc getCombinedPath(String JavaDoc folder, String JavaDoc file,
1317                                       String JavaDoc separator) {
1318    String JavaDoc[] a0 = Utils.tokenize(folder, "/\\");
1319    String JavaDoc[] a1 = Utils.tokenize(file, "/\\");
1320
1321    int i0 = a0.length;
1322    int i1 = 0;
1323
1324    while (i1 < a1.length && a1[i1].equals("..")) {
1325      i0--;
1326      i1++;
1327    }
1328
1329    if (i0 < 0) {
1330      throw new IllegalArgumentException JavaDoc("Not enough levels");
1331    }
1332
1333    String JavaDoc result = null;
1334
1335    for (int i = 0; i < i0; i++) {
1336      if (!a0[i].equals(".")) {
1337        result = (result == null) ? a0[i] : result + separator + a0[i];
1338      }
1339    }
1340
1341    for (int i = i1; i < a1.length; i++) {
1342      if (!a1[i].equals(".")) {
1343        result = (result == null) ? a1[i] : result + separator + a1[i];
1344      }
1345    }
1346
1347    return noNull(result);
1348  }
1349
1350  /**
1351   * Returns the full path of the file without having to catch exceptions,
1352   * using {@link java.io.File#getCanonicalPath()} or {@link java.io.File#getAbsoluteFile()}
1353   *
1354   * @param f the File to be processed
1355   *
1356   * @return <code>f.getCanonicalPath()</code>, or
1357   * <code>f.getAbsolutePath()</code> if an exception is thrown
1358   */

1359  public static String JavaDoc getFilePath(File f) {
1360    try {
1361      return f.getCanonicalPath();
1362    } catch (IOException ex) {}
1363
1364    return f.getAbsolutePath();
1365  }
1366
1367  public static String JavaDoc getExtension(File file, boolean includeDot) {
1368    return getExtension(file.getName(), includeDot);
1369  }
1370
1371  public static String JavaDoc getExtension(Path path, boolean includeDot) {
1372    return getExtension(path.getLastElement(), includeDot);
1373  }
1374
1375  /**
1376   * Returns the extension of the given file name, with or without the dot.
1377   *
1378   * @param fileName the name of the File to be processed
1379   * @param includeDot if true, the dot is returned with the extension
1380   *
1381   * @return the extension
1382   */

1383  public static String JavaDoc getExtension(String JavaDoc fileName, boolean includeDot) {
1384    if (fileName == null) {
1385      return null;
1386    }
1387
1388    int dot = fileName.lastIndexOf('.');
1389    return (dot == -1) ? "" :
1390      fileName.substring(includeDot ? dot : dot + 1).toLowerCase();
1391  }
1392
1393  /**
1394   * Removes the extension from a file name. The dot is removed too.
1395   *
1396   * @param o the file Object <i>(as {@link java.io.File} or {@link org.meshcms.util.Path} )</i>
1397   *
1398   * @return the name without extension
1399   */

1400  public static String JavaDoc removeExtension(Object JavaDoc o) {
1401    String JavaDoc fileName = null;
1402
1403    if (o instanceof File) {
1404      fileName = ((File) o).getName();
1405    } else if (o instanceof Path) {
1406      fileName = ((Path) o).getLastElement();
1407    } else if (o != null) {
1408      fileName = o.toString();
1409    }
1410
1411    if (fileName == null) {
1412      return null;
1413    }
1414
1415    int dot = fileName.lastIndexOf('.');
1416    return (dot == -1) ? fileName : fileName.substring(0, dot);
1417  }
1418
1419  /**
1420   * Returns the common part at the beginning of two strings.
1421   *
1422   * @param s1 the String 1
1423   * @param s2 the String 2
1424   *
1425   * @return null if at least one of the strings is null, otherwise the common
1426   * part is returned, that can be empty
1427   */

1428  public static String JavaDoc getCommonPart(String JavaDoc s1, String JavaDoc s2) {
1429    if (s1 == null || s2 == null) {
1430      return null;
1431    }
1432
1433    int len = Math.min(s1.length(), s2.length());
1434
1435    for (int i = 0; i < len; i++) {
1436      if (s1.charAt(i) != s2.charAt(i)) {
1437        return s1.substring(0, i);
1438      }
1439    }
1440
1441    return s1.length() < s2.length() ? s1 : s2;
1442  }
1443
1444  /**
1445   * Converts the underscores to spaces and, if requested, applies the title case
1446   * to a string.
1447   *
1448   * @param s the String to be beautified
1449   * @param titleCase flag if to title case. See {@link Character#toTitleCase(char)}
1450   *
1451   * @return the converted String.
1452   */

1453  public static String JavaDoc beautify(String JavaDoc s, boolean titleCase) {
1454    StringBuffer JavaDoc sb = new StringBuffer JavaDoc(s.length());
1455    boolean nextUpper = true;
1456
1457    for (int i = 0; i < s.length(); i++) {
1458      char c = s.charAt(i);
1459
1460      if (c == '_') {
1461        c = ' ';
1462      }
1463
1464      if (c == ' ') {
1465        nextUpper = true;
1466      } else {
1467        if (titleCase && nextUpper) {
1468          c = Character.toTitleCase(c);
1469          nextUpper = false;
1470        }
1471      }
1472
1473      sb.append(c);
1474    }
1475
1476    return sb.toString();
1477  }
1478
1479  /**
1480   * A "brute force" method to delete a file that might be in use by
1481   * another thread or application. This method simply tries again and again
1482   * for 20 seconds then gives up.
1483   *
1484   * @param file the file to be deleted
1485   *
1486   * @return <code>true</code> if the delete operation succeded, and <code>false<code> otherwise.
1487   */

1488  public static boolean forceDelete(File file) {
1489    if (!file.exists()) {
1490      return true;
1491    }
1492
1493    /* do not force on directories */
1494    if (file.isDirectory()) {
1495      return file.delete();
1496    }
1497
1498    for (int i = 1; i < 20; i++) {
1499      if (file.delete()) {
1500        return true;
1501      }
1502
1503      try {
1504        Thread.sleep(i * 100L);
1505      } catch (InterruptedException JavaDoc ex) {}
1506    }
1507
1508    return false;
1509  }
1510
1511  /**
1512   * A "brute force" method to move or rename a file that might be in use by
1513   * another thread or application. This method simply tries again and again
1514   * for 20 seconds then gives up.
1515   *
1516   * @param oldFile the old(source) File
1517   * @param newFile the new(destination) File
1518   * @param overwrite if true, tries to delete newFile before renaming oldFile
1519   *
1520   * @return the result of the operation
1521   */

1522  public static boolean forceRenameTo(File oldFile, File newFile,
1523      boolean overwrite) {
1524    if (newFile.exists()) {
1525      if (overwrite) {
1526        if (!forceDelete(newFile)) {
1527          return false;
1528        }
1529      } else {
1530        return false;
1531      }
1532    }
1533
1534    for (int i = 0; i < 20; i++) {
1535      if (oldFile.renameTo(newFile)) {
1536        return true;
1537      }
1538
1539      try {
1540        Thread.sleep(i * 100L);
1541      } catch (InterruptedException JavaDoc ex) {}
1542    }
1543
1544    return false;
1545  }
1546
1547  /**
1548   * Returns a nicer representation of the length of the file. The file length
1549   * is returned as bytes, kilobytes or megabytes, with the unit attached.
1550   * @see #formatFileLength(long)
1551   *
1552   * @param file the File
1553   *
1554   * @return the nicely formatted length of this file
1555   */

1556  public static String JavaDoc formatFileLength(File file) {
1557    return formatFileLength(file.length());
1558  }
1559
1560  /**
1561   * Returns a nicer representation of the number as a file length. The number
1562   * is returned as bytes, kilobytes or megabytes, with the unit attached.
1563   *
1564   * @param length the number<i>(file length or whatever)</i> to be formatted.
1565   *
1566   * @return the nicely formatted number as a String.
1567   */

1568  public static String JavaDoc formatFileLength(long length) {
1569    DecimalFormat format = new DecimalFormat("###0.##");
1570    double num = length;
1571    String JavaDoc unit;
1572
1573    if (length < KBYTE) {
1574      unit = "B";
1575    } else if (length < MBYTE) {
1576      num /= KBYTE;
1577      unit = "KB";
1578    } else if (length < GBYTE) {
1579      num /= MBYTE;
1580      unit = "MB";
1581    } else {
1582      num /= GBYTE;
1583      unit = "GB";
1584    }
1585
1586    return format.format(num) + unit;
1587  }
1588
1589  /**
1590   * Encodes a path as a URL, using UTF-8.
1591   * @see #encodeURL(String)
1592   *
1593   * @param path the Path to be encoded
1594   *
1595   * @return the encoded URL as String, or the original URL as String if an exception occures.
1596   */

1597  public static String JavaDoc encodeURL(Path path) {
1598    return encodeURL(path.toString());
1599  }
1600
1601  /**
1602   * Encodes a URL using UTF-8 (by "ignoring" exceptions).
1603   * @see java.net.URLEncoder#encode(String, String)
1604   *
1605   * @param url the URL to be encoded
1606   *
1607   * @return the encoded URL, or the original URL if an exception occures.
1608   */

1609  public static String JavaDoc encodeURL(String JavaDoc url) {
1610    try {
1611      url = URLEncoder.encode(url, "UTF-8");
1612    } catch (UnsupportedEncodingException ex) {
1613      ex.printStackTrace();
1614    }
1615
1616    return url;
1617  }
1618
1619  /**
1620   * Decodes a URL using UTF-8 (by "ignoring" exceptions).
1621   * @see java.net.URLDecoder#decode(String, String)
1622   *
1623   * @param url the URL to be decoded
1624   *
1625   * @return the decoded URL, or the original URL if an exception occures.
1626   */

1627  public static String JavaDoc decodeURL(String JavaDoc url) {
1628    try {
1629      url = URLDecoder.decode(url, "UTF-8");
1630    } catch (UnsupportedEncodingException ex) {
1631      ex.printStackTrace();
1632    }
1633
1634    return url;
1635  }
1636
1637  /**
1638   * Returns the java.util.Locale object for a given locale name (e.g. en_US).
1639   *
1640   * @param localeName the locale name to be searched.
1641   *
1642   * @return the found {@link java.util.Locale} object for the given locale name, or null
1643   * if not found.
1644   */

1645  public static Locale getLocale(String JavaDoc localeName) {
1646    if (!isNullOrEmpty(localeName)) {
1647      Locale[] locales = Locale.getAvailableLocales();
1648
1649      for (int i = 0; i < locales.length; i++) {
1650        if (localeName.equals(locales[i].toString())) {
1651          return locales[i];
1652        }
1653      }
1654    }
1655
1656    return null;
1657  }
1658
1659  /**
1660   * Returns all the locales that have no country and no variant.
1661   *
1662   * @return an array with all the locales without country and variant.
1663   */

1664  public static Locale[] getLanguageLocales() {
1665    Locale[] all = Locale.getAvailableLocales();
1666    List list = new ArrayList();
1667
1668    for (int i = 0; i < all.length; i++) {
1669      if (all[i].toString().length() == 2) {
1670        list.add(all[i]);
1671      }
1672    }
1673
1674    return (Locale[]) list.toArray(new Locale[list.size()]);
1675  }
1676
1677  public static String JavaDoc toTitleCase(String JavaDoc s) {
1678    char[] chars = s.trim().toLowerCase().toCharArray();
1679    boolean found = false;
1680
1681    for (int i = 0; i < chars.length; i++) {
1682      if (!found && Character.isLetter(chars[i])) {
1683        chars[i] = Character.toUpperCase(chars[i]);
1684        found = true;
1685      } else if (Character.isWhitespace(chars[i])) {
1686        found = false;
1687      }
1688    }
1689
1690    return String.valueOf(chars);
1691  }
1692  
1693  public static String JavaDoc[] commonPart(String JavaDoc[] sa1, String JavaDoc[] sa2, boolean fromEnd) {
1694    int len1 = sa1.length;
1695    int len2 = sa2.length;
1696    int cnt = Math.min(len1, len2);
1697    
1698    if (fromEnd) {
1699      for (int i = 1; i <= cnt; i++) {
1700        if (!sa1[len1 - i].equals(sa2[len2 - i])) {
1701          cnt = i - 1;
1702        }
1703      }
1704    } else {
1705      for (int i = 0; i < cnt; i++) {
1706        if (!sa1[i].equals(sa2[i])) {
1707          cnt = i;
1708        }
1709      }
1710    }
1711    
1712    String JavaDoc[] result = new String JavaDoc[cnt];
1713    System.arraycopy(sa1, fromEnd ? len1 - cnt : 0, result, 0, cnt);
1714    return result;
1715  }
1716}
1717
Popular Tags