Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 132   Methods: 4
NCLOC: 50   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
PreemptingClassLoader.java 75% 88.5% 100% 85.7%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.reflect;
 36   
 37    import java.io.InputStream;
 38    import java.io.IOException;
 39   
 40    import edu.rice.cs.plt.iter.IterUtil;
 41    import edu.rice.cs.plt.io.IOUtil;
 42   
 43    /**
 44    * <p>A class loader that claims a set of classes available in its parent as its own. This allows classes
 45    * that would otherwise be loaded by a grandparent or distant ancestor to be loaded in a context that
 46    * gives them access to all classes available to the parent. For example, if a library must be loaded
 47    * with a {@link PathClassLoader}, while an adapter for that library is available on the static class
 48    * path (without any custom class loading), loading the adapter directly will lead to errors &mdash; the
 49    * library will not be visible to it. When loaded instead by this class loader, the adapter can see both the
 50    * application classes and the library classes.</p>
 51    *
 52    * <p>The implementation is somewhat limited: the only way to access a class definition in an arbitrary class
 53    * loader without actually <em>defining</em> that class is to invoke {@link ClassLoader#getResource} with
 54    * the name of a {@code .class} file. To work correctly, then, all preempted classes must be available
 55    * as class files via {@code parent.getResource(classFileName)}. Most class loaders (especially those
 56    * that simply load class files from some repository) follow the convention that a class will be loaded
 57    * only if a resource with the appropriate class file name exists. But there is no guarantee that all
 58    * class loaders (such as those that generate class definitions on the fly) will follow this convention.</p>
 59    */
 60    public class PreemptingClassLoader extends AbstractClassLoader {
 61   
 62    private Iterable<String> _prefixes;
 63   
 64    /**
 65    * @param parent The source of all classes and resources to be loaded
 66    * @param prefixes A set of class name prefixes for which all matching classes will be defined by
 67    * this loader rather than a parent. Each prefix must be a package or class name
 68    * (partial names, like {@code "java.lang.Stri"}, will not match the full class name).
 69    */
 70  5 public PreemptingClassLoader(ClassLoader parent, String... prefixes) {
 71  5 this(parent, IterUtil.asIterable(prefixes));
 72    }
 73   
 74    /**
 75    * @param parent The source of all classes and resources to be loaded
 76    * @param prefixes A set of class name prefixes for which all matching classes will be defined by
 77    * this loader rather than a parent. Each prefix must be a package or class name
 78    * (partial names, like {@code "java.lang.Stri"}, will not match the full class name).
 79    */
 80  5 public PreemptingClassLoader(ClassLoader parent, Iterable<? extends String> prefixes) {
 81  5 super(parent);
 82  5 _prefixes = IterUtil.snapshot(prefixes);
 83    }
 84   
 85    /**
 86    * Load a class by following the standard search strategy specified by
 87    * {@link ClassLoader#loadClass(String, boolean)}, with one exception: if the name matches the set
 88    * of prefixes to preemptively load, the corresponding class file will be
 89    * loaded as a resource and defined here rather than allowing the parent to define the class.
 90    */
 91  23 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
 92  12 if (!shouldPreempt(name)) { return super.loadClass(name, resolve); }
 93    else {
 94  11 Class<?> result = findLoadedClass(name); // check if already loaded
 95  11 if (result == null) {
 96  6 String filename = name.replace('.', '/') + ".class";
 97  6 InputStream in = IOUtil.asBuffered(getResourceAsStream(filename));
 98  0 if (in == null) { throw new ClassNotFoundException("Resource not found: " + filename); }
 99  6 try {
 100  6 byte[] data = IOUtil.toByteArray(in);
 101   
 102    // define package
 103  6 definePackageForClass(name);
 104   
 105  6 result = defineClass(name, data, 0, data.length);
 106    }
 107    catch (IOException e) {
 108  0 throw new ClassNotFoundException("Error in reading " + filename, e);
 109    }
 110    finally {
 111  6 try { in.close(); }
 112    catch (IOException e) { /* ignore */ }
 113    }
 114    }
 115    // if no exceptions have occurred, result is no longer null
 116  0 if (resolve) { resolveClass(result); }
 117  11 return result;
 118    }
 119    }
 120   
 121  23 private boolean shouldPreempt(String name) {
 122    // TODO: improve efficiency by using a sorted data structure
 123  23 for (String p : _prefixes) {
 124  23 if (name.startsWith(p)) {
 125  11 if (name.equals(p) || name.startsWith(p + ".") || name.startsWith(p + "$")) {
 126  11 return true;
 127    }
 128    }
 129    }
 130  12 return false;
 131    }
 132    }