1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.tools;
20
21 import java.io.BufferedWriter;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25 import java.io.UncheckedIOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Properties;
33 import java.util.TreeMap;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 import java.util.spi.ToolProvider;
37
38 import org.apache.velocity.VelocityContext;
39 import org.apache.velocity.app.VelocityEngine;
40 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
41 import org.jboss.forge.roaster.Roaster;
42 import org.jboss.forge.roaster.model.JavaDocCapable;
43 import org.jboss.forge.roaster.model.JavaDocTag;
44 import org.jboss.forge.roaster.model.JavaType;
45 import org.jboss.forge.roaster.model.source.FieldSource;
46 import org.jboss.forge.roaster.model.source.JavaClassSource;
47
48 public class CollectConfiguration {
49 public static void main(String[] args) throws Exception {
50 Path start = Paths.get(args.length > 0 ? args[0] : ".");
51 Path output = Paths.get(args.length > 1 ? args[1] : "output");
52
53 TreeMap<String, ConfigurationKey> discoveredKeys = new TreeMap<>();
54 Files.walk(start)
55 .map(Path::toAbsolutePath)
56 .filter(p -> p.getFileName().toString().endsWith(".java"))
57 .filter(p -> p.toString().contains("/src/main/java/"))
58 .filter(p -> !p.toString().endsWith("/module-info.java"))
59 .forEach(p -> {
60 JavaType<?> type = parse(p);
61 if (type instanceof JavaClassSource javaClassSource) {
62 javaClassSource.getFields().stream()
63 .filter(CollectConfiguration::hasConfigurationSource)
64 .forEach(f -> {
65 Map<String, String> constants = extractConstants(Paths.get(p.toString()
66 .replace("/src/main/java/", "/target/classes/")
67 .replace(".java", ".class")));
68
69 String name = f.getName();
70 String key = constants.get(name);
71 String fqName = f.getOrigin().getCanonicalName() + "." + name;
72 String configurationType = getConfigurationType(f);
73 String defValue = getTag(f, "@configurationDefaultValue");
74 if (defValue != null && defValue.startsWith("{@link #") && defValue.endsWith("}")) {
75
76 String lookupValue =
77 constants.get(defValue.substring(8, defValue.length() - 1));
78 if (lookupValue == null) {
79
80
81
82 throw new IllegalArgumentException(
83 "Could not look up " + defValue + " for configuration " + fqName);
84 }
85 defValue = lookupValue;
86 }
87 if ("java.lang.Long".equals(configurationType)
88 && (defValue.endsWith("l") || defValue.endsWith("L"))) {
89 defValue = defValue.substring(0, defValue.length() - 1);
90 }
91 discoveredKeys.put(
92 key,
93 new ConfigurationKey(
94 key,
95 defValue,
96 fqName,
97 f.getJavaDoc().getText(),
98 nvl(getSince(f), ""),
99 getConfigurationSource(f),
100 configurationType,
101 toBoolean(getTag(f, "@configurationRepoIdSuffix"))));
102 });
103 }
104 });
105
106 VelocityEngine velocityEngine = new VelocityEngine();
107 Properties properties = new Properties();
108 properties.setProperty("resource.loaders", "classpath");
109 properties.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
110 velocityEngine.init(properties);
111
112 VelocityContext context = new VelocityContext();
113 context.put("keys", discoveredKeys.values());
114
115 try (BufferedWriter fileWriter = Files.newBufferedWriter(output)) {
116 velocityEngine.getTemplate("page.vm").merge(context, fileWriter);
117 }
118 }
119
120 private static JavaType<?> parse(Path path) {
121 try {
122 return Roaster.parse(path.toFile());
123 } catch (IOException e) {
124 throw new UncheckedIOException(e);
125 }
126 }
127
128 private static boolean toBoolean(String value) {
129 return ("yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value));
130 }
131
132
133
134
135 public static class ConfigurationKey {
136 private final String key;
137 private final String defaultValue;
138 private final String fqName;
139 private final String description;
140 private final String since;
141 private final String configurationSource;
142 private final String configurationType;
143 private final boolean supportRepoIdSuffix;
144
145 @SuppressWarnings("checkstyle:parameternumber")
146 public ConfigurationKey(
147 String key,
148 String defaultValue,
149 String fqName,
150 String description,
151 String since,
152 String configurationSource,
153 String configurationType,
154 boolean supportRepoIdSuffix) {
155 this.key = key;
156 this.defaultValue = defaultValue;
157 this.fqName = fqName;
158 this.description = description;
159 this.since = since;
160 this.configurationSource = configurationSource;
161 this.configurationType = configurationType;
162 this.supportRepoIdSuffix = supportRepoIdSuffix;
163 }
164
165 public String getKey() {
166 return key;
167 }
168
169 public String getDefaultValue() {
170 return defaultValue;
171 }
172
173 public String getFqName() {
174 return fqName;
175 }
176
177 public String getDescription() {
178 return description;
179 }
180
181 public String getSince() {
182 return since;
183 }
184
185 public String getConfigurationSource() {
186 return configurationSource;
187 }
188
189 public String getConfigurationType() {
190 return configurationType;
191 }
192
193 public boolean isSupportRepoIdSuffix() {
194 return supportRepoIdSuffix;
195 }
196 }
197
198 private static String nvl(String string, String def) {
199 return string == null ? def : string;
200 }
201
202 private static boolean hasConfigurationSource(JavaDocCapable<?> javaDocCapable) {
203 return getTag(javaDocCapable, "@configurationSource") != null;
204 }
205
206 private static String getConfigurationType(JavaDocCapable<?> javaDocCapable) {
207 String type = getTag(javaDocCapable, "@configurationType");
208 if (type != null) {
209 String linkPrefix = "{@link ";
210 String linkSuffix = "}";
211 if (type.startsWith(linkPrefix) && type.endsWith(linkSuffix)) {
212 type = type.substring(linkPrefix.length(), type.length() - linkSuffix.length());
213 }
214 String javaLangPackage = "java.lang.";
215 if (type.startsWith(javaLangPackage)) {
216 type = type.substring(javaLangPackage.length());
217 }
218 }
219 return nvl(type, "n/a");
220 }
221
222 private static String getConfigurationSource(JavaDocCapable<?> javaDocCapable) {
223 String source = getTag(javaDocCapable, "@configurationSource");
224 if ("{@link RepositorySystemSession#getConfigProperties()}".equals(source)) {
225 return "Session Configuration";
226 } else if ("{@link System#getProperty(String,String)}".equals(source)) {
227 return "Java System Properties";
228 } else {
229 return source;
230 }
231 }
232
233 private static String getSince(JavaDocCapable<?> javaDocCapable) {
234 List<JavaDocTag> tags;
235 if (javaDocCapable != null) {
236 if (javaDocCapable instanceof FieldSource<?> fieldSource) {
237 tags = fieldSource.getJavaDoc().getTags("@since");
238 if (tags.isEmpty()) {
239 return getSince(fieldSource.getOrigin());
240 } else {
241 return tags.get(0).getValue();
242 }
243 } else if (javaDocCapable instanceof JavaClassSource classSource) {
244 tags = classSource.getJavaDoc().getTags("@since");
245 if (!tags.isEmpty()) {
246 return tags.get(0).getValue();
247 }
248 }
249 }
250 return null;
251 }
252
253 private static String getTag(JavaDocCapable<?> javaDocCapable, String tagName) {
254 List<JavaDocTag> tags;
255 if (javaDocCapable != null) {
256 if (javaDocCapable instanceof FieldSource<?> fieldSource) {
257 tags = fieldSource.getJavaDoc().getTags(tagName);
258 if (tags.isEmpty()) {
259 return getTag(fieldSource.getOrigin(), tagName);
260 } else {
261 return tags.get(0).getValue();
262 }
263 }
264 }
265 return null;
266 }
267
268 private static final Pattern CONSTANT_PATTERN = Pattern.compile(".*static final.* ([A-Z_]+) = (.*);");
269
270 private static final ToolProvider JAVAP = ToolProvider.findFirst("javap").orElseThrow();
271
272
273
274
275
276
277
278
279
280 private static Map<String, String> extractConstants(Path file) {
281 StringWriter out = new StringWriter();
282 JAVAP.run(new PrintWriter(out), new PrintWriter(System.err), "-constants", file.toString());
283 Map<String, String> result = new HashMap<>();
284 out.getBuffer().toString().lines().forEach(l -> {
285 Matcher matcher = CONSTANT_PATTERN.matcher(l);
286 if (matcher.matches()) {
287 result.put(matcher.group(1), matcher.group(2));
288 }
289 });
290 return result;
291 }
292 }