Fix nasty bug #706. See trac.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / util / 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.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
26 import net.sourceforge.phpdt.core.compiler.CharOperation;
27 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
28
29 public class Util {
30
31         public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
32
33         public static char[] LINE_SEPARATOR_CHARS = LINE_SEPARATOR.toCharArray();
34
35         // public final static char[] SUFFIX_class = ".class".toCharArray();
36         // //$NON-NLS-1$
37         // public final static char[] SUFFIX_CLASS = ".CLASS".toCharArray();
38         // //$NON-NLS-1$
39         public final static char[] SUFFIX_java = ".java".toCharArray(); //$NON-NLS-1$
40
41         public final static char[] SUFFIX_JAVA = ".JAVA".toCharArray(); //$NON-NLS-1$
42         // public final static char[] SUFFIX_jar = ".jar".toCharArray();
43         // //$NON-NLS-1$
44         // public final static char[] SUFFIX_JAR = ".JAR".toCharArray();
45         // //$NON-NLS-1$
46
47         public final static char[] SUFFIX_zip = ".zip".toCharArray(); //$NON-NLS-1$
48
49         public final static char[] SUFFIX_ZIP = ".ZIP".toCharArray(); //$NON-NLS-1$
50
51         private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$
52
53         private final static char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$
54
55         private static final int DEFAULT_READING_SIZE = 8192;
56
57         /* Bundle containing messages */
58         protected static ResourceBundle bundle;
59
60         private final static String bundleName = "net.sourceforge.phpdt.internal.compiler.util.messages"; //$NON-NLS-1$
61         static {
62                 relocalize();
63         }
64
65         /**
66          * Lookup the message with the given ID in this catalog and bind its
67          * substitution locations with the given strings.
68          */
69         public static String bind(String id, String binding1, String binding2) {
70                 return bind(id, new String[] { binding1, binding2 });
71         }
72
73         /**
74          * Lookup the message with the given ID in this catalog and bind its
75          * substitution locations with the given string.
76          */
77         public static String bind(String id, String binding) {
78                 return bind(id, new String[] { binding });
79         }
80
81         /**
82          * Lookup the message with the given ID in this catalog and bind its
83          * substitution locations with the given string values.
84          */
85         public static String bind(String id, String[] bindings) {
86                 if (id == null)
87                         return "No message available"; //$NON-NLS-1$
88                 String message = null;
89                 try {
90                         message = bundle.getString(id);
91                 } catch (MissingResourceException e) {
92                         // If we got an exception looking for the message, fail gracefully
93                         // by just returning
94                         // the id we were looking for. In most cases this is
95                         // semi-informative so is not too bad.
96                         return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-2$ //$NON-NLS-1$
97                 }
98                 // for compatibility with MessageFormat which eliminates double quotes
99                 // in original message
100                 char[] messageWithNoDoubleQuotes = CharOperation.replace(message
101                                 .toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE);
102                 message = new String(messageWithNoDoubleQuotes);
103
104                 if (bindings == null)
105                         return message;
106
107                 int length = message.length();
108                 int start = -1;
109                 int end = length;
110                 StringBuffer output = new StringBuffer(80);
111                 while (true) {
112                         if ((end = message.indexOf('{', start)) > -1) {
113                                 output.append(message.substring(start + 1, end));
114                                 if ((start = message.indexOf('}', end)) > -1) {
115                                         int index = -1;
116                                         try {
117                                                 index = Integer.parseInt(message.substring(end + 1,
118                                                                 start));
119                                                 output.append(bindings[index]);
120                                         } catch (NumberFormatException nfe) {
121                                                 output.append(message.substring(end + 1, start + 1));
122                                         } catch (ArrayIndexOutOfBoundsException e) {
123                                                 output
124                                                                 .append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$
125                                         }
126                                 } else {
127                                         output.append(message.substring(end, length));
128                                         break;
129                                 }
130                         } else {
131                                 output.append(message.substring(start + 1, length));
132                                 break;
133                         }
134                 }
135                 return output.toString();
136         }
137
138         /**
139          * Lookup the message with the given ID in this catalog
140          */
141         public static String bind(String id) {
142                 return bind(id, (String[]) null);
143         }
144
145         /**
146          * Creates a NLS catalog for the given locale.
147          */
148         public static void relocalize() {
149                 try {
150                         bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
151                 } catch (MissingResourceException e) {
152                         System.out
153                                         .println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$
154                         throw e;
155                 }
156         }
157
158         /**
159          * Returns the given bytes as a char array using a given encoding (null
160          * means platform default).
161          */
162         public static char[] bytesToChar(byte[] bytes, String encoding)
163                         throws IOException {
164
165                 return getInputStreamAsCharArray(new ByteArrayInputStream(bytes),
166                                 bytes.length, encoding);
167
168         }
169
170         /**
171          * Returns the contents of the given file as a byte array.
172          * 
173          * @throws IOException
174          *             if a problem occured reading the file.
175          */
176         public static byte[] getFileByteContent(File file) throws IOException {
177                 InputStream stream = null;
178                 try {
179                         stream = new BufferedInputStream(new FileInputStream(file));
180                         return getInputStreamAsByteArray(stream, (int) file.length());
181                 } finally {
182                         if (stream != null) {
183                                 try {
184                                         stream.close();
185                                 } catch (IOException e) {
186                                 }
187                         }
188                 }
189         }
190
191         /**
192          * Returns the contents of the given file as a char array. When encoding is
193          * null, then the platform default one is used
194          * 
195          * @throws IOException
196          *             if a problem occured reading the file.
197          */
198         public static char[] getFileCharContent(File file, String encoding)
199                         throws IOException {
200                 InputStream stream = null;
201                 try {
202                         stream = new BufferedInputStream(new FileInputStream(file));
203                         return Util.getInputStreamAsCharArray(stream, (int) file.length(),
204                                         encoding);
205                 } finally {
206                         if (stream != null) {
207                                 try {
208                                         stream.close();
209                                 } catch (IOException e) {
210                                 }
211                         }
212                 }
213         }
214
215         /**
216          * Returns the given input stream's contents as a byte array. If a length is
217          * specified (ie. if length != -1), only length bytes are returned.
218          * Otherwise all bytes in the stream are returned. Note this doesn't close
219          * the stream.
220          * 
221          * @throws IOException
222          *             if a problem occured reading the stream.
223          */
224         public static byte[] getInputStreamAsByteArray(InputStream stream,
225                         int length) throws IOException {
226                 byte[] contents;
227                 if (length == -1) {
228                         contents = new byte[0];
229                         int contentsLength = 0;
230                         int amountRead = -1;
231                         do {
232                                 int amountRequested = Math.max(stream.available(),
233                                                 DEFAULT_READING_SIZE); // read at least 8K
234
235                                 // resize contents if needed
236                                 if (contentsLength + amountRequested > contents.length) {
237                                         System.arraycopy(contents, 0,
238                                                         contents = new byte[contentsLength
239                                                                         + amountRequested], 0, contentsLength);
240                                 }
241
242                                 // read as many bytes as possible
243                                 amountRead = stream.read(contents, contentsLength,
244                                                 amountRequested);
245
246                                 if (amountRead > 0) {
247                                         // remember length of contents
248                                         contentsLength += amountRead;
249                                 }
250                         } while (amountRead != -1);
251
252                         // resize contents if necessary
253                         if (contentsLength < contents.length) {
254                                 System.arraycopy(contents, 0,
255                                                 contents = new byte[contentsLength], 0, contentsLength);
256                         }
257                 } else {
258                         contents = new byte[length];
259                         int len = 0;
260                         int readSize = 0;
261                         while ((readSize != -1) && (len != length)) {
262                                 // See PR 1FMS89U
263                                 // We record first the read size. In this case len is the actual
264                                 // read size.
265                                 len += readSize;
266                                 readSize = stream.read(contents, len, length - len);
267                         }
268                 }
269
270                 return contents;
271         }
272
273         /**
274          * Returns the given input stream's contents as a character array. If a
275          * length is specified (ie. if length != -1), only length chars are
276          * returned. Otherwise all chars in the stream are returned. Note this
277          * doesn't close the stream.
278          * 
279          * @throws IOException
280          *             if a problem occured reading the stream.
281          */
282         public static char[] getInputStreamAsCharArray(InputStream stream,
283                         int length, String encoding) throws IOException {
284                 InputStreamReader reader = null;
285                 reader = encoding == null ? new InputStreamReader(stream)
286                                 : new InputStreamReader(stream, encoding);
287                 char[] contents;
288                 if (length == -1) {
289                         contents = CharOperation.NO_CHAR;
290                         int contentsLength = 0;
291                         int amountRead = -1;
292                         do {
293                                 int amountRequested = Math.max(stream.available(),
294                                                 DEFAULT_READING_SIZE); // read at least 8K
295
296                                 // resize contents if needed
297                                 if (contentsLength + amountRequested > contents.length) {
298                                         System.arraycopy(contents, 0,
299                                                         contents = new char[contentsLength
300                                                                         + amountRequested], 0, contentsLength);
301                                 }
302
303                                 // read as many chars as possible
304                                 amountRead = reader.read(contents, contentsLength,
305                                                 amountRequested);
306
307                                 if (amountRead > 0) {
308                                         // remember length of contents
309                                         contentsLength += amountRead;
310                                 }
311                         } while (amountRead != -1);
312
313                         // resize contents if necessary
314                         if (contentsLength < contents.length) {
315                                 System.arraycopy(contents, 0,
316                                                 contents = new char[contentsLength], 0, contentsLength);
317                         }
318                 } else {
319                         contents = new char[length];
320                         int len = 0;
321                         int readSize = 0;
322                         while ((readSize != -1) && (len != length)) {
323                                 // See PR 1FMS89U
324                                 // We record first the read size. In this case len is the actual
325                                 // read size.
326                                 len += readSize;
327                                 readSize = reader.read(contents, len, length - len);
328                         }
329                         // See PR 1FMS89U
330                         // Now we need to resize in case the default encoding used more than
331                         // one byte for each
332                         // character
333                         if (len != length)
334                                 System.arraycopy(contents, 0, (contents = new char[len]), 0,
335                                                 len);
336                 }
337
338                 return contents;
339         }
340
341         /**
342          * Returns the contents of the given zip entry as a byte array.
343          * 
344          * @throws IOException
345          *             if a problem occured reading the zip entry.
346          */
347         public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip)
348                         throws IOException {
349
350                 InputStream stream = null;
351                 try {
352                         stream = new BufferedInputStream(zip.getInputStream(ze));
353                         return getInputStreamAsByteArray(stream, (int) ze.getSize());
354                 } finally {
355                         if (stream != null) {
356                                 try {
357                                         stream.close();
358                                 } catch (IOException e) {
359                                 }
360                         }
361                 }
362         }
363
364         /**
365          * Returns true iff str.toLowerCase().endsWith(".jar") ||
366          * str.toLowerCase().endsWith(".zip") implementation is not creating extra
367          * strings.
368          */
369         // public final static boolean isArchiveFileName(String name) {
370         // int nameLength = name == null ? 0 : name.length();
371         // int suffixLength = SUFFIX_JAR.length;
372         // if (nameLength < suffixLength) return false;
373         // 
374         // // try to match as JAR file
375         // for (int i = 0; i < suffixLength; i++) {
376         // char c = name.charAt(nameLength - i - 1);
377         // int suffixIndex = suffixLength - i - 1;
378         // if (c != SUFFIX_jar[suffixIndex] && c != SUFFIX_JAR[suffixIndex]) {
379         //
380         // // try to match as ZIP file
381         // suffixLength = SUFFIX_ZIP.length;
382         // if (nameLength < suffixLength) return false;
383         // for (int j = 0; j < suffixLength; j++) {
384         // c = name.charAt(nameLength - j - 1);
385         // suffixIndex = suffixLength - j - 1;
386         // if (c != SUFFIX_zip[suffixIndex] && c != SUFFIX_ZIP[suffixIndex]) return
387         // false;
388         // }
389         // return true;
390         // }
391         // }
392         // return true;
393         // }
394         /**
395          * Returns true iff str.toLowerCase().endsWith(".class") implementation is
396          * not creating extra strings.
397          */
398         // public final static boolean isClassFileName(String name) {
399         // int nameLength = name == null ? 0 : name.length();
400         // int suffixLength = SUFFIX_CLASS.length;
401         // if (nameLength < suffixLength) return false;
402         //
403         // for (int i = 0; i < suffixLength; i++) {
404         // char c = name.charAt(nameLength - i - 1);
405         // int suffixIndex = suffixLength - i - 1;
406         // if (c != SUFFIX_class[suffixIndex] && c != SUFFIX_CLASS[suffixIndex])
407         // return false;
408         // }
409         // return true;
410         // }
411         /**
412          * Returns true iff str.toLowerCase().endsWith(".java") implementation is
413          * not creating extra strings.
414          */
415         public final static boolean isJavaFileName(String name) {
416                 return PHPFileUtil.isPHPFileName(name);
417                 // int nameLength = name == null ? 0 : name.length();
418                 // int suffixLength = SUFFIX_JAVA.length;
419                 // if (nameLength < suffixLength) return false;
420                 //
421                 // for (int i = 0; i < suffixLength; i++) {
422                 // char c = name.charAt(nameLength - i - 1);
423                 // int suffixIndex = suffixLength - i - 1;
424                 // if (c != SUFFIX_java[suffixIndex] && c != SUFFIX_JAVA[suffixIndex])
425                 // return false;
426                 // }
427                 // return true;
428         }
429 }