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.core.type.filter;
018
019import java.io.IOException;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.springframework.core.type.ClassMetadata;
025import org.springframework.core.type.classreading.MetadataReader;
026import org.springframework.core.type.classreading.MetadataReaderFactory;
027import org.springframework.lang.Nullable;
028
029/**
030 * Type filter that is aware of traversing over hierarchy.
031 *
032 * <p>This filter is useful when matching needs to be made based on potentially the
033 * whole class/interface hierarchy. The algorithm employed uses a succeed-fast
034 * strategy: if at any time a match is declared, no further processing is
035 * carried out.
036 *
037 * @author Ramnivas Laddad
038 * @author Mark Fisher
039 * @since 2.5
040 */
041public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilter {
042
043        protected final Log logger = LogFactory.getLog(getClass());
044
045        private final boolean considerInherited;
046
047        private final boolean considerInterfaces;
048
049
050        protected AbstractTypeHierarchyTraversingFilter(boolean considerInherited, boolean considerInterfaces) {
051                this.considerInherited = considerInherited;
052                this.considerInterfaces = considerInterfaces;
053        }
054
055
056        @Override
057        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
058                        throws IOException {
059
060                // This method optimizes avoiding unnecessary creation of ClassReaders
061                // as well as visiting over those readers.
062                if (matchSelf(metadataReader)) {
063                        return true;
064                }
065                ClassMetadata metadata = metadataReader.getClassMetadata();
066                if (matchClassName(metadata.getClassName())) {
067                        return true;
068                }
069
070                if (this.considerInherited) {
071                        String superClassName = metadata.getSuperClassName();
072                        if (superClassName != null) {
073                                // Optimization to avoid creating ClassReader for super class.
074                                Boolean superClassMatch = matchSuperClass(superClassName);
075                                if (superClassMatch != null) {
076                                        if (superClassMatch.booleanValue()) {
077                                                return true;
078                                        }
079                                }
080                                else {
081                                        // Need to read super class to determine a match...
082                                        try {
083                                                if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
084                                                        return true;
085                                                }
086                                        }
087                                        catch (IOException ex) {
088                                                if (logger.isDebugEnabled()) {
089                                                        logger.debug("Could not read super class [" + metadata.getSuperClassName() +
090                                                                        "] of type-filtered class [" + metadata.getClassName() + "]");
091                                                }
092                                        }
093                                }
094                        }
095                }
096
097                if (this.considerInterfaces) {
098                        for (String ifc : metadata.getInterfaceNames()) {
099                                // Optimization to avoid creating ClassReader for super class
100                                Boolean interfaceMatch = matchInterface(ifc);
101                                if (interfaceMatch != null) {
102                                        if (interfaceMatch.booleanValue()) {
103                                                return true;
104                                        }
105                                }
106                                else {
107                                        // Need to read interface to determine a match...
108                                        try {
109                                                if (match(ifc, metadataReaderFactory)) {
110                                                        return true;
111                                                }
112                                        }
113                                        catch (IOException ex) {
114                                                if (logger.isDebugEnabled()) {
115                                                        logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
116                                                                        metadata.getClassName() + "]");
117                                                }
118                                        }
119                                }
120                        }
121                }
122
123                return false;
124        }
125
126        private boolean match(String className, MetadataReaderFactory metadataReaderFactory) throws IOException {
127                return match(metadataReaderFactory.getMetadataReader(className), metadataReaderFactory);
128        }
129
130        /**
131         * Override this to match self characteristics alone. Typically,
132         * the implementation will use a visitor to extract information
133         * to perform matching.
134         */
135        protected boolean matchSelf(MetadataReader metadataReader) {
136                return false;
137        }
138
139        /**
140         * Override this to match on type name.
141         */
142        protected boolean matchClassName(String className) {
143                return false;
144        }
145
146        /**
147         * Override this to match on super type name.
148         */
149        @Nullable
150        protected Boolean matchSuperClass(String superClassName) {
151                return null;
152        }
153
154        /**
155         * Override this to match on interface type name.
156         */
157        @Nullable
158        protected Boolean matchInterface(String interfaceName) {
159                return null;
160        }
161
162}