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