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