removed Makefile; lifted repo/org.ibex.tool/src/ to src/
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / util / Util.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.util;
12
13 import java.io.BufferedInputStream;
14 import java.io.ByteArrayInputStream;
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InputStreamReader;
20 import java.util.Locale;
21 import java.util.MissingResourceException;
22 import java.util.ResourceBundle;
23 import java.util.zip.ZipEntry;
24 import java.util.zip.ZipFile;
25 import org.eclipse.jdt.core.compiler.CharOperation;
26
27 public class Util implements SuffixConstants {
28
29         public interface Displayable {
30                 String displayString(Object o);
31         }
32
33         public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
34         public static char[] LINE_SEPARATOR_CHARS = LINE_SEPARATOR.toCharArray();
35         
36         private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$
37         private final static char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$
38         private static final int DEFAULT_READING_SIZE = 8192;
39
40         /* Bundle containing messages */
41         protected static ResourceBundle bundle;
42         private final static String bundleName =
43                 "org.eclipse.jdt.internal.compiler.util.messages"; //$NON-NLS-1$
44         static {
45                 relocalize();
46         }
47         /**
48          * Lookup the message with the given ID in this catalog and bind its
49          * substitution locations with the given strings.
50          */
51         public static String bind(String id, String binding1, String binding2) {
52                 return bind(id, new String[] { binding1, binding2 });
53         }
54         /**
55          * Lookup the message with the given ID in this catalog and bind its
56          * substitution locations with the given string.
57          */
58         public static String bind(String id, String binding) {
59                 return bind(id, new String[] { binding });
60         }
61         /**
62          * Lookup the message with the given ID in this catalog and bind its
63          * substitution locations with the given string values.
64          */
65         public static String bind(String id, String[] bindings) {
66                 if (id == null)
67                         return "No message available"; //$NON-NLS-1$
68                 String message = null;
69                 try {
70                         message = bundle.getString(id);
71                 } catch (MissingResourceException e) {
72                         // If we got an exception looking for the message, fail gracefully by just returning
73                         // the id we were looking for.  In most cases this is semi-informative so is not too bad.
74                         return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-2$ //$NON-NLS-1$
75                 }
76                 // for compatibility with MessageFormat which eliminates double quotes in original message
77                 char[] messageWithNoDoubleQuotes =
78                         CharOperation.replace(message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE);
79         
80                 if (bindings == null) return new String(messageWithNoDoubleQuotes);
81         
82                 int length = messageWithNoDoubleQuotes.length;
83                 int start = 0;
84                 int end = length;
85                 StringBuffer output = null;
86                 while (true) {
87                         if ((end = CharOperation.indexOf('{', messageWithNoDoubleQuotes, start)) > -1) {
88                                 if (output == null) output = new StringBuffer(length+bindings.length*20);
89                                 output.append(messageWithNoDoubleQuotes, start, end - start);
90                                 if ((start = CharOperation.indexOf('}', messageWithNoDoubleQuotes, end + 1)) > -1) {
91                                         int index = -1;
92                                         String argId = new String(messageWithNoDoubleQuotes, end + 1, start - end - 1);
93                                         try {
94                                                 index = Integer.parseInt(argId);
95                                                 output.append(bindings[index]);
96                                         } catch (NumberFormatException nfe) { // could be nested message ID {compiler.name}
97                                                 boolean done = false;
98                                                 if (!id.equals(argId)) {
99                                                         String argMessage = null;
100                                                         try {
101                                                                 argMessage = bundle.getString(argId);
102                                                                 output.append(argMessage);
103                                                                 done = true;
104                                                         } catch (MissingResourceException e) {
105                                                                 // unable to bind argument, ignore (will leave argument in)
106                                                         }
107                                                 }
108                                                 if (!done) output.append(messageWithNoDoubleQuotes, end + 1, start - end);
109                                         } catch (ArrayIndexOutOfBoundsException e) {
110                                                 output.append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$
111                                         }
112                                         start++;
113                                 } else {
114                                         output.append(messageWithNoDoubleQuotes, end, length);
115                                         break;
116                                 }
117                         } else {
118                                 if (output == null) return new String(messageWithNoDoubleQuotes);
119                                 output.append(messageWithNoDoubleQuotes, start, length - start);
120                                 break;
121                         }
122                 }
123                 return output.toString();
124         }
125         /**
126          * Lookup the message with the given ID in this catalog 
127          */
128         public static String bind(String id) {
129                 return bind(id, (String[]) null);
130         }
131         /**
132          * Creates a NLS catalog for the given locale.
133          */
134         public static void relocalize() {
135                 try {
136                         bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
137                 } catch(MissingResourceException e) {
138                         System.out.println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$
139                         throw e;
140                 }
141         }
142         /**
143          * Returns the given bytes as a char array using a given encoding (null means platform default).
144          */
145         public static char[] bytesToChar(byte[] bytes, String encoding) throws IOException {
146
147                 return getInputStreamAsCharArray(new ByteArrayInputStream(bytes), bytes.length, encoding);
148
149         }
150         /**
151          * Returns the contents of the given file as a byte array.
152          * @throws IOException if a problem occured reading the file.
153          */
154         public static byte[] getFileByteContent(File file) throws IOException {
155                 InputStream stream = null;
156                 try {
157                         stream = new BufferedInputStream(new FileInputStream(file));
158                         return getInputStreamAsByteArray(stream, (int) file.length());
159                 } finally {
160                         if (stream != null) {
161                                 try {
162                                         stream.close();
163                                 } catch (IOException e) {
164                                         // ignore
165                                 }
166                         }
167                 }
168         }
169         /**
170          * Returns the contents of the given file as a char array.
171          * When encoding is null, then the platform default one is used
172          * @throws IOException if a problem occured reading the file.
173          */
174         public static char[] getFileCharContent(File file, String encoding) throws IOException {
175                 InputStream stream = null;
176                 try {
177                         stream = new BufferedInputStream(new FileInputStream(file));
178                         return getInputStreamAsCharArray(stream, (int) file.length(), encoding);
179                 } finally {
180                         if (stream != null) {
181                                 try {
182                                         stream.close();
183                                 } catch (IOException e) {
184                                         // ignore
185                                 }
186                         }
187                 }
188         }
189         /*
190          * NIO support to get input stream as byte array.
191          * Not used as with JDK 1.4.2 this support is slower than standard IO one...
192          * Keep it as comment for future in case of next JDK versions improve performance
193          * in this area...
194          *
195         public static byte[] getInputStreamAsByteArray(FileInputStream stream, int length)
196                 throws IOException {
197
198                 FileChannel channel = stream.getChannel();
199                 int size = (int)channel.size();
200                 if (length >= 0 && length < size) size = length;
201                 byte[] contents = new byte[size];
202                 ByteBuffer buffer = ByteBuffer.wrap(contents);
203                 channel.read(buffer);
204                 return contents;
205         }
206         */
207         /**
208          * Returns the given input stream's contents as a byte array.
209          * If a length is specified (ie. if length != -1), only length bytes
210          * are returned. Otherwise all bytes in the stream are returned.
211          * Note this doesn't close the stream.
212          * @throws IOException if a problem occured reading the stream.
213          */
214         public static byte[] getInputStreamAsByteArray(InputStream stream, int length)
215                 throws IOException {
216                 byte[] contents;
217                 if (length == -1) {
218                         contents = new byte[0];
219                         int contentsLength = 0;
220                         int amountRead = -1;
221                         do {
222                                 int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);  // read at least 8K
223                                 
224                                 // resize contents if needed
225                                 if (contentsLength + amountRequested > contents.length) {
226                                         System.arraycopy(
227                                                 contents,
228                                                 0,
229                                                 contents = new byte[contentsLength + amountRequested],
230                                                 0,
231                                                 contentsLength);
232                                 }
233
234                                 // read as many bytes as possible
235                                 amountRead = stream.read(contents, contentsLength, amountRequested);
236
237                                 if (amountRead > 0) {
238                                         // remember length of contents
239                                         contentsLength += amountRead;
240                                 }
241                         } while (amountRead != -1); 
242
243                         // resize contents if necessary
244                         if (contentsLength < contents.length) {
245                                 System.arraycopy(
246                                         contents,
247                                         0,
248                                         contents = new byte[contentsLength],
249                                         0,
250                                         contentsLength);
251                         }
252                 } else {
253                         contents = new byte[length];
254                         int len = 0;
255                         int readSize = 0;
256                         while ((readSize != -1) && (len != length)) {
257                                 // See PR 1FMS89U
258                                 // We record first the read size. In this case len is the actual read size.
259                                 len += readSize;
260                                 readSize = stream.read(contents, len, length - len);
261                         }
262                 }
263
264                 return contents;
265         }
266         /*
267          * NIO support to get input stream as char array.
268          * Not used as with JDK 1.4.2 this support is slower than standard IO one...
269          * Keep it as comment for future in case of next JDK versions improve performance
270          * in this area...
271         public static char[] getInputStreamAsCharArray(FileInputStream stream, int length, String encoding)
272                 throws IOException {
273                 
274                 FileChannel channel = stream.getChannel();
275                 int size = (int)channel.size();
276                 if (length >= 0 && length < size) size = length;
277                 Charset charset = encoding==null?systemCharset:Charset.forName(encoding);
278                 if (charset != null) {
279                         MappedByteBuffer bbuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
280                     CharsetDecoder decoder = charset.newDecoder();
281                     CharBuffer buffer = decoder.decode(bbuffer);
282                     char[] contents = new char[buffer.limit()];
283                     buffer.get(contents);
284                     return contents;
285                 }
286                 throw new UnsupportedCharsetException(SYSTEM_FILE_ENCODING);
287         }
288         */
289         /**
290          * Returns the given input stream's contents as a character array.
291          * If a length is specified (ie. if length != -1), only length chars
292          * are returned. Otherwise all chars in the stream are returned.
293          * Note this doesn't close the stream.
294          * @throws IOException if a problem occured reading the stream.
295          */
296         public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding)
297                 throws IOException {
298                 InputStreamReader reader = null;
299                 reader = encoding == null
300                                         ? new InputStreamReader(stream)
301                                         : new InputStreamReader(stream, encoding);
302                 char[] contents;
303                 if (length == -1) {
304                         contents = CharOperation.NO_CHAR;
305                         int contentsLength = 0;
306                         int amountRead = -1;
307                         do {
308                                 int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);  // read at least 8K
309
310                                 // resize contents if needed
311                                 if (contentsLength + amountRequested > contents.length) {
312                                         System.arraycopy(
313                                                 contents,
314                                                 0,
315                                                 contents = new char[contentsLength + amountRequested],
316                                                 0,
317                                                 contentsLength);
318                                 }
319
320                                 // read as many chars as possible
321                                 amountRead = reader.read(contents, contentsLength, amountRequested);
322
323                                 if (amountRead > 0) {
324                                         // remember length of contents
325                                         contentsLength += amountRead;
326                                 }
327                         } while (amountRead != -1);
328
329                         // Do not keep first character for UTF-8 BOM encoding
330                         int start = 0;
331                         if (contentsLength > 0 && "UTF-8".equals(encoding)) { //$NON-NLS-1$
332                                 if (contents[0] == 0xFEFF) { // if BOM char then skip
333                                         contentsLength--;
334                                         start = 1;
335                                 }
336                         }
337                         // resize contents if necessary
338                         if (contentsLength < contents.length) {
339                                 System.arraycopy(
340                                         contents,
341                                         start,
342                                         contents = new char[contentsLength],
343                                         0,
344                                         contentsLength);
345                         }
346                 } else {
347                         contents = new char[length];
348                         int len = 0;
349                         int readSize = 0;
350                         while ((readSize != -1) && (len != length)) {
351                                 // See PR 1FMS89U
352                                 // We record first the read size. In this case len is the actual read size.
353                                 len += readSize;
354                                 readSize = reader.read(contents, len, length - len);
355                         }
356                         // Do not keep first character for UTF-8 BOM encoding
357                         int start = 0;
358                         if (length > 0 && "UTF-8".equals(encoding)) { //$NON-NLS-1$
359                                 if (contents[0] == 0xFEFF) { // if BOM char then skip
360                                         len--;
361                                         start = 1;
362                                 }
363                         }
364                         // See PR 1FMS89U
365                         // Now we need to resize in case the default encoding used more than one byte for each
366                         // character
367                         if (len != length)
368                                 System.arraycopy(contents, start, (contents = new char[len]), 0, len);
369                 }
370
371                 return contents;
372         }
373         
374         /**
375          * Returns the contents of the given zip entry as a byte array.
376          * @throws IOException if a problem occured reading the zip entry.
377          */
378         public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip)
379                 throws IOException {
380
381                 InputStream stream = null;
382                 try {
383                         stream = new BufferedInputStream(zip.getInputStream(ze));
384                         return getInputStreamAsByteArray(stream, (int) ze.getSize());
385                 } finally {
386                         if (stream != null) {
387                                 try {
388                                         stream.close();
389                                 } catch (IOException e) {
390                                         // ignore
391                                 }
392                         }
393                 }
394         }
395         /**
396          * Returns true iff str.toLowerCase().endsWith(".jar") || str.toLowerCase().endsWith(".zip")
397          * implementation is not creating extra strings.
398          */
399         public final static boolean isArchiveFileName(String name) {
400                 int nameLength = name == null ? 0 : name.length();
401                 int suffixLength = SUFFIX_JAR.length;
402                 if (nameLength < suffixLength) return false;
403
404                 // try to match as JAR file
405                 for (int i = 0; i < suffixLength; i++) {
406                         char c = name.charAt(nameLength - i - 1);
407                         int suffixIndex = suffixLength - i - 1;
408                         if (c != SUFFIX_jar[suffixIndex] && c != SUFFIX_JAR[suffixIndex]) {
409
410                                 // try to match as ZIP file
411                                 suffixLength = SUFFIX_ZIP.length;
412                                 if (nameLength < suffixLength) return false;
413                                 for (int j = 0; j < suffixLength; j++) {
414                                         c = name.charAt(nameLength - j - 1);
415                                         suffixIndex = suffixLength - j - 1;
416                                         if (c != SUFFIX_zip[suffixIndex] && c != SUFFIX_ZIP[suffixIndex]) return false;
417                                 }
418                                 return true;
419                         }
420                 }
421                 return true;            
422         }       
423         /**
424          * Returns true iff str.toLowerCase().endsWith(".class")
425          * implementation is not creating extra strings.
426          */
427         public final static boolean isClassFileName(String name) {
428                 int nameLength = name == null ? 0 : name.length();
429                 int suffixLength = SUFFIX_CLASS.length;
430                 if (nameLength < suffixLength) return false;
431
432                 for (int i = 0; i < suffixLength; i++) {
433                         char c = name.charAt(nameLength - i - 1);
434                         int suffixIndex = suffixLength - i - 1;
435                         if (c != SUFFIX_class[suffixIndex] && c != SUFFIX_CLASS[suffixIndex]) return false;
436                 }
437                 return true;            
438         }       
439         /**
440          * Returns true iff str.toLowerCase().endsWith(".class")
441          * implementation is not creating extra strings.
442          */
443         public final static boolean isClassFileName(char[] name) {
444                 int nameLength = name == null ? 0 : name.length;
445                 int suffixLength = SUFFIX_CLASS.length;
446                 if (nameLength < suffixLength) return false;
447
448                 for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
449                         char c = name[offset + i];
450                         if (c != SUFFIX_class[i] && c != SUFFIX_CLASS[i]) return false;
451                 }
452                 return true;            
453         }       
454         /**
455          * Returns true iff str.toLowerCase().endsWith(".java")
456          * implementation is not creating extra strings.
457          */
458         public final static boolean isJavaFileName(String name) {
459                 int nameLength = name == null ? 0 : name.length();
460                 int suffixLength = SUFFIX_JAVA.length;
461                 if (nameLength < suffixLength) return false;
462
463                 for (int i = 0; i < suffixLength; i++) {
464                         char c = name.charAt(nameLength - i - 1);
465                         int suffixIndex = suffixLength - i - 1;
466                         if (c != SUFFIX_java[suffixIndex] && c != SUFFIX_JAVA[suffixIndex]) return false;
467                 }
468                 return true;            
469         }
470         /**
471          * Returns true iff str.toLowerCase().endsWith(".java")
472          * implementation is not creating extra strings.
473          */
474         public final static boolean isJavaFileName(char[] name) {
475                 int nameLength = name == null ? 0 : name.length;
476                 int suffixLength = SUFFIX_JAVA.length;
477                 if (nameLength < suffixLength) return false;
478
479                 for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
480                         char c = name[offset + i];
481                         if (c != SUFFIX_java[i] && c != SUFFIX_JAVA[i]) return false;
482                 }
483                 return true;            
484         }
485         /**
486          * Converts an array of Objects into String.
487          */
488         public static String toString(Object[] objects) {
489                 return toString(objects, 
490                         new Displayable(){ 
491                                 public String displayString(Object o) { 
492                                         if (o == null) return "null"; //$NON-NLS-1$
493                                         return o.toString(); 
494                                 }
495                         });
496         }
497
498         /**
499          * Converts an array of Objects into String.
500          */
501         public static String toString(Object[] objects, Displayable renderer) {
502                 if (objects == null) return ""; //$NON-NLS-1$
503                 StringBuffer buffer = new StringBuffer(10);
504                 for (int i = 0; i < objects.length; i++){
505                         if (i > 0) buffer.append(", "); //$NON-NLS-1$
506                         buffer.append(renderer.displayString(objects[i]));
507                 }
508                 return buffer.toString();
509         }
510
511         /**
512          * Converts a boolean value into Boolean.
513          * @param bool The boolean to convert
514          * @return The corresponding Boolean object (TRUE or FALSE).
515          */
516         public static Boolean toBoolean(boolean bool) {
517                 if (bool) {
518                         return Boolean.TRUE;
519                 } else {
520                         return Boolean.FALSE;
521                 }
522         }
523 }