001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: ReplacementClassLoader.java,v $ 023 Revision 1.7 2004/05/05 21:22:45 markl 024 Comment header updates. 025 026 Revision 1.6 2004/03/22 06:58:47 markl 027 fix for compilation error 028 029 Revision 1.5 2003/01/19 09:42:39 markl 030 Javadoc & comment header updates. 031 032 Revision 1.4 2001/08/28 21:37:54 markl 033 Fixed call to stream method. 034 035 Revision 1.3 2001/03/12 03:16:49 markl 036 *** empty log message *** 037 038 Revision 1.2 1999/01/10 03:55:05 markl 039 added GPL header & RCS tag 040 ---------------------------------------------------------------------------- 041*/ 042 043package kiwi.util; 044 045import java.util.*; 046import java.io.*; 047 048import kiwi.io.StreamUtils; 049 050/** A custom class loader that can be used to selectively replace core classes. 051 * 052 * @author Mark Lindner 053 */ 054 055public class ReplacementClassLoader extends ClassLoader 056 { 057 /** The path (relative to the anchor) relative to which classes will be 058 * loaded. 059 */ 060 public static final String codebase = "patches"; 061 private Hashtable replacements, classes; 062 private Class clazz; 063 064 /** Construct a new <code>ReplacementClassLoader</code>. The class loader 065 * will become active once a class is loaded explicitly via its 066 * <code>loadClass()</code> method. All classes loaded by that class will, 067 * in turn, be loaded by this class loader. 068 * 069 * @param replacementList A list of full-qualified names of classes that 070 * will be loaded from a local source. 071 * 072 * @param clazz The class relative to which the <i>patches</i> resource 073 * directory is located. This resource directory contains the class 074 * hierarchy of replacement classes. 075 */ 076 077 public ReplacementClassLoader(String replacementList[], Class clazz) 078 { 079 replacements = new Hashtable(); 080 classes = new Hashtable(); 081 this.clazz = clazz; 082 083 for(int i = 0; i < replacementList.length; i++) 084 { 085 replacements.put(replacementList[i], Void.class); 086 } 087 } 088 089 /** Load a class. 090 * 091 * @param name The fully-qualified name of the class to load. 092 * @param resolve A flag specifying whether the class should be resolved. 093 * 094 * @exception java.lang.ClassNotFoundException If the named class could not 095 * be found. 096 */ 097 098 protected Class loadClass(String name, boolean resolve) 099 throws ClassNotFoundException 100 { 101 Class c; 102 byte bytecodes[]; 103 104 System.out.println("Loading: " + name); 105 106 // already loaded? 107 108 if((c = (Class)classes.get(name)) == null) 109 { 110 InputStream in = null; 111 112 System.out.println("Checking if " + name + " is patched"); 113 if(replacements.get(name) != null) 114 { 115 System.out.println("Patching " + name + "..."); 116 in = clazz.getResourceAsStream(translateName(name)); 117 118 System.out.println("Translated: " + translateName(name)); 119 120 if(in == null) 121 { 122 System.out.println("Can't find it there...trying system!"); 123 c = findSystemClass(name); 124 } 125 else 126 { 127 try 128 { 129 bytecodes = StreamUtils.readStreamToByteArray(in); 130 } 131 catch(IOException ex) 132 { 133 throw(new ClassFormatError(name)); 134 } 135 finally 136 { 137 try 138 { 139 in.close(); 140 } 141 catch(IOException ex) {} 142 } 143 144 c = defineClass(name, bytecodes, 0, bytecodes.length); 145 if(c == null) 146 throw new ClassNotFoundException(name); 147 } 148 } 149 else 150 c = findSystemClass(name); 151 152 classes.put(name, c); 153 } 154 if(resolve) resolveClass(c); 155 156 return(c); 157 } 158 159 /* translate class name to resource path */ 160 161 private String translateName(String name) 162 { 163 StringBuffer sb = new StringBuffer(100); 164 StringTokenizer st = new StringTokenizer(name, "."); 165 boolean first = true; 166 167 sb.append(codebase); 168 while(st.hasMoreTokens()) 169 { 170 sb.append('/'); 171 sb.append(st.nextToken()); 172 } 173 sb.append(".txt"); 174 return(sb.toString()); 175 } 176 177 } 178 179/* end of source file */