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