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