Changes:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / Util.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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;
12
13 import java.util.Locale;
14 import java.util.MissingResourceException;
15 import java.util.ResourceBundle;
16 import java.util.StringTokenizer;
17
18 import net.sourceforge.phpdt.core.JavaModelException;
19 import net.sourceforge.phpdt.core.compiler.CharOperation;
20 import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
21 import net.sourceforge.phpdt.internal.corext.Assert;
22 import net.sourceforge.phpeclipse.PHPCore;
23
24 import org.eclipse.core.resources.IResource;
25 import org.eclipse.core.runtime.IPath;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.Status;
28
29 /**
30  * Provides convenient utility methods to other types in this package.
31  */
32 public class Util {
33
34         private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$
35         private final static char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$
36         private static final String ARGUMENTS_DELIMITER = "#"; //$NON-NLS-1$
37         private static final String EMPTY_ARGUMENT = "   "; //$NON-NLS-1$
38
39         public interface Comparable {
40                 /**
41                  * Returns 0 if this and c are equal, >0 if this is greater than c,
42                  * or <0 if this is less than c.
43                  */
44                 int compareTo(Comparable c);
45         }
46
47         public interface Comparer {
48                 /**
49                  * Returns 0 if a and b are equal, >0 if a is greater than b,
50                  * or <0 if a is less than b.
51                  */
52                 int compare(Object a, Object b);
53         }
54         
55         public interface Displayable {
56                 String displayString(Object o);
57         }
58         
59         public static final String[] fgEmptyStringArray = new String[0];
60
61         /**
62          * Are we running JDK 1.1?
63          */
64         private static boolean JDK1_1 = false;
65
66         /* Bundle containing messages */
67         protected static ResourceBundle bundle;
68         private final static String bundleName = "org.eclipse.jdt.internal.core.messages"; //$NON-NLS-1$
69
70         public final static char[] SUFFIX_class = ".class".toCharArray(); //$NON-NLS-1$
71         public final static char[] SUFFIX_CLASS = ".CLASS".toCharArray(); //$NON-NLS-1$
72         public final static char[] SUFFIX_java = ".java".toCharArray(); //$NON-NLS-1$
73         public final static char[] SUFFIX_JAVA = ".JAVA".toCharArray(); //$NON-NLS-1$
74         public final static char[] SUFFIX_jar = ".jar".toCharArray(); //$NON-NLS-1$
75         public final static char[] SUFFIX_JAR = ".JAR".toCharArray(); //$NON-NLS-1$
76         public final static char[] SUFFIX_zip = ".zip".toCharArray(); //$NON-NLS-1$
77         public final static char[] SUFFIX_ZIP = ".ZIP".toCharArray(); //$NON-NLS-1$
78
79         static {
80                 String ver = System.getProperty("java.version"); //$NON-NLS-1$
81                 JDK1_1 = ((ver != null) && ver.startsWith("1.1")); //$NON-NLS-1$
82                 relocalize();
83         }       
84         
85         /**
86          * Lookup the message with the given ID in this catalog 
87          */
88         public static String bind(String id) {
89                 return bind(id, (String[])null);
90         }
91         
92         /**
93          * Lookup the message with the given ID in this catalog and bind its
94          * substitution locations with the given string values.
95          */
96         public static String bind(String id, String[] bindings) {
97                 if (id == null)
98                         return "No message available"; //$NON-NLS-1$
99                 String message = null;
100                 try {
101                         message = bundle.getString(id);
102                 } catch (MissingResourceException e) {
103                         // If we got an exception looking for the message, fail gracefully by just returning
104                         // the id we were looking for.  In most cases this is semi-informative so is not too bad.
105                         return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-2$ //$NON-NLS-1$
106                 }
107                 // for compatibility with MessageFormat which eliminates double quotes in original message
108                 char[] messageWithNoDoubleQuotes =
109                 CharOperation.replace(message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE);
110                 message = new String(messageWithNoDoubleQuotes);
111         
112                 if (bindings == null)
113                         return message;
114         
115                 int length = message.length();
116                 int start = -1;
117                 int end = length;
118                 StringBuffer output = new StringBuffer(80);
119                 while (true) {
120                         if ((end = message.indexOf('{', start)) > -1) {
121                                 output.append(message.substring(start + 1, end));
122                                 if ((start = message.indexOf('}', end)) > -1) {
123                                         int index = -1;
124                                         try {
125                                                 index = Integer.parseInt(message.substring(end + 1, start));
126                                                 output.append(bindings[index]);
127                                         } catch (NumberFormatException nfe) {
128                                                 output.append(message.substring(end + 1, start + 1));
129                                         } catch (ArrayIndexOutOfBoundsException e) {
130                                                 output.append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$
131                                         }
132                                 } else {
133                                         output.append(message.substring(end, length));
134                                         break;
135                                 }
136                         } else {
137                                 output.append(message.substring(start + 1, length));
138                                 break;
139                         }
140                 }
141                 return output.toString();
142         }
143
144         /**
145          * Lookup the message with the given ID in this catalog and bind its
146          * substitution locations with the given string.
147          */
148         public static String bind(String id, String binding) {
149                 return bind(id, new String[] {binding});
150         }
151         
152         /**
153          * Lookup the message with the given ID in this catalog and bind its
154          * substitution locations with the given strings.
155          */
156         public static String bind(String id, String binding1, String binding2) {
157                 return bind(id, new String[] {binding1, binding2});
158         }
159
160         /**
161          * Checks the type signature in String sig, 
162          * starting at start and ending before end (end is not included).
163          * Returns the index of the character immediately after the signature if valid,
164          * or -1 if not valid.
165          */
166         private static int checkTypeSignature(String sig, int start, int end, boolean allowVoid) {
167                 if (start >= end) return -1;
168                 int i = start;
169                 char c = sig.charAt(i++);
170                 int nestingDepth = 0;
171                 while (c == '[') {
172                         ++nestingDepth;
173                         if (i >= end) return -1;
174                         c = sig.charAt(i++);
175                 }
176                 switch (c) {
177                         case 'B':
178                         case 'C': 
179                         case 'D':
180                         case 'F':
181                         case 'I':
182                         case 'J':
183                         case 'S': 
184                         case 'Z':
185                                 break;
186                         case 'V':
187                                 if (!allowVoid) return -1;
188                                 // array of void is not allowed
189                                 if (nestingDepth != 0) return -1;
190                                 break;
191                         case 'L':
192                                 int semicolon = sig.indexOf(';', i);
193                                 // Must have at least one character between L and ;
194                                 if (semicolon <= i || semicolon >= end) return -1;
195                                 i = semicolon + 1;
196                                 break;
197                         default:
198                                 return -1;
199                 }
200                 return i;
201         }
202         
203         /**
204          * Combines two hash codes to make a new one.
205          */
206         public static int combineHashCodes(int hashCode1, int hashCode2) {
207                 return hashCode1 * 17 + hashCode2;
208         }
209         
210         /**
211          * Compares two byte arrays.  
212          * Returns <0 if a byte in a is less than the corresponding byte in b, or if a is shorter, or if a is null.
213          * Returns >0 if a byte in a is greater than the corresponding byte in b, or if a is longer, or if b is null.
214          * Returns 0 if they are equal or both null.
215          */
216         public static int compare(byte[] a, byte[] b) {
217                 if (a == b)
218                         return 0;
219                 if (a == null)
220                         return -1;
221                 if (b == null)
222                         return 1;
223                 int len = Math.min(a.length, b.length);
224                 for (int i = 0; i < len; ++i) {
225                         int diff = a[i] - b[i];
226                         if (diff != 0)
227                                 return diff;
228                 }
229                 if (a.length > len)
230                         return 1;
231                 if (b.length > len)
232                         return -1;
233                 return 0;
234         }
235
236         /**
237          * Compares two char arrays lexicographically. 
238          * The comparison is based on the Unicode value of each character in
239          * the char arrays. 
240          * @return  the value <code>0</code> if a is equal to
241          *          b; a value less than <code>0</code> if a
242          *          is lexicographically less than b; and a
243          *          value greater than <code>0</code> if a is
244          *          lexicographically greater than b.
245          */
246         public static int compare(char[] v1, char[] v2) {
247                 int len1 = v1.length;
248                 int len2 = v2.length;
249                 int n = Math.min(len1, len2);
250                 int i = 0;
251                 while (n-- != 0) {
252                         if (v1[i] != v2[i]) {
253                                 return v1[i] - v2[i];
254                         }
255                         ++i;
256                 }
257                 return len1 - len2;
258         }
259
260         /**
261          * Concatenate two strings with a char in between.
262          * @see #concat(String, String)
263          */
264         public static String concat(String s1, char c, String s2) {
265                 if (s1 == null) s1 = "null"; //$NON-NLS-1$
266                 if (s2 == null) s2 = "null"; //$NON-NLS-1$
267                 int l1 = s1.length();
268                 int l2 = s2.length();
269                 char[] buf = new char[l1 + 1 + l2];
270                 s1.getChars(0, l1, buf, 0);
271                 buf[l1] = c;
272                 s2.getChars(0, l2, buf, l1 + 1);
273                 return new String(buf);
274         }
275         
276         /**
277          * Concatenate two strings.
278          * Much faster than using +, which:
279          *              - creates a StringBuffer,
280          *              - which is synchronized,
281          *              - of default size, so the resulting char array is
282          *        often larger than needed.
283          * This implementation creates an extra char array, since the
284          * String constructor copies its argument, but there's no way around this.
285          */
286         public static String concat(String s1, String s2) {
287                 if (s1 == null) s1 = "null"; //$NON-NLS-1$
288                 if (s2 == null) s2 = "null"; //$NON-NLS-1$
289                 int l1 = s1.length();
290                 int l2 = s2.length();
291                 char[] buf = new char[l1 + l2];
292                 s1.getChars(0, l1, buf, 0);
293                 s2.getChars(0, l2, buf, l1);
294                 return new String(buf);
295         }
296
297         /**
298          * Concatenate three strings.
299          * @see #concat(String, String)
300          */
301         public static String concat(String s1, String s2, String s3) {
302                 if (s1 == null) s1 = "null"; //$NON-NLS-1$
303                 if (s2 == null) s2 = "null"; //$NON-NLS-1$
304                 if (s3 == null) s3 = "null"; //$NON-NLS-1$
305                 int l1 = s1.length();
306                 int l2 = s2.length();
307                 int l3 = s3.length();
308                 char[] buf = new char[l1 + l2 + l3];
309                 s1.getChars(0, l1, buf, 0);
310                 s2.getChars(0, l2, buf, l1);
311                 s3.getChars(0, l3, buf, l1 + l2);
312                 return new String(buf);
313         }
314         
315         /**
316          * Converts a type signature from the IBinaryType representation to the DC representation.
317          */
318         public static String convertTypeSignature(char[] sig) {
319                 return new String(sig).replace('/', '.');
320         }
321
322         /**
323          * Returns true iff str.toLowerCase().endsWith(end.toLowerCase())
324          * implementation is not creating extra strings.
325          */
326         public final static boolean endsWithIgnoreCase(String str, String end) {
327                 
328                 int strLength = str == null ? 0 : str.length();
329                 int endLength = end == null ? 0 : end.length();
330                 
331                 // return false if the string is smaller than the end.
332                 if(endLength > strLength)
333                         return false;
334                         
335                 // return false if any character of the end are
336                 // not the same in lower case.
337                 for(int i = 1 ; i <= endLength; i++){
338                         if(Character.toLowerCase(end.charAt(endLength - i)) != Character.toLowerCase(str.charAt(strLength - i)))
339                                 return false;
340                 }
341                 
342                 return true;
343         }
344
345         /**
346          * Compares two arrays using equals() on the elements.
347          * Either or both arrays may be null.
348          * Returns true if both are null.
349          * Returns false if only one is null.
350          * If both are arrays, returns true iff they have the same length and
351          * all elements are equal.
352          */
353         public static boolean equalArraysOrNull(int[] a, int[] b) {
354                 if (a == b)
355                         return true;
356                 if (a == null || b == null)
357                         return false;
358                 int len = a.length;
359                 if (len != b.length)
360                         return false;
361                 for (int i = 0; i < len; ++i) {
362                         if (a[i] != b[i])
363                                 return false;
364                 }
365                 return true;
366         }
367
368         /**
369          * Compares two arrays using equals() on the elements.
370          * Either or both arrays may be null.
371          * Returns true if both are null.
372          * Returns false if only one is null.
373          * If both are arrays, returns true iff they have the same length and
374          * all elements compare true with equals.
375          */
376         public static boolean equalArraysOrNull(Object[] a, Object[] b) {
377                 if (a == b)     return true;
378                 if (a == null || b == null) return false;
379
380                 int len = a.length;
381                 if (len != b.length) return false;
382                 for (int i = 0; i < len; ++i) {
383                         if (a[i] == null) {
384                                 if (b[i] != null) return false;
385                         } else {
386                                 if (!a[i].equals(b[i])) return false;
387                         }
388                 }
389                 return true;
390         }
391         
392         /**
393          * Compares two String arrays using equals() on the elements.
394          * The arrays are first sorted.
395          * Either or both arrays may be null.
396          * Returns true if both are null.
397          * Returns false if only one is null.
398          * If both are arrays, returns true iff they have the same length and
399          * iff, after sorting both arrays, all elements compare true with equals.
400          * The original arrays are left untouched.
401          */
402         public static boolean equalArraysOrNullSortFirst(String[] a, String[] b) {
403                 if (a == b)     return true;
404                 if (a == null || b == null) return false;
405                 int len = a.length;
406                 if (len != b.length) return false;
407                 if (len >= 2) {  // only need to sort if more than two items
408                         a = sortCopy(a);
409                         b = sortCopy(b);
410                 }
411                 for (int i = 0; i < len; ++i) {
412                         if (!a[i].equals(b[i])) return false;
413                 }
414                 return true;
415         }
416         
417         /**
418          * Compares two arrays using equals() on the elements.
419          * The arrays are first sorted.
420          * Either or both arrays may be null.
421          * Returns true if both are null.
422          * Returns false if only one is null.
423          * If both are arrays, returns true iff they have the same length and
424          * iff, after sorting both arrays, all elements compare true with equals.
425          * The original arrays are left untouched.
426          */
427         public static boolean equalArraysOrNullSortFirst(Comparable[] a, Comparable[] b) {
428                 if (a == b)     return true;
429                 if (a == null || b == null) return false;
430                 int len = a.length;
431                 if (len != b.length) return false;
432                 if (len >= 2) {  // only need to sort if more than two items
433                         a = sortCopy(a);
434                         b = sortCopy(b);
435                 }
436                 for (int i = 0; i < len; ++i) {
437                         if (!a[i].equals(b[i])) return false;
438                 }
439                 return true;
440         }
441         
442         /**
443          * Compares two objects using equals().
444          * Either or both array may be null.
445          * Returns true if both are null.
446          * Returns false if only one is null.
447          * Otherwise, return the result of comparing with equals().
448          */
449         public static boolean equalOrNull(Object a, Object b) {
450                 if (a == b) {
451                         return true;
452                 }
453                 if (a == null || b == null) {
454                         return false;
455                 }
456                 return a.equals(b);
457         }
458         
459         /**
460          * Given a qualified name, extract the last component.
461          * If the input is not qualified, the same string is answered.
462          */
463         public static String extractLastName(String qualifiedName) {
464                 int i = qualifiedName.lastIndexOf('.');
465                 if (i == -1) return qualifiedName;
466                 return qualifiedName.substring(i+1);
467         }
468         
469         /**
470          * Extracts the parameter types from a method signature.
471          */
472         public static String[] extractParameterTypes(char[] sig) {
473                 int count = getParameterCount(sig);
474                 String[] result = new String[count];
475                 if (count == 0)
476                         return result;
477                 int i = CharOperation.indexOf('(', sig) + 1;
478                 count = 0;
479                 int len = sig.length;
480                 int start = i;
481                 for (;;) {
482                         if (i == len)
483                                 break;
484                         char c = sig[i];
485                         if (c == ')')
486                                 break;
487                         if (c == '[') {
488                                 ++i;
489                         } else
490                                 if (c == 'L') {
491                                         i = CharOperation.indexOf(';', sig, i + 1) + 1;
492                                         Assert.isTrue(i != 0);
493                                         result[count++] = convertTypeSignature(CharOperation.subarray(sig, start, i));
494                                         start = i;
495                                 } else {
496                                         ++i;
497                                         result[count++] = convertTypeSignature(CharOperation.subarray(sig, start, i));
498                                         start = i;
499                                 }
500                 }
501                 return result;
502         }
503
504         /**
505          * Extracts the return type from a method signature.
506          */
507         public static String extractReturnType(String sig) {
508                 int i = sig.lastIndexOf(')');
509                 Assert.isTrue(i != -1);
510                 return sig.substring(i+1);      
511         }
512         
513         /**
514          * Finds the first line separator used by the given text.
515          *
516          * @return </code>"\n"</code> or </code>"\r"</code> or  </code>"\r\n"</code>,
517          *                      or <code>null</code> if none found
518          */
519         public static String findLineSeparator(char[] text) {
520                 // find the first line separator
521                 int length = text.length;
522                 if (length > 0) {
523                         char nextChar = text[0];
524                         for (int i = 0; i < length; i++) {
525                                 char currentChar = nextChar;
526                                 nextChar = i < length-1 ? text[i+1] : ' ';
527                                 switch (currentChar) {
528                                         case '\n': return "\n"; //$NON-NLS-1$
529                                         case '\r': return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$
530                                 }
531                         }
532                 }
533                 // not found
534                 return null;
535         }
536         
537         /**
538          * Returns the line separator used by the given buffer.
539          * Uses the given text if none found.
540          *
541          * @return </code>"\n"</code> or </code>"\r"</code> or  </code>"\r\n"</code>
542          */
543         private static String getLineSeparator(char[] text, char[] buffer) {
544                 // search in this buffer's contents first
545                 String lineSeparator = findLineSeparator(buffer);
546                 if (lineSeparator == null) {
547                         // search in the given text
548                         lineSeparator = findLineSeparator(text);
549                         if (lineSeparator == null) {
550                                 // default to system line separator
551                                 return System.getProperty("line.separator");
552                         }
553                 }
554                 return lineSeparator;
555         }
556                 
557         /**
558          * Returns the number of parameter types in a method signature.
559          */
560         public static int getParameterCount(char[] sig) {
561                 int i = CharOperation.indexOf('(', sig) + 1;
562                 Assert.isTrue(i != 0);
563                 int count = 0;
564                 int len = sig.length;
565                 for (;;) {
566                         if (i == len)
567                                 break;
568                         char c = sig[i];
569                         if (c == ')')
570                                 break;
571                         if (c == '[') {
572                                 ++i;
573                         } else
574                                 if (c == 'L') {
575                                         ++count;
576                                         i = CharOperation.indexOf(';', sig, i + 1) + 1;
577                                         Assert.isTrue(i != 0);
578                                 } else {
579                                         ++count;
580                                         ++i;
581                                 }
582                 }
583                 return count;
584         }
585         
586 //      /**
587 //       * Returns the given file's contents as a byte array.
588 //       */
589 //      public static byte[] getResourceContentsAsByteArray(IFile file) throws JavaModelException {
590 //              InputStream stream= null;
591 //              try {
592 //                      stream = new BufferedInputStream(file.getContents(true));
593 //              } catch (CoreException e) {
594 //                      throw new JavaModelException(e);
595 //              }
596 //              try {
597 //                      return org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, -1);
598 //              } catch (IOException e) {
599 //                      throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
600 //              } finally {
601 //                      try {
602 //                              stream.close();
603 //                      } catch (IOException e) {
604 //                      }
605 //              }
606 //      }
607 //      
608 //      /**
609 //       * Returns the given file's contents as a character array.
610 //       */
611 //      public static char[] getResourceContentsAsCharArray(IFile file) throws JavaModelException {
612 //              String encoding = JavaCore.create(file.getProject()).getOption(JavaCore.CORE_ENCODING, true);
613 //              return getResourceContentsAsCharArray(file, encoding);
614 //      }
615 //
616 //      public static char[] getResourceContentsAsCharArray(IFile file, String encoding) throws JavaModelException {
617 //              InputStream stream= null;
618 //              try {
619 //                      stream = new BufferedInputStream(file.getContents(true));
620 //              } catch (CoreException e) {
621 //                      throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
622 //              }
623 //              try {
624 //                      return org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsCharArray(stream, -1, encoding);
625 //              } catch (IOException e) {
626 //                      throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
627 //              } finally {
628 //                      try {
629 //                              stream.close();
630 //                      } catch (IOException e) {
631 //                      }
632 //              }
633 //      }
634 //      
635 //      /**
636 //       * Returns a trimmed version the simples names returned by Signature.
637 //       */
638 //      public static String[] getTrimmedSimpleNames(String name) {
639 //              String[] result = Signature.getSimpleNames(name);
640 //              if (result == null) return null;
641 //              for (int i = 0, length = result.length; i < length; i++) {
642 //                      result[i] = result[i].trim();
643 //              }
644 //              return result;
645 //      }
646         
647         /**
648          * Returns true iff str.toLowerCase().endsWith(".class")
649          * implementation is not creating extra strings.
650          */
651         public final static boolean isClassFileName(String name) {
652                 int nameLength = name == null ? 0 : name.length();
653                 int suffixLength = SUFFIX_CLASS.length;
654                 if (nameLength < suffixLength) return false;
655
656                 for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
657                         char c = name.charAt(offset + i);
658                         if (c != SUFFIX_class[i] && c != SUFFIX_CLASS[i]) return false;
659                 }
660                 return true;            
661         }
662         
663         /*
664          * Returns whether the given java element is exluded from its root's classpath.
665          */
666 //      public static final boolean isExcluded(IJavaElement element) {
667 //              int elementType = element.getElementType();
668 //              switch (elementType) {
669 //                      case IJavaElement.PACKAGE_FRAGMENT:
670 //                              PackageFragmentRoot root = (PackageFragmentRoot)element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
671 //                              IResource resource = element.getResource();
672 //                              return resource != null && Util.isExcluded(resource, root.fullExclusionPatternChars());
673 //                      case IJavaElement.COMPILATION_UNIT:
674 //                              root = (PackageFragmentRoot)element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
675 //                              resource = element.getResource();
676 //                              if (resource != null && Util.isExcluded(resource, root.fullExclusionPatternChars()))
677 //                                      return true;
678 //                              return isExcluded(element.getParent());
679 //                      default:
680 //                              IJavaElement cu = element.getAncestor(IJavaElement.COMPILATION_UNIT);
681 //                              return cu != null && isExcluded(cu);
682 //              }
683 //      }
684         /*
685          * Returns whether the given resource path matches one of the exclusion
686          * patterns.
687          * 
688          * @see IClasspathEntry#getExclusionPatterns
689          */
690         public final static boolean isExcluded(IPath resourcePath, char[][] exclusionPatterns) {
691                 if (exclusionPatterns == null) return false;
692                 char[] path = resourcePath.toString().toCharArray();
693                 for (int i = 0, length = exclusionPatterns.length; i < length; i++)
694                         if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/'))
695                                 return true;
696                 return false;
697         }       
698         
699         /*
700          * Returns whether the given resource matches one of the exclusion patterns.
701          * 
702          * @see IClasspathEntry#getExclusionPatterns
703          */
704         public final static boolean isExcluded(IResource resource, char[][] exclusionPatterns) {
705                 IPath path = resource.getFullPath();
706                 // ensure that folders are only excluded if all of their children are excluded
707                 if (resource.getType() == IResource.FOLDER)
708                         path = path.append("*"); //$NON-NLS-1$
709                 return isExcluded(path, exclusionPatterns);
710         }
711
712         /**
713          * Returns true iff str.toLowerCase().endsWith(".jar" or ".zip")
714          * implementation is not creating extra strings.
715          */
716         public final static boolean isArchiveFileName(String name) {
717                 int nameLength = name == null ? 0 : name.length();
718                 int suffixLength = SUFFIX_JAR.length;
719                 if (nameLength < suffixLength) return false;
720
721                 int i, offset;
722                 for ( i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
723                         char c = name.charAt(offset + i);
724                         if (c != SUFFIX_jar[i] && c != SUFFIX_JAR[i]) break;
725                 }
726                 if (i == suffixLength) return true;             
727                 for ( i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
728                         char c = name.charAt(offset + i);
729                         if (c != SUFFIX_zip[i] && c != SUFFIX_ZIP[i]) return false;
730                 }
731                 return true;
732         }
733         
734         /**
735          * Validate the given compilation unit name.
736          * A compilation unit name must obey the following rules:
737          * <ul>
738          * <li> it must not be null
739          * <li> it must include the <code>".java"</code> suffix
740          * <li> its prefix must be a valid identifier
741          * </ul>
742          * </p>
743          * @param name the name of a compilation unit
744          * @return a status object with code <code>IStatus.OK</code> if
745          *              the given name is valid as a compilation unit name, otherwise a status 
746          *              object indicating what is wrong with the name
747          */
748 //      public static boolean isValidCompilationUnitName(String name) {
749 //              return JavaConventions.validateCompilationUnitName(name).getSeverity() != IStatus.ERROR;
750 //      }
751
752         /**
753          * Validate the given .class file name.
754          * A .class file name must obey the following rules:
755          * <ul>
756          * <li> it must not be null
757          * <li> it must include the <code>".class"</code> suffix
758          * <li> its prefix must be a valid identifier
759          * </ul>
760          * </p>
761          * @param name the name of a .class file
762          * @return a status object with code <code>IStatus.OK</code> if
763          *              the given name is valid as a .class file name, otherwise a status 
764          *              object indicating what is wrong with the name
765          */
766 //      public static boolean isValidClassFileName(String name) {
767 //              return JavaConventions.validateClassFileName(name).getSeverity() != IStatus.ERROR;
768 //      }
769
770         /**
771          * Returns true iff str.toLowerCase().endsWith(".java")
772          * implementation is not creating extra strings.
773          */
774         public final static boolean isJavaFileName(String name) {
775                 int nameLength = name == null ? 0 : name.length();
776                 int suffixLength = SUFFIX_JAVA.length;
777                 if (nameLength < suffixLength) return false;
778
779                 for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
780                         char c = name.charAt(offset + i);
781                         if (c != SUFFIX_java[i] && c != SUFFIX_JAVA[i]) return false;
782                 }
783                 return true;            
784         }
785
786         /**
787          * Returns true if the given method signature is valid,
788          * false if it is not.
789          */
790         public static boolean isValidMethodSignature(String sig) {
791                 int len = sig.length();
792                 if (len == 0) return false;
793                 int i = 0;
794                 char c = sig.charAt(i++);
795                 if (c != '(') return false;
796                 if (i >= len) return false;
797                 while (sig.charAt(i) != ')') {
798                         // Void is not allowed as a parameter type.
799                         i = checkTypeSignature(sig, i, len, false);
800                         if (i == -1) return false;
801                         if (i >= len) return false;
802                 }
803                 ++i;
804                 i = checkTypeSignature(sig, i, len, true);
805                 return i == len;
806         }
807         
808         /**
809          * Returns true if the given type signature is valid,
810          * false if it is not.
811          */
812         public static boolean isValidTypeSignature(String sig, boolean allowVoid) {
813                 int len = sig.length();
814                 return checkTypeSignature(sig, 0, len, allowVoid) == len;
815         }
816         
817         /**
818          * Returns true if the given folder name is valid for a package,
819          * false if it is not.
820          */
821 //      public static boolean isValidFolderNameForPackage(String folderName) {
822 //              return JavaConventions.validateIdentifier(folderName).getSeverity() != IStatus.ERROR;
823 //      }       
824
825         /*
826          * Add a log entry
827          */
828         public static void log(Throwable e, String message) {
829                 Throwable nestedException;
830                 if (e instanceof JavaModelException 
831                                 && (nestedException = ((JavaModelException)e).getException()) != null) {
832                         e = nestedException;
833                 }
834                 IStatus status= new Status(
835                         IStatus.ERROR, 
836                         PHPCore.getPlugin().getDescriptor().getUniqueIdentifier(), 
837                         IStatus.ERROR, 
838                         message, 
839                         e); 
840                 PHPCore.getPlugin().getLog().log(status);
841         }       
842         
843         /**
844          * Normalizes the cariage returns in the given text.
845          * They are all changed  to use the given buffer's line separator.
846          */
847         public static char[] normalizeCRs(char[] text, char[] buffer) {
848                 CharArrayBuffer result = new CharArrayBuffer();
849                 int lineStart = 0;
850                 int length = text.length;
851                 if (length == 0) return text;
852                 String lineSeparator = getLineSeparator(text, buffer);
853                 char nextChar = text[0];
854                 for (int i = 0; i < length; i++) {
855                         char currentChar = nextChar;
856                         nextChar = i < length-1 ? text[i+1] : ' ';
857                         switch (currentChar) {
858                                 case '\n':
859                                         int lineLength = i-lineStart;
860                                         char[] line = new char[lineLength];
861                                         System.arraycopy(text, lineStart, line, 0, lineLength);
862                                         result.append(line);
863                                         result.append(lineSeparator);
864                                         lineStart = i+1;
865                                         break;
866                                 case '\r':
867                                         lineLength = i-lineStart;
868                                         if (lineLength >= 0) {
869                                                 line = new char[lineLength];
870                                                 System.arraycopy(text, lineStart, line, 0, lineLength);
871                                                 result.append(line);
872                                                 result.append(lineSeparator);
873                                                 if (nextChar == '\n') {
874                                                         nextChar = ' ';
875                                                         lineStart = i+2;
876                                                 } else {
877                                                         // when line separator are mixed in the same file
878                                                         // \r might not be followed by a \n. If not, we should increment
879                                                         // lineStart by one and not by two.
880                                                         lineStart = i+1;
881                                                 }
882                                         } else {
883                                                 // when line separator are mixed in the same file
884                                                 // we need to prevent NegativeArraySizeException
885                                                 lineStart = i+1;
886                                         }
887                                         break;
888                         }
889                 }
890                 char[] lastLine;
891                 if (lineStart > 0) {
892                         int lastLineLength = length-lineStart;
893                         if (lastLineLength > 0) {
894                                 lastLine = new char[lastLineLength];
895                                 System.arraycopy(text, lineStart, lastLine, 0, lastLineLength);
896                                 result.append(lastLine);
897                         }
898                         return result.getContents();
899                 } else {
900                         return text;
901                 }
902         }
903
904         /**
905          * Normalizes the cariage returns in the given text.
906          * They are all changed  to use given buffer's line sepatator.
907          */
908         public static String normalizeCRs(String text, String buffer) {
909                 return new String(normalizeCRs(text.toCharArray(), buffer.toCharArray()));
910         }
911
912         /**
913          * Sort the objects in the given collection using the given sort order.
914          */
915         private static void quickSort(Object[] sortedCollection, int left, int right, int[] sortOrder) {
916                 int original_left = left;
917                 int original_right = right;
918                 int mid = sortOrder[ (left + right) / 2];
919                 do {
920                         while (sortOrder[left] < mid) {
921                                 left++;
922                         }
923                         while (mid < sortOrder[right]) {
924                                 right--;
925                         }
926                         if (left <= right) {
927                                 Object tmp = sortedCollection[left];
928                                 sortedCollection[left] = sortedCollection[right];
929                                 sortedCollection[right] = tmp;
930                                 int tmp2 = sortOrder[left];
931                                 sortOrder[left] = sortOrder[right];
932                                 sortOrder[right] = tmp2;
933                                 left++;
934                                 right--;
935                         }
936                 } while (left <= right);
937                 if (original_left < right) {
938                         quickSort(sortedCollection, original_left, right, sortOrder);
939                 }
940                 if (left < original_right) {
941                         quickSort(sortedCollection, left, original_right, sortOrder);
942                 }
943         }
944
945         /**
946          * Sort the objects in the given collection using the given comparer.
947          */
948         private static void quickSort(Object[] sortedCollection, int left, int right, Comparer comparer) {
949                 int original_left = left;
950                 int original_right = right;
951                 Object mid = sortedCollection[ (left + right) / 2];
952                 do {
953                         while (comparer.compare(sortedCollection[left], mid) < 0) {
954                                 left++;
955                         }
956                         while (comparer.compare(mid, sortedCollection[right]) < 0) {
957                                 right--;
958                         }
959                         if (left <= right) {
960                                 Object tmp = sortedCollection[left];
961                                 sortedCollection[left] = sortedCollection[right];
962                                 sortedCollection[right] = tmp;
963                                 left++;
964                                 right--;
965                         }
966                 } while (left <= right);
967                 if (original_left < right) {
968                         quickSort(sortedCollection, original_left, right, comparer);
969                 }
970                 if (left < original_right) {
971                         quickSort(sortedCollection, left, original_right, comparer);
972                 }
973         }
974
975         /**
976          * Sort the strings in the given collection.
977          */
978         private static void quickSort(String[] sortedCollection, int left, int right) {
979                 int original_left = left;
980                 int original_right = right;
981                 String mid = sortedCollection[ (left + right) / 2];
982                 do {
983                         while (sortedCollection[left].compareTo(mid) < 0) {
984                                 left++;
985                         }
986                         while (mid.compareTo(sortedCollection[right]) < 0) {
987                                 right--;
988                         }
989                         if (left <= right) {
990                                 String tmp = sortedCollection[left];
991                                 sortedCollection[left] = sortedCollection[right];
992                                 sortedCollection[right] = tmp;
993                                 left++;
994                                 right--;
995                         }
996                 } while (left <= right);
997                 if (original_left < right) {
998                         quickSort(sortedCollection, original_left, right);
999                 }
1000                 if (left < original_right) {
1001                         quickSort(sortedCollection, left, original_right);
1002                 }
1003         }
1004
1005         /**
1006          * Converts the given relative path into a package name.
1007          * Returns null if the path is not a valid package name.
1008          */
1009 //      public static String packageName(IPath pkgPath) {
1010 //              StringBuffer pkgName = new StringBuffer(IPackageFragment.DEFAULT_PACKAGE_NAME);
1011 //              for (int j = 0, max = pkgPath.segmentCount(); j < max; j++) {
1012 //                      String segment = pkgPath.segment(j);
1013 //                      if (!isValidFolderNameForPackage(segment)) {
1014 //                              return null;
1015 //                      }
1016 //                      pkgName.append(segment);
1017 //                      if (j < pkgPath.segmentCount() - 1) {
1018 //                              pkgName.append("." ); //$NON-NLS-1$
1019 //                      }
1020 //              }
1021 //              return pkgName.toString();
1022 //      }
1023
1024         /**
1025          * Sort the comparable objects in the given collection.
1026          */
1027         private static void quickSort(Comparable[] sortedCollection, int left, int right) {
1028                 int original_left = left;
1029                 int original_right = right;
1030                 Comparable mid = sortedCollection[ (left + right) / 2];
1031                 do {
1032                         while (sortedCollection[left].compareTo(mid) < 0) {
1033                                 left++;
1034                         }
1035                         while (mid.compareTo(sortedCollection[right]) < 0) {
1036                                 right--;
1037                         }
1038                         if (left <= right) {
1039                                 Comparable tmp = sortedCollection[left];
1040                                 sortedCollection[left] = sortedCollection[right];
1041                                 sortedCollection[right] = tmp;
1042                                 left++;
1043                                 right--;
1044                         }
1045                 } while (left <= right);
1046                 if (original_left < right) {
1047                         quickSort(sortedCollection, original_left, right);
1048                 }
1049                 if (left < original_right) {
1050                         quickSort(sortedCollection, left, original_right);
1051                 }
1052         }
1053
1054         /**
1055          * Sort the strings in the given collection in reverse alphabetical order.
1056          */
1057         private static void quickSortReverse(String[] sortedCollection, int left, int right) {
1058                 int original_left = left;
1059                 int original_right = right;
1060                 String mid = sortedCollection[ (left + right) / 2];
1061                 do {
1062                         while (sortedCollection[left].compareTo(mid) > 0) {
1063                                 left++;
1064                         }
1065                         while (mid.compareTo(sortedCollection[right]) > 0) {
1066                                 right--;
1067                         }
1068                         if (left <= right) {
1069                                 String tmp = sortedCollection[left];
1070                                 sortedCollection[left] = sortedCollection[right];
1071                                 sortedCollection[right] = tmp;
1072                                 left++;
1073                                 right--;
1074                         }
1075                 } while (left <= right);
1076                 if (original_left < right) {
1077                         quickSortReverse(sortedCollection, original_left, right);
1078                 }
1079                 if (left < original_right) {
1080                         quickSortReverse(sortedCollection, left, original_right);
1081                 }
1082         }
1083
1084         /**
1085          * Sorts an array of objects in place, using the sort order given for each item.
1086          */
1087         public static void sort(Object[] objects, int[] sortOrder) {
1088                 if (objects.length > 1)
1089                         quickSort(objects, 0, objects.length - 1, sortOrder);
1090         }
1091
1092         /**
1093          * Sorts an array of objects in place.
1094          * The given comparer compares pairs of items.
1095          */
1096         public static void sort(Object[] objects, Comparer comparer) {
1097                 if (objects.length > 1)
1098                         quickSort(objects, 0, objects.length - 1, comparer);
1099         }
1100
1101         /**
1102          * Sorts an array of strings in place using quicksort.
1103          */
1104         public static void sort(String[] strings) {
1105                 if (strings.length > 1)
1106                         quickSort(strings, 0, strings.length - 1);
1107         }
1108
1109         /**
1110          * Sorts an array of Comparable objects in place.
1111          */
1112         public static void sort(Comparable[] objects) {
1113                 if (objects.length > 1)
1114                         quickSort(objects, 0, objects.length - 1);
1115         }
1116
1117         /**
1118          * Sorts an array of Strings, returning a new array
1119          * with the sorted items.  The original array is left untouched.
1120          */
1121         public static Object[] sortCopy(Object[] objects, Comparer comparer) {
1122                 int len = objects.length;
1123                 Object[] copy = new Object[len];
1124                 System.arraycopy(objects, 0, copy, 0, len);
1125                 sort(copy, comparer);
1126                 return copy;
1127         }
1128
1129         /**
1130          * Sorts an array of Strings, returning a new array
1131          * with the sorted items.  The original array is left untouched.
1132          */
1133         public static String[] sortCopy(String[] objects) {
1134                 int len = objects.length;
1135                 String[] copy = new String[len];
1136                 System.arraycopy(objects, 0, copy, 0, len);
1137                 sort(copy);
1138                 return copy;
1139         }
1140
1141         /**
1142          * Sorts an array of Comparable objects, returning a new array
1143          * with the sorted items.  The original array is left untouched.
1144          */
1145         public static Comparable[] sortCopy(Comparable[] objects) {
1146                 int len = objects.length;
1147                 Comparable[] copy = new Comparable[len];
1148                 System.arraycopy(objects, 0, copy, 0, len);
1149                 sort(copy);
1150                 return copy;
1151         }
1152
1153         /**
1154          * Sorts an array of strings in place using quicksort
1155          * in reverse alphabetical order.
1156          */
1157         public static void sortReverseOrder(String[] strings) {
1158                 if (strings.length > 1)
1159                         quickSortReverse(strings, 0, strings.length - 1);
1160         }
1161
1162         /**
1163          * Converts a String[] to char[][].
1164          */
1165         public static char[][] toCharArrays(String[] a) {
1166                 int len = a.length;
1167                 char[][] result = new char[len][];
1168                 for (int i = 0; i < len; ++i) {
1169                         result[i] = toChars(a[i]);
1170                 }
1171                 return result;
1172         }
1173
1174         /**
1175          * Converts a String to char[].
1176          */
1177         public static char[] toChars(String s) {
1178                 int len = s.length();
1179                 char[] chars = new char[len];
1180                 s.getChars(0, len, chars, 0);
1181                 return chars;
1182         }
1183
1184         /**
1185          * Converts a String to char[][], where segments are separate by '.'.
1186          */
1187         public static char[][] toCompoundChars(String s) {
1188                 int len = s.length();
1189                 if (len == 0) {
1190                         return CharOperation.NO_CHAR_CHAR;
1191                 }
1192                 int segCount = 1;
1193                 for (int off = s.indexOf('.'); off != -1; off = s.indexOf('.', off + 1)) {
1194                         ++segCount;
1195                 }
1196                 char[][] segs = new char[segCount][];
1197                 int start = 0;
1198                 for (int i = 0; i < segCount; ++i) {
1199                         int dot = s.indexOf('.', start);
1200                         int end = (dot == -1 ? s.length() : dot);
1201                         segs[i] = new char[end - start];
1202                         s.getChars(start, end, segs[i], 0);
1203                         start = end + 1;
1204                 }
1205                 return segs;
1206         }
1207
1208         /**
1209          * Converts a char[][] to String, where segments are separated by '.'.
1210          */
1211         public static String toString(char[][] c) {
1212                 StringBuffer sb = new StringBuffer();
1213                 for (int i = 0, max = c.length; i < max; ++i) {
1214                         if (i != 0) sb.append('.');
1215                         sb.append(c[i]);
1216                 }
1217                 return sb.toString();
1218         }
1219
1220         /**
1221          * Converts a char[][] and a char[] to String, where segments are separated by '.'.
1222          */
1223         public static String toString(char[][] c, char[] d) {
1224                 if (c == null) return new String(d);
1225                 StringBuffer sb = new StringBuffer();
1226                 for (int i = 0, max = c.length; i < max; ++i) {
1227                         sb.append(c[i]);
1228                         sb.append('.');
1229                 }
1230                 sb.append(d);
1231                 return sb.toString();
1232         }
1233
1234         /**
1235          * Converts a char[] to String.
1236          */
1237         public static String toString(char[] c) {
1238                 return new String(c);
1239         }
1240
1241         /**
1242          * Converts an array of Objects into String.
1243          */
1244         public static String toString(Object[] objects) {
1245                 return toString(objects, 
1246                         new Displayable(){ 
1247                                 public String displayString(Object o) { 
1248                                         if (o == null) return "null"; //$NON-NLS-1$
1249                                         return o.toString(); 
1250                                 }
1251                         });
1252         }
1253
1254         /**
1255          * Converts an array of Objects into String.
1256          */
1257         public static String toString(Object[] objects, Displayable renderer) {
1258                 if (objects == null) return ""; //$NON-NLS-1$
1259                 StringBuffer buffer = new StringBuffer(10);
1260                 for (int i = 0; i < objects.length; i++){
1261                         if (i > 0) buffer.append(", "); //$NON-NLS-1$
1262                         buffer.append(renderer.displayString(objects[i]));
1263                 }
1264                 return buffer.toString();
1265         }
1266         
1267         /**
1268          * Asserts that the given method signature is valid.
1269          */
1270         public static void validateMethodSignature(String sig) {
1271                 Assert.isTrue(isValidMethodSignature(sig));
1272         }
1273
1274         /**
1275          * Asserts that the given type signature is valid.
1276          */
1277         public static void validateTypeSignature(String sig, boolean allowVoid) {
1278                 Assert.isTrue(isValidTypeSignature(sig, allowVoid));
1279         }
1280
1281         /**
1282          * Creates a NLS catalog for the given locale.
1283          */
1284         public static void relocalize() {
1285                 try {
1286                         bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
1287                 } catch(MissingResourceException e) {
1288                         System.out.println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$
1289                         throw e;
1290                 }
1291         }
1292         
1293         /**
1294          * Put all the arguments in one String.
1295          */
1296         public static String getProblemArgumentsForMarker(String[] arguments){
1297                 StringBuffer args = new StringBuffer(10);
1298                 
1299                 args.append(arguments.length);
1300                 args.append(':');
1301                 
1302                         
1303                 for (int j = 0; j < arguments.length; j++) {
1304                         if(j != 0)
1305                                 args.append(ARGUMENTS_DELIMITER);
1306                         
1307                         if(arguments[j].length() == 0) {
1308                                 args.append(EMPTY_ARGUMENT);
1309                         } else {                        
1310                                 args.append(arguments[j]);
1311                         }
1312                 }
1313                 
1314                 return args.toString();
1315         }
1316         
1317         /**
1318          * Separate all the arguments of a String made by getProblemArgumentsForMarker
1319          */
1320         public static String[] getProblemArgumentsFromMarker(String argumentsString){
1321                 if (argumentsString == null) return null;
1322                 int index = argumentsString.indexOf(':');
1323                 if(index == -1)
1324                         return null;
1325                 
1326                 int length = argumentsString.length();
1327                 int numberOfArg;
1328                 try{
1329                         numberOfArg = Integer.parseInt(argumentsString.substring(0 , index));
1330                 } catch (NumberFormatException e) {
1331                         return null;
1332                 }
1333                 argumentsString = argumentsString.substring(index + 1, length);
1334                 
1335                 String[] args = new String[length];
1336                 int count = 0;
1337                 
1338                 StringTokenizer tokenizer = new StringTokenizer(argumentsString, ARGUMENTS_DELIMITER);
1339                 while(tokenizer.hasMoreTokens()) {
1340                         String argument = tokenizer.nextToken();
1341                         if(argument.equals(EMPTY_ARGUMENT))
1342                                 argument = "";  //$NON-NLS-1$
1343                         args[count++] = argument;
1344                 }
1345                 
1346                 if(count != numberOfArg)
1347                         return null;
1348                 
1349                 System.arraycopy(args, 0, args = new String[count], 0, count);
1350                 return args;
1351         }
1352         
1353 }