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;
020import java.util.Arrays;
021
022import org.springframework.aop.ClassFilter;
023import org.springframework.lang.Nullable;
024import org.springframework.util.Assert;
025import org.springframework.util.ObjectUtils;
026
027/**
028 * Static utility methods for composing {@link ClassFilter ClassFilters}.
029 *
030 * @author Rod Johnson
031 * @author Rob Harrop
032 * @author Juergen Hoeller
033 * @author Sam Brannen
034 * @since 11.11.2003
035 * @see MethodMatchers
036 * @see Pointcuts
037 */
038public abstract class ClassFilters {
039
040        /**
041         * Match all classes that <i>either</i> (or both) of the given ClassFilters matches.
042         * @param cf1 the first ClassFilter
043         * @param cf2 the second ClassFilter
044         * @return a distinct ClassFilter that matches all classes that either
045         * of the given ClassFilter matches
046         */
047        public static ClassFilter union(ClassFilter cf1, ClassFilter cf2) {
048                Assert.notNull(cf1, "First ClassFilter must not be null");
049                Assert.notNull(cf2, "Second ClassFilter must not be null");
050                return new UnionClassFilter(new ClassFilter[] {cf1, cf2});
051        }
052
053        /**
054         * Match all classes that <i>either</i> (or all) of the given ClassFilters matches.
055         * @param classFilters the ClassFilters to match
056         * @return a distinct ClassFilter that matches all classes that either
057         * of the given ClassFilter matches
058         */
059        public static ClassFilter union(ClassFilter[] classFilters) {
060                Assert.notEmpty(classFilters, "ClassFilter array must not be empty");
061                return new UnionClassFilter(classFilters);
062        }
063
064        /**
065         * Match all classes that <i>both</i> of the given ClassFilters match.
066         * @param cf1 the first ClassFilter
067         * @param cf2 the second ClassFilter
068         * @return a distinct ClassFilter that matches all classes that both
069         * of the given ClassFilter match
070         */
071        public static ClassFilter intersection(ClassFilter cf1, ClassFilter cf2) {
072                Assert.notNull(cf1, "First ClassFilter must not be null");
073                Assert.notNull(cf2, "Second ClassFilter must not be null");
074                return new IntersectionClassFilter(new ClassFilter[] {cf1, cf2});
075        }
076
077        /**
078         * Match all classes that <i>all</i> of the given ClassFilters match.
079         * @param classFilters the ClassFilters to match
080         * @return a distinct ClassFilter that matches all classes that both
081         * of the given ClassFilter match
082         */
083        public static ClassFilter intersection(ClassFilter[] classFilters) {
084                Assert.notEmpty(classFilters, "ClassFilter array must not be empty");
085                return new IntersectionClassFilter(classFilters);
086        }
087
088
089        /**
090         * ClassFilter implementation for a union of the given ClassFilters.
091         */
092        @SuppressWarnings("serial")
093        private static class UnionClassFilter implements ClassFilter, Serializable {
094
095                private final ClassFilter[] filters;
096
097                UnionClassFilter(ClassFilter[] filters) {
098                        this.filters = filters;
099                }
100
101                @Override
102                public boolean matches(Class<?> clazz) {
103                        for (ClassFilter filter : this.filters) {
104                                if (filter.matches(clazz)) {
105                                        return true;
106                                }
107                        }
108                        return false;
109                }
110
111                @Override
112                public boolean equals(@Nullable Object other) {
113                        return (this == other || (other instanceof UnionClassFilter &&
114                                        ObjectUtils.nullSafeEquals(this.filters, ((UnionClassFilter) other).filters)));
115                }
116
117                @Override
118                public int hashCode() {
119                        return ObjectUtils.nullSafeHashCode(this.filters);
120                }
121
122                @Override
123                public String toString() {
124                        return getClass().getName() + ": " + Arrays.toString(this.filters);
125                }
126
127        }
128
129
130        /**
131         * ClassFilter implementation for an intersection of the given ClassFilters.
132         */
133        @SuppressWarnings("serial")
134        private static class IntersectionClassFilter implements ClassFilter, Serializable {
135
136                private final ClassFilter[] filters;
137
138                IntersectionClassFilter(ClassFilter[] filters) {
139                        this.filters = filters;
140                }
141
142                @Override
143                public boolean matches(Class<?> clazz) {
144                        for (ClassFilter filter : this.filters) {
145                                if (!filter.matches(clazz)) {
146                                        return false;
147                                }
148                        }
149                        return true;
150                }
151
152                @Override
153                public boolean equals(@Nullable Object other) {
154                        return (this == other || (other instanceof IntersectionClassFilter &&
155                                        ObjectUtils.nullSafeEquals(this.filters, ((IntersectionClassFilter) other).filters)));
156                }
157
158                @Override
159                public int hashCode() {
160                        return ObjectUtils.nullSafeHashCode(this.filters);
161                }
162
163                @Override
164                public String toString() {
165                        return getClass().getName() + ": " + Arrays.toString(this.filters);
166                }
167
168        }
169
170}