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 }