• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ljacqu / JavaTypeResolver / 6312439557

26 Sep 2023 12:12PM UTC coverage: 97.895% (+0.003%) from 97.892%
6312439557

Pull #102

github

ljacqu
#63 FieldUtils: Shorten method names, return streams, util to group by name
Pull Request #102: Field utils

615 of 643 branches covered (0.0%)

Branch coverage included in aggregate %.

32 of 32 new or added lines in 1 file covered. (100.0%)

1152 of 1162 relevant lines covered (99.14%)

4.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.0
/src/main/java/ch/jalu/typeresolver/FieldUtils.java
1
package ch.jalu.typeresolver;
2

3
import ch.jalu.typeresolver.classutil.ClassUtils;
4

5
import java.lang.reflect.Field;
6
import java.lang.reflect.Modifier;
7
import java.util.Arrays;
8
import java.util.LinkedHashMap;
9
import java.util.LinkedList;
10
import java.util.List;
11
import java.util.Optional;
12
import java.util.function.BiConsumer;
13
import java.util.function.Consumer;
14
import java.util.stream.Collector;
15
import java.util.stream.Collectors;
16
import java.util.stream.Stream;
17

18
/**
19
 * Class with utilities for processing fields.
20
 */
21
public final class FieldUtils {
22

23
    private FieldUtils() {
24
    }
25

26
    /**
27
     * Returns a human-friendly representation of a field, e.g. "ArrayList#size".
28
     *
29
     * @param field the field to format
30
     * @return text to reference a field
31
     */
32
    public static String formatField(Field field) {
33
        return ClassUtils.getSemanticName(field.getDeclaringClass()) + "#" + field.getName();
14✔
34
    }
35

36
    /**
37
     * Specifies whether a field is a "regular" (= non-synthetic) instance field. This method exists for brevity and
38
     * because it can be easily forgotten to exclude synthetic fields.
39
     *
40
     * @param field the field to process
41
     * @return true if the field is not synthetic and not static, false otherwise
42
     */
43
    public static boolean isRegularInstanceField(Field field) {
44
        return !field.isSynthetic() && !Modifier.isStatic(field.getModifiers());
11✔
45
    }
46

47
    /**
48
     * Specifies whether a field is a "regular" (= non-synthetic) static field. This method exists for brevity and
49
     * because it can be easily forgotten to exclude synthetic fields.
50
     *
51
     * @param field the field to process
52
     * @return true if the field is not synthetic and static, false otherwise
53
     */
54
    public static boolean isRegularStaticField(Field field) {
55
        return !field.isSynthetic() && Modifier.isStatic(field.getModifiers());
11✔
56
    }
57

58
    /**
59
     * Returns all fields from the given class and its parents, recursively. The fields of the top-most parent
60
     * in this class's hierarchy are returned first.
61
     *
62
     * @param clazz the class whose fields (incl. its parents' fields) should be returned
63
     * @return a stream of all fields, with top-most parent's fields first, and this class's fields last
64
     */
65
    public static Stream<Field> getAllFields(Class<?> clazz) {
66
        return getAllFields(clazz, true);
4✔
67
    }
68

69
    /**
70
     * Returns all fields from the given class and its parents, recursively. Depending on the parameter, fields are
71
     * either returned from top-to-bottom or bottom-to-top relative to the class's hierarchy.
72
     *
73
     * @param clazz the class whose fields (incl. its parents' fields) should be returned
74
     * @param topParentFirst true if the top-most parent's fields should come first, false for last
75
     * @return a stream of all fields, in the specified order
76
     */
77
    public static Stream<Field> getAllFields(Class<?> clazz, boolean topParentFirst) {
78
        LinkedList<Class<?>> classes = new LinkedList<>();
4✔
79
        collectParents(clazz, (topParentFirst ? classes::addFirst : classes::addLast));
15✔
80

81
        return classes.stream()
4✔
82
            .flatMap(clz -> Arrays.stream(clz.getDeclaredFields()));
5✔
83
    }
84

85
    /**
86
     * Returns all non-synthetic, non-static fields of the given class, including all fields from superclasses.
87
     *
88
     * @param clazz the class whose instance fields should be retrieved
89
     * @return all non-synthetic instance fields
90
     */
91
    public static List<Field> collectAllRegularInstanceFields(Class<?> clazz) {
92
        return getAllFields(clazz, true)
5✔
93
            .filter(FieldUtils::isRegularInstanceField)
1✔
94
            .collect(Collectors.toList());
3✔
95
    }
96

97
    /**
98
     * Returns an optional with a field on the given class having the given name, or an empty optional if the name
99
     * did not match any field. This method does not check the fields of the class's superclasses.
100
     *
101
     * @param clazz the class to check
102
     * @param name the name of the field to resolve
103
     * @return optional with the field in the class, or empty optional if there is no match
104
     */
105
    public static Optional<Field> tryFindField(Class<?> clazz, String name) {
106
        try {
107
            return Optional.of(clazz.getDeclaredField(name));
5✔
108
        } catch (NoSuchFieldException ignore) {
1✔
109
            return Optional.empty();
2✔
110
        }
111
    }
112

113
    /**
114
     * Returns an optional with a field of the given name on the class or one of its superclasses. If multiple fields
115
     * match the given name (on different superclasses), the field on the class that is lowest in the hierarchy is
116
     * returned. An empty optional is returned if no field was matched.
117
     *
118
     * @param clazz the class (and its superclasses) to inspect
119
     * @param name the name of the field to look for
120
     * @return optional with a field of the given name
121
     */
122
    public static Optional<Field> tryFindFieldInClassOrParent(Class<?> clazz, String name) {
123
        Class<?> currentClass = clazz;
2✔
124
        while (currentClass != null) {
2✔
125
            Optional<Field> field = tryFindField(currentClass, name);
4✔
126
            if (field.isPresent()) {
3✔
127
                return field;
2✔
128
            }
129
            currentClass = currentClass.getSuperclass();
3✔
130
        }
1✔
131
        return Optional.empty();
2✔
132
    }
133

134
    /**
135
     * Collector for a stream of fields to group them by name, with the parameter indicating whether the first
136
     * encountered field with a given name should be retained, or the last one. This collector does not support
137
     * parallel streams.
138
     * <p>
139
     * Note that this collector is especially useful if you are only dealing with instance fields: for a mechanism
140
     * that processes the instance fields of classes (e.g. for serialization), you may want to only consider the
141
     * lowest-most declared field per any given name so that behavior can be overridden.
142
     * <p>
143
     * To get <b>all fields</b> grouped by name, use {@code stream.collect(Collectors.groupingBy(Field::getName))}.
144
     *
145
     * @param firstFieldWins true if the first encountered field with a given name should be kept;
146
     *                       false to keep the last one
147
     * @return collector to collect names by field
148
     */
149
    public static Collector<Field, ?, LinkedHashMap<String, Field>> collectByName(boolean firstFieldWins) {
150
        BiConsumer<LinkedHashMap<String, Field>, Field> accumulator = firstFieldWins
6✔
151
            ? (map, field) -> map.putIfAbsent(field.getName(), field)
8✔
152
            : (map, field) -> map.put(field.getName(), field);
8✔
153

154
        return Collector.of(LinkedHashMap::new, accumulator,
7✔
155
            (a, b) -> { throw new UnsupportedOperationException(); });
×
156
    }
157

158
    private static void collectParents(Class<?> clazz, Consumer<Class<?>> classAdder) {
159
        Class<?> currentClass = clazz;
2✔
160
        while (currentClass != null) {
2✔
161
            classAdder.accept(currentClass);
3✔
162
            currentClass = currentClass.getSuperclass();
4✔
163
        }
164
    }
1✔
165
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc