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