1 //----------------------------------------------------------------------
2 //
3 // PerfectJPattern: "Design patterns are good but components are better!"
4 // NameMatchAdaptingStrategy.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.adapter;
22
23 import java.lang.reflect.*;
24 import java.util.*;
25
26 import org.apache.commons.lang.*;
27 import org.perfectjpattern.core.api.structural.adapter.*;
28
29 /**
30 * Concrete implementation of {@link IAdaptingStrategy}. Implements the name
31 * matching strategy. The <code>Adaptee</code> and <code>Target</code> methods
32 * are matched by name. The method correspondence is provided by the user as
33 * a {@link Map} of <code>Target</code> method names keyed by <code>
34 * Adaptee</code> method names. The mapping does not have to be exhaustive,
35 * those methods not explicitly mapped will be resolved against the Adaptee
36 * and Adapter using exact match.
37 *
38 * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a>
39 * @version $Revision: 1.0 $Date: Jan 28, 2009 3:42:25 PM $
40 */
41 public final
42 class NameMatchAdaptingStrategy
43 extends ExactMatchAdaptingStrategy
44 implements IAdaptingStrategy
45 {
46 //------------------------------------------------------------------------
47 // public
48 //------------------------------------------------------------------------
49 /**
50 * Constructs a {@link NameMatchAdaptingStrategy} from a map that defines
51 * the correspondence between Target and Adaptee i.e. Adaptee method names
52 * keyed by Target method names.
53 *
54 * @param aMethodsMapping Map of Target method names keyed by Target
55 * method names
56 * @throws IllegalArgumentException 'aMethodsMap' must not be null
57 */
58 public
59 NameMatchAdaptingStrategy(Map<String, String> aMethodsMapping)
60 {
61 Validate.notNull(aMethodsMapping, "'aMethodsMap' must not be null");
62
63 theMethodsMapping = aMethodsMapping;
64 }
65
66 //------------------------------------------------------------------------
67 /**
68 * {@inheritDoc}
69 */
70 @Override
71 public void
72 validate(Class<?> aTargetClass, Object anAdaptee, Object anAdapter)
73 throws NoSuchMethodError
74 {
75 Validate.notNull(aTargetClass, "'aTargetClass' must not be null");
76 Validate.notNull(anAdaptee, "'anAdaptee' must not be null");
77 Validate.notNull(anAdapter, "'anAdapter' must not be null");
78
79 Class<?> myAdapteeClass = anAdaptee.getClass();
80
81 // check that all aMethodsMap keys exist in the Target interface and
82 // that they match the corresponding definitions in the Adaptee
83 // interface
84 Set<String> myTargetMethods = new HashSet<String>();
85 for (Method myTargetMethod : aTargetClass.getMethods())
86 {
87 myTargetMethods.add(myTargetMethod.getName());
88 }
89
90 for (Map.Entry<String, String> myEntry : theMethodsMapping.entrySet())
91 {
92 String myTargetMethodName = myEntry.getKey();
93 String myAdapteeMethodName = myEntry.getValue();
94
95 boolean myFound = false;
96 for (Method myAdapteeMethod : myAdapteeClass.getMethods())
97 {
98 if (myAdapteeMethodName.equals(myAdapteeMethod.getName()))
99 {
100 Class<?>[] myParameterTypes = myAdapteeMethod.
101 getParameterTypes();
102
103 try
104 {
105 aTargetClass.getMethod(myTargetMethodName,
106 myParameterTypes);
107
108 myTargetMethods.remove(myTargetMethodName);
109
110 myFound = true;
111
112 break;
113 }
114 // CHECKSTYLE:OFF
115 catch (Exception anException)
116 // CHECKSTYLE:ON
117 {
118 // not found
119 }
120 }
121 }
122
123 if (!myFound)
124 {
125 throw new NoSuchMethodError("Method mismatch between Target " +
126 "and Adaptee '" + myTargetMethodName + "'");
127 }
128 }
129
130 // check that if the mapping is not exhaustive then the adapter
131 // implements the remaining methods
132 if (myTargetMethods.size() > 0)
133 {
134 validate(aTargetClass, anAdaptee, myTargetMethods);
135 validate(aTargetClass, anAdapter, myTargetMethods);
136
137 // if still there are remaining then throw exception
138 if (myTargetMethods.size() > 0)
139 {
140 throw new NoSuchMethodError("Not all target methods " +
141 "are implemented: '" + Arrays.toString(myTargetMethods.
142 toArray()) + "'");
143 }
144 }
145 }
146
147 //------------------------------------------------------------------------
148 /**
149 * {@inheritDoc}
150 */
151 @Override
152 public Method
153 resolve(Class<?> aTargetClass, Object anAdaptee, Object anAdapter,
154 Method aTargetMethod)
155 throws IllegalArgumentException, NoSuchMethodError
156 {
157 Validate.notNull(aTargetClass, "'aTargetClass' must not be null");
158 Validate.notNull(anAdaptee, "'anAdaptee' must not be null");
159 Validate.notNull(anAdapter, "'anAdapter' must not be null");
160 Validate.notNull(aTargetMethod, "'aTargetMethod' must not be null");
161
162 String myAdapteeMethodName = theMethodsMapping.get(aTargetMethod.
163 getName());
164
165 Method myResultMethod = null;
166 if (myAdapteeMethodName != null)
167 {
168 myResultMethod = super.resolve(anAdaptee, aTargetMethod,
169 myAdapteeMethodName);
170 }
171 else
172 {
173 myResultMethod = super.resolve(aTargetClass, anAdaptee, anAdapter,
174 aTargetMethod);
175 }
176
177 return myResultMethod;
178 }
179
180 //------------------------------------------------------------------------
181 // members
182 //------------------------------------------------------------------------
183 private final Map<String, String> theMethodsMapping;
184 }