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

AuthMe / ConfigMe / 11407666717

18 Oct 2024 04:46PM UTC coverage: 99.322% (-0.09%) from 99.411%
11407666717

Pull #398

github

ljacqu
Fix toString of NumberType to be like class name
Pull Request #398: #135 Support more ways to create beans (e.g. Java records)

557 of 574 branches covered (97.04%)

201 of 202 new or added lines in 11 files covered. (99.5%)

7 existing lines in 2 files now uncovered.

1612 of 1623 relevant lines covered (99.32%)

4.6 hits per line

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

94.74
/src/main/java/ch/jalu/configme/configurationdata/ConfigurationDataBuilder.java
1
package ch.jalu.configme.configurationdata;
2

3
import ch.jalu.configme.Comment;
4
import ch.jalu.configme.SettingsHolder;
5
import ch.jalu.configme.exception.ConfigMeException;
6
import ch.jalu.configme.internal.ReflectionHelper;
7
import ch.jalu.configme.properties.Property;
8
import org.jetbrains.annotations.NotNull;
9
import org.jetbrains.annotations.Nullable;
10

11
import java.lang.reflect.Constructor;
12
import java.lang.reflect.Field;
13
import java.lang.reflect.InvocationTargetException;
14
import java.lang.reflect.Modifier;
15
import java.util.ArrayList;
16
import java.util.Arrays;
17
import java.util.Collections;
18
import java.util.List;
19
import java.util.stream.Stream;
20

21
/**
22
 * Utility class responsible for creating {@link ConfigurationData} by retrieving {@link Property} fields
23
 * from {@link SettingsHolder} implementations and gathering all comments.
24
 */
