001: /*
002: * Copyright 2002-2006 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.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.Serializable;
022: import java.lang.reflect.Method;
023: import java.util.Arrays;
024:
025: import org.springframework.util.Assert;
026: import org.springframework.util.ObjectUtils;
027: import org.springframework.util.StringUtils;
028:
029: /**
030: * Abstract base regular expression pointcut bean. JavaBean properties are:
031: * <ul>
032: * <li>pattern: regular expression for the fully-qualified method names to match.
033: * The exact regexp syntax will depend on the subclass (e.g. Perl5 regular expressions)
034: * <li>patterns: alternative property taking a String array of patterns. The result will
035: * be the union of these patterns.
036: * </ul>
037: *
038: * <p>Note: the regular expressions must be a match. For example,
039: * <code>.*get.*</code> will match com.mycom.Foo.getBar().
040: * <code>get.*</code> will not.
041: *
042: * <p>This base class is serializable. Subclasses should declare all fields transient
043: * - the initPatternRepresentation method in this class will be invoked again on the
044: * client side on deserialization.
045: *
046: * @author Rod Johnson
047: * @author Juergen Hoeller
048: * @author Rob Harrop
049: * @since 1.1
050: * @see Perl5RegexpMethodPointcut
051: * @see JdkRegexpMethodPointcut
052: */
053: public abstract class AbstractRegexpMethodPointcut extends
054: StaticMethodMatcherPointcut implements Serializable {
055:
056: /** Regular expressions to match */
057: private String[] patterns = new String[0];
058:
059: /** Regaular expressions <strong>not</strong> to match */
060: private String[] excludedPatterns = new String[0];
061:
062: /**
063: * Convenience method when we have only a single pattern.
064: * Use either this method or {@link #setPatterns}, not both.
065: * @see #setPatterns
066: */
067: public void setPattern(String pattern) {
068: setPatterns(new String[] { pattern });
069: }
070:
071: /**
072: * Set the regular expressions defining methods to match.
073: * Matching will be the union of all these; if any match,
074: * the pointcut matches.
075: */
076: public void setPatterns(String[] patterns) {
077: Assert.notEmpty(patterns, "'patterns' must not be empty");
078: this .patterns = new String[patterns.length];
079: for (int i = 0; i < patterns.length; i++) {
080: this .patterns[i] = StringUtils.trimWhitespace(patterns[i]);
081: }
082: initPatternRepresentation(this .patterns);
083: }
084:
085: /**
086: * Return the regular expressions for method matching.
087: */
088: public String[] getPatterns() {
089: return this .patterns;
090: }
091:
092: /**
093: * Convenience method when we have only a single exclusion pattern.
094: * Use either this method or {@link #setExcludedPatterns}, not both.
095: * @see #setExcludedPatterns
096: */
097: public void setExcludedPattern(String excludedPattern) {
098: setExcludedPatterns(new String[] { excludedPattern });
099: }
100:
101: /**
102: * Set the regular expressions defining methods to match for exclusion.
103: * Matching will be the union of all these; if any match,
104: * the pointcut matches.
105: */
106: public void setExcludedPatterns(String[] excludedPatterns) {
107: Assert.notEmpty(excludedPatterns,
108: "'excludedPatterns' must not be empty");
109: this .excludedPatterns = new String[excludedPatterns.length];
110: for (int i = 0; i < excludedPatterns.length; i++) {
111: this .excludedPatterns[i] = StringUtils
112: .trimWhitespace(excludedPatterns[i]);
113: }
114: initExcludedPatternRepresentation(this .excludedPatterns);
115: }
116:
117: /**
118: * Returns the regular expressions for exclusion matching.
119: */
120: public String[] getExcludedPatterns() {
121: return this .excludedPatterns;
122: }
123:
124: /**
125: * Try to match the regular expression against the fully qualified name
126: * of the method's declaring class, plus the name of the method.
127: * Note that the declaring class is that class that originally declared
128: * the method, not necessarily the class that's currently exposing it.
129: * <p>For example, "java.lang.Object.hashCode" matches any subclass
130: * of Object's <code>hashCode()</code> method.
131: */
132: public final boolean matches(Method method, Class targetClass) {
133: // TODO use target class here?
134: String patt = method.getDeclaringClass().getName() + "."
135: + method.getName();
136: for (int i = 0; i < this .patterns.length; i++) {
137: boolean matched = matches(patt, i);
138: if (matched) {
139: for (int j = 0; j < this .excludedPatterns.length; j++) {
140: boolean excluded = matchesExclusion(patt, j);
141: if (excluded) {
142: return false;
143: }
144: }
145: return true;
146: }
147: }
148: return false;
149: }
150:
151: /**
152: * Subclasses must implement this to initialize regexp pointcuts.
153: * Can be invoked multiple times.
154: * <p>This method will be invoked from the setPatterns method,
155: * and also on deserialization.
156: * @param patterns the patterns to initialize
157: * @throws IllegalArgumentException in case of an invalid pattern
158: */
159: protected abstract void initPatternRepresentation(String[] patterns)
160: throws IllegalArgumentException;
161:
162: protected abstract void initExcludedPatternRepresentation(
163: String[] excludedPatterns) throws IllegalArgumentException;
164:
165: /**
166: * Does the pattern at the given index match this string?
167: * @param pattern <code>String</code> pattern to match
168: * @param patternIndex index of pattern from 0
169: * @return <code>true</code> if there is a match, else <code>false</code>.
170: */
171: protected abstract boolean matches(String pattern, int patternIndex);
172:
173: /**
174: * Does the exclusion pattern at the given index match this string?
175: * @param pattern <code>String</code> pattern to match.
176: * @param patternIndex index of pattern starting from 0.
177: * @return <code>true</code> if there is a match, else <code>false</code>.
178: */
179: protected abstract boolean matchesExclusion(String pattern,
180: int patternIndex);
181:
182: public boolean equals(Object other) {
183: if (this == other) {
184: return true;
185: }
186: if (!(other instanceof AbstractRegexpMethodPointcut)) {
187: return false;
188: }
189: AbstractRegexpMethodPointcut otherPointcut = (AbstractRegexpMethodPointcut) other;
190: return (Arrays.equals(this .patterns, otherPointcut.patterns) && Arrays
191: .equals(this .excludedPatterns,
192: otherPointcut.excludedPatterns));
193: }
194:
195: public int hashCode() {
196: int result = 27;
197: for (int i = 0; i < this .patterns.length; i++) {
198: String pattern = this .patterns[i];
199: result = 13 * result + pattern.hashCode();
200: }
201: for (int i = 0; i < this .excludedPatterns.length; i++) {
202: String excludedPattern = this .excludedPatterns[i];
203: result = 13 * result + excludedPattern.hashCode();
204: }
205: return result;
206: }
207:
208: public String toString() {
209: return getClass().getName() + ": patterns "
210: + ObjectUtils.nullSafeToString(this .patterns)
211: + ", excluded patterns "
212: + ObjectUtils.nullSafeToString(this .excludedPatterns);
213: }
214:
215: //---------------------------------------------------------------------
216: // Serialization support
217: //---------------------------------------------------------------------
218:
219: private void readObject(ObjectInputStream ois) throws IOException,
220: ClassNotFoundException {
221: // Rely on default serialization; just initialize state after deserialization.
222: ois.defaultReadObject();
223:
224: // Ask subclass to reinitialize.
225: initPatternRepresentation(this.patterns);
226: initExcludedPatternRepresentation(this.excludedPatterns);
227: }
228:
229: }
|