-
-
Notifications
You must be signed in to change notification settings - Fork 690
/
ConfigurationBuilder.java
241 lines (208 loc) · 9.49 KB
/
ConfigurationBuilder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package org.reflections.util;
import org.reflections.Configuration;
import org.reflections.ReflectionsException;
import org.reflections.scanners.Scanner;
import org.reflections.scanners.Scanners;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* {@link Configuration} builder for instantiating Reflections
* <pre>{@code
* // add urls for package prefix, use default scanners
* new Reflections(
* new ConfigurationBuilder()
* .forPackage("org.reflections"))
*
* new Reflections(
* new ConfigurationBuilder()
* .addUrls(ClasspathHelper.forPackage("org.reflections")) // add urls for package prefix
* .addScanners(Scanners.values()) // use all standard scanners
* .filterInputsBy(new FilterBuilder().includePackage(...))) // optionally filter inputs
* }</pre>
* <p>defaults scanners: {@link Scanners#SubTypes} and {@link Scanners#TypesAnnotated}
* <p><i>(breaking changes) Inputs filter will NOT be set automatically, consider adding in case too many classes are scanned.</i>
*/
public class ConfigurationBuilder implements Configuration {
public static final Set<Scanner> DEFAULT_SCANNERS = new HashSet<>(Arrays.asList(Scanners.TypesAnnotated, Scanners.SubTypes));
public static final Predicate<String> DEFAULT_INPUTS_FILTER = t -> true;
private Set<Scanner> scanners;
private Set<URL> urls;
private Predicate<String> inputsFilter;
private boolean isParallel = true;
private ClassLoader[] classLoaders;
private boolean expandSuperTypes = true;
public ConfigurationBuilder() {
urls = new HashSet<>();
}
/** constructs a {@link ConfigurationBuilder}.
* <p>each parameter in {@code params} is referred by its type:
* <ul>
* <li>{@link String} - add urls using {@link ClasspathHelper#forPackage(String, ClassLoader...)} and an input filter
* <li>{@link Class} - add urls using {@link ClasspathHelper#forClass(Class, ClassLoader...)} and an input filter
* <li>{@link Scanner} - use scanner, overriding default scanners
* <li>{@link URL} - add url for scanning
* <li>{@link Predicate} - set/override inputs filter
* <li>{@link ClassLoader} - use these classloaders in order to find urls using ClasspathHelper and for resolving types
* <li>{@code Object[]} - flatten and use each element as above
* </ul>
* input filter will be set according to given packages
* <p></p><i>prefer using the explicit accessor methods instead:</i>
* <pre>{@code new ConfigurationBuilder().forPackage(...).setScanners(...)}</pre>
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static ConfigurationBuilder build(Object... params) {
final ConfigurationBuilder builder = new ConfigurationBuilder();
// flatten
List<Object> parameters = new ArrayList<>();
for (Object param : params) {
if (param != null) {
if (param.getClass().isArray()) { for (Object p : (Object[]) param) parameters.add(p); }
else if (param instanceof Iterable) { for (Object p : (Iterable) param) parameters.add(p); }
else parameters.add(param);
}
}
ClassLoader[] loaders = Stream.of(params).filter(p -> p instanceof ClassLoader).distinct().toArray(ClassLoader[]::new);
if (loaders.length != 0) { builder.addClassLoaders(loaders); }
FilterBuilder inputsFilter = new FilterBuilder();
builder.filterInputsBy(inputsFilter);
for (Object param : parameters) {
if (param instanceof String && !((String) param).isEmpty()) {
builder.forPackage((String) param, loaders);
inputsFilter.includePackage((String) param);
} else if (param instanceof Class && !Scanner.class.isAssignableFrom((Class) param)) {
builder.addUrls(ClasspathHelper.forClass((Class) param, loaders));
inputsFilter.includePackage(((Class) param).getPackage().getName());
} else if (param instanceof URL) {
builder.addUrls((URL) param);
} else if (param instanceof Scanner) {
builder.addScanners((Scanner) param);
} else if (param instanceof Class && Scanner.class.isAssignableFrom((Class) param)) {
try { builder.addScanners(((Class<Scanner>) param).getDeclaredConstructor().newInstance()); }
catch (Exception e) { throw new RuntimeException(e); }
} else if (param instanceof Predicate) {
builder.filterInputsBy((Predicate<String>) param);
} else throw new ReflectionsException("could not use param '" + param + "'");
}
if (builder.getUrls().isEmpty()) {
// scan all classpath if no urls provided todo avoid
builder.addUrls(ClasspathHelper.forClassLoader(loaders));
}
return builder;
}
/** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code pkg}*/
public ConfigurationBuilder forPackage(String pkg, ClassLoader... classLoaders) {
return addUrls(ClasspathHelper.forPackage(pkg, classLoaders));
}
/** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code packages}*/
public ConfigurationBuilder forPackages(String... packages) {
for (String pkg : packages) forPackage(pkg);
return this;
}
@Override
/* @inherited */
public Set<Scanner> getScanners() {
return scanners != null ? scanners : DEFAULT_SCANNERS;
}
/** set the scanners instances for scanning different metadata */
public ConfigurationBuilder setScanners(Scanner... scanners) {
this.scanners = new HashSet<>(Arrays.asList(scanners));
return this;
}
/** set the scanners instances for scanning different metadata */
public ConfigurationBuilder addScanners(Scanner... scanners) {
if (this.scanners == null) setScanners(scanners); else this.scanners.addAll(Arrays.asList(scanners));
return this;
}
@Override
/* @inherited */
public Set<URL> getUrls() {
return urls;
}
/** set the urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder setUrls(Collection<URL> urls) {
this.urls = new HashSet<>(urls);
return this;
}
/** set the urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder setUrls(URL... urls) {
return setUrls(Arrays.asList(urls));
}
/** add urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder addUrls(Collection<URL> urls) {
this.urls.addAll(urls);
return this;
}
/** add urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder addUrls(URL... urls) {
return addUrls(Arrays.asList(urls));
}
@Override
/* @inherited */
public Predicate<String> getInputsFilter() {
return inputsFilter != null ? inputsFilter : DEFAULT_INPUTS_FILTER;
}
/** sets the input filter for all resources to be scanned.
* <p>prefer using {@link FilterBuilder} */
public ConfigurationBuilder setInputsFilter(Predicate<String> inputsFilter) {
this.inputsFilter = inputsFilter;
return this;
}
/** sets the input filter for all resources to be scanned.
* <p>prefer using {@link FilterBuilder} */
public ConfigurationBuilder filterInputsBy(Predicate<String> inputsFilter) {
return setInputsFilter(inputsFilter);
}
@Override
/* @inherited */
public boolean isParallel() {
return isParallel;
}
/** if true, scan urls in parallel. */
public ConfigurationBuilder setParallel(boolean parallel) {
isParallel = parallel;
return this;
}
@Override
/* @inherited */
public ClassLoader[] getClassLoaders() {
return classLoaders;
}
/** set optional class loaders used for resolving types. */
public ConfigurationBuilder setClassLoaders(ClassLoader[] classLoaders) {
this.classLoaders = classLoaders;
return this;
}
/** add optional class loaders used for resolving types. */
public ConfigurationBuilder addClassLoaders(ClassLoader... classLoaders) {
this.classLoaders = this.classLoaders == null ? classLoaders :
Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)).distinct().toArray(ClassLoader[]::new);
return this;
}
@Override
/* @inherited */
public boolean shouldExpandSuperTypes() {
return expandSuperTypes;
}
/** if set to true, Reflections will expand super types after scanning.
* <p>see {@link org.reflections.Reflections#expandSuperTypes(Map, Map)} */
public ConfigurationBuilder setExpandSuperTypes(boolean expandSuperTypes) {
this.expandSuperTypes = expandSuperTypes;
return this;
}
}