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