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 }