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