001/*
002 * Copyright 2002-2019 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 *      https://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
017package org.springframework.aop.support;
018
019import java.io.Serializable;
020
021import org.springframework.aop.ClassFilter;
022import org.springframework.aop.MethodMatcher;
023import org.springframework.aop.Pointcut;
024import org.springframework.util.Assert;
025
026/**
027 * Convenient class for building up pointcuts.
028 *
029 * <p>All methods return {@code ComposablePointcut}, so we can use concise idioms
030 * like in the following example.
031 *
032 * <pre class="code">Pointcut pc = new ComposablePointcut()
033 *                      .union(classFilter)
034 *                      .intersection(methodMatcher)
035 *                      .intersection(pointcut);</pre>
036 *
037 * @author Rod Johnson
038 * @author Juergen Hoeller
039 * @author Rob Harrop
040 * @since 11.11.2003
041 * @see Pointcuts
042 */
043public class ComposablePointcut implements Pointcut, Serializable {
044
045        /** use serialVersionUID from Spring 1.2 for interoperability */
046        private static final long serialVersionUID = -2743223737633663832L;
047
048        private ClassFilter classFilter;
049
050        private MethodMatcher methodMatcher;
051
052
053        /**
054         * Create a default ComposablePointcut, with {@code ClassFilter.TRUE}
055         * and {@code MethodMatcher.TRUE}.
056         */
057        public ComposablePointcut() {
058                this.classFilter = ClassFilter.TRUE;
059                this.methodMatcher = MethodMatcher.TRUE;
060        }
061
062        /**
063         * Create a ComposablePointcut based on the given Pointcut.
064         * @param pointcut the original Pointcut
065         */
066        public ComposablePointcut(Pointcut pointcut) {
067                Assert.notNull(pointcut, "Pointcut must not be null");
068                this.classFilter = pointcut.getClassFilter();
069                this.methodMatcher = pointcut.getMethodMatcher();
070        }
071
072        /**
073         * Create a ComposablePointcut for the given ClassFilter,
074         * with {@code MethodMatcher.TRUE}.
075         * @param classFilter the ClassFilter to use
076         */
077        public ComposablePointcut(ClassFilter classFilter) {
078                Assert.notNull(classFilter, "ClassFilter must not be null");
079                this.classFilter = classFilter;
080                this.methodMatcher = MethodMatcher.TRUE;
081        }
082
083        /**
084         * Create a ComposablePointcut for the given MethodMatcher,
085         * with {@code ClassFilter.TRUE}.
086         * @param methodMatcher the MethodMatcher to use
087         */
088        public ComposablePointcut(MethodMatcher methodMatcher) {
089                Assert.notNull(methodMatcher, "MethodMatcher must not be null");
090                this.classFilter = ClassFilter.TRUE;
091                this.methodMatcher = methodMatcher;
092        }
093
094        /**
095         * Create a ComposablePointcut for the given ClassFilter and MethodMatcher.
096         * @param classFilter the ClassFilter to use
097         * @param methodMatcher the MethodMatcher to use
098         */
099        public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher) {
100                Assert.notNull(classFilter, "ClassFilter must not be null");
101                Assert.notNull(methodMatcher, "MethodMatcher must not be null");
102                this.classFilter = classFilter;
103                this.methodMatcher = methodMatcher;
104        }
105
106
107        /**
108         * Apply a union with the given ClassFilter.
109         * @param other the ClassFilter to apply a union with
110         * @return this composable pointcut (for call chaining)
111         */
112        public ComposablePointcut union(ClassFilter other) {
113                this.classFilter = ClassFilters.union(this.classFilter, other);
114                return this;
115        }
116
117        /**
118         * Apply an intersection with the given ClassFilter.
119         * @param other the ClassFilter to apply an intersection with
120         * @return this composable pointcut (for call chaining)
121         */
122        public ComposablePointcut intersection(ClassFilter other) {
123                this.classFilter = ClassFilters.intersection(this.classFilter, other);
124                return this;
125        }
126
127        /**
128         * Apply a union with the given MethodMatcher.
129         * @param other the MethodMatcher to apply a union with
130         * @return this composable pointcut (for call chaining)
131         */
132        public ComposablePointcut union(MethodMatcher other) {
133                this.methodMatcher = MethodMatchers.union(this.methodMatcher, other);
134                return this;
135        }
136
137        /**
138         * Apply an intersection with the given MethodMatcher.
139         * @param other the MethodMatcher to apply an intersection with
140         * @return this composable pointcut (for call chaining)
141         */
142        public ComposablePointcut intersection(MethodMatcher other) {
143                this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other);
144                return this;
145        }
146
147        /**
148         * Apply a union with the given Pointcut.
149         * <p>Note that for a Pointcut union, methods will only match if their
150         * original ClassFilter (from the originating Pointcut) matches as well.
151         * MethodMatchers and ClassFilters from different Pointcuts will never
152         * get interleaved with each other.
153         * @param other the Pointcut to apply a union with
154         * @return this composable pointcut (for call chaining)
155         */
156        public ComposablePointcut union(Pointcut other) {
157                this.methodMatcher = MethodMatchers.union(
158                                this.methodMatcher, this.classFilter, other.getMethodMatcher(), other.getClassFilter());
159                this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter());
160                return this;
161        }
162
163        /**
164         * Apply an intersection with the given Pointcut.
165         * @param other the Pointcut to apply an intersection with
166         * @return this composable pointcut (for call chaining)
167         */
168        public ComposablePointcut intersection(Pointcut other) {
169                this.classFilter = ClassFilters.intersection(this.classFilter, other.getClassFilter());
170                this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other.getMethodMatcher());
171                return this;
172        }
173
174
175        @Override
176        public ClassFilter getClassFilter() {
177                return this.classFilter;
178        }
179
180        @Override
181        public MethodMatcher getMethodMatcher() {
182                return this.methodMatcher;
183        }
184
185        @Override
186        public boolean equals(Object other) {
187                if (this == other) {
188                        return true;
189                }
190                if (!(other instanceof ComposablePointcut)) {
191                        return false;
192                }
193                ComposablePointcut otherPointcut = (ComposablePointcut) other;
194                return (this.classFilter.equals(otherPointcut.classFilter) &&
195                                this.methodMatcher.equals(otherPointcut.methodMatcher));
196        }
197
198        @Override
199        public int hashCode() {
200                return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode();
201        }
202
203        @Override
204        public String toString() {
205                return "ComposablePointcut: " + this.classFilter + ", " + this.methodMatcher;
206        }
207
208}