001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    package org.apache.commons.betwixt.strategy;
018    
019    import java.util.ArrayList;
020    import java.util.Iterator;
021    
022    /** 
023     * <p>ClassNormalizer that uses a list of substitutions.</p>
024     * <p>
025     * This <code>ClassNormalizer</code> checks a list (in order) to find a matching
026     * Class. 
027     * This match can be performed either strictly (using equality) or taking into account
028     * inheritance and implementation.
029     * If a match is found then the first substituted class is returned as the normalization.
030     * </p>
031     * @author Robert Burrell Donkin
032     * @since 0.5
033     */
034    public class ListedClassNormalizer extends ClassNormalizer {
035    
036        /** Entries to be normalized */
037        private ArrayList normalizations = new ArrayList();
038        /** Should the equality (rather than isAssignabledFrom) be used to check */
039        private boolean strickCheck = false;
040    
041        /**
042          * Is strict checking of substitutions on?
043          * @return true is equality is used to compare classes when considering substition,
044          * otherwise isAssignableFrom will be used so that super classes and super interfaces 
045          * will be matched.
046          */
047        public boolean isStrickCheck() {
048            return strickCheck;
049        }
050    
051        /**
052          * Sets strict checking of substitutions?
053          * @param strickCheck if true then equality will be used to compare classes 
054          * when considering substition,
055          * otherwise isAssignableFrom will be used so that super classes and super interfaces 
056          * will be matched.
057          */
058        public void setStrickCheck(boolean strickCheck) {
059            this.strickCheck = strickCheck;
060        }
061    
062        /**
063          * Adds this given substitution to the list.
064          * No warning is given if the match has already been added to the list.
065          * @param match if any classes matching this then the normal class will be substituted
066          * @param substitute the normalized Class if the primary class is matched
067          */
068        public void addSubstitution( Class match, Class substitute ) {
069            normalizations.add( new ListEntry( match, substitute ));
070        }
071        
072        /**
073          * Adds the given substitute to the list.
074          * This is a convenience method useful when {@link #isStrickCheck} is false.
075          * In this case, any subclasses (if this is a class) or implementating classes
076          * if this is an interface) will be subsituted with this value.
077          * @param substitute sustitude this Class
078          */
079        public void addSubstitution( Class substitute ) {
080            addSubstitution( substitute, substitute );
081        }
082    
083        /**
084          * Normalize given class.
085          * The normalized Class is the Class that Betwixt should 
086          * introspect. 
087          * This strategy class allows the introspected Class to be 
088          * varied.
089          *
090          * @param clazz the class to normalize, not null
091          * @return this implementation check it's list of substitutations in order
092          * and returns the first that matchs. If {@link #isStrickCheck} then equality 
093          * is used otherwise isAssignableFrom is used (so that super class and interfaces are matched).
094          */
095        public Class normalize( Class clazz ) {
096            Iterator it = normalizations.iterator();
097            while ( it.hasNext() ) {
098                ListEntry entry = (ListEntry) it.next();
099                if ( strickCheck ) {
100                    if ( entry.match.equals( clazz ) ) {
101                        return entry.substitute;
102                    }
103                } else {
104                    if ( entry.match.isAssignableFrom( clazz )) {
105                        return entry.substitute;
106                    }
107                }
108            }
109            
110            return clazz;
111        }
112        
113        /** Holds list entries */
114        private class ListEntry {        
115            /** Class to be check */
116            Class match;
117            /** Substituted to be returned */
118            Class substitute;
119            
120            /** 
121              * Base constructor 
122              * @param match match this Class
123              * @param subsistute substitute matches with this Class
124              */
125            ListEntry( Class match, Class substitute ) {
126                this.match = match;
127                this.substitute = substitute;
128            }
129        }
130    }