001package com.ganteater.ae;
002
003import java.util.Hashtable;
004
005/**
006 * A simple test class loader capable of loading from multiple sources, such as
007 * local files or a URL.
008 *
009 * This class is derived from an article by Chuck McManis
010 * http://www.javaworld.com/javaworld/jw-10-1996/indepth.src.html with large
011 * modifications.
012 *
013 * Note that this has been updated to use the non-deprecated version of
014 * defineClass() -- JDM.
015 *
016 * @author Jack Harich - 8/18/97
017 * @author John D. Mitchell - 99.03.04
018 */
019public abstract class MultiClassLoader extends ClassLoader {
020
021//---------- Fields --------------------------------------
022        private Hashtable<String, Class<?>> classes = new Hashtable<>();
023        private char classNameReplacementChar;
024
025        protected boolean monitorOn = false;
026        protected boolean sourceMonitorOn = true;
027
028//---------- Initialization ------------------------------
029        public MultiClassLoader() {
030        }
031
032//---------- Superclass Overrides ------------------------
033        /**
034         * This is a simple version for external clients since they will always want the
035         * class resolved before it is returned to them.
036         */
037        public Class<?> loadClass(String className) throws ClassNotFoundException {
038                return (loadClass(className, true));
039        }
040
041//---------- Abstract Implementation ---------------------
042        public synchronized Class<?> loadClass(String className, boolean resolveIt) throws ClassNotFoundException {
043
044                Class<?> result;
045                byte[] classBytes;
046                monitor(">> MultiClassLoader.loadClass(" + className + ", " + resolveIt + ")");
047
048                // ----- Check our local cache of classes
049                result = (Class<?>) classes.get(className);
050                if (result != null) {
051                        monitor(">> returning cached activeTaskArray.");
052                        return result;
053                }
054
055                // ----- Check with the primordial class loader
056                try {
057                        result = super.findSystemClass(className);
058                        monitor(">> returning system class (in CLASSPATH).");
059                        return result;
060                } catch (ClassNotFoundException e) {
061                        monitor(">> Not a system class.");
062                }
063
064                // ----- Try to load it from preferred source
065                // Note loadClassBytes() is an abstract method
066                classBytes = loadClassBytes(className);
067                if (classBytes == null) {
068                        throw new ClassNotFoundException();
069                }
070
071                // ----- Define it (parse the class file)
072                result = defineClass(className, classBytes, 0, classBytes.length);
073                if (result == null) {
074                        throw new ClassFormatError();
075                }
076
077                // ----- Resolve if necessary
078                if (resolveIt)
079                        resolveClass(result);
080
081                // Done
082                classes.put(className, result);
083                monitor(">> Returning newly loaded class.");
084                return result;
085        }
086
087//---------- Public Methods ------------------------------
088        /**
089         * This optional call allows a class name such as "COM.test.Hello" to be changed
090         * to "COM_test_Hello", which is useful for storing classes from different
091         * packages in the same retrival directory. In the above example the char would
092         * be '_'.
093         */
094        public void setClassNameReplacementChar(char replacement) {
095                classNameReplacementChar = replacement;
096        }
097
098//---------- Protected Methods ---------------------------
099        protected abstract byte[] loadClassBytes(String className);
100
101        protected String formatClassName(String className) {
102                if (classNameReplacementChar == '\u0000') {
103                        // '/' is used to map the package to the path
104                        return className.replace('.', '/') + ".class";
105                } else {
106                        // Replace '.' with custom char, such as '_'
107                        return className.replace('.', classNameReplacementChar) + ".class";
108                }
109        }
110
111        protected void monitor(String text) {
112                if (monitorOn)
113                        print(text);
114        }
115
116//--- Std
117        protected static void print(String text) {
118                System.out.println(text);
119        }
120
121} // End class