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