1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.tools.plugin.generator;
20
21 import javax.swing.text.MutableAttributeSet;
22 import javax.swing.text.html.HTML;
23 import javax.swing.text.html.HTMLEditorKit;
24 import javax.swing.text.html.parser.ParserDelegator;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.StringReader;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.net.URLClassLoader;
34 import java.nio.charset.StandardCharsets;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.HashMap;
38 import java.util.LinkedList;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Stack;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 import org.apache.maven.artifact.Artifact;
46 import org.apache.maven.artifact.DependencyResolutionRequiredException;
47 import org.apache.maven.plugin.descriptor.MojoDescriptor;
48 import org.apache.maven.plugin.descriptor.PluginDescriptor;
49 import org.apache.maven.project.MavenProject;
50 import org.apache.maven.reporting.MavenReport;
51 import org.codehaus.plexus.component.repository.ComponentDependency;
52 import org.codehaus.plexus.util.StringUtils;
53 import org.codehaus.plexus.util.xml.XMLWriter;
54 import org.w3c.tidy.Tidy;
55
56
57
58
59
60
61 public final class GeneratorUtils {
62 private GeneratorUtils() {
63
64 }
65
66
67
68
69
70 public static void writeDependencies(XMLWriter w, PluginDescriptor pluginDescriptor) {
71 w.startElement("dependencies");
72
73 List<ComponentDependency> deps = pluginDescriptor.getDependencies();
74 for (ComponentDependency dep : deps) {
75 w.startElement("dependency");
76
77 element(w, "groupId", dep.getGroupId());
78
79 element(w, "artifactId", dep.getArtifactId());
80
81 element(w, "type", dep.getType());
82
83 element(w, "version", dep.getVersion());
84
85 w.endElement();
86 }
87
88 w.endElement();
89 }
90
91
92
93
94
95
96 public static void element(XMLWriter w, String name, String value) {
97 w.startElement(name);
98
99 if (value == null) {
100 value = "";
101 }
102
103 w.writeText(value);
104
105 w.endElement();
106 }
107
108
109
110
111
112 public static List<ComponentDependency> toComponentDependencies(Collection<Artifact> artifacts) {
113 List<ComponentDependency> componentDeps = new LinkedList<>();
114
115 for (Artifact artifact : artifacts) {
116 if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) {
117 continue;
118 }
119
120 ComponentDependency cd = new ComponentDependency();
121
122 cd.setArtifactId(artifact.getArtifactId());
123 cd.setGroupId(artifact.getGroupId());
124 cd.setVersion(artifact.getVersion());
125 cd.setType(artifact.getType());
126
127 componentDeps.add(cd);
128 }
129
130 return componentDeps;
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145 private static String quoteReplacement(String s) {
146 if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1)) {
147 return s;
148 }
149
150 StringBuilder sb = new StringBuilder();
151 for (int i = 0; i < s.length(); i++) {
152 char c = s.charAt(i);
153 if (c == '\\') {
154 sb.append('\\');
155 sb.append('\\');
156 } else if (c == '$') {
157 sb.append('\\');
158 sb.append('$');
159 } else {
160 sb.append(c);
161 }
162 }
163
164 return sb.toString();
165 }
166
167
168
169
170
171
172
173
174
175 @Deprecated
176 static String decodeJavadocTags(String description) {
177 if (StringUtils.isEmpty(description)) {
178 return "";
179 }
180
181 StringBuffer decoded = new StringBuffer(description.length() + 1024);
182
183 Matcher matcher = Pattern.compile("\\{@(\\w+)\\s*([^\\}]*)\\}").matcher(description);
184 while (matcher.find()) {
185 String tag = matcher.group(1);
186 String text = matcher.group(2);
187 text = StringUtils.replace(text, "&", "&");
188 text = StringUtils.replace(text, "<", "<");
189 text = StringUtils.replace(text, ">", ">");
190 if ("code".equals(tag)) {
191 text = "<code>" + text + "</code>";
192 } else if ("link".equals(tag) || "linkplain".equals(tag) || "value".equals(tag)) {
193 String pattern = "(([^#\\.\\s]+\\.)*([^#\\.\\s]+))?" + "(#([^\\(\\s]*)(\\([^\\)]*\\))?\\s*(\\S.*)?)?";
194 final int label = 7;
195 final int clazz = 3;
196 final int member = 5;
197 final int args = 6;
198 Matcher link = Pattern.compile(pattern).matcher(text);
199 if (link.matches()) {
200 text = link.group(label);
201 if (StringUtils.isEmpty(text)) {
202 text = link.group(clazz);
203 if (StringUtils.isEmpty(text)) {
204 text = "";
205 }
206 if (StringUtils.isNotEmpty(link.group(member))) {
207 if (StringUtils.isNotEmpty(text)) {
208 text += '.';
209 }
210 text += link.group(member);
211 if (StringUtils.isNotEmpty(link.group(args))) {
212 text += "()";
213 }
214 }
215 }
216 }
217 if (!"linkplain".equals(tag)) {
218 text = "<code>" + text + "</code>";
219 }
220 }
221 matcher.appendReplacement(decoded, (text != null) ? quoteReplacement(text) : "");
222 }
223 matcher.appendTail(decoded);
224
225 return decoded.toString();
226 }
227
228
229
230
231
232
233
234
235 @Deprecated
236 public static String makeHtmlValid(String description) {
237
238 if (StringUtils.isEmpty(description)) {
239 return "";
240 }
241
242 String commentCleaned = decodeJavadocTags(description);
243
244
245 Tidy tidy = new Tidy();
246 tidy.setDocType("loose");
247 tidy.setXHTML(true);
248 tidy.setXmlOut(true);
249 tidy.setInputEncoding("UTF-8");
250 tidy.setOutputEncoding("UTF-8");
251 tidy.setMakeClean(true);
252 tidy.setNumEntities(true);
253 tidy.setQuoteNbsp(false);
254 tidy.setQuiet(true);
255 tidy.setShowWarnings(true);
256
257 ByteArrayOutputStream out = new ByteArrayOutputStream(commentCleaned.length() + 256);
258 tidy.parse(new ByteArrayInputStream(commentCleaned.getBytes(StandardCharsets.UTF_8)), out);
259 commentCleaned = new String(out.toByteArray(), StandardCharsets.UTF_8);
260
261 if (StringUtils.isEmpty(commentCleaned)) {
262 return "";
263 }
264
265
266 String ls = System.getProperty("line.separator");
267 int startPos = commentCleaned.indexOf("<body>" + ls) + 6 + ls.length();
268 int endPos = commentCleaned.indexOf(ls + "</body>");
269 commentCleaned = commentCleaned.substring(startPos, endPos);
270
271 return commentCleaned;
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292 @Deprecated
293 public static String toText(String html) {
294 if (StringUtils.isEmpty(html)) {
295 return "";
296 }
297
298 final StringBuilder sb = new StringBuilder();
299
300 HTMLEditorKit.Parser parser = new ParserDelegator();
301 HTMLEditorKit.ParserCallback htmlCallback = new MojoParserCallback(sb);
302
303 try {
304 parser.parse(new StringReader(makeHtmlValid(html)), htmlCallback, true);
305 } catch (IOException e) {
306 throw new RuntimeException(e);
307 }
308
309 return sb.toString().replace('\"', '\'');
310 }
311
312
313
314
315 private static class MojoParserCallback extends HTMLEditorKit.ParserCallback {
316
317
318
319 class Counter {
320 int value;
321 }
322
323
324
325
326 private boolean body;
327
328
329
330
331 private int preformatted;
332
333
334
335
336 private int depth;
337
338
339
340
341
342 private Stack<Counter> numbering = new Stack<>();
343
344
345
346
347
348
349 private boolean pendingNewline;
350
351
352
353
354 private boolean simpleTag;
355
356
357
358
359 private final StringBuilder sb;
360
361
362
363
364 MojoParserCallback(StringBuilder sb) {
365 this.sb = sb;
366 }
367
368
369 @Override
370 public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
371 simpleTag = true;
372 if (body && HTML.Tag.BR.equals(t)) {
373 newline(false);
374 }
375 }
376
377
378 @Override
379 public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
380 simpleTag = false;
381 if (body && (t.breaksFlow() || t.isBlock())) {
382 newline(true);
383 }
384 if (HTML.Tag.OL.equals(t)) {
385 numbering.push(new Counter());
386 } else if (HTML.Tag.UL.equals(t)) {
387 numbering.push(null);
388 } else if (HTML.Tag.LI.equals(t)) {
389 Counter counter = numbering.peek();
390 if (counter == null) {
391 text("-\t");
392 } else {
393 text(++counter.value + ".\t");
394 }
395 depth++;
396 } else if (HTML.Tag.DD.equals(t)) {
397 depth++;
398 } else if (t.isPreformatted()) {
399 preformatted++;
400 } else if (HTML.Tag.BODY.equals(t)) {
401 body = true;
402 }
403 }
404
405
406 @Override
407 public void handleEndTag(HTML.Tag t, int pos) {
408 if (HTML.Tag.OL.equals(t) || HTML.Tag.UL.equals(t)) {
409 numbering.pop();
410 } else if (HTML.Tag.LI.equals(t) || HTML.Tag.DD.equals(t)) {
411 depth--;
412 } else if (t.isPreformatted()) {
413 preformatted--;
414 } else if (HTML.Tag.BODY.equals(t)) {
415 body = false;
416 }
417 if (body && (t.breaksFlow() || t.isBlock()) && !HTML.Tag.LI.equals(t)) {
418 if ((HTML.Tag.P.equals(t)
419 || HTML.Tag.PRE.equals(t)
420 || HTML.Tag.OL.equals(t)
421 || HTML.Tag.UL.equals(t)
422 || HTML.Tag.DL.equals(t))
423 && numbering.isEmpty()) {
424 pendingNewline = false;
425 newline(pendingNewline);
426 } else {
427 newline(true);
428 }
429 }
430 }
431
432
433 @Override
434 public void handleText(char[] data, int pos) {
435
436
437
438
439 int offset = 0;
440 if (simpleTag && data[0] == '>') {
441 simpleTag = false;
442 for (++offset; offset < data.length && data[offset] <= ' '; ) {
443 offset++;
444 }
445 }
446 if (offset < data.length) {
447 String text = new String(data, offset, data.length - offset);
448 text(text);
449 }
450 }
451
452
453 @Override
454 public void flush() {
455 flushPendingNewline();
456 }
457
458
459
460
461
462
463
464
465 private void newline(boolean implicit) {
466 if (implicit) {
467 pendingNewline = true;
468 } else {
469 flushPendingNewline();
470 sb.append('\n');
471 }
472 }
473
474
475
476
477 private void flushPendingNewline() {
478 if (pendingNewline) {
479 pendingNewline = false;
480 if (sb.length() > 0) {
481 sb.append('\n');
482 }
483 }
484 }
485
486
487
488
489
490
491
492 private void text(String data) {
493 flushPendingNewline();
494 if (sb.length() <= 0 || sb.charAt(sb.length() - 1) == '\n') {
495 for (int i = 0; i < depth; i++) {
496 sb.append('\t');
497 }
498 }
499 String text;
500 if (preformatted > 0) {
501 text = data;
502 } else {
503 text = data.replace('\n', ' ');
504 }
505 sb.append(text);
506 }
507 }
508
509
510
511
512
513
514
515 public static String discoverPackageName(PluginDescriptor pluginDescriptor) {
516 Map<String, Integer> packageNames = new HashMap<>();
517
518 List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
519 if (mojoDescriptors == null) {
520 return "";
521 }
522 for (MojoDescriptor descriptor : mojoDescriptors) {
523
524 String impl = descriptor.getImplementation();
525 if (StringUtils.equals(descriptor.getGoal(), "help") && StringUtils.equals("HelpMojo", impl)) {
526 continue;
527 }
528 if (impl.lastIndexOf('.') != -1) {
529 String name = impl.substring(0, impl.lastIndexOf('.'));
530 if (packageNames.get(name) != null) {
531 int next = (packageNames.get(name)).intValue() + 1;
532 packageNames.put(name, Integer.valueOf(next));
533 } else {
534 packageNames.put(name, Integer.valueOf(1));
535 }
536 } else {
537 packageNames.put("", Integer.valueOf(1));
538 }
539 }
540
541 String packageName = "";
542 int max = 0;
543 for (Map.Entry<String, Integer> entry : packageNames.entrySet()) {
544 int value = entry.getValue().intValue();
545 if (value > max) {
546 max = value;
547 packageName = entry.getKey();
548 }
549 }
550
551 return packageName;
552 }
553
554
555
556
557
558
559
560
561 @SuppressWarnings("unchecked")
562 public static boolean isMavenReport(String impl, MavenProject project) throws IllegalArgumentException {
563 if (impl == null) {
564 throw new IllegalArgumentException("mojo implementation should be declared");
565 }
566
567 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
568 if (project != null) {
569 List<String> classPathStrings;
570 try {
571 classPathStrings = project.getCompileClasspathElements();
572 if (project.getExecutionProject() != null) {
573 classPathStrings.addAll(project.getExecutionProject().getCompileClasspathElements());
574 }
575 } catch (DependencyResolutionRequiredException e) {
576 throw new IllegalArgumentException(e);
577 }
578
579 List<URL> urls = new ArrayList<>(classPathStrings.size());
580 for (String classPathString : classPathStrings) {
581 try {
582 urls.add(new File(classPathString).toURL());
583 } catch (MalformedURLException e) {
584 throw new IllegalArgumentException(e);
585 }
586 }
587
588 classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader);
589 }
590
591 try {
592 Class<?> clazz = Class.forName(impl, false, classLoader);
593
594 return MavenReport.class.isAssignableFrom(clazz);
595 } catch (ClassNotFoundException e) {
596 return false;
597 }
598 }
599 }