Delete association of htmledit.gif for the PHPUnitEditor
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / util / Util.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.util;
12
13 import java.io.BufferedInputStream;
14 import java.io.DataInput;
15 import java.io.EOFException;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.io.PrintStream;
20 import java.io.UTFDataFormatException;
21 import java.util.Locale;
22 import java.util.MissingResourceException;
23 import java.util.ResourceBundle;
24 import java.util.StringTokenizer;
25
26 import net.sourceforge.phpdt.core.IJavaElement;
27 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
28 import net.sourceforge.phpdt.core.IPackageFragment;
29 import net.sourceforge.phpdt.core.JavaCore;
30 import net.sourceforge.phpdt.core.JavaModelException;
31 import net.sourceforge.phpdt.core.Signature;
32 import net.sourceforge.phpdt.core.compiler.CharOperation;
33 import net.sourceforge.phpdt.internal.core.Assert;
34 import net.sourceforge.phpdt.internal.core.PackageFragmentRoot;
35 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
36 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeReference;
37
38 import org.eclipse.core.resources.IFile;
39 import org.eclipse.core.resources.IFolder;
40 import org.eclipse.core.resources.IResource;
41 import org.eclipse.core.runtime.CoreException;
42 import org.eclipse.core.runtime.IPath;
43 import org.eclipse.core.runtime.IStatus;
44 import org.eclipse.core.runtime.Status;
45 import org.eclipse.jface.text.BadLocationException;
46 import org.eclipse.text.edits.MalformedTreeException;
47 import org.eclipse.text.edits.TextEdit;
48
49 /**
50  * Provides convenient utility methods to other types in this package.
51  */
52 public class Util {
53
54   public interface Comparable {
55     /**
56      * Returns 0 if this and c are equal, >0 if this is greater than c, or <0 if this is less than c.
57      */
58     int compareTo(Comparable c);
59   }
60
61   public interface Comparer {
62     /**
63      * Returns 0 if a and b are equal, >0 if a is greater than b, or <0 if a is less than b.
64      */
65     int compare(Object a, Object b);
66   }
67
68   private static final String ARGUMENTS_DELIMITER = "#"; //$NON-NLS-1$
69
70   /* Bundle containing messages */
71   protected static ResourceBundle bundle;
72
73   private final static String bundleName = "net.sourceforge.phpdt.internal.core.util.messages"; //$NON-NLS-1$
74
75   private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$
76
77   private static final String EMPTY_ARGUMENT = "   "; //$NON-NLS-1$
78
79   public static final String[] fgEmptyStringArray = new String[0];
80
81   private final static char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$
82
83   static {
84     relocalize();
85   }
86
87   private Util() {
88     // cannot be instantiated
89   }
90
91   /**
92    * Lookup the message with the given ID in this catalog
93    */
94   public static String bind(String id) {
95     return bind(id, (String[]) null);
96   }
97
98   /**
99    * Lookup the message with the given ID in this catalog and bind its substitution locations with the given string.
100    */
101   public static String bind(String id, String binding) {
102     return bind(id, new String[] { binding });
103   }
104
105   /**
106    * Lookup the message with the given ID in this catalog and bind its substitution locations with the given strings.
107    */
108   public static String bind(String id, String binding1, String binding2) {
109     return bind(id, new String[] { binding1, binding2 });
110   }
111
112   /**
113    * Lookup the message with the given ID in this catalog and bind its substitution locations with the given string values.
114    */
115   public static String bind(String id, String[] bindings) {
116     if (id == null)
117       return "No message available"; //$NON-NLS-1$
118     String message = null;
119     try {
120       message = bundle.getString(id);
121     } catch (MissingResourceException e) {
122       // If we got an exception looking for the message, fail gracefully by just returning
123       // the id we were looking for. In most cases this is semi-informative so is not too bad.
124       return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-2$ //$NON-NLS-1$
125     }
126     // for compatibility with MessageFormat which eliminates double quotes in original message
127     char[] messageWithNoDoubleQuotes = CharOperation.replace(message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE);
128
129     if (bindings == null)
130       return new String(messageWithNoDoubleQuotes);
131
132     int length = messageWithNoDoubleQuotes.length;
133     int start = 0;
134     int end = length;
135     StringBuffer output = null;
136     while (true) {
137       if ((end = CharOperation.indexOf('{', messageWithNoDoubleQuotes, start)) > -1) {
138         if (output == null)
139           output = new StringBuffer(length + bindings.length * 20);
140         output.append(messageWithNoDoubleQuotes, start, end - start);
141         if ((start = CharOperation.indexOf('}', messageWithNoDoubleQuotes, end + 1)) > -1) {
142           int index = -1;
143           String argId = new String(messageWithNoDoubleQuotes, end + 1, start - end - 1);
144           try {
145             index = Integer.parseInt(argId);
146             output.append(bindings[index]);
147           } catch (NumberFormatException nfe) { // could be nested message ID {compiler.name}
148             boolean done = false;
149             if (!id.equals(argId)) {
150               String argMessage = null;
151               try {
152                 argMessage = bundle.getString(argId);
153                 output.append(argMessage);
154                 done = true;
155               } catch (MissingResourceException e) {
156                 // unable to bind argument, ignore (will leave argument in)
157               }
158             }
159             if (!done)
160               output.append(messageWithNoDoubleQuotes, end + 1, start - end);
161           } catch (ArrayIndexOutOfBoundsException e) {
162             output.append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$
163           }
164           start++;
165         } else {
166           output.append(messageWithNoDoubleQuotes, end, length);
167           break;
168         }
169       } else {
170         if (output == null)
171           return new String(messageWithNoDoubleQuotes);
172         output.append(messageWithNoDoubleQuotes, start, length - start);
173         break;
174       }
175     }
176     return output.toString();
177   }
178
179   /**
180    * Checks the type signature in String sig, starting at start and ending before end (end is not included). Returns the index of
181    * the character immediately after the signature if valid, or -1 if not valid.
182    */
183   private static int checkTypeSignature(String sig, int start, int end, boolean allowVoid) {
184     if (start >= end)
185       return -1;
186     int i = start;
187     char c = sig.charAt(i++);
188     int nestingDepth = 0;
189     while (c == '[') {
190       ++nestingDepth;
191       if (i >= end)
192         return -1;
193       c = sig.charAt(i++);
194     }
195     switch (c) {
196     case 'B':
197     case 'C':
198     case 'D':
199     case 'F':
200     case 'I':
201     case 'J':
202     case 'S':
203     case 'Z':
204       break;
205     case 'V':
206       if (!allowVoid)
207         return -1;
208       // array of void is not allowed
209       if (nestingDepth != 0)
210         return -1;
211       break;
212     case 'L':
213       int semicolon = sig.indexOf(';', i);
214       // Must have at least one character between L and ;
215       if (semicolon <= i || semicolon >= end)
216         return -1;
217       i = semicolon + 1;
218       break;
219     default:
220       return -1;
221     }
222     return i;
223   }
224
225   /**
226    * Combines two hash codes to make a new one.
227    */
228   public static int combineHashCodes(int hashCode1, int hashCode2) {
229     return hashCode1 * 17 + hashCode2;
230   }
231
232   /**
233    * Compares two byte arrays. Returns <0 if a byte in a is less than the corresponding byte in b, or if a is shorter, or if a is
234    * null. Returns >0 if a byte in a is greater than the corresponding byte in b, or if a is longer, or if b is null. Returns 0 if
235    * they are equal or both null.
236    */
237   public static int compare(byte[] a, byte[] b) {
238     if (a == b)
239       return 0;
240     if (a == null)
241       return -1;
242     if (b == null)
243       return 1;
244     int len = Math.min(a.length, b.length);
245     for (int i = 0; i < len; ++i) {
246       int diff = a[i] - b[i];
247       if (diff != 0)
248         return diff;
249     }
250     if (a.length > len)
251       return 1;
252     if (b.length > len)
253       return -1;
254     return 0;
255   }
256
257   /**
258    * Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings.
259    * 
260    * @return the value <code>0</code> if the str1 is equal to str2; a value less than <code>0</code> if str1 is
261    *         lexicographically less than str2; and a value greater than <code>0</code> if str1 is lexicographically greater than
262    *         str2.
263    */
264   public static int compare(char[] str1, char[] str2) {
265     int len1 = str1.length;
266     int len2 = str2.length;
267     int n = Math.min(len1, len2);
268     int i = 0;
269     while (n-- != 0) {
270       char c1 = str1[i];
271       char c2 = str2[i++];
272       if (c1 != c2) {
273         return c1 - c2;
274       }
275     }
276     return len1 - len2;
277   }
278
279   /**
280    * Concatenate two strings with a char in between.
281    * 
282    * @see #concat(String, String)
283    */
284   public static String concat(String s1, char c, String s2) {
285     if (s1 == null)
286       s1 = "null"; //$NON-NLS-1$
287     if (s2 == null)
288       s2 = "null"; //$NON-NLS-1$
289     int l1 = s1.length();
290     int l2 = s2.length();
291     char[] buf = new char[l1 + 1 + l2];
292     s1.getChars(0, l1, buf, 0);
293     buf[l1] = c;
294     s2.getChars(0, l2, buf, l1 + 1);
295     return new String(buf);
296   }
297
298   /**
299    * Concatenate two strings. Much faster than using +, which: - creates a StringBuffer, - which is synchronized, - of default size,
300    * so the resulting char array is often larger than needed. This implementation creates an extra char array, since the String
301    * constructor copies its argument, but there's no way around this.
302    */
303   public static String concat(String s1, String s2) {
304     if (s1 == null)
305       s1 = "null"; //$NON-NLS-1$
306     if (s2 == null)
307       s2 = "null"; //$NON-NLS-1$
308     int l1 = s1.length();
309     int l2 = s2.length();
310     char[] buf = new char[l1 + l2];
311     s1.getChars(0, l1, buf, 0);
312     s2.getChars(0, l2, buf, l1);
313     return new String(buf);
314   }
315
316   /**
317    * Concatenate three strings.
318    * 
319    * @see #concat(String, String)
320    */
321   public static String concat(String s1, String s2, String s3) {
322     if (s1 == null)
323       s1 = "null"; //$NON-NLS-1$
324     if (s2 == null)
325       s2 = "null"; //$NON-NLS-1$
326     if (s3 == null)
327       s3 = "null"; //$NON-NLS-1$
328     int l1 = s1.length();
329     int l2 = s2.length();
330     int l3 = s3.length();
331     char[] buf = new char[l1 + l2 + l3];
332     s1.getChars(0, l1, buf, 0);
333     s2.getChars(0, l2, buf, l1);
334     s3.getChars(0, l3, buf, l1 + l2);
335     return new String(buf);
336   }
337
338   /**
339    * Converts a type signature from the IBinaryType representation to the DC representation.
340    */
341   public static String convertTypeSignature(char[] sig) {
342     return new String(sig).replace('/', '.');
343   }
344
345   /**
346    * Apply the given edit on the given string and return the updated string. Return the given string if anything wrong happen while
347    * applying the edit.
348    * 
349    * @param original
350    *          the given string
351    * @param edit
352    *          the given edit
353    * 
354    * @return the updated string
355    */
356   public final static String editedString(String original, TextEdit edit) {
357     if (edit == null) {
358       return original;
359     }
360     SimpleDocument document = new SimpleDocument(original);
361     try {
362       edit.apply(document, TextEdit.NONE);
363       return document.get();
364     } catch (MalformedTreeException e) {
365       e.printStackTrace();
366     } catch (BadLocationException e) {
367       e.printStackTrace();
368     }
369     return original;
370   }
371
372   /**
373    * Returns true iff str.toLowerCase().endsWith(end.toLowerCase()) implementation is not creating extra strings.
374    */
375   public final static boolean endsWithIgnoreCase(String str, String end) {
376
377     int strLength = str == null ? 0 : str.length();
378     int endLength = end == null ? 0 : end.length();
379
380     // return false if the string is smaller than the end.
381     if (endLength > strLength)
382       return false;
383
384     // return false if any character of the end are
385     // not the same in lower case.
386     for (int i = 1; i <= endLength; i++) {
387       if (Character.toLowerCase(end.charAt(endLength - i)) != Character.toLowerCase(str.charAt(strLength - i)))
388         return false;
389     }
390
391     return true;
392   }
393
394   /**
395    * Compares two arrays using equals() on the elements. Either or both arrays may be null. Returns true if both are null. Returns
396    * false if only one is null. If both are arrays, returns true iff they have the same length and all elements are equal.
397    */
398   public static boolean equalArraysOrNull(int[] a, int[] b) {
399     if (a == b)
400       return true;
401     if (a == null || b == null)
402       return false;
403     int len = a.length;
404     if (len != b.length)
405       return false;
406     for (int i = 0; i < len; ++i) {
407       if (a[i] != b[i])
408         return false;
409     }
410     return true;
411   }
412
413   /**
414    * Compares two arrays using equals() on the elements. Either or both arrays may be null. Returns true if both are null. Returns
415    * false if only one is null. If both are arrays, returns true iff they have the same length and all elements compare true with
416    * equals.
417    */
418   public static boolean equalArraysOrNull(Object[] a, Object[] b) {
419     if (a == b)
420       return true;
421     if (a == null || b == null)
422       return false;
423
424     int len = a.length;
425     if (len != b.length)
426       return false;
427     for (int i = 0; i < len; ++i) {
428       if (a[i] == null) {
429         if (b[i] != null)
430           return false;
431       } else {
432         if (!a[i].equals(b[i]))
433           return false;
434       }
435     }
436     return true;
437   }
438
439   /**
440    * Compares two arrays using equals() on the elements. The arrays are first sorted. Either or both arrays may be null. Returns
441    * true if both are null. Returns false if only one is null. If both are arrays, returns true iff they have the same length and
442    * iff, after sorting both arrays, all elements compare true with equals. The original arrays are left untouched.
443    */
444   public static boolean equalArraysOrNullSortFirst(Comparable[] a, Comparable[] b) {
445     if (a == b)
446       return true;
447     if (a == null || b == null)
448       return false;
449     int len = a.length;
450     if (len != b.length)
451       return false;
452     if (len >= 2) { // only need to sort if more than two items
453       a = sortCopy(a);
454       b = sortCopy(b);
455     }
456     for (int i = 0; i < len; ++i) {
457       if (!a[i].equals(b[i]))
458         return false;
459     }
460     return true;
461   }
462
463   /**
464    * Compares two String arrays using equals() on the elements. The arrays are first sorted. Either or both arrays may be null.
465    * Returns true if both are null. Returns false if only one is null. If both are arrays, returns true iff they have the same
466    * length and iff, after sorting both arrays, all elements compare true with equals. The original arrays are left untouched.
467    */
468   public static boolean equalArraysOrNullSortFirst(String[] a, String[] b) {
469     if (a == b)
470       return true;
471     if (a == null || b == null)
472       return false;
473     int len = a.length;
474     if (len != b.length)
475       return false;
476     if (len >= 2) { // only need to sort if more than two items
477       a = sortCopy(a);
478       b = sortCopy(b);
479     }
480     for (int i = 0; i < len; ++i) {
481       if (!a[i].equals(b[i]))
482         return false;
483     }
484     return true;
485   }
486
487   /**
488    * Compares two objects using equals(). Either or both array may be null. Returns true if both are null. Returns false if only one
489    * is null. Otherwise, return the result of comparing with equals().
490    */
491   public static boolean equalOrNull(Object a, Object b) {
492     if (a == b) {
493       return true;
494     }
495     if (a == null || b == null) {
496       return false;
497     }
498     return a.equals(b);
499   }
500
501   /**
502    * Given a qualified name, extract the last component. If the input is not qualified, the same string is answered.
503    */
504   public static String extractLastName(String qualifiedName) {
505     int i = qualifiedName.lastIndexOf('.');
506     if (i == -1)
507       return qualifiedName;
508     return qualifiedName.substring(i + 1);
509   }
510
511   /**
512    * Extracts the parameter types from a method signature.
513    */
514   public static String[] extractParameterTypes(char[] sig) {
515     int count = getParameterCount(sig);
516     String[] result = new String[count];
517     if (count == 0)
518       return result;
519     int i = CharOperation.indexOf('(', sig) + 1;
520     count = 0;
521     int len = sig.length;
522     int start = i;
523     for (;;) {
524       if (i == len)
525         break;
526       char c = sig[i];
527       if (c == ')')
528         break;
529       if (c == '[') {
530         ++i;
531       } else if (c == 'L') {
532         i = CharOperation.indexOf(';', sig, i + 1) + 1;
533         Assert.isTrue(i != 0);
534         result[count++] = convertTypeSignature(CharOperation.subarray(sig, start, i));
535         start = i;
536       } else {
537         ++i;
538         result[count++] = convertTypeSignature(CharOperation.subarray(sig, start, i));
539         start = i;
540       }
541     }
542     return result;
543   }
544
545   /**
546    * Extracts the return type from a method signature.
547    */
548   public static String extractReturnType(String sig) {
549     int i = sig.lastIndexOf(')');
550     Assert.isTrue(i != -1);
551     return sig.substring(i + 1);
552   }
553
554   private static IFile findFirstClassFile(IFolder folder) {
555     try {
556       IResource[] members = folder.members();
557       for (int i = 0, max = members.length; i < max; i++) {
558         IResource member = members[i];
559         if (member.getType() == IResource.FOLDER) {
560           return findFirstClassFile((IFolder) member);
561 //        } else if (net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(member.getName())) {
562 //          return (IFile) member;
563         }
564       }
565     } catch (CoreException e) {
566       // ignore
567     }
568     return null;
569   }
570
571   /**
572    * Finds the first line separator used by the given text.
573    * 
574    * @return</code> "\n"</code> or</code> "\r"</code> or</code> "\r\n"</code>, or <code>null</code> if none found
575    */
576   public static String findLineSeparator(char[] text) {
577     // find the first line separator
578     int length = text.length;
579     if (length > 0) {
580       char nextChar = text[0];
581       for (int i = 0; i < length; i++) {
582         char currentChar = nextChar;
583         nextChar = i < length - 1 ? text[i + 1] : ' ';
584         switch (currentChar) {
585         case '\n':
586           return "\n"; //$NON-NLS-1$
587         case '\r':
588           return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$
589         }
590       }
591     }
592     // not found
593     return null;
594   }
595
596   //    public static IClassFileAttribute getAttribute(IClassFileReader classFileReader, char[] attributeName) {
597   //            IClassFileAttribute[] attributes = classFileReader.getAttributes();
598   //            for (int i = 0, max = attributes.length; i < max; i++) {
599   //                    if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) {
600   //                            return attributes[i];
601   //                    }
602   //            }
603   //            return null;
604   //    }
605   //    
606   //    public static IClassFileAttribute getAttribute(ICodeAttribute codeAttribute, char[] attributeName) {
607   //            IClassFileAttribute[] attributes = codeAttribute.getAttributes();
608   //            for (int i = 0, max = attributes.length; i < max; i++) {
609   //                    if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) {
610   //                            return attributes[i];
611   //                    }
612   //            }
613   //            return null;
614   //    }
615
616   //    public static IClassFileAttribute getAttribute(IFieldInfo fieldInfo, char[] attributeName) {
617   //            IClassFileAttribute[] attributes = fieldInfo.getAttributes();
618   //            for (int i = 0, max = attributes.length; i < max; i++) {
619   //                    if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) {
620   //                            return attributes[i];
621   //                    }
622   //            }
623   //            return null;
624   //    }
625   //
626   //    public static IClassFileAttribute getAttribute(IMethodInfo methodInfo, char[] attributeName) {
627   //            IClassFileAttribute[] attributes = methodInfo.getAttributes();
628   //            for (int i = 0, max = attributes.length; i < max; i++) {
629   //                    if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) {
630   //                            return attributes[i];
631   //                    }
632   //            }
633   //            return null;
634   //    }
635   /**
636    * Get the jdk level of this root. The value can be:
637    * <ul>
638    * <li>major < <16 + minor : see predefined constants on ClassFileConstants</li>
639    * <li><code>0</null> if the root is a source package fragment root or if a Java model exception occured</li>
640    * </ul>
641    * Returns the jdk level
642    */
643   //    public static long getJdkLevel(Object targetLibrary) {
644   //            try {
645   //                            ClassFileReader reader = null;
646   //                            if (targetLibrary instanceof IFolder) {
647   //                                    IFile classFile = findFirstClassFile((IFolder) targetLibrary); // only internal classfolders are allowed
648   //                                    if (classFile != null) {
649   //                                            byte[] bytes = Util.getResourceContentsAsByteArray(classFile);
650   //                                            IPath location = classFile.getLocation();
651   //                                            reader = new ClassFileReader(bytes, location == null ? null : location.toString().toCharArray());
652   //                                    }
653   //                            } else {
654   //                                    // root is a jar file or a zip file
655   //                                    ZipFile jar = null;
656   //                                    try {
657   //                                            IPath path = null;
658   //                                            if (targetLibrary instanceof IResource) {
659   //                                                    path = ((IResource)targetLibrary).getLocation();
660   //                                            } else if (targetLibrary instanceof File){
661   //                                                    File f = (File) targetLibrary;
662   //                                                    if (!f.isDirectory()) {
663   //                                                            path = new Path(((File)targetLibrary).getPath());
664   //                                                    }
665   //                                            }
666   //                                            if (path != null) {
667   //                                                    jar = JavaModelManager.getJavaModelManager().getZipFile(path);
668   //                                                    for (Enumeration e= jar.entries(); e.hasMoreElements();) {
669   //                                                            ZipEntry member= (ZipEntry) e.nextElement();
670   //                                                            String entryName= member.getName();
671   //                                                            if (net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(entryName)) {
672   //                                                                    reader = ClassFileReader.read(jar, entryName);
673   //                                                                    break;
674   //                                                            }
675   //                                                    }
676   //                                            }
677   //                                    } catch (CoreException e) {
678   //                                            // ignore
679   //                                    } finally {
680   //                                            JavaModelManager.getJavaModelManager().closeZipFile(jar);
681   //                                    }
682   //                            }
683   //                            if (reader != null) {
684   //                                    return reader.getVersion();
685   //                            }
686   //            } catch(JavaModelException e) {
687   //                    // ignore
688   //            } catch(ClassFormatException e) {
689   //                    // ignore
690   //            } catch(IOException e) {
691   //                    // ignore
692   //            }
693   //            return 0;
694   //    }
695   /**
696    * Returns the line separator used by the given buffer. Uses the given text if none found.
697    * 
698    * @return</code> "\n"</code> or</code> "\r"</code> or</code> "\r\n"</code>
699    */
700   private static String getLineSeparator(char[] text, char[] buffer) {
701     // search in this buffer's contents first
702     String lineSeparator = findLineSeparator(buffer);
703     if (lineSeparator == null) {
704       // search in the given text
705       lineSeparator = findLineSeparator(text);
706       if (lineSeparator == null) {
707         // default to system line separator
708         return net.sourceforge.phpdt.internal.compiler.util.Util.LINE_SEPARATOR;
709       }
710     }
711     return lineSeparator;
712   }
713
714   /**
715    * Returns the number of parameter types in a method signature.
716    */
717   public static int getParameterCount(char[] sig) {
718     int i = CharOperation.indexOf('(', sig) + 1;
719     Assert.isTrue(i != 0);
720     int count = 0;
721     int len = sig.length;
722     for (;;) {
723       if (i == len)
724         break;
725       char c = sig[i];
726       if (c == ')')
727         break;
728       if (c == '[') {
729         ++i;
730       } else if (c == 'L') {
731         ++count;
732         i = CharOperation.indexOf(';', sig, i + 1) + 1;
733         Assert.isTrue(i != 0);
734       } else {
735         ++count;
736         ++i;
737       }
738     }
739     return count;
740   }
741
742   /**
743    * Put all the arguments in one String.
744    */
745   public static String getProblemArgumentsForMarker(String[] arguments) {
746     StringBuffer args = new StringBuffer(10);
747
748     args.append(arguments.length);
749     args.append(':');
750
751     for (int j = 0; j < arguments.length; j++) {
752       if (j != 0)
753         args.append(ARGUMENTS_DELIMITER);
754
755       if (arguments[j].length() == 0) {
756         args.append(EMPTY_ARGUMENT);
757       } else {
758         args.append(arguments[j]);
759       }
760     }
761
762     return args.toString();
763   }
764
765   /**
766    * Separate all the arguments of a String made by getProblemArgumentsForMarker
767    */
768   public static String[] getProblemArgumentsFromMarker(String argumentsString) {
769     if (argumentsString == null)
770       return null;
771     int index = argumentsString.indexOf(':');
772     if (index == -1)
773       return null;
774
775     int length = argumentsString.length();
776     int numberOfArg;
777     try {
778       numberOfArg = Integer.parseInt(argumentsString.substring(0, index));
779     } catch (NumberFormatException e) {
780       return null;
781     }
782     argumentsString = argumentsString.substring(index + 1, length);
783
784     String[] args = new String[length];
785     int count = 0;
786
787     StringTokenizer tokenizer = new StringTokenizer(argumentsString, ARGUMENTS_DELIMITER);
788     while (tokenizer.hasMoreTokens()) {
789       String argument = tokenizer.nextToken();
790       if (argument.equals(EMPTY_ARGUMENT))
791         argument = ""; //$NON-NLS-1$
792       args[count++] = argument;
793     }
794
795     if (count != numberOfArg)
796       return null;
797
798     System.arraycopy(args, 0, args = new String[count], 0, count);
799     return args;
800   }
801
802   /**
803    * Returns the given file's contents as a byte array.
804    */
805   public static byte[] getResourceContentsAsByteArray(IFile file) throws JavaModelException {
806     InputStream stream = null;
807     try {
808       stream = new BufferedInputStream(file.getContents(true));
809     } catch (CoreException e) {
810       throw new JavaModelException(e);
811     }
812     try {
813       return net.sourceforge.phpdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, -1);
814     } catch (IOException e) {
815       throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
816     } finally {
817       try {
818         stream.close();
819       } catch (IOException e) {
820         // ignore
821       }
822     }
823   }
824
825   /**
826    * Returns the given file's contents as a character array.
827    */
828   public static char[] getResourceContentsAsCharArray(IFile file) throws JavaModelException {
829     // Get encoding from file
830     String encoding = null;
831     try {
832       encoding = file.getCharset();
833     } catch (CoreException ce) {
834       // do not use any encoding
835     }
836     return getResourceContentsAsCharArray(file, encoding);
837   }
838
839   public static char[] getResourceContentsAsCharArray(IFile file, String encoding) throws JavaModelException {
840     // Get resource contents
841     InputStream stream = null;
842     try {
843       stream = new BufferedInputStream(file.getContents(true));
844     } catch (CoreException e) {
845       throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
846     }
847     try {
848       return net.sourceforge.phpdt.internal.compiler.util.Util.getInputStreamAsCharArray(stream, -1, encoding);
849     } catch (IOException e) {
850       throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
851     } finally {
852       try {
853         stream.close();
854       } catch (IOException e) {
855         // ignore
856       }
857     }
858   }
859
860   /**
861    * Returns a trimmed version the simples names returned by Signature.
862    */
863   public static String[] getTrimmedSimpleNames(String name) {
864     String[] result = Signature.getSimpleNames(name);
865     if (result == null)
866       return null;
867     for (int i = 0, length = result.length; i < length; i++) {
868       result[i] = result[i].trim();
869     }
870     return result;
871   }
872
873   /*
874    * Returns the index of the most specific argument paths which is strictly enclosing the path to check
875    */
876   public static int indexOfEnclosingPath(IPath checkedPath, IPath[] paths, int pathCount) {
877
878     int bestMatch = -1, bestLength = -1;
879     for (int i = 0; i < pathCount; i++) {
880       if (paths[i].equals(checkedPath))
881         continue;
882       if (paths[i].isPrefixOf(checkedPath)) {
883         int currentLength = paths[i].segmentCount();
884         if (currentLength > bestLength) {
885           bestLength = currentLength;
886           bestMatch = i;
887         }
888       }
889     }
890     return bestMatch;
891   }
892
893   /*
894    * Returns the index of the first argument paths which is equal to the path to check
895    */
896   public static int indexOfMatchingPath(IPath checkedPath, IPath[] paths, int pathCount) {
897
898     for (int i = 0; i < pathCount; i++) {
899       if (paths[i].equals(checkedPath))
900         return i;
901     }
902     return -1;
903   }
904
905   /*
906    * Returns the index of the first argument paths which is strictly nested inside the path to check
907    */
908   public static int indexOfNestedPath(IPath checkedPath, IPath[] paths, int pathCount) {
909
910     for (int i = 0; i < pathCount; i++) {
911       if (checkedPath.equals(paths[i]))
912         continue;
913       if (checkedPath.isPrefixOf(paths[i]))
914         return i;
915     }
916     return -1;
917   }
918
919   /*
920    * Returns whether the given java element is exluded from its root's classpath. It doesn't check whether the root itself is on the
921    * classpath or not
922    */
923   public static final boolean isExcluded(IJavaElement element) {
924     int elementType = element.getElementType();
925     PackageFragmentRoot root = null;
926     IResource resource = null;
927     switch (elementType) {
928     case IJavaElement.JAVA_MODEL:
929     case IJavaElement.JAVA_PROJECT:
930     case IJavaElement.PACKAGE_FRAGMENT_ROOT:
931       return false;
932
933     //                  case IJavaElement.PACKAGE_FRAGMENT:
934     //                          PackageFragmentRoot root = (PackageFragmentRoot)element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
935     //                          IResource resource = element.getResource();
936     //                          return resource != null && isExcluded(resource, root.fullInclusionPatternChars(), root.fullExclusionPatternChars());
937
938     case IJavaElement.COMPILATION_UNIT:
939       root = (PackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
940       resource = element.getResource();
941       //                                if (resource != null && isExcluded(resource, root.fullInclusionPatternChars(), root.fullExclusionPatternChars()))
942       //                                        return true;
943       return isExcluded(element.getParent());
944
945     default:
946       IJavaElement cu = element.getAncestor(IJavaElement.COMPILATION_UNIT);
947       return cu != null && isExcluded(cu);
948     }
949   }
950
951   /*
952    * Returns whether the given resource path matches one of the inclusion/exclusion patterns. NOTE: should not be asked directly
953    * using pkg root pathes
954    * 
955    * @see IClasspathEntry#getInclusionPatterns
956    * @see IClasspathEntry#getExclusionPatterns
957    */
958   public final static boolean isExcluded(IPath resourcePath, char[][] inclusionPatterns, char[][] exclusionPatterns,
959       boolean isFolderPath) {
960     if (inclusionPatterns == null && exclusionPatterns == null)
961       return false;
962     char[] path = resourcePath.toString().toCharArray();
963
964     inclusionCheck: if (inclusionPatterns != null) {
965       for (int i = 0, length = inclusionPatterns.length; i < length; i++) {
966         char[] pattern = inclusionPatterns[i];
967         char[] folderPattern = pattern;
968         if (isFolderPath) {
969           int lastSlash = CharOperation.lastIndexOf('/', pattern);
970           if (lastSlash != -1 && lastSlash != pattern.length - 1) { // trailing slash -> adds '**' for free (see
971                                                                     // http://ant.apache.org/manual/dirtasks.html)
972             int star = CharOperation.indexOf('*', pattern, lastSlash);
973             if ((star == -1 || star >= pattern.length - 1 || pattern[star + 1] != '*')) {
974               folderPattern = CharOperation.subarray(pattern, 0, lastSlash);
975             }
976           }
977         }
978         if (CharOperation.pathMatch(folderPattern, path, true, '/')) {
979           break inclusionCheck;
980         }
981       }
982       return true; // never included
983     }
984     if (isFolderPath) {
985       path = CharOperation.concat(path, new char[] { '*' }, '/');
986     }
987     exclusionCheck: if (exclusionPatterns != null) {
988       for (int i = 0, length = exclusionPatterns.length; i < length; i++) {
989         if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/')) {
990           return true;
991         }
992       }
993     }
994     return false;
995   }
996
997   public final static boolean isExcluded(IResource resource, char[][] exclusionPatterns) {
998     IPath path = resource.getFullPath();
999     // ensure that folders are only excluded if all of their children are excluded
1000     return isExcluded(path, null, exclusionPatterns, resource.getType() == IResource.FOLDER);
1001   }
1002
1003   /*
1004    * Returns whether the given resource matches one of the exclusion patterns. NOTE: should not be asked directly using pkg root
1005    * pathes
1006    * 
1007    * @see IClasspathEntry#getExclusionPatterns
1008    */
1009   public final static boolean isExcluded(IResource resource, char[][] inclusionPatterns, char[][] exclusionPatterns) {
1010     IPath path = resource.getFullPath();
1011     // ensure that folders are only excluded if all of their children are excluded
1012     return isExcluded(path, inclusionPatterns, exclusionPatterns, resource.getType() == IResource.FOLDER);
1013   }
1014
1015   /**
1016    * Validate the given .class file name. A .class file name must obey the following rules:
1017    * <ul>
1018    * <li>it must not be null
1019    * <li>it must include the <code>".class"</code> suffix
1020    * <li>its prefix must be a valid identifier
1021    * </ul>
1022    * </p>
1023    * 
1024    * @param name
1025    *          the name of a .class file
1026    * @return a status object with code <code>IStatus.OK</code> if the given name is valid as a .class file name, otherwise a
1027    *         status object indicating what is wrong with the name
1028    */
1029   //    public static boolean isValidClassFileName(String name) {
1030   //            return JavaConventions.validateClassFileName(name).getSeverity() != IStatus.ERROR;
1031   //    }
1032   /**
1033    * Validate the given compilation unit name. A compilation unit name must obey the following rules:
1034    * <ul>
1035    * <li>it must not be null
1036    * <li>it must include the <code>".java"</code> suffix
1037    * <li>its prefix must be a valid identifier
1038    * </ul>
1039    * </p>
1040    * 
1041    * @param name
1042    *          the name of a compilation unit
1043    * @return a status object with code <code>IStatus.OK</code> if the given name is valid as a compilation unit name, otherwise a
1044    *         status object indicating what is wrong with the name
1045    */
1046   public static boolean isValidCompilationUnitName(String name) {
1047     return PHPFileUtil.isPHPFileName(name);
1048     //          return JavaConventions.validateCompilationUnitName(name).getSeverity() != IStatus.ERROR;
1049   }
1050
1051   /**
1052    * Returns true if the given folder name is valid for a package, false if it is not.
1053    */
1054         public static boolean isValidFolderNameForPackage(String folderName) {
1055   //            return JavaConventions.validateIdentifier(folderName).getSeverity() != IStatus.ERROR;
1056           return true;
1057         }
1058   /**
1059    * Returns true if the given method signature is valid, false if it is not.
1060    */
1061   public static boolean isValidMethodSignature(String sig) {
1062     int len = sig.length();
1063     if (len == 0)
1064       return false;
1065     int i = 0;
1066     char c = sig.charAt(i++);
1067     if (c != '(')
1068       return false;
1069     if (i >= len)
1070       return false;
1071     while (sig.charAt(i) != ')') {
1072       // Void is not allowed as a parameter type.
1073       i = checkTypeSignature(sig, i, len, false);
1074       if (i == -1)
1075         return false;
1076       if (i >= len)
1077         return false;
1078     }
1079     ++i;
1080     i = checkTypeSignature(sig, i, len, true);
1081     return i == len;
1082   }
1083
1084   /**
1085    * Returns true if the given type signature is valid, false if it is not.
1086    */
1087   public static boolean isValidTypeSignature(String sig, boolean allowVoid) {
1088     int len = sig.length();
1089     return checkTypeSignature(sig, 0, len, allowVoid) == len;
1090   }
1091
1092   /*
1093    * Add a log entry
1094    */
1095   public static void log(Throwable e, String message) {
1096     Throwable nestedException;
1097     if (e instanceof JavaModelException && (nestedException = ((JavaModelException) e).getException()) != null) {
1098       e = nestedException;
1099     }
1100     IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, message, e);
1101     JavaCore.getPlugin().getLog().log(status);
1102   }
1103
1104   /**
1105    * Normalizes the cariage returns in the given text. They are all changed to use the given buffer's line separator.
1106    */
1107   public static char[] normalizeCRs(char[] text, char[] buffer) {
1108     CharArrayBuffer result = new CharArrayBuffer();
1109     int lineStart = 0;
1110     int length = text.length;
1111     if (length == 0)
1112       return text;
1113     String lineSeparator = getLineSeparator(text, buffer);
1114     char nextChar = text[0];
1115     for (int i = 0; i < length; i++) {
1116       char currentChar = nextChar;
1117       nextChar = i < length - 1 ? text[i + 1] : ' ';
1118       switch (currentChar) {
1119       case '\n':
1120         int lineLength = i - lineStart;
1121         char[] line = new char[lineLength];
1122         System.arraycopy(text, lineStart, line, 0, lineLength);
1123         result.append(line);
1124         result.append(lineSeparator);
1125         lineStart = i + 1;
1126         break;
1127       case '\r':
1128         lineLength = i - lineStart;
1129         if (lineLength >= 0) {
1130           line = new char[lineLength];
1131           System.arraycopy(text, lineStart, line, 0, lineLength);
1132           result.append(line);
1133           result.append(lineSeparator);
1134           if (nextChar == '\n') {
1135             nextChar = ' ';
1136             lineStart = i + 2;
1137           } else {
1138             // when line separator are mixed in the same file
1139             // \r might not be followed by a \n. If not, we should increment
1140             // lineStart by one and not by two.
1141             lineStart = i + 1;
1142           }
1143         } else {
1144           // when line separator are mixed in the same file
1145           // we need to prevent NegativeArraySizeException
1146           lineStart = i + 1;
1147         }
1148         break;
1149       }
1150     }
1151     char[] lastLine;
1152     if (lineStart > 0) {
1153       int lastLineLength = length - lineStart;
1154       if (lastLineLength > 0) {
1155         lastLine = new char[lastLineLength];
1156         System.arraycopy(text, lineStart, lastLine, 0, lastLineLength);
1157         result.append(lastLine);
1158       }
1159       return result.getContents();
1160     }
1161     return text;
1162   }
1163
1164   /**
1165    * Normalizes the cariage returns in the given text. They are all changed to use given buffer's line sepatator.
1166    */
1167   public static String normalizeCRs(String text, String buffer) {
1168     return new String(normalizeCRs(text.toCharArray(), buffer.toCharArray()));
1169   }
1170
1171   /**
1172    * Converts the given relative path into a package name. Returns null if the path is not a valid package name.
1173    */
1174   public static String packageName(IPath pkgPath) {
1175     StringBuffer pkgName = new StringBuffer(IPackageFragment.DEFAULT_PACKAGE_NAME);
1176     for (int j = 0, max = pkgPath.segmentCount(); j < max; j++) {
1177       String segment = pkgPath.segment(j);
1178       //                        if (!isValidFolderNameForPackage(segment)) {
1179       //                                return null;
1180       //                        }
1181       pkgName.append(segment);
1182       if (j < pkgPath.segmentCount() - 1) {
1183         pkgName.append("."); //$NON-NLS-1$
1184       }
1185     }
1186     return pkgName.toString();
1187   }
1188
1189   /**
1190    * Returns the length of the common prefix between s1 and s2.
1191    */
1192   public static int prefixLength(char[] s1, char[] s2) {
1193     int len = 0;
1194     int max = Math.min(s1.length, s2.length);
1195     for (int i = 0; i < max && s1[i] == s2[i]; ++i)
1196       ++len;
1197     return len;
1198   }
1199
1200   /**
1201    * Returns the length of the common prefix between s1 and s2.
1202    */
1203   public static int prefixLength(String s1, String s2) {
1204     int len = 0;
1205     int max = Math.min(s1.length(), s2.length());
1206     for (int i = 0; i < max && s1.charAt(i) == s2.charAt(i); ++i)
1207       ++len;
1208     return len;
1209   }
1210
1211   private static void quickSort(char[][] list, int left, int right) {
1212     int original_left = left;
1213     int original_right = right;
1214     char[] mid = list[(left + right) / 2];
1215     do {
1216       while (compare(list[left], mid) < 0) {
1217         left++;
1218       }
1219       while (compare(mid, list[right]) < 0) {
1220         right--;
1221       }
1222       if (left <= right) {
1223         char[] tmp = list[left];
1224         list[left] = list[right];
1225         list[right] = tmp;
1226         left++;
1227         right--;
1228       }
1229     } while (left <= right);
1230     if (original_left < right) {
1231       quickSort(list, original_left, right);
1232     }
1233     if (left < original_right) {
1234       quickSort(list, left, original_right);
1235     }
1236   }
1237
1238   /**
1239    * Sort the comparable objects in the given collection.
1240    */
1241   private static void quickSort(Comparable[] sortedCollection, int left, int right) {
1242     int original_left = left;
1243     int original_right = right;
1244     Comparable mid = sortedCollection[(left + right) / 2];
1245     do {
1246       while (sortedCollection[left].compareTo(mid) < 0) {
1247         left++;
1248       }
1249       while (mid.compareTo(sortedCollection[right]) < 0) {
1250         right--;
1251       }
1252       if (left <= right) {
1253         Comparable tmp = sortedCollection[left];
1254         sortedCollection[left] = sortedCollection[right];
1255         sortedCollection[right] = tmp;
1256         left++;
1257         right--;
1258       }
1259     } while (left <= right);
1260     if (original_left < right) {
1261       quickSort(sortedCollection, original_left, right);
1262     }
1263     if (left < original_right) {
1264       quickSort(sortedCollection, left, original_right);
1265     }
1266   }
1267
1268   private static void quickSort(int[] list, int left, int right) {
1269     int original_left = left;
1270     int original_right = right;
1271     int mid = list[(left + right) / 2];
1272     do {
1273       while (list[left] < mid) {
1274         left++;
1275       }
1276       while (mid < list[right]) {
1277         right--;
1278       }
1279       if (left <= right) {
1280         int tmp = list[left];
1281         list[left] = list[right];
1282         list[right] = tmp;
1283         left++;
1284         right--;
1285       }
1286     } while (left <= right);
1287     if (original_left < right) {
1288       quickSort(list, original_left, right);
1289     }
1290     if (left < original_right) {
1291       quickSort(list, left, original_right);
1292     }
1293   }
1294
1295   /**
1296    * Sort the objects in the given collection using the given comparer.
1297    */
1298   private static void quickSort(Object[] sortedCollection, int left, int right, Comparer comparer) {
1299     int original_left = left;
1300     int original_right = right;
1301     Object mid = sortedCollection[(left + right) / 2];
1302     do {
1303       while (comparer.compare(sortedCollection[left], mid) < 0) {
1304         left++;
1305       }
1306       while (comparer.compare(mid, sortedCollection[right]) < 0) {
1307         right--;
1308       }
1309       if (left <= right) {
1310         Object tmp = sortedCollection[left];
1311         sortedCollection[left] = sortedCollection[right];
1312         sortedCollection[right] = tmp;
1313         left++;
1314         right--;
1315       }
1316     } while (left <= right);
1317     if (original_left < right) {
1318       quickSort(sortedCollection, original_left, right, comparer);
1319     }
1320     if (left < original_right) {
1321       quickSort(sortedCollection, left, original_right, comparer);
1322     }
1323   }
1324
1325   /**
1326    * Sort the objects in the given collection using the given sort order.
1327    */
1328   private static void quickSort(Object[] sortedCollection, int left, int right, int[] sortOrder) {
1329     int original_left = left;
1330     int original_right = right;
1331     int mid = sortOrder[(left + right) / 2];
1332     do {
1333       while (sortOrder[left] < mid) {
1334         left++;
1335       }
1336       while (mid < sortOrder[right]) {
1337         right--;
1338       }
1339       if (left <= right) {
1340         Object tmp = sortedCollection[left];
1341         sortedCollection[left] = sortedCollection[right];
1342         sortedCollection[right] = tmp;
1343         int tmp2 = sortOrder[left];
1344         sortOrder[left] = sortOrder[right];
1345         sortOrder[right] = tmp2;
1346         left++;
1347         right--;
1348       }
1349     } while (left <= right);
1350     if (original_left < right) {
1351       quickSort(sortedCollection, original_left, right, sortOrder);
1352     }
1353     if (left < original_right) {
1354       quickSort(sortedCollection, left, original_right, sortOrder);
1355     }
1356   }
1357
1358   /**
1359    * Sort the strings in the given collection.
1360    */
1361   private static void quickSort(String[] sortedCollection, int left, int right) {
1362     int original_left = left;
1363     int original_right = right;
1364     String mid = sortedCollection[(left + right) / 2];
1365     do {
1366       while (sortedCollection[left].compareTo(mid) < 0) {
1367         left++;
1368       }
1369       while (mid.compareTo(sortedCollection[right]) < 0) {
1370         right--;
1371       }
1372       if (left <= right) {
1373         String tmp = sortedCollection[left];
1374         sortedCollection[left] = sortedCollection[right];
1375         sortedCollection[right] = tmp;
1376         left++;
1377         right--;
1378       }
1379     } while (left <= right);
1380     if (original_left < right) {
1381       quickSort(sortedCollection, original_left, right);
1382     }
1383     if (left < original_right) {
1384       quickSort(sortedCollection, left, original_right);
1385     }
1386   }
1387
1388   /**
1389    * Sort the strings in the given collection in reverse alphabetical order.
1390    */
1391   private static void quickSortReverse(String[] sortedCollection, int left, int right) {
1392     int original_left = left;
1393     int original_right = right;
1394     String mid = sortedCollection[(left + right) / 2];
1395     do {
1396       while (sortedCollection[left].compareTo(mid) > 0) {
1397         left++;
1398       }
1399       while (mid.compareTo(sortedCollection[right]) > 0) {
1400         right--;
1401       }
1402       if (left <= right) {
1403         String tmp = sortedCollection[left];
1404         sortedCollection[left] = sortedCollection[right];
1405         sortedCollection[right] = tmp;
1406         left++;
1407         right--;
1408       }
1409     } while (left <= right);
1410     if (original_left < right) {
1411       quickSortReverse(sortedCollection, original_left, right);
1412     }
1413     if (left < original_right) {
1414       quickSortReverse(sortedCollection, left, original_right);
1415     }
1416   }
1417
1418   /**
1419    * Reads in a string from the specified data input stream. The string has been encoded using a modified UTF-8 format.
1420    * <p>
1421    * The first two bytes are read as if by <code>readUnsignedShort</code>. This value gives the number of following bytes that
1422    * are in the encoded string, not the length of the resulting string. The following bytes are then interpreted as bytes encoding
1423    * characters in the UTF-8 format and are converted into characters.
1424    * <p>
1425    * This method blocks until all the bytes are read, the end of the stream is detected, or an exception is thrown.
1426    * 
1427    * @param in
1428    *          a data input stream.
1429    * @return a Unicode string.
1430    * @exception EOFException
1431    *              if the input stream reaches the end before all the bytes.
1432    * @exception IOException
1433    *              if an I/O error occurs.
1434    * @exception UTFDataFormatException
1435    *              if the bytes do not represent a valid UTF-8 encoding of a Unicode string.
1436    * @see java.io.DataInputStream#readUnsignedShort()
1437    */
1438   public final static char[] readUTF(DataInput in) throws IOException {
1439     int utflen = in.readUnsignedShort();
1440     char str[] = new char[utflen];
1441     int count = 0;
1442     int strlen = 0;
1443     while (count < utflen) {
1444       int c = in.readUnsignedByte();
1445       int char2, char3;
1446       switch (c >> 4) {
1447       case 0:
1448       case 1:
1449       case 2:
1450       case 3:
1451       case 4:
1452       case 5:
1453       case 6:
1454       case 7:
1455         // 0xxxxxxx
1456         count++;
1457         str[strlen++] = (char) c;
1458         break;
1459       case 12:
1460       case 13:
1461         // 110x xxxx 10xx xxxx
1462         count += 2;
1463         if (count > utflen)
1464           throw new UTFDataFormatException();
1465         char2 = in.readUnsignedByte();
1466         if ((char2 & 0xC0) != 0x80)
1467           throw new UTFDataFormatException();
1468         str[strlen++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
1469         break;
1470       case 14:
1471         // 1110 xxxx 10xx xxxx 10xx xxxx
1472         count += 3;
1473         if (count > utflen)
1474           throw new UTFDataFormatException();
1475         char2 = in.readUnsignedByte();
1476         char3 = in.readUnsignedByte();
1477         if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
1478           throw new UTFDataFormatException();
1479         str[strlen++] = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
1480         break;
1481       default:
1482         // 10xx xxxx, 1111 xxxx
1483         throw new UTFDataFormatException();
1484       }
1485     }
1486     if (strlen < utflen) {
1487       System.arraycopy(str, 0, str = new char[strlen], 0, strlen);
1488     }
1489     return str;
1490   }
1491
1492   /**
1493    * Creates a NLS catalog for the given locale.
1494    */
1495   public static void relocalize() {
1496     try {
1497       bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
1498     } catch (MissingResourceException e) {
1499       System.out.println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$
1500       throw e;
1501     }
1502   }
1503
1504   public static void sort(char[][] list) {
1505     if (list.length > 1)
1506       quickSort(list, 0, list.length - 1);
1507   }
1508
1509   /**
1510    * Sorts an array of Comparable objects in place.
1511    */
1512   public static void sort(Comparable[] objects) {
1513     if (objects.length > 1)
1514       quickSort(objects, 0, objects.length - 1);
1515   }
1516
1517   public static void sort(int[] list) {
1518     if (list.length > 1)
1519       quickSort(list, 0, list.length - 1);
1520   }
1521
1522   /**
1523    * Sorts an array of objects in place. The given comparer compares pairs of items.
1524    */
1525   public static void sort(Object[] objects, Comparer comparer) {
1526     if (objects.length > 1)
1527       quickSort(objects, 0, objects.length - 1, comparer);
1528   }
1529
1530   /**
1531    * Sorts an array of objects in place, using the sort order given for each item.
1532    */
1533   public static void sort(Object[] objects, int[] sortOrder) {
1534     if (objects.length > 1)
1535       quickSort(objects, 0, objects.length - 1, sortOrder);
1536   }
1537
1538   /**
1539    * Sorts an array of strings in place using quicksort.
1540    */
1541   public static void sort(String[] strings) {
1542     if (strings.length > 1)
1543       quickSort(strings, 0, strings.length - 1);
1544   }
1545
1546   /**
1547    * Sorts an array of Comparable objects, returning a new array with the sorted items. The original array is left untouched.
1548    */
1549   public static Comparable[] sortCopy(Comparable[] objects) {
1550     int len = objects.length;
1551     Comparable[] copy = new Comparable[len];
1552     System.arraycopy(objects, 0, copy, 0, len);
1553     sort(copy);
1554     return copy;
1555   }
1556
1557   /**
1558    * Sorts an array of Strings, returning a new array with the sorted items. The original array is left untouched.
1559    */
1560   public static Object[] sortCopy(Object[] objects, Comparer comparer) {
1561     int len = objects.length;
1562     Object[] copy = new Object[len];
1563     System.arraycopy(objects, 0, copy, 0, len);
1564     sort(copy, comparer);
1565     return copy;
1566   }
1567
1568   /**
1569    * Sorts an array of Strings, returning a new array with the sorted items. The original array is left untouched.
1570    */
1571   public static String[] sortCopy(String[] objects) {
1572     int len = objects.length;
1573     String[] copy = new String[len];
1574     System.arraycopy(objects, 0, copy, 0, len);
1575     sort(copy);
1576     return copy;
1577   }
1578
1579   /**
1580    * Sorts an array of strings in place using quicksort in reverse alphabetical order.
1581    */
1582   public static void sortReverseOrder(String[] strings) {
1583     if (strings.length > 1)
1584       quickSortReverse(strings, 0, strings.length - 1);
1585   }
1586
1587   /**
1588    * Converts a String[] to char[][].
1589    */
1590   public static char[][] toCharArrays(String[] a) {
1591     int len = a.length;
1592     char[][] result = new char[len][];
1593     for (int i = 0; i < len; ++i) {
1594       result[i] = toChars(a[i]);
1595     }
1596     return result;
1597   }
1598
1599   /**
1600    * Converts a String to char[].
1601    */
1602   public static char[] toChars(String s) {
1603     int len = s.length();
1604     char[] chars = new char[len];
1605     s.getChars(0, len, chars, 0);
1606     return chars;
1607   }
1608
1609   /**
1610    * Converts a String to char[][], where segments are separate by '.'.
1611    */
1612   public static char[][] toCompoundChars(String s) {
1613     int len = s.length();
1614     if (len == 0) {
1615       return CharOperation.NO_CHAR_CHAR;
1616     }
1617     int segCount = 1;
1618     for (int off = s.indexOf('.'); off != -1; off = s.indexOf('.', off + 1)) {
1619       ++segCount;
1620     }
1621     char[][] segs = new char[segCount][];
1622     int start = 0;
1623     for (int i = 0; i < segCount; ++i) {
1624       int dot = s.indexOf('.', start);
1625       int end = (dot == -1 ? s.length() : dot);
1626       segs[i] = new char[end - start];
1627       s.getChars(start, end, segs[i], 0);
1628       start = end + 1;
1629     }
1630     return segs;
1631   }
1632
1633   /**
1634    * Converts a char[] to String.
1635    */
1636   public static String toString(char[] c) {
1637     return new String(c);
1638   }
1639
1640   /**
1641    * Converts a char[][] to String, where segments are separated by '.'.
1642    */
1643   public static String toString(char[][] c) {
1644     StringBuffer sb = new StringBuffer();
1645     for (int i = 0, max = c.length; i < max; ++i) {
1646       if (i != 0)
1647         sb.append('.');
1648       sb.append(c[i]);
1649     }
1650     return sb.toString();
1651   }
1652
1653   /**
1654    * Converts a char[][] and a char[] to String, where segments are separated by '.'.
1655    */
1656   public static String toString(char[][] c, char[] d) {
1657     if (c == null)
1658       return new String(d);
1659     StringBuffer sb = new StringBuffer();
1660     for (int i = 0, max = c.length; i < max; ++i) {
1661       sb.append(c[i]);
1662       sb.append('.');
1663     }
1664     sb.append(d);
1665     return sb.toString();
1666   }
1667
1668   /*
1669    * Returns the unresolved type parameter signatures of the given method e.g. {"QString;", "[int", "[[Qjava.util.Vector;"}
1670    */
1671   //    public static String[] typeParameterSignatures(AbstractMethodDeclaration method) {
1672   //            Argument[] args = method.arguments;
1673   //            if (args != null) {
1674   //                    int length = args.length;
1675   //                    String[] signatures = new String[length];
1676   //                    for (int i = 0; i < args.length; i++) {
1677   //                            Argument arg = args[i];
1678   //                            signatures[i] = typeSignature(arg.type);
1679   //                    }
1680   //                    return signatures;
1681   //            }
1682   //            return new String[0];
1683   //    }
1684   /*
1685    * Returns the unresolved type signature of the given type reference, e.g. "QString;", "[int", "[[Qjava.util.Vector;"
1686    */
1687   //    public static String typeSignature(TypeReference type) {
1688   //            char[][] compoundName = type.getTypeName();
1689   //            char[] typeName =CharOperation.concatWith(compoundName, '.');
1690   //            String signature = Signature.createTypeSignature(typeName, false/*don't resolve*/);
1691   //            int dimensions = type.dimensions();
1692   //            if (dimensions > 0) {
1693   //                    signature = Signature.createArraySignature(signature, dimensions);
1694   //            }
1695   //            return signature;
1696   //    }
1697   /*
1698          * Returns the unresolved type signature of the given type reference, 
1699          * e.g. "QString;", "[int", "[[Qjava.util.Vector;"
1700          */
1701         public static String typeSignature(TypeReference type) {
1702                 char[][] compoundName = type.getTypeName();
1703                 char[] typeName =CharOperation.concatWith(compoundName, '.');
1704                 String signature = Signature.createTypeSignature(typeName, false/*don't resolve*/);
1705                 int dimensions = type.dimensions();
1706                 if (dimensions > 0) {
1707                         signature =  Signature.createArraySignature(signature, dimensions);
1708                 }
1709                 return signature;
1710         }
1711   /**
1712    * Asserts that the given method signature is valid.
1713    */
1714   public static void validateMethodSignature(String sig) {
1715     Assert.isTrue(isValidMethodSignature(sig));
1716   }
1717
1718   /**
1719    * Asserts that the given type signature is valid.
1720    */
1721   public static void validateTypeSignature(String sig, boolean allowVoid) {
1722     Assert.isTrue(isValidTypeSignature(sig, allowVoid));
1723   }
1724
1725   public static void verbose(String log) {
1726     verbose(log, System.out);
1727   }
1728
1729   public static synchronized void verbose(String log, PrintStream printStream) {
1730     int start = 0;
1731     do {
1732       int end = log.indexOf('\n', start);
1733       printStream.print(Thread.currentThread());
1734       printStream.print(" "); //$NON-NLS-1$
1735       printStream.print(log.substring(start, end == -1 ? log.length() : end + 1));
1736       start = end + 1;
1737     } while (start != 0);
1738     printStream.println();
1739   }
1740
1741   /**
1742    * Writes a string to the given output stream using UTF-8 encoding in a machine-independent manner.
1743    * <p>
1744    * First, two bytes are written to the output stream as if by the <code>writeShort</code> method giving the number of bytes to
1745    * follow. This value is the number of bytes actually written out, not the length of the string. Following the length, each
1746    * character of the string is output, in sequence, using the UTF-8 encoding for the character.
1747    * 
1748    * @param str
1749    *          a string to be written.
1750    * @return the number of bytes written to the stream.
1751    * @exception IOException
1752    *              if an I/O error occurs.
1753    * @since JDK1.0
1754    */
1755   public static int writeUTF(OutputStream out, char[] str) throws IOException {
1756     int strlen = str.length;
1757     int utflen = 0;
1758     for (int i = 0; i < strlen; i++) {
1759       int c = str[i];
1760       if ((c >= 0x0001) && (c <= 0x007F)) {
1761         utflen++;
1762       } else if (c > 0x07FF) {
1763         utflen += 3;
1764       } else {
1765         utflen += 2;
1766       }
1767     }
1768     if (utflen > 65535)
1769       throw new UTFDataFormatException();
1770     out.write((utflen >>> 8) & 0xFF);
1771     out.write((utflen >>> 0) & 0xFF);
1772     if (strlen == utflen) {
1773       for (int i = 0; i < strlen; i++)
1774         out.write(str[i]);
1775     } else {
1776       for (int i = 0; i < strlen; i++) {
1777         int c = str[i];
1778         if ((c >= 0x0001) && (c <= 0x007F)) {
1779           out.write(c);
1780         } else if (c > 0x07FF) {
1781           out.write(0xE0 | ((c >> 12) & 0x0F));
1782           out.write(0x80 | ((c >> 6) & 0x3F));
1783           out.write(0x80 | ((c >> 0) & 0x3F));
1784         } else {
1785           out.write(0xC0 | ((c >> 6) & 0x1F));
1786           out.write(0x80 | ((c >> 0) & 0x3F));
1787         }
1788       }
1789     }
1790     return utflen + 2; // the number of bytes written to the stream
1791   }
1792 }