25
public class ConfigurationDataBuilder {
26

27
    private final @NotNull PropertyListBuilder propertyListBuilder;
28
    private final @NotNull CommentsConfiguration commentsConfiguration;
29

30
    /**
31
     * Constructor. Use {@link #createConfiguration(Class[])} or a similar static method to create configuration data.
32
     * Use the constructors of this class only if you are overriding specific behavior.
33
     */
34
    protected ConfigurationDataBuilder() {
35
        this(new PropertyListBuilder(), new CommentsConfiguration());
8✔
36
    }
1✔
37

38
    /**
39
     * Constructor. Use {@link #createConfiguration(Class[])} or a similar static method to create configuration data.
40
     * Use the constructors of this class only if you are overriding specific behavior.
41
     *
42
     * @param propertyListBuilder property list builder to order and validate property paths
43
     * @param commentsConfiguration comments configuration to keep track of all comments
44
     */
45
    public ConfigurationDataBuilder(@NotNull PropertyListBuilder propertyListBuilder,
46
                                    @NotNull CommentsConfiguration commentsConfiguration) {
2✔
47
        this.propertyListBuilder = propertyListBuilder;
3✔
48
        this.commentsConfiguration = commentsConfiguration;
3✔
49
    }
1✔
50

51
    /**
52
     * Collects all properties and comment data from the provided classes.
53
     * Properties are sorted by their group, and each group is sorted by order of encounter.
54
     *
55
     * @param classes the classes to scan for their property data
56
     * @return collected configuration data
57
     */
58
    @SafeVarargs
59
    public static @NotNull ConfigurationData createConfiguration(@NotNull Class<? extends SettingsHolder>... classes) {
60
        return createConfiguration(Arrays.asList(classes));
4✔
61
    }
62

63
    /**
64
     * Collects all properties and comment data from the provided classes.
65
     * Properties are sorted by their group, and each group is sorted by order of encounter.
66
     *
67
     * @param classes the classes to scan for their property data
68
     * @return collected configuration data
69
     */
70
    public static @NotNull ConfigurationData createConfiguration(
71
                                                           @NotNull Iterable<Class<? extends SettingsHolder>> classes) {
72
        ConfigurationDataBuilder builder = new ConfigurationDataBuilder();
4✔
73
        return builder.collectData(classes);
4✔
74
    }
75

76
    /**
77
     * Manually creates configuration data with the given properties, without any comments. Note that the given
78
     * properties must be in an order that is suitable for exporting. For instance, the default YAML file resource
79
     * requires that all properties with the same parent be grouped together (see {@link PropertyListBuilder}).
80
     *
81
     * @param properties the properties that make up the configuration data
82
     * @return configuration data with the given properties
83
     */
84
    public static @NotNull ConfigurationData createConfiguration(@NotNull List<? extends Property<?>> properties) {
85
        return new ConfigurationDataImpl(properties, Collections.emptyMap());
6✔
86
    }
87

88
    /**
89
     * Manually creates configuration data with the given properties and comments. Note that the given
90
     * properties must be in an order that is suitable for exporting. For instance, the default YAML file resource
91
     * requires that all properties with the same parent be grouped together.
92
     *
93
     * @param properties the properties that make up the configuration data
94
     * @param commentsConfiguration the comments to include in the export
95
     * @return configuration data with the given properties
96
     */
97
    public static @NotNull ConfigurationData createConfiguration(@NotNull List<? extends Property<?>> properties,
98
                                                                 @NotNull CommentsConfiguration commentsConfiguration) {
99
        return new ConfigurationDataImpl(properties, commentsConfiguration.getAllComments());
7✔
100
    }
101

102
    /**
103
     * Collects property data and comment info from the given class and creates a configuration data
104
     * instance with it.
105
     *
106
     * @param classes the classes to process
107
     * @return configuration data with the classes' data
108
     */
109
    public @NotNull ConfigurationData collectData(@NotNull Iterable<Class<? extends SettingsHolder>> classes) {
110
        for (Class<? extends SettingsHolder> clazz : classes) {
10✔
111
            collectProperties(clazz);
3✔
112
            collectSectionComments(clazz);
3✔
113
        }
1✔
114
        return new ConfigurationDataImpl(propertyListBuilder.create(), commentsConfiguration.getAllComments());
10✔
115
    }
116

117
    /**
118
     * Registers all property fields of the given class to this instance's property list builder.
119
     *
120
     * @param clazz the class to process
121
     */
122
    protected void collectProperties(@NotNull Class<?> clazz) {
123
        findFieldsToProcess(clazz).forEach(field -> {
6✔
124
            Property<?> property = getPropertyField(field);
4✔
125
            if (property != null) {
2✔
126
                propertyListBuilder.add(property);
4✔
127
                setCommentForPropertyField(field, property.getPath());
5✔
128
            }
129
        });
1✔
130
    }
1✔
131

132
    protected final @NotNull PropertyListBuilder getPropertyListBuilder() {
133
        return propertyListBuilder;
3✔
134
    }
135

136
    protected final @NotNull CommentsConfiguration getCommentsConfiguration() {
137
        return commentsConfiguration;
3✔
138
    }
139

140
    protected void setCommentForPropertyField(@NotNull Field field, @NotNull String path) {
141
        Comment commentAnnotation = field.getAnnotation(Comment.class);
5✔
142
        if (commentAnnotation != null) {
2✔
143
            commentsConfiguration.setComment(path, commentAnnotation.value());
6✔
144
        }
145
    }
1✔
146

147
    /**
148
     * Returns the given field's value if it is a static {@link Property}.
149
     *
150
     * @param field the field's value to return
151
     * @return the property the field defines, or null if not applicable
152
     */
153
    protected @Nullable Property<?> getPropertyField(@NotNull Field field) {
154
        if (Property.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
9✔
155
            try {
156
                ReflectionHelper.setAccessibleIfNeeded(field);
2✔
157
                return (Property<?>) field.get(null);
5✔
UNCOV
158
            } catch (IllegalAccessException e) {
×
UNCOV
159
                throw new ConfigMeException("Could not fetch field '" + field.getName() + "' from class '"
×
UNCOV
160
                    + field.getDeclaringClass().getSimpleName() + "'. Is it maybe not public?", e);
×
161
            }
162
        }
163
        return null;
2✔
164
    }
165

166
    protected void collectSectionComments(@NotNull Class<? extends SettingsHolder> clazz) {
167
        SettingsHolder settingsHolder = createSettingsHolderInstance(clazz);
4✔
168
        settingsHolder.registerComments(commentsConfiguration);
4✔
169
    }
1✔
170

171
    /**
172
     * Creates an instance of the given settings holder class.
173
     *
174
     * @param clazz the class to instantiate
175
     * @param <T> the class type
176
     * @return instance of the class
177
     */
178
    protected <T extends SettingsHolder> @NotNull T createSettingsHolderInstance(@NotNull Class<T> clazz) {
179
        try {
180
            Constructor<T> constructor = clazz.getDeclaredConstructor();
5✔
181
            ReflectionHelper.setAccessibleIfNeeded(constructor);
2✔
182
            return constructor.newInstance();
6✔
183
        } catch (NoSuchMethodException e) {
1✔
184
            throw new ConfigMeException("Expected no-args constructor to be available for " + clazz, e);
13✔
185
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
1✔
186
            throw new ConfigMeException("Could not create instance of " + clazz, e);
13✔
187
        }
188
    }
189

190
    /**
191
     * Returns all fields of the class which should be considered as potential {@link Property} definitions.
192
     * Considers the class's parents.
193
     *
194
     * @param clazz the class whose fields should be returned
195
     * @return stream of all the fields to process
196
     */
197
    protected @NotNull Stream<Field> findFieldsToProcess(@NotNull Class<?> clazz) {
198
        // In most cases we expect the class not to have any parent, so we check here and "fast track" this case
199
        if (Object.class.equals(clazz.getSuperclass())) {
5✔
200
            return Arrays.stream(clazz.getDeclaredFields());
4✔
201
        }
202

203
        List<Class<?>> classes = new ArrayList<>();
4✔
204
        Class<?> currentClass = clazz;
2✔
205
        while (currentClass != null && !currentClass.equals(Object.class)) {
6✔
206
            classes.add(currentClass);
4✔
207
            currentClass = currentClass.getSuperclass();
4✔
208
        }
209
        Collections.reverse(classes);
2✔
210

211
        return classes.stream()
4✔
212
            .map(Class::getDeclaredFields)
2✔
213
            .flatMap(Arrays::stream);
1✔
214
    }
215
}
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