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    
018    package org.apache.commons.proxy.factory.util;
019    
020    import org.apache.commons.proxy.ProxyFactory;
021    import org.apache.commons.proxy.exception.ProxyFactoryException;
022    
023    import java.io.Serializable;
024    import java.lang.reflect.Constructor;
025    import java.lang.reflect.Modifier;
026    import java.util.Collection;
027    import java.util.LinkedList;
028    import java.util.List;
029    
030    /**
031     * A useful superclass for a {@link ProxyFactory} which supports subclassing rather than merely implementing interfaces.
032     *
033     * @author James Carman
034     * @since 1.0
035     */
036    public abstract class AbstractSubclassingProxyFactory extends ProxyFactory
037    {
038    //**********************************************************************************************************************
039    // Static Methods
040    //**********************************************************************************************************************
041    
042        /**
043         * Returns either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
044         * class from <code>proxyClasses</code>.
045         *
046         * @param proxyClasses the proxy classes
047         * @return either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
048         *         class from <code>proxyClasses</code>
049         * @throws ProxyFactoryException if multiple non-interface classes are contained in <code>proxyClasses</code> or any
050         *                               of the non-interface classes are final
051         */
052        public static Class getSuperclass( Class[] proxyClasses )
053        {
054            final Class[] superclasses = toNonInterfaces(proxyClasses);
055            switch( superclasses.length )
056            {
057                case 0:
058                    return Object.class;
059                case 1:
060                    final Class superclass = superclasses[0];
061                    if( Modifier.isFinal(superclass.getModifiers()) )
062                    {
063                        throw new ProxyFactoryException(
064                                "Proxy class cannot extend " + superclass.getName() + " as it is final.");
065                    }
066                    if( !hasSuitableDefaultConstructor(superclass) )
067                    {
068                        throw new ProxyFactoryException("Proxy class cannot extend " + superclass.getName() +
069                                ", because it has no visible \"default\" constructor.");
070                    }
071                    return superclass;
072                default:
073                    final StringBuffer errorMessage = new StringBuffer("Proxy class cannot extend ");
074                    for( int i = 0; i < superclasses.length; i++ )
075                    {
076                        Class c = superclasses[i];
077                        errorMessage.append(c.getName());
078                        if( i != superclasses.length - 1 )
079                        {
080                            errorMessage.append(", ");
081                        }
082                    }
083                    errorMessage.append("; multiple inheritance not allowed.");
084                    throw new ProxyFactoryException(errorMessage.toString());
085            }
086        }
087    
088        private static boolean hasSuitableDefaultConstructor( Class superclass )
089        {
090            final Constructor[] declaredConstructors = superclass.getDeclaredConstructors();
091            for( int i = 0; i < declaredConstructors.length; i++ )
092            {
093                Constructor constructor = declaredConstructors[i];
094                if( constructor.getParameterTypes().length == 0 && ( Modifier.isPublic(constructor.getModifiers()) ||
095                        Modifier.isProtected(constructor.getModifiers()) ) )
096                {
097                    return true;
098                }
099            }
100            return false;
101        }
102    
103        /**
104         * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes.
105         * <p/>
106         * <b>Note</b>: This class will append {@link Serializable} to the end of the list if it's
107         * not found!
108         *
109         * @param proxyClasses the proxy classes
110         * @return the <code>proxyClasses</code> transformed into an array of only the interface classes
111         */
112        protected static Class[] toInterfaces( Class[] proxyClasses )
113        {
114            final Collection interfaces = new LinkedList();
115            boolean serializableFound = false;
116            for( int i = 0; i < proxyClasses.length; i++ )
117            {
118                Class proxyInterface = proxyClasses[i];
119                if( proxyInterface.isInterface() )
120                {
121                    interfaces.add(proxyInterface);
122                }
123                serializableFound |= ( Serializable.class.equals(proxyInterface) );
124            }
125            if( !serializableFound )
126            {
127                interfaces.add(Serializable.class);
128            }
129            return ( Class[] ) interfaces.toArray(new Class[interfaces.size()]);
130        }
131    
132        private static Class[] toNonInterfaces( Class[] proxyClasses )
133        {
134            final List superclasses = new LinkedList();
135            for( int i = 0; i < proxyClasses.length; i++ )
136            {
137                Class proxyClass = proxyClasses[i];
138                if( !proxyClass.isInterface() )
139                {
140                    superclasses.add(proxyClass);
141                }
142            }
143            return ( Class[] ) superclasses.toArray(new Class[superclasses.size()]);
144        }
145    
146    //**********************************************************************************************************************
147    // Other Methods
148    //**********************************************************************************************************************
149    
150        /**
151         * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>.
152         *
153         * @param proxyClasses the proxy classes
154         * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code>
155         */
156        public boolean canProxy( Class[] proxyClasses )
157        {
158            try
159            {
160                getSuperclass(proxyClasses);
161                return true;
162            }
163            catch( ProxyFactoryException e )
164            {
165                return false;
166            }
167        }
168    }
169