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 package org.apache.commons.proxy2.impl;
18
19 import java.io.Serializable;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Modifier;
22 import java.util.LinkedHashSet;
23 import java.util.Set;
24
25 import org.apache.commons.proxy2.exception.ProxyFactoryException;
26
27 /**
28 * Parent {@link AbstractProxyFactory} for implementations that permit the generation of proxies with a specific
29 * inheritance hierarchy.
30 */
31 public abstract class AbstractSubclassingProxyFactory extends AbstractProxyFactory
32 {
33 //******************************************************************************************************************
34 // ProxyFactory Implementation
35 //******************************************************************************************************************
36
37 /**
38 * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>.
39 *
40 * @param proxyClasses
41 * the proxy classes
42 * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code>
43 */
44 @Override
45 public boolean canProxy(Class<?>... proxyClasses)
46 {
47 try
48 {
49 getSuperclass(proxyClasses);
50 return true;
51 }
52 catch (ProxyFactoryException e)
53 {
54 return false;
55 }
56 }
57
58 //******************************************************************************************************************
59 // Other Methods
60 //******************************************************************************************************************
61
62 private static boolean hasSuitableDefaultConstructor(Class<?> superclass)
63 {
64 final Constructor<?>[] declaredConstructors = superclass.getDeclaredConstructors();
65 for (int i = 0; i < declaredConstructors.length; i++)
66 {
67 Constructor<?> constructor = declaredConstructors[i];
68 if (constructor.getParameterTypes().length == 0
69 && (Modifier.isPublic(constructor.getModifiers()) || Modifier.isProtected(constructor
70 .getModifiers())))
71 {
72 return true;
73 }
74 }
75 return false;
76 }
77
78 private static Class<?>[] toNonInterfaces(Class<?>[] proxyClasses)
79 {
80 final Set<Class<?>> superclasses = new LinkedHashSet<Class<?>>();
81 for (Class<?> proxyClass : proxyClasses)
82 {
83 if (!proxyClass.isInterface())
84 {
85 superclasses.add(proxyClass);
86 }
87 }
88 return superclasses.toArray(new Class[superclasses.size()]);
89 }
90
91 /**
92 * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes.
93 * <p/>
94 * <b>Note</b>: This class will append {@link Serializable} to the end of the list if it's not found!
95 *
96 * @param proxyClasses
97 * the proxy classes
98 * @return the <code>proxyClasses</code> transformed into an array of only the interface classes
99 */
100 protected static Class<?>[] toInterfaces(Class<?>[] proxyClasses)
101 {
102 final Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
103 for (Class<?> proxyClass : proxyClasses)
104 {
105 if (proxyClass.isInterface())
106 {
107 interfaces.add(proxyClass);
108 }
109 }
110 interfaces.add(Serializable.class);
111 return interfaces.toArray(new Class[interfaces.size()]);
112 }
113
114 /**
115 * Returns either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
116 * class from <code>proxyClasses</code>.
117 *
118 * @param proxyClasses
119 * the proxy classes
120 * @return either {@link Object} if all of the <code>proxyClasses</code> are interfaces or the single non-interface
121 * class from <code>proxyClasses</code>
122 * @throws ProxyFactoryException
123 * if multiple non-interface classes are contained in <code>proxyClasses</code> or any of the
124 * non-interface classes are final
125 */
126 public static Class<?> getSuperclass(Class<?>[] proxyClasses)
127 {
128 final Class<?>[] superclasses = toNonInterfaces(proxyClasses);
129 switch (superclasses.length)
130 {
131 case 0:
132 return Object.class;
133 case 1:
134 final Class<?> superclass = superclasses[0];
135 if (Modifier.isFinal(superclass.getModifiers()))
136 {
137 throw new ProxyFactoryException("Proxy class cannot extend " + superclass.getName()
138 + " as it is final.");
139 }
140 if (!hasSuitableDefaultConstructor(superclass))
141 {
142 throw new ProxyFactoryException("Proxy class cannot extend " + superclass.getName()
143 + ", because it has no visible \"default\" constructor.");
144 }
145 return superclass;
146 default:
147 final StringBuilder errorMessage = new StringBuilder("Proxy class cannot extend ");
148 for (int i = 0; i < superclasses.length; i++)
149 {
150 Class<?> c = superclasses[i];
151 errorMessage.append(c.getName());
152 if (i != superclasses.length - 1)
153 {
154 errorMessage.append(", ");
155 }
156 }
157 errorMessage.append("; multiple inheritance not allowed.");
158 throw new ProxyFactoryException(errorMessage.toString());
159 }
160 }
161 }