001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.proxy.invoker;
019    
020    import org.apache.commons.proxy.Invoker;
021    import org.apache.commons.proxy.ObjectProvider;
022    
023    import java.lang.reflect.Method;
024    
025    /**
026     * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">&quot;duck typing&quot;</a>, meaning
027     * that it finds a matching method on the object returned from the target provider and invokes it.  This class is
028     * useful for adapting an existing class to an interface it does not implement.
029     * <p>
030     * <b>Example:</b>
031     * </p>
032     * <p>
033     * <pre>
034     * public class LegacyDuck // Does not implement interface!
035     * {
036     *   public void quack()
037     *   {
038     *     // Quacking logic...
039     *   }
040     * }
041     * <p/>
042     * public interface Duck
043     * {
044     *   public void quack();
045     * }
046     * <p/>
047     * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck
048     * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
049     * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } );
050     * </pre>
051     * </p>
052     */
053    public class DuckTypingInvoker implements Invoker
054    {
055    //----------------------------------------------------------------------------------------------------------------------
056    // Fields
057    //----------------------------------------------------------------------------------------------------------------------
058    
059        private final ObjectProvider targetProvider;
060    
061    //----------------------------------------------------------------------------------------------------------------------
062    // Constructors
063    //----------------------------------------------------------------------------------------------------------------------
064    
065        public DuckTypingInvoker( final ObjectProvider targetProvider )
066        {
067            this.targetProvider = targetProvider;
068        }
069    
070    //----------------------------------------------------------------------------------------------------------------------
071    // Interface Invoker
072    //----------------------------------------------------------------------------------------------------------------------
073    
074        public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable
075        {
076            final Object target = targetProvider.getObject();
077            final Class targetClass = target.getClass();
078            try
079            {
080                final Method targetMethod = targetClass.getMethod( method.getName(), method.getParameterTypes() );
081                if ( method.getReturnType().isAssignableFrom( targetMethod.getReturnType() ) )
082                {
083                    return targetMethod.invoke( target, arguments );
084                }
085                throw new UnsupportedOperationException(
086                        "Target type " + targetClass.getName() + " method has incompatible return type." );
087            }
088            catch ( NoSuchMethodException e )
089            {
090                throw new UnsupportedOperationException(
091                        "Target type " + targetClass.getName() + " does not have a method matching " + method + "." );
092            }
093        }
094    }