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