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 java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.lang.reflect.Method;
26 import java.net.MalformedURLException;
27 import java.net.SocketTimeoutException;
28 import java.net.URL;
29 import java.text.DateFormat;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 import java.util.Properties;
40 import java.util.Set;
41 import java.util.StringTokenizer;
42 import java.util.WeakHashMap;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46 import org.apache.commons.httpclient.Credentials;
47 import org.apache.commons.httpclient.HttpClient;
48 import org.apache.commons.httpclient.HttpStatus;
49 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
50 import org.apache.commons.httpclient.UsernamePasswordCredentials;
51 import org.apache.commons.httpclient.auth.AuthScope;
52 import org.apache.commons.httpclient.methods.GetMethod;
53 import org.apache.commons.httpclient.params.HttpClientParams;
54 import org.apache.commons.httpclient.params.HttpMethodParams;
55 import org.apache.jena.rdf.model.Model;
56 import org.apache.jena.rdf.model.ModelFactory;
57 import org.apache.jena.rdf.model.RDFReader;
58 import org.apache.jena.rdf.model.impl.RDFDefaultErrorHandler;
59 import org.apache.maven.model.Contributor;
60 import org.apache.maven.project.MavenProject;
61 import org.apache.maven.settings.Proxy;
62 import org.apache.maven.settings.Settings;
63 import org.apache.maven.wagon.proxy.ProxyInfo;
64 import org.apache.maven.wagon.proxy.ProxyUtils;
65 import org.codehaus.plexus.i18n.I18N;
66 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
67 import org.codehaus.plexus.interpolation.InterpolationException;
68 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
69 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
70 import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
71 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
72 import org.codehaus.plexus.util.StringUtils;
73 import org.codehaus.plexus.util.introspection.ClassMap;
74 import org.codehaus.plexus.util.xml.XMLWriter;
75 import org.codehaus.plexus.util.xml.XmlWriterUtil;
76
77
78
79
80
81
82
83 public class DoapUtil {
84
85 private static final String EMAIL_REGEX =
86 "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
87
88
89 private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
90
91
92 private static final int REPEAT_EQUALS = 21;
93
94
95 public static final int DEFAULT_TIMEOUT = 2000;
96
97
98 protected static final String RDF_RESOURCE = "rdf:resource";
99
100
101 protected static final String RDF_NODE_ID = "rdf:nodeID";
102
103
104 private static Map<String, DoapUtil.Organization> organizations = new HashMap<>();
105
106
107
108
109
110
111 public static void writeHeader(XMLWriter writer) {
112 XmlWriterUtil.writeLineBreak(writer);
113
114 XmlWriterUtil.writeCommentLineBreak(writer);
115 XmlWriterUtil.writeComment(
116 writer,
117 StringUtils.repeat("=", REPEAT_EQUALS) + " - DO NOT EDIT THIS FILE! - "
118 + StringUtils.repeat("=", REPEAT_EQUALS));
119 XmlWriterUtil.writeCommentLineBreak(writer);
120 XmlWriterUtil.writeComment(writer, " ");
121 XmlWriterUtil.writeComment(writer, "Any modifications will be overwritten.");
122 XmlWriterUtil.writeComment(writer, " ");
123 DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US);
124 XmlWriterUtil.writeComment(
125 writer,
126 "Generated by Maven Doap Plugin " + getPluginVersion() + " on "
127 + dateFormat.format(new Date(System.currentTimeMillis())));
128 XmlWriterUtil.writeComment(writer, "See: http://maven.apache.org/plugins/maven-doap-plugin/");
129 XmlWriterUtil.writeComment(writer, " ");
130 XmlWriterUtil.writeCommentLineBreak(writer);
131
132 XmlWriterUtil.writeLineBreak(writer);
133 }
134
135
136
137
138
139
140
141
142
143 public static void writeComment(XMLWriter writer, String comment) throws IllegalArgumentException {
144 if (comment == null || comment.isEmpty()) {
145 throw new IllegalArgumentException("comment should be defined");
146 }
147
148 XmlWriterUtil.writeLineBreak(writer);
149 XmlWriterUtil.writeCommentText(writer, comment, 2);
150 }
151
152
153
154
155
156
157
158
159 public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
160 throws IllegalArgumentException {
161 if (name == null || name.isEmpty()) {
162 throw new IllegalArgumentException("name should be defined");
163 }
164
165 if (value != null) {
166 writeStartElement(writer, xmlnsPrefix, name);
167 writer.writeText(value);
168 writer.endElement();
169 }
170 }
171
172
173
174
175
176
177
178
179
180 public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value, String lang)
181 throws IllegalArgumentException {
182 if (lang == null || lang.isEmpty()) {
183 writeElement(writer, xmlnsPrefix, name, value);
184 return;
185 }
186
187 if (name == null || name.isEmpty()) {
188 throw new IllegalArgumentException("name should be defined");
189 }
190
191 if (value != null) {
192 writeStartElement(writer, xmlnsPrefix, name);
193 writer.addAttribute("xml:lang", lang);
194 writer.writeText(value);
195 writer.endElement();
196 }
197 }
198
199
200
201
202
203
204
205
206 public static void writeStartElement(XMLWriter writer, String xmlnsPrefix, String name)
207 throws IllegalArgumentException {
208 if (name == null || name.isEmpty()) {
209 throw new IllegalArgumentException("name should be defined");
210 }
211
212 if (xmlnsPrefix != null && !xmlnsPrefix.isEmpty()) {
213 writer.startElement(xmlnsPrefix + ":" + name);
214 } else {
215 writer.startElement(name);
216 }
217 }
218
219
220
221
222
223
224
225
226 public static void writeRdfResourceElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
227 throws IllegalArgumentException {
228 if (name == null || name.isEmpty()) {
229 throw new IllegalArgumentException("name should be defined");
230 }
231
232 if (value != null) {
233 writeStartElement(writer, xmlnsPrefix, name);
234 writer.addAttribute(RDF_RESOURCE, value);
235 writer.endElement();
236 }
237 }
238
239
240
241
242
243
244
245 public static void writeRdfNodeIdElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
246 throws IllegalArgumentException {
247 if (name == null || name.isEmpty()) {
248 throw new IllegalArgumentException("name should be defined");
249 }
250
251 if (value != null) {
252 writeStartElement(writer, xmlnsPrefix, name);
253 writer.addAttribute(RDF_NODE_ID, value);
254 writer.endElement();
255 }
256 }
257
258
259
260
261
262
263 public static List<Contributor> getContributorsWithDeveloperRole(
264 I18N i18n, List<Contributor> developersOrContributors) {
265 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("developers");
266 }
267
268
269
270
271
272
273 public static List<Contributor> getContributorsWithDocumenterRole(
274 I18N i18n, List<Contributor> developersOrContributors) {
275 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("documenters");
276 }
277
278
279
280
281
282
283 public static List<Contributor> getContributorsWithHelperRole(
284 I18N i18n, List<Contributor> developersOrContributors) {
285 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("helpers");
286 }
287
288
289
290
291
292
293 public static List<Contributor> getContributorsWithMaintainerRole(
294 I18N i18n, List<Contributor> developersOrContributors) {
295 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("maintainers");
296 }
297
298
299
300
301
302
303 public static List<Contributor> getContributorsWithTesterRole(
304 I18N i18n, List<Contributor> developersOrContributors) {
305 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("testers");
306 }
307
308
309
310
311
312
313 public static List<Contributor> getContributorsWithTranslatorRole(
314 I18N i18n, List<Contributor> developersOrContributors) {
315 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("translators");
316 }
317
318
319
320
321
322
323 public static List<Contributor> getContributorsWithUnknownRole(
324 I18N i18n, List<Contributor> developersOrContributors) {
325 return filterContributorsByDoapRoles(i18n, developersOrContributors).get("unknowns");
326 }
327
328
329
330
331
332
333
334 public static class Organization {
335 private String name;
336
337 private String url;
338
339 private List<String> members = new LinkedList<>();
340
341 public Organization(String name, String url) {
342 this.name = name;
343 this.url = url;
344 }
345
346 public void setName(String name) {
347 this.name = name;
348 }
349
350 public String getName() {
351 return name;
352 }
353
354 public void setUrl(String url) {
355 this.url = url;
356 }
357
358 public String getUrl() {
359 return url;
360 }
361
362 public void addMember(String nodeId) {
363 members.add(nodeId);
364 }
365
366 public List<String> getMembers() {
367 return members;
368 }
369 }
370
371
372
373
374
375
376
377
378 public static DoapUtil.Organization addOrganization(String name, String url) {
379 Organization organization = organizations.get(name);
380
381 if (organization == null) {
382 organization = new DoapUtil.Organization(name, url);
383 }
384
385 organizations.put(name, organization);
386
387 return organization;
388 }
389
390
391 private static int nodeNumber = 1;
392
393
394
395
396
397
398
399
400 public static String getNodeId() {
401 return "b" + nodeNumber++;
402 }
403
404
405
406
407
408
409 public static Set<Entry<String, DoapUtil.Organization>> getOrganizations() {
410 return organizations.entrySet();
411 }
412
413
414
415
416
417
418
419
420 public static List<String> validate(File doapFile) {
421 if (doapFile == null || !doapFile.isFile()) {
422 throw new IllegalArgumentException("The DOAP file should exist");
423 }
424
425 Model model = ModelFactory.createDefaultModel();
426 RDFReader r = model.getReader("RDF/XML");
427 r.setProperty("error-mode", "strict-error");
428 final List<String> errors = new ArrayList<>();
429 r.setErrorHandler(new RDFDefaultErrorHandler() {
430 @Override
431 public void error(Exception e) {
432 errors.add(e.getMessage());
433 }
434 });
435
436 try {
437 r.read(model, doapFile.toURI().toURL().toString());
438 } catch (MalformedURLException e) {
439
440 }
441
442 return errors;
443 }
444
445
446
447
448
449
450 public static boolean isValidEmail(String str) {
451 if (str == null || str.isEmpty()) {
452 return false;
453 }
454
455 Matcher matcher = EMAIL_PATTERN.matcher(str);
456 return matcher.matches();
457 }
458
459
460
461
462
463
464
465
466
467
468 @SuppressWarnings("checkstyle:emptyblock")
469 public static void fetchURL(Settings settings, URL url) throws IOException {
470 if (url == null) {
471 throw new IllegalArgumentException("The url is null");
472 }
473
474 if ("file".equals(url.getProtocol())) {
475
476
477
478 try (InputStream in = url.openStream()) {}
479 return;
480 }
481
482
483 HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
484 httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(DEFAULT_TIMEOUT);
485 httpClient.getHttpConnectionManager().getParams().setSoTimeout(DEFAULT_TIMEOUT);
486 httpClient.getParams().setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
487
488
489 httpClient
490 .getParams()
491 .setParameter(HttpMethodParams.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
492
493 if (settings != null && settings.getActiveProxy() != null) {
494 Proxy activeProxy = settings.getActiveProxy();
495
496 ProxyInfo proxyInfo = new ProxyInfo();
497 proxyInfo.setNonProxyHosts(activeProxy.getNonProxyHosts());
498
499 if (StringUtils.isNotEmpty(activeProxy.getHost())
500 && !ProxyUtils.validateNonProxyHosts(proxyInfo, url.getHost())) {
501 httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), activeProxy.getPort());
502
503 if (StringUtils.isNotEmpty(activeProxy.getUsername()) && activeProxy.getPassword() != null) {
504 Credentials credentials =
505 new UsernamePasswordCredentials(activeProxy.getUsername(), activeProxy.getPassword());
506
507 httpClient.getState().setProxyCredentials(AuthScope.ANY, credentials);
508 }
509 }
510 }
511
512 GetMethod getMethod = new GetMethod(url.toString());
513 try {
514 int status;
515 try {
516 status = httpClient.executeMethod(getMethod);
517 } catch (SocketTimeoutException e) {
518
519 status = httpClient.executeMethod(getMethod);
520 }
521
522 if (status != HttpStatus.SC_OK) {
523 throw new FileNotFoundException(url.toString());
524 }
525 } finally {
526 getMethod.releaseConnection();
527 }
528 }
529
530
531
532
533
534
535
536
537
538
539 public static String interpolate(String value, final MavenProject project, Settings settings) {
540 if (project == null) {
541 throw new IllegalArgumentException("project is required");
542 }
543
544 if (value == null) {
545 return value;
546 }
547
548 if (!value.contains("${")) {
549 return value.trim();
550 }
551
552 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
553 try {
554 interpolator.addValueSource(new EnvarBasedValueSource());
555 } catch (IOException e) {
556
557 }
558 interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
559 interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
560 interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
561 interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
562 interpolator.addValueSource(new ObjectBasedValueSource(project) {
563 @Override
564 public Object getValue(String expression) {
565 try {
566 return ReflectionValueExtractor.evaluate(expression, project, true);
567 } catch (Exception e) {
568 addFeedback("Failed to extract \'" + expression + "\' from: " + project, e);
569 }
570
571 return null;
572 }
573 });
574
575 if (settings != null) {
576 interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
577 }
578
579 String interpolatedValue = value;
580 try {
581 interpolatedValue = interpolator.interpolate(value).trim();
582 } catch (InterpolationException e) {
583
584 }
585
586 if (interpolatedValue.startsWith("${")) {
587 return null;
588 }
589
590 return interpolatedValue;
591 }
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609 private static Map<String, List<Contributor>> filterContributorsByDoapRoles(
610 I18N i18n, List<Contributor> developersOrContributors) {
611 Map<String, List<Contributor>> returnMap = new HashMap<>(7);
612 returnMap.put("maintainers", new ArrayList<>());
613 returnMap.put("developers", new ArrayList<>());
614 returnMap.put("documenters", new ArrayList<>());
615 returnMap.put("translators", new ArrayList<>());
616 returnMap.put("testers", new ArrayList<>());
617 returnMap.put("helpers", new ArrayList<>());
618 returnMap.put("unknowns", new ArrayList<>());
619
620 if (developersOrContributors == null || developersOrContributors.isEmpty()) {
621 return returnMap;
622 }
623
624 for (Contributor contributor : developersOrContributors) {
625 List<String> roles = contributor.getRoles();
626
627 if (roles != null && roles.size() != 0) {
628 for (String role : roles) {
629 role = role.toLowerCase(Locale.ENGLISH);
630 if (role.contains(getLowerCaseString(i18n, "doap.maintainer"))) {
631 if (!returnMap.get("maintainers").contains(contributor)) {
632 returnMap.get("maintainers").add(contributor);
633 }
634 } else if (role.contains(getLowerCaseString(i18n, "doap.developer"))) {
635 if (!returnMap.get("developers").contains(contributor)) {
636 returnMap.get("developers").add(contributor);
637 }
638 } else if (role.contains(getLowerCaseString(i18n, "doap.documenter"))) {
639 if (!returnMap.get("documenters").contains(contributor)) {
640 returnMap.get("documenters").add(contributor);
641 }
642 } else if (role.contains(getLowerCaseString(i18n, "doap.translator"))) {
643 if (!returnMap.get("translators").contains(contributor)) {
644 returnMap.get("translators").add(contributor);
645 }
646 } else if (role.contains(getLowerCaseString(i18n, "doap.tester"))) {
647 if (!returnMap.get("testers").contains(contributor)) {
648 returnMap.get("testers").add(contributor);
649 }
650 } else if (role.contains(getLowerCaseString(i18n, "doap.helper"))) {
651 if (!returnMap.get("helpers").contains(contributor)) {
652 returnMap.get("helpers").add(contributor);
653 }
654 } else if (role.contains(getLowerCaseString(i18n, "doap.emeritus"))) {
655
656 } else {
657 if (!returnMap.get("unknowns").contains(contributor)) {
658 returnMap.get("unknowns").add(contributor);
659 }
660 }
661 }
662 } else {
663 if (!returnMap.get("unknowns").contains(contributor)) {
664 returnMap.get("unknowns").add(contributor);
665 }
666 }
667 }
668
669 return returnMap;
670 }
671
672
673
674
675
676
677 private static String getLowerCaseString(I18N i18n, String key) {
678 return i18n.getString("doap-person", Locale.ENGLISH, key).toLowerCase(Locale.ENGLISH);
679 }
680
681
682
683
684 private static String getPluginVersion() {
685 Properties pomProperties = new Properties();
686
687 try (InputStream is = DoapUtil.class.getResourceAsStream(
688 "/META-INF/maven/org.apache.maven.plugins/" + "maven-doap-plugin/pom.properties")) {
689 if (is == null) {
690 return "<unknown>";
691 }
692
693 pomProperties.load(is);
694
695 return pomProperties.getProperty("version", "<unknown>");
696 } catch (IOException e) {
697 return "<unknown>";
698 }
699 }
700
701
702
703
704 static class ReflectionValueExtractor {
705 @SuppressWarnings("rawtypes")
706 private static final Class[] CLASS_ARGS = new Class[0];
707
708 private static final Object[] OBJECT_ARGS = new Object[0];
709
710
711
712
713
714 @SuppressWarnings("rawtypes")
715 private static final Map<Class, ClassMap> CLASS_MAPS = new WeakHashMap<>();
716
717 private ReflectionValueExtractor() {}
718
719 public static Object evaluate(String expression, Object root) throws Exception {
720 return evaluate(expression, root, true);
721 }
722
723
724 public static Object evaluate(String expression, Object root, boolean trimRootToken) throws Exception {
725
726 if (trimRootToken) {
727 expression = expression.substring(expression.indexOf('.') + 1);
728 }
729
730 Object value = root;
731
732
733
734
735
736
737 StringTokenizer parser = new StringTokenizer(expression, ".");
738
739 while (parser.hasMoreTokens()) {
740 String token = parser.nextToken();
741 if (value == null) {
742 return null;
743 }
744
745 StringTokenizer parser2 = new StringTokenizer(token, "[]");
746 int index = -1;
747 if (parser2.countTokens() > 1) {
748 token = parser2.nextToken();
749 try {
750 index = Integer.parseInt(parser2.nextToken());
751 } catch (NumberFormatException e) {
752
753 }
754 }
755
756 final ClassMap classMap = getClassMap(value.getClass());
757
758 final String methodBase = StringUtils.capitalizeFirstLetter(token);
759
760 String methodName = "get" + methodBase;
761
762 Method method = classMap.findMethod(methodName, CLASS_ARGS);
763
764 if (method == null) {
765
766 methodName = "is" + methodBase;
767
768 method = classMap.findMethod(methodName, CLASS_ARGS);
769 }
770
771 if (method == null) {
772 return null;
773 }
774
775 value = method.invoke(value, OBJECT_ARGS);
776 if (value == null) {
777 return null;
778 }
779 if (Collection.class.isAssignableFrom(value.getClass())) {
780 ClassMap classMap2 = getClassMap(value.getClass());
781
782 Method method2 = classMap2.findMethod("toArray", CLASS_ARGS);
783
784 value = method2.invoke(value, OBJECT_ARGS);
785 }
786 if (value.getClass().isArray()) {
787 value = ((Object[]) value)[index];
788 }
789 }
790
791 return value;
792 }
793
794 private static ClassMap getClassMap(Class<? extends Object> clazz) {
795 ClassMap classMap = CLASS_MAPS.get(clazz);
796
797 if (classMap == null) {
798 classMap = new ClassMap(clazz);
799
800 CLASS_MAPS.put(clazz, classMap);
801 }
802
803 return classMap;
804 }
805 }
806 }