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