1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.smartics.testdoc.collect.generator;
17
18 import java.io.File;
19 import java.io.StringWriter;
20 import java.nio.charset.Charset;
21 import java.nio.charset.IllegalCharsetNameException;
22 import java.nio.charset.UnsupportedCharsetException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Locale;
28
29 import javax.tools.Diagnostic;
30 import javax.tools.DiagnosticCollector;
31 import javax.tools.JavaCompiler;
32 import javax.tools.JavaCompiler.CompilationTask;
33 import javax.tools.JavaFileObject;
34 import javax.tools.StandardJavaFileManager;
35 import javax.tools.ToolProvider;
36
37 import org.apache.commons.io.FileUtils;
38 import org.apache.commons.lang.StringUtils;
39
40 import de.smartics.testdoc.collect.processor.TestDocProcessor;
41 import de.smartics.testdoc.core.TestDocProperty;
42 import de.smartics.testdoc.core.adapter.SingletonInMemoryExportAdapter;
43 import de.smartics.testdoc.core.export.ExportAdapter;
44 import de.smartics.testdoc.core.export.ExportException;
45
46
47
48
49
50
51
52
53 public final class TestDocGenerator
54 {
55
56
57
58
59
60
61
62
63
64 private final TestDocProcessor processor = new TestDocProcessor();
65
66
67
68
69 private final Config config;
70
71
72
73
74 private final Diagnostics diagnostics = new Diagnostics();
75
76
77
78
79
80
81
82
83
84
85
86
87 public TestDocGenerator(final Config config) throws IllegalArgumentException
88 {
89 checkArguments(config);
90
91 this.config = config;
92 }
93
94
95
96
97
98
99 public static final class Config
100 {
101
102
103
104 private final Charset charset;
105
106
107
108
109 private final Locale locale;
110
111
112
113
114 private final Collection<File> rootDirs;
115
116
117
118
119
120 private final List<String> classPathElements;
121
122
123
124
125
126 private final ExportAdapter exportAdapter;
127
128
129
130
131
132
133 private Config(final Builder builder)
134 {
135 this.charset = builder.charset;
136 this.locale = builder.locale;
137 this.rootDirs = builder.rootDirs;
138 this.classPathElements = builder.classPathElements;
139 this.exportAdapter = builder.exportAdapter;
140 }
141
142
143
144
145
146
147
148 public static final class Builder
149 {
150
151
152
153 private Charset charset;
154
155
156
157
158 private Locale locale;
159
160
161
162
163 private Collection<File> rootDirs;
164
165
166
167
168
169 private ExportAdapter exportAdapter;
170
171
172
173
174
175 private List<String> classPathElements;
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 public void setCharsetByEncoding(final String encoding)
191 throws IllegalCharsetNameException, IllegalArgumentException,
192 UnsupportedCharsetException
193 {
194 setCharset(Charset.forName(encoding));
195 }
196
197
198
199
200
201
202 public void setCharset(final Charset charset)
203 {
204 this.charset = charset;
205 }
206
207
208
209
210
211
212 public void setLocale(final Locale locale)
213 {
214 this.locale = locale;
215 }
216
217
218
219
220
221
222
223 public void setRootDirsAsString(final Collection<String> rootDirs)
224 {
225 final List<File> rootDirFiles = new ArrayList<File>(rootDirs.size());
226 for (final String dirName : rootDirs)
227 {
228 final File file = new File(dirName);
229 rootDirFiles.add(file);
230 }
231 setRootDirs(rootDirFiles);
232 }
233
234
235
236
237
238
239
240 public void setRootDirs(final Collection<File> rootDirs)
241 {
242 this.rootDirs = rootDirs;
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 @SuppressWarnings("unchecked")
276 public void setExportAdapterName(final String exportAdapterName)
277 throws InstantiationException, IllegalAccessException,
278 ExceptionInInitializerError, ClassNotFoundException
279 {
280 final Class<? extends ExportAdapter> exportAdapterType =
281 (Class<? extends ExportAdapter>) Class.forName(exportAdapterName);
282 setExportAdapterName(exportAdapterType);
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 public void setExportAdapterName(
313 final Class<? extends ExportAdapter> exportAdapterType)
314 throws InstantiationException, IllegalAccessException,
315 ExceptionInInitializerError, SecurityException
316 {
317 this.exportAdapter = exportAdapterType.newInstance();
318 }
319
320
321
322
323
324
325 Config build()
326 {
327 if (exportAdapter == null)
328 {
329 exportAdapter = new SingletonInMemoryExportAdapter();
330 }
331
332 return new Config(this);
333 }
334
335 public void setClassPathElements(final List<String> classPathElements)
336 {
337 this.classPathElements = classPathElements;
338 }
339 }
340
341 @SuppressWarnings("unchecked")
342 private List<File> collectSourceFiles()
343 {
344 final List<File> srcFiles = new ArrayList<File>(512);
345 final String[] extensions = new String[]
346 { "java" };
347 for (final File srcRootDir : rootDirs)
348 {
349 final Collection<File> files =
350 FileUtils.listFiles(srcRootDir, extensions, true);
351 srcFiles.addAll(files);
352 }
353 return srcFiles;
354 }
355
356 public List<String> getCompilerOptions()
357 {
358 final String classPath = toString(classPathElements);
359 return Arrays.asList(
360 "-A" + TestDocProperty.EXPORT_ADAPTER_CLASS_NAME.getName() + '='
361 + exportAdapter.getClass().getName(), "-Aclasspath=" + classPath,
362 "-classpath", classPath);
363 }
364
365 private static String toString(final List<String> classPathElements)
366 {
367 if (!classPathElements.isEmpty())
368 {
369 final StringBuilder buffer = new StringBuilder(256);
370 for (final String classPathElement : classPathElements)
371 {
372 buffer.append(classPathElement).append(File.pathSeparatorChar);
373 }
374 buffer.setLength(buffer.length() - 1);
375 return buffer.toString();
376 }
377 return StringUtils.EMPTY;
378 }
379 }
380
381
382
383
384 private final class Diagnostics
385 {
386
387
388
389 private final DiagnosticCollector<JavaFileObject> collector =
390 new DiagnosticCollector<JavaFileObject>();
391
392
393
394
395 private final StringWriter output = new StringWriter();
396
397 public String toString()
398 {
399 final StringBuilder buffer = new StringBuilder(512);
400 buffer.append("Compiler output:\n").append(output.toString());
401 buffer.append("\nDiagnostics:\n");
402
403 final Locale locale =
404 config.locale != null ? config.locale : Locale.getDefault();
405 for (final Diagnostic<?> diagnostic : collector.getDiagnostics())
406 {
407 buffer.append(diagnostic.getMessage(locale)).append('\n');
408 }
409
410 return buffer.toString();
411 }
412 }
413
414
415
416
417
418 private static void checkArguments(final Config config)
419 throws IllegalArgumentException
420 {
421 if (config == null)
422 {
423 throw new IllegalArgumentException(
424 "The configuration for the generation helper must not be 'null'.");
425 }
426 }
427
428
429
430
431
432
433
434
435
436
437 public void generate() throws ExportException
438 {
439 final List<File> srcFiles = config.collectSourceFiles();
440 final CompilationTask task = createCompilationTask(srcFiles);
441 task.setProcessors(Arrays.asList(processor));
442 try
443 {
444 final boolean success = task.call();
445 if (!success)
446 {
447 throw new ExportException(
448 "The test doc information cannot be generated.\n" + diagnostics);
449 }
450 }
451 catch (final RuntimeException e)
452 {
453 throw new ExportException(
454 "The test doc information cannot be generated.\n" + diagnostics, e);
455 }
456 }
457
458 private CompilationTask createCompilationTask(final List<File> srcFiles)
459 {
460 final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
461 final StandardJavaFileManager fileManager =
462 compiler.getStandardFileManager(diagnostics.collector, config.locale,
463 config.charset);
464 final Iterable<? extends JavaFileObject> compilationUnits =
465 fileManager.getJavaFileObjects(srcFiles.toArray(new File[srcFiles
466 .size()]));
467
468 final List<String> options = config.getCompilerOptions();
469
470 final CompilationTask task =
471 compiler.getTask(diagnostics.output, fileManager,
472 diagnostics.collector, options, null, compilationUnits);
473 return task;
474 }
475
476
477
478
479
480
481
482
483
484 public static void runGenerator(final TestDocGenerator.Config.Builder builder)
485 {
486 final TestDocGenerator.Config config = builder.build();
487
488 final TestDocGenerator generator = new TestDocGenerator(config);
489 generator.generate();
490 }
491
492
493
494 }