|
17 | 17 | package org.springframework.expression.spel.ast;
|
18 | 18 |
|
19 | 19 | import java.util.ArrayList;
|
| 20 | +import java.util.Collections; |
20 | 21 | import java.util.List;
|
21 | 22 |
|
22 | 23 | import org.springframework.expression.PropertyAccessor;
|
| 24 | +import org.springframework.expression.TargetedAccessor; |
23 | 25 | import org.springframework.lang.Nullable;
|
24 | 26 | import org.springframework.util.ObjectUtils;
|
25 | 27 |
|
26 | 28 | /**
|
27 | 29 | * Utility methods for use in the AST classes.
|
28 | 30 | *
|
29 | 31 | * @author Andy Clement
|
| 32 | + * @author Sam Brannen |
30 | 33 | * @since 3.0.2
|
31 | 34 | */
|
32 | 35 | public abstract class AstUtils {
|
33 | 36 |
|
34 | 37 | /**
|
35 |
| - * Determine the set of property accessors that should be used to try to |
36 |
| - * access a property on the specified target type. |
| 38 | + * Determine the set of accessors that should be used to try to access an |
| 39 | + * element on the specified target type. |
37 | 40 | * <p>The accessors are considered to be in an ordered list; however, in the
|
38 | 41 | * returned list any accessors that are exact matches for the input target
|
39 |
| - * type (as opposed to 'general' accessors that could work for any type) are |
| 42 | + * type (as opposed to 'generic' accessors that could work for any type) are |
40 | 43 | * placed at the start of the list. In addition, if there are specific
|
41 | 44 | * accessors that exactly name the class in question and accessors that name
|
42 | 45 | * a specific class which is a supertype of the class in question, the latter
|
43 | 46 | * are put at the end of the specific accessors set and will be tried after
|
44 | 47 | * exactly matching accessors but before generic accessors.
|
45 |
| - * @param targetType the type upon which property access is being attempted |
46 |
| - * @param propertyAccessors the list of property accessors to process |
47 |
| - * @return a list of accessors that should be tried in order to access the property |
| 48 | + * @param targetType the type upon which element access is being attempted |
| 49 | + * @param accessors the list of element accessors to process |
| 50 | + * @return a list of accessors that should be tried in order to access the |
| 51 | + * element on the specified target type, or an empty list if no suitable |
| 52 | + * accessor could be found |
| 53 | + * @since 6.2 |
48 | 54 | */
|
49 |
| - public static List<PropertyAccessor> getPropertyAccessorsToTry( |
50 |
| - @Nullable Class<?> targetType, List<PropertyAccessor> propertyAccessors) { |
| 55 | + public static <T extends TargetedAccessor> List<T> getAccessorsToTry( |
| 56 | + @Nullable Class<?> targetType, List<T> accessors) { |
| 57 | + |
| 58 | + if (accessors.isEmpty()) { |
| 59 | + return Collections.emptyList(); |
| 60 | + } |
51 | 61 |
|
52 |
| - List<PropertyAccessor> specificAccessors = new ArrayList<>(); |
53 |
| - List<PropertyAccessor> generalAccessors = new ArrayList<>(); |
54 |
| - for (PropertyAccessor accessor : propertyAccessors) { |
| 62 | + List<T> exactMatches = new ArrayList<>(); |
| 63 | + List<T> inexactMatches = new ArrayList<>(); |
| 64 | + List<T> genericMatches = new ArrayList<>(); |
| 65 | + for (T accessor : accessors) { |
55 | 66 | Class<?>[] targets = accessor.getSpecificTargetClasses();
|
56 | 67 | if (ObjectUtils.isEmpty(targets)) {
|
57 | 68 | // generic accessor that says it can be used for any type
|
58 |
| - generalAccessors.add(accessor); |
| 69 | + genericMatches.add(accessor); |
59 | 70 | }
|
60 | 71 | else if (targetType != null) {
|
61 | 72 | for (Class<?> clazz : targets) {
|
62 | 73 | if (clazz == targetType) {
|
63 |
| - // add exact matches to the specificAccessors list |
64 |
| - specificAccessors.add(accessor); |
| 74 | + exactMatches.add(accessor); |
65 | 75 | }
|
66 | 76 | else if (clazz.isAssignableFrom(targetType)) {
|
67 |
| - // add supertype matches to the front of the generalAccessors list |
68 |
| - generalAccessors.add(0, accessor); |
| 77 | + inexactMatches.add(accessor); |
69 | 78 | }
|
70 | 79 | }
|
71 | 80 | }
|
72 | 81 | }
|
73 |
| - List<PropertyAccessor> accessors = new ArrayList<>(specificAccessors.size() + generalAccessors.size()); |
74 |
| - accessors.addAll(specificAccessors); |
75 |
| - accessors.addAll(generalAccessors); |
76 |
| - return accessors; |
| 82 | + |
| 83 | + int size = exactMatches.size() + inexactMatches.size() + genericMatches.size(); |
| 84 | + if (size == 0) { |
| 85 | + return Collections.emptyList(); |
| 86 | + } |
| 87 | + else { |
| 88 | + List<T> result = new ArrayList<>(size); |
| 89 | + result.addAll(exactMatches); |
| 90 | + result.addAll(inexactMatches); |
| 91 | + result.addAll(genericMatches); |
| 92 | + return result; |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Determine the set of property accessors that should be used to try to |
| 98 | + * access a property on the specified target type. |
| 99 | + * <p>The accessors are considered to be in an ordered list; however, in the |
| 100 | + * returned list any accessors that are exact matches for the input target |
| 101 | + * type (as opposed to 'generic' accessors that could work for any type) are |
| 102 | + * placed at the start of the list. In addition, if there are specific |
| 103 | + * accessors that exactly name the class in question and accessors that name |
| 104 | + * a specific class which is a supertype of the class in question, the latter |
| 105 | + * are put at the end of the specific accessors set and will be tried after |
| 106 | + * exactly matching accessors but before generic accessors. |
| 107 | + * @param targetType the type upon which property access is being attempted |
| 108 | + * @param propertyAccessors the list of property accessors to process |
| 109 | + * @return a list of accessors that should be tried in order to access the |
| 110 | + * property on the specified target type, or an empty list if no suitable |
| 111 | + * accessor could be found |
| 112 | + * @see #getAccessorsToTry(Class, List) |
| 113 | + */ |
| 114 | + public static List<PropertyAccessor> getPropertyAccessorsToTry( |
| 115 | + @Nullable Class<?> targetType, List<PropertyAccessor> propertyAccessors) { |
| 116 | + |
| 117 | + return getAccessorsToTry(targetType, propertyAccessors); |
77 | 118 | }
|
78 | 119 |
|
79 | 120 | }
|
0 commit comments