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 */ 017package org.apache.commons.proxy2.impl; 018 019import java.io.Serializable; 020import java.lang.reflect.Constructor; 021import java.lang.reflect.Modifier; 022import java.util.LinkedHashSet; 023import java.util.Set; 024 025import org.apache.commons.proxy2.exception.ProxyFactoryException; 026 027/** 028 * Parent {@link AbstractProxyFactory} for implementations that permit the generation of proxies with a specific 029 * inheritance hierarchy. 030 */ 031public abstract class AbstractSubclassingProxyFactory extends AbstractProxyFactory 032{ 033 //****************************************************************************************************************** 034 // ProxyFactory Implementation 035 //****************************************************************************************************************** 036 037 /** 038 * Returns true if a suitable superclass can be found, given the desired <code>proxyClasses</code>. 039 * 040 * @param proxyClasses 041 * the proxy classes 042 * @return true if a suitable superclass can be found, given the desired <code>proxyClasses</code> 043 */ 044 @Override 045 public boolean canProxy(Class<?>... proxyClasses) 046 { 047 try 048 { 049 getSuperclass(proxyClasses); 050 return true; 051 } 052 catch (ProxyFactoryException e) 053 { 054 return false; 055 } 056 } 057 058 //****************************************************************************************************************** 059 // Other Methods 060 //****************************************************************************************************************** 061 062 private static boolean hasSuitableDefaultConstructor(Class<?> superclass) 063 { 064 final Constructor<?>[] declaredConstructors = superclass.getDeclaredConstructors(); 065 for (int i = 0; i < declaredConstructors.length; i++) 066 { 067 Constructor<?> constructor = declaredConstructors[i]; 068 if (constructor.getParameterTypes().length == 0 069 && (Modifier.isPublic(constructor.getModifiers()) || Modifier.isProtected(constructor 070 .getModifiers()))) 071 { 072 return true; 073 } 074 } 075 return false; 076 } 077 078 private static Class<?>[] toNonInterfaces(Class<?>[] proxyClasses) 079 { 080 final Set<Class<?>> superclasses = new LinkedHashSet<Class<?>>(); 081 for (Class<?> proxyClass : proxyClasses) 082 { 083 if (!proxyClass.isInterface()) 084 { 085 superclasses.add(proxyClass); 086 } 087 } 088 return superclasses.toArray(new Class[superclasses.size()]); 089 } 090 091 /** 092 * Returns the <code>proxyClasses</code> transformed into an array of only the interface classes. 093 * <p/> 094 * <b>Note</b>: This class will append {@link Serializable} to the end of the list if it's not found! 095 * 096 * @param proxyClasses 097 * the proxy classes 098 * @return the <code>proxyClasses</code> transformed into an array of only the interface classes 099 */ 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}