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 */