1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.proxy.invoker;
19
20 import org.apache.commons.proxy.Invoker;
21 import org.apache.commons.proxy.ObjectProvider;
22
23 import java.lang.reflect.Method;
24
25 /**
26 * An invoker which supports <a href="http://en.wikipedia.org/wiki/Duck_typing">"duck typing"</a>, meaning
27 * that it finds a matching method on the object returned from the target provider and invokes it. This class is
28 * useful for adapting an existing class to an interface it does not implement.
29 * <p>
30 * <b>Example:</b>
31 * </p>
32 * <p>
33 * <pre>
34 * public class LegacyDuck // Does not implement interface!
35 * {
36 * public void quack()
37 * {
38 * // Quacking logic...
39 * }
40 * }
41 * <p/>
42 * public interface Duck
43 * {
44 * public void quack();
45 * }
46 * <p/>
47 * ObjectProvider targetProvider = new ConstantProvider(new LegacyDuck()); // Always returns a "legacy" duck
48 * DuckTypingInvoker invoker = new DuckTypingInvoker(targetProvider);
49 * Duck duck = ( Duck )proxyFactory.createInvokerProxy( invoker, new Class[] { Duck.class } );
50 * </pre>
51 * </p>
52 */
53 public class DuckTypingInvoker implements Invoker
54 {
55 //----------------------------------------------------------------------------------------------------------------------
56 // Fields
57 //----------------------------------------------------------------------------------------------------------------------
58
59 private final ObjectProvider targetProvider;
60
61 //----------------------------------------------------------------------------------------------------------------------
62 // Constructors
63 //----------------------------------------------------------------------------------------------------------------------
64
65 public DuckTypingInvoker( final ObjectProvider targetProvider )
66 {
67 this.targetProvider = targetProvider;
68 }
69
70 //----------------------------------------------------------------------------------------------------------------------
71 // Interface Invoker
72 //----------------------------------------------------------------------------------------------------------------------
73
74 public Object invoke( final Object proxy, final Method method, final Object[] arguments ) throws Throwable
75 {
76 final Object target = targetProvider.getObject();
77 final Class targetClass = target.getClass();
78 try
79 {
80 final Method targetMethod = targetClass.getMethod( method.getName(), method.getParameterTypes() );
81 if ( method.getReturnType().isAssignableFrom( targetMethod.getReturnType() ) )
82 {
83 return targetMethod.invoke( target, arguments );
84 }
85 throw new UnsupportedOperationException(
86 "Target type " + targetClass.getName() + " method has incompatible return type." );
87 }
88 catch ( NoSuchMethodException e )
89 {
90 throw new UnsupportedOperationException(
91 "Target type " + targetClass.getName() + " does not have a method matching " + method + "." );
92 }
93 }
94 }