001/*
002 * Copyright 2002-2017 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;
020import java.lang.reflect.Method;
021
022import org.springframework.aop.ClassFilter;
023import org.springframework.aop.MethodMatcher;
024import org.springframework.aop.Pointcut;
025import org.springframework.util.Assert;
026import org.springframework.util.ObjectUtils;
027
028/**
029 * Pointcut and method matcher for use in simple <b>cflow</b>-style pointcut.
030 * Note that evaluating such pointcuts is 10-15 times slower than evaluating
031 * normal pointcuts, but they are useful in some cases.
032 *
033 * @author Rod Johnson
034 * @author Rob Harrop
035 * @author Juergen Hoeller
036 */
037@SuppressWarnings("serial")
038public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
039
040        private Class<?> clazz;
041
042        private String methodName;
043
044        private volatile int evaluations;
045
046
047        /**
048         * Construct a new pointcut that matches all control flows below that class.
049         * @param clazz the clazz
050         */
051        public ControlFlowPointcut(Class<?> clazz) {
052                this(clazz, null);
053        }
054
055        /**
056         * Construct a new pointcut that matches all calls below the given method
057         * in the given class. If no method name is given, matches all control flows
058         * below the given class.
059         * @param clazz the clazz
060         * @param methodName the name of the method (may be {@code null})
061         */
062        public ControlFlowPointcut(Class<?> clazz, String methodName) {
063                Assert.notNull(clazz, "Class must not be null");
064                this.clazz = clazz;
065                this.methodName = methodName;
066        }
067
068
069        /**
070         * Subclasses can override this for greater filtering (and performance).
071         */
072        @Override
073        public boolean matches(Class<?> clazz) {
074                return true;
075        }
076
077        /**
078         * Subclasses can override this if it's possible to filter out
079         * some candidate classes.
080         */
081        @Override
082        public boolean matches(Method method, Class<?> targetClass) {
083                return true;
084        }
085
086        @Override
087        public boolean isRuntime() {
088                return true;
089        }
090
091        @Override
092        public boolean matches(Method method, Class<?> targetClass, Object... args) {
093                this.evaluations++;
094
095                for (StackTraceElement element : new Throwable().getStackTrace()) {
096                        if (element.getClassName().equals(this.clazz.getName()) &&
097                                        (this.methodName == null || element.getMethodName().equals(this.methodName))) {
098                                return true;
099                        }
100                }
101                return false;
102        }
103
104        /**
105         * It's useful to know how many times we've fired, for optimization.
106         */
107        public int getEvaluations() {
108                return this.evaluations;
109        }
110
111
112        @Override
113        public ClassFilter getClassFilter() {
114                return this;
115        }
116
117        @Override
118        public MethodMatcher getMethodMatcher() {
119                return this;
120        }
121
122
123        @Override
124        public boolean equals(Object other) {
125                if (this == other) {
126                        return true;
127                }
128                if (!(other instanceof ControlFlowPointcut)) {
129                        return false;
130                }
131                ControlFlowPointcut that = (ControlFlowPointcut) other;
132                return (this.clazz.equals(that.clazz)) && ObjectUtils.nullSafeEquals(that.methodName, this.methodName);
133        }
134
135        @Override
136        public int hashCode() {
137                int code = this.clazz.hashCode();
138                if (this.methodName != null) {
139                        code = 37 * code + this.methodName.hashCode();
140                }
141                return code;
142        }
143
144}