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.aspectj;
018
019import org.aspectj.weaver.tools.PointcutParser;
020import org.aspectj.weaver.tools.TypePatternMatcher;
021
022import org.springframework.aop.ClassFilter;
023import org.springframework.util.Assert;
024import org.springframework.util.StringUtils;
025
026/**
027 * Spring AOP {@link ClassFilter} implementation using AspectJ type matching.
028 *
029 * @author Rod Johnson
030 * @author Juergen Hoeller
031 * @since 2.0
032 */
033public class TypePatternClassFilter implements ClassFilter {
034
035        private String typePattern;
036
037        private TypePatternMatcher aspectJTypePatternMatcher;
038
039
040        /**
041         * Creates a new instance of the {@link TypePatternClassFilter} class.
042         * <p>This is the JavaBean constructor; be sure to set the
043         * {@link #setTypePattern(String) typePattern} property, else a
044         * no doubt fatal {@link IllegalStateException} will be thrown
045         * when the {@link #matches(Class)} method is first invoked.
046         */
047        public TypePatternClassFilter() {
048        }
049
050        /**
051         * Create a fully configured {@link TypePatternClassFilter} using the
052         * given type pattern.
053         * @param typePattern the type pattern that AspectJ weaver should parse
054         * @throws IllegalArgumentException if the supplied {@code typePattern} is {@code null}
055         * or is recognized as invalid
056         */
057        public TypePatternClassFilter(String typePattern) {
058                setTypePattern(typePattern);
059        }
060
061
062        /**
063         * Set the AspectJ type pattern to match.
064         * <p>Examples include:
065         * <code class="code">
066         * org.springframework.beans.*
067         * </code>
068         * This will match any class or interface in the given package.
069         * <code class="code">
070         * org.springframework.beans.ITestBean+
071         * </code>
072         * This will match the {@code ITestBean} interface and any class
073         * that implements it.
074         * <p>These conventions are established by AspectJ, not Spring AOP.
075         * @param typePattern the type pattern that AspectJ weaver should parse
076         * @throws IllegalArgumentException if the supplied {@code typePattern} is {@code null}
077         * or is recognized as invalid
078         */
079        public void setTypePattern(String typePattern) {
080                Assert.notNull(typePattern, "Type pattern must not be null");
081                this.typePattern = typePattern;
082                this.aspectJTypePatternMatcher =
083                                PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution().
084                                parseTypePattern(replaceBooleanOperators(typePattern));
085        }
086
087        /**
088         * Return the AspectJ type pattern to match.
089         */
090        public String getTypePattern() {
091                return this.typePattern;
092        }
093
094
095        /**
096         * Should the pointcut apply to the given interface or target class?
097         * @param clazz candidate target class
098         * @return whether the advice should apply to this candidate target class
099         * @throws IllegalStateException if no {@link #setTypePattern(String)} has been set
100         */
101        @Override
102        public boolean matches(Class<?> clazz) {
103                Assert.state(this.aspectJTypePatternMatcher != null, "No type pattern has been set");
104                return this.aspectJTypePatternMatcher.matches(clazz);
105        }
106
107        /**
108         * If a type pattern has been specified in XML, the user cannot
109         * write {@code and} as "&&" (though &amp;&amp; will work).
110         * We also allow {@code and} between two sub-expressions.
111         * <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
112         */
113        private String replaceBooleanOperators(String pcExpr) {
114                String result = StringUtils.replace(pcExpr," and "," && ");
115                result = StringUtils.replace(result, " or ", " || ");
116                return StringUtils.replace(result, " not ", " ! ");
117        }
118}