1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.proxy.factory.util;
19
20 import org.apache.commons.proxy.ProxyFactory;
21 import org.apache.commons.proxy.exception.ProxyFactoryException;
22
23 import java.lang.reflect.Constructor;
24 import java.lang.reflect.Modifier;
25 import java.util.Collection;
26 import java.util.LinkedList;
27 import java.util.List;
28
29 /**
30 * A useful superclass for a {@link ProxyFactory} which supports subclassing rather than merely implementing interfaces.
31 *
32 * @author James Carman
33 * @since 1.0
34 */
35 public abstract class AbstractSubclassingProxyFactory extends ProxyFactory
36 {
37 //----------------------------------------------------------------------------------------------------------------------
38 // Static Methods
39 //----------------------------------------------------------------------------------------------------------------------
40
41 private static boolean hasSuitableDefaultConstructor( Class superclass )
42 {
43 final Constructor[] declaredConstructors = superclass.getDeclaredConstructors();
44 for( int i = 0; i < declaredConstructors.length; i++ )
45 {
46 Constructor constructor = declaredConstructors[i];
47 if( constructor.getParameterTypes().length == 0 && ( Modifier.isPublic( constructor.getModifiers() ) ||
48 Modifier.isProtected( constructor.getModifiers() ) ) )
49 {
50 return true;
51 }
52 }
53 return false;
54 }
55
56 /**
57 * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes.
58 *
59 * @param proxyClasses the proxy classes
60 * @return the <code>proxyClasses</code> transformed into an array of only the interface classes
61 */
62 protected static Class[] toInterfaces( Class[] proxyClasses )
63 {
64 final Collection interfaces = new LinkedList();
65 for( int i = 0; i < proxyClasses.length; i++ )
66 {
67 Class proxyInterface = proxyClasses[i];
68 if( proxyInterface.isInterface() )
69 {
70 interfaces.add( proxyInterface );
71 }
72 }
73 return ( Class[] ) interfaces.toArray( new Class[interfaces.size()] );
74 }
75
76 private static Class[] toNonInterfaces( Class[] proxyClasses )
77 {
78 final List superclasses = new LinkedList();
79 for( int i = 0; i < proxyClasses.length; i++ )
80 {
81 Class proxyClass = proxyClasses[i];
82 if( !proxyClass.isInterface() )
83 {
84 superclasses.add( proxyClass );
85 }
86 }
87 return ( Class[] ) superclasses.toArray( new Class[superclasses.size()] );
88 }
89
90 //----------------------------------------------------------------------------------------------------------------------
91 // Other Methods
92 //----------------------------------------------------------------------------------------------------------------------
93
94 /**
95 * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>.
96 *
97 * @param proxyClasses the proxy classes
98 * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code>
99 */
100 public boolean canProxy( Class[] proxyClasses )
101 {
102 try
103 {
104 getSuperclass( proxyClasses );
105 return true;
106 }
107 catch( ProxyFactoryException e )
108 {
109 return false;
110 }
111 }
112
113 /**
114 * Returns either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
115 * class from <code>proxyClasses</code>.
116 *
117 * @param proxyClasses the proxy classes
118 * @return either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
119 * class from <code>proxyClasses</code>
120 * @throws ProxyFactoryException if multiple non-interface classes are contained in <code>proxyClasses</code> or any
121 * of the non-interface classes are final
122 */
123 public static Class getSuperclass( Class[] proxyClasses )
124 {
125 final Class[] superclasses = toNonInterfaces( proxyClasses );
126 switch( superclasses.length )
127 {
128 case 0:
129 return Object.class;
130 case 1:
131 final Class superclass = superclasses[0];
132 if( Modifier.isFinal( superclass.getModifiers() ) )
133 {
134 throw new ProxyFactoryException(
135 "Proxy class cannot extend " + superclass.getName() + " as it is final." );
136 }
137 if( !hasSuitableDefaultConstructor( superclass ) )
138 {
139 throw new ProxyFactoryException( "Proxy class cannot extend " + superclass.getName() +
140 ", because it has no visible \"default\" constructor." );
141 }
142 return superclass;
143 default:
144 final StringBuffer errorMessage = new StringBuffer( "Proxy class cannot extend " );
145 for( int i = 0; i < superclasses.length; i++ )
146 {
147 Class c = superclasses[i];
148 errorMessage.append( c.getName() );
149 if( i != superclasses.length - 1 )
150 {
151 errorMessage.append( ", " );
152 }
153 }
154 errorMessage.append( "; multiple inheritance not allowed." );
155 throw new ProxyFactoryException( errorMessage.toString() );
156 }
157 }
158 }
159