/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package net.sf.cglib;

import java.lang.reflect.*;
import java.util.*;
import net.sf.cglib.util.*;

/**
 * Similar to Delegator, but dynamically looks up the interface
 * implementation for every method call.
 * @author Chris Nokleberg
 * @version $Id: LookupDelegator.java,v 1.1 2003-07-08 05:56:46 herbyderby Exp $
 */
abstract public class LookupDelegator {
    private static final FactoryCache cache = new FactoryCache(LookupDelegator.class);
    private static final Constructor GENERATOR =
      ReflectUtils.findConstructor("LookupDelegator$Generator(Class[])");
    private static final Method NEW_INSTANCE =
      ReflectUtils.findMethod("LookupDelegator.newInstance(LookupDelegator$Callback)");
    private static final Method LOOKUP_DELEGATE =
      ReflectUtils.findMethod("LookupDelegator$Callback.lookupDelegate(String)");
                        
    public interface Callback {
        Object lookupDelegate(String className);
    }

    protected Callback callback;
    
    protected LookupDelegator() {
    }

    abstract protected LookupDelegator newInstance(Callback callback);

    /**
     * Returns an object that implements all of the specified interfaces.
     * When any method of the object is called, the callback 
     * is used to lookup an object of the appropriate type by passing
     * the fully-qualified name of the corresponding interface.
     * The object returned by the callback is then used to invoke the
     * @param interfaces the array of interfaces to implement
     * @param callback used to lookup implementing objects during each method call
     * @param loader ClassLoader for enhanced class, uses "current" if null
     */
    public static Object create(Class[] interfaces, Callback callback, ClassLoader loader) {
        LookupDelegator factory =
            (LookupDelegator)cache.getFactory(loader,
                                              ConstructorProxy.newClassKey(interfaces),
                                              GENERATOR,
                                              interfaces);
        return factory.newInstance(callback);
    }

    private static class Generator extends CodeGenerator {
        private Class[] interfaces;
        
        public Generator(Class[] interfaces) {
            setSuperclass(LookupDelegator.class);
            addInterfaces(interfaces);
            this.interfaces = interfaces;
        }

        protected void generate() throws NoSuchFieldException {
            null_constructor();

            // newInstance
            begin_method(NEW_INSTANCE);
            new_instance_this();
            dup();
            invoke_constructor_this();
            dup();
            load_arg(0);
            super_putfield("callback");
            return_value();
            end_method();

            // method proxies
            Set methodSet = new HashSet();
            for (int i = 0; i < interfaces.length; i++) {
                Method[] methods = interfaces[i].getMethods();
                for (int j = 0; j < methods.length; j++) {
                    Method method = methods[j];
                    if (!methodSet.contains(method)) {
                        methodSet.add(method);
                        generateProxy(method);
                    }
                }
            }
        }

        protected void generateProxy(Method method) throws NoSuchFieldException {
            begin_method(method);
            load_this();
            super_getfield("callback");
            push(method.getDeclaringClass().getName());
            invoke(LOOKUP_DELEGATE);
            checkcast(method.getDeclaringClass());
            load_args();
            invoke(method);
            return_value();
            end_method();
        }
    }
}

@@ -2835,17 +2835,17 @@
 ava,v 1.
-1
+2
  2003-07
@@ -2849,19 +2849,19 @@
 -07-
-08 05:56:46
+10 00:05:30
  her
@@ -5804,27 +5804,42 @@
 Proxy(method
+, interfaces%5Bi%5D
 );%0A
-
             
@@ -5941,16 +5941,30 @@
 d method
+, Class source
 ) throws
@@ -6107,34 +6107,14 @@
 ush(
-method.getDeclaringClass()
+source
 .get

Make sure the classname passed to lookupDelegate is one of argument interfaces