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