001package Torello.Java.Additional;
002
003import java.io.UncheckedIOException;
004
005import Torello.Java.FileRW;
006
007import java.io.IOException;
008
009/**
010 * This utilizes the class {@link ConstantPool} to facilitate loading a Java-Class
011 * from any directory.  There is no requirement that the {@code '.class'} file be properly placed
012 * in a sub-directory that has a name which mirrors the full Package-Name of the class which is to
013 * be loaded.
014 */
015public class ByteArrClassLoader extends ClassLoader
016{
017    public ByteArrClassLoader(ClassLoader parentClassLoader)
018    { super(parentClassLoader); }
019
020    public ByteArrClassLoader() { super(); }
021
022    public Class<?> defineClass(String name, byte[] classBytes)
023    { return defineClass(name, classBytes, 0, classBytes.length); }
024
025    /**
026     * Retrieves a Java {@code java.lang.Class} instance from a {@code '.class'}-File, using only
027     * the File-Name of said file.
028     * 
029     * @param classFileName The File-Name of any Java {@code '.class'}-File
030     * @return An instance of {@code java.lang.Class}
031     * @see #defineClass(String, byte[])
032     */
033    public Class<?> readJavaClass(final String classFileName)
034        throws IOException
035    {
036        final byte[] bArr = FileRW.readBinary(classFileName);
037        return this.defineClass(extractClassName(bArr), bArr);
038    }
039
040    /**
041     * Retrieves a Java {@code java.lang.Class} instance from a {@code '.class'}-File, using only
042     * the File-Name of said file.
043     * 
044     * <BR /><BR /><B CLASS=JDDescLabel>Exception Suppression:</B>
045     * 
046     * <BR />This method suppresses the {@code java.io.IOException}, and wraps it in Java's
047     * {@code UncheckedIOExceptoin}.  This can make things such as Lambda-Expressions easier.
048     * 
049     * @param classFileName The File-Name of any Java {@code '.class'}-File
050     * @return An instance of {@code java.lang.Class}
051     * @see #defineClass(String, byte[])
052     */
053    public Class<?> readJavaClassNOIOE(final String classFileName)
054    {
055        try 
056        { 
057            final byte[] bArr = FileRW.readBinary(classFileName);
058            return this.defineClass(extractClassName(bArr), bArr);
059        }
060
061        catch(IOException ioe) 
062            { throw new UncheckedIOException(ioe); }
063    }
064
065    /**
066     * Retrieves a Java {@code java.lang.Class} instance from a {@code byte[]}-Array which has 
067     * been loaded from disk from a {@code '.class'}-File.
068     * 
069     * @param bArr Any Java {@code '.class'}-File, as a {@code byte[]}-Array
070     * @return An instance of {@code java.lang.Class}
071     * @see #defineClass(String, byte[])
072     */
073    public Class<?> readJavaClass(final byte[] bArr)
074    { return this.defineClass(extractClassName(bArr), bArr); }
075
076    // Exception-Message Helper ...
077    private static final String exMsgStart(int classRecordIndex)
078    {
079        return
080            "The byte[]-Array location which must contain Constant-Pool Table-Index for this " +
081            "Class' Name  actually contained a bad-value (" + classRecordIndex + ") because it " +
082            "pointed to an index that ";
083    }
084
085    /**
086     * Loads the Constant-Pool from a Java {@code '.class'}-File and extracts that class'
087     * name.
088     * 
089     * @param bArr The {@code byte[]}-Array associated with a Java Class.
090     * @see ConstantPool
091     */
092    public static String extractClassName(byte[] bArr)
093    {
094        final ConstantPool cp = new ConstantPool(bArr);
095
096        final int i = cp.tableSizeBytes + 13;
097
098        if (i >= bArr.length) throw new ClassFileArrayException
099            ("The provided byte[]-Array isn't long enough to be a valid Java '.class' File.");
100
101        final int classRecordIndex =
102            ((bArr[i-1] & 0xFF) << 8) | (bArr[i] & 0xFF);
103
104        if (classRecordIndex >= cp.tableSize) throw new ClassFileArrayException(
105            exMsgStart(classRecordIndex) + "was out of range of the " +
106            "ContantPool's size (which is " + cp.tableSize + ')'
107        );
108
109        if (cp.tags.get(classRecordIndex) != ConstantPool.TAG_CLASS)
110            throw new ClassFileArrayException
111                (exMsgStart(classRecordIndex) + "does not contain a 'Class' Tag-Kind ");
112
113        String ret = (String) cp.dereferencedValues.get(classRecordIndex);
114
115        return ret.replace('/', '.');
116    }
117}