1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.doap;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.text.DateFormat;
29 import java.text.ParseException;
30 import java.text.SimpleDateFormat;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.Date;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.Set;
41 import java.util.TimeZone;
42
43 import org.apache.maven.artifact.Artifact;
44 import org.apache.maven.artifact.factory.ArtifactFactory;
45 import org.apache.maven.artifact.repository.ArtifactRepository;
46 import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata;
47 import org.apache.maven.artifact.repository.metadata.RepositoryMetadata;
48 import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager;
49 import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException;
50 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
51 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
52 import org.apache.maven.artifact.resolver.ArtifactResolver;
53 import org.apache.maven.model.Contributor;
54 import org.apache.maven.model.Developer;
55 import org.apache.maven.model.License;
56 import org.apache.maven.plugin.AbstractMojo;
57 import org.apache.maven.plugin.MojoExecutionException;
58 import org.apache.maven.plugin.doap.options.ASFExtOptions;
59 import org.apache.maven.plugin.doap.options.ASFExtOptionsUtil;
60 import org.apache.maven.plugin.doap.options.DoapArtifact;
61 import org.apache.maven.plugin.doap.options.DoapOptions;
62 import org.apache.maven.plugin.doap.options.ExtOptions;
63 import org.apache.maven.plugin.doap.options.Standard;
64 import org.apache.maven.plugins.annotations.Mojo;
65 import org.apache.maven.plugins.annotations.Parameter;
66 import org.apache.maven.project.MavenProject;
67 import org.apache.maven.project.MavenProjectBuilder;
68 import org.apache.maven.project.ProjectBuildingException;
69 import org.apache.maven.scm.manager.NoSuchScmProviderException;
70 import org.apache.maven.scm.manager.ScmManager;
71 import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
72 import org.apache.maven.scm.repository.ScmRepository;
73 import org.apache.maven.scm.repository.ScmRepositoryException;
74 import org.apache.maven.settings.Settings;
75 import org.codehaus.plexus.i18n.I18N;
76 import org.codehaus.plexus.util.FileUtils;
77 import org.codehaus.plexus.util.StringUtils;
78 import org.codehaus.plexus.util.WriterFactory;
79 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
80 import org.codehaus.plexus.util.xml.XMLWriter;
81
82
83
84
85
86
87
88
89
90
91
92
93
94 @Mojo(name = "generate")
95 public class DoapMojo extends AbstractMojo {
96
97
98
99 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
100
101
102
103
104 private static final DateFormat REPOSITORY_DATE_FORMAT;
105
106
107
108
109 private static final DateFormat DOAP_DATE_FORMAT;
110
111 static {
112 REPOSITORY_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss", Locale.ENGLISH);
113 REPOSITORY_DATE_FORMAT.setTimeZone(UTC_TIME_ZONE);
114
115 DOAP_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
116 DOAP_DATE_FORMAT.setTimeZone(UTC_TIME_ZONE);
117 }
118
119
120
121
122
123
124
125
126
127
128 @Inject
129 private ScmManager scmManager;
130
131
132
133
134
135
136 @Inject
137 private ArtifactFactory artifactFactory;
138
139
140
141
142
143
144 @Inject
145 private RepositoryMetadataManager repositoryMetadataManager;
146
147
148
149
150
151
152 @Inject
153 private I18N i18n;
154
155
156
157
158
159
160
161
162 @Parameter(defaultValue = "${project}", readonly = true, required = true)
163 private MavenProject project;
164
165
166
167
168 @Parameter(property = "doapFile", defaultValue = "doap_${project.artifactId}.rdf", required = true)
169 private String doapFile;
170
171
172
173
174
175
176 @Parameter(defaultValue = "${project.reporting.outputDirectory}", required = true)
177 private String outputDirectory;
178
179
180
181
182
183
184 @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
185 private ArtifactRepository localRepository;
186
187
188
189
190
191
192 @Parameter(defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true)
193 private List<ArtifactRepository> remoteRepositories;
194
195
196
197
198
199
200 @Inject
201 private ArtifactFactory factory;
202
203
204
205
206
207
208 @Inject
209 private MavenProjectBuilder mavenProjectBuilder;
210
211
212
213
214
215
216 @Inject
217 private ArtifactResolver resolver;
218
219
220
221
222
223
224 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
225 protected Settings settings;
226
227
228
229
230
231
232
233
234
235
236
237 @Parameter(property = "category")
238 private String category;
239
240
241
242
243
244
245
246 @Parameter(property = "language")
247 private String language;
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 @Parameter(property = "doapOptions")
266 private DoapOptions doapOptions;
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 @Parameter(property = "asfExtOptions")
294 private ASFExtOptions asfExtOptions;
295
296
297
298
299
300
301
302
303
304 @Parameter(property = "lang", defaultValue = "en", required = true)
305 private String lang;
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323 @Parameter(property = "about", defaultValue = "${project.url}")
324 private String about;
325
326
327
328
329
330
331 @Parameter(defaultValue = "true")
332 private boolean validate;
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 @Parameter
352 private DoapArtifact artifact;
353
354
355
356
357
358
359 @Parameter(property = "maven.doap.skip", defaultValue = "false")
360 private boolean skip;
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 @Parameter(property = "extOptions")
383 private ExtOptions[] extOptions;
384
385
386
387
388
389
390 private UserMessages messages = new UserMessages();
391
392
393
394
395
396
397
398
399 public void execute() throws MojoExecutionException {
400 if (skip) {
401 getLog().info("Skipping DOAP generation");
402 return;
403 }
404
405
406 if (artifact != null) {
407 MavenProject givenProject = getMavenProject(artifact);
408 if (givenProject != null) {
409 File outDir = new File(outputDirectory);
410 if (!outDir.isAbsolute()) {
411 outDir = new File(project.getBasedir(), outputDirectory);
412 }
413 File outFile = new File(outDir, artifact.getDoapFileName());
414 writeDoapFile(givenProject, outFile);
415 return;
416 }
417 }
418
419
420 File outFile = new File(doapFile);
421 if (!outFile.isAbsolute()) {
422 outFile = new File(project.getBasedir(), doapFile);
423 }
424 if (!doapFile.replaceAll("\\\\", "/").contains("/")) {
425 File outDir = new File(outputDirectory);
426 if (!outDir.isAbsolute()) {
427 outDir = new File(project.getBasedir(), outputDirectory);
428 }
429 outFile = new File(outDir, doapFile);
430 }
431 writeDoapFile(project, outFile);
432 }
433
434
435
436
437
438
439
440
441
442
443 private MavenProject getMavenProject(DoapArtifact artifact) {
444 if (artifact == null) {
445 return null;
446 }
447
448 if (StringUtils.isEmpty(artifact.getGroupId())
449 || StringUtils.isEmpty(artifact.getArtifactId())
450 || StringUtils.isEmpty(artifact.getVersion())) {
451 getLog().warn("Missing groupId or artifactId or version in <artifact/> parameter, ignored it.");
452 return null;
453 }
454
455 getLog().info("Using artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
456 + artifact.getVersion());
457
458 try {
459 Artifact art = factory.createProjectArtifact(
460 artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), Artifact.SCOPE_COMPILE);
461
462 if (art.getFile() == null) {
463 MavenProject proj = mavenProjectBuilder.buildFromRepository(art, remoteRepositories, localRepository);
464 art = proj.getArtifact();
465
466 resolver.resolve(art, remoteRepositories, localRepository);
467
468 return proj;
469 }
470 } catch (ArtifactResolutionException e) {
471 getLog().error("ArtifactResolutionException: " + e.getMessage() + "\nIgnored <artifact/> parameter.");
472 } catch (ArtifactNotFoundException e) {
473 getLog().error("ArtifactNotFoundException: " + e.getMessage() + "\nIgnored <artifact/> parameter.");
474 } catch (ProjectBuildingException e) {
475 getLog().error("ProjectBuildingException: " + e.getMessage() + "\nIgnored <artifact/> parameter.");
476 }
477
478 return null;
479 }
480
481
482
483
484
485
486
487
488 private void writeDoapFile(MavenProject project, File outputFile) throws MojoExecutionException {
489
490
491
492
493 if (!asfExtOptions.isIncluded() && ASFExtOptionsUtil.isASFProject(project)) {
494 getLog().info("This project is an ASF project, ASF Extensions to DOAP will be added.");
495 asfExtOptions.setIncluded(true);
496 }
497
498
499
500
501
502 Writer w;
503 try {
504 if (!outputFile.getParentFile().exists()) {
505 FileUtils.mkdir(outputFile.getParentFile().getAbsolutePath());
506 }
507
508 w = WriterFactory.newXmlWriter(outputFile);
509 } catch (IOException e) {
510 throw new MojoExecutionException("Error creating DOAP file " + outputFile.getAbsolutePath(), e);
511 }
512
513 try {
514 doWrite(project, outputFile, w);
515 } finally {
516
517 try {
518 w.close();
519 } catch (IOException e) {
520 throw new MojoExecutionException("Error when closing the writer.", e);
521 }
522 }
523
524 if (!messages.getWarnMessages().isEmpty()) {
525 for (String warn : messages.getWarnMessages()) {
526 getLog().warn(warn);
527 }
528 }
529
530 if (!messages.getErrorMessages().isEmpty()) {
531 getLog().error("");
532 for (String error : messages.getErrorMessages()) {
533 getLog().error(error);
534 }
535 getLog().error("");
536
537 if (ASFExtOptionsUtil.isASFProject(project)) {
538 getLog().error("For more information about the errors and possible solutions, "
539 + "please read the plugin documentation:");
540 getLog().error("http://maven.apache.org/plugins/maven-doap-plugin/usage.html#DOAP_ASF_Configuration");
541 throw new MojoExecutionException("The generated DOAP doesn't respect ASF rules, see above.");
542 }
543 }
544
545 if (validate) {
546 List<String> errors = DoapUtil.validate(outputFile);
547 if (!errors.isEmpty()) {
548 getLog().error("");
549 for (String error : errors) {
550 getLog().error(error);
551 }
552 getLog().error("");
553
554 throw new MojoExecutionException("Error parsing the generated DOAP file, see above.");
555 }
556 }
557 }
558
559 private void doWrite(MavenProject project, File outputFile, Writer w) throws MojoExecutionException {
560 if (asfExtOptions.isIncluded()) {
561 getLog().info("Generating an ASF DOAP file " + outputFile.getAbsolutePath());
562 } else {
563 getLog().info("Generating a pure DOAP file " + outputFile.getAbsolutePath());
564 }
565
566 XMLWriter writer = new PrettyPrintXMLWriter(w, project.getModel().getModelEncoding(), null);
567
568
569
570
571
572 DoapUtil.writeHeader(writer);
573
574
575 DoapUtil.writeStartElement(writer, "rdf", "RDF");
576 if (Arrays.binarySearch(Locale.getISOLanguages(), lang) < 0) {
577 messages.addMessage(new String[] {"doapOptions", "lang"}, lang, UserMessages.INVALID_ISO_DATE);
578 throw new MojoExecutionException(messages.getErrorMessages().get(0));
579 }
580 writer.addAttribute("xml:lang", lang);
581 if (StringUtils.isEmpty(doapOptions.getXmlnsNamespaceURI())) {
582 messages.addMessage(new String[] {"doapOptions", "xmlnsNamespaceURI"}, null, UserMessages.REQUIRED);
583 throw new MojoExecutionException(messages.getErrorMessages().get(0));
584 }
585 writer.addAttribute(
586 "xmlns" + (StringUtils.isEmpty(doapOptions.getXmlnsPrefix()) ? "" : ":" + doapOptions.getXmlnsPrefix()),
587 doapOptions.getXmlnsNamespaceURI());
588 writer.addAttribute("xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
589 writer.addAttribute("xmlns:foaf", "http://xmlns.com/foaf/0.1/");
590 if (asfExtOptions.isIncluded()) {
591 if (StringUtils.isEmpty(asfExtOptions.getXmlnsPrefix())) {
592 messages.addMessage(new String[] {"doapOptions", "xmlnsPrefix"}, null, UserMessages.REQUIRED);
593 throw new MojoExecutionException(messages.getErrorMessages().get(0));
594 }
595 if (StringUtils.isEmpty(asfExtOptions.getXmlnsNamespaceURI())) {
596 messages.addMessage(new String[] {"doapOptions", "xmlnsNamespaceURI"}, null, UserMessages.REQUIRED);
597 }
598 writer.addAttribute(
599 "xmlns"
600 + (StringUtils.isEmpty(asfExtOptions.getXmlnsPrefix())
601 ? ""
602 : ":" + asfExtOptions.getXmlnsPrefix()),
603 asfExtOptions.getXmlnsNamespaceURI());
604 }
605 if (extOptions != null
606 && extOptions.length > 0
607 && !extOptions[0].getExtensions().isEmpty()) {
608 for (ExtOptions extOption : extOptions) {
609 if (StringUtils.isEmpty(extOption.getXmlnsPrefix())) {
610 messages.addMessage(
611 new String[] {"extOptions", "extOption", "xmlnsPrefix"}, null, UserMessages.REQUIRED);
612 throw new MojoExecutionException(messages.getErrorMessages().get(0));
613 }
614 if (StringUtils.isEmpty(extOption.getXmlnsNamespaceURI())) {
615 messages.addMessage(
616 new String[] {"extOptions", "extOption", "xmlnsNamespaceURI"}, null, UserMessages.REQUIRED);
617 throw new MojoExecutionException(messages.getErrorMessages().get(0));
618 }
619 writer.addAttribute(
620 "xmlns"
621 + (StringUtils.isEmpty(extOption.getXmlnsPrefix())
622 ? ""
623 : ":" + extOption.getXmlnsPrefix()),
624 extOption.getXmlnsNamespaceURI());
625 }
626 }
627
628
629 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "Project");
630 boolean added = false;
631 if (artifact != null) {
632 String about = project.getUrl();
633
634 if (about != null && !about.isEmpty()) {
635 try {
636 new URL(about);
637
638 writer.addAttribute("rdf:about", about);
639 added = true;
640 } catch (MalformedURLException e) {
641
642 }
643 }
644
645 if (!added) {
646 messages.getWarnMessages()
647 .add("The project's url defined from " + artifact.toConfiguration()
648 + " is empty or not a valid URL, using <about/> parameter.");
649 }
650 }
651
652 if (!added) {
653 if (about != null && !about.isEmpty()) {
654 try {
655 new URL(about);
656
657 writer.addAttribute("rdf:about", about);
658 } catch (MalformedURLException e) {
659 messages.addMessage(new String[] {"about"}, about, UserMessages.INVALID_URL);
660 }
661 added = true;
662 }
663 }
664
665 if (!added) {
666 messages.addMessage(new String[] {"about"}, null, UserMessages.RECOMMENDED);
667 }
668
669
670 writeName(writer, project);
671
672
673 writeDescription(writer, project);
674
675
676 writeImplements(writer);
677
678
679 writeAudience(writer);
680
681
682 writeVendor(writer, project);
683
684
685 writeCreated(writer, project);
686
687
688 writeHomepage(writer, project);
689
690
691 writeBlog(writer);
692
693
694 writeLicenses(writer, project);
695
696
697 writeProgrammingLanguage(writer, project);
698
699
700 writeCategory(writer, project);
701
702
703 writeOS(writer, project);
704
705
706 writePlateform(writer);
707
708
709 writeLanguage(writer);
710
711
712 writeSourceRepositories(writer, project);
713
714
715 writeBugDatabase(writer, project);
716
717
718 writeMailingList(writer, project);
719
720
721 writeDownloadPage(writer, project);
722
723
724 writeScreenshots(writer, project);
725
726
727 writeServiceEndpoint(writer);
728
729
730 writeWiki(writer, project);
731
732
733 writeReleases(writer, project);
734
735
736 List<Developer> developers = project.getDevelopers();
737 writeContributors(writer, new ArrayList<Contributor>(developers));
738
739
740 List<Contributor> contributors = project.getContributors();
741 writeContributors(writer, contributors);
742
743
744 Map<Object, String> map = doapOptions.getExtra();
745 writeExtra(writer, project, "Extra DOAP vocabulary.", map, doapOptions.getXmlnsPrefix());
746
747
748 writeASFext(writer, project);
749
750
751 writeExtensions(writer);
752
753 writer.endElement();
754
755 writeOrganizations(writer);
756
757 writer.endElement();
758 }
759
760
761
762
763
764
765
766
767 private void writeName(XMLWriter writer, MavenProject project) {
768 String name = DoapUtil.interpolate(doapOptions.getName(), project, settings);
769 if (name == null || name.isEmpty()) {
770 messages.addMessage(
771 new String[] {"doapOptions", "name"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
772 return;
773 }
774
775 DoapUtil.writeComment(writer, "A name of something.");
776 if (ASFExtOptionsUtil.isASFProject(project)
777 && !name.toLowerCase(Locale.ENGLISH).startsWith("apache")) {
778 name = "Apache " + name;
779 }
780 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "name", name);
781 }
782
783
784
785
786
787
788
789
790
791 private void writeDescription(XMLWriter writer, MavenProject project) {
792 boolean addComment = false;
793 String description = DoapUtil.interpolate(doapOptions.getDescription(), project, settings);
794 if (description == null || description.isEmpty()) {
795 messages.addMessage(
796 new String[] {"doapOptions", "description"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
797 } else {
798 DoapUtil.writeComment(writer, "Plain text description of a project, of 2-4 sentences in length.");
799 addComment = true;
800 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "description", description, lang);
801 }
802
803 String comment = "Short plain text description of a project.";
804 String shortdesc = DoapUtil.interpolate(doapOptions.getShortdesc(), project, settings);
805 if (shortdesc == null || shortdesc.isEmpty()) {
806 messages.addMessage(
807 new String[] {"doapOptions", "shortdesc"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
808 return;
809 }
810 if (description.equals(shortdesc)) {
811
812 String sentence = StringUtils.split(shortdesc, ".")[0];
813 if (StringUtils.split(sentence, " ").length > 10) {
814 messages.addMessage(new String[] {"doapOptions", "shortdesc"}, null, UserMessages.SHORT_DESC_TOO_LONG);
815 return;
816 }
817 if (!addComment) {
818 DoapUtil.writeComment(writer, comment);
819 }
820 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "shortdesc", sentence, lang);
821 return;
822 }
823 if (!addComment) {
824 DoapUtil.writeComment(writer, comment);
825 }
826 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "shortdesc", shortdesc, lang);
827 }
828
829
830
831
832
833
834
835
836 private void writeCreated(XMLWriter writer, MavenProject project) {
837 String created = DoapUtil.interpolate(doapOptions.getCreated(), project, settings);
838 if (created == null || created.isEmpty()) {
839 messages.addMessage(
840 new String[] {"doapOptions", "created"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
841 return;
842 }
843
844 try {
845 DOAP_DATE_FORMAT.parse(created);
846 } catch (ParseException e) {
847 messages.addMessage(new String[] {"doapOptions", "created"}, null, UserMessages.INVALID_DATE);
848 return;
849 }
850
851 DoapUtil.writeComment(writer, "Date when something was created, in YYYY-MM-DD form. e.g. 2004-04-05");
852 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "created", created);
853 }
854
855
856
857
858
859
860
861
862
863 private void writeHomepage(XMLWriter writer, MavenProject project) {
864 String homepage = DoapUtil.interpolate(doapOptions.getHomepage(), project, settings);
865 if (homepage == null || homepage.isEmpty()) {
866 messages.addMessage(
867 new String[] {"doapOptions", "homepage"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
868 } else {
869 try {
870 new URL(homepage);
871
872 DoapUtil.writeComment(writer, "URL of a project's homepage, associated with exactly one project.");
873 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "homepage", homepage);
874 } catch (MalformedURLException e) {
875 messages.addMessage(new String[] {"doapOptions", "homepage"}, homepage, UserMessages.INVALID_URL);
876 }
877 }
878
879 if (StringUtils.isNotEmpty(doapOptions.getOldHomepage())) {
880 String oldHomepage = DoapUtil.interpolate(doapOptions.getOldHomepage(), project, settings);
881 if (oldHomepage == null || oldHomepage.isEmpty()) {
882 return;
883 }
884
885 try {
886 new URL(oldHomepage);
887
888 DoapUtil.writeComment(writer, "URL of a project's past homepage, associated with exactly one project.");
889 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "old-homepage", oldHomepage);
890 } catch (MalformedURLException e) {
891 messages.addMessage(new String[] {"doapOptions", "oldHomepage"}, oldHomepage, UserMessages.INVALID_URL);
892 }
893 }
894 }
895
896
897
898
899
900
901
902
903
904 private void writeProgrammingLanguage(XMLWriter writer, MavenProject project) {
905 if (StringUtils.isEmpty(doapOptions.getProgrammingLanguage()) && (language == null || language.isEmpty())) {
906 messages.addMessage(
907 new String[] {"doapOptions", "programmingLanguage"},
908 null,
909 UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
910 return;
911 }
912
913 boolean addComment = false;
914 String comment = "Programming language.";
915 if (language != null && !language.isEmpty())
916 {
917 getLog().warn("The <language/> parameter is deprecated, please use "
918 + messages.toConfiguration(new String[] {"doapOptions", "programmingLanguage"}, null)
919 + " parameter instead of.");
920
921 language = language.trim();
922
923 if (asfExtOptions.isIncluded()) {
924 String asfLanguage = ASFExtOptionsUtil.getProgrammingLanguageSupportedByASF(language);
925 if (asfLanguage == null) {
926 messages.getErrorMessages()
927 .add("The deprecated " + messages.toConfiguration(new String[] {"language"}, language)
928 + " parameter is not supported by ASF. Should be one of "
929 + Arrays.toString(ASFExtOptionsUtil.PROGRAMMING_LANGUAGES));
930 } else {
931 DoapUtil.writeComment(writer, comment);
932 addComment = true;
933 DoapUtil.writeElement(
934 writer, doapOptions.getXmlnsPrefix(), "programming-language", asfLanguage.trim());
935 }
936 } else {
937 DoapUtil.writeComment(writer, comment);
938 addComment = true;
939 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "programming-language", language.trim());
940 }
941 }
942
943 if (StringUtils.isNotEmpty(doapOptions.getProgrammingLanguage())) {
944 String[] languages = StringUtils.split(doapOptions.getProgrammingLanguage(), ",");
945 for (String language : languages) {
946 language = language.trim();
947
948 if (asfExtOptions.isIncluded()) {
949 String asfLanguage = ASFExtOptionsUtil.getProgrammingLanguageSupportedByASF(language);
950 if (asfLanguage == null) {
951 messages.getErrorMessages()
952 .add("The "
953 + messages.toConfiguration(
954 new String[] {"doapOptions", "programmingLanguage"}, language)
955 + " parameter is not supported by ASF. "
956 + "Should be one of "
957 + Arrays.toString(ASFExtOptionsUtil.PROGRAMMING_LANGUAGES));
958 } else {
959 if (!addComment) {
960 DoapUtil.writeComment(writer, comment);
961 addComment = true;
962 }
963 DoapUtil.writeElement(
964 writer, doapOptions.getXmlnsPrefix(), "programming-language", asfLanguage);
965 }
966 } else {
967 if (!addComment) {
968 DoapUtil.writeComment(writer, comment);
969 addComment = true;
970 }
971 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "programming-language", language);
972 }
973 }
974 }
975 }
976
977
978
979
980
981
982
983
984 private void writeCategory(XMLWriter writer, MavenProject project) {
985 if (StringUtils.isEmpty(doapOptions.getCategory()) && (category == null || category.isEmpty())) {
986 messages.addMessage(
987 new String[] {"doapOptions", "category"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
988 return;
989 }
990
991
992 boolean addComment = false;
993 String comment = "A category of project.";
994 if (category != null && !category.isEmpty())
995 {
996 getLog().warn("The <category/> parameter is deprecated, please use "
997 + messages.toConfiguration(new String[] {"doapOptions", "category"}, null)
998 + " parameter instead of.");
999
1000 category = category.trim();
1001
1002 if (asfExtOptions.isIncluded()) {
1003 String asfCategory = ASFExtOptionsUtil.getCategorySupportedByASF(category);
1004 if (asfCategory == null) {
1005 messages.getErrorMessages()
1006 .add("The deprecated " + messages.toConfiguration(new String[] {"category"}, category)
1007 + " parameter is not supported by ASF. Should be one of "
1008 + Arrays.toString(ASFExtOptionsUtil.CATEGORIES));
1009 } else {
1010 DoapUtil.writeComment(writer, comment);
1011 addComment = true;
1012 DoapUtil.writeRdfResourceElement(
1013 writer,
1014 doapOptions.getXmlnsPrefix(),
1015 "category",
1016 ASFExtOptionsUtil.CATEGORY_RESOURCE + asfCategory);
1017 }
1018 } else {
1019 DoapUtil.writeComment(writer, comment);
1020 addComment = true;
1021 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "category", category);
1022 }
1023 }
1024
1025 if (StringUtils.isNotEmpty(doapOptions.getCategory())) {
1026 String[] categories = StringUtils.split(doapOptions.getCategory(), ",");
1027 for (String category : categories) {
1028 category = category.trim();
1029
1030 if (asfExtOptions.isIncluded()) {
1031 String asfCategory = ASFExtOptionsUtil.getCategorySupportedByASF(category);
1032 if (asfCategory == null) {
1033 messages.getErrorMessages()
1034 .add("The "
1035 + messages.toConfiguration(new String[] {"doapOptions", "category"}, category)
1036 + " parameter is not supported by ASF. Should be one of "
1037 + Arrays.toString(ASFExtOptionsUtil.CATEGORIES));
1038 } else {
1039 if (!addComment) {
1040 DoapUtil.writeComment(writer, comment);
1041 addComment = true;
1042 }
1043 DoapUtil.writeRdfResourceElement(
1044 writer,
1045 doapOptions.getXmlnsPrefix(),
1046 "category",
1047 ASFExtOptionsUtil.CATEGORY_RESOURCE + asfCategory);
1048 }
1049 } else {
1050 if (!addComment) {
1051 DoapUtil.writeComment(writer, comment);
1052 addComment = true;
1053 }
1054 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "category", category);
1055 }
1056 }
1057 }
1058 }
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068 private void writeDownloadPage(XMLWriter writer, MavenProject project) {
1069 String downloadPage = DoapUtil.interpolate(doapOptions.getDownloadPage(), project, settings);
1070 if (downloadPage == null || downloadPage.isEmpty()) {
1071 messages.addMessage(
1072 new String[] {"doapOptions", "downloadPage"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1073 return;
1074 }
1075
1076 try {
1077 new URL(downloadPage);
1078
1079 DoapUtil.writeComment(writer, "Download page.");
1080 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "download-page", downloadPage);
1081 } catch (MalformedURLException e) {
1082 messages.addMessage(new String[] {"doapOptions", "downloadPage"}, downloadPage, UserMessages.INVALID_URL);
1083 }
1084
1085 if (StringUtils.isNotEmpty(doapOptions.getDownloadMirror())) {
1086 boolean addComment = false;
1087 String[] downloadMirrors = StringUtils.split(doapOptions.getDownloadMirror(), ",");
1088 for (String downloadMirror : downloadMirrors) {
1089 downloadMirror = downloadMirror.trim();
1090
1091 try {
1092 new URL(downloadMirror);
1093
1094 if (!addComment) {
1095 DoapUtil.writeComment(writer, "Mirror of software download web page.");
1096 addComment = true;
1097 }
1098 DoapUtil.writeRdfResourceElement(
1099 writer, doapOptions.getXmlnsPrefix(), "download-mirror", downloadMirror);
1100 } catch (MalformedURLException e) {
1101 messages.addMessage(
1102 new String[] {"doapOptions", "downloadMirror"}, downloadMirror, UserMessages.INVALID_URL);
1103 }
1104 }
1105 }
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115 private void writeOS(XMLWriter writer, MavenProject project) {
1116 String osList = DoapUtil.interpolate(doapOptions.getOs(), project, settings);
1117 if (osList == null || osList.isEmpty()) {
1118 return;
1119 }
1120
1121 DoapUtil.writeComment(writer, "Operating system that a project is limited to.");
1122 String[] oses = StringUtils.split(osList, ",");
1123 for (String os : oses) {
1124 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "os", os.trim());
1125 }
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135 private void writeScreenshots(XMLWriter writer, MavenProject project) {
1136 String screenshots = DoapUtil.interpolate(doapOptions.getScreenshots(), project, settings);
1137 if (screenshots == null || screenshots.isEmpty()) {
1138 return;
1139 }
1140
1141 screenshots = screenshots.trim();
1142 try {
1143 new URL(screenshots);
1144 } catch (MalformedURLException e) {
1145 messages.addMessage(new String[] {"doapOptions", "screenshots"}, screenshots, UserMessages.INVALID_URL);
1146 return;
1147 }
1148
1149 DoapUtil.writeComment(writer, "Web page with screenshots of project.");
1150 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "screenshots", screenshots);
1151 }
1152
1153
1154
1155
1156
1157
1158
1159
1160 private void writeWiki(XMLWriter writer, MavenProject project) {
1161 String wiki = DoapUtil.interpolate(doapOptions.getWiki(), project, settings);
1162 if (wiki == null || wiki.isEmpty()) {
1163 return;
1164 }
1165
1166 wiki = wiki.trim();
1167 try {
1168 new URL(wiki);
1169 } catch (MalformedURLException e) {
1170 messages.addMessage(new String[] {"doapOptions", "wiki"}, wiki, UserMessages.INVALID_URL);
1171 return;
1172 }
1173
1174 DoapUtil.writeComment(writer, "URL of Wiki for collaborative discussion of project.");
1175 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "wiki", wiki);
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185 private void writeLicenses(XMLWriter writer, MavenProject project) {
1186 String license = DoapUtil.interpolate(doapOptions.getLicense(), project, settings);
1187 if (license == null || license.isEmpty()) {
1188 boolean added = false;
1189 @SuppressWarnings("unchecked")
1190 List<License> licenses = project.getLicenses();
1191 if (licenses.size() > 1) {
1192 for (int i = 1; i < licenses.size(); i++) {
1193 if (StringUtils.isEmpty(licenses.get(i).getUrl())) {
1194 continue;
1195 }
1196
1197 String licenseUrl = licenses.get(i).getUrl().trim();
1198 try {
1199 new URL(licenseUrl);
1200
1201 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "license", licenseUrl);
1202 added = true;
1203 } catch (MalformedURLException e) {
1204 messages.addMessage(
1205 new String[] {"project", "licenses", "license", "url"},
1206 licenseUrl,
1207 UserMessages.INVALID_URL);
1208 }
1209 }
1210 }
1211
1212 if (!added) {
1213 messages.addMessage(
1214 new String[] {"doapOptions", "license"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1215 }
1216 return;
1217 }
1218
1219 try {
1220 new URL(license);
1221
1222 DoapUtil.writeComment(writer, "The URI of the license the software is distributed under.");
1223 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "license", license);
1224 } catch (MalformedURLException e) {
1225 messages.addMessage(new String[] {"doapOptions", "license"}, license, UserMessages.INVALID_URL);
1226 }
1227 }
1228
1229
1230
1231
1232
1233
1234
1235
1236 private void writeBugDatabase(XMLWriter writer, MavenProject project) {
1237 String bugDatabase = DoapUtil.interpolate(doapOptions.getBugDatabase(), project, settings);
1238 if (bugDatabase == null || bugDatabase.isEmpty()) {
1239 messages.addMessage(
1240 new String[] {"doapOptions", "bugDatabase"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1241 return;
1242 }
1243
1244 try {
1245 new URL(bugDatabase);
1246
1247 DoapUtil.writeComment(writer, "Bug database.");
1248 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "bug-database", bugDatabase);
1249 } catch (MalformedURLException e) {
1250 messages.addMessage(new String[] {"doapOptions", "bugDatabase"}, bugDatabase, UserMessages.INVALID_URL);
1251 }
1252 }
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262 private void writeMailingList(XMLWriter writer, MavenProject project) {
1263 String ml = DoapUtil.interpolate(doapOptions.getMailingList(), project, settings);
1264 if (ml == null || ml.isEmpty()) {
1265 messages.addMessage(
1266 new String[] {"doapOptions", "mailingList"}, null, UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1267 return;
1268 }
1269
1270 try {
1271 new URL(ml);
1272
1273 DoapUtil.writeComment(writer, "Mailing lists.");
1274 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "mailing-list", ml);
1275 } catch (MalformedURLException e) {
1276 messages.addMessage(new String[] {"doapOptions", "mailingList"}, ml, UserMessages.INVALID_URL);
1277 }
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289 private void writeReleases(XMLWriter writer, MavenProject project) throws MojoExecutionException {
1290 Artifact artifact = artifactFactory.createArtifact(
1291 project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
1292 RepositoryMetadata metadata = new ArtifactRepositoryMetadata(artifact);
1293
1294 for (ArtifactRepository repo : remoteRepositories) {
1295 if (repo.isBlacklisted()) {
1296 continue;
1297 }
1298 if (repo.getSnapshots().isEnabled()) {
1299 continue;
1300 }
1301 if (repo.getReleases().isEnabled()) {
1302 try {
1303 repositoryMetadataManager.resolveAlways(metadata, localRepository, repo);
1304 break;
1305 } catch (RepositoryMetadataResolutionException e) {
1306 throw new MojoExecutionException(
1307 metadata.extendedToString() + " could not be retrieved from repositories due to an error: "
1308 + e.getMessage(),
1309 e);
1310 }
1311 }
1312 }
1313
1314 if (metadata.getMetadata().getVersioning() == null) {
1315 messages.getWarnMessages()
1316 .add("No versioning was found for " + artifact.getGroupId() + ":" + artifact.getArtifactId()
1317 + ". Ignored DOAP <release/> tag.");
1318 return;
1319 }
1320
1321 List<String> versions = metadata.getMetadata().getVersioning().getVersions();
1322
1323
1324 Collections.reverse(versions);
1325 boolean addComment = false;
1326 int i = 0;
1327 for (String version : versions) {
1328 if (!addComment) {
1329 DoapUtil.writeComment(writer, "Project releases.");
1330 addComment = true;
1331 }
1332
1333 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "release");
1334 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "Version");
1335
1336 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "name");
1337 writer.writeText(project.getName() + " - " + version);
1338 writer.endElement();
1339
1340 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "revision", version);
1341
1342
1343 for (ArtifactRepository repo : remoteRepositories) {
1344 Artifact artifactRelease = artifactFactory.createArtifact(
1345 project.getGroupId(), project.getArtifactId(), version, null, project.getPackaging());
1346
1347 if (artifactRelease == null) {
1348 continue;
1349 }
1350
1351 String fileRelease = repo.getUrl() + "/" + repo.pathOf(artifactRelease);
1352 try {
1353 DoapUtil.fetchURL(settings, new URL(fileRelease));
1354 } catch (IOException e) {
1355 getLog().debug("IOException :" + e.getMessage());
1356 continue;
1357 }
1358 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "file-release", fileRelease);
1359
1360 Date releaseDate = null;
1361 try {
1362 releaseDate = REPOSITORY_DATE_FORMAT.parse(
1363 metadata.getMetadata().getVersioning().getLastUpdated());
1364 } catch (ParseException e) {
1365 getLog().error("Unable to parse date '"
1366 + metadata.getMetadata().getVersioning().getLastUpdated() + "'");
1367 continue;
1368 }
1369
1370
1371 if (i == 0) {
1372 DoapUtil.writeElement(
1373 writer, doapOptions.getXmlnsPrefix(), "created", DOAP_DATE_FORMAT.format(releaseDate));
1374 }
1375 }
1376
1377 writer.endElement();
1378 writer.endElement();
1379
1380 i++;
1381 }
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392 private void writeSourceRepositories(XMLWriter writer, MavenProject project) {
1393 String anonymousConnection = DoapUtil.interpolate(doapOptions.getScmAnonymous(), project, settings);
1394 if (anonymousConnection == null || anonymousConnection.isEmpty()) {
1395 messages.addMessage(
1396 new String[] {"doapOptions", "scmAnonymousConnection"},
1397 null,
1398 UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1399 } else {
1400 DoapUtil.writeComment(writer, "Anonymous Source Repository.");
1401
1402 try {
1403 new URL(anonymousConnection);
1404
1405 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "repository");
1406 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "Repository");
1407 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "location", anonymousConnection);
1408 writer.endElement();
1409 writer.endElement();
1410 } catch (MalformedURLException e) {
1411 writeSourceRepository(writer, project, anonymousConnection);
1412 }
1413 }
1414
1415 String devConnection = DoapUtil.interpolate(doapOptions.getScmDeveloper(), project, settings);
1416 if (devConnection == null || devConnection.isEmpty()) {
1417 messages.addMessage(
1418 new String[] {"doapOptions", "scmDeveloperConnection"},
1419 null,
1420 UserMessages.REQUIRED_BY_ASF_OR_RECOMMENDED);
1421 } else {
1422 DoapUtil.writeComment(writer, "Developer Source Repository.");
1423
1424 try {
1425 new URL(devConnection);
1426
1427 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "repository");
1428 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "Repository");
1429 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "location", devConnection);
1430 writer.endElement();
1431 writer.endElement();
1432 } catch (MalformedURLException e) {
1433 writeSourceRepository(writer, project, devConnection);
1434 }
1435 }
1436 }
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456 private void writeSourceRepository(XMLWriter writer, MavenProject project, String connection) {
1457 ScmRepository repository = getScmRepository(connection);
1458
1459 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "repository");
1460
1461 if (isScmSystem(repository, "svn")) {
1462 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "SVNRepository");
1463
1464 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
1465
1466 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "location", svnRepo.getUrl());
1467 } else {
1468
1469
1470
1471
1472
1473 DoapUtil.writeStartElement(writer, doapOptions.getXmlnsPrefix(), "Repository");
1474
1475 if (connection.length() < 4) {
1476 throw new IllegalArgumentException("The source repository connection is too short.");
1477 }
1478
1479 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "location", connection.substring(4));
1480 }
1481
1482 DoapUtil.writeRdfResourceElement(
1483 writer, doapOptions.getXmlnsPrefix(), "browse", project.getScm().getUrl());
1484
1485 writer.endElement();
1486 writer.endElement();
1487 }
1488
1489
1490
1491
1492
1493
1494
1495 private void writeContributors(XMLWriter writer, List<Contributor> contributors) {
1496 if (contributors == null || contributors.isEmpty()) {
1497 return;
1498 }
1499
1500 boolean isDeveloper =
1501 Developer.class.isAssignableFrom(contributors.get(0).getClass());
1502 if (isDeveloper) {
1503 DoapUtil.writeComment(writer, "Main committers.");
1504 } else {
1505 DoapUtil.writeComment(writer, "Contributed persons.");
1506 }
1507
1508 List<Contributor> maintainers = DoapUtil.getContributorsWithMaintainerRole(i18n, contributors);
1509 List<Contributor> developers = DoapUtil.getContributorsWithDeveloperRole(i18n, contributors);
1510 List<Contributor> documenters = DoapUtil.getContributorsWithDocumenterRole(i18n, contributors);
1511 List<Contributor> translators = DoapUtil.getContributorsWithTranslatorRole(i18n, contributors);
1512 List<Contributor> testers = DoapUtil.getContributorsWithTesterRole(i18n, contributors);
1513 List<Contributor> helpers = DoapUtil.getContributorsWithHelperRole(i18n, contributors);
1514 List<Contributor> unknowns = DoapUtil.getContributorsWithUnknownRole(i18n, contributors);
1515
1516
1517 if (isDeveloper) {
1518 maintainers.addAll(unknowns);
1519 } else {
1520 helpers.addAll(unknowns);
1521 }
1522
1523
1524 if (developers.size() != 0) {
1525 writeContributor(writer, developers, "developer");
1526 }
1527 if (documenters.size() != 0) {
1528 writeContributor(writer, documenters, "documenter");
1529 }
1530 if (helpers.size() != 0) {
1531 writeContributor(writer, helpers, "helper");
1532 }
1533 if (maintainers.size() != 0) {
1534 writeContributor(writer, maintainers, "maintainer");
1535 }
1536 if (testers.size() != 0) {
1537 writeContributor(writer, testers, "tester");
1538 }
1539 if (translators.size() != 0) {
1540 writeContributor(writer, translators, "translator");
1541 }
1542 }
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561 private void writeContributor(XMLWriter writer, List<Contributor> developersOrContributors, String doapType) {
1562 if (developersOrContributors == null || developersOrContributors.isEmpty()) {
1563 return;
1564 }
1565
1566 sortContributors(developersOrContributors);
1567
1568 for (Contributor developersOrContributor : developersOrContributors) {
1569 writeContributor(writer, developersOrContributor, doapOptions.getXmlnsPrefix(), doapType);
1570 }
1571 }
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592 private void writeContributor(
1593 XMLWriter writer, Contributor developerOrContributor, String xmlsPrefix, String doapType) {
1594 if (developerOrContributor == null) {
1595 return;
1596 }
1597
1598 if (doapType == null || doapType.isEmpty()) {
1599 throw new IllegalArgumentException("doapType is required.");
1600 }
1601
1602 String name = developerOrContributor.getName();
1603 String email = developerOrContributor.getEmail();
1604 String organization = developerOrContributor.getOrganization();
1605 String organizationUrl = developerOrContributor.getOrganizationUrl();
1606 String homepage = developerOrContributor.getUrl();
1607 String nodeId = null;
1608
1609
1610 if (name == null || name.isEmpty()) {
1611 messages.addMessage(
1612 new String[] {"project", "developers|contributors", "developer|contributor", "name"},
1613 null,
1614 UserMessages.REQUIRED);
1615 return;
1616 }
1617
1618 if (!(organization == null || organization.isEmpty())
1619 || !(organizationUrl == null || organizationUrl.isEmpty())) {
1620 DoapUtil.Organization doapOrganization = DoapUtil.addOrganization(organization, organizationUrl);
1621 nodeId = DoapUtil.getNodeId();
1622 doapOrganization.addMember(nodeId);
1623 }
1624
1625 DoapUtil.writeStartElement(writer, xmlsPrefix, doapType);
1626 DoapUtil.writeStartElement(writer, "foaf", "Person");
1627 if (nodeId != null && !nodeId.isEmpty()) {
1628 writer.addAttribute("rdf:nodeID", nodeId);
1629 }
1630 DoapUtil.writeStartElement(writer, "foaf", "name");
1631 writer.writeText(name);
1632 writer.endElement();
1633 if (email != null && !email.isEmpty()) {
1634 if (DoapUtil.isValidEmail(email)) {
1635 DoapUtil.writeRdfResourceElement(writer, "foaf", "mbox", "mailto:" + email);
1636 } else {
1637 messages.addMessage(
1638 new String[] {"project", "developers|contributors", "developer|contributor", "email"},
1639 null,
1640 UserMessages.INVALID_EMAIL);
1641 }
1642 }
1643 if ((organization != null && !organization.isEmpty())
1644 && (organizationUrl != null && !organizationUrl.isEmpty())) {
1645 try {
1646 new URL(organizationUrl);
1647
1648 DoapUtil.addOrganization(organization, organizationUrl);
1649 } catch (MalformedURLException e) {
1650 messages.addMessage(
1651 new String[] {"project", "developers|contributors", "developer|contributor", "organizationUrl"},
1652 organizationUrl,
1653 UserMessages.INVALID_URL);
1654 }
1655 }
1656 if (homepage != null && !homepage.isEmpty()) {
1657 try {
1658 new URL(homepage);
1659
1660 DoapUtil.writeRdfResourceElement(writer, "foaf", "homepage", homepage);
1661 } catch (MalformedURLException e) {
1662 messages.addMessage(
1663 new String[] {"project", "developers|contributors", "developer|contributor", "homepage"},
1664 homepage,
1665 UserMessages.INVALID_URL);
1666 }
1667 }
1668 writer.endElement();
1669 writer.endElement();
1670 }
1671
1672
1673
1674
1675
1676
1677
1678 private ScmRepository getScmRepository(String scmUrl) {
1679 ScmRepository repo = null;
1680 if (!(scmUrl == null || scmUrl.isEmpty())) {
1681 try {
1682 repo = scmManager.makeScmRepository(scmUrl);
1683 } catch (NoSuchScmProviderException | ScmRepositoryException e) {
1684 if (getLog().isDebugEnabled()) {
1685 getLog().debug(e.getMessage(), e);
1686 }
1687 }
1688 }
1689
1690 return repo;
1691 }
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702 private void writeASFext(XMLWriter writer, MavenProject project) {
1703 if (!asfExtOptions.isIncluded()) {
1704 return;
1705 }
1706
1707 DoapUtil.writeComment(writer, "ASF extension.");
1708
1709
1710 String pmc = DoapUtil.interpolate(asfExtOptions.getPmc(), project, settings);
1711 if (pmc != null && !pmc.isEmpty()) {
1712 DoapUtil.writeRdfResourceElement(writer, asfExtOptions.getXmlnsPrefix(), "pmc", pmc);
1713 } else {
1714 messages.addMessage(new String[] {"asfExtOptions", "pmc"}, null, UserMessages.REQUIRED_BY_ASF);
1715 }
1716
1717
1718 String name = DoapUtil.interpolate(asfExtOptions.getName(), project, settings);
1719 if (name != null && !name.isEmpty()) {
1720 if (!name.toLowerCase(Locale.ENGLISH).trim().startsWith("apache")) {
1721 name = "Apache " + name;
1722 }
1723 DoapUtil.writeElement(writer, asfExtOptions.getXmlnsPrefix(), "name", name);
1724 } else {
1725 messages.addMessage(new String[] {"asfExtOptions", "name"}, null, UserMessages.REQUIRED_BY_ASF);
1726 }
1727
1728 String homepage = DoapUtil.interpolate(doapOptions.getHomepage(), project, settings);
1729 if (homepage != null && !homepage.isEmpty()) {
1730 try {
1731 new URL(homepage);
1732
1733 DoapUtil.writeRdfResourceElement(writer, "foaf", "homepage", homepage);
1734 } catch (MalformedURLException e) {
1735 messages.addMessage(new String[] {"doapOptions", "homepage"}, homepage, UserMessages.INVALID_URL);
1736 }
1737 }
1738
1739
1740 if (StringUtils.isEmpty(asfExtOptions.getCharter())) {
1741 messages.addMessage(new String[] {"asfExtOptions", "charter"}, null, UserMessages.REQUIRED_BY_ASF);
1742 } else {
1743 DoapUtil.writeElement(writer, asfExtOptions.getXmlnsPrefix(), "charter", asfExtOptions.getCharter());
1744 }
1745
1746
1747 @SuppressWarnings("unchecked")
1748 List<Developer> developers = new ArrayList<Developer>(project.getDevelopers());
1749 sortContributors(developers);
1750
1751 if (StringUtils.isNotEmpty(asfExtOptions.getChair())) {
1752 DoapUtil.writeStartElement(writer, asfExtOptions.getXmlnsPrefix(), "chair");
1753 DoapUtil.writeStartElement(writer, "foaf", "Person");
1754 DoapUtil.writeStartElement(writer, "foaf", "name");
1755 writer.writeText(asfExtOptions.getChair());
1756 writer.endElement();
1757 writer.endElement();
1758 writer.endElement();
1759 } else {
1760 Developer chair = ASFExtOptionsUtil.findChair(developers);
1761 if (chair != null) {
1762 writeContributor(writer, chair, asfExtOptions.getXmlnsPrefix(), "chair");
1763 developers.remove(chair);
1764 } else {
1765 messages.addMessage(new String[] {"asfExtOptions", "chair"}, null, UserMessages.REQUIRED_BY_ASF);
1766 }
1767 }
1768
1769
1770 if (developers != null && developers.size() > 0) {
1771 List<Developer> pmcMembers = ASFExtOptionsUtil.findPMCMembers(developers);
1772 for (Developer pmcMember : pmcMembers) {
1773 writeContributor(writer, pmcMember, asfExtOptions.getXmlnsPrefix(), "member");
1774 }
1775 }
1776
1777 writeASFImplements(writer);
1778
1779 Map<Object, String> map = asfExtOptions.getExtra();
1780 writeExtra(writer, project, "Extra ASFExt vocabulary.", map, asfExtOptions.getXmlnsPrefix());
1781 }
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791 private void writeASFImplements(XMLWriter writer) {
1792 if (asfExtOptions.getStandards() == null || asfExtOptions.getStandards().isEmpty()) {
1793 return;
1794 }
1795
1796 for (Standard standard : asfExtOptions.getStandards()) {
1797 DoapUtil.writeStartElement(writer, asfExtOptions.getXmlnsPrefix(), "implements");
1798 DoapUtil.writeStartElement(writer, asfExtOptions.getXmlnsPrefix(), "Standard");
1799
1800 if (StringUtils.isEmpty(standard.getTitle())) {
1801 messages.addMessage(
1802 new String[] {"asfExtOptions", "standards", "title"}, null, UserMessages.REQUIRED_BY_ASF);
1803 } else {
1804 DoapUtil.writeElement(
1805 writer,
1806 asfExtOptions.getXmlnsPrefix(),
1807 "title",
1808 standard.getTitle().trim());
1809 }
1810
1811 if (StringUtils.isEmpty(standard.getBody())) {
1812 messages.addMessage(
1813 new String[] {"asfExtOptions", "standards", "body"}, null, UserMessages.REQUIRED_BY_ASF);
1814 } else {
1815 DoapUtil.writeElement(
1816 writer,
1817 asfExtOptions.getXmlnsPrefix(),
1818 "body",
1819 standard.getBody().trim());
1820 }
1821
1822 if (StringUtils.isEmpty(standard.getId())) {
1823 messages.addMessage(
1824 new String[] {"asfExtOptions", "standards", "id"}, null, UserMessages.REQUIRED_BY_ASF);
1825 } else {
1826 DoapUtil.writeElement(
1827 writer,
1828 asfExtOptions.getXmlnsPrefix(),
1829 "id",
1830 standard.getId().trim());
1831 }
1832
1833 if (StringUtils.isNotEmpty(standard.getUrl())) {
1834 String standardUrl = standard.getUrl().trim();
1835 try {
1836 new URL(standardUrl);
1837
1838 DoapUtil.writeElement(writer, asfExtOptions.getXmlnsPrefix(), "url", standardUrl);
1839 } catch (MalformedURLException e) {
1840 messages.addMessage(
1841 new String[] {"asfExtOptions", "standards", "url"}, standardUrl, UserMessages.INVALID_URL);
1842 }
1843 }
1844
1845 writer.endElement();
1846 writer.endElement();
1847 }
1848 }
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866 private void writeOrganizations(XMLWriter writer) {
1867 Set<Entry<String, DoapUtil.Organization>> organizations = DoapUtil.getOrganizations();
1868
1869 for (Entry<String, DoapUtil.Organization> organizationEntry : organizations) {
1870 DoapUtil.Organization organization = organizationEntry.getValue();
1871
1872 DoapUtil.writeStartElement(writer, "foaf", "Organization");
1873 if (!StringUtils.isEmpty(organization.getName())) {
1874 DoapUtil.writeElement(writer, "foaf", "name", organization.getName());
1875 }
1876 if (!StringUtils.isEmpty(organization.getUrl())) {
1877 try {
1878 new URL(organization.getUrl());
1879
1880 DoapUtil.writeRdfResourceElement(writer, "foaf", "homepage", organization.getUrl());
1881 } catch (MalformedURLException e) {
1882 messages.errorMessages.add(
1883 "The organization URL " + organization.getUrl() + " is not a valid URL.");
1884 }
1885 }
1886 List<String> members = organization.getMembers();
1887 for (String member : members) {
1888 DoapUtil.writeRdfNodeIdElement(writer, "foaf", "member", member);
1889 }
1890 writer.endElement();
1891 }
1892 }
1893
1894
1895
1896
1897
1898
1899
1900
1901 private void writeAudience(XMLWriter writer) {
1902 String audience = DoapUtil.interpolate(doapOptions.getAudience(), project, settings);
1903 if (audience == null || audience.isEmpty()) {
1904 return;
1905 }
1906
1907 DoapUtil.writeComment(writer, "Audience.");
1908 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "audience", audience);
1909 }
1910
1911
1912
1913
1914
1915
1916
1917
1918 private void writeBlog(XMLWriter writer) {
1919 String blog = DoapUtil.interpolate(doapOptions.getBlog(), project, settings);
1920 if (StringUtils.isEmpty(doapOptions.getBlog())) {
1921 return;
1922 }
1923
1924 blog = blog.trim();
1925 try {
1926 new URL(blog);
1927 } catch (MalformedURLException e) {
1928 messages.addMessage(new String[] {"doapOptions", "blog"}, blog, UserMessages.INVALID_URL);
1929 return;
1930 }
1931
1932 DoapUtil.writeComment(writer, "Blog page.");
1933 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "blog", blog);
1934 }
1935
1936
1937
1938
1939
1940
1941
1942
1943 private void writePlateform(XMLWriter writer) {
1944 if (StringUtils.isEmpty(doapOptions.getPlatform())) {
1945 return;
1946 }
1947
1948 DoapUtil.writeComment(writer, "Platform.");
1949 String[] platforms = StringUtils.split(doapOptions.getPlatform(), ",");
1950 for (String platform : platforms) {
1951 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "platform", platform.trim());
1952 }
1953 }
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963 private void writeVendor(XMLWriter writer, MavenProject project) {
1964 String vendor = DoapUtil.interpolate(doapOptions.getVendor(), project, settings);
1965 if (vendor == null || vendor.isEmpty()) {
1966 return;
1967 }
1968
1969 DoapUtil.writeComment(writer, "Vendor.");
1970 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "vendor", vendor);
1971 }
1972
1973
1974
1975
1976
1977
1978
1979
1980 private void writeLanguage(XMLWriter writer) {
1981 if (StringUtils.isEmpty(doapOptions.getLanguage())) {
1982 return;
1983 }
1984
1985 boolean addComment = false;
1986 String[] languages = StringUtils.split(doapOptions.getLanguage(), ",");
1987 for (String language : languages) {
1988 language = language.trim();
1989
1990 if (Arrays.binarySearch(Locale.getISOLanguages(), language) < 0) {
1991 messages.addMessage(new String[] {"doapOptions", "languages"}, language, UserMessages.INVALID_ISO_DATE);
1992 continue;
1993 }
1994
1995 if (!addComment) {
1996 DoapUtil.writeComment(writer, "Language.");
1997 addComment = true;
1998 }
1999 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "language", language);
2000 }
2001 }
2002
2003
2004
2005
2006
2007
2008
2009
2010 private void writeServiceEndpoint(XMLWriter writer) {
2011 String serviceEndpoint = DoapUtil.interpolate(doapOptions.getServiceEndpoint(), project, settings);
2012 if (serviceEndpoint == null || serviceEndpoint.isEmpty()) {
2013 return;
2014 }
2015
2016 serviceEndpoint = serviceEndpoint.trim();
2017 try {
2018 new URL(serviceEndpoint);
2019 } catch (MalformedURLException e) {
2020 messages.addMessage(
2021 new String[] {"doapOptions", "serviceEndpoint"}, serviceEndpoint, UserMessages.INVALID_URL);
2022 return;
2023 }
2024
2025 DoapUtil.writeComment(writer, "Service endpoint.");
2026 DoapUtil.writeRdfResourceElement(writer, doapOptions.getXmlnsPrefix(), "service-endpoint", serviceEndpoint);
2027 }
2028
2029
2030
2031
2032
2033
2034
2035
2036 private void writeImplements(XMLWriter writer) {
2037 if (StringUtils.isEmpty(doapOptions.getImplementations())) {
2038 return;
2039 }
2040
2041 DoapUtil.writeComment(writer, "Implements.");
2042 String[] implementations = StringUtils.split(doapOptions.getImplementations(), ",");
2043 for (String implementation : implementations) {
2044 DoapUtil.writeElement(writer, doapOptions.getXmlnsPrefix(), "implements", implementation.trim());
2045 }
2046 }
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058 private void writeExtra(
2059 XMLWriter writer, MavenProject project, String comment, Map<Object, String> map, String xmlnsPrefix) {
2060 if (map == null || map.isEmpty()) {
2061 return;
2062 }
2063
2064 boolean addComment = false;
2065 for (Map.Entry<Object, String> entry : map.entrySet()) {
2066 String key = (String) entry.getKey();
2067 String value = entry.getValue();
2068
2069 if (value == null) {
2070 continue;
2071 }
2072
2073 String interpolatedValue = DoapUtil.interpolate(value, project, settings);
2074 if (interpolatedValue == null) {
2075 continue;
2076 }
2077
2078 if (!addComment) {
2079 DoapUtil.writeComment(writer, comment);
2080 addComment = true;
2081 }
2082
2083 try {
2084 new URL(interpolatedValue);
2085
2086 DoapUtil.writeRdfResourceElement(writer, xmlnsPrefix, key, interpolatedValue);
2087 } catch (MalformedURLException e) {
2088 DoapUtil.writeElement(writer, xmlnsPrefix, key, interpolatedValue);
2089 }
2090 }
2091 }
2092
2093
2094
2095
2096
2097
2098
2099 private void writeExtensions(XMLWriter writer) {
2100 if (!(extOptions != null
2101 && extOptions.length > 0
2102 && !extOptions[0].getExtensions().isEmpty())) {
2103 return;
2104 }
2105
2106 for (ExtOptions extOption : extOptions) {
2107 Map<Object, String> map = extOption.getExtensions();
2108 writeExtra(writer, project, "Other extension vocabulary.", map, extOption.getXmlnsPrefix());
2109 }
2110 }
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127 private static boolean isScmSystem(ScmRepository scmRepository, String scmProvider) {
2128 if (scmProvider == null || scmProvider.isEmpty()) {
2129 return false;
2130 }
2131
2132 if (scmRepository != null && scmProvider.equalsIgnoreCase(scmRepository.getProvider())) {
2133 return true;
2134 }
2135
2136 return false;
2137 }
2138
2139
2140
2141
2142
2143
2144
2145 @SuppressWarnings({"unchecked", "rawtypes"})
2146 private static void sortContributors(List contributors) {
2147 Collections.sort(contributors, new Comparator<Contributor>() {
2148 public int compare(Contributor contributor1, Contributor contributor2) {
2149 if (contributor1 == contributor2) {
2150 return 0;
2151 }
2152
2153 if (contributor1 == null && contributor2 != null) {
2154 return -1;
2155 }
2156 if (contributor1 != null && contributor2 == null) {
2157 return +1;
2158 }
2159
2160 if (Developer.class.isAssignableFrom(contributor1.getClass())
2161 && Developer.class.isAssignableFrom(contributor2.getClass())) {
2162 Developer developer1 = (Developer) contributor1;
2163 Developer developer2 = (Developer) contributor2;
2164
2165 if (developer1.getId() == null && developer2.getId() != null) {
2166 return -1;
2167 }
2168 if (developer1.getId() != null && developer2.getId() == null) {
2169 return +1;
2170 }
2171
2172 return developer1.getId().compareTo(developer2.getId());
2173 }
2174
2175 if (contributor1.getName() == null && contributor2.getName() != null) {
2176 return -1;
2177 }
2178 if (contributor1.getName() != null && contributor2.getName() == null) {
2179 return +1;
2180 }
2181 return contributor1.getName().compareTo(contributor2.getName());
2182 }
2183 });
2184 }
2185
2186
2187
2188
2189
2190
2191 private class UserMessages {
2192 public static final int REQUIRED = 10;
2193
2194 public static final int REQUIRED_BY_ASF_OR_RECOMMENDED = 11;
2195
2196 public static final int REQUIRED_BY_ASF = 12;
2197
2198 public static final int RECOMMENDED = 20;
2199
2200 public static final int INVALID_URL = 30;
2201
2202 public static final int INVALID_DATE = 31;
2203
2204 public static final int INVALID_ISO_DATE = 32;
2205
2206 public static final int INVALID_EMAIL = 33;
2207
2208 public static final int SHORT_DESC_TOO_LONG = 34;
2209
2210 private List<String> errorMessages = new ArrayList<>();
2211
2212 private List<String> warnMessages = new ArrayList<>();
2213
2214
2215
2216
2217 public List<String> getErrorMessages() {
2218 return errorMessages;
2219 }
2220
2221
2222
2223
2224 public List<String> getWarnMessages() {
2225 return warnMessages;
2226 }
2227
2228
2229
2230
2231
2232
2233 protected void addMessage(String[] tags, String value, int errorId) {
2234 if (tags == null) {
2235 throw new IllegalArgumentException("tags is required");
2236 }
2237
2238 boolean isPom = false;
2239 if (tags[0].equalsIgnoreCase("project")) {
2240 isPom = true;
2241 }
2242 switch (errorId) {
2243 case REQUIRED:
2244 errorMessages.add("A " + toConfiguration(tags, null) + " parameter is required.");
2245 break;
2246 case REQUIRED_BY_ASF_OR_RECOMMENDED:
2247 if (isPom) {
2248 if (asfExtOptions.isIncluded()) {
2249 errorMessages.add("A POM " + toConfiguration(tags, null) + " value is required by ASF.");
2250 } else {
2251 warnMessages.add("No POM " + toConfiguration(tags, null)
2252 + " value is defined, it is highly recommended to have one.");
2253 }
2254 } else {
2255 if (asfExtOptions.isIncluded()) {
2256 errorMessages.add("A " + toConfiguration(tags, null) + " parameter is required by ASF.");
2257 } else {
2258 warnMessages.add("No " + toConfiguration(tags, null)
2259 + " parameter defined, it is highly recommended to have one.");
2260 }
2261 }
2262 break;
2263 case REQUIRED_BY_ASF:
2264 if (isPom) {
2265 errorMessages.add("A POM " + toConfiguration(tags, null) + " value is required by ASF.");
2266 } else {
2267 errorMessages.add("A " + toConfiguration(tags, null) + " parameter is required by ASF.");
2268 }
2269 break;
2270 case RECOMMENDED:
2271 warnMessages.add("No " + toConfiguration(tags, null)
2272 + " parameter defined, it is highly recommended to have one.");
2273 break;
2274 case INVALID_URL:
2275 if (isPom) {
2276 errorMessages.add("The POM " + toConfiguration(tags, value) + " value is not a valid URL.");
2277 } else {
2278 errorMessages.add("The " + toConfiguration(tags, value) + " parameter is not a valid URL.");
2279 }
2280 break;
2281 case INVALID_DATE:
2282 errorMessages.add("The " + toConfiguration(tags, value) + " parameter should be in YYYY-MM-DD.");
2283 break;
2284 case INVALID_EMAIL:
2285 errorMessages.add("The POM " + toConfiguration(tags, value) + " value is not a valid email.");
2286 break;
2287 case INVALID_ISO_DATE:
2288 errorMessages.add(
2289 "The " + toConfiguration(tags, value) + " parameter is not a valid ISO language.");
2290 break;
2291 case SHORT_DESC_TOO_LONG:
2292 errorMessages.add("The " + toConfiguration(tags, value)
2293 + " first sentence is too long maximum words number is 10.");
2294 break;
2295 default:
2296 throw new IllegalArgumentException("Unknown errorId=" + errorId);
2297 }
2298 }
2299
2300
2301
2302
2303
2304
2305 protected String toConfiguration(String[] tags, String value) {
2306 if (tags == null) {
2307 throw new IllegalArgumentException("tags is required");
2308 }
2309
2310 StringBuilder sb = new StringBuilder();
2311 for (int i = 0; i < tags.length; i++) {
2312 if (i == tags.length - 1 && (value == null || value.isEmpty())) {
2313 sb.append("<").append(tags[i]).append("/>");
2314 } else {
2315 sb.append("<").append(tags[i]).append(">");
2316 }
2317 }
2318 if (value != null && !value.isEmpty()) {
2319 sb.append(value);
2320 }
2321 for (int i = tags.length - 1; i >= 0; i--) {
2322 if (!(i == tags.length - 1 && (value == null || value.isEmpty()))) {
2323 sb.append("</").append(tags[i]).append(">");
2324 }
2325 }
2326
2327 return sb.toString();
2328 }
2329 }
2330 }