1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package Torello.Java.Additional;

import java.io.UncheckedIOException;

import Torello.Java.FileRW;

import java.io.IOException;

/**
 * This utilizes the class {@link ConstantPool} to facilitate loading a Java-Class
 * from any directory.  There is no requirement that the {@code '.class'} file be properly placed
 * in a sub-directory that has a name which mirrors the full Package-Name of the class which is to
 * be loaded.
 */
public class ByteArrClassLoader extends ClassLoader
{
    public ByteArrClassLoader(ClassLoader parentClassLoader)
    { super(parentClassLoader); }

    public ByteArrClassLoader() { super(); }

    public Class<?> defineClass(String name, byte[] classBytes)
    { return defineClass(name, classBytes, 0, classBytes.length); }

    /**
     * Retrieves a Java {@code java.lang.Class} instance from a {@code '.class'}-File, using only
     * the File-Name of said file.
     * 
     * @param classFileName The File-Name of any Java {@code '.class'}-File
     * @return An instance of {@code java.lang.Class}
     * @see #defineClass(String, byte[])
     */
    public Class<?> readJavaClass(final String classFileName)
        throws IOException
    {
        final byte[] bArr = FileRW.readBinary(classFileName);
        return this.defineClass(extractClassName(bArr), bArr);
    }

    /**
     * Retrieves a Java {@code java.lang.Class} instance from a {@code '.class'}-File, using only
     * the File-Name of said file.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Exception Suppression:</B>
     * 
     * <BR />This method suppresses the {@code java.io.IOException}, and wraps it in Java's
     * {@code UncheckedIOExceptoin}.  This can make things such as Lambda-Expressions easier.
     * 
     * @param classFileName The File-Name of any Java {@code '.class'}-File
     * @return An instance of {@code java.lang.Class}
     * @see #defineClass(String, byte[])
     */
    public Class<?> readJavaClassNOIOE(final String classFileName)
    {
        try 
        { 
            final byte[] bArr = FileRW.readBinary(classFileName);
            return this.defineClass(extractClassName(bArr), bArr);
        }

        catch(IOException ioe) 
            { throw new UncheckedIOException(ioe); }
    }

    /**
     * Retrieves a Java {@code java.lang.Class} instance from a {@code byte[]}-Array which has 
     * been loaded from disk from a {@code '.class'}-File.
     * 
     * @param bArr Any Java {@code '.class'}-File, as a {@code byte[]}-Array
     * @return An instance of {@code java.lang.Class}
     * @see #defineClass(String, byte[])
     */
    public Class<?> readJavaClass(final byte[] bArr)
    { return this.defineClass(extractClassName(bArr), bArr); }

    // Exception-Message Helper ...
    private static final String exMsgStart(int classRecordIndex)
    {
        return
            "The byte[]-Array location which must contain Constant-Pool Table-Index for this " +
            "Class' Name  actually contained a bad-value (" + classRecordIndex + ") because it " +
            "pointed to an index that ";
    }

    /**
     * Loads the Constant-Pool from a Java {@code '.class'}-File and extracts that class'
     * name.
     * 
     * @param bArr The {@code byte[]}-Array associated with a Java Class.
     * @see ConstantPool
     */
    public static String extractClassName(byte[] bArr)
    {
        final ConstantPool cp = new ConstantPool(bArr);

        final int i = cp.tableSizeBytes + 13;

        if (i >= bArr.length) throw new ClassFileArrayException
            ("The provided byte[]-Array isn't long enough to be a valid Java '.class' File.");

        final int classRecordIndex =
            ((bArr[i-1] & 0xFF) << 8) | (bArr[i] & 0xFF);

        if (classRecordIndex >= cp.tableSize) throw new ClassFileArrayException(
            exMsgStart(classRecordIndex) + "was out of range of the " +
            "ContantPool's size (which is " + cp.tableSize + ')'
        );

        if (cp.tags.get(classRecordIndex) != ConstantPool.TAG_CLASS)
            throw new ClassFileArrayException
                (exMsgStart(classRecordIndex) + "does not contain a 'Class' Tag-Kind ");

        String ret = (String) cp.dereferencedValues.get(classRecordIndex);

        return ret.replace('/', '.');
    }
}