FileLocator.java revision 762:425ca13f66db
1/*
2 * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997,1998
28 * RMI-IIOP v1.0
29 *
30 */
31
32package com.sun.tools.corba.se.idl.som.cff;
33
34import java.lang.Exception;
35import java.lang.String;
36import java.lang.System;
37import java.io.BufferedInputStream;
38import java.io.DataInputStream;
39import java.io.File;
40import java.io.FileInputStream;
41import java.io.FileNotFoundException;
42import java.io.InputStream;
43import java.io.IOException;
44import java.util.Locale;
45import java.util.NoSuchElementException;
46import java.util.Properties;
47import java.util.StringTokenizer;
48import java.util.zip.*;
49
50/**
51 * FileLocator is an abstract class (one that cannot be instantiated) that
52 * provides class methods for finding files in the directories or zip
53 * archives that make up the CLASSPATH.
54 *
55 * @author      Larry K. Raper
56 */
57public abstract class FileLocator extends Object {
58
59    /* Class variables */
60
61
62    static final Properties pp = System.getProperties ();
63    static final String classPath = pp.getProperty ("java.class.path", ".");
64    static final String pathSeparator = pp.getProperty ("path.separator", ";");
65
66    /* Instance variables */
67
68    /* [None, no instances of this class are ever instantiated.] */
69
70    /**
71     * locateClassFile returns a DataInputStream with mark/reset
72     * capability that can be used to read the requested class file.  The
73     * CLASSPATH is used to locate the class.
74     *
75     * @param classFileName The name of the class to locate.  The class name
76     * should be given in fully-qualified form, for example:
77     * <pre>
78     *     java.lang.Object
79     *     java.io.DataInputStream
80     * </pre>
81     *
82     * @exception java.io.FileNotFoundException The requested class file
83     * could not be found.
84     * @exception java.io.IOException The requested class file
85     * could not be opened.
86     */
87    public static DataInputStream locateClassFile (String classFileName)
88        throws FileNotFoundException, IOException {
89
90        boolean notFound = true;
91        StringTokenizer st;
92        String path = "";
93        String pathNameForm;
94        File cf = null;
95        NamedDataInputStream result;
96
97        st = new StringTokenizer (classPath, pathSeparator, false);
98        pathNameForm = classFileName.replace ('.', File.separatorChar) +
99            ".class";
100
101        while (st.hasMoreTokens () && notFound) {
102
103            try {path = st.nextToken ();}
104                catch (NoSuchElementException nse) {break;}
105            int pLen = path.length ();
106            String pathLast4 = pLen > 3 ? path.substring (pLen - 4) : "";
107            if (pathLast4.equalsIgnoreCase (".zip") ||
108                pathLast4.equalsIgnoreCase (".jar")) {
109
110                try {
111
112                    result = locateInZipFile (path, classFileName, true, true);
113                    if (result == null)
114                        continue;
115                    return (DataInputStream) result;
116
117                } catch (ZipException zfe) {
118                    continue;
119                } catch (IOException ioe) {
120                    continue;
121                }
122
123            } else {
124                try {cf = new File (path + File.separator + pathNameForm);
125                } catch (NullPointerException npe) { continue; }
126                if ((cf != null) && cf.exists ())
127                    notFound = false;
128            }
129        }
130
131        if (notFound) {
132
133            /* Make one last attempt to find the file in the current
134             * directory
135             */
136
137            int lastdot = classFileName.lastIndexOf ('.');
138            String simpleName =
139                (lastdot >= 0) ? classFileName.substring (lastdot+1) :
140                classFileName;
141
142            result = new NamedDataInputStream (new BufferedInputStream (
143               new FileInputStream (simpleName + ".class")),
144                   simpleName + ".class", false);
145            return (DataInputStream) result;
146        }
147
148        result = new NamedDataInputStream (new BufferedInputStream (
149            new FileInputStream (cf)), path + File.separator + pathNameForm,
150                false);
151        return (DataInputStream) result;
152
153    }
154
155    /**
156     * locateLocaleSpecificFileInClassPath returns a DataInputStream that
157     * can be used to read the requested file, but the name of the file is
158     * determined using information from the current locale and the supplied
159     * file name (which is treated as a "base" name, and is supplemented with
160     * country and language related suffixes, obtained from the current
161     * locale).  The CLASSPATH is used to locate the file.
162     *
163     * @param fileName The name of the file to locate.  The file name
164     * may be qualified with a partial path name, using '/' as the separator
165     * character or using separator characters appropriate for the host file
166     * system, in which case each directory or zip file in the CLASSPATH will
167     * be used as a base for finding the fully-qualified file.
168     * Here is an example of how the supplied fileName is used as a base
169     * for locating a locale-specific file:
170     *
171     * <pre>
172     *     Supplied fileName: a/b/c/x.y,  current locale: US English
173     *
174     *                     Look first for: a/b/c/x_en_US.y
175     *     (if that fails) Look next for:  a/b/c/x_en.y
176     *     (if that fails) Look last for:  a/b/c/x.y
177     *
178     *     All elements of the class path are searched for each name,
179     *     before the next possible name is tried.
180     * </pre>
181     *
182     * @exception java.io.FileNotFoundException The requested class file
183     * could not be found.
184     * @exception java.io.IOException The requested class file
185     * could not be opened.
186     */
187    public static DataInputStream locateLocaleSpecificFileInClassPath (
188        String fileName) throws FileNotFoundException, IOException {
189
190        String localeSuffix = "_" + Locale.getDefault ().toString ();
191        int lastSlash = fileName.lastIndexOf ('/');
192        int lastDot   = fileName.lastIndexOf ('.');
193        String fnFront, fnEnd;
194        DataInputStream result = null;
195        boolean lastAttempt = false;
196
197        if ((lastDot > 0) && (lastDot > lastSlash)) {
198            fnFront = fileName.substring (0, lastDot);
199            fnEnd   = fileName.substring (lastDot);
200        } else {
201            fnFront = fileName;
202            fnEnd   = "";
203        }
204
205        while (true) {
206            if (lastAttempt)
207                result = locateFileInClassPath (fileName);
208            else try {
209                result = locateFileInClassPath (fnFront + localeSuffix + fnEnd);
210            } catch (Exception e) { /* ignore */ }
211            if ((result != null) || lastAttempt)
212                break;
213            int lastUnderbar = localeSuffix.lastIndexOf ('_');
214            if (lastUnderbar > 0)
215                localeSuffix = localeSuffix.substring (0, lastUnderbar);
216            else
217                lastAttempt = true;
218        }
219        return result;
220
221    }
222
223    /**
224     * locateFileInClassPath returns a DataInputStream that can be used
225     * to read the requested file.  The resource is located in the java.corba
226     * module or if not found, then the CLASSPATH is searched.
227     *
228     * @param fileName The name of the file to locate.  The file name
229     * may be qualified with a partial path name, using '/' as the separator
230     * character or using separator characters appropriate for the host file
231     * system, in which case each directory or zip file in the CLASSPATH will
232     * be used as a base for finding the fully-qualified file.
233     *
234     * @exception java.io.FileNotFoundException The requested class file
235     * could not be found.
236     * @exception java.io.IOException The requested class file
237     * could not be opened.
238     */
239    public static DataInputStream locateFileInClassPath (String fileName)
240        throws FileNotFoundException, IOException {
241
242        // The resource should be in the java.corba module
243        InputStream in = FileLocator.class.getResourceAsStream("/" + fileName);
244        if (in != null) {
245            return new DataInputStream(in);
246        }
247
248        boolean notFound = true;
249        StringTokenizer st;
250        String path = "";
251        File cf = null;
252        NamedDataInputStream result;
253
254        String zipEntryName = File.separatorChar == '/' ? fileName :
255            fileName.replace (File.separatorChar, '/');
256
257        String localFileName = File.separatorChar == '/' ? fileName :
258            fileName.replace ('/', File.separatorChar);
259
260        st = new StringTokenizer (classPath, pathSeparator, false);
261
262        while (st.hasMoreTokens () && notFound) {
263
264            try {path = st.nextToken ();}
265                catch (NoSuchElementException nse) {break;}
266            int pLen = path.length ();
267            String pathLast4 = pLen > 3 ? path.substring (pLen - 4) : "";
268            if (pathLast4.equalsIgnoreCase (".zip") ||
269                pathLast4.equalsIgnoreCase (".jar")) {
270
271                try {
272
273                    result = locateInZipFile (path, zipEntryName, false, false);
274                    if (result == null)
275                        continue;
276                    return (DataInputStream) result;
277
278                } catch (ZipException zfe) {
279                    continue;
280                } catch (IOException ioe) {
281                    continue;
282                }
283
284            } else {
285                try {cf = new File (path + File.separator + localFileName);
286                } catch (NullPointerException npe) { continue; }
287                if ((cf != null) && cf.exists ())
288                    notFound = false;
289            }
290        }
291
292        if (notFound) {
293
294            /* Make one last attempt to find the file in the current
295             * directory
296             */
297
298            int lastpart = localFileName.lastIndexOf (File.separator);
299            String simpleName =
300                (lastpart >= 0) ? localFileName.substring (lastpart+1) :
301                localFileName;
302
303            result = new NamedDataInputStream (new BufferedInputStream (
304               new FileInputStream (simpleName)), simpleName, false);
305            return (DataInputStream) result;
306        }
307
308        result = new NamedDataInputStream (new BufferedInputStream (
309            new FileInputStream (cf)), path + File.separator + localFileName,
310                false);
311        return (DataInputStream) result;
312
313    }
314
315    /**
316     * Returns the fully qualified file name associated with the passed
317     * DataInputStream <i>if the DataInputStream was created using one
318     * of the static locate methods supplied with this class</i>, otherwise
319     * returns a zero length string.
320     */
321    public static String getFileNameFromStream (DataInputStream ds) {
322
323        if (ds instanceof NamedDataInputStream)
324            return ((NamedDataInputStream) ds).fullyQualifiedFileName;
325        return "";
326
327    }
328
329    /**
330     * Returns an indication of whether the passed DataInputStream is
331     * associated with a member of a zip file <i>if the DataInputStream was
332     * created using one of the static locate methods supplied with this
333     * class</i>, otherwise returns false.
334     */
335    public static boolean isZipFileAssociatedWithStream (DataInputStream ds) {
336
337        if (ds instanceof NamedDataInputStream)
338            return ((NamedDataInputStream) ds).inZipFile;
339        return false;
340
341    }
342
343    private static NamedDataInputStream locateInZipFile (String zipFileName,
344        String fileName, boolean wantClass, boolean buffered)
345        throws ZipException, IOException {
346
347        ZipFile zf;
348        ZipEntry ze;
349        zf = new ZipFile (zipFileName);
350
351        if (zf == null)
352            return null;
353        String zeName = wantClass ?
354            fileName.replace ('.', '/') + ".class" :
355            fileName;
356
357        //  This code works with JDK 1.0 level SUN zip classes
358        //
359
360        //  ze = zf.get (zeName);
361        //  if (ze == null)
362        //      return null;
363        //  return new NamedDataInputStream (
364        //      new BufferedInputStream (new ZipInputStream (ze)),
365        //          zipFileName + '(' +zeName + ')', true);
366
367        //  This code works with JDK 1.0.2 and JDK 1.1 level SUN zip classes
368        //
369
370            ze = zf.getEntry (zeName);
371            if (ze == null) {
372                zf.close(); // D55355, D56419
373                zf = null;
374                return null;
375            }
376            InputStream istream = zf.getInputStream(ze);
377            if (buffered)
378                istream = new BufferedInputStream(istream);
379            return new NamedDataInputStream (istream,
380                    zipFileName + '(' + zeName + ')', true);
381
382    }
383
384}
385
386/**
387 * This class is used to associate a filename with a DataInputStream
388 * The host platform's file naming conventions are assumed for the filename.
389 *
390 * @author      Larry K. Raper
391 *
392 */
393/* default access */ class NamedDataInputStream extends DataInputStream {
394
395    /* Instance variables */
396
397    /**
398     * The name of the file associated with the DataInputStream.
399     */
400    public String fullyQualifiedFileName;
401
402    /**
403     * Indicates whether or not the file is contained in a .zip file.
404     */
405    public boolean inZipFile;
406
407    /* Constructors */
408
409    protected NamedDataInputStream (InputStream in, String fullyQualifiedName,
410        boolean inZipFile) {
411
412        super (in);
413        this.fullyQualifiedFileName = fullyQualifiedName;
414        this.inZipFile = inZipFile;
415
416    }
417
418}
419