Skip to content

Package: ReflectionUtils$GenericArrayTypeImpl

ReflectionUtils$GenericArrayTypeImpl

nameinstructionbranchcomplexitylinemethod
ReflectionUtils.GenericArrayTypeImpl(Type)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
equals(Object)
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getGenericComponentType()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
hashCode()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
toString()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0 which is available at
6: * http://www.eclipse.org/legal/epl-2.0,
7: * or the Eclipse Distribution License v. 1.0 which is available at
8: * http://www.eclipse.org/org/documents/edl-v10.php.
9: *
10: * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11: */
12:
13: package org.eclipse.yasson.internal;
14:
15: import java.lang.reflect.Constructor;
16: import java.lang.reflect.GenericArrayType;
17: import java.lang.reflect.InvocationTargetException;
18: import java.lang.reflect.Modifier;
19: import java.lang.reflect.ParameterizedType;
20: import java.lang.reflect.Type;
21: import java.lang.reflect.TypeVariable;
22: import java.lang.reflect.WildcardType;
23: import java.security.AccessController;
24: import java.security.PrivilegedAction;
25: import java.util.Arrays;
26: import java.util.List;
27: import java.util.Objects;
28: import java.util.Optional;
29: import java.util.logging.Logger;
30:
31: import jakarta.json.bind.JsonbException;
32:
33: import org.eclipse.yasson.internal.properties.MessageKeys;
34: import org.eclipse.yasson.internal.properties.Messages;
35:
36: /**
37: * Utility class for resolution of generics during unmarshalling.
38: */
39: public class ReflectionUtils {
40:
41: private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName());
42:
43: private ReflectionUtils() {
44: throw new IllegalStateException("Utility classes should not be instantiated.");
45: }
46:
47: /**
48: * Get raw type by type.
49: * Only for ParametrizedTypes, GenericArrayTypes and Classes.
50: *
51: * Empty optional is returned if raw type cannot be resolved.
52: *
53: * @param type Type to get class information from, not null.
54: * @return Class of a type.
55: */
56: public static Optional<Class<?>> getOptionalRawType(Type type) {
57: if (type instanceof Class) {
58: return Optional.of((Class<?>) type);
59: } else if (type instanceof ParameterizedType) {
60: return Optional.of((Class<?>) ((ParameterizedType) type).getRawType());
61: } else if (type instanceof GenericArrayType) {
62: return Optional.of(((GenericArrayType) type).getClass());
63: } else if (type instanceof TypeVariable) {
64: TypeVariable<?> typeVariable = TypeVariable.class.cast(type);
65: if (Objects.nonNull(typeVariable.getBounds())) {
66: Optional<Class<?>> specializedClass = Optional.empty();
67: for (Type bound : typeVariable.getBounds()) {
68: Optional<Class<?>> boundRawType = getOptionalRawType(bound);
69: if (boundRawType.isPresent() && !Object.class.equals(boundRawType.get())) {
70: if (!specializedClass.isPresent() || specializedClass.get().isAssignableFrom(boundRawType.get())) {
71: specializedClass = Optional.of(boundRawType.get());
72: }
73: }
74: }
75: return specializedClass;
76: }
77: }
78: return Optional.empty();
79: }
80:
81: /**
82: * Get raw type by type.
83: * Resolves only ParametrizedTypes, GenericArrayTypes and Classes.
84: *
85: * Exception is thrown if raw type cannot be resolved.
86: *
87: * @param type Type to get class information from, not null.
88: * @return Class of a raw type.
89: */
90: public static Class<?> getRawType(Type type) {
91: return getOptionalRawType(type)
92: .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.TYPE_RESOLUTION_ERROR, type)));
93: }
94:
95: /**
96: * Get a raw type of any type.
97: * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar.
98: * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific
99: * bound is a {@link TypeVariable}, perform typevar resolution.
100: *
101: * @param chain hierarchy of all wrapping types.
102: * @param type type to resolve, typically field type or generic bound, not null.
103: * @return resolved raw class
104: */
105: public static Class<?> resolveRawType(List<Type> chain, Type type) {
106: if (type instanceof Class) {
107: return (Class<?>) type;
108: } else if (type instanceof ParameterizedType) {
109: return (Class<?>) ((ParameterizedType) type).getRawType();
110: } else {
111: return getRawType(resolveType(chain, type));
112: }
113: }
114:
115: /**
116: * Resolve a type by chain.
117: * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar.
118: * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific
119: * bound is a {@link TypeVariable}, perform typevar resolution.
120: *
121: * @param chain hierarchy of all wrapping types.
122: * @param type type to resolve, typically field type or generic bound, not null.
123: * @return resolved type
124: */
125: public static Type resolveType(List<Type> chain, Type type) {
126: return resolveType(chain, type, true);
127: }
128:
129: private static Type resolveType(List<Type> chain, Type type, boolean warn) {
130: Type toResolve = type;
131: if (type instanceof GenericArrayType) {
132: toResolve = ((GenericArrayType) type).getGenericComponentType();
133: Type resolved = resolveType(chain, toResolve);
134: return new GenericArrayTypeImpl(resolved);
135: }
136: if (toResolve instanceof WildcardType) {
137: return resolveMostSpecificBound(chain, (WildcardType) toResolve, warn);
138: } else if (toResolve instanceof TypeVariable) {
139: return resolveItemVariableType(chain, (TypeVariable<?>) toResolve, warn);
140: } else if (toResolve instanceof ParameterizedType) {
141: return resolveTypeArguments((ParameterizedType) toResolve, chain.get(chain.size() - 1));
142: }
143: return type;
144: }
145:
146: /**
147: * Resolves type by item information and wraps it with {@link Optional}.
148: *
149: * @param chain hierarchy of all wrapping types.
150: * @param type type
151: * @return resolved type wrapped with Optional
152: */
153: public static Optional<Type> resolveOptionalType(List<Type> chain, Type type) {
154: try {
155: return Optional.of(resolveType(chain, type, false));
156: } catch (RuntimeException e) {
157: return Optional.empty();
158: }
159: }
160:
161: /**
162: * Resolve a bounded type variable type by its wrapper types.
163: * Resolution could be done only if a compile time generic information is provided, either:
164: * by generic field or subclass of a generic class.
165: *
166: * @param chain chain to search "runtime" generic type of a TypeVariable.
167: * @param typeVariable type to search in chain for, not null.
168: * @param warn whether or not to log a warning message when bounds are not found
169: * @return Type of a generic "runtime" bound, not null.
170: */
171: public static Type resolveItemVariableType(List<Type> chain, TypeVariable<?> typeVariable, boolean warn) {
172: // if (chain == null) {
173: // Optional<Class<?>> optionalRawType = getOptionalRawType(typeVariable);
174: // if (optionalRawType.isPresent()) {
175: // return optionalRawType.get();
176: // }
177:
178: // //Bound not found, treat it as an Object.class
179: // if (warn) {
180: // LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND,
181: // typeVariable,
182: // typeVariable.getGenericDeclaration()));
183: // }
184: // return Object.class;
185: // }
186: Type returnType = typeVariable;
187: for (int i = chain.size() - 1; i >= 0; i--) {
188: Type type = chain.get(i);
189: Type tmp = new VariableTypeInheritanceSearch().searchParametrizedType(type, (TypeVariable<?>) returnType);
190: if (tmp != null) {
191: returnType = tmp;
192: }
193: if (!(returnType instanceof TypeVariable)) {
194: break;
195: }
196: }
197: if (returnType instanceof TypeVariable) {
198: // throw new JsonbException("Could not resolve: " + unresolvedType);
199: return Object.class;
200: }
201: return returnType;
202:
203: // //Embedded items doesn't hold information about variable types
204: // if (chain instanceof EmbeddedItem) {
205: // return resolveItemVariableType(chain.getWrapper(), typeVariable, warn);
206: // }
207: //
208: // ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(chain.getRuntimeType());
209: //
210: // VariableTypeInheritanceSearch search = new VariableTypeInheritanceSearch();
211: // Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable);
212: // if (foundType != null) {
213: // if (foundType instanceof TypeVariable) {
214: // return resolveItemVariableType(chain.getWrapper(), (TypeVariable<?>) foundType, warn);
215: // }
216: // return foundType;
217: // }
218: //
219: // return resolveItemVariableType(chain.getWrapper(), typeVariable, warn);
220: }
221:
222: /**
223: * Resolves {@link TypeVariable} arguments of generic types.
224: *
225: * @param typeToResolve type to resolve
226: * @param typeToSearch type to search
227: * @return resolved type
228: */
229: public static Type resolveTypeArguments(ParameterizedType typeToResolve, Type typeToSearch) {
230: final Type[] unresolvedArgs = typeToResolve.getActualTypeArguments();
231: Type[] resolvedArgs = new Type[unresolvedArgs.length];
232: for (int i = 0; i < unresolvedArgs.length; i++) {
233: Type unresolvedArg = unresolvedArgs[i];
234: if (!(unresolvedArg instanceof TypeVariable) && !(unresolvedArg instanceof GenericArrayType)) {
235: resolvedArgs[i] = unresolvedArg;
236: } else {
237: Type variableType = unresolvedArg;
238: if (variableType instanceof GenericArrayType) {
239: variableType = ((GenericArrayType) variableType).getGenericComponentType();
240: }
241: resolvedArgs[i] = new VariableTypeInheritanceSearch()
242: .searchParametrizedType(typeToSearch, (TypeVariable<?>) variableType);
243: if (resolvedArgs[i] == null) {
244: if (typeToSearch instanceof Class) {
245: return Object.class;
246: }
247: //No generic information available
248: throw new IllegalStateException(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND,
249: variableType,
250: typeToSearch));
251: }
252: }
253: if (resolvedArgs[i] instanceof ParameterizedType) {
254: resolvedArgs[i] = resolveTypeArguments((ParameterizedType) resolvedArgs[i], typeToSearch);
255: } else if (unresolvedArg instanceof GenericArrayType) {
256: resolvedArgs[i] = new GenericArrayTypeImpl(resolvedArgs[i]);
257: }
258: }
259: return Arrays.equals(resolvedArgs, unresolvedArgs)
260: ? typeToResolve
261: : new ResolvedParameterizedType(typeToResolve, resolvedArgs);
262: }
263:
264: /**
265: * Create instance with constructor.
266: *
267: * @param constructor const not null
268: * @param <T> type of instance
269: * @return instance
270: */
271: public static <T> T createNoArgConstructorInstance(Constructor<T> constructor) {
272: Objects.requireNonNull(constructor);
273: try {
274: return constructor.newInstance();
275: } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
276: throw new JsonbException("Can't create instance", e);
277: }
278: }
279:
280: /**
281: * Get default no argument constructor of the class.
282: *
283: * @param clazz Class to get constructor from
284: * @param <T> Class generic type
285: * @param required if true, throws an exception if the default constructor is missing.
286: * If false, returns null in that case
287: * @return the constructor of the class, or null. Depending on required.
288: */
289: public static <T> Constructor<T> getDefaultConstructor(Class<T> clazz, boolean required) {
290: Objects.requireNonNull(clazz);
291: return AccessController.doPrivileged((PrivilegedAction<Constructor<T>>) () -> {
292: try {
293: final Constructor<T> declaredConstructor = clazz.getDeclaredConstructor();
294: if (declaredConstructor.getModifiers() == Modifier.PROTECTED) {
295: declaredConstructor.setAccessible(true);
296: }
297: return declaredConstructor;
298: } catch (NoSuchMethodException | RuntimeException e) {
299: if (required) {
300: throw new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, clazz), e);
301: }
302: return null;
303: }
304: });
305: }
306:
307: /**
308: * For generic adapters like:
309: * <p>
310: * {@code
311: * interface ContainerAdapter<T> extends JsonbAdapter<Box<T>, Crate<T>>...;
312: * class IntegerBoxToCrateAdapter implements ContainerAdapter<Integer>...;
313: * }
314: * </p>
315: * We need to find a JsonbAdapter class which will hold basic generic type arguments,
316: * and resolve them if they are TypeVariables from there.
317: *
318: * @param classToSearch class to resolve parameterized interface
319: * @param parameterizedInterface interface to search
320: * @return type of JsonbAdapter
321: */
322: public static ParameterizedType findParameterizedType(Class<?> classToSearch, Class<?> parameterizedInterface) {
323: Class current = classToSearch;
324: while (current != Object.class) {
325: for (Type currentInterface : current.getGenericInterfaces()) {
326: if (currentInterface instanceof ParameterizedType
327: && parameterizedInterface.isAssignableFrom(
328: ReflectionUtils.getRawType(((ParameterizedType) currentInterface).getRawType()))) {
329: return (ParameterizedType) currentInterface;
330: }
331: }
332: current = current.getSuperclass();
333: }
334: throw new JsonbException(Messages.getMessage(MessageKeys.NON_PARAMETRIZED_TYPE, parameterizedInterface));
335: }
336:
337: /**
338: * Check if type needs resolution. If type is a class or a parametrized type with all type arguments as classes
339: * than it is considered resolved. If any of types is type variable or wildcard type is not resolved.
340: *
341: * @param type Type to check.
342: * @return True if resolved
343: */
344: public static boolean isResolvedType(Type type) {
345: if (type instanceof ParameterizedType) {
346: for (Type typeArg : ((ParameterizedType) type).getActualTypeArguments()) {
347: if (!isResolvedType(typeArg)) {
348: return false;
349: }
350: }
351: return true;
352: }
353: return type instanceof Class<?>
354: }
355:
356: private static ParameterizedType findParameterizedSuperclass(Type type) {
357: if (type == null || type instanceof ParameterizedType) {
358: return (ParameterizedType) type;
359: }
360: if (!(type instanceof Class)) {
361: throw new JsonbException("Can't resolve ParameterizedType superclass for: " + type);
362: }
363: return findParameterizedSuperclass(((Class) type).getGenericSuperclass());
364: }
365:
366: /**
367: * Resolves a wildcard most specific upper or lower bound.
368: *
369: * @param chain Type.
370: * @param wildcardType Wildcard type.
371: * @return The most specific type.
372: */
373: private static Type resolveMostSpecificBound(List<Type> chain, WildcardType wildcardType, boolean warn) {
374: Class<?> result = Object.class;
375: for (Type upperBound : wildcardType.getUpperBounds()) {
376: result = getMostSpecificBound(chain, result, upperBound, warn);
377: }
378: for (Type lowerBound : wildcardType.getLowerBounds()) {
379: result = getMostSpecificBound(chain, result, lowerBound, warn);
380: }
381: return result;
382: }
383:
384: private static Class<?> getMostSpecificBound(List<Type> chain, Class<?> result, Type bound, boolean warn) {
385: if (bound == Object.class) {
386: return result;
387: }
388: //if bound is type variable search recursively for wrapper generic expansion
389: Type resolvedBoundType = bound instanceof TypeVariable ? resolveType(chain, bound, warn) : bound;
390: Class<?> boundRawType = getRawType(resolvedBoundType);
391: //resolved class is a subclass of a result candidate
392: if (result.isAssignableFrom(boundRawType)) {
393: result = boundRawType;
394: }
395: return result;
396: }
397:
398: public static final class GenericArrayTypeImpl implements GenericArrayType {
399: private final Type genericComponentType;
400:
401: // private constructor enforces use of static factory
402: private GenericArrayTypeImpl(Type ct) {
403: genericComponentType = ct;
404: }
405:
406: /**
407: * Returns a {@code Type} object representing the component type
408: * of this array.
409: *
410: * @return a {@code Type} object representing the component type
411: * of this array
412: * @since 1.5
413: */
414: public Type getGenericComponentType() {
415: return genericComponentType; // return cached component type
416: }
417:
418: public String toString() {
419: return getGenericComponentType().getTypeName() + "[]";
420: }
421:
422: @Override
423: public boolean equals(Object o) {
424:• if (o instanceof GenericArrayType) {
425: GenericArrayType that = (GenericArrayType) o;
426:
427: return Objects.equals(genericComponentType, that.getGenericComponentType());
428: } else {
429: return false;
430: }
431: }
432:
433: @Override
434: public int hashCode() {
435: return Objects.hashCode(genericComponentType);
436: }
437: }
438: }