View Javadoc
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 }