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    *      https://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.jexl3.internal.introspection;
18  
19  import java.lang.reflect.Modifier;
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.LinkedHashSet;
23  import java.util.Set;
24  
25  /**
26   * Miscellaneous introspection methods.
27   * <p>The main algorithm is computing the actual ordered complete set of classes and interfaces that a given class
28   * extends or implements. This set order is based on the super-class then (recursive interface) declaration order,
29   * attempting to reflect the (hopefully intended) abstraction order (from strong to weak).</p>
30   */
31  public class ClassMisc {
32  
33    /**
34     * Collect super classes and interfaces in super-order.
35     * <p>This orders from stronger to weaker abstraction in the sense that
36     * Integer is a stronger abstraction than Number.</p>
37     *
38     * @param superSet  the set of super classes to collect into
39     * @param baseClass the root class.
40     */
41    private static void addSuperClasses(final Set<Class<?>> superSet, final Class<?> baseClass) {
42      for (Class<?> clazz = baseClass.getSuperclass(); clazz != null && !Object.class.equals(clazz); clazz = clazz.getSuperclass()) {
43        if (Modifier.isPublic(clazz.getModifiers())) {
44          superSet.add(clazz);
45        }
46      }
47      // recursive visit interfaces in super order
48      for (Class<?> clazz = baseClass; clazz != null && !Object.class.equals(clazz); clazz = clazz.getSuperclass()) {
49        addSuperInterfaces(superSet, clazz);
50      }
51    }
52  
53    /**
54     * Recursively add super-interfaces in super-order.
55     * <p>On the premise that a class also tends to enumerate interface in the order of weaker abstraction and
56     * that interfaces follow the same convention (strong implements weak).</p>
57     *
58     * @param superSet the set of super classes to fill
59     * @param clazz    the root class.
60     */
61    private static void addSuperInterfaces(final Set<Class<?>> superSet, final Class<?> clazz) {
62      for (final Class<?> inter : clazz.getInterfaces()) {
63        superSet.add(inter);
64        addSuperInterfaces(superSet, inter);
65      }
66    }
67  
68    /**
69     * Gets the closest common super-class of two classes.
70     * <p>When building an array, this helps strong-typing the result.</p>
71     *
72     * @param baseClass the class to serve as base
73     * @param other     the other class
74     * @return Object.class if nothing in common, the closest common class or interface otherwise
75     */
76    public static Class<?> getCommonSuperClass(final Class<?> baseClass, final Class<?> other) {
77      if (baseClass == null || other == null) {
78        return null;
79      }
80      if (baseClass != Object.class && other != Object.class) {
81        final Set<Class<?>> superSet = new LinkedHashSet<>();
82        addSuperClasses(superSet, baseClass);
83        for (final Class<?> superClass : superSet) {
84          if (superClass.isAssignableFrom(other)) {
85            return superClass;
86          }
87        }
88      }
89      return Object.class;
90    }
91  
92    /**
93     * Build the set of super classes and interfaces common to a collection of classes.
94     * <p>The returned set is ordered and puts classes in order of super-class appearance then
95     * interfaces of each super-class.</p>
96     *
97     * @param baseClass    the class to serve as base
98     * @param otherClasses the (optional) other classes
99     * @return an empty set if nothing in common, the set of common classes and interfaces that
100    *  does not contain the baseClass nor Object class
101    */
102   public static Set<Class<?>> getSuperClasses(final Class<?> baseClass, final Class<?>... otherClasses) {
103     if (baseClass == null) {
104       return Collections.emptySet();
105     }
106     final Set<Class<?>> superSet = new LinkedHashSet<>();
107     addSuperClasses(superSet, baseClass);
108     // intersect otherClasses
109     if (otherClasses.length > 0) {
110       for (final Class<?> other : otherClasses) {
111         // remove classes from $superSet that $other is not assignable to
112         final Iterator<Class<?>> superClass = superSet.iterator();
113         while (superClass.hasNext()) {
114           if (!superClass.next().isAssignableFrom(other)) {
115             superClass.remove();
116           }
117         }
118         if (superSet.isEmpty()) {
119           return Collections.emptySet();
120         }
121       }
122     }
123     return superSet;
124   }
125 
126   /**
127    * Lets not instantiate it.
128    */
129   private ClassMisc() {}
130 }