001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.support;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.Method;
021:
022: import org.springframework.aop.ClassFilter;
023: import org.springframework.aop.IntroductionAwareMethodMatcher;
024: import org.springframework.aop.MethodMatcher;
025: import org.springframework.util.Assert;
026:
027: /**
028: * Static utility methods for composing
029: * {@link org.springframework.aop.MethodMatcher MethodMatchers}.
030: *
031: * <p>A MethodMatcher may be evaluated statically (based on method
032: * and target class) or need further evaluation dynamically
033: * (based on arguments at the time of method invocation).
034: *
035: * @author Rod Johnson
036: * @author Rob Harrop
037: * @author Juergen Hoeller
038: * @since 11.11.2003
039: * @see ClassFilters
040: * @see Pointcuts
041: */
042: public abstract class MethodMatchers {
043:
044: /**
045: * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches.
046: * @param mm1 the first MethodMatcher
047: * @param mm2 the second MethodMatcher
048: * @return a distinct MethodMatcher that matches all methods that either
049: * of the given MethodMatchers matches
050: */
051: public static MethodMatcher union(MethodMatcher mm1,
052: MethodMatcher mm2) {
053: return new UnionMethodMatcher(mm1, mm2);
054: }
055:
056: /**
057: * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches.
058: * @param mm1 the first MethodMatcher
059: * @param cf1 the corresponding ClassFilter for the first MethodMatcher
060: * @param mm2 the second MethodMatcher
061: * @param cf2 the corresponding ClassFilter for the second MethodMatcher
062: * @return a distinct MethodMatcher that matches all methods that either
063: * of the given MethodMatchers matches
064: */
065: static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1,
066: MethodMatcher mm2, ClassFilter cf2) {
067: return new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2,
068: cf2);
069: }
070:
071: /**
072: * Match all methods that <i>both</i> of the given MethodMatchers match.
073: * @param mm1 the first MethodMatcher
074: * @param mm2 the second MethodMatcher
075: * @return a distinct MethodMatcher that matches all methods that both
076: * of the given MethodMatchers match
077: */
078: public static MethodMatcher intersection(MethodMatcher mm1,
079: MethodMatcher mm2) {
080: return new IntersectionMethodMatcher(mm1, mm2);
081: }
082:
083: /**
084: * Apply the given MethodMatcher to the given Method, supporting an
085: * {@link org.springframework.aop.IntroductionAwareMethodMatcher}
086: * (if applicable).
087: * @param mm the MethodMatcher to apply (may be an IntroductionAwareMethodMatcher)
088: * @param method the candidate method
089: * @param targetClass the target class (may be <code>null</code>, in which case
090: * the candidate class must be taken to be the method's declaring class)
091: * @param hasIntroductions <code>true</code> if the object on whose behalf we are
092: * asking is the subject on one or more introductions; <code>false</code> otherwise
093: * @return whether or not this method matches statically
094: */
095: public static boolean matches(MethodMatcher mm, Method method,
096: Class targetClass, boolean hasIntroductions) {
097: Assert.notNull(mm, "MethodMatcher must not be null");
098: return ((mm instanceof IntroductionAwareMethodMatcher && ((IntroductionAwareMethodMatcher) mm)
099: .matches(method, targetClass, hasIntroductions)) || mm
100: .matches(method, targetClass));
101: }
102:
103: /**
104: * MethodMatcher implementation for a union of two given MethodMatchers.
105: */
106: private static class UnionMethodMatcher implements
107: IntroductionAwareMethodMatcher, Serializable {
108:
109: private MethodMatcher mm1;
110: private MethodMatcher mm2;
111:
112: public UnionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) {
113: Assert.notNull(mm1, "First MethodMatcher must not be null");
114: Assert
115: .notNull(mm2,
116: "Second MethodMatcher must not be null");
117: this .mm1 = mm1;
118: this .mm2 = mm2;
119: }
120:
121: public boolean matches(Method method, Class targetClass,
122: boolean hasIntroductions) {
123: return (matchesClass1(targetClass) && MethodMatchers
124: .matches(this .mm1, method, targetClass,
125: hasIntroductions))
126: || (matchesClass2(targetClass) && MethodMatchers
127: .matches(this .mm2, method, targetClass,
128: hasIntroductions));
129: }
130:
131: public boolean matches(Method method, Class targetClass) {
132: return (matchesClass1(targetClass) && this .mm1.matches(
133: method, targetClass))
134: || (matchesClass2(targetClass) && this .mm2.matches(
135: method, targetClass));
136: }
137:
138: protected boolean matchesClass1(Class targetClass) {
139: return true;
140: }
141:
142: protected boolean matchesClass2(Class targetClass) {
143: return true;
144: }
145:
146: public boolean isRuntime() {
147: return this .mm1.isRuntime() || this .mm2.isRuntime();
148: }
149:
150: public boolean matches(Method method, Class targetClass,
151: Object[] args) {
152: return this .mm1.matches(method, targetClass, args)
153: || this .mm2.matches(method, targetClass, args);
154: }
155:
156: public boolean equals(Object obj) {
157: if (this == obj) {
158: return true;
159: }
160: if (!(obj instanceof UnionMethodMatcher)) {
161: return false;
162: }
163: UnionMethodMatcher that = (UnionMethodMatcher) obj;
164: return (this .mm1.equals(that.mm1) && this .mm2
165: .equals(that.mm2));
166: }
167:
168: public int hashCode() {
169: int hashCode = 17;
170: hashCode = 37 * hashCode + this .mm1.hashCode();
171: hashCode = 37 * hashCode + this .mm2.hashCode();
172: return hashCode;
173: }
174: }
175:
176: /**
177: * MethodMatcher implementation for a union of two given MethodMatchers,
178: * supporting an associated ClassFilter per MethodMatcher.
179: */
180: private static class ClassFilterAwareUnionMethodMatcher extends
181: UnionMethodMatcher {
182:
183: private final ClassFilter cf1;
184: private final ClassFilter cf2;
185:
186: public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1,
187: ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) {
188: super (mm1, mm2);
189: this .cf1 = cf1;
190: this .cf2 = cf2;
191: }
192:
193: protected boolean matchesClass1(Class targetClass) {
194: return this .cf1.matches(targetClass);
195: }
196:
197: protected boolean matchesClass2(Class targetClass) {
198: return this .cf2.matches(targetClass);
199: }
200:
201: public boolean equals(Object other) {
202: if (this == other) {
203: return true;
204: }
205: if (!(other instanceof ClassFilterAwareUnionMethodMatcher)) {
206: return false;
207: }
208: ClassFilterAwareUnionMethodMatcher that = (ClassFilterAwareUnionMethodMatcher) other;
209: return (this .cf1.equals(that.cf1)
210: && this .cf2.equals(that.cf2) && super .equals(other));
211: }
212: }
213:
214: /**
215: * MethodMatcher implementation for an intersection of two given MethodMatchers.
216: */
217: private static class IntersectionMethodMatcher implements
218: IntroductionAwareMethodMatcher, Serializable {
219:
220: private MethodMatcher mm1;
221: private MethodMatcher mm2;
222:
223: public IntersectionMethodMatcher(MethodMatcher mm1,
224: MethodMatcher mm2) {
225: Assert.notNull(mm1, "First MethodMatcher must not be null");
226: Assert
227: .notNull(mm2,
228: "Second MethodMatcher must not be null");
229: this .mm1 = mm1;
230: this .mm2 = mm2;
231: }
232:
233: public boolean matches(Method method, Class targetClass,
234: boolean hasIntroductions) {
235: return MethodMatchers.matches(this .mm1, method,
236: targetClass, hasIntroductions)
237: && MethodMatchers.matches(this .mm2, method,
238: targetClass, hasIntroductions);
239: }
240:
241: public boolean matches(Method method, Class targetClass) {
242: return this .mm1.matches(method, targetClass)
243: && this .mm2.matches(method, targetClass);
244: }
245:
246: public boolean isRuntime() {
247: return this .mm1.isRuntime() || this .mm2.isRuntime();
248: }
249:
250: public boolean matches(Method method, Class targetClass,
251: Object[] args) {
252: // Because a dynamic intersection may be composed of a static and dynamic part,
253: // we must avoid calling the 3-arg matches method on a dynamic matcher, as
254: // it will probably be an unsupported operation.
255: boolean aMatches = this .mm1.isRuntime() ? this .mm1.matches(
256: method, targetClass, args) : this .mm1.matches(
257: method, targetClass);
258: boolean bMatches = this .mm2.isRuntime() ? this .mm2.matches(
259: method, targetClass, args) : this .mm2.matches(
260: method, targetClass);
261: return aMatches && bMatches;
262: }
263:
264: public boolean equals(Object other) {
265: if (this == other) {
266: return true;
267: }
268: if (!(other instanceof IntersectionMethodMatcher)) {
269: return false;
270: }
271: IntersectionMethodMatcher that = (IntersectionMethodMatcher) other;
272: return (this .mm1.equals(that.mm1) && this .mm2
273: .equals(that.mm2));
274: }
275:
276: public int hashCode() {
277: int hashCode = 17;
278: hashCode = 37 * hashCode + this .mm1.hashCode();
279: hashCode = 37 * hashCode + this.mm2.hashCode();
280: return hashCode;
281: }
282: }
283:
284: }
|