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