Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 374   Methods: 22
NCLOC: 209   Classes: 8
 
 Source file Conditionals Statements Methods TOTAL
ClassPathLoader.java 47.2% 56.9% 54.5% 54.4%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-2002 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
 55   
 
 56   
 import java.io.BufferedInputStream;
 57   
 import java.io.ByteArrayInputStream;
 58   
 import java.io.ByteArrayOutputStream;
 59   
 import java.io.File;
 60   
 import java.io.FileInputStream;
 61   
 import java.io.FilenameFilter;
 62   
 import java.io.IOException;
 63   
 import java.io.InputStream;
 64   
 import java.util.Enumeration;
 65   
 import java.util.Hashtable;
 66   
 import java.util.NoSuchElementException;
 67   
 import java.util.StringTokenizer;
 68   
 import java.util.Vector;
 69   
 import java.util.zip.ZipEntry;
 70   
 import java.util.zip.ZipFile;
 71   
 
 72   
 /**
 73   
  * Core of the bytecode analyzer. It loads classes from a given classpath.
 74   
  *
 75   
  * @author <a href="sbailliez@imediation.com">Stephane Bailliez</a>
 76   
  */
 77   
 public class ClassPathLoader {
 78   
 
 79   
     public static final FileLoader NULL_LOADER = new NullLoader();
 80   
 
 81   
     /** the list of files to look for */
 82   
     private File[] files;
 83   
 
 84   
     /**
 85   
      * create a new instance with a given classpath. It must be urls
 86   
      * separated by the platform specific path separator.
 87   
      * @param classPath the classpath to load all the classes from.
 88   
      */
 89  1
     public ClassPathLoader(String classPath) {
 90  1
         StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator);
 91  1
         Vector entries = new Vector();
 92  1
         while (st.hasMoreTokens()) {
 93  3
             File file = new File(st.nextToken());
 94  3
             entries.addElement(file);
 95   
         }
 96  1
         files = new File[entries.size()];
 97  1
         entries.copyInto(files);
 98   
     }
 99   
 
 100   
     /**
 101   
      * create a new instance with a given set of urls.
 102   
      * @param entries valid file urls (either .jar, .zip or directory)
 103   
      */
 104  1
     public ClassPathLoader(String[] entries) {
 105  1
         files = new File[entries.length];
 106  1
         for (int i = 0; i < entries.length; i++) {
 107  3
             files[i] = new File(entries[i]);
 108   
         }
 109   
     }
 110   
 
 111   
     /**
 112   
      * create a new instance with a given set of urls
 113   
      * @param entries file urls to look for classes (.jar, .zip or directory)
 114   
      */
 115  0
     public ClassPathLoader(File[] entries) {
 116  0
         files = entries;
 117   
     }
 118   
 
 119   
     /** the interface to implement to look up for specific resources */
 120   
     public interface FileLoader {
 121   
         /** the file url that is looked for .class files */
 122   
         File getFile();
 123   
 
 124   
         /** return the set of classes found in the file */
 125   
         ClassFile[] getClasses() throws IOException;
 126   
     }
 127   
 
 128   
     /**
 129   
      * @return the set of <tt>FileLoader</tt> loaders matching the given classpath.
 130   
      */
 131  2
     public Enumeration loaders() {
 132  2
         return new LoaderEnumeration();
 133   
     }
 134   
 
 135   
     /**
 136   
      * return the whole set of classes in the classpath. Note that this method
 137   
      * can be very resource demanding since it must load all bytecode from
 138   
      * all classes in all resources in the classpath at a time.
 139   
      * To process it in a less resource demanding way, it is maybe better to
 140   
      * use the <tt>loaders()</tt> that will return loader one by one.
 141   
      *
 142   
      * @return the hashtable containing ALL classes that are found in the given
 143   
      * classpath. Note that the first entry of a given classname will shadow
 144   
      * classes with the same name (as a classloader does)
 145   
      */
 146  1
     public Hashtable getClasses() throws IOException {
 147  1
         Hashtable map = new Hashtable();
 148  1
         Enumeration enum = loaders();
 149  1
         while (enum.hasMoreElements()) {
 150  3
             FileLoader loader = (FileLoader) enum.nextElement();
 151  3
             System.out.println("Processing " + loader.getFile());
 152  3
             long t0 = System.currentTimeMillis();
 153  3
             ClassFile[] classes = loader.getClasses();
 154  3
             long dt = System.currentTimeMillis() - t0;
 155  3
             System.out.println("" + classes.length + " classes loaded in " + dt + "ms");
 156  3
             for (int j = 0; j < classes.length; j++) {
 157  8693
                 String name = classes[j].getFullName();
 158   
                 // do not allow duplicates entries to preserve 'classpath' behavior
 159   
                 // first class in wins
 160  8693
                 if (!map.containsKey(name)) {
 161  8693
                     map.put(name, classes[j]);
 162   
                 }
 163   
             }
 164   
         }
 165  1
         return map;
 166   
     }
 167   
 
 168   
     /** the loader enumeration that will return loaders */
 169   
     private class LoaderEnumeration implements Enumeration {
 170   
         private int index = 0;
 171   
 
 172  8
         public boolean hasMoreElements() {
 173  8
             return index < files.length;
 174   
         }
 175   
 
 176  6
         public Object nextElement() {
 177  6
             if (index >= files.length) {
 178  0
                 throw new NoSuchElementException();
 179   
             }
 180  6
             File file = files[index++];
 181  6
             if (!file.exists()) {
 182  0
                 return new NullLoader(file);
 183   
             }
 184  6
             if (file.isDirectory()) {
 185   
                 // it's a directory
 186  0
                 return new DirectoryLoader(file);
 187  6
             } else if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) {
 188   
                 // it's a jar/zip file
 189  6
                 return new JarLoader(file);
 190   
             }
 191  0
             return new NullLoader(file);
 192   
 
 193   
         }
 194   
     }
 195   
 
 196   
     /**
 197   
      * useful methods to read the whole input stream in memory so that
 198   
      * it can be accessed faster. Processing rt.jar and tools.jar from JDK 1.3.1
 199   
      * brings time from 50s to 7s.
 200   
      */
 201  17386
     public static InputStream getCachedStream(InputStream is) throws IOException {
 202  17386
         final InputStream bis = new BufferedInputStream(is);
 203  17386
         final byte[] buffer = new byte[8192];
 204  17386
         final ByteArrayOutputStream bos = new ByteArrayOutputStream(2048);
 205  17386
         int n;
 206  17386
         bos.reset();
 207  ?
         while ((n = bis.read(buffer, 0, buffer.length)) != -1) {
 208  19034
             bos.write(buffer, 0, n);
 209   
         }
 210  17386
         is.close();
 211  17386
         return new ByteArrayInputStream(bos.toByteArray());
 212   
     }
 213   
 }
 214   
 
 215   
 /** a null loader to return when the file is not valid */
 216   
 final class NullLoader implements ClassPathLoader.FileLoader {
 217   
     private File file;
 218   
 
 219  1
     NullLoader() {
 220  1
         this(null);
 221   
     }
 222   
 
 223  1
     NullLoader(File file) {
 224  1
         this.file = file;
 225   
     }
 226   
 
 227  0
     public File getFile() {
 228  0
         return file;
 229   
     }
 230   
 
 231  0
     public ClassFile[] getClasses() throws IOException {
 232  0
         return new ClassFile[0];
 233   
     }
 234   
 }
 235   
 
 236   
 /**
 237   
  * jar loader specified in looking for classes in jar and zip
 238   
  * @todo read the jar manifest in case there is a Class-Path
 239   
  * entry.
 240   
  */
 241   
 final class JarLoader implements ClassPathLoader.FileLoader {
 242   
     private File file;
 243   
 
 244  6
     JarLoader(File file) {
 245  6
         this.file = file;
 246   
     }
 247   
 
 248  6
     public File getFile() {
 249  6
         return file;
 250   
     }
 251   
 
 252  6
     public ClassFile[] getClasses() throws IOException {
 253  6
         ZipFile zipFile = new ZipFile(file);
 254  6
         Vector v = new Vector();
 255  6
         Enumeration entries = zipFile.entries();
 256  6
         while (entries.hasMoreElements()) {
 257  17972
             ZipEntry entry = (ZipEntry) entries.nextElement();
 258  17972
             if (entry.getName().endsWith(".class")) {
 259  17386
                 InputStream is = ClassPathLoader.getCachedStream(zipFile.getInputStream(entry));
 260  17386
                 ClassFile classFile = new ClassFile(is);
 261  17386
                 is.close();
 262  17386
                 v.addElement(classFile);
 263   
             }
 264   
         }
 265  6
         ClassFile[] classes = new ClassFile[v.size()];
 266  6
         v.copyInto(classes);
 267  6
         return classes;
 268   
     }
 269   
 }
 270   
 
 271   
 /**
 272   
  * directory loader that will look all classes recursively
 273   
  * @todo should discard classes which package name does not
 274   
  * match the directory ?
 275   
  */
 276   
 final class DirectoryLoader implements ClassPathLoader.FileLoader {
 277   
     private File directory;
 278   
     private static final FilenameFilter DIRECTORY_FILTER = new DirectoryFilter();
 279   
     private static final FilenameFilter CLASS_FILTER = new ClassFilter();
 280   
 
 281  0
     DirectoryLoader(File dir) {
 282  0
         directory = dir;
 283   
     }
 284   
 
 285  0
     public File getFile() {
 286  0
         return directory;
 287   
     }
 288   
 
 289  0
     public ClassFile[] getClasses() throws IOException {
 290  0
         Vector v = new Vector(127);
 291  0
         Vector files = listFiles(directory, CLASS_FILTER, true);
 292  0
         final int filesCount = files.size();
 293  0
         for (int i = 0; i < filesCount; i++) {
 294  0
             File file = (File) files.elementAt(i);
 295  0
             InputStream is = null;
 296  0
             try {
 297  0
                 is = ClassPathLoader.getCachedStream(new FileInputStream(file));
 298  0
                 ClassFile classFile = new ClassFile(is);
 299  0
                 is.close();
 300  0
                 is = null;
 301  0
                 v.addElement(classFile);
 302   
             } finally {
 303  0
                 if (is != null) {
 304  0
                     try {
 305  0
                         is.close();
 306   
                     } catch (IOException ignored) {
 307   
                     }
 308   
                 }
 309   
             }
 310   
         }
 311  0
         ClassFile[] classes = new ClassFile[v.size()];
 312  0
         v.copyInto(classes);
 313  0
         return classes;
 314   
     }
 315   
 
 316   
     /**
 317   
      * List files that obeys to a specific filter recursively from a given base
 318   
      * directory.
 319   
      * @param   directory   the directory where to list the files from.
 320   
      * @param   filter      the file filter to apply
 321   
      * @param   recurse     tells whether or not the listing is recursive.
 322   
      * @return  the list of <tt>File</tt> objects that applies to the given
 323   
      *          filter.
 324   
      */
 325  0
     public static Vector listFiles(File directory, FilenameFilter filter, boolean recurse) {
 326  0
         if (!directory.isDirectory()) {
 327  0
             throw new IllegalArgumentException(directory + " is not a directory");
 328   
         }
 329  0
         Vector list = new Vector(512);
 330  0
         listFilesTo(list, directory, filter, recurse);
 331  0
         return list;
 332   
     }
 333   
 
 334   
     /**
 335   
      * List and add files to a given list. As a convenience it sends back the
 336   
      * instance of the list given as a parameter.
 337   
      * @param   list    the list of files where the filtered files should be added
 338   
      * @param   directory   the directory where to list the files from.
 339   
      * @param   filter      the file filter to apply
 340   
      * @param   recurse     tells whether or not the listing is recursive.
 341   
      * @return  the list instance that was passed as the <tt>list</tt> argument.
 342   
      */
 343  0
     private static Vector listFilesTo(Vector list, File directory, FilenameFilter filter, boolean recurse) {
 344  0
         String[] files = directory.list(filter);
 345  0
         for (int i = 0; i < files.length; i++) {
 346  0
             list.addElement(new File(directory, files[i]));
 347   
         }
 348  0
         files = null;   // we don't need it anymore
 349  0
         if (recurse) {
 350  0
             String[] subdirs = directory.list(DIRECTORY_FILTER);
 351  0
             for (int i = 0; i < subdirs.length; i++) {
 352  0
                 listFilesTo(list, new File(directory, subdirs[i]), filter, recurse);
 353   
             }
 354   
         }
 355  0
         return list;
 356   
     }
 357   
 
 358   
 }
 359   
 
 360   
 /** Convenient filter that accepts only directory <tt>File</tt> */
 361   
 final class DirectoryFilter implements FilenameFilter {
 362  0
     public boolean accept(File directory, String name) {
 363  0
         File pathname = new File(directory, name);
 364  0
         return pathname.isDirectory();
 365   
     }
 366   
 }
 367   
 
 368   
 /** convenient filter to accept only .class files */
 369   
 final class ClassFilter implements FilenameFilter {
 370  0
     public boolean accept(File dir, String name) {
 371  0
         return name.endsWith(".class");
 372   
     }
 373   
 }
 374