Home | History | Annotate | Download | only in javassist
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist;
     17 
     18 import java.io.*;
     19 import java.util.jar.*;
     20 import java.net.MalformedURLException;
     21 import java.net.URL;
     22 import java.util.Hashtable;
     23 
     24 final class ClassPathList {
     25     ClassPathList next;
     26     ClassPath path;
     27 
     28     ClassPathList(ClassPath p, ClassPathList n) {
     29         next = n;
     30         path = p;
     31     }
     32 }
     33 
     34 final class DirClassPath implements ClassPath {
     35     String directory;
     36 
     37     DirClassPath(String dirName) {
     38         directory = dirName;
     39     }
     40 
     41     public InputStream openClassfile(String classname) {
     42         try {
     43             char sep = File.separatorChar;
     44             String filename = directory + sep
     45                 + classname.replace('.', sep) + ".class";
     46             return new FileInputStream(filename.toString());
     47         }
     48         catch (FileNotFoundException e) {}
     49         catch (SecurityException e) {}
     50         return null;
     51     }
     52 
     53     public URL find(String classname) {
     54         char sep = File.separatorChar;
     55         String filename = directory + sep
     56             + classname.replace('.', sep) + ".class";
     57         File f = new File(filename);
     58         if (f.exists())
     59             try {
     60                 return f.getCanonicalFile().toURI().toURL();
     61             }
     62             catch (MalformedURLException e) {}
     63             catch (IOException e) {}
     64 
     65         return null;
     66     }
     67 
     68     public void close() {}
     69 
     70     public String toString() {
     71         return directory;
     72     }
     73 }
     74 
     75 final class JarDirClassPath implements ClassPath {
     76     JarClassPath[] jars;
     77 
     78     JarDirClassPath(String dirName) throws NotFoundException {
     79         File[] files = new File(dirName).listFiles(new FilenameFilter() {
     80             public boolean accept(File dir, String name) {
     81                 name = name.toLowerCase();
     82                 return name.endsWith(".jar") || name.endsWith(".zip");
     83             }
     84         });
     85 
     86         if (files != null) {
     87             jars = new JarClassPath[files.length];
     88             for (int i = 0; i < files.length; i++)
     89                 jars[i] = new JarClassPath(files[i].getPath());
     90         }
     91     }
     92 
     93     public InputStream openClassfile(String classname) throws NotFoundException {
     94         if (jars != null)
     95             for (int i = 0; i < jars.length; i++) {
     96                 InputStream is = jars[i].openClassfile(classname);
     97                 if (is != null)
     98                     return is;
     99             }
    100 
    101         return null;    // not found
    102     }
    103 
    104     public URL find(String classname) {
    105         if (jars != null)
    106             for (int i = 0; i < jars.length; i++) {
    107                 URL url = jars[i].find(classname);
    108                 if (url != null)
    109                     return url;
    110             }
    111 
    112         return null;    // not found
    113     }
    114 
    115     public void close() {
    116         if (jars != null)
    117             for (int i = 0; i < jars.length; i++)
    118                 jars[i].close();
    119     }
    120 }
    121 
    122 final class JarClassPath implements ClassPath {
    123     JarFile jarfile;
    124     String jarfileURL;
    125 
    126     JarClassPath(String pathname) throws NotFoundException {
    127         try {
    128             jarfile = new JarFile(pathname);
    129             jarfileURL = new File(pathname).getCanonicalFile()
    130                                            .toURI().toURL().toString();
    131             return;
    132         }
    133         catch (IOException e) {}
    134         throw new NotFoundException(pathname);
    135     }
    136 
    137     public InputStream openClassfile(String classname)
    138         throws NotFoundException
    139     {
    140         try {
    141             String jarname = classname.replace('.', '/') + ".class";
    142             JarEntry je = jarfile.getJarEntry(jarname);
    143             if (je != null)
    144                 return jarfile.getInputStream(je);
    145             else
    146                 return null;    // not found
    147         }
    148         catch (IOException e) {}
    149         throw new NotFoundException("broken jar file?: "
    150                                     + jarfile.getName());
    151     }
    152 
    153     public URL find(String classname) {
    154         String jarname = classname.replace('.', '/') + ".class";
    155         JarEntry je = jarfile.getJarEntry(jarname);
    156         if (je != null)
    157             try {
    158                 return new URL("jar:" + jarfileURL + "!/" + jarname);
    159             }
    160             catch (MalformedURLException e) {}
    161 
    162         return null;            // not found
    163     }
    164 
    165     public void close() {
    166         try {
    167             jarfile.close();
    168             jarfile = null;
    169         }
    170         catch (IOException e) {}
    171     }
    172 
    173     public String toString() {
    174         return jarfile == null ? "<null>" : jarfile.toString();
    175     }
    176 }
    177 
    178 final class ClassPoolTail {
    179     protected ClassPathList pathList;
    180     private Hashtable packages;         // should be synchronized.
    181 
    182     public ClassPoolTail() {
    183         pathList = null;
    184         packages = new Hashtable();
    185     }
    186 
    187     public String toString() {
    188         StringBuffer buf = new StringBuffer();
    189         buf.append("[class path: ");
    190         ClassPathList list = pathList;
    191         while (list != null) {
    192             buf.append(list.path.toString());
    193             buf.append(File.pathSeparatorChar);
    194             list = list.next;
    195         }
    196 
    197         buf.append(']');
    198         return buf.toString();
    199     }
    200 
    201     public synchronized ClassPath insertClassPath(ClassPath cp) {
    202         pathList = new ClassPathList(cp, pathList);
    203         return cp;
    204     }
    205 
    206     public synchronized ClassPath appendClassPath(ClassPath cp) {
    207         ClassPathList tail = new ClassPathList(cp, null);
    208         ClassPathList list = pathList;
    209         if (list == null)
    210             pathList = tail;
    211         else {
    212             while (list.next != null)
    213                 list = list.next;
    214 
    215             list.next = tail;
    216         }
    217 
    218         return cp;
    219     }
    220 
    221     public synchronized void removeClassPath(ClassPath cp) {
    222         ClassPathList list = pathList;
    223         if (list != null)
    224             if (list.path == cp)
    225                 pathList = list.next;
    226             else {
    227                 while (list.next != null)
    228                     if (list.next.path == cp)
    229                         list.next = list.next.next;
    230                     else
    231                         list = list.next;
    232             }
    233 
    234         cp.close();
    235     }
    236 
    237     public ClassPath appendSystemPath() {
    238         return appendClassPath(new ClassClassPath());
    239     }
    240 
    241     public ClassPath insertClassPath(String pathname)
    242         throws NotFoundException
    243     {
    244         return insertClassPath(makePathObject(pathname));
    245     }
    246 
    247     public ClassPath appendClassPath(String pathname)
    248         throws NotFoundException
    249     {
    250         return appendClassPath(makePathObject(pathname));
    251     }
    252 
    253     private static ClassPath makePathObject(String pathname)
    254         throws NotFoundException
    255     {
    256         String lower = pathname.toLowerCase();
    257         if (lower.endsWith(".jar") || lower.endsWith(".zip"))
    258             return new JarClassPath(pathname);
    259 
    260         int len = pathname.length();
    261         if (len > 2 && pathname.charAt(len - 1) == '*'
    262             && (pathname.charAt(len - 2) == '/'
    263                 || pathname.charAt(len - 2) == File.separatorChar)) {
    264             String dir = pathname.substring(0, len - 2);
    265             return new JarDirClassPath(dir);
    266         }
    267 
    268         return new DirClassPath(pathname);
    269     }
    270 
    271     /**
    272      * You can record "System" so that java.lang.System can be quickly
    273      * found although "System" is not a package name.
    274      */
    275     public void recordInvalidClassName(String name) {
    276         packages.put(name, name);
    277     }
    278 
    279     /**
    280      * This method does not close the output stream.
    281      */
    282     void writeClassfile(String classname, OutputStream out)
    283         throws NotFoundException, IOException, CannotCompileException
    284     {
    285         InputStream fin = openClassfile(classname);
    286         if (fin == null)
    287             throw new NotFoundException(classname);
    288 
    289         try {
    290             copyStream(fin, out);
    291         }
    292         finally {
    293             fin.close();
    294         }
    295     }
    296 
    297     /*
    298     -- faster version --
    299     void checkClassName(String classname) throws NotFoundException {
    300         if (find(classname) == null)
    301             throw new NotFoundException(classname);
    302     }
    303 
    304     -- slower version --
    305 
    306     void checkClassName(String classname) throws NotFoundException {
    307         InputStream fin = openClassfile(classname);
    308         try {
    309             fin.close();
    310         }
    311         catch (IOException e) {}
    312     }
    313     */
    314 
    315 
    316     /**
    317      * Opens the class file for the class specified by
    318      * <code>classname</code>.
    319      *
    320      * @param classname             a fully-qualified class name
    321      * @return null                 if the file has not been found.
    322      * @throws NotFoundException    if any error is reported by ClassPath.
    323      */
    324     InputStream openClassfile(String classname)
    325         throws NotFoundException
    326     {
    327         if (packages.get(classname) != null)
    328             return null;    // not found
    329 
    330         ClassPathList list = pathList;
    331         InputStream ins = null;
    332         NotFoundException error = null;
    333         while (list != null) {
    334             try {
    335                 ins = list.path.openClassfile(classname);
    336             }
    337             catch (NotFoundException e) {
    338                 if (error == null)
    339                     error = e;
    340             }
    341 
    342             if (ins == null)
    343                 list = list.next;
    344             else
    345                 return ins;
    346         }
    347 
    348         if (error != null)
    349             throw error;
    350         else
    351             return null;    // not found
    352     }
    353 
    354     /**
    355      * Searches the class path to obtain the URL of the class file
    356      * specified by classname.  It is also used to determine whether
    357      * the class file exists.
    358      *
    359      * @param classname     a fully-qualified class name.
    360      * @return null if the class file could not be found.
    361      */
    362     public URL find(String classname) {
    363         if (packages.get(classname) != null)
    364             return null;
    365 
    366         ClassPathList list = pathList;
    367         URL url = null;
    368         while (list != null) {
    369             url = list.path.find(classname);
    370             if (url == null)
    371                 list = list.next;
    372             else
    373                 return url;
    374         }
    375 
    376         return null;
    377     }
    378 
    379     /**
    380      * Reads from an input stream until it reaches the end.
    381      *
    382      * @return          the contents of that input stream
    383      */
    384     public static byte[] readStream(InputStream fin) throws IOException {
    385         byte[][] bufs = new byte[8][];
    386         int bufsize = 4096;
    387 
    388         for (int i = 0; i < 8; ++i) {
    389             bufs[i] = new byte[bufsize];
    390             int size = 0;
    391             int len = 0;
    392             do {
    393                 len = fin.read(bufs[i], size, bufsize - size);
    394                 if (len >= 0)
    395                     size += len;
    396                 else {
    397                     byte[] result = new byte[bufsize - 4096 + size];
    398                     int s = 0;
    399                     for (int j = 0; j < i; ++j) {
    400                         System.arraycopy(bufs[j], 0, result, s, s + 4096);
    401                         s = s + s + 4096;
    402                     }
    403 
    404                     System.arraycopy(bufs[i], 0, result, s, size);
    405                     return result;
    406                 }
    407             } while (size < bufsize);
    408             bufsize *= 2;
    409         }
    410 
    411         throw new IOException("too much data");
    412     }
    413 
    414     /**
    415      * Reads from an input stream and write to an output stream
    416      * until it reaches the end.  This method does not close the
    417      * streams.
    418      */
    419     public static void copyStream(InputStream fin, OutputStream fout)
    420         throws IOException
    421     {
    422         int bufsize = 4096;
    423         for (int i = 0; i < 8; ++i) {
    424             byte[] buf = new byte[bufsize];
    425             int size = 0;
    426             int len = 0;
    427             do {
    428                 len = fin.read(buf, size, bufsize - size);
    429                 if (len >= 0)
    430                     size += len;
    431                 else {
    432                     fout.write(buf, 0, size);
    433                     return;
    434                 }
    435             } while (size < bufsize);
    436             fout.write(buf);
    437             bufsize *= 2;
    438         }
    439 
    440         throw new IOException("too much data");
    441     }
    442 }
    443