1 //----------------------------------------------------------------------
2 //
3 // PerfectJPattern: "Design patterns are good but components are better!"
4 // AbstractSurrogate.java Copyright (c) 2009 Giovanni Azua Garcia
5 // bravegag@hotmail.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 3
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, see <http://www.gnu.org/licenses/>.
19 //
20 //----------------------------------------------------------------------
21 package org.perfectjpattern.core.structural;
22
23 import java.lang.reflect.*;
24 import java.util.*;
25
26 import org.apache.commons.lang.*;
27 import org.perfectjpattern.core.api.structural.*;
28
29 /**
30 * Concrete implementation of {@link ISurrogate} that groups common
31 * functionality for single instance surrogates.
32 *
33 * @param <C> <code>Component</code> or wrapper element type
34 * @param <U> <code>Underlying</code> wrapped element type
35 *
36 * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a>
37 * @version $Revision: 1.0 $Date: Apr 5, 2008 4:04:47 PM $
38 */
39 public abstract
40 class AbstractSurrogate<C, U>
41 implements ISurrogate<C>
42 {
43 //------------------------------------------------------------------------
44 // public
45 //------------------------------------------------------------------------
46 /**
47 * Creates an AbstractSurrogate<C> given a <code>Component</code>
48 * interface type and an instance of the underlying wrapped object.
49 *
50 * @param aSurrogateClass The desired <code>Surrogate</code> interface type
51 * @param anUnderlying <code>Underlying</code> instance to surrogate
52 * @throws IllegalArgumentException 'anInterface' must not be null
53 * @throws IllegalArgumentException 'anInterface' must be an interface type
54 * @throws IllegalArgumentException 'aComponent' must not be null
55 */
56 @SuppressWarnings("unchecked")
57 public
58 AbstractSurrogate(Class<C> aSurrogateClass, U anUnderlying)
59 throws IllegalArgumentException
60 {
61 Validate.notNull(aSurrogateClass, "'anInterface' must not be null");
62 Validate.isTrue(aSurrogateClass.isInterface(),
63 "'aSurrogateClass' must be an interface type");
64 Validate.notNull(anUnderlying, "'anUnderlying' must not be null");
65
66 theComponentClass = aSurrogateClass;
67 theUnderlying = anUnderlying;
68
69 final ClassLoader myClassLoader = aSurrogateClass.getClassLoader();
70 theComponent = (C) Proxy.newProxyInstance(myClassLoader, new Class[] {
71 aSurrogateClass }, this);
72 }
73
74 //------------------------------------------------------------------------
75 /**
76 * {@inheritDoc}
77 */
78 public final C
79 getComponent()
80 {
81 return theComponent;
82 }
83
84 //------------------------------------------------------------------------
85 public final Object
86 invoke(Object aProxy, Method aMethod, Object[] anArguments)
87 throws Throwable
88 {
89 Object myResult = null;
90
91 Method myMethod = null;
92 if (theLookup.containsKey(aMethod))
93 {
94 myMethod = theLookup.get(aMethod);
95 }
96 else
97 {
98 try
99 {
100 // check whether this method is implemented by the
101 // surrogate i.e. this
102 myMethod = getClass().getMethod(aMethod.getName(), aMethod.
103 getParameterTypes());
104 theLookup.put(aMethod, myMethod);
105 }
106 catch (NoSuchMethodException anException)
107 {
108 // pass it on to the underlying then
109 }
110 }
111
112 // if the method is decorated then pass control to the surrogate
113 // otherwise forward to the underlying to execute it
114 if (myMethod != null)
115 {
116 myResult = myMethod.invoke(this, anArguments);
117 }
118 else
119 {
120 // first make sure that the underlying implements such method
121 try
122 {
123 // invoke underlying
124 myResult = invokeUnderlying(aMethod, anArguments);
125
126 // cache the association
127 theLookup.put(aMethod, myMethod);
128 }
129 catch (NoSuchMethodException anException)
130 {
131 throw new NoSuchMethodError("Underlying component does not " +
132 "implement '" + aMethod.getName() + "'");
133 }
134 }
135
136 return myResult;
137 }
138
139 //------------------------------------------------------------------------
140 /**
141 * {@inheritDoc}
142 */
143 @Override
144 public final boolean
145 equals(Object anObject)
146 {
147 boolean myResult = false;
148
149 // FindBugs successfully identifies this equals implementation as
150 // unusual, the reason is that a Surrogate impersonates the real
151 // instance so this equals implementation is an attempt to provide
152 // the same identity view of the Surrogate
153 if (anObject instanceof Proxy)
154 {
155 Proxy myProxy = (Proxy) anObject;
156
157 myResult = myProxy.equals(theUnderlying);
158 }
159 else
160 {
161 myResult = theUnderlying.equals(anObject);
162 }
163
164 return myResult;
165 }
166
167 //------------------------------------------------------------------------
168 /**
169 * {@inheritDoc}
170 */
171 @Override
172 public final int
173 hashCode()
174 {
175 return theUnderlying.hashCode();
176 }
177
178 //------------------------------------------------------------------------
179 // protected
180 //------------------------------------------------------------------------
181 /**
182 * {@inheritDoc}
183 */
184 protected final U
185 getUnderlying()
186 {
187 return theUnderlying;
188 }
189
190 //------------------------------------------------------------------------
191 /**
192 * Returns the result of the Method invocation. This method has two
193 * main purposes:
194 *
195 * <ul>
196 * <li>Provides facility to invoke a method on the actual Underlying</li>
197 * <li>Concrete surrogate implementations that override this method have
198 * a single point to control access to the actual Underlying method
199 * invokation.</li>
200 * </ul>
201 *
202 * @param aMethod Method to invoke
203 * @param anArguments Array of input arguments for the Method to invoke
204 * @return result of the Method invocation
205 * @throws Throwable
206 */
207 protected Object
208 invokeUnderlying(Method aMethod, Object[] anArguments)
209 throws Throwable
210 {
211 return aMethod.invoke(theUnderlying, anArguments);
212 }
213
214 //------------------------------------------------------------------------
215 /**
216 * Returns the Component class
217 *
218 * @return the Component class
219 */
220 protected final Class<C>
221 getComponentClass()
222 {
223 return theComponentClass;
224 }
225
226 //------------------------------------------------------------------------
227 // members
228 //------------------------------------------------------------------------
229 /**
230 * <code>Component</code> instance
231 */
232 private final C theComponent;
233
234 //------------------------------------------------------------------------
235 /**
236 * <code>Component</code> class type
237 */
238 private final Class<C> theComponentClass;
239
240 /**
241 * Reference to the actual <code>Underlying</code> type wrapped by this
242 * surrogate instance.
243 */
244 private final U theUnderlying;
245
246 /**
247 * <code>Map<Method, Method><code> that contains all methods
248 * implemented by the <code>Component</code> type
249 */
250 private final Map<Method, Method> theLookup = new HashMap<Method, Method>();
251 }