FileLocator.java revision 608:7e06bf1dcb09
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 CLASSPATH is used to locate the file.
226     *
227     * @param fileName The name of the file to locate.  The file name
228     * may be qualified with a partial path name, using '/' as the separator
229     * character or using separator characters appropriate for the host file
230     * system, in which case each directory or zip file in the CLASSPATH will
231     * be used as a base for finding the fully-qualified file.
232     *
233     * @exception java.io.FileNotFoundException The requested class file
234     * could not be found.
235     * @exception java.io.IOException The requested class file
236     * could not be opened.
237     */
238    public static DataInputStream locateFileInClassPath (String fileName)
239        throws FileNotFoundException, IOException {
240
241        boolean notFound = true;
242        StringTokenizer st;
243        String path = "";
244        File cf = null;
245        NamedDataInputStream result;
246
247        String zipEntryName = File.separatorChar == '/' ? fileName :
248            fileName.replace (File.separatorChar, '/');
249
250        String localFileName = File.separatorChar == '/' ? fileName :
251            fileName.replace ('/', File.separatorChar);
252
253        st = new StringTokenizer (classPath, pathSeparator, false);
254
255        while (st.hasMoreTokens () && notFound) {
256
257            try {path = st.nextToken ();}
258                catch (NoSuchElementException nse) {break;}
259            int pLen = path.length ();
260            String pathLast4 = pLen > 3 ? path.substring (pLen - 4) : "";
261            if (pathLast4.equalsIgnoreCase (".zip") ||
262                pathLast4.equalsIgnoreCase (".jar")) {
263
264                try {
265
266                    result = locateInZipFile (path, zipEntryName, false, false);
267                    if (result == null)
268                        continue;
269                    return (DataInputStream) result;
270
271                } catch (ZipException zfe) {
272                    continue;
273                } catch (IOException ioe) {
274                    continue;
275                }
276
277            } else {
278                try {cf = new File (path + File.separator + localFileName);
279                } catch (NullPointerException npe) { continue; }
280                if ((cf != null) && cf.exists ())
281                    notFound = false;
282            }
283        }
284
285        if (notFound) {
286
287            /* Make one last attempt to find the file in the current
288             * directory
289             */
290
291            int lastpart = localFileName.lastIndexOf (File.separator);
292            String simpleName =
293                (lastpart >= 0) ? localFileName.substring (lastpart+1) :
294                localFileName;
295
296            result = new NamedDataInputStream (new BufferedInputStream (
297               new FileInputStream (simpleName)), simpleName, false);
298            return (DataInputStream) result;
299        }
300
301        result = new NamedDataInputStream (new BufferedInputStream (
302            new FileInputStream (cf)), path + File.separator + localFileName,
303                false);
304        return (DataInputStream) result;
305
306    }
307
308    /**
309     * Returns the fully qualified file name associated with the passed
310     * DataInputStream <i>if the DataInputStream was created using one
311     * of the static locate methods supplied with this class</i>, otherwise
312     * returns a zero length string.
313     */
314    public static String getFileNameFromStream (DataInputStream ds) {
315
316        if (ds instanceof NamedDataInputStream)
317            return ((NamedDataInputStream) ds).fullyQualifiedFileName;
318        return "";
319
320    }
321
322    /**
323     * Returns an indication of whether the passed DataInputStream is
324     * associated with a member of a zip file <i>if the DataInputStream was
325     * created using one of the static locate methods supplied with this
326     * class</i>, otherwise returns false.
327     */
328    public static boolean isZipFileAssociatedWithStream (DataInputStream ds) {
329
330        if (ds instanceof NamedDataInputStream)
331            return ((NamedDataInputStream) ds).inZipFile;
332        return false;
333
334    }
335
336    private static NamedDataInputStream locateInZipFile (String zipFileName,
337        String fileName, boolean wantClass, boolean buffered)
338        throws ZipException, IOException {
339
340        ZipFile zf;
341        ZipEntry ze;
342        zf = new ZipFile (zipFileName);
343
344        if (zf == null)
345            return null;
346        String zeName = wantClass ?
347            fileName.replace ('.', '/') + ".class" :
348            fileName;
349
350        //  This code works with JDK 1.0 level SUN zip classes
351        //
352
353        //  ze = zf.get (zeName);
354        //  if (ze == null)
355        //      return null;
356        //  return new NamedDataInputStream (
357        //      new BufferedInputStream (new ZipInputStream (ze)),
358        //          zipFileName + '(' +zeName + ')', true);
359
360        //  This code works with JDK 1.0.2 and JDK 1.1 level SUN zip classes
361        //
362
363            ze = zf.getEntry (zeName);
364            if (ze == null) {
365                zf.close(); // D55355, D56419
366                zf = null;
367                return null;
368            }
369            InputStream istream = zf.getInputStream(ze);
370            if (buffered)
371                istream = new BufferedInputStream(istream);
372            return new NamedDataInputStream (istream,
373                    zipFileName + '(' + zeName + ')', true);
374
375    }
376
377}
378
379/**
380 * This class is used to associate a filename with a DataInputStream
381 * The host platform's file naming conventions are assumed for the filename.
382 *
383 * @author      Larry K. Raper
384 *
385 */
386/* default access */ class NamedDataInputStream extends DataInputStream {
387
388    /* Instance variables */
389
390    /**
391     * The name of the file associated with the DataInputStream.
392     */
393    public String fullyQualifiedFileName;
394
395    /**
396     * Indicates whether or not the file is contained in a .zip file.
397     */
398    public boolean inZipFile;
399
400    /* Constructors */
401
402    protected NamedDataInputStream (InputStream in, String fullyQualifiedName,
403        boolean inZipFile) {
404
405        super (in);
406        this.fullyQualifiedFileName = fullyQualifiedName;
407        this.inZipFile = inZipFile;
408
409    }
410
411}
412