1 package org.apache.maven.plugins.javadoc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.lang3.BooleanUtils;
23 import org.apache.commons.lang3.ClassUtils;
24 import org.apache.commons.lang3.StringUtils;
25 import org.apache.maven.archiver.MavenArchiver;
26 import org.apache.maven.artifact.Artifact;
27 import org.apache.maven.artifact.ArtifactUtils;
28 import org.apache.maven.artifact.handler.ArtifactHandler;
29 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
30 import org.apache.maven.artifact.repository.ArtifactRepository;
31 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
32 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
33 import org.apache.maven.artifact.versioning.ArtifactVersion;
34 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.model.Dependency;
37 import org.apache.maven.model.Plugin;
38 import org.apache.maven.model.Resource;
39 import org.apache.maven.plugin.AbstractMojo;
40 import org.apache.maven.plugin.MojoExecution;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugin.MojoFailureException;
43 import org.apache.maven.plugins.annotations.Component;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.plugins.javadoc.options.BootclasspathArtifact;
46 import org.apache.maven.plugins.javadoc.options.DocletArtifact;
47 import org.apache.maven.plugins.javadoc.options.Group;
48 import org.apache.maven.plugins.javadoc.options.JavadocOptions;
49 import org.apache.maven.plugins.javadoc.options.JavadocPathArtifact;
50 import org.apache.maven.plugins.javadoc.options.OfflineLink;
51 import org.apache.maven.plugins.javadoc.options.ResourcesArtifact;
52 import org.apache.maven.plugins.javadoc.options.Tag;
53 import org.apache.maven.plugins.javadoc.options.Taglet;
54 import org.apache.maven.plugins.javadoc.options.TagletArtifact;
55 import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Writer;
56 import org.apache.maven.plugins.javadoc.resolver.JavadocBundle;
57 import org.apache.maven.plugins.javadoc.resolver.ResourceResolver;
58 import org.apache.maven.plugins.javadoc.resolver.SourceResolverConfig;
59 import org.apache.maven.project.DefaultProjectBuildingRequest;
60 import org.apache.maven.project.MavenProject;
61 import org.apache.maven.project.ProjectBuilder;
62 import org.apache.maven.project.ProjectBuildingException;
63 import org.apache.maven.project.ProjectBuildingRequest;
64 import org.apache.maven.reporting.MavenReportException;
65 import org.apache.maven.settings.Proxy;
66 import org.apache.maven.settings.Settings;
67 import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
68 import org.apache.maven.shared.artifact.filter.resolve.AndFilter;
69 import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter;
70 import org.apache.maven.shared.artifact.filter.resolve.PatternInclusionsFilter;
71 import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
72 import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
73 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
74 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
75 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
76 import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
77 import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
78 import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
79 import org.apache.maven.shared.invoker.MavenInvocationException;
80 import org.apache.maven.toolchain.Toolchain;
81 import org.apache.maven.toolchain.ToolchainManager;
82 import org.apache.maven.wagon.PathUtils;
83 import org.codehaus.plexus.archiver.ArchiverException;
84 import org.codehaus.plexus.archiver.UnArchiver;
85 import org.codehaus.plexus.archiver.manager.ArchiverManager;
86 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
87 import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
88 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
89 import org.codehaus.plexus.languages.java.jpms.LocationManager;
90 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
91 import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
92 import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
93 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
94 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
95 import org.codehaus.plexus.languages.java.version.JavaVersion;
96 import org.codehaus.plexus.util.DirectoryScanner;
97 import org.codehaus.plexus.util.FileUtils;
98 import org.codehaus.plexus.util.IOUtil;
99 import org.codehaus.plexus.util.ReaderFactory;
100 import org.codehaus.plexus.util.WriterFactory;
101 import org.codehaus.plexus.util.cli.CommandLineException;
102 import org.codehaus.plexus.util.cli.CommandLineUtils;
103 import org.codehaus.plexus.util.cli.Commandline;
104 import org.codehaus.plexus.util.xml.Xpp3Dom;
105
106 import java.io.File;
107 import java.io.FileNotFoundException;
108 import java.io.IOException;
109 import java.io.InputStream;
110 import java.io.Writer;
111 import java.lang.reflect.Method;
112 import java.net.MalformedURLException;
113 import java.net.URI;
114 import java.net.URISyntaxException;
115 import java.net.URL;
116 import java.net.URLClassLoader;
117 import java.nio.charset.Charset;
118 import java.nio.charset.StandardCharsets;
119 import java.nio.file.Files;
120 import java.nio.file.Path;
121 import java.nio.file.StandardCopyOption;
122 import java.util.ArrayList;
123 import java.util.Arrays;
124 import java.util.Calendar;
125 import java.util.Collection;
126 import java.util.Collections;
127 import java.util.Date;
128 import java.util.HashMap;
129 import java.util.HashSet;
130 import java.util.LinkedHashMap;
131 import java.util.LinkedHashSet;
132 import java.util.LinkedList;
133 import java.util.List;
134 import java.util.Locale;
135 import java.util.Map;
136 import java.util.Map.Entry;
137 import java.util.Optional;
138 import java.util.Properties;
139 import java.util.Set;
140 import java.util.StringTokenizer;
141 import java.util.stream.Collectors;
142
143 import static org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast;
144 import static org.apache.maven.plugins.javadoc.JavadocUtil.toRelative;
145 import static org.apache.maven.plugins.javadoc.JavadocUtil.toList;
146 import static org.apache.maven.plugins.javadoc.JavadocUtil.isEmpty;
147 import static org.apache.maven.plugins.javadoc.JavadocUtil.isNotEmpty;
148
149
150
151
152
153
154
155
156
157
158 public abstract class AbstractJavadocMojo
159 extends AbstractMojo
160 {
161
162
163
164
165
166
167
168 public static final String JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "javadoc-resources";
169
170
171
172
173
174
175
176
177 public static final String TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "test-javadoc-resources";
178
179
180
181
182 protected static final String DEBUG_JAVADOC_SCRIPT_NAME = "javadoc." + ( SystemUtils.IS_OS_WINDOWS ? "bat" : "sh" );
183
184
185
186
187
188 protected static final String OPTIONS_FILE_NAME = "options";
189
190
191
192
193
194 protected static final String PACKAGES_FILE_NAME = "packages";
195
196
197
198
199
200 protected static final String ARGFILE_FILE_NAME = "argfile";
201
202
203
204
205
206 protected static final String FILES_FILE_NAME = "files";
207
208
209
210
211 private static final String RESOURCE_DIR = ClassUtils.getPackageName( JavadocReport.class ).replace( '.', '/' );
212
213
214
215
216 private static final String DEFAULT_CSS_NAME = "stylesheet.css";
217
218
219
220
221 private static final String RESOURCE_CSS_DIR = RESOURCE_DIR + "/css";
222
223 private static final String PACKAGE_LIST = "package-list";
224 private static final String ELEMENT_LIST = "element-list";
225
226
227
228
229
230
231
232
233
234 private static final JavaVersion SINCE_JAVADOC_1_4 = JavaVersion.parse( "1.4" );
235
236
237
238
239
240
241
242
243
244 private static final JavaVersion SINCE_JAVADOC_1_4_2 = JavaVersion.parse( "1.4.2" );
245
246
247
248
249
250
251
252
253
254 private static final JavaVersion SINCE_JAVADOC_1_5 = JavaVersion.parse( "1.5" );
255
256
257
258
259
260
261
262
263 private static final JavaVersion SINCE_JAVADOC_1_6 = JavaVersion.parse( "1.6" );
264
265
266
267
268
269
270
271
272 private static final JavaVersion SINCE_JAVADOC_1_8 = JavaVersion.parse( "1.8" );
273
274
275
276
277 private static final JavaVersion JAVA_VERSION = JavaVersion.JAVA_SPECIFICATION_VERSION;
278
279
280
281
282
283
284
285
286
287
288 @Component
289 private ArchiverManager archiverManager;
290
291 @Component
292 private ResourceResolver resourceResolver;
293
294 @Component
295 private ArtifactResolver artifactResolver;
296
297 @Component
298 private ArtifactHandlerManager artifactHandlerManager;
299
300 @Component
301 private DependencyResolver dependencyResolver;
302
303
304
305
306
307
308 @Component
309 private ProjectBuilder mavenProjectBuilder;
310
311
312 @Component
313 private ToolchainManager toolchainManager;
314
315 final LocationManager locationManager = new LocationManager();
316
317
318
319
320
321
322
323
324
325 @Parameter( defaultValue = "${session}", readonly = true, required = true )
326 protected MavenSession session;
327
328
329
330
331
332
333 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
334 private Settings settings;
335
336
337
338
339 @Parameter( defaultValue = "${project}", readonly = true, required = true )
340 protected MavenProject project;
341
342 @Parameter( defaultValue = "${mojoExecution}", readonly = true )
343 private MojoExecution mojo;
344
345
346
347
348 @Parameter( defaultValue = "${settings.offline}", required = true, readonly = true )
349 private boolean isOffline;
350
351
352
353
354
355
356
357
358
359
360
361 @Parameter( defaultValue = "${basedir}/src/main/javadoc" )
362 private File javadocDirectory;
363
364
365
366
367
368
369
370 @Parameter
371 private String[] additionalOptions;
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 @Parameter( property = "additionalJOption" )
389 private String additionalJOption;
390
391
392
393
394
395
396
397
398
399
400
401 @Parameter
402 private String[] additionalJOptions;
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424 @Parameter( property = "resourcesArtifacts" )
425 private ResourcesArtifact[] resourcesArtifacts;
426
427
428
429
430 @Parameter( property = "localRepository" )
431 private ArtifactRepository localRepository;
432
433
434
435
436 @Parameter( property = "reactorProjects", readonly = true )
437 private List<MavenProject> reactorProjects;
438
439
440
441
442
443
444
445
446 @Parameter( property = "debug", defaultValue = "false" )
447 private boolean debug;
448
449
450
451
452
453
454
455 @Parameter( property = "javadocExecutable" )
456 private String javadocExecutable;
457
458
459
460
461
462
463 @Parameter( property = "javadocVersion" )
464 private String javadocVersion;
465
466
467
468
469 private JavaVersion javadocRuntimeVersion;
470
471
472
473
474
475
476 @Parameter( property = "maven.javadoc.skip", defaultValue = "false" )
477 protected boolean skip;
478
479
480
481
482
483
484 @Parameter( property = "maven.javadoc.failOnError", defaultValue = "true" )
485 protected boolean failOnError;
486
487
488
489
490
491
492
493 @Parameter( property = "maven.javadoc.failOnWarnings", defaultValue = "false" )
494 protected boolean failOnWarnings;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 @Parameter( property = "useStandardDocletOptions", defaultValue = "true" )
516 protected boolean useStandardDocletOptions;
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 @Parameter( property = "detectLinks", defaultValue = "false" )
537 private boolean detectLinks;
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555 @Parameter( property = "detectOfflineLinks", defaultValue = "true" )
556 private boolean detectOfflineLinks;
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576 @Parameter( property = "detectJavaApiLink", defaultValue = "true" )
577 private boolean detectJavaApiLink;
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595 @Parameter( property = "javaApiLinks" )
596 private Properties javaApiLinks;
597
598
599
600
601
602
603
604 @Parameter( property = "validateLinks", defaultValue = "false" )
605 private boolean validateLinks;
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620 @Parameter( property = "bootclasspath" )
621 private String bootclasspath;
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645 @Parameter( property = "bootclasspathArtifacts" )
646 private BootclasspathArtifact[] bootclasspathArtifacts;
647
648
649
650
651
652
653
654
655
656
657
658 @Parameter( property = "breakiterator", defaultValue = "false" )
659 private boolean breakiterator;
660
661
662
663
664
665
666 @Parameter( property = "doclet" )
667 private String doclet;
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688 @Parameter( property = "docletArtifact" )
689 private DocletArtifact docletArtifact;
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714 @Parameter( property = "docletArtifacts" )
715 private DocletArtifact[] docletArtifacts;
716
717
718
719
720
721
722
723
724
725 @Parameter( property = "docletPath" )
726 private String docletPath;
727
728
729
730
731
732
733
734
735
736
737
738 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
739 private String encoding;
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764 @Parameter( property = "excludePackageNames" )
765 private String excludePackageNames;
766
767
768
769
770
771
772
773 @Parameter( property = "extdirs" )
774 private String extdirs;
775
776
777
778
779
780
781 @Parameter( property = "locale" )
782 private String locale;
783
784
785
786
787
788
789
790
791 @Parameter( property = "maxmemory" )
792 private String maxmemory;
793
794
795
796
797
798
799
800
801 @Parameter( property = "minmemory" )
802 private String minmemory;
803
804
805
806
807
808
809
810
811 @Parameter( property = "old", defaultValue = "false" )
812 private boolean old;
813
814
815
816
817
818
819
820
821
822
823 @Parameter( property = "overview", defaultValue = "${basedir}/src/main/javadoc/overview.html" )
824 private File overview;
825
826
827
828
829
830
831
832
833
834
835
836
837
838 @Parameter( property = "quiet", defaultValue = "false" )
839 private boolean quiet;
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856 @Parameter( property = "show", defaultValue = "protected" )
857 private String show;
858
859
860
861
862
863
864
865
866
867
868 @Parameter( property = "source", defaultValue = "${maven.compiler.source}" )
869 private String source;
870
871
872
873
874
875
876
877 @Parameter( defaultValue = "${maven.compiler.release}" )
878 private String release;
879
880
881
882
883
884
885
886
887 @Parameter( property = "sourcepath" )
888 private String sourcepath;
889
890
891
892
893
894
895
896
897
898
899
900 @Parameter( property = "subpackages" )
901 private String subpackages;
902
903
904
905
906
907
908
909 @Parameter( property = "verbose", defaultValue = "false" )
910 private boolean verbose;
911
912
913
914
915
916
917
918
919
920
921
922 @Parameter( property = "author", defaultValue = "true" )
923 private boolean author;
924
925
926
927
928
929
930
931
932
933
934
935
936 @Parameter( property = "bottom",
937 defaultValue = "Copyright © {inceptionYear}–{currentYear} {organizationName}. "
938 + "All rights reserved." )
939 private String bottom;
940
941
942
943
944
945
946
947
948 @Parameter( property = "charset" )
949 private String charset;
950
951
952
953
954
955
956
957
958 @Parameter( property = "docencoding", defaultValue = "${project.reporting.outputEncoding}" )
959 private String docencoding;
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978 @Parameter( property = "docfilessubdirs", defaultValue = "false" )
979 private boolean docfilessubdirs;
980
981
982
983
984
985
986
987
988 @Parameter( property = "doclint" )
989 private String doclint;
990
991
992
993
994
995
996
997 @Parameter( property = "doctitle", defaultValue = "${project.name} ${project.version} API" )
998 private String doctitle;
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012 @Parameter( property = "excludedocfilessubdir" )
1013 private String excludedocfilessubdir;
1014
1015
1016
1017
1018
1019
1020 @Parameter( property = "footer" )
1021 private String footer;
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 @Parameter
1056 private Group[] groups;
1057
1058
1059
1060
1061
1062
1063 @Parameter( property = "header" )
1064 private String header;
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108 @Parameter( property = "helpfile" )
1109 private String helpfile;
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127 @Parameter( property = "keywords", defaultValue = "false" )
1128 private boolean keywords;
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 @Parameter( property = "links" )
1155 protected ArrayList<String> links;
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 @Parameter
1176 private List<DependencyLink> dependencyLinks = new ArrayList<>();
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 @Parameter( property = "linksource", defaultValue = "false" )
1190 private boolean linksource;
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 @Parameter( property = "nocomment", defaultValue = "false" )
1202 private boolean nocomment;
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212 @Parameter( property = "nodeprecated", defaultValue = "false" )
1213 private boolean nodeprecated;
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223 @Parameter( property = "nodeprecatedlist", defaultValue = "false" )
1224 private boolean nodeprecatedlist;
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234 @Parameter( property = "nohelp", defaultValue = "false" )
1235 private boolean nohelp;
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245 @Parameter( property = "noindex", defaultValue = "false" )
1246 private boolean noindex;
1247
1248
1249
1250
1251
1252
1253
1254 @Parameter( property = "nonavbar", defaultValue = "false" )
1255 private boolean nonavbar;
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267 @Parameter( property = "nooverview", defaultValue = "false" )
1268 private boolean nooverview;
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284 @Parameter( property = "noqualifier" )
1285 private String noqualifier;
1286
1287
1288
1289
1290
1291
1292
1293 @Parameter( property = "nosince", defaultValue = "false" )
1294 private boolean nosince;
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309 @Parameter( property = "notimestamp", defaultValue = "false" )
1310 private boolean notimestamp;
1311
1312
1313
1314
1315
1316
1317 @Parameter( property = "notree", defaultValue = "false" )
1318 private boolean notree;
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342 @Parameter( property = "offlineLinks" )
1343 private OfflineLink[] offlineLinks;
1344
1345
1346
1347
1348
1349
1350 @Parameter( property = "destDir", alias = "destDir", defaultValue = "${project.build.directory}/apidocs",
1351 required = true )
1352 protected File outputDirectory;
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363 @Parameter( property = "packagesheader" )
1364 private String packagesheader;
1365
1366
1367
1368
1369
1370
1371 @Parameter( property = "serialwarn", defaultValue = "false" )
1372 private boolean serialwarn;
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389 @Parameter( property = "sourcetab", alias = "linksourcetab" )
1390 private int sourcetab;
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402 @Parameter( property = "splitindex", defaultValue = "false" )
1403 private boolean splitindex;
1404
1405
1406
1407
1408
1409
1410
1411
1412 @Parameter( property = "stylesheet", defaultValue = "java" )
1413 private String stylesheet;
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455 @Parameter( property = "stylesheetfile" )
1456 private String stylesheetfile;
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468 @Parameter
1469 private String[] addStylesheets;
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479 @Parameter( property = "taglet" )
1480 private String taglet;
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511 @Parameter( property = "tagletArtifact" )
1512 private TagletArtifact tagletArtifact;
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540 @Parameter( property = "tagletArtifacts" )
1541 private TagletArtifact[] tagletArtifacts;
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 @Parameter( property = "tagletpath" )
1554 private String tagletpath;
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584 @Parameter( property = "taglets" )
1585 private Taglet[] taglets;
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619 @Parameter( property = "tags" )
1620 private Tag[] tags;
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631 @Parameter( property = "top" )
1632 private String top;
1633
1634
1635
1636
1637
1638
1639
1640 @Parameter( property = "use", defaultValue = "true" )
1641 private boolean use;
1642
1643
1644
1645
1646
1647
1648
1649 @Parameter( property = "version", defaultValue = "true" )
1650 private boolean version;
1651
1652
1653
1654
1655
1656
1657
1658
1659 @Parameter( property = "windowtitle", defaultValue = "${project.name} ${project.version} API" )
1660 private String windowtitle;
1661
1662
1663
1664
1665
1666
1667
1668 @Parameter( defaultValue = "false" )
1669 private boolean includeDependencySources;
1670
1671
1672
1673
1674
1675
1676
1677 @Parameter( defaultValue = "${project.build.directory}/distro-javadoc-sources" )
1678 private File sourceDependencyCacheDir;
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 @Parameter( defaultValue = "false" )
1690 @Deprecated
1691 private boolean includeTransitiveDependencySources;
1692
1693
1694
1695
1696
1697
1698
1699 @Parameter
1700 private List<String> dependencySourceIncludes;
1701
1702
1703
1704
1705
1706
1707
1708 @Parameter
1709 private List<String> dependencySourceExcludes;
1710
1711
1712
1713
1714
1715
1716
1717 @Parameter( defaultValue = "${project.build.directory}/javadoc-bundle-options", readonly = true )
1718 private File javadocOptionsDir;
1719
1720
1721
1722
1723
1724
1725
1726 private transient List<JavadocBundle> dependencyJavadocBundles;
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743 @Parameter
1744 private List<AdditionalDependency> additionalDependencies;
1745
1746
1747
1748
1749
1750
1751
1752 @Parameter
1753 private List<String> sourceFileIncludes;
1754
1755
1756
1757
1758
1759
1760
1761 @Parameter
1762 private List<String> sourceFileExcludes;
1763
1764
1765
1766
1767
1768 @Parameter( defaultValue = "true", property = "maven.javadoc.applyJavadocSecurityFix" )
1769 private boolean applyJavadocSecurityFix = true;
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804 @Parameter
1805 private Map<String, String> jdkToolchain;
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815 @Parameter( property = "staleDataPath",
1816 defaultValue = "${project.build.directory}/maven-javadoc-plugin-stale-data.txt" )
1817 private File staleDataPath;
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827 @Parameter( property = "maven.javadoc.skippedModules" )
1828 private String skippedModules;
1829
1830
1831
1832
1833
1834
1835
1836
1837 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
1838 protected String outputTimestamp;
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851 protected boolean isAggregator()
1852 {
1853 return false;
1854 }
1855
1856
1857
1858
1859
1860
1861 protected boolean isTest()
1862 {
1863 return false;
1864 }
1865
1866
1867
1868
1869 protected String getOutputDirectory()
1870 {
1871 return outputDirectory.getAbsoluteFile().toString();
1872 }
1873
1874 protected MavenProject getProject()
1875 {
1876 return project;
1877 }
1878
1879
1880
1881
1882
1883
1884 protected List<File> getProjectBuildOutputDirs( MavenProject p )
1885 {
1886 if ( StringUtils.isEmpty( p.getBuild().getOutputDirectory() ) )
1887 {
1888 return Collections.emptyList();
1889 }
1890
1891 return Collections.singletonList( new File( p.getBuild().getOutputDirectory() ) );
1892 }
1893
1894
1895
1896
1897
1898
1899
1900 protected File getClassesFile( MavenProject project )
1901 {
1902 if ( !isAggregator() && isTest() )
1903 {
1904 return null;
1905 }
1906
1907 if ( project.getArtifact() != null && project.getArtifact().getFile() != null )
1908 {
1909 File artifactFile = project.getArtifact().getFile();
1910 if ( artifactFile.isDirectory() || artifactFile.getName().endsWith( ".jar" ) )
1911 {
1912 return artifactFile;
1913 }
1914 }
1915 else if ( project.getExecutionProject() != null
1916 && project.getExecutionProject().getArtifact() != null
1917 && project.getExecutionProject().getArtifact().getFile() != null )
1918 {
1919 File artifactFile = project.getExecutionProject().getArtifact().getFile();
1920 if ( artifactFile.isDirectory() || artifactFile.getName().endsWith( ".jar" ) )
1921 {
1922 return artifactFile;
1923 }
1924 }
1925
1926 if ( project.getBuild().getOutputDirectory() != null )
1927 {
1928 return new File( project.getBuild().getOutputDirectory() );
1929 }
1930 else
1931 {
1932 return null;
1933 }
1934 }
1935
1936
1937
1938
1939
1940 protected List<String> getProjectSourceRoots( MavenProject p )
1941 {
1942 if ( "pom".equals( p.getPackaging().toLowerCase() ) )
1943 {
1944 return Collections.emptyList();
1945 }
1946
1947 return ( p.getCompileSourceRoots() == null
1948 ? Collections.<String>emptyList()
1949 : new LinkedList<>( p.getCompileSourceRoots() ) );
1950 }
1951
1952
1953
1954
1955
1956 protected List<String> getExecutionProjectSourceRoots( MavenProject p )
1957 {
1958 if ( "pom".equals( p.getExecutionProject().getPackaging().toLowerCase() ) )
1959 {
1960 return Collections.emptyList();
1961 }
1962
1963 return ( p.getExecutionProject().getCompileSourceRoots() == null
1964 ? Collections.<String>emptyList()
1965 : new LinkedList<>( p.getExecutionProject().getCompileSourceRoots() ) );
1966 }
1967
1968
1969
1970
1971 protected File getJavadocDirectory()
1972 {
1973 return javadocDirectory;
1974 }
1975
1976
1977
1978
1979 protected String getDoclint()
1980 {
1981 return doclint;
1982 }
1983
1984
1985
1986
1987 protected String getDoctitle()
1988 {
1989 return doctitle;
1990 }
1991
1992
1993
1994
1995 protected File getOverview()
1996 {
1997 return overview;
1998 }
1999
2000
2001
2002
2003 protected String getWindowtitle()
2004 {
2005 return windowtitle;
2006 }
2007
2008
2009
2010
2011 private String getCharset()
2012 {
2013 return ( StringUtils.isEmpty( charset ) ) ? getDocencoding() : charset;
2014 }
2015
2016
2017
2018
2019 private String getDocencoding()
2020 {
2021 return ( StringUtils.isEmpty( docencoding ) ) ? ReaderFactory.UTF_8 : docencoding;
2022 }
2023
2024
2025
2026
2027 private String getEncoding()
2028 {
2029 return ( StringUtils.isEmpty( encoding ) ) ? ReaderFactory.FILE_ENCODING : encoding;
2030 }
2031
2032 @Override
2033 public void execute()
2034 throws MojoExecutionException, MojoFailureException
2035 {
2036 verifyRemovedParameter( "aggregator" );
2037 verifyRemovedParameter( "proxyHost" );
2038 verifyRemovedParameter( "proxyPort" );
2039 verifyReplacedParameter( "additionalparam", "additionalOptions" );
2040
2041 doExecute();
2042 }
2043
2044 abstract void doExecute() throws MojoExecutionException, MojoFailureException;
2045
2046 protected final void verifyRemovedParameter( String paramName )
2047 {
2048 Xpp3Dom configDom = mojo.getConfiguration();
2049 if ( configDom != null )
2050 {
2051 if ( configDom.getChild( paramName ) != null )
2052 {
2053 throw new IllegalArgumentException( "parameter '" + paramName
2054 + "' has been removed from the plugin, please verify documentation." );
2055 }
2056 }
2057 }
2058
2059 private void verifyReplacedParameter( String oldParamName, String newParamNew )
2060 {
2061 Xpp3Dom configDom = mojo.getConfiguration();
2062 if ( configDom != null )
2063 {
2064 if ( configDom.getChild( oldParamName ) != null )
2065 {
2066 throw new IllegalArgumentException( "parameter '" + oldParamName
2067 + "' has been replaced with " + newParamNew + ", please verify documentation." );
2068 }
2069 }
2070 }
2071
2072
2073
2074
2075
2076
2077
2078
2079 protected void executeReport( Locale unusedLocale )
2080 throws MavenReportException
2081 {
2082 if ( skip )
2083 {
2084 getLog().info( "Skipping javadoc generation" );
2085 return;
2086 }
2087
2088 if ( getLog().isDebugEnabled() )
2089 {
2090 this.debug = true;
2091 }
2092
2093
2094
2095 try
2096 {
2097 buildJavadocOptions();
2098 }
2099 catch ( IOException e )
2100 {
2101 throw new MavenReportException( "Failed to generate javadoc options file: " + e.getMessage(), e );
2102 }
2103
2104 Collection<JavadocModule> sourcePaths = getSourcePaths();
2105
2106 Collection<Path> collectedSourcePaths = sourcePaths.stream()
2107 .flatMap( e -> e.getSourcePaths().stream() )
2108 .collect( Collectors.toList() );
2109
2110 Map<Path, Collection<String>> files = getFiles( collectedSourcePaths );
2111 if ( !canGenerateReport( files ) )
2112 {
2113 return;
2114 }
2115
2116
2117
2118
2119
2120 String jExecutable;
2121 try
2122 {
2123 jExecutable = getJavadocExecutable();
2124 }
2125 catch ( IOException e )
2126 {
2127 throw new MavenReportException( "Unable to find javadoc command: " + e.getMessage(), e );
2128 }
2129 setFJavadocVersion( new File( jExecutable ) );
2130
2131 Collection<String> packageNames;
2132 if ( javadocRuntimeVersion.isAtLeast( "9" ) )
2133 {
2134 packageNames = getPackageNamesRespectingJavaModules( sourcePaths );
2135 }
2136 else
2137 {
2138 packageNames = getPackageNames( files );
2139 }
2140
2141
2142
2143
2144
2145 File javadocOutputDirectory = new File( getOutputDirectory() );
2146 if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.isDirectory() )
2147 {
2148 throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not a directory." );
2149 }
2150 if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.canWrite() )
2151 {
2152 throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not writable." );
2153 }
2154 javadocOutputDirectory.mkdirs();
2155
2156
2157
2158
2159
2160 copyAllResources( javadocOutputDirectory );
2161
2162
2163
2164
2165
2166 Commandline cmd = new Commandline();
2167 cmd.getShell().setQuotedArgumentsEnabled( false );
2168 cmd.setWorkingDirectory( javadocOutputDirectory.getAbsolutePath() );
2169 cmd.setExecutable( jExecutable );
2170
2171
2172
2173
2174
2175 addMemoryArg( cmd, "-Xmx", this.maxmemory );
2176 addMemoryArg( cmd, "-Xms", this.minmemory );
2177 addProxyArg( cmd );
2178
2179 if ( StringUtils.isNotEmpty( additionalJOption ) )
2180 {
2181 cmd.createArg().setValue( additionalJOption );
2182 }
2183
2184 if ( additionalJOptions != null && additionalJOptions.length != 0 )
2185 {
2186 for ( String jo : additionalJOptions )
2187 {
2188 cmd.createArg().setValue( jo );
2189 }
2190 }
2191
2192
2193
2194
2195 List<String> standardDocletArguments = new ArrayList<>();
2196
2197 Set<OfflineLink> offlineLinks;
2198 if ( StringUtils.isEmpty( doclet ) || useStandardDocletOptions )
2199 {
2200 offlineLinks = getLinkofflines();
2201 addStandardDocletOptions( javadocOutputDirectory, standardDocletArguments, offlineLinks );
2202 }
2203 else
2204 {
2205 offlineLinks = Collections.emptySet();
2206 }
2207
2208
2209
2210
2211 List<String> javadocArguments = new ArrayList<>();
2212
2213 addJavadocOptions( javadocOutputDirectory, javadocArguments, sourcePaths, offlineLinks );
2214
2215
2216
2217
2218
2219 List<String> arguments = new ArrayList<>( javadocArguments.size() + standardDocletArguments.size() );
2220 arguments.addAll( javadocArguments );
2221 arguments.addAll( standardDocletArguments );
2222
2223 if ( arguments.size() > 0 )
2224 {
2225 addCommandLineOptions( cmd, arguments, javadocOutputDirectory );
2226 }
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236 boolean includesExcludesActive =
2237 ( sourceFileIncludes != null && !sourceFileIncludes.isEmpty() )
2238 || ( sourceFileExcludes != null && !sourceFileExcludes.isEmpty() );
2239 if ( includesExcludesActive && !StringUtils.isEmpty( subpackages ) )
2240 {
2241 getLog().warn( "sourceFileIncludes and sourceFileExcludes have no effect when subpackages are specified!" );
2242 includesExcludesActive = false;
2243 }
2244 if ( !packageNames.isEmpty() && !includesExcludesActive )
2245 {
2246 addCommandLinePackages( cmd, javadocOutputDirectory, packageNames );
2247
2248
2249
2250
2251
2252 List<String> specialFiles = getSpecialFiles( files );
2253
2254 if ( !specialFiles.isEmpty() )
2255 {
2256 addCommandLineArgFile( cmd, javadocOutputDirectory, specialFiles );
2257 }
2258 }
2259 else
2260 {
2261
2262
2263
2264
2265 List<String> allFiles = new ArrayList<>();
2266 for ( Map.Entry<Path, Collection<String>> filesEntry : files.entrySet() )
2267 {
2268 for ( String file : filesEntry.getValue() )
2269 {
2270 allFiles.add( filesEntry.getKey().resolve( file ).toString() );
2271 }
2272 }
2273
2274 if ( !files.isEmpty() )
2275 {
2276 addCommandLineArgFile( cmd, javadocOutputDirectory, allFiles );
2277 }
2278 }
2279
2280
2281
2282
2283
2284 executeJavadocCommandLine( cmd, javadocOutputDirectory );
2285
2286
2287
2288
2289 if ( !debug )
2290 {
2291 for ( int i = 0; i < cmd.getArguments().length; i++ )
2292 {
2293 String arg = cmd.getArguments()[i].trim();
2294
2295 if ( !arg.startsWith( "@" ) )
2296 {
2297 continue;
2298 }
2299
2300 File argFile = new File( javadocOutputDirectory, arg.substring( 1 ) );
2301 if ( argFile.exists() )
2302 {
2303 argFile.delete();
2304 }
2305 }
2306
2307 File scriptFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
2308 if ( scriptFile.exists() )
2309 {
2310 scriptFile.delete();
2311 }
2312 }
2313 if ( applyJavadocSecurityFix )
2314 {
2315
2316 try
2317 {
2318 final int patched = fixFrameInjectionBug( javadocOutputDirectory, getDocencoding() );
2319 if ( patched > 0 )
2320 {
2321 getLog().info(
2322 String.format( "Fixed Javadoc frame injection vulnerability (CVE-2013-1571) in %d files.",
2323 patched ) );
2324 }
2325 }
2326 catch ( IOException e )
2327 {
2328 throw new MavenReportException( "Failed to patch javadocs vulnerability: " + e.getMessage(), e );
2329 }
2330 }
2331 else
2332 {
2333 getLog().info( "applying javadoc security fix has been disabled" );
2334 }
2335 }
2336
2337
2338
2339
2340
2341
2342
2343
2344 protected Map<Path, Collection<String>> getFiles( Collection<Path> sourcePaths )
2345 throws MavenReportException
2346 {
2347 Map<Path, Collection<String>> mappedFiles = new LinkedHashMap<>( sourcePaths.size() );
2348 if ( StringUtils.isEmpty( subpackages ) )
2349 {
2350 Collection<String> excludedPackages = getExcludedPackages();
2351
2352 final boolean autoExclude;
2353 if ( release != null )
2354 {
2355 autoExclude = JavaVersion.parse( release ).isBefore( "9" );
2356 }
2357 else if ( source != null )
2358 {
2359 autoExclude = JavaVersion.parse( source ).isBefore( "9" );
2360 }
2361 else
2362 {
2363 autoExclude = false;
2364 }
2365
2366 for ( Path sourcePath : sourcePaths )
2367 {
2368 File sourceDirectory = sourcePath.toFile();
2369 List<String> files = new ArrayList<>( JavadocUtil.getFilesFromSource( sourceDirectory,
2370 sourceFileIncludes, sourceFileExcludes,
2371 excludedPackages ) );
2372
2373 if ( autoExclude && files.remove( "module-info.java" ) )
2374 {
2375 getLog().debug( "Auto exclude module-info.java due to source value" );
2376 }
2377 mappedFiles.put( sourcePath, files );
2378 }
2379 }
2380
2381 return mappedFiles;
2382 }
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392 protected Collection<JavadocModule> getSourcePaths()
2393 throws MavenReportException
2394 {
2395 Collection<JavadocModule> mappedSourcePaths = new ArrayList<>();
2396
2397 if ( StringUtils.isEmpty( sourcepath ) )
2398 {
2399 if ( !"pom".equals( project.getPackaging() ) )
2400 {
2401 Set<Path> sourcePaths =
2402 new LinkedHashSet<>( JavadocUtil.pruneDirs( project, getProjectSourceRoots( project ) ) );
2403
2404 if ( project.getExecutionProject() != null )
2405 {
2406 sourcePaths.addAll( JavadocUtil.pruneDirs( project, getExecutionProjectSourceRoots( project ) ) );
2407 }
2408
2409
2410
2411
2412
2413
2414 if ( getJavadocDirectory() != null )
2415 {
2416 File javadocDir = getJavadocDirectory();
2417 if ( javadocDir.exists() && javadocDir.isDirectory() )
2418 {
2419 Collection<Path> l =
2420 JavadocUtil.pruneDirs( project,
2421 Collections.singletonList( getJavadocDirectory().getAbsolutePath() ) );
2422 sourcePaths.addAll( l );
2423 }
2424 }
2425 if ( !sourcePaths.isEmpty() )
2426 {
2427 mappedSourcePaths.add( buildJavadocModule( project, sourcePaths ) );
2428 }
2429 }
2430
2431 if ( isAggregator() )
2432 {
2433 for ( MavenProject subProject : getAggregatedProjects() )
2434 {
2435 if ( subProject != project )
2436 {
2437 Collection<Path> additionalSourcePaths = new ArrayList<>();
2438
2439 List<String> sourceRoots = getProjectSourceRoots( subProject );
2440
2441 if ( subProject.getExecutionProject() != null )
2442 {
2443 sourceRoots.addAll( getExecutionProjectSourceRoots( subProject ) );
2444 }
2445
2446 ArtifactHandler artifactHandler = subProject.getArtifact().getArtifactHandler();
2447 if ( "java".equals( artifactHandler.getLanguage() ) )
2448 {
2449 additionalSourcePaths.addAll( JavadocUtil.pruneDirs( subProject, sourceRoots ) );
2450 }
2451
2452 if ( getJavadocDirectory() != null )
2453 {
2454 String javadocDirRelative =
2455 PathUtils.toRelative( project.getBasedir(),
2456 getJavadocDirectory().getAbsolutePath() );
2457 File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
2458 if ( javadocDir.exists() && javadocDir.isDirectory() )
2459 {
2460 Collection<Path> l = JavadocUtil.pruneDirs( subProject, Collections.singletonList(
2461 javadocDir.getAbsolutePath() ) );
2462 additionalSourcePaths.addAll( l );
2463 }
2464 }
2465
2466 if ( !additionalSourcePaths.isEmpty() )
2467 {
2468 mappedSourcePaths.add( buildJavadocModule( subProject , additionalSourcePaths ) );
2469 }
2470 }
2471 }
2472 }
2473
2474 if ( includeDependencySources )
2475 {
2476 mappedSourcePaths.addAll( getDependencySourcePaths() );
2477 }
2478 }
2479 else
2480 {
2481 Collection<Path> sourcePaths =
2482 JavadocUtil.pruneDirs( project,
2483 new ArrayList<>( Arrays.asList( JavadocUtil.splitPath( sourcepath ) ) ) );
2484 if ( getJavadocDirectory() != null )
2485 {
2486 Collection<Path> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
2487 getJavadocDirectory().getAbsolutePath() ) );
2488 sourcePaths.addAll( l );
2489 }
2490
2491 if ( !sourcePaths.isEmpty() )
2492 {
2493 mappedSourcePaths.add( new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(),
2494 project.getArtifactId() ),
2495 getClassesFile( project ),
2496 sourcePaths ) );
2497 }
2498 }
2499
2500 return mappedSourcePaths;
2501 }
2502
2503 private JavadocModule buildJavadocModule( MavenProject project, Collection<Path> sourcePaths )
2504 {
2505 File classessFile = getClassesFile( project );
2506 ResolvePathResult resolvePathResult = getResolvePathResult( classessFile );
2507 if ( resolvePathResult == null )
2508 {
2509 return new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2510 classessFile,
2511 sourcePaths );
2512 }
2513 else
2514 {
2515 return new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2516 classessFile,
2517 sourcePaths,
2518 resolvePathResult.getModuleDescriptor(),
2519 resolvePathResult.getModuleNameSource() );
2520 }
2521 }
2522
2523
2524
2525
2526
2527
2528
2529 private Set<MavenProject> modulesForAggregatedProject( MavenProject aggregatedProject,
2530 Map<Path, MavenProject> reactorProjectsMap )
2531 {
2532
2533
2534
2535
2536
2537 if ( aggregatedProject.getModules().isEmpty() )
2538 {
2539 return Collections.singleton( aggregatedProject );
2540 }
2541
2542 Path basePath = aggregatedProject.getBasedir().toPath();
2543 List<Path> modulePaths = new LinkedList<>();
2544 for ( String module : aggregatedProject.getModules() )
2545 {
2546 modulePaths.add( basePath.resolve( module ).normalize() );
2547 }
2548
2549 Set<MavenProject> aggregatedModules = new LinkedHashSet<>();
2550
2551 for ( Path modulePath : modulePaths )
2552 {
2553 MavenProject module = reactorProjectsMap.remove( modulePath );
2554 if ( module != null )
2555 {
2556 aggregatedModules.addAll( modulesForAggregatedProject( module, reactorProjectsMap ) );
2557 }
2558 }
2559
2560 return aggregatedModules;
2561 }
2562
2563
2564
2565
2566
2567
2568
2569 protected SourceResolverConfig configureDependencySourceResolution( final SourceResolverConfig config )
2570 {
2571 return config.withCompileSources();
2572 }
2573
2574
2575
2576
2577
2578
2579
2580 protected final Collection<JavadocModule> getDependencySourcePaths()
2581 throws MavenReportException
2582 {
2583 try
2584 {
2585 if ( sourceDependencyCacheDir.exists() )
2586 {
2587 FileUtils.forceDelete( sourceDependencyCacheDir );
2588 sourceDependencyCacheDir.mkdirs();
2589 }
2590 }
2591 catch ( IOException e )
2592 {
2593 throw new MavenReportException(
2594 "Failed to delete cache directory: " + sourceDependencyCacheDir + "\nReason: " + e.getMessage(), e );
2595 }
2596
2597 final SourceResolverConfig config = getDependencySourceResolverConfig();
2598
2599 try
2600 {
2601 return resourceResolver.resolveDependencySourcePaths( config );
2602 }
2603 catch ( final ArtifactResolutionException | ArtifactNotFoundException e )
2604 {
2605 throw new MavenReportException(
2606 "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e );
2607 }
2608 }
2609
2610
2611
2612
2613
2614
2615
2616 private TransformableFilter createDependencyArtifactFilter()
2617 {
2618 Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
2619
2620 List<String> artifactPatterns = new ArrayList<>( dependencyArtifacts.size() );
2621 for ( Artifact artifact : dependencyArtifacts )
2622 {
2623 artifactPatterns.add( artifact.getGroupId() + ":" + artifact.getArtifactId() );
2624 }
2625
2626 return new PatternInclusionsFilter( artifactPatterns );
2627 }
2628
2629
2630
2631
2632
2633
2634
2635 private SourceResolverConfig getDependencySourceResolverConfig()
2636 {
2637 final List<TransformableFilter> andFilters = new ArrayList<>();
2638
2639 final List<String> dependencyIncludes = dependencySourceIncludes;
2640 final List<String> dependencyExcludes = dependencySourceExcludes;
2641
2642 if ( !includeTransitiveDependencySources || isNotEmpty( dependencyIncludes ) || isNotEmpty(
2643 dependencyExcludes ) )
2644 {
2645 if ( !includeTransitiveDependencySources )
2646 {
2647 andFilters.add( createDependencyArtifactFilter() );
2648 }
2649
2650 if ( isNotEmpty( dependencyIncludes ) )
2651 {
2652 andFilters.add( new PatternInclusionsFilter( dependencyIncludes ) );
2653 }
2654
2655 if ( isNotEmpty( dependencyExcludes ) )
2656 {
2657 andFilters.add( new PatternExclusionsFilter( dependencyExcludes ) );
2658 }
2659 }
2660
2661 return configureDependencySourceResolution( new SourceResolverConfig( project,
2662 getProjectBuildingRequest( project ),
2663 sourceDependencyCacheDir )
2664 .withReactorProjects( this.reactorProjects ) )
2665 .withFilter( new AndFilter( andFilters ) );
2666
2667 }
2668
2669 private ProjectBuildingRequest getProjectBuildingRequest( MavenProject currentProject )
2670 {
2671 return new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() )
2672 .setRemoteRepositories( currentProject.getRemoteArtifactRepositories() );
2673 }
2674
2675
2676
2677
2678
2679
2680
2681
2682 protected boolean canGenerateReport( Map<Path, Collection<String>> files )
2683 {
2684 for ( Collection<String> filesValues : files.values() )
2685 {
2686 if ( !filesValues.isEmpty() )
2687 {
2688 return true;
2689 }
2690 }
2691
2692 return !StringUtils.isEmpty( subpackages );
2693 }
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707 private String getExcludedPackages( Collection<Path> sourcePaths )
2708 throws MavenReportException
2709 {
2710 List<String> excludedNames = null;
2711
2712 if ( StringUtils.isNotEmpty( sourcepath ) && StringUtils.isNotEmpty( subpackages ) )
2713 {
2714 Collection<String> excludedPackages = getExcludedPackages();
2715
2716 excludedNames = JavadocUtil.getExcludedPackages( sourcePaths, excludedPackages );
2717 }
2718
2719 String excludeArg = "";
2720 if ( StringUtils.isNotEmpty( subpackages ) && excludedNames != null )
2721 {
2722
2723 excludeArg = StringUtils.join( excludedNames.iterator(), ":" );
2724 }
2725
2726 return excludeArg;
2727 }
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737 private String getSourcePath( Collection<Path> sourcePaths )
2738 {
2739 String sourcePath = null;
2740
2741 if ( StringUtils.isEmpty( subpackages ) || StringUtils.isNotEmpty( sourcepath ) )
2742 {
2743 sourcePath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
2744 }
2745
2746 return sourcePath;
2747 }
2748
2749
2750
2751
2752
2753
2754
2755
2756 private Collection<String> getExcludedPackages()
2757 throws MavenReportException
2758 {
2759 Set<String> excluded = new LinkedHashSet<>();
2760
2761 if ( includeDependencySources )
2762 {
2763 try
2764 {
2765 resolveDependencyBundles();
2766 }
2767 catch ( IOException e )
2768 {
2769 throw new MavenReportException(
2770 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
2771 }
2772
2773 if ( isNotEmpty( dependencyJavadocBundles ) )
2774 {
2775 for ( JavadocBundle bundle : dependencyJavadocBundles )
2776 {
2777 JavadocOptions options = bundle.getOptions();
2778 if ( options != null && isNotEmpty( options.getExcludePackageNames() ) )
2779 {
2780 excluded.addAll( options.getExcludePackageNames() );
2781 }
2782 }
2783 }
2784 }
2785
2786
2787 if ( StringUtils.isNotEmpty( excludePackageNames ) )
2788 {
2789 List<String> packageNames = Arrays.asList( excludePackageNames.split( "[,:;]" ) );
2790 excluded.addAll( trimValues( packageNames ) );
2791 }
2792
2793 return excluded;
2794 }
2795
2796 private static List<String> trimValues( List<String> items )
2797 {
2798 List<String> result = new ArrayList<>( items.size() );
2799 for ( String item : items )
2800 {
2801 String trimmed = item.trim();
2802 if ( StringUtils.isEmpty( trimmed ) )
2803 {
2804 continue;
2805 }
2806 result.add( trimmed );
2807 }
2808 return result;
2809 }
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821 private Collection<File> getPathElements()
2822 throws MavenReportException
2823 {
2824 Set<File> classpathElements = new LinkedHashSet<>();
2825 Map<String, Artifact> compileArtifactMap = new LinkedHashMap<>();
2826
2827 if ( isTest() )
2828 {
2829 classpathElements.addAll( getProjectBuildOutputDirs( project ) );
2830 }
2831
2832 populateCompileArtifactMap( compileArtifactMap, project.getArtifacts() );
2833
2834 if ( isAggregator() )
2835 {
2836 Collection<MavenProject> aggregatorProjects = getAggregatedProjects();
2837
2838 List<String> reactorArtifacts = new ArrayList<>();
2839 for ( MavenProject p : aggregatorProjects )
2840 {
2841 reactorArtifacts.add( p.getGroupId() + ':' + p.getArtifactId() );
2842 }
2843
2844 TransformableFilter dependencyFilter = new AndFilter( Arrays.asList(
2845 new PatternExclusionsFilter( reactorArtifacts ),
2846 getDependencyScopeFilter() ) );
2847
2848 for ( MavenProject subProject : aggregatorProjects )
2849 {
2850 if ( subProject != project )
2851 {
2852 File projectArtifactFile = getClassesFile( subProject );
2853 if ( projectArtifactFile != null )
2854 {
2855 classpathElements.add( projectArtifactFile );
2856 }
2857 else
2858 {
2859 classpathElements.addAll( getProjectBuildOutputDirs( subProject ) );
2860 }
2861
2862 try
2863 {
2864 StringBuilder sb = new StringBuilder();
2865
2866 sb.append( "Compiled artifacts for " );
2867 sb.append( subProject.getGroupId() ).append( ":" );
2868 sb.append( subProject.getArtifactId() ).append( ":" );
2869 sb.append( subProject.getVersion() ).append( '\n' );
2870
2871 ProjectBuildingRequest buildingRequest = getProjectBuildingRequest( subProject );
2872
2873 List<Dependency> managedDependencies = null;
2874 if ( subProject.getDependencyManagement() != null )
2875 {
2876 managedDependencies = subProject.getDependencyManagement().getDependencies();
2877 }
2878
2879 for ( ArtifactResult artifactResult
2880 : dependencyResolver.resolveDependencies( buildingRequest,
2881 subProject.getDependencies(),
2882 managedDependencies,
2883 dependencyFilter ) )
2884 {
2885 populateCompileArtifactMap( compileArtifactMap,
2886 Collections.singletonList( artifactResult.getArtifact() ) );
2887
2888 sb.append( artifactResult.getArtifact().getFile() ).append( '\n' );
2889 }
2890
2891 if ( getLog().isDebugEnabled() )
2892 {
2893 getLog().debug( sb.toString() );
2894 }
2895
2896 }
2897 catch ( DependencyResolverException e )
2898 {
2899 throw new MavenReportException( e.getMessage(), e );
2900 }
2901 }
2902 }
2903 }
2904
2905 for ( Artifact a : compileArtifactMap.values() )
2906 {
2907 classpathElements.add( a.getFile() );
2908 }
2909
2910 if ( additionalDependencies != null )
2911 {
2912 for ( Dependency dependency : additionalDependencies )
2913 {
2914 Artifact artifact = resolveDependency( dependency );
2915 getLog().debug( "add additional artifact with path " + artifact.getFile() );
2916 classpathElements.add( artifact.getFile() );
2917 }
2918 }
2919
2920 return classpathElements;
2921 }
2922
2923 protected ScopeFilter getDependencyScopeFilter()
2924 {
2925 return ScopeFilter.including( Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM );
2926 }
2927
2928
2929
2930
2931
2932
2933 public Artifact resolveDependency( Dependency dependency )
2934 throws MavenReportException
2935 {
2936 DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
2937 coordinate.setGroupId( dependency.getGroupId() );
2938 coordinate.setArtifactId( dependency.getArtifactId() );
2939 coordinate.setVersion( dependency.getVersion() );
2940 coordinate.setClassifier( dependency.getClassifier() );
2941 coordinate.setExtension( artifactHandlerManager.getArtifactHandler( dependency.getType() ).getExtension() );
2942
2943 try
2944 {
2945 return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
2946 }
2947 catch ( ArtifactResolverException e )
2948 {
2949 throw new MavenReportException( "artifact resolver problem - " + e.getMessage(), e );
2950 }
2951 }
2952
2953
2954
2955
2956 protected final Toolchain getToolchain()
2957 {
2958 Toolchain tc = null;
2959
2960 if ( jdkToolchain != null )
2961 {
2962
2963 try
2964 {
2965 Method getToolchainsMethod =
2966 toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
2967 Map.class );
2968
2969 @SuppressWarnings( "unchecked" )
2970 List<Toolchain> tcs =
2971 (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
2972 jdkToolchain );
2973
2974 if ( tcs != null && tcs.size() > 0 )
2975 {
2976 tc = tcs.get( 0 );
2977 }
2978 }
2979 catch ( SecurityException | ReflectiveOperationException e )
2980 {
2981
2982 }
2983 }
2984
2985 if ( tc == null )
2986 {
2987 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
2988 }
2989
2990 return tc;
2991 }
2992
2993
2994
2995
2996
2997
2998
2999
3000 private void populateCompileArtifactMap( Map<String, Artifact> compileArtifactMap,
3001 Collection<Artifact> artifactList )
3002 throws MavenReportException
3003 {
3004 if ( artifactList == null )
3005 {
3006 return;
3007 }
3008
3009 for ( Artifact newArtifact : artifactList )
3010 {
3011 File file = newArtifact.getFile();
3012
3013 if ( file == null )
3014 {
3015 throw new MavenReportException(
3016 "Error in plugin descriptor - " + "dependency was not resolved for artifact: "
3017 + newArtifact.getGroupId() + ":" + newArtifact.getArtifactId() + ":"
3018 + newArtifact.getVersion() );
3019 }
3020
3021 if ( compileArtifactMap.get( newArtifact.getDependencyConflictId() ) != null )
3022 {
3023 Artifact oldArtifact = compileArtifactMap.get( newArtifact.getDependencyConflictId() );
3024
3025 ArtifactVersion oldVersion = new DefaultArtifactVersion( oldArtifact.getVersion() );
3026 ArtifactVersion newVersion = new DefaultArtifactVersion( newArtifact.getVersion() );
3027 if ( newVersion.compareTo( oldVersion ) > 0 )
3028 {
3029 compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
3030 }
3031 }
3032 else
3033 {
3034 compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
3035 }
3036 }
3037 }
3038
3039
3040
3041
3042
3043
3044
3045 private String getBottomText()
3046 {
3047 final String inceptionYear = project.getInceptionYear();
3048
3049
3050 final Calendar currentYearCal = Calendar.getInstance();
3051 final Date reproducibleDate = new MavenArchiver().parseOutputTimestamp( outputTimestamp );
3052 if ( reproducibleDate != null )
3053 {
3054 currentYearCal.setTime( reproducibleDate );
3055 }
3056 final String currentYear = String.valueOf( currentYearCal.get( Calendar.YEAR ) );
3057
3058 String theBottom = StringUtils.replace( this.bottom, "{currentYear}", currentYear );
3059
3060 if ( ( inceptionYear == null ) || inceptionYear.equals( currentYear ) )
3061 {
3062 theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
3063 }
3064 else
3065 {
3066 theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
3067 }
3068
3069 if ( project.getOrganization() == null )
3070 {
3071 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
3072 }
3073 else
3074 {
3075 if ( StringUtils.isNotEmpty( project.getOrganization().getName() ) )
3076 {
3077 if ( StringUtils.isNotEmpty( project.getOrganization().getUrl() ) )
3078 {
3079 theBottom = StringUtils.replace( theBottom, "{organizationName}",
3080 "<a href=\"" + project.getOrganization().getUrl() + "\">"
3081 + project.getOrganization().getName() + "</a>" );
3082 }
3083 else
3084 {
3085 theBottom =
3086 StringUtils.replace( theBottom, "{organizationName}", project.getOrganization().getName() );
3087 }
3088 }
3089 else
3090 {
3091 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
3092 }
3093 }
3094
3095 return theBottom;
3096 }
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113 private Optional<File> getStylesheetFile( final File javadocOutputDirectory )
3114 {
3115 if ( StringUtils.isEmpty( stylesheetfile ) )
3116 {
3117 if ( "java".equalsIgnoreCase( stylesheet ) )
3118 {
3119
3120 return Optional.empty();
3121 }
3122
3123
3124 return Optional.of( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ) );
3125 }
3126
3127 if ( new File( stylesheetfile ).exists() )
3128 {
3129 return Optional.of( new File( stylesheetfile ) );
3130 }
3131
3132 return getResource( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ), stylesheetfile );
3133 }
3134
3135 private void addAddStyleSheets( List<String> arguments ) throws MavenReportException
3136 {
3137 if ( addStylesheets == null )
3138 {
3139 return;
3140 }
3141
3142 for ( String addStylesheet : addStylesheets )
3143 {
3144 Optional<File> styleSheet = getAddStylesheet( getJavadocDirectory(), addStylesheet );
3145
3146 if ( styleSheet.isPresent() )
3147 {
3148 addArgIfNotEmpty( arguments, "--add-stylesheet",
3149 JavadocUtil.quotedPathArgument( styleSheet.get().getAbsolutePath() ),
3150 JavaVersion.parse( "10" ) );
3151 }
3152 }
3153 }
3154
3155
3156 private Optional<File> getAddStylesheet( final File javadocOutputDirectory, final String stylesheet )
3157 throws MavenReportException
3158 {
3159 if ( StringUtils.isEmpty( stylesheet ) )
3160 {
3161 return Optional.empty();
3162 }
3163
3164 File addstylesheetfile = new File( getJavadocDirectory(), stylesheet );
3165 if ( addstylesheetfile.exists() )
3166 {
3167 Optional<File> stylesheetfile = getStylesheetFile( javadocOutputDirectory );
3168 if ( stylesheetfile.isPresent() )
3169 {
3170 if ( stylesheetfile.get().getName().equals( addstylesheetfile.getName() ) )
3171 {
3172 throw new MavenReportException( "additional stylesheet must have a different name "
3173 + "than stylesheetfile: " + stylesheetfile.get().getName() );
3174 }
3175 }
3176
3177 return Optional.of( addstylesheetfile );
3178 }
3179
3180 throw new MavenReportException( "additional stylesheet file does not exist: "
3181 + addstylesheetfile.getAbsolutePath() );
3182 }
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196 private Optional<File> getHelpFile( final File javadocOutputDirectory )
3197 {
3198 if ( StringUtils.isEmpty( helpfile ) )
3199 {
3200 return Optional.empty();
3201 }
3202
3203 if ( new File( helpfile ).exists() )
3204 {
3205 return Optional.of( new File( helpfile ) );
3206 }
3207
3208 return getResource( new File( javadocOutputDirectory, "help-doc.html" ), helpfile );
3209 }
3210
3211
3212
3213
3214
3215
3216
3217
3218 private String getAccessLevel()
3219 {
3220 String accessLevel;
3221 if ( "public".equalsIgnoreCase( show ) || "protected".equalsIgnoreCase( show ) || "package".equalsIgnoreCase(
3222 show ) || "private".equalsIgnoreCase( show ) )
3223 {
3224 accessLevel = "-" + show;
3225 }
3226 else
3227 {
3228 if ( getLog().isErrorEnabled() )
3229 {
3230 getLog().error( "Unrecognized access level to show '" + show + "'. Defaulting to protected." );
3231 }
3232 accessLevel = "-protected";
3233 }
3234
3235 return accessLevel;
3236 }
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246 private String getBootclassPath()
3247 throws MavenReportException
3248 {
3249 Set<BootclasspathArtifact> bootclasspathArtifacts = collectBootClasspathArtifacts();
3250
3251 List<String> bootclassPath = new ArrayList<>();
3252 for ( BootclasspathArtifact aBootclasspathArtifact : bootclasspathArtifacts )
3253 {
3254 if ( ( StringUtils.isNotEmpty( aBootclasspathArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
3255 aBootclasspathArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3256 aBootclasspathArtifact.getVersion() ) ) )
3257 {
3258 bootclassPath.addAll( getArtifactsAbsolutePath( aBootclasspathArtifact ) );
3259 }
3260 }
3261
3262 bootclassPath = JavadocUtil.pruneFiles( bootclassPath );
3263
3264 StringBuilder path = new StringBuilder();
3265 path.append( StringUtils.join( bootclassPath.iterator(), File.pathSeparator ) );
3266
3267 if ( StringUtils.isNotEmpty( bootclasspath ) )
3268 {
3269 path.append( JavadocUtil.unifyPathSeparator( bootclasspath ) );
3270 }
3271
3272 return path.toString();
3273 }
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287 private String getDocletPath()
3288 throws MavenReportException
3289 {
3290 Set<DocletArtifact> docletArtifacts = collectDocletArtifacts();
3291 List<String> pathParts = new ArrayList<>();
3292
3293 for ( DocletArtifact docletArtifact : docletArtifacts )
3294 {
3295 if ( !isDocletArtifactEmpty( docletArtifact ) )
3296 {
3297 pathParts.addAll( getArtifactsAbsolutePath( docletArtifact ) );
3298 }
3299 }
3300
3301 if ( !StringUtils.isEmpty( docletPath ) )
3302 {
3303 pathParts.add( JavadocUtil.unifyPathSeparator( docletPath ) );
3304 }
3305
3306 String path = StringUtils.join( pathParts.iterator(), File.pathSeparator );
3307
3308 if ( StringUtils.isEmpty( path ) && getLog().isWarnEnabled() )
3309 {
3310 getLog().warn( "No docletpath option was found. Please review <docletpath/> or <docletArtifact/>"
3311 + " or <doclets/>." );
3312 }
3313
3314 return path;
3315 }
3316
3317
3318
3319
3320
3321
3322
3323
3324 private boolean isDocletArtifactEmpty( DocletArtifact aDocletArtifact )
3325 {
3326 if ( aDocletArtifact == null )
3327 {
3328 return true;
3329 }
3330
3331 return StringUtils.isEmpty( aDocletArtifact.getGroupId() ) && StringUtils.isEmpty(
3332 aDocletArtifact.getArtifactId() ) && StringUtils.isEmpty( aDocletArtifact.getVersion() );
3333 }
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343 private String getTagletPath()
3344 throws MavenReportException
3345 {
3346 Set<TagletArtifact> tArtifacts = collectTagletArtifacts();
3347 Collection<String> pathParts = new ArrayList<>();
3348
3349 for ( TagletArtifact tagletArtifact : tArtifacts )
3350 {
3351 if ( ( tagletArtifact != null ) && ( StringUtils.isNotEmpty( tagletArtifact.getGroupId() ) )
3352 && ( StringUtils.isNotEmpty( tagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3353 tagletArtifact.getVersion() ) ) )
3354 {
3355 pathParts.addAll( getArtifactsAbsolutePath( tagletArtifact ) );
3356 }
3357 }
3358
3359 Set<Taglet> taglets = collectTaglets();
3360 for ( Taglet taglet : taglets )
3361 {
3362 if ( taglet == null )
3363 {
3364 continue;
3365 }
3366
3367 if ( ( taglet.getTagletArtifact() != null ) && ( StringUtils.isNotEmpty(
3368 taglet.getTagletArtifact().getGroupId() ) ) && ( StringUtils.isNotEmpty(
3369 taglet.getTagletArtifact().getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3370 taglet.getTagletArtifact().getVersion() ) ) )
3371 {
3372 pathParts.addAll( JavadocUtil.pruneFiles( getArtifactsAbsolutePath( taglet.getTagletArtifact() ) ) );
3373 }
3374 else if ( StringUtils.isNotEmpty( taglet.getTagletpath() ) )
3375 {
3376 for ( Path dir : JavadocUtil.pruneDirs( project, Collections.singletonList( taglet.getTagletpath() ) ) )
3377 {
3378 pathParts.add( dir.toString() );
3379 }
3380 }
3381 }
3382
3383 StringBuilder path = new StringBuilder();
3384 path.append( StringUtils.join( pathParts.iterator(), File.pathSeparator ) );
3385
3386 if ( StringUtils.isNotEmpty( tagletpath ) )
3387 {
3388 path.append( JavadocUtil.unifyPathSeparator( tagletpath ) );
3389 }
3390
3391 return path.toString();
3392 }
3393
3394 private Set<String> collectLinks()
3395 throws MavenReportException
3396 {
3397 Set<String> links = new LinkedHashSet<>();
3398
3399 if ( includeDependencySources )
3400 {
3401 try
3402 {
3403 resolveDependencyBundles();
3404 }
3405 catch ( IOException e )
3406 {
3407 throw new MavenReportException(
3408 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3409 }
3410
3411 if ( isNotEmpty( dependencyJavadocBundles ) )
3412 {
3413 for ( JavadocBundle bundle : dependencyJavadocBundles )
3414 {
3415 JavadocOptions options = bundle.getOptions();
3416 if ( options != null && isNotEmpty( options.getLinks() ) )
3417 {
3418 links.addAll( options.getLinks() );
3419 }
3420 }
3421 }
3422 }
3423
3424 if ( isNotEmpty( this.links ) )
3425 {
3426 links.addAll( this.links );
3427 }
3428
3429 links.addAll( getDependenciesLinks() );
3430
3431 return followLinks( links );
3432 }
3433
3434 private Set<Group> collectGroups()
3435 throws MavenReportException
3436 {
3437 Set<Group> groups = new LinkedHashSet<>();
3438
3439 if ( includeDependencySources )
3440 {
3441 try
3442 {
3443 resolveDependencyBundles();
3444 }
3445 catch ( IOException e )
3446 {
3447 throw new MavenReportException(
3448 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3449 }
3450
3451 if ( isNotEmpty( dependencyJavadocBundles ) )
3452 {
3453 for ( JavadocBundle bundle : dependencyJavadocBundles )
3454 {
3455 JavadocOptions options = bundle.getOptions();
3456 if ( options != null && isNotEmpty( options.getGroups() ) )
3457 {
3458 groups.addAll( options.getGroups() );
3459 }
3460 }
3461 }
3462 }
3463
3464 if ( this.groups != null && this.groups.length > 0 )
3465 {
3466 groups.addAll( Arrays.asList( this.groups ) );
3467 }
3468
3469 return groups;
3470 }
3471
3472 private Set<ResourcesArtifact> collectResourcesArtifacts()
3473 throws MavenReportException
3474 {
3475 Set<ResourcesArtifact> result = new LinkedHashSet<>();
3476
3477 if ( includeDependencySources )
3478 {
3479 try
3480 {
3481 resolveDependencyBundles();
3482 }
3483 catch ( IOException e )
3484 {
3485 throw new MavenReportException(
3486 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3487 }
3488
3489 if ( isNotEmpty( dependencyJavadocBundles ) )
3490 {
3491 for ( JavadocBundle bundle : dependencyJavadocBundles )
3492 {
3493 JavadocOptions options = bundle.getOptions();
3494 if ( options != null && isNotEmpty( options.getResourcesArtifacts() ) )
3495 {
3496 result.addAll( options.getResourcesArtifacts() );
3497 }
3498 }
3499 }
3500 }
3501
3502 if ( this.resourcesArtifacts != null && this.resourcesArtifacts.length > 0 )
3503 {
3504 result.addAll( Arrays.asList( this.resourcesArtifacts ) );
3505 }
3506
3507 return result;
3508 }
3509
3510 private Set<BootclasspathArtifact> collectBootClasspathArtifacts()
3511 throws MavenReportException
3512 {
3513 Set<BootclasspathArtifact> result = new LinkedHashSet<>();
3514
3515 if ( includeDependencySources )
3516 {
3517 try
3518 {
3519 resolveDependencyBundles();
3520 }
3521 catch ( IOException e )
3522 {
3523 throw new MavenReportException(
3524 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3525 }
3526
3527 if ( isNotEmpty( dependencyJavadocBundles ) )
3528 {
3529 for ( JavadocBundle bundle : dependencyJavadocBundles )
3530 {
3531 JavadocOptions options = bundle.getOptions();
3532 if ( options != null && isNotEmpty( options.getBootclasspathArtifacts() ) )
3533 {
3534 result.addAll( options.getBootclasspathArtifacts() );
3535 }
3536 }
3537 }
3538 }
3539
3540 if ( this.bootclasspathArtifacts != null && this.bootclasspathArtifacts.length > 0 )
3541 {
3542 result.addAll( Arrays.asList( this.bootclasspathArtifacts ) );
3543 }
3544
3545 return result;
3546 }
3547
3548 private Set<OfflineLink> collectOfflineLinks()
3549 throws MavenReportException
3550 {
3551 Set<OfflineLink> result = new LinkedHashSet<>();
3552
3553 OfflineLink javaApiLink = getDefaultJavadocApiLink();
3554 if ( javaApiLink != null )
3555 {
3556 result.add( javaApiLink );
3557 }
3558
3559 if ( includeDependencySources )
3560 {
3561 try
3562 {
3563 resolveDependencyBundles();
3564 }
3565 catch ( IOException e )
3566 {
3567 throw new MavenReportException(
3568 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3569 }
3570
3571 if ( isNotEmpty( dependencyJavadocBundles ) )
3572 {
3573 for ( JavadocBundle bundle : dependencyJavadocBundles )
3574 {
3575 JavadocOptions options = bundle.getOptions();
3576 if ( options != null && isNotEmpty( options.getOfflineLinks() ) )
3577 {
3578 result.addAll( options.getOfflineLinks() );
3579 }
3580 }
3581 }
3582 }
3583
3584 if ( this.offlineLinks != null && this.offlineLinks.length > 0 )
3585 {
3586 result.addAll( Arrays.asList( this.offlineLinks ) );
3587 }
3588
3589 return result;
3590 }
3591
3592 private Set<Tag> collectTags()
3593 throws MavenReportException
3594 {
3595 Set<Tag> tags = new LinkedHashSet<>();
3596
3597 if ( includeDependencySources )
3598 {
3599 try
3600 {
3601 resolveDependencyBundles();
3602 }
3603 catch ( IOException e )
3604 {
3605 throw new MavenReportException(
3606 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3607 }
3608
3609 if ( isNotEmpty( dependencyJavadocBundles ) )
3610 {
3611 for ( JavadocBundle bundle : dependencyJavadocBundles )
3612 {
3613 JavadocOptions options = bundle.getOptions();
3614 if ( options != null && isNotEmpty( options.getTags() ) )
3615 {
3616 tags.addAll( options.getTags() );
3617 }
3618 }
3619 }
3620 }
3621
3622 if ( this.tags != null && this.tags.length > 0 )
3623 {
3624 tags.addAll( Arrays.asList( this.tags ) );
3625 }
3626
3627 return tags;
3628 }
3629
3630 private Set<TagletArtifact> collectTagletArtifacts()
3631 throws MavenReportException
3632 {
3633 Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
3634
3635 if ( includeDependencySources )
3636 {
3637 try
3638 {
3639 resolveDependencyBundles();
3640 }
3641 catch ( IOException e )
3642 {
3643 throw new MavenReportException(
3644 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3645 }
3646
3647 if ( isNotEmpty( dependencyJavadocBundles ) )
3648 {
3649 for ( JavadocBundle bundle : dependencyJavadocBundles )
3650 {
3651 JavadocOptions options = bundle.getOptions();
3652 if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
3653 {
3654 tArtifacts.addAll( options.getTagletArtifacts() );
3655 }
3656 }
3657 }
3658 }
3659
3660 if ( tagletArtifact != null )
3661 {
3662 tArtifacts.add( tagletArtifact );
3663 }
3664
3665 if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
3666 {
3667 tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
3668 }
3669
3670 return tArtifacts;
3671 }
3672
3673 private Set<DocletArtifact> collectDocletArtifacts()
3674 throws MavenReportException
3675 {
3676 Set<DocletArtifact> dArtifacts = new LinkedHashSet<>();
3677
3678 if ( includeDependencySources )
3679 {
3680 try
3681 {
3682 resolveDependencyBundles();
3683 }
3684 catch ( IOException e )
3685 {
3686 throw new MavenReportException(
3687 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3688 }
3689
3690 if ( isNotEmpty( dependencyJavadocBundles ) )
3691 {
3692 for ( JavadocBundle bundle : dependencyJavadocBundles )
3693 {
3694 JavadocOptions options = bundle.getOptions();
3695 if ( options != null && isNotEmpty( options.getDocletArtifacts() ) )
3696 {
3697 dArtifacts.addAll( options.getDocletArtifacts() );
3698 }
3699 }
3700 }
3701 }
3702
3703 if ( docletArtifact != null )
3704 {
3705 dArtifacts.add( docletArtifact );
3706 }
3707
3708 if ( docletArtifacts != null && docletArtifacts.length > 0 )
3709 {
3710 dArtifacts.addAll( Arrays.asList( docletArtifacts ) );
3711 }
3712
3713 return dArtifacts;
3714 }
3715
3716 private Set<Taglet> collectTaglets()
3717 throws MavenReportException
3718 {
3719 Set<Taglet> result = new LinkedHashSet<>();
3720
3721 if ( includeDependencySources )
3722 {
3723 try
3724 {
3725 resolveDependencyBundles();
3726 }
3727 catch ( IOException e )
3728 {
3729 throw new MavenReportException(
3730 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3731 }
3732
3733 if ( isNotEmpty( dependencyJavadocBundles ) )
3734 {
3735 for ( JavadocBundle bundle : dependencyJavadocBundles )
3736 {
3737 JavadocOptions options = bundle.getOptions();
3738 if ( options != null && isNotEmpty( options.getTaglets() ) )
3739 {
3740 result.addAll( options.getTaglets() );
3741 }
3742 }
3743 }
3744 }
3745
3746 if ( taglets != null && taglets.length > 0 )
3747 {
3748 result.addAll( Arrays.asList( taglets ) );
3749 }
3750
3751 return result;
3752 }
3753
3754
3755
3756
3757
3758
3759
3760
3761 private List<String> getArtifactsAbsolutePath( JavadocPathArtifact javadocArtifact )
3762 throws MavenReportException
3763 {
3764 if ( ( StringUtils.isEmpty( javadocArtifact.getGroupId() ) ) && ( StringUtils.isEmpty(
3765 javadocArtifact.getArtifactId() ) ) && ( StringUtils.isEmpty( javadocArtifact.getVersion() ) ) )
3766 {
3767 return Collections.emptyList();
3768 }
3769
3770 List<String> path = new ArrayList<>();
3771
3772 try
3773 {
3774 Artifact artifact = createAndResolveArtifact( javadocArtifact );
3775 path.add( artifact.getFile().getAbsolutePath() );
3776
3777 DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate();
3778 coordinate.setGroupId( javadocArtifact.getGroupId() );
3779 coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3780 coordinate.setVersion( javadocArtifact.getVersion() );
3781
3782 Iterable<ArtifactResult> deps =
3783 dependencyResolver.resolveDependencies( getProjectBuildingRequest( project ), coordinate,
3784 ScopeFilter.including( "compile", "provided" ) );
3785 for ( ArtifactResult a : deps )
3786 {
3787 path.add( a.getArtifact().getFile().getAbsolutePath() );
3788 }
3789
3790 return path;
3791 }
3792 catch ( ArtifactResolverException e )
3793 {
3794 throw new MavenReportException( "Unable to resolve artifact:" + javadocArtifact, e );
3795 }
3796 catch ( DependencyResolverException e )
3797 {
3798 throw new MavenReportException( "Unable to resolve dependencies for:" + javadocArtifact, e );
3799 }
3800 }
3801
3802
3803
3804
3805
3806
3807
3808
3809 private Artifact createAndResolveArtifact( JavadocPathArtifact javadocArtifact )
3810 throws ArtifactResolverException
3811 {
3812 DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
3813 coordinate.setGroupId( javadocArtifact.getGroupId() );
3814 coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3815 coordinate.setVersion( javadocArtifact.getVersion() );
3816 coordinate.setClassifier( javadocArtifact.getClassifier() );
3817
3818 return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
3819 }
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829 private void addMemoryArg( Commandline cmd, String arg, String memory )
3830 {
3831 if ( StringUtils.isNotEmpty( memory ) )
3832 {
3833 try
3834 {
3835 cmd.createArg().setValue( "-J" + arg + JavadocUtil.parseJavadocMemory( memory ) );
3836 }
3837 catch ( IllegalArgumentException e )
3838 {
3839 if ( getLog().isErrorEnabled() )
3840 {
3841 getLog().error( "Malformed memory pattern for '" + arg + memory + "'. Ignore this option." );
3842 }
3843 }
3844 }
3845 }
3846
3847
3848
3849
3850
3851
3852 private void addProxyArg( Commandline cmd )
3853 {
3854 if ( settings == null || settings.getProxies().isEmpty() )
3855 {
3856 return;
3857 }
3858
3859 Map<String, Proxy> activeProxies = new HashMap<>();
3860
3861 for ( Proxy proxy : settings.getProxies() )
3862 {
3863 if ( proxy.isActive() )
3864 {
3865 String protocol = proxy.getProtocol();
3866
3867 if ( !activeProxies.containsKey( protocol ) )
3868 {
3869 activeProxies.put( protocol, proxy );
3870 }
3871 }
3872 }
3873
3874 if ( activeProxies.containsKey( "https" ) )
3875 {
3876 Proxy httpsProxy = activeProxies.get( "https" );
3877 if ( StringUtils.isNotEmpty( httpsProxy.getHost() ) )
3878 {
3879 cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpsProxy.getHost() );
3880 cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpsProxy.getPort() );
3881
3882 if ( StringUtils.isNotEmpty( httpsProxy.getNonProxyHosts() )
3883 && ( !activeProxies.containsKey( "http" )
3884 || StringUtils.isEmpty( activeProxies.get( "http" ).getNonProxyHosts() ) ) )
3885 {
3886 cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
3887 + httpsProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
3888 }
3889 }
3890 }
3891
3892 if ( activeProxies.containsKey( "http" ) )
3893 {
3894 Proxy httpProxy = activeProxies.get( "http" );
3895 if ( StringUtils.isNotEmpty( httpProxy.getHost() ) )
3896 {
3897 cmd.createArg().setValue( "-J-Dhttp.proxyHost=" + httpProxy.getHost() );
3898 cmd.createArg().setValue( "-J-Dhttp.proxyPort=" + httpProxy.getPort() );
3899
3900 if ( !activeProxies.containsKey( "https" ) )
3901 {
3902 cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpProxy.getHost() );
3903 cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpProxy.getPort() );
3904 }
3905
3906 if ( StringUtils.isNotEmpty( httpProxy.getNonProxyHosts() ) )
3907 {
3908 cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
3909 + httpProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
3910 }
3911 }
3912 }
3913
3914
3915 }
3916
3917
3918
3919
3920
3921
3922
3923
3924 private String getJavadocExecutable()
3925 throws IOException
3926 {
3927 Toolchain tc = getToolchain();
3928
3929 if ( tc != null )
3930 {
3931 getLog().info( "Toolchain in maven-javadoc-plugin: " + tc );
3932 if ( javadocExecutable != null )
3933 {
3934 getLog().warn( "Toolchains are ignored, 'javadocExecutable' parameter is set to " + javadocExecutable );
3935 }
3936 else
3937 {
3938 javadocExecutable = tc.findTool( "javadoc" );
3939 }
3940 }
3941
3942 String javadocCommand = "javadoc" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
3943
3944 File javadocExe;
3945
3946
3947
3948
3949 if ( StringUtils.isNotEmpty( javadocExecutable ) )
3950 {
3951 javadocExe = new File( javadocExecutable );
3952
3953 if ( javadocExe.isDirectory() )
3954 {
3955 javadocExe = new File( javadocExe, javadocCommand );
3956 }
3957
3958 if ( SystemUtils.IS_OS_WINDOWS && javadocExe.getName().indexOf( '.' ) < 0 )
3959 {
3960 javadocExe = new File( javadocExe.getPath() + ".exe" );
3961 }
3962
3963 if ( !javadocExe.isFile() )
3964 {
3965 throw new IOException( "The javadoc executable '" + javadocExe
3966 + "' doesn't exist or is not a file. Verify the <javadocExecutable/> parameter." );
3967 }
3968
3969 return javadocExe.getAbsolutePath();
3970 }
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006 if ( SystemUtils.IS_OS_AIX )
4007 {
4008 javadocExe =
4009 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", javadocCommand );
4010 }
4011
4012
4013 else if ( SystemUtils.IS_OS_MAC_OSX && !JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "1.7" ) )
4014
4015 {
4016 javadocExe = new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
4017 }
4018 else if ( isJavaVersionAtLeast( org.apache.commons.lang3.JavaVersion.JAVA_9 ) )
4019 {
4020 javadocExe =
4021 new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
4022 }
4023 else
4024 {
4025
4026 javadocExe =
4027 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", javadocCommand );
4028 }
4029
4030
4031
4032
4033 if ( !javadocExe.exists() || !javadocExe.isFile() )
4034 {
4035 Properties env = CommandLineUtils.getSystemEnvVars();
4036 String javaHome = env.getProperty( "JAVA_HOME" );
4037 if ( StringUtils.isEmpty( javaHome ) )
4038 {
4039 throw new IOException( "The environment variable JAVA_HOME is not correctly set." );
4040 }
4041 if ( ( !new File( javaHome ).getCanonicalFile().exists() )
4042 || ( new File( javaHome ).getCanonicalFile().isFile() ) )
4043 {
4044 throw new IOException( "The environment variable JAVA_HOME=" + javaHome
4045 + " doesn't exist or is not a valid directory." );
4046 }
4047
4048 javadocExe = new File( javaHome + File.separator + "bin", javadocCommand );
4049 }
4050
4051 if ( !javadocExe.getCanonicalFile().exists() || !javadocExe.getCanonicalFile().isFile() )
4052 {
4053 throw new IOException( "The javadoc executable '" + javadocExe
4054 + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." );
4055 }
4056
4057 return javadocExe.getAbsolutePath();
4058 }
4059
4060
4061
4062
4063
4064
4065
4066
4067 private void setFJavadocVersion( File jExecutable )
4068 throws MavenReportException
4069 {
4070 JavaVersion jVersion;
4071 try
4072 {
4073 jVersion = JavadocUtil.getJavadocVersion( jExecutable );
4074 }
4075 catch ( IOException | CommandLineException | IllegalArgumentException e )
4076 {
4077 if ( getLog().isWarnEnabled() )
4078 {
4079 getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
4080 getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
4081 }
4082 jVersion = JAVA_VERSION;
4083 }
4084
4085 if ( StringUtils.isNotEmpty( javadocVersion ) )
4086 {
4087 try
4088 {
4089 javadocRuntimeVersion = JavaVersion.parse( javadocVersion );
4090 }
4091 catch ( NumberFormatException e )
4092 {
4093 throw new MavenReportException( "Unable to parse javadoc version: " + e.getMessage(), e );
4094 }
4095
4096 if ( javadocRuntimeVersion.compareTo( jVersion ) != 0 && getLog().isWarnEnabled() )
4097 {
4098 getLog().warn( "Are you sure about the <javadocVersion/> parameter? It seems to be " + jVersion );
4099 }
4100 }
4101 else
4102 {
4103 javadocRuntimeVersion = jVersion;
4104 }
4105 }
4106
4107
4108
4109
4110
4111
4112
4113
4114 private boolean isJavaDocVersionAtLeast( JavaVersion requiredVersion )
4115 {
4116 return JAVA_VERSION.compareTo( requiredVersion ) >= 0;
4117 }
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127 private void addArgIf( List<String> arguments, boolean b, String value )
4128 {
4129 if ( b )
4130 {
4131 arguments.add( value );
4132 }
4133 }
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146 private void addArgIf( List<String> arguments, boolean b, String value, JavaVersion requiredJavaVersion )
4147 {
4148 if ( b )
4149 {
4150 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4151 {
4152 addArgIf( arguments, true, value );
4153 }
4154 else
4155 {
4156 if ( getLog().isWarnEnabled() )
4157 {
4158 getLog().warn( value + " option is not supported on Java version < " + requiredJavaVersion
4159 + ". Ignore this option." );
4160 }
4161 }
4162 }
4163 }
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176 private void addArgIfNotEmpty( List<String> arguments, String key, String value )
4177 {
4178 addArgIfNotEmpty( arguments, key, value, false );
4179 }
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
4197 boolean splitValue, JavaVersion requiredJavaVersion )
4198 {
4199 if ( StringUtils.isNotEmpty( value ) )
4200 {
4201 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4202 {
4203 addArgIfNotEmpty( arguments, key, value, repeatKey, splitValue );
4204 }
4205 else
4206 {
4207 if ( getLog().isWarnEnabled() )
4208 {
4209 getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion
4210 + ". Ignore this option." );
4211 }
4212 }
4213 }
4214 }
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
4229 boolean splitValue )
4230 {
4231 if ( StringUtils.isNotEmpty( value ) )
4232 {
4233 if ( StringUtils.isNotEmpty( key ) )
4234 {
4235 arguments.add( key );
4236 }
4237
4238 if ( splitValue )
4239 {
4240 StringTokenizer token = new StringTokenizer( value, "," );
4241 while ( token.hasMoreTokens() )
4242 {
4243 String current = token.nextToken().trim();
4244
4245 if ( StringUtils.isNotEmpty( current ) )
4246 {
4247 arguments.add( current );
4248
4249 if ( token.hasMoreTokens() && repeatKey )
4250 {
4251 arguments.add( key );
4252 }
4253 }
4254 }
4255 }
4256 else
4257 {
4258 arguments.add( value );
4259 }
4260 }
4261 }
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey )
4275 {
4276 addArgIfNotEmpty( arguments, key, value, repeatKey, true );
4277 }
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289 private void addArgIfNotEmpty( List<String> arguments, String key, String value,
4290 JavaVersion requiredJavaVersion )
4291 {
4292 addArgIfNotEmpty( arguments, key, value, requiredJavaVersion, false );
4293 }
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307 private void addArgIfNotEmpty( List<String> arguments, String key, String value, JavaVersion requiredJavaVersion,
4308 boolean repeatKey )
4309 {
4310 if ( StringUtils.isNotEmpty( value ) )
4311 {
4312 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4313 {
4314 addArgIfNotEmpty( arguments, key, value, repeatKey );
4315 }
4316 else
4317 {
4318 if ( getLog().isWarnEnabled() )
4319 {
4320 getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion );
4321 }
4322 }
4323 }
4324 }
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339 private void addLinkofflineArguments( List<String> arguments, Set<OfflineLink> offlineLinksList )
4340 throws MavenReportException
4341 {
4342 for ( OfflineLink offlineLink : offlineLinksList )
4343 {
4344 String url = offlineLink.getUrl();
4345 if ( StringUtils.isEmpty( url ) )
4346 {
4347 continue;
4348 }
4349 url = cleanUrl( url );
4350
4351 String location = offlineLink.getLocation();
4352 if ( StringUtils.isEmpty( location ) )
4353 {
4354 continue;
4355 }
4356 if ( isValidJavadocLink( location, false ) )
4357 {
4358 addArgIfNotEmpty( arguments, "-linkoffline",
4359 JavadocUtil.quotedPathArgument( url ) + " " + JavadocUtil.quotedPathArgument(
4360 location ), true );
4361 }
4362 }
4363 }
4364
4365 private Set<OfflineLink> getLinkofflines() throws MavenReportException
4366 {
4367 Set<OfflineLink> offlineLinksList = collectOfflineLinks();
4368
4369 offlineLinksList.addAll( getModulesLinks() );
4370
4371 return offlineLinksList;
4372 }
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393 private void addLinkArguments( List<String> arguments )
4394 throws MavenReportException
4395 {
4396 Set<String> links = collectLinks();
4397
4398 for ( String link : links )
4399 {
4400 if ( StringUtils.isEmpty( link ) )
4401 {
4402 continue;
4403 }
4404
4405 if ( isOffline && !link.startsWith( "file:" ) )
4406 {
4407 continue;
4408 }
4409
4410 while ( link.endsWith( "/" ) )
4411 {
4412 link = link.substring( 0, link.lastIndexOf( "/" ) );
4413 }
4414
4415 addArgIfNotEmpty( arguments, "-link", JavadocUtil.quotedPathArgument( link ), true, false );
4416 }
4417 }
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428 private void copyAllResources( File javadocOutputDirectory )
4429 throws MavenReportException
4430 {
4431
4432
4433
4434
4435 try
4436 {
4437 copyDefaultStylesheet( javadocOutputDirectory );
4438 }
4439 catch ( IOException e )
4440 {
4441 throw new MavenReportException( "Unable to copy default stylesheet: " + e.getMessage(), e );
4442 }
4443
4444
4445
4446
4447
4448 if ( docfilessubdirs )
4449 {
4450
4451
4452
4453
4454 try
4455 {
4456 copyJavadocResources( javadocOutputDirectory );
4457 }
4458 catch ( IOException e )
4459 {
4460 throw new MavenReportException( "Unable to copy javadoc resources: " + e.getMessage(), e );
4461 }
4462 }
4463
4464
4465
4466
4467
4468 copyAdditionalJavadocResources( javadocOutputDirectory );
4469 }
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481 private void copyDefaultStylesheet( File anOutputDirectory )
4482 throws IOException
4483 {
4484 if ( StringUtils.isNotEmpty( stylesheetfile ) )
4485 {
4486 return;
4487 }
4488
4489 if ( !stylesheet.equalsIgnoreCase( "maven" ) )
4490 {
4491 return;
4492 }
4493
4494 URL url = getClass().getClassLoader().getResource( RESOURCE_CSS_DIR + "/" + DEFAULT_CSS_NAME );
4495 File outFile = new File( anOutputDirectory, DEFAULT_CSS_NAME );
4496 JavadocUtil.copyResource( url, outFile );
4497 }
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509 private void copyJavadocResources( File anOutputDirectory )
4510 throws IOException
4511 {
4512 if ( anOutputDirectory == null || !anOutputDirectory.exists() )
4513 {
4514 throw new IOException( "The outputDirectory " + anOutputDirectory + " doesn't exists." );
4515 }
4516
4517 if ( includeDependencySources )
4518 {
4519 resolveDependencyBundles();
4520 if ( isNotEmpty( dependencyJavadocBundles ) )
4521 {
4522 for ( JavadocBundle bundle : dependencyJavadocBundles )
4523 {
4524 File dir = bundle.getResourcesDirectory();
4525 JavadocOptions options = bundle.getOptions();
4526 if ( dir != null && dir.isDirectory() )
4527 {
4528 JavadocUtil.copyJavadocResources( anOutputDirectory, dir, options == null
4529 ? null
4530 : options.getExcludedDocfilesSubdirs() );
4531 }
4532 }
4533 }
4534 }
4535
4536 if ( getJavadocDirectory() != null )
4537 {
4538 JavadocUtil.copyJavadocResources( anOutputDirectory, getJavadocDirectory(), excludedocfilessubdir );
4539 }
4540
4541 if ( isAggregator() )
4542 {
4543 for ( MavenProject subProject : getAggregatedProjects() )
4544 {
4545 if ( subProject != project && getJavadocDirectory() != null )
4546 {
4547 String javadocDirRelative =
4548 PathUtils.toRelative( project.getBasedir(), getJavadocDirectory().getAbsolutePath() );
4549 File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
4550 JavadocUtil.copyJavadocResources( anOutputDirectory, javadocDir, excludedocfilessubdir );
4551 }
4552 }
4553 }
4554 }
4555
4556 private synchronized void resolveDependencyBundles()
4557 throws IOException
4558 {
4559 if ( dependencyJavadocBundles == null )
4560 {
4561 dependencyJavadocBundles =
4562 resourceResolver.resolveDependencyJavadocBundles( getDependencySourceResolverConfig() );
4563 if ( dependencyJavadocBundles == null )
4564 {
4565 dependencyJavadocBundles = new ArrayList<>();
4566 }
4567 }
4568 }
4569
4570
4571
4572
4573
4574
4575
4576
4577 private void copyAdditionalJavadocResources( File anOutputDirectory )
4578 throws MavenReportException
4579 {
4580 Set<ResourcesArtifact> resourcesArtifacts = collectResourcesArtifacts();
4581 if ( isEmpty( resourcesArtifacts ) )
4582 {
4583 return;
4584 }
4585
4586 UnArchiver unArchiver;
4587 try
4588 {
4589 unArchiver = archiverManager.getUnArchiver( "jar" );
4590 }
4591 catch ( NoSuchArchiverException e )
4592 {
4593 throw new MavenReportException(
4594 "Unable to extract resources artifact. " + "No archiver for 'jar' available.", e );
4595 }
4596
4597 for ( ResourcesArtifact item : resourcesArtifacts )
4598 {
4599 Artifact artifact;
4600 try
4601 {
4602 artifact = createAndResolveArtifact( item );
4603 }
4604 catch ( ArtifactResolverException e )
4605 {
4606 throw new MavenReportException( "Unable to resolve artifact:" + item, e );
4607 }
4608
4609 unArchiver.setSourceFile( artifact.getFile() );
4610 unArchiver.setDestDirectory( anOutputDirectory );
4611
4612 IncludeExcludeFileSelector[] selectors =
4613 new IncludeExcludeFileSelector[]{ new IncludeExcludeFileSelector() };
4614 selectors[0].setExcludes( new String[]{ "META-INF/**" } );
4615 unArchiver.setFileSelectors( selectors );
4616
4617 getLog().info( "Extracting contents of resources artifact: " + artifact.getArtifactId() );
4618 try
4619 {
4620 unArchiver.extract();
4621 }
4622 catch ( ArchiverException e )
4623 {
4624 throw new MavenReportException(
4625 "Extraction of resources failed. Artifact that failed was: " + artifact.getArtifactId(), e );
4626 }
4627 }
4628 }
4629
4630
4631
4632
4633
4634 private List<String> getPackageNames( Map<Path, Collection<String>> sourcePaths )
4635 {
4636 List<String> returnList = new ArrayList<>();
4637
4638 if ( !StringUtils.isEmpty( sourcepath ) )
4639 {
4640 return returnList;
4641 }
4642
4643 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4644 {
4645 for ( String currentFile : currentPathEntry.getValue() )
4646 {
4647
4648
4649
4650
4651 if ( currentFile.contains( "doc-files" ) )
4652 {
4653 continue;
4654 }
4655
4656 int lastIndexOfSeparator = currentFile.lastIndexOf( "/" );
4657 if ( lastIndexOfSeparator != -1 )
4658 {
4659 String packagename = currentFile.substring( 0, lastIndexOfSeparator ).replace( '/', '.' );
4660
4661 if ( !returnList.contains( packagename ) )
4662 {
4663 returnList.add( packagename );
4664 }
4665 }
4666 }
4667 }
4668
4669 return returnList;
4670 }
4671
4672
4673
4674
4675
4676
4677
4678
4679 private Collection<String> getPackageNamesRespectingJavaModules( Collection<JavadocModule> javadocModules )
4680 throws MavenReportException
4681 {
4682 if ( !StringUtils.isEmpty( sourcepath ) )
4683 {
4684 return Collections.emptyList();
4685 }
4686
4687 Set<String> returnList = new LinkedHashSet<>();
4688 for ( JavadocModule javadocModule : javadocModules )
4689 {
4690 Collection<Path> artifactSourcePaths = javadocModule.getSourcePaths();
4691 Set<String> exportedPackages = new HashSet<>();
4692 boolean exportAllPackages;
4693 ResolvePathResult resolvedPath = getResolvePathResult( javadocModule.getArtifactFile() );
4694 if ( resolvedPath != null && resolvedPath.getModuleNameSource() == ModuleNameSource.MODULEDESCRIPTOR )
4695 {
4696 Set<JavaModuleDescriptor.JavaExports> exports = resolvedPath.getModuleDescriptor().exports();
4697 if ( exports.isEmpty() )
4698 {
4699 continue;
4700 }
4701 for ( JavaModuleDescriptor.JavaExports export : exports )
4702 {
4703 exportedPackages.add( export.source() );
4704 }
4705 exportAllPackages = false;
4706 }
4707 else
4708 {
4709 exportAllPackages = true;
4710 }
4711
4712 for ( Map.Entry<Path, Collection<String>> currentPathEntry : getFiles( artifactSourcePaths ).entrySet() )
4713 {
4714 for ( String currentFile : currentPathEntry.getValue() )
4715 {
4716
4717
4718
4719
4720 if ( currentFile.contains( "doc-files" ) )
4721 {
4722 continue;
4723 }
4724
4725 int lastIndexOfSeparator = currentFile.lastIndexOf( '/' );
4726 if ( lastIndexOfSeparator != -1 )
4727 {
4728 String packagename =
4729 currentFile.substring( 0, lastIndexOfSeparator ).replace( '/', '.' );
4730
4731 if ( exportAllPackages || exportedPackages.contains( packagename ) )
4732 {
4733 returnList.add( packagename );
4734 }
4735 }
4736 }
4737 }
4738 }
4739
4740 return returnList;
4741 }
4742
4743
4744
4745
4746
4747 private List<String> getFilesWithUnnamedPackages( Map<Path, Collection<String>> sourcePaths )
4748 {
4749 List<String> returnList = new ArrayList<>();
4750
4751 if ( !StringUtils.isEmpty( sourcepath ) )
4752 {
4753 return returnList;
4754 }
4755
4756 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4757 {
4758 Path currentSourcePath = currentPathEntry.getKey();
4759
4760 for ( String currentFile : currentPathEntry.getValue() )
4761 {
4762
4763
4764
4765
4766 if ( currentFile.contains( "doc-files" ) )
4767 {
4768 continue;
4769 }
4770
4771 if ( currentFile.indexOf( File.separatorChar ) == -1 )
4772 {
4773 returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
4774 }
4775 }
4776 }
4777
4778 return returnList;
4779 }
4780
4781
4782
4783
4784
4785
4786 private List<String> getSpecialFiles( Map<Path, Collection<String>> sourcePaths )
4787 {
4788 if ( !StringUtils.isEmpty( sourcepath ) )
4789 {
4790 return new ArrayList<>();
4791 }
4792
4793 boolean containsModuleDescriptor = false;
4794 for ( Collection<String> sourcepathFiles : sourcePaths.values() )
4795 {
4796 containsModuleDescriptor = sourcepathFiles.contains( "module-info.java" );
4797 if ( containsModuleDescriptor )
4798 {
4799 break;
4800 }
4801 }
4802
4803 if ( containsModuleDescriptor )
4804 {
4805 return getModuleSourcePathFiles( sourcePaths );
4806 }
4807 else
4808 {
4809 return getFilesWithUnnamedPackages( sourcePaths );
4810 }
4811 }
4812
4813 private List<String> getModuleSourcePathFiles( Map<Path, Collection<String>> sourcePaths )
4814 {
4815 List<String> returnList = new ArrayList<>();
4816
4817 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4818 {
4819 Path currentSourcePath = currentPathEntry.getKey();
4820 if ( currentPathEntry.getValue().contains( "module-info.java" ) )
4821 {
4822 returnList.add( currentSourcePath.resolve( "module-info.java" ).toAbsolutePath().toString() );
4823 }
4824 else
4825 {
4826 for ( String currentFile : currentPathEntry.getValue() )
4827 {
4828
4829
4830
4831
4832 if ( currentFile.contains( "doc-files" ) )
4833 {
4834 continue;
4835 }
4836
4837 returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
4838 }
4839 }
4840 }
4841 return returnList;
4842 }
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856 private void addCommandLineOptions( Commandline cmd, List<String> arguments, File javadocOutputDirectory )
4857 throws MavenReportException
4858 {
4859 File optionsFile = new File( javadocOutputDirectory, OPTIONS_FILE_NAME );
4860
4861 StringBuilder options = new StringBuilder();
4862 options.append( StringUtils.join( arguments.iterator(),
4863 SystemUtils.LINE_SEPARATOR ) );
4864
4865 Charset outputFileEncoding;
4866 if ( JAVA_VERSION.isAtLeast( "9" ) && JAVA_VERSION.isBefore( "12" ) )
4867 {
4868 outputFileEncoding = StandardCharsets.UTF_8;
4869 }
4870 else
4871 {
4872 outputFileEncoding = Charset.defaultCharset();
4873 }
4874 try
4875 {
4876 Files.write( optionsFile.toPath(), Collections.singleton( options ), outputFileEncoding );
4877 }
4878 catch ( IOException e )
4879 {
4880 throw new MavenReportException(
4881 "Unable to write '" + optionsFile.getName() + "' temporary file for command execution", e );
4882 }
4883
4884 cmd.createArg().setValue( "@" + OPTIONS_FILE_NAME );
4885 }
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905 private void addCommandLineArgFile( Commandline cmd, File javadocOutputDirectory, List<String> files )
4906 throws MavenReportException
4907 {
4908 File argfileFile;
4909 if ( JAVA_VERSION.compareTo( SINCE_JAVADOC_1_4 ) >= 0 )
4910 {
4911 argfileFile = new File( javadocOutputDirectory, ARGFILE_FILE_NAME );
4912 cmd.createArg().setValue( "@" + ARGFILE_FILE_NAME );
4913 }
4914 else
4915 {
4916 argfileFile = new File( javadocOutputDirectory, FILES_FILE_NAME );
4917 cmd.createArg().setValue( "@" + FILES_FILE_NAME );
4918 }
4919
4920 List<String> quotedFiles = new ArrayList<>( files.size() );
4921 for ( String file : files )
4922 {
4923 quotedFiles.add( JavadocUtil.quotedPathArgument( file ) );
4924 }
4925
4926 Charset cs;
4927 if ( JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "9" )
4928 && JavaVersion.JAVA_SPECIFICATION_VERSION.isBefore( "12" ) )
4929 {
4930 cs = StandardCharsets.UTF_8;
4931 }
4932 else
4933 {
4934 cs = Charset.defaultCharset();
4935 }
4936
4937 try
4938 {
4939 Files.write( argfileFile.toPath(), quotedFiles, cs );
4940 }
4941 catch ( IOException e )
4942 {
4943 throw new MavenReportException(
4944 "Unable to write '" + argfileFile.getName() + "' temporary file for command execution", e );
4945 }
4946 }
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960 private void addCommandLinePackages( Commandline cmd, File javadocOutputDirectory, Collection<String> packageNames )
4961 throws MavenReportException
4962 {
4963 File packagesFile = new File( javadocOutputDirectory, PACKAGES_FILE_NAME );
4964
4965 try
4966 {
4967 FileUtils.fileWrite( packagesFile.getAbsolutePath(), null ,
4968 StringUtils.join( packageNames.iterator(), SystemUtils.LINE_SEPARATOR ) );
4969 }
4970 catch ( IOException e )
4971 {
4972 throw new MavenReportException(
4973 "Unable to write '" + packagesFile.getName() + "' temporary file for command execution", e );
4974 }
4975
4976 cmd.createArg().setValue( "@" + PACKAGES_FILE_NAME );
4977 }
4978
4979
4980
4981
4982
4983
4984 private void validateJavadocOptions()
4985 throws MavenReportException
4986 {
4987
4988 if ( StringUtils.isNotEmpty( getEncoding() ) && !JavadocUtil.validateEncoding( getEncoding() ) )
4989 {
4990 throw new MavenReportException( "Unsupported option <encoding/> '" + getEncoding() + "'" );
4991 }
4992
4993
4994 if ( StringUtils.isNotEmpty( this.locale ) )
4995 {
4996 StringTokenizer tokenizer = new StringTokenizer( this.locale, "_" );
4997 final int maxTokens = 3;
4998 if ( tokenizer.countTokens() > maxTokens )
4999 {
5000 throw new MavenReportException(
5001 "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
5002 }
5003
5004 Locale localeObject = null;
5005 if ( tokenizer.hasMoreTokens() )
5006 {
5007 String language = tokenizer.nextToken().toLowerCase( Locale.ENGLISH );
5008 if ( !Arrays.asList( Locale.getISOLanguages() ).contains( language ) )
5009 {
5010 throw new MavenReportException(
5011 "Unsupported language '" + language + "' in option <locale/> '" + this.locale + "'" );
5012 }
5013 localeObject = new Locale( language );
5014
5015 if ( tokenizer.hasMoreTokens() )
5016 {
5017 String country = tokenizer.nextToken().toUpperCase( Locale.ENGLISH );
5018 if ( !Arrays.asList( Locale.getISOCountries() ).contains( country ) )
5019 {
5020 throw new MavenReportException(
5021 "Unsupported country '" + country + "' in option <locale/> '" + this.locale + "'" );
5022 }
5023 localeObject = new Locale( language, country );
5024
5025 if ( tokenizer.hasMoreTokens() )
5026 {
5027 String variant = tokenizer.nextToken();
5028 localeObject = new Locale( language, country, variant );
5029 }
5030 }
5031 }
5032
5033 if ( localeObject == null )
5034 {
5035 throw new MavenReportException(
5036 "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
5037 }
5038
5039 this.locale = localeObject.toString();
5040 final List<Locale> availableLocalesList = Arrays.asList( Locale.getAvailableLocales() );
5041 if ( StringUtils.isNotEmpty( localeObject.getVariant() ) && !availableLocalesList.contains( localeObject ) )
5042 {
5043 StringBuilder sb = new StringBuilder();
5044 sb.append( "Unsupported option <locale/> with variant '" ).append( this.locale );
5045 sb.append( "'" );
5046
5047 localeObject = new Locale( localeObject.getLanguage(), localeObject.getCountry() );
5048 this.locale = localeObject.toString();
5049
5050 sb.append( ", trying to use <locale/> without variant, i.e. '" ).append( this.locale ).append( "'" );
5051 if ( getLog().isWarnEnabled() )
5052 {
5053 getLog().warn( sb.toString() );
5054 }
5055 }
5056
5057 if ( !availableLocalesList.contains( localeObject ) )
5058 {
5059 throw new MavenReportException( "Unsupported option <locale/> '" + this.locale + "'" );
5060 }
5061 }
5062 }
5063
5064
5065
5066
5067
5068
5069
5070
5071 private void validateStandardDocletOptions()
5072 throws MavenReportException
5073 {
5074
5075 if ( StringUtils.isNotEmpty( getDocencoding() ) && !JavadocUtil.validateEncoding( getDocencoding() ) )
5076 {
5077 throw new MavenReportException( "Unsupported option <docencoding/> '" + getDocencoding() + "'" );
5078 }
5079
5080
5081 if ( StringUtils.isNotEmpty( getCharset() ) && !JavadocUtil.validateEncoding( getCharset() ) )
5082 {
5083 throw new MavenReportException( "Unsupported option <charset/> '" + getCharset() + "'" );
5084 }
5085
5086
5087 if ( StringUtils.isNotEmpty( helpfile ) && nohelp )
5088 {
5089 throw new MavenReportException( "Option <nohelp/> conflicts with <helpfile/>" );
5090 }
5091
5092
5093 if ( getOverview() != null && getOverview().exists() && nooverview )
5094 {
5095 throw new MavenReportException( "Option <nooverview/> conflicts with <overview/>" );
5096 }
5097
5098
5099 if ( splitindex && noindex )
5100 {
5101 throw new MavenReportException( "Option <noindex/> conflicts with <splitindex/>" );
5102 }
5103
5104
5105 if ( StringUtils.isNotEmpty( stylesheet ) && !( stylesheet.equalsIgnoreCase( "maven" )
5106 || stylesheet.equalsIgnoreCase( "java" ) ) )
5107 {
5108 throw new MavenReportException( "Option <stylesheet/> supports only \"maven\" or \"java\" value." );
5109 }
5110 }
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124 private void addJavadocOptions( File javadocOutputDirectory,
5125 List<String> arguments,
5126 Collection<JavadocModule> allSourcePaths,
5127 Set<OfflineLink> offlineLinks )
5128 throws MavenReportException
5129 {
5130 Collection<Path> sourcePaths = allSourcePaths.stream()
5131 .flatMap( e -> e.getSourcePaths().stream() )
5132 .collect( Collectors.toList() );
5133
5134 validateJavadocOptions();
5135
5136
5137 addArgIfNotEmpty( arguments, "-locale", JavadocUtil.quotedArgument( this.locale ) );
5138
5139
5140
5141 if ( old && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_4 ) )
5142 {
5143 if ( getLog().isWarnEnabled() )
5144 {
5145 getLog().warn( "Javadoc 1.4+ doesn't support the -1.1 switch anymore. Ignore this option." );
5146 }
5147 }
5148 else
5149 {
5150 addArgIf( arguments, old, "-1.1" );
5151 }
5152
5153 addArgIfNotEmpty( arguments, "-bootclasspath", JavadocUtil.quotedPathArgument( getBootclassPath() ) );
5154
5155 if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5156 {
5157 addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_5 );
5158 }
5159
5160 List<MavenProject> aggregatedProjects = reactorProjects;
5161 Map<String, MavenProject> reactorKeys = new HashMap<>( aggregatedProjects.size() );
5162 for ( MavenProject reactorProject : aggregatedProjects )
5163 {
5164 reactorKeys.put( ArtifactUtils.versionlessKey( reactorProject.getGroupId(),
5165 reactorProject.getArtifactId() ), reactorProject );
5166 }
5167
5168 Map<String, JavaModuleDescriptor> allModuleDescriptors = new HashMap<>();
5169
5170 boolean supportModulePath = javadocRuntimeVersion.isAtLeast( "9" );
5171 if ( release != null )
5172 {
5173 supportModulePath &= JavaVersion.parse( release ).isAtLeast( "9" );
5174 }
5175 else if ( source != null )
5176 {
5177 supportModulePath &= JavaVersion.parse( source ).isAtLeast( "9" );
5178 }
5179
5180 if ( supportModulePath )
5181 {
5182 for ( JavadocModule entry : allSourcePaths )
5183 {
5184 if ( entry.getModuleNameSource() == null || entry.getModuleNameSource() == ModuleNameSource.FILENAME )
5185 {
5186 Path moduleDescriptor = findMainDescriptor( entry.getSourcePaths() );
5187
5188 if ( moduleDescriptor != null )
5189 {
5190 try
5191 {
5192 allModuleDescriptors.put( entry.getGa(),
5193 locationManager.parseModuleDescriptor( moduleDescriptor ).getModuleDescriptor() );
5194 }
5195 catch ( IOException e )
5196 {
5197 throw new MavenReportException( e.getMessage(), e );
5198 }
5199 }
5200 }
5201 else
5202 {
5203 allModuleDescriptors.put( entry.getGa(), entry.getModuleDescriptor() );
5204 }
5205 }
5206 }
5207
5208 Collection<String> additionalModules = new ArrayList<>();
5209
5210 ResolvePathResult mainResolvePathResult = null;
5211
5212 Map<String, Collection<Path>> patchModules = new HashMap<>();
5213
5214 Path moduleSourceDir = null;
5215 if ( supportModulePath && !allModuleDescriptors.isEmpty() )
5216 {
5217 Collection<String> unnamedProjects = new ArrayList<>();
5218 for ( JavadocModule javadocModule : allSourcePaths )
5219 {
5220 MavenProject aggregatedProject = reactorKeys.get( javadocModule.getGa() );
5221 if ( aggregatedProject != null && !"pom".equals( aggregatedProject.getPackaging() ) )
5222 {
5223 ResolvePathResult result = null;
5224
5225
5226 File artifactFile = getClassesFile( aggregatedProject );
5227 if ( artifactFile != null )
5228 {
5229 ResolvePathRequest<File> request = ResolvePathRequest.ofFile( artifactFile );
5230 try
5231 {
5232 result = locationManager.resolvePath( request );
5233 }
5234 catch ( RuntimeException e )
5235 {
5236
5237 if ( !"java.lang.module.FindException".equals( e.getClass().getName() ) )
5238 {
5239 throw e;
5240 }
5241 }
5242 catch ( IOException e )
5243 {
5244 throw new MavenReportException( e.getMessage(), e );
5245 }
5246 }
5247 else
5248 {
5249 Path moduleDescriptor = findMainDescriptor( javadocModule.getSourcePaths() );
5250
5251 if ( moduleDescriptor != null )
5252 {
5253 try
5254 {
5255 result = locationManager.parseModuleDescriptor( moduleDescriptor );
5256 }
5257 catch ( IOException e )
5258 {
5259 throw new MavenReportException( e.getMessage(), e );
5260 }
5261 }
5262 }
5263
5264 if ( result != null && result.getModuleDescriptor() != null )
5265 {
5266 moduleSourceDir = javadocOutputDirectory.toPath().resolve( "src" );
5267 try
5268 {
5269 moduleSourceDir = Files.createDirectories( moduleSourceDir );
5270
5271 additionalModules.add( result.getModuleDescriptor().name() );
5272
5273 patchModules.put( result.getModuleDescriptor().name(), javadocModule.getSourcePaths() );
5274
5275 Path modulePath = moduleSourceDir.resolve( result.getModuleDescriptor().name() );
5276 if ( !Files.isDirectory( modulePath ) )
5277 {
5278 Files.createDirectory( modulePath );
5279 }
5280 }
5281 catch ( IOException e )
5282 {
5283 throw new MavenReportException( e.getMessage(), e );
5284 }
5285 }
5286 else
5287 {
5288 unnamedProjects.add( javadocModule.getGa() );
5289 }
5290
5291 if ( aggregatedProject.equals( getProject() ) )
5292 {
5293 mainResolvePathResult = result;
5294 }
5295 }
5296 else
5297 {
5298
5299 getLog().error( "no reactor project: " + javadocModule.getGa() );
5300 }
5301 }
5302
5303 if ( !unnamedProjects.isEmpty() )
5304 {
5305 getLog().error( "Creating an aggregated report for both named and unnamed modules is not possible." );
5306 getLog().error( "Ensure that every module has a module descriptor or is a jar with a MANIFEST.MF "
5307 + "containing an Automatic-Module-Name." );
5308 getLog().error( "Fix the following projects:" );
5309 for ( String unnamedProject : unnamedProjects )
5310 {
5311 getLog().error( " - " + unnamedProject );
5312 }
5313 throw new MavenReportException( "Aggregator report contains named and unnamed modules" );
5314 }
5315
5316 if ( mainResolvePathResult != null
5317 && ModuleNameSource.MANIFEST.equals( mainResolvePathResult.getModuleNameSource() ) )
5318 {
5319 arguments.add( "--add-modules" );
5320 arguments.add( "ALL-MODULE-PATH" );
5321 }
5322 }
5323
5324
5325 boolean moduleDescriptorSource = false;
5326 for ( Path sourcepath : sourcePaths )
5327 {
5328 if ( Files.isRegularFile( sourcepath.resolve( "module-info.java" ) ) )
5329 {
5330 moduleDescriptorSource = true;
5331 break;
5332 }
5333 }
5334
5335 final ModuleNameSource mainModuleNameSource;
5336 if ( mainResolvePathResult != null )
5337 {
5338 mainModuleNameSource = mainResolvePathResult.getModuleNameSource();
5339 }
5340 else
5341 {
5342 mainModuleNameSource = null;
5343 }
5344
5345 if ( supportModulePath
5346 && ( isAggregator()
5347 || ModuleNameSource.MODULEDESCRIPTOR.equals( mainModuleNameSource )
5348 || ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) ) )
5349 {
5350 List<File> pathElements = new ArrayList<>( getPathElements() );
5351 File artifactFile = getClassesFile( project );
5352 if ( artifactFile != null )
5353 {
5354 pathElements.add( 0, artifactFile );
5355 }
5356
5357 ResolvePathsRequest<File> request =
5358 ResolvePathsRequest.ofFiles( pathElements );
5359
5360 String mainModuleName = null;
5361 if ( mainResolvePathResult != null )
5362 {
5363 request.setModuleDescriptor( mainResolvePathResult.getModuleDescriptor() );
5364 mainModuleName = mainResolvePathResult.getModuleDescriptor().name();
5365 }
5366
5367 request.setAdditionalModules( additionalModules );
5368 request.setIncludeStatic( isAggregator() );
5369
5370 try
5371 {
5372 ResolvePathsResult<File> result = locationManager.resolvePaths( request );
5373
5374 Set<File> modulePathElements = new HashSet<>( result.getModulepathElements().keySet() ) ;
5375
5376 Collection<File> classPathElements = new ArrayList<>( result.getClasspathElements().size() );
5377
5378 for ( File file : result.getClasspathElements() )
5379 {
5380 if ( file.isDirectory() && new File( file, "module-info.class" ).exists() )
5381 {
5382 modulePathElements.add( file );
5383 }
5384 else if ( ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) )
5385 {
5386 ModuleNameSource depModuleNameSource =
5387 locationManager.resolvePath( ResolvePathRequest.ofFile( file ) ).getModuleNameSource();
5388 if ( ModuleNameSource.MODULEDESCRIPTOR.equals( depModuleNameSource )
5389 || ModuleNameSource.MANIFEST.equals( depModuleNameSource ) )
5390 {
5391 modulePathElements.add( file );
5392 }
5393 else
5394 {
5395 patchModules.get( mainModuleName ).add( file.toPath() );
5396 }
5397 }
5398 else
5399 {
5400 classPathElements.add( file );
5401 }
5402 }
5403
5404
5405 for ( Entry<File, Exception> pathExceptionEntry : result.getPathExceptions().entrySet() )
5406 {
5407 Exception exception = pathExceptionEntry.getValue();
5408
5409 if ( "java.lang.module.FindException".equals( exception.getClass().getName() ) )
5410 {
5411 File jarPath = pathExceptionEntry.getKey();
5412 classPathElements.add( jarPath );
5413 }
5414 }
5415
5416 String classpath = StringUtils.join( classPathElements.iterator(), File.pathSeparator );
5417 addArgIfNotEmpty( arguments, "--class-path", JavadocUtil.quotedPathArgument( classpath ), false,
5418 false );
5419
5420 String modulepath =
5421 StringUtils.join( modulePathElements.iterator(), File.pathSeparator );
5422 addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ), false,
5423 false );
5424 }
5425 catch ( IOException e )
5426 {
5427 throw new MavenReportException( e.getMessage(), e );
5428 }
5429 }
5430 else if ( supportModulePath && moduleDescriptorSource && !isTest() )
5431 {
5432 String modulepath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
5433 addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ) , false, false );
5434 }
5435 else
5436 {
5437 String classpath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
5438 addArgIfNotEmpty( arguments, "-classpath", JavadocUtil.quotedPathArgument( classpath ) , false, false );
5439 }
5440
5441 for ( Entry<String, Collection<Path>> entry : patchModules.entrySet() )
5442 {
5443 addArgIfNotEmpty( arguments, "--patch-module", entry.getKey() + '='
5444 + JavadocUtil.quotedPathArgument( getSourcePath( entry.getValue() ) ),
5445 false, false );
5446 }
5447
5448 if ( StringUtils.isNotEmpty( doclet ) )
5449 {
5450 addArgIfNotEmpty( arguments, "-doclet", JavadocUtil.quotedArgument( doclet ) );
5451 addArgIfNotEmpty( arguments, "-docletpath", JavadocUtil.quotedPathArgument( getDocletPath() ) );
5452 }
5453
5454 if ( StringUtils.isEmpty( encoding ) )
5455 {
5456 getLog().warn(
5457 "Source files encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
5458 + ", i.e. build is platform dependent!" );
5459 }
5460 addArgIfNotEmpty( arguments, "-encoding", JavadocUtil.quotedArgument( getEncoding() ) );
5461
5462 addArgIfNotEmpty( arguments, "-extdirs",
5463 JavadocUtil.quotedPathArgument( JavadocUtil.unifyPathSeparator( extdirs ) ) );
5464
5465 if ( ( getOverview() != null ) && ( getOverview().exists() ) )
5466 {
5467 addArgIfNotEmpty( arguments, "-overview",
5468 JavadocUtil.quotedPathArgument( getOverview().getAbsolutePath() ) );
5469 }
5470
5471 arguments.add( getAccessLevel() );
5472
5473 if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5474 {
5475 addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_5 );
5476 }
5477
5478 if ( release != null )
5479 {
5480 arguments.add( "--release" );
5481 arguments.add( release );
5482 }
5483 else
5484 {
5485 addArgIfNotEmpty( arguments, "-source", JavadocUtil.quotedArgument( source ), SINCE_JAVADOC_1_4 );
5486 }
5487
5488 if ( ( StringUtils.isEmpty( sourcepath ) ) && ( StringUtils.isNotEmpty( subpackages ) ) )
5489 {
5490 sourcepath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
5491 }
5492
5493 if ( moduleSourceDir == null )
5494 {
5495 addArgIfNotEmpty( arguments, "-sourcepath",
5496 JavadocUtil.quotedPathArgument( getSourcePath( sourcePaths ) ), false, false );
5497 }
5498 else if ( mainResolvePathResult == null
5499 || ModuleNameSource.MODULEDESCRIPTOR.equals( mainResolvePathResult.getModuleNameSource() ) )
5500 {
5501 addArgIfNotEmpty( arguments, "--module-source-path",
5502 JavadocUtil.quotedPathArgument( moduleSourceDir.toString() ) );
5503 }
5504
5505
5506 if ( StringUtils.isNotEmpty( sourcepath ) && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5507 {
5508 addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_5 );
5509 }
5510
5511
5512 addArgIfNotEmpty( arguments, "-exclude", getExcludedPackages( sourcePaths ), SINCE_JAVADOC_1_4 );
5513
5514 addArgIf( arguments, verbose, "-verbose" );
5515
5516 if ( additionalOptions != null && additionalOptions.length > 0 )
5517 {
5518 for ( String additionalOption : additionalOptions )
5519 {
5520 arguments.add( additionalOption.replaceAll( "(?<!\\\\)\\\\(?!\\\\|:)", "\\\\" ) );
5521 }
5522 }
5523 }
5524
5525 private ResolvePathResult getResolvePathResult( File artifactFile )
5526 {
5527 if ( artifactFile == null )
5528 {
5529 return null;
5530 }
5531
5532 ResolvePathResult resolvePathResult = null;
5533 ResolvePathRequest<File> resolvePathRequest = ResolvePathRequest.ofFile( artifactFile );
5534 try
5535 {
5536 resolvePathResult = locationManager.resolvePath( resolvePathRequest );
5537
5538
5539 if ( resolvePathResult.getModuleDescriptor() == null )
5540 {
5541 return null;
5542 }
5543 }
5544 catch ( IOException | RuntimeException e )
5545 {
5546 if ( getLog().isDebugEnabled() )
5547 {
5548 Throwable cause = e;
5549 while ( cause.getCause() != null )
5550 {
5551 cause = cause.getCause();
5552 }
5553
5554 getLog().debug( "resolve path for: " + artifactFile + " cause error: " + cause );
5555 }
5556 }
5557 return resolvePathResult;
5558 }
5559
5560 private Path findMainDescriptor( Collection<Path> roots ) throws MavenReportException
5561 {
5562 for ( Map.Entry<Path, Collection<String>> entry : getFiles( roots ).entrySet() )
5563 {
5564 if ( entry.getValue().contains( "module-info.java" ) )
5565 {
5566 return entry.getKey().resolve( "module-info.java" );
5567 }
5568 }
5569 return null;
5570 }
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584 private void addStandardDocletOptions( File javadocOutputDirectory,
5585 List<String> arguments,
5586 Set<OfflineLink> offlineLinks )
5587 throws MavenReportException
5588 {
5589 validateStandardDocletOptions();
5590
5591
5592
5593 addArgIf( arguments, author, "-author" );
5594
5595 addArgIfNotEmpty( arguments, "-bottom", JavadocUtil.quotedArgument( getBottomText() ), false, false );
5596
5597 if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5598 {
5599 addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_4 );
5600 }
5601
5602 addArgIfNotEmpty( arguments, "-charset", JavadocUtil.quotedArgument( getCharset() ) );
5603
5604 addArgIfNotEmpty( arguments, "-d", JavadocUtil.quotedPathArgument( javadocOutputDirectory.toString() ) );
5605
5606 addArgIfNotEmpty( arguments, "-docencoding", JavadocUtil.quotedArgument( getDocencoding() ) );
5607
5608 addArgIf( arguments, docfilessubdirs, "-docfilessubdirs", SINCE_JAVADOC_1_4 );
5609
5610 addArgIf( arguments, StringUtils.isNotEmpty( doclint ), "-Xdoclint:" + getDoclint(), SINCE_JAVADOC_1_8 );
5611
5612 addArgIfNotEmpty( arguments, "-doctitle", JavadocUtil.quotedArgument( getDoctitle() ), false, false );
5613
5614 if ( docfilessubdirs )
5615 {
5616 addArgIfNotEmpty( arguments, "-excludedocfilessubdir",
5617 JavadocUtil.quotedPathArgument( excludedocfilessubdir ), SINCE_JAVADOC_1_4 );
5618 }
5619
5620 addArgIfNotEmpty( arguments, "-footer", JavadocUtil.quotedArgument( footer ), false, false );
5621
5622 addGroups( arguments );
5623
5624 addArgIfNotEmpty( arguments, "-header", JavadocUtil.quotedArgument( header ), false, false );
5625
5626 Optional<File> helpFile = getHelpFile( javadocOutputDirectory );
5627 if ( helpFile.isPresent() )
5628 {
5629 addArgIfNotEmpty( arguments, "-helpfile",
5630 JavadocUtil.quotedPathArgument( helpFile.get().getAbsolutePath() ) );
5631 }
5632
5633 addArgIf( arguments, keywords, "-keywords", SINCE_JAVADOC_1_4_2 );
5634
5635 addLinkArguments( arguments );
5636
5637 addLinkofflineArguments( arguments, offlineLinks );
5638
5639 addArgIf( arguments, linksource, "-linksource", SINCE_JAVADOC_1_4 );
5640
5641 if ( sourcetab > 0 )
5642 {
5643 if ( javadocRuntimeVersion == SINCE_JAVADOC_1_4_2 )
5644 {
5645 addArgIfNotEmpty( arguments, "-linksourcetab", String.valueOf( sourcetab ) );
5646 }
5647 addArgIfNotEmpty( arguments, "-sourcetab", String.valueOf( sourcetab ), SINCE_JAVADOC_1_5 );
5648 }
5649
5650 addArgIf( arguments, nocomment, "-nocomment", SINCE_JAVADOC_1_4 );
5651
5652 addArgIf( arguments, nodeprecated, "-nodeprecated" );
5653
5654 addArgIf( arguments, nodeprecatedlist, "-nodeprecatedlist" );
5655
5656 addArgIf( arguments, nohelp, "-nohelp" );
5657
5658 addArgIf( arguments, noindex, "-noindex" );
5659
5660 addArgIf( arguments, nonavbar, "-nonavbar" );
5661
5662 addArgIf( arguments, nooverview, "-nooverview" );
5663
5664 addArgIfNotEmpty( arguments, "-noqualifier", JavadocUtil.quotedArgument( noqualifier ), SINCE_JAVADOC_1_4 );
5665
5666 addArgIf( arguments, nosince, "-nosince" );
5667
5668 addArgIf( arguments, notimestamp, "-notimestamp", SINCE_JAVADOC_1_5 );
5669
5670 addArgIf( arguments, notree, "-notree" );
5671
5672 addArgIfNotEmpty( arguments, "-packagesheader", JavadocUtil.quotedArgument( packagesheader ),
5673 SINCE_JAVADOC_1_4_2 );
5674
5675 if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5676 {
5677 addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_4 );
5678 }
5679
5680 addArgIf( arguments, serialwarn, "-serialwarn" );
5681
5682 addArgIf( arguments, splitindex, "-splitindex" );
5683
5684 Optional<File> stylesheetfile = getStylesheetFile( javadocOutputDirectory );
5685
5686 if ( stylesheetfile.isPresent() )
5687 {
5688 addArgIfNotEmpty( arguments, "-stylesheetfile",
5689 JavadocUtil.quotedPathArgument( stylesheetfile.get().getAbsolutePath() ) );
5690 }
5691
5692 addAddStyleSheets( arguments );
5693
5694 if ( StringUtils.isNotEmpty( sourcepath ) && !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5695 {
5696 addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_4 );
5697 }
5698
5699 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet ), SINCE_JAVADOC_1_4 );
5700 addTaglets( arguments );
5701 addTagletsFromTagletArtifacts( arguments );
5702 addArgIfNotEmpty( arguments, "-tagletpath", JavadocUtil.quotedPathArgument( getTagletPath() ),
5703 SINCE_JAVADOC_1_4 );
5704
5705 addTags( arguments );
5706
5707 addArgIfNotEmpty( arguments, "-top", JavadocUtil.quotedArgument( top ), false, false, SINCE_JAVADOC_1_6 );
5708
5709 addArgIf( arguments, use, "-use" );
5710
5711 addArgIf( arguments, version, "-version" );
5712
5713 addArgIfNotEmpty( arguments, "-windowtitle", JavadocUtil.quotedArgument( getWindowtitle() ), false, false );
5714 }
5715
5716
5717
5718
5719
5720
5721
5722 private void addGroups( List<String> arguments )
5723 throws MavenReportException
5724 {
5725 Set<Group> groups = collectGroups();
5726 if ( isEmpty( groups ) )
5727 {
5728 return;
5729 }
5730
5731 for ( Group group : groups )
5732 {
5733 if ( group == null || StringUtils.isEmpty( group.getTitle() ) || StringUtils.isEmpty(
5734 group.getPackages() ) )
5735 {
5736 if ( getLog().isWarnEnabled() )
5737 {
5738 getLog().warn( "A group option is empty. Ignore this option." );
5739 }
5740 }
5741 else
5742 {
5743 String groupTitle = StringUtils.replace( group.getTitle(), ",", "," );
5744 addArgIfNotEmpty( arguments, "-group",
5745 JavadocUtil.quotedArgument( groupTitle ) + " " + JavadocUtil.quotedArgument(
5746 group.getPackages() ), true );
5747 }
5748 }
5749 }
5750
5751
5752
5753
5754
5755
5756
5757 private void addTags( List<String> arguments )
5758 throws MavenReportException
5759 {
5760 final String lineSeparator;
5761 if ( javadocRuntimeVersion.isBefore( "9" ) )
5762 {
5763 lineSeparator = " ";
5764 }
5765 else
5766 {
5767 lineSeparator = " \\\\" + SystemUtils.LINE_SEPARATOR;
5768 }
5769
5770 for ( Tag tag : collectTags() )
5771 {
5772 if ( StringUtils.isEmpty( tag.getName() ) )
5773 {
5774 if ( getLog().isWarnEnabled() )
5775 {
5776 getLog().warn( "A tag name is empty. Ignore this option." );
5777 }
5778 }
5779 else
5780 {
5781 String value = "\"" + tag.getName();
5782 if ( StringUtils.isNotEmpty( tag.getPlacement() ) )
5783 {
5784 value += ":" + tag.getPlacement().replaceAll( "\\R", lineSeparator );
5785 if ( StringUtils.isNotEmpty( tag.getHead() ) )
5786 {
5787 value += ":" + tag.getHead().replaceAll( "\\R", lineSeparator );
5788 }
5789 }
5790 value += "\"";
5791 addArgIfNotEmpty( arguments, "-tag", value, SINCE_JAVADOC_1_4 );
5792 }
5793 }
5794 }
5795
5796
5797
5798
5799
5800
5801 private void addTaglets( List<String> arguments )
5802 {
5803 if ( taglets == null )
5804 {
5805 return;
5806 }
5807
5808 for ( Taglet taglet1 : taglets )
5809 {
5810 if ( ( taglet1 == null ) || ( StringUtils.isEmpty( taglet1.getTagletClass() ) ) )
5811 {
5812 if ( getLog().isWarnEnabled() )
5813 {
5814 getLog().warn( "A taglet option is empty. Ignore this option." );
5815 }
5816 }
5817 else
5818 {
5819 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet1.getTagletClass() ),
5820 SINCE_JAVADOC_1_4 );
5821 }
5822 }
5823 }
5824
5825
5826
5827
5828
5829
5830
5831
5832 private void addTagletsFromTagletArtifacts( List<String> arguments )
5833 throws MavenReportException
5834 {
5835 Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
5836 if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
5837 {
5838 tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
5839 }
5840
5841 if ( includeDependencySources )
5842 {
5843 try
5844 {
5845 resolveDependencyBundles();
5846 }
5847 catch ( IOException e )
5848 {
5849 throw new MavenReportException(
5850 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
5851 }
5852
5853 if ( isNotEmpty( dependencyJavadocBundles ) )
5854 {
5855 for ( JavadocBundle bundle : dependencyJavadocBundles )
5856 {
5857 JavadocOptions options = bundle.getOptions();
5858 if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
5859 {
5860 tArtifacts.addAll( options.getTagletArtifacts() );
5861 }
5862 }
5863 }
5864 }
5865
5866 if ( isEmpty( tArtifacts ) )
5867 {
5868 return;
5869 }
5870
5871 List<String> tagletsPath = new ArrayList<>();
5872
5873 for ( TagletArtifact aTagletArtifact : tArtifacts )
5874 {
5875 if ( ( StringUtils.isNotEmpty( aTagletArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
5876 aTagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty( aTagletArtifact.getVersion() ) ) )
5877 {
5878 Artifact artifact;
5879 try
5880 {
5881 artifact = createAndResolveArtifact( aTagletArtifact );
5882 }
5883 catch ( ArtifactResolverException e )
5884 {
5885 throw new MavenReportException( "Unable to resolve artifact:" + aTagletArtifact, e );
5886 }
5887
5888 tagletsPath.add( artifact.getFile().getAbsolutePath() );
5889 }
5890 }
5891
5892 tagletsPath = JavadocUtil.pruneFiles( tagletsPath );
5893
5894 for ( String tagletJar : tagletsPath )
5895 {
5896 if ( !tagletJar.toLowerCase( Locale.ENGLISH ).endsWith( ".jar" ) )
5897 {
5898 continue;
5899 }
5900
5901 List<String> tagletClasses;
5902 try
5903 {
5904 tagletClasses = JavadocUtil.getTagletClassNames( new File( tagletJar ) );
5905 }
5906 catch ( IOException e )
5907 {
5908 if ( getLog().isWarnEnabled() )
5909 {
5910 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5911 + "'. Try to specify them with <taglets/>." );
5912 }
5913 if ( getLog().isDebugEnabled() )
5914 {
5915 getLog().debug( "IOException: " + e.getMessage(), e );
5916 }
5917 continue;
5918 }
5919 catch ( ClassNotFoundException e )
5920 {
5921 if ( getLog().isWarnEnabled() )
5922 {
5923 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5924 + "'. Try to specify them with <taglets/>." );
5925 }
5926 if ( getLog().isDebugEnabled() )
5927 {
5928 getLog().debug( "ClassNotFoundException: " + e.getMessage(), e );
5929 }
5930 continue;
5931 }
5932 catch ( NoClassDefFoundError e )
5933 {
5934 if ( getLog().isWarnEnabled() )
5935 {
5936 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5937 + "'. Try to specify them with <taglets/>." );
5938 }
5939 if ( getLog().isDebugEnabled() )
5940 {
5941 getLog().debug( "NoClassDefFoundError: " + e.getMessage(), e );
5942 }
5943 continue;
5944 }
5945
5946 if ( tagletClasses != null && !tagletClasses.isEmpty() )
5947 {
5948 for ( String tagletClass : tagletClasses )
5949 {
5950 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( tagletClass ),
5951 SINCE_JAVADOC_1_4 );
5952 }
5953 }
5954 }
5955 }
5956
5957
5958
5959
5960
5961
5962
5963
5964 private void executeJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
5965 throws MavenReportException
5966 {
5967 if ( staleDataPath != null )
5968 {
5969 if ( !isUpToDate( cmd ) )
5970 {
5971 doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
5972 StaleHelper.writeStaleData( cmd, staleDataPath.toPath() );
5973 }
5974 }
5975 else
5976 {
5977 doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
5978 }
5979 }
5980
5981
5982
5983
5984
5985
5986
5987
5988 private boolean isUpToDate( Commandline cmd )
5989 throws MavenReportException
5990 {
5991 try
5992 {
5993 String curdata = StaleHelper.getStaleData( cmd );
5994 Path cacheData = staleDataPath.toPath();
5995 String prvdata;
5996 if ( Files.isRegularFile( cacheData ) )
5997 {
5998 prvdata = new String( Files.readAllBytes( cacheData ), StandardCharsets.UTF_8 );
5999 }
6000 else
6001 {
6002 prvdata = null;
6003 }
6004 if ( curdata.equals( prvdata ) )
6005 {
6006 getLog().info( "Skipping javadoc generation, everything is up to date." );
6007 return true;
6008 }
6009 else
6010 {
6011 if ( prvdata == null )
6012 {
6013 getLog().info( "No previous run data found, generating javadoc." );
6014 }
6015 else
6016 {
6017 getLog().info( "Configuration changed, re-generating javadoc." );
6018 }
6019 }
6020 }
6021 catch ( IOException e )
6022 {
6023 throw new MavenReportException( "Error checking uptodate status", e );
6024 }
6025 return false;
6026 }
6027
6028
6029
6030
6031
6032
6033
6034
6035 private void doExecuteJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
6036 throws MavenReportException
6037 {
6038 if ( getLog().isDebugEnabled() )
6039 {
6040
6041 getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
6042 }
6043
6044 String cmdLine = null;
6045 if ( debug )
6046 {
6047 cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
6048
6049 writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
6050 }
6051
6052 CommandLineUtils.StringStreamConsumer err = new JavadocUtil.JavadocOutputStreamConsumer();
6053 CommandLineUtils.StringStreamConsumer out = new JavadocUtil.JavadocOutputStreamConsumer();
6054 try
6055 {
6056 int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
6057
6058 String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() );
6059
6060 if ( exitCode != 0 )
6061 {
6062 if ( cmdLine == null )
6063 {
6064 cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
6065 }
6066 writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
6067
6068 if ( StringUtils.isNotEmpty( output ) && StringUtils.isEmpty( err.getOutput() )
6069 && isJavadocVMInitError( output ) )
6070 {
6071 throw new MavenReportException( output + '\n' + '\n' + JavadocUtil.ERROR_INIT_VM + '\n'
6072 + "Or, try to reduce the Java heap size for the Javadoc goal using "
6073 + "-Dminmemory=<size> and -Dmaxmemory=<size>." + '\n' + '\n' + "Command line was: " + cmdLine
6074 + '\n' + '\n' + "Refer to the generated Javadoc files in '" + javadocOutputDirectory
6075 + "' dir.\n" );
6076 }
6077
6078 if ( StringUtils.isNotEmpty( output ) )
6079 {
6080 getLog().info( output );
6081 }
6082
6083 StringBuilder msg = new StringBuilder( "\nExit code: " );
6084 msg.append( exitCode );
6085 if ( StringUtils.isNotEmpty( err.getOutput() ) )
6086 {
6087 msg.append( " - " ).append( err.getOutput() );
6088 }
6089 msg.append( '\n' );
6090 msg.append( "Command line was: " ).append( cmdLine ).append( '\n' ).append( '\n' );
6091
6092 msg.append( "Refer to the generated Javadoc files in '" ).append( javadocOutputDirectory )
6093 .append( "' dir.\n" );
6094
6095 throw new MavenReportException( msg.toString() );
6096 }
6097
6098 if ( StringUtils.isNotEmpty( output ) )
6099 {
6100 getLog().info( output );
6101 }
6102 }
6103 catch ( CommandLineException e )
6104 {
6105 throw new MavenReportException( "Unable to execute javadoc command: " + e.getMessage(), e );
6106 }
6107
6108
6109
6110
6111
6112 if ( containsWarnings( err.getOutput() ) )
6113 {
6114 if ( getLog().isWarnEnabled() )
6115 {
6116 getLog().warn( "Javadoc Warnings" );
6117
6118 StringTokenizer token = new StringTokenizer( err.getOutput(), "\n" );
6119 while ( token.hasMoreTokens() )
6120 {
6121 String current = token.nextToken().trim();
6122
6123 getLog().warn( current );
6124 }
6125 }
6126
6127 if ( failOnWarnings )
6128 {
6129 throw new MavenReportException( "Project contains Javadoc Warnings" );
6130 }
6131 }
6132 }
6133
6134 private boolean containsWarnings( String output )
6135 {
6136
6137 if ( this.javadocRuntimeVersion.isBefore( "17" ) )
6138 {
6139 return StringUtils.isNotEmpty( output );
6140 }
6141 else
6142 {
6143 return Arrays.stream( output.split( "\\R" ) )
6144 .reduce( ( first, second ) -> second )
6145 .filter( line -> line.matches( "\\d+ warnings?" ) )
6146 .isPresent();
6147 }
6148 }
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159 private int fixFrameInjectionBug( File javadocOutputDirectory, String outputEncoding )
6160 throws IOException
6161 {
6162 final String fixData;
6163
6164 try ( InputStream in = this.getClass().getResourceAsStream( "frame-injection-fix.txt" ) )
6165 {
6166 if ( in == null )
6167 {
6168 throw new FileNotFoundException( "Missing resource 'frame-injection-fix.txt' in classpath." );
6169 }
6170 fixData = org.codehaus.plexus.util.StringUtils
6171 .unifyLineSeparators( IOUtil.toString( in, "US-ASCII" ) ).trim();
6172 }
6173
6174 final DirectoryScanner ds = new DirectoryScanner();
6175 ds.setBasedir( javadocOutputDirectory );
6176 ds.setCaseSensitive( false );
6177 ds.setIncludes( new String[]{ "**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm" } );
6178 ds.addDefaultExcludes();
6179 ds.scan();
6180 int patched = 0;
6181 for ( String f : ds.getIncludedFiles() )
6182 {
6183 final File file = new File( javadocOutputDirectory, f );
6184
6185
6186 final String fileContents = FileUtils.fileRead( file, outputEncoding );
6187
6188 if ( !StringUtils.contains( fileContents, "function validURL(url) {" ) )
6189 {
6190
6191 final String patchedFileContents =
6192 StringUtils.replaceOnce( fileContents, "function loadFrames() {", fixData );
6193 if ( !patchedFileContents.equals( fileContents ) )
6194 {
6195 FileUtils.fileWrite( file, outputEncoding, patchedFileContents );
6196 patched++;
6197 }
6198 }
6199 }
6200 return patched;
6201 }
6202
6203
6204
6205
6206
6207
6208
6209
6210 private Optional<File> getResource( File outputFile, String inputResourceName )
6211 {
6212 if ( inputResourceName.startsWith( "/" ) )
6213 {
6214 inputResourceName = inputResourceName.replaceFirst( "//*", "" );
6215 }
6216
6217 List<String> classPath = new ArrayList<>();
6218 classPath.add( project.getBuild().getSourceDirectory() );
6219
6220 URL resourceURL = getResource( classPath, inputResourceName );
6221 if ( resourceURL != null )
6222 {
6223 getLog().debug( inputResourceName + " found in the main src directory of the project." );
6224 return Optional.of( FileUtils.toFile( resourceURL ) );
6225 }
6226
6227 classPath.clear();
6228 List<Resource> resources = project.getBuild().getResources();
6229 for ( Resource resource : resources )
6230 {
6231 classPath.add( resource.getDirectory() );
6232 }
6233 resourceURL = getResource( classPath, inputResourceName );
6234 if ( resourceURL != null )
6235 {
6236 getLog().debug( inputResourceName + " found in the main resources directories of the project." );
6237 return Optional.of( FileUtils.toFile( resourceURL ) );
6238 }
6239
6240 if ( javadocDirectory.exists() )
6241 {
6242 classPath.clear();
6243 classPath.add( javadocDirectory.getAbsolutePath() );
6244 resourceURL = getResource( classPath, inputResourceName );
6245 if ( resourceURL != null )
6246 {
6247 getLog().debug( inputResourceName + " found in the main javadoc directory of the project." );
6248 return Optional.of( FileUtils.toFile( resourceURL ) );
6249 }
6250 }
6251
6252 classPath.clear();
6253 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
6254 Plugin javadocPlugin = getPlugin( project, pluginId );
6255 if ( javadocPlugin != null && javadocPlugin.getDependencies() != null )
6256 {
6257 List<Dependency> dependencies = javadocPlugin.getDependencies();
6258 for ( Dependency dependency : dependencies )
6259 {
6260 ResourcesArtifact resourceArtifact = new ResourcesArtifact();
6261 resourceArtifact.setGroupId( dependency.getGroupId() );
6262 resourceArtifact.setArtifactId( dependency.getArtifactId() );
6263 resourceArtifact.setVersion( dependency.getVersion() );
6264 resourceArtifact.setClassifier( dependency.getClassifier() );
6265 Artifact artifact = null;
6266 try
6267 {
6268 artifact = createAndResolveArtifact( resourceArtifact );
6269 }
6270 catch ( Exception e )
6271 {
6272 logError( "Unable to retrieve the dependency: " + dependency + ". Ignored.", e );
6273 }
6274
6275 if ( artifact != null && artifact.getFile().exists() )
6276 {
6277 classPath.add( artifact.getFile().getAbsolutePath() );
6278 }
6279 }
6280 resourceURL = getResource( classPath, inputResourceName );
6281 if ( resourceURL != null )
6282 {
6283 getLog().debug( inputResourceName + " found in javadoc plugin dependencies." );
6284 try
6285 {
6286 JavadocUtil.copyResource( resourceURL, outputFile );
6287
6288 return Optional.of( outputFile );
6289 }
6290 catch ( IOException e )
6291 {
6292 logError( "IOException: " + e.getMessage(), e );
6293 }
6294 }
6295 }
6296
6297 getLog().warn( "Unable to find the resource '" + inputResourceName + "'. Using default Javadoc resources." );
6298
6299 return Optional.empty();
6300 }
6301
6302
6303
6304
6305
6306
6307
6308
6309 private URL getResource( final List<String> classPath, final String resource )
6310 {
6311 List<URL> urls = new ArrayList<>( classPath.size() );
6312 for ( String filename : classPath )
6313 {
6314 try
6315 {
6316 urls.add( new File( filename ).toURI().toURL() );
6317 }
6318 catch ( MalformedURLException e )
6319 {
6320 getLog().error( "MalformedURLException: " + e.getMessage() );
6321 }
6322 }
6323
6324 URLClassLoader javadocClassLoader = new URLClassLoader( urls.toArray( new URL[urls.size()] ), null );
6325 try
6326 {
6327 return javadocClassLoader.getResource( resource );
6328 }
6329 finally
6330 {
6331 try
6332 {
6333 javadocClassLoader.close();
6334 }
6335 catch ( IOException ex )
6336 {
6337
6338 }
6339 }
6340 }
6341
6342
6343
6344
6345
6346
6347 private String getFullJavadocGoal()
6348 {
6349 String javadocPluginVersion = null;
6350 String resource = "META-INF/maven/org.apache.maven.plugins/maven-javadoc-plugin/pom.properties";
6351 try ( InputStream resourceAsStream
6352 = AbstractJavadocMojo.class.getClassLoader().getResourceAsStream( resource ) )
6353 {
6354 if ( resourceAsStream != null )
6355 {
6356 Properties properties = new Properties();
6357 properties.load( resourceAsStream );
6358 if ( StringUtils.isNotEmpty( properties.getProperty( "version" ) ) )
6359 {
6360 javadocPluginVersion = properties.getProperty( "version" );
6361 }
6362 }
6363 }
6364 catch ( IOException e )
6365 {
6366
6367 }
6368
6369 StringBuilder sb = new StringBuilder();
6370
6371 sb.append( "org.apache.maven.plugins:maven-javadoc-plugin:" );
6372 if ( StringUtils.isNotEmpty( javadocPluginVersion ) )
6373 {
6374 sb.append( javadocPluginVersion ).append( ":" );
6375 }
6376
6377 if ( this instanceof TestJavadocReport )
6378 {
6379 sb.append( "test-javadoc" );
6380 }
6381 else
6382 {
6383 sb.append( "javadoc" );
6384 }
6385
6386 return sb.toString();
6387 }
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399 private List<OfflineLink> getModulesLinks()
6400 throws MavenReportException
6401 {
6402 List<MavenProject> aggregatedProjects = reactorProjects;
6403 if ( !detectOfflineLinks || isAggregator() || aggregatedProjects.isEmpty() )
6404 {
6405 return Collections.emptyList();
6406 }
6407
6408 getLog().debug( "Trying to add links for modules..." );
6409
6410 Set<String> dependencyArtifactIds = new HashSet<>();
6411 final Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
6412 for ( Artifact artifact : dependencyArtifacts )
6413 {
6414 dependencyArtifactIds.add( artifact.getId() );
6415 }
6416
6417 List<OfflineLink> modulesLinks = new ArrayList<>();
6418 String javadocDirRelative = PathUtils.toRelative( project.getBasedir(), getOutputDirectory() );
6419 for ( MavenProject p : aggregatedProjects )
6420 {
6421 if ( !dependencyArtifactIds.contains( p.getArtifact().getId() ) || ( p.getUrl() == null ) )
6422 {
6423 continue;
6424 }
6425
6426 File location = new File( p.getBasedir(), javadocDirRelative );
6427
6428 if ( !location.exists() )
6429 {
6430 if ( getLog().isDebugEnabled() )
6431 {
6432 getLog().debug( "Javadoc directory not found: " + location );
6433 }
6434
6435 String javadocGoal = getFullJavadocGoal();
6436 getLog().info(
6437 "The goal '" + javadocGoal + "' has not been previously called for the module: '" + p.getId()
6438 + "'. Trying to invoke it..." );
6439
6440 File invokerDir = new File( project.getBuild().getDirectory(), "invoker" );
6441 invokerDir.mkdirs();
6442 File invokerLogFile = FileUtils.createTempFile( "maven-javadoc-plugin", ".txt", invokerDir );
6443 try
6444 {
6445 JavadocUtil.invokeMaven( getLog(), new File( localRepository.getBasedir() ), p.getFile(),
6446 Collections.singletonList( javadocGoal ), null, invokerLogFile,
6447 session.getRequest().getGlobalSettingsFile() );
6448 }
6449 catch ( MavenInvocationException e )
6450 {
6451 logError( "MavenInvocationException: " + e.getMessage(), e );
6452
6453 String invokerLogContent = JavadocUtil.readFile( invokerLogFile, null );
6454
6455
6456
6457
6458 if ( invokerLogContent != null && invokerLogContent.contains( JavadocUtil.ERROR_INIT_VM ) )
6459 {
6460 throw new MavenReportException( e.getMessage(), e );
6461 }
6462 }
6463 finally
6464 {
6465
6466 if ( !location.exists() )
6467 {
6468 getLog().warn( "Creating fake javadoc directory to prevent repeated invocations: " + location );
6469 location.mkdirs();
6470 }
6471 }
6472 }
6473
6474 if ( location.exists() )
6475 {
6476 String url = getJavadocLink( p );
6477
6478 OfflineLink ol = new OfflineLink();
6479 ol.setUrl( url );
6480 ol.setLocation( location.getAbsolutePath() );
6481
6482 if ( getLog().isDebugEnabled() )
6483 {
6484 getLog().debug( "Added Javadoc offline link: " + url + " for the module: " + p.getId() );
6485 }
6486
6487 modulesLinks.add( ol );
6488 }
6489 }
6490
6491 return modulesLinks;
6492 }
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503 private List<String> getDependenciesLinks()
6504 {
6505 if ( !detectLinks )
6506 {
6507 return Collections.emptyList();
6508 }
6509
6510 getLog().debug( "Trying to add links for dependencies..." );
6511
6512 List<String> dependenciesLinks = new ArrayList<>();
6513
6514 final Set<Artifact> dependencies = project.getDependencyArtifacts();
6515 for ( Artifact artifact : dependencies )
6516 {
6517 if ( artifact.getFile() == null || !artifact.getFile().exists() )
6518 {
6519 continue;
6520 }
6521
6522 Optional<DependencyLink> depLink =
6523 this.dependencyLinks.stream().filter( d -> matches( d, artifact ) ).findAny();
6524
6525 final String url;
6526 final boolean detected;
6527 if ( depLink.isPresent() )
6528 {
6529 url = depLink.get().getUrl();
6530 detected = false;
6531 }
6532 else
6533 {
6534 try
6535 {
6536 MavenProject artifactProject =
6537 mavenProjectBuilder.build( artifact, getProjectBuildingRequest( project ) ).getProject();
6538
6539 url = getJavadocLink( artifactProject );
6540 detected = true;
6541 }
6542 catch ( ProjectBuildingException e )
6543 {
6544 logError( "ProjectBuildingException for " + artifact.toString() + ": " + e.getMessage(), e );
6545 continue;
6546 }
6547 }
6548
6549 if ( url != null && isValidJavadocLink( url, detected ) )
6550 {
6551 getLog().debug( "Added Javadoc link: " + url + " for " + artifact.getId() );
6552
6553 dependenciesLinks.add( url );
6554 }
6555 }
6556
6557 return dependenciesLinks;
6558 }
6559
6560 private boolean matches( DependencyLink d, Artifact artifact )
6561 {
6562 if ( d.getGroupId() != null && !d.getGroupId().equals( artifact.getGroupId() ) )
6563 {
6564 return false;
6565 }
6566 if ( d.getArtifactId() != null && !d.getArtifactId().equals( artifact.getArtifactId() ) )
6567 {
6568 return false;
6569 }
6570 if ( d.getClassifier() != null && !d.getClassifier().equals( artifact.getClassifier() ) )
6571 {
6572 return false;
6573 }
6574 return true;
6575 }
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588 protected final OfflineLink getDefaultJavadocApiLink()
6589 {
6590 if ( !detectJavaApiLink )
6591 {
6592 return null;
6593 }
6594
6595 final JavaVersion javaApiversion;
6596 if ( release != null )
6597 {
6598 javaApiversion = JavaVersion.parse( release );
6599 }
6600 else if ( source != null && !source.isEmpty() )
6601 {
6602 javaApiversion = JavaVersion.parse( source );
6603 }
6604 else
6605 {
6606 final String pluginId = "org.apache.maven.plugins:maven-compiler-plugin";
6607 String sourceConfigured = getPluginParameter( project, pluginId, "source" );
6608 if ( sourceConfigured != null )
6609 {
6610 javaApiversion = JavaVersion.parse( sourceConfigured );
6611 }
6612 else
6613 {
6614 getLog().debug( "No maven-compiler-plugin defined in ${build.plugins} or in "
6615 + "${project.build.pluginManagement} for the " + project.getId()
6616 + ". Added Javadoc API link according the javadoc executable version i.e.: "
6617 + javadocRuntimeVersion );
6618
6619 javaApiversion = javadocRuntimeVersion;
6620 }
6621 }
6622
6623 final String javaApiKey;
6624 if ( javaApiversion.asMajor().isAtLeast( "9" ) )
6625 {
6626 javaApiKey = "api_" + javaApiversion.asMajor();
6627 }
6628 else
6629 {
6630 javaApiKey = "api_1." + javaApiversion.asMajor().toString().charAt( 0 );
6631 }
6632
6633 final String javaApiLink;
6634 if ( javaApiLinks != null && javaApiLinks.containsKey( javaApiKey ) )
6635 {
6636 javaApiLink = javaApiLinks.getProperty( javaApiKey );
6637 }
6638 else if ( javaApiversion.isAtLeast( "16" ) )
6639 {
6640 javaApiLink = null;
6641 }
6642 else if ( javaApiversion.isAtLeast( "11" ) )
6643 {
6644 javaApiLink =
6645 String.format( "https://docs.oracle.com/en/java/javase/%s/docs/api/", javaApiversion.getValue( 1 ) );
6646 }
6647 else if ( javaApiversion.asMajor().isAtLeast( "6" ) )
6648 {
6649 javaApiLink =
6650 String.format( "https://docs.oracle.com/javase/%s/docs/api/", javaApiversion.asMajor().getValue( 1 ) );
6651 }
6652 else if ( javaApiversion.isAtLeast( "1.5" ) )
6653 {
6654 javaApiLink = "https://docs.oracle.com/javase/1.5.0/docs/api/";
6655 }
6656 else
6657 {
6658 javaApiLink = null;
6659 }
6660
6661 if ( getLog().isDebugEnabled() )
6662 {
6663 if ( javaApiLink != null )
6664 {
6665 getLog().debug( "Found Java API link: " + javaApiLink );
6666 }
6667 else
6668 {
6669 getLog().debug( "No Java API link found." );
6670 }
6671 }
6672
6673 if ( javaApiLink == null )
6674 {
6675 return null;
6676 }
6677
6678 final Path javaApiListFile;
6679 final String resourceName;
6680 if ( javaApiversion.isAtLeast( "10" ) )
6681 {
6682 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "element-list" );
6683 resourceName = "java-api-element-list-" + javaApiversion.toString().substring( 0, 2 );
6684 }
6685 else if ( javaApiversion.asMajor().isAtLeast( "9" ) )
6686 {
6687 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
6688 resourceName = "java-api-package-list-9";
6689 }
6690 else
6691 {
6692 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
6693 resourceName = "java-api-package-list-1." + javaApiversion.asMajor().toString().charAt( 0 );
6694 }
6695
6696 OfflineLink link = new OfflineLink();
6697 link.setLocation( javaApiListFile.getParent().toAbsolutePath().toString() );
6698 link.setUrl( javaApiLink );
6699
6700 InputStream in = this.getClass().getResourceAsStream( resourceName );
6701 if ( in != null )
6702 {
6703 try ( InputStream closableIS = in )
6704 {
6705
6706 Files.copy( closableIS, javaApiListFile, StandardCopyOption.REPLACE_EXISTING );
6707 }
6708 catch ( IOException ioe )
6709 {
6710 logError( "Can't get " + resourceName + ": " + ioe.getMessage(), ioe );
6711 return null;
6712 }
6713 }
6714
6715 return link;
6716 }
6717
6718
6719
6720
6721
6722
6723
6724
6725 private Set<String> followLinks( Set<String> links )
6726 {
6727 Set<String> redirectLinks = new LinkedHashSet<>( links.size() );
6728 for ( String link : links )
6729 {
6730 try
6731 {
6732 redirectLinks.add( JavadocUtil.getRedirectUrl( new URI( link ).toURL(), settings ).toString() );
6733 }
6734 catch ( Exception e )
6735 {
6736
6737 getLog().debug( "Could not follow " + link + ". Reason: " + e.getMessage() );
6738
6739
6740
6741
6742 redirectLinks.add( link );
6743 }
6744 }
6745 return redirectLinks;
6746 }
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757 protected boolean isValidJavadocLink( String link, boolean detecting )
6758 {
6759 try
6760 {
6761 final URI packageListUri;
6762 final URI elementListUri;
6763
6764 if ( link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "http:" ) || link.trim().toLowerCase(
6765 Locale.ENGLISH ).startsWith( "https:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith(
6766 "ftp:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "file:" ) )
6767 {
6768 packageListUri = new URI( link + '/' + PACKAGE_LIST );
6769 elementListUri = new URI( link + '/' + ELEMENT_LIST );
6770 }
6771 else
6772 {
6773
6774 File dir = new File( link );
6775 if ( !dir.isAbsolute() )
6776 {
6777 dir = new File( getOutputDirectory(), link );
6778 }
6779 if ( !dir.isDirectory() )
6780 {
6781 if ( detecting )
6782 {
6783 getLog().warn( "The given File link: " + dir + " is not a dir." );
6784 }
6785 else
6786 {
6787 getLog().error( "The given File link: " + dir + " is not a dir." );
6788 }
6789 }
6790 packageListUri = new File( dir, PACKAGE_LIST ).toURI();
6791 elementListUri = new File( dir, ELEMENT_LIST ).toURI();
6792 }
6793
6794
6795 try
6796 {
6797 if ( JavadocUtil.isValidElementList( elementListUri.toURL(), settings, validateLinks ) )
6798 {
6799 return true;
6800 }
6801 }
6802 catch ( IOException e )
6803 {
6804 }
6805
6806 if ( JavadocUtil.isValidPackageList( packageListUri.toURL(), settings, validateLinks ) )
6807 {
6808 return true;
6809 }
6810
6811 if ( getLog().isErrorEnabled() )
6812 {
6813 if ( detecting )
6814 {
6815 getLog().warn( "Invalid links: "
6816 + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
6817 }
6818 else
6819 {
6820 getLog().error( "Invalid links: "
6821 + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
6822 }
6823 }
6824
6825 return false;
6826 }
6827 catch ( URISyntaxException e )
6828 {
6829 if ( getLog().isErrorEnabled() )
6830 {
6831 if ( detecting )
6832 {
6833 getLog().warn( "Malformed link: " + e.getInput() + ". Ignored it." );
6834 }
6835 else
6836 {
6837 getLog().error( "Malformed link: " + e.getInput() + ". Ignored it." );
6838 }
6839 }
6840 return false;
6841 }
6842 catch ( IOException e )
6843 {
6844 if ( getLog().isErrorEnabled() )
6845 {
6846 if ( detecting )
6847 {
6848 getLog().warn( "Error fetching link: " + link + ". Ignored it." );
6849 }
6850 else
6851 {
6852 getLog().error( "Error fetching link: " + link + ". Ignored it." );
6853 }
6854 }
6855 return false;
6856 }
6857 }
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867 private void writeDebugJavadocScript( String cmdLine, File javadocOutputDirectory )
6868 {
6869 File commandLineFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
6870 commandLineFile.getParentFile().mkdirs();
6871
6872 try
6873 {
6874 FileUtils.fileWrite( commandLineFile.getAbsolutePath(), null , cmdLine );
6875
6876 if ( !SystemUtils.IS_OS_WINDOWS )
6877 {
6878 Runtime.getRuntime().exec( new String[]{ "chmod", "a+x", commandLineFile.getAbsolutePath() } );
6879 }
6880 }
6881 catch ( IOException e )
6882 {
6883 logError( "Unable to write '" + commandLineFile.getName() + "' debug script file", e );
6884 }
6885 }
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895 private boolean isJavadocVMInitError( String output )
6896 {
6897
6898
6899
6900
6901 return !( output.contains( "Javadoc" ) || output.contains( "javadoc" ) );
6902 }
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914 private static String getJavadocLink( MavenProject p )
6915 {
6916 if ( p.getUrl() == null )
6917 {
6918 return null;
6919 }
6920
6921 String url = cleanUrl( p.getUrl() );
6922 String destDir = "apidocs";
6923
6924 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
6925 String destDirConfigured = getPluginParameter( p, pluginId, "destDir" );
6926 if ( destDirConfigured != null )
6927 {
6928 destDir = destDirConfigured;
6929 }
6930
6931 return url + "/" + destDir;
6932 }
6933
6934
6935
6936
6937
6938
6939 private static String cleanUrl( String url )
6940 {
6941 if ( url == null )
6942 {
6943 return "";
6944 }
6945
6946 url = url.trim();
6947 while ( url.endsWith( "/" ) )
6948 {
6949 url = url.substring( 0, url.lastIndexOf( "/" ) );
6950 }
6951
6952 return url;
6953 }
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963 private static Plugin getPlugin( MavenProject p, String pluginId )
6964 {
6965 if ( ( p.getBuild() == null ) || ( p.getBuild().getPluginsAsMap() == null ) )
6966 {
6967 return null;
6968 }
6969
6970 Plugin plugin = p.getBuild().getPluginsAsMap().get( pluginId );
6971
6972 if ( ( plugin == null ) && ( p.getBuild().getPluginManagement() != null ) && (
6973 p.getBuild().getPluginManagement().getPluginsAsMap() != null ) )
6974 {
6975 plugin = p.getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
6976 }
6977
6978 return plugin;
6979 }
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989 private static String getPluginParameter( MavenProject p, String pluginId, String param )
6990 {
6991
6992 Plugin plugin = getPlugin( p, pluginId );
6993 if ( plugin != null )
6994 {
6995 Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
6996 if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
6997 && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
6998 {
6999 return xpp3Dom.getChild( param ).getValue();
7000 }
7001 }
7002
7003 return null;
7004 }
7005
7006
7007
7008
7009
7010
7011
7012
7013 protected final File getJavadocOptionsFile()
7014 {
7015 if ( javadocOptionsDir != null && !javadocOptionsDir.exists() )
7016 {
7017 javadocOptionsDir.mkdirs();
7018 }
7019
7020 return new File( javadocOptionsDir, "javadoc-options-" + getAttachmentClassifier() + ".xml" );
7021 }
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032 protected final JavadocOptions buildJavadocOptions()
7033 throws IOException
7034 {
7035 JavadocOptions options = new JavadocOptions();
7036
7037 options.setBootclasspathArtifacts( toList( bootclasspathArtifacts ) );
7038 options.setDocfilesSubdirsUsed( docfilessubdirs );
7039 options.setDocletArtifacts( toList( docletArtifact, docletArtifacts ) );
7040 options.setExcludedDocfilesSubdirs( excludedocfilessubdir );
7041 options.setExcludePackageNames( toList( excludePackageNames ) );
7042 options.setGroups( toList( groups ) );
7043 options.setLinks( links );
7044 options.setOfflineLinks( toList( offlineLinks ) );
7045 options.setResourcesArtifacts( toList( resourcesArtifacts ) );
7046 options.setTagletArtifacts( toList( tagletArtifact, tagletArtifacts ) );
7047 options.setTaglets( toList( taglets ) );
7048 options.setTags( toList( tags ) );
7049
7050 if ( getProject() != null && getJavadocDirectory() != null )
7051 {
7052 options.setJavadocResourcesDirectory(
7053 toRelative( getProject().getBasedir(), getJavadocDirectory().getAbsolutePath() ) );
7054 }
7055
7056 File optionsFile = getJavadocOptionsFile();
7057
7058 try ( Writer writer = WriterFactory.newXmlWriter( optionsFile ) )
7059 {
7060 new JavadocOptionsXpp3Writer().write( writer, options );
7061 }
7062
7063 return options;
7064 }
7065
7066
7067
7068
7069
7070
7071 protected String getAttachmentClassifier()
7072 {
7073 return JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER;
7074 }
7075
7076
7077
7078
7079
7080
7081
7082 protected void logError( String message, Throwable t )
7083 {
7084 if ( getLog().isDebugEnabled() )
7085 {
7086 getLog().error( message, t );
7087 }
7088 else
7089 {
7090 getLog().error( message );
7091 }
7092 }
7093
7094
7095
7096
7097
7098
7099 protected void failOnError( String prefix, Exception e )
7100 throws MojoExecutionException
7101 {
7102 if ( failOnError )
7103 {
7104 if ( e instanceof RuntimeException )
7105 {
7106 throw (RuntimeException) e;
7107 }
7108 throw new MojoExecutionException( prefix + ": " + e.getMessage(), e );
7109 }
7110
7111 getLog().error( prefix + ": " + e.getMessage(), e );
7112 }
7113
7114
7115
7116
7117
7118
7119 private List<MavenProject> getAggregatedProjects()
7120 {
7121 if ( this.reactorProjects == null )
7122 {
7123 return Collections.emptyList();
7124 }
7125 Map<Path, MavenProject> reactorProjectsMap = new HashMap<>();
7126 for ( MavenProject reactorProject : this.reactorProjects )
7127 {
7128 if ( !isSkippedJavadoc( reactorProject ) &&
7129 !isSkippedModule( reactorProject ) )
7130 {
7131 reactorProjectsMap.put( reactorProject.getBasedir().toPath(), reactorProject );
7132 }
7133 }
7134
7135 return new ArrayList<>( modulesForAggregatedProject( project, reactorProjectsMap ) );
7136 }
7137
7138
7139
7140
7141
7142 protected boolean isSkippedModule( MavenProject mavenProject )
7143 {
7144 if ( StringUtils.isEmpty( this.skippedModules ) )
7145 {
7146 return false;
7147 }
7148 List<String> modulesToSkip = Arrays.asList( StringUtils.split( this.skippedModules, ',' ) );
7149 return modulesToSkip.contains( mavenProject.getArtifactId() );
7150 }
7151
7152
7153
7154
7155
7156 protected boolean isSkippedJavadoc( MavenProject mavenProject )
7157 {
7158 String property = mavenProject.getProperties().getProperty( "maven.javadoc.skip" );
7159 if ( property != null )
7160 {
7161 boolean skip = BooleanUtils.toBoolean( property );
7162 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + skip );
7163 return skip;
7164 }
7165 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
7166 property = getPluginParameter( mavenProject, pluginId, "skip" );
7167 if ( property != null )
7168 {
7169 boolean skip = BooleanUtils.toBoolean( property );
7170 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + skip );
7171 return skip;
7172 }
7173 if ( mavenProject.getParent() != null )
7174 {
7175 return isSkippedJavadoc( mavenProject.getParent() );
7176 }
7177 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + false );
7178 return false;
7179 }
7180
7181 }