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,
309 List<Contributor> developersOrContributors )
310 {
311 return filterContributorsByDoapRoles( i18n, developersOrContributors ).get( "helpers" );
312 }
313
314
315
316
317
318
319 public static List<Contributor> getContributorsWithMaintainerRole( I18N i18n,
320 List<Contributor> developersOrContributors )
321 {
322 return filterContributorsByDoapRoles( i18n, developersOrContributors ).get( "maintainers" );
323 }
324
325
326
327
328
329
330 public static List<Contributor> getContributorsWithTesterRole( I18N i18n,
331 List<Contributor> developersOrContributors )
332 {
333 return filterContributorsByDoapRoles( i18n, developersOrContributors ).get( "testers" );
334 }
335
336
337
338
339
340
341 public static List<Contributor> getContributorsWithTranslatorRole( I18N i18n,
342 List<Contributor> developersOrContributors )
343 {
344 return filterContributorsByDoapRoles( i18n, developersOrContributors ).get( "translators" );
345 }
346
347
348
349
350
351
352 public static List<Contributor> getContributorsWithUnknownRole( I18N i18n,
353 List<Contributor> developersOrContributors )
354 {
355 return filterContributorsByDoapRoles( i18n, developersOrContributors ).get( "unknowns" );
356 }
357
358
359
360
361
362
363
364
365 public static class Organization
366 {
367 private String name;
368
369 private String url;
370
371 private List<String> members = new LinkedList<String>();
372
373 public Organization( String name, String url )
374 {
375 this.name = name;
376 this.url = url;
377 }
378
379 public void setName( String name )
380 {
381 this.name = name;
382 }
383
384 public String getName()
385 {
386 return name;
387 }
388
389 public void setUrl( String url )
390 {
391 this.url = url;
392 }
393
394 public String getUrl()
395 {
396 return url;
397 }
398
399 public void addMember( String nodeId )
400 {
401 members.add( nodeId );
402 }
403
404 public List<String> getMembers()
405 {
406 return members;
407 }
408 }
409
410
411
412
413
414
415
416
417 public static DoapUtil.Organization addOrganization( String name, String url )
418 {
419 Organization organization = organizations.get( name );
420
421 if ( organization == null )
422 {
423 organization = new DoapUtil.Organization( name, url );
424 }
425
426 organizations.put( name, organization );
427
428 return organization;
429 }
430
431
432 private static int nodeNumber = 1;
433
434
435
436
437
438
439
440
441 public static String getNodeId()
442 {
443 return "b" + nodeNumber++;
444 }
445
446
447
448
449
450
451 public static Set<Entry<String, DoapUtil.Organization>> getOrganizations()
452 {
453 return organizations.entrySet();
454 }
455
456
457
458
459
460
461
462
463 public static List<String> validate( File doapFile )
464 {
465 if ( doapFile == null || !doapFile.isFile() )
466 {
467 throw new IllegalArgumentException( "The DOAP file should exist" );
468 }
469
470 Model model = ModelFactory.createDefaultModel();
471 RDFReader r = model.getReader( "RDF/XML" );
472 r.setProperty( "error-mode", "strict-error" );
473 final List<String> errors = new ArrayList<String>();
474 r.setErrorHandler( new RDFDefaultErrorHandler()
475 {
476 @Override
477 public void error( Exception e )
478 {
479 errors.add( e.getMessage() );
480 }
481 } );
482
483 try
484 {
485 r.read( model, doapFile.toURI().toURL().toString() );
486 }
487 catch ( MalformedURLException e )
488 {
489
490 }
491
492 return errors;
493 }
494
495
496
497
498
499
500 public static boolean isValidEmail( String str )
501 {
502 if ( StringUtils.isEmpty( str ) )
503 {
504 return false;
505 }
506
507 Matcher matcher = EMAIL_PATTERN.matcher( str );
508 return matcher.matches();
509 }
510
511
512
513
514
515
516
517
518
519
520 public static void fetchURL( Settings settings, URL url )
521 throws IOException
522 {
523 if ( url == null )
524 {
525 throw new IllegalArgumentException( "The url is null" );
526 }
527
528 if ( "file".equals( url.getProtocol() ) )
529 {
530 InputStream in = null;
531 try
532 {
533 in = url.openStream();
534 }
535 finally
536 {
537 IOUtil.close( in );
538 }
539
540 return;
541 }
542
543
544 HttpClient httpClient = new HttpClient( new MultiThreadedHttpConnectionManager() );
545 httpClient.getHttpConnectionManager().getParams().setConnectionTimeout( DEFAULT_TIMEOUT );
546 httpClient.getHttpConnectionManager().getParams().setSoTimeout( DEFAULT_TIMEOUT );
547 httpClient.getParams().setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
548
549
550 httpClient.getParams().setParameter( HttpMethodParams.USER_AGENT,
551 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" );
552
553 if ( settings != null && settings.getActiveProxy() != null )
554 {
555 Proxy activeProxy = settings.getActiveProxy();
556
557 ProxyInfo proxyInfo = new ProxyInfo();
558 proxyInfo.setNonProxyHosts( activeProxy.getNonProxyHosts() );
559
560 if ( StringUtils.isNotEmpty( activeProxy.getHost() )
561 && !ProxyUtils.validateNonProxyHosts( proxyInfo, url.getHost() ) )
562 {
563 httpClient.getHostConfiguration().setProxy( activeProxy.getHost(), activeProxy.getPort() );
564
565 if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) && activeProxy.getPassword() != null )
566 {
567 Credentials credentials =
568 new UsernamePasswordCredentials( activeProxy.getUsername(), activeProxy.getPassword() );
569
570 httpClient.getState().setProxyCredentials( AuthScope.ANY, credentials );
571 }
572 }
573 }
574
575 GetMethod getMethod = new GetMethod( url.toString() );
576 try
577 {
578 int status;
579 try
580 {
581 status = httpClient.executeMethod( getMethod );
582 }
583 catch ( SocketTimeoutException e )
584 {
585
586 status = httpClient.executeMethod( getMethod );
587 }
588
589 if ( status != HttpStatus.SC_OK )
590 {
591 throw new FileNotFoundException( url.toString() );
592 }
593 }
594 finally
595 {
596 getMethod.releaseConnection();
597 }
598 }
599
600
601
602
603
604
605
606
607
608
609 public static String interpolate( String value, final MavenProject project, Settings settings )
610 {
611 if ( project == null )
612 {
613 throw new IllegalArgumentException( "project is required" );
614 }
615
616 if ( value == null )
617 {
618 return value;
619 }
620
621 if ( !value.contains( "${" ) )
622 {
623 return value.trim();
624 }
625
626 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
627 try
628 {
629 interpolator.addValueSource( new EnvarBasedValueSource() );
630 }
631 catch ( IOException e )
632 {
633
634 }
635 interpolator.addValueSource( new PropertiesBasedValueSource( System.getProperties() ) );
636 interpolator.addValueSource( new PropertiesBasedValueSource( project.getProperties() ) );
637 interpolator.addValueSource( new PrefixedObjectValueSource( "project", project ) );
638 interpolator.addValueSource( new PrefixedObjectValueSource( "pom", project ) );
639 interpolator.addValueSource( new ObjectBasedValueSource( project )
640 {
641 @Override
642 public Object getValue( String expression )
643 {
644 try
645 {
646 return ReflectionValueExtractor.evaluate( expression, project, true );
647 }
648 catch ( Exception e )
649 {
650 addFeedback( "Failed to extract \'" + expression + "\' from: " + project, e );
651 }
652
653 return null;
654 }
655 } );
656
657 if ( settings != null )
658 {
659 interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
660 }
661
662 String interpolatedValue = value;
663 try
664 {
665 interpolatedValue = interpolator.interpolate( value ).trim();
666 }
667 catch ( InterpolationException e )
668 {
669
670 }
671
672 if ( interpolatedValue.startsWith( "${" ) )
673 {
674 return null;
675 }
676
677 return interpolatedValue;
678 }
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696 private static Map<String, List<Contributor>> filterContributorsByDoapRoles( I18N i18n,
697 List<Contributor> developersOrContributors )
698 {
699 Map<String, List<Contributor>> returnMap = new HashMap<String, List<Contributor>>( 7 );
700 returnMap.put( "maintainers", new ArrayList<Contributor>() );
701 returnMap.put( "developers", new ArrayList<Contributor>() );
702 returnMap.put( "documenters", new ArrayList<Contributor>() );
703 returnMap.put( "translators", new ArrayList<Contributor>() );
704 returnMap.put( "testers", new ArrayList<Contributor>() );
705 returnMap.put( "helpers", new ArrayList<Contributor>() );
706 returnMap.put( "unknowns", new ArrayList<Contributor>() );
707
708 if ( developersOrContributors == null || developersOrContributors.isEmpty() )
709 {
710 return returnMap;
711 }
712
713 for ( Contributor contributor : developersOrContributors )
714 {
715 List<String> roles = contributor.getRoles();
716
717 if ( roles != null && roles.size() != 0 )
718 {
719 for ( String role : roles )
720 {
721 role = role.toLowerCase( Locale.ENGLISH );
722 if ( role.contains( getLowerCaseString( i18n, "doap.maintainer" ) ) )
723 {
724 if ( !returnMap.get( "maintainers" ).contains( contributor ) )
725 {
726 returnMap.get( "maintainers" ).add( contributor );
727 }
728 }
729 else if ( role.contains( getLowerCaseString( i18n, "doap.developer" ) ) )
730 {
731 if ( !returnMap.get( "developers" ).contains( contributor ) )
732 {
733 returnMap.get( "developers" ).add( contributor );
734 }
735 }
736 else if ( role.contains( getLowerCaseString( i18n, "doap.documenter" ) ) )
737 {
738 if ( !returnMap.get( "documenters" ).contains( contributor ) )
739 {
740 returnMap.get( "documenters" ).add( contributor );
741 }
742 }
743 else if ( role.contains( getLowerCaseString( i18n, "doap.translator" ) ) )
744 {
745 if ( !returnMap.get( "translators" ).contains( contributor ) )
746 {
747 returnMap.get( "translators" ).add( contributor );
748 }
749 }
750 else if ( role.contains( getLowerCaseString( i18n, "doap.tester" ) ) )
751 {
752 if ( !returnMap.get( "testers" ).contains( contributor ) )
753 {
754 returnMap.get( "testers" ).add( contributor );
755 }
756 }
757 else if ( role.contains( getLowerCaseString( i18n, "doap.helper" ) ) )
758 {
759 if ( !returnMap.get( "helpers" ).contains( contributor ) )
760 {
761 returnMap.get( "helpers" ).add( contributor );
762 }
763 }
764 else if ( role.contains( getLowerCaseString( i18n, "doap.emeritus" ) ) )
765 {
766
767 }
768 else
769 {
770 if ( !returnMap.get( "unknowns" ).contains( contributor ) )
771 {
772 returnMap.get( "unknowns" ).add( contributor );
773 }
774 }
775 }
776 }
777 else
778 {
779 if ( !returnMap.get( "unknowns" ).contains( contributor ) )
780 {
781 returnMap.get( "unknowns" ).add( contributor );
782 }
783 }
784 }
785
786 return returnMap;
787 }
788
789
790
791
792
793
794 private static String getLowerCaseString( I18N i18n, String key )
795 {
796 return i18n.getString( "doap-person", Locale.ENGLISH, key ).toLowerCase( Locale.ENGLISH );
797 }
798
799
800
801
802 private static String getPluginVersion()
803 {
804 Properties pomProperties = new Properties();
805 InputStream is = null;
806 try
807 {
808 is =
809 DoapUtil.class.getResourceAsStream( "/META-INF/maven/org.apache.maven.plugins/"
810 + "maven-doap-plugin/pom.properties" );
811 if ( is == null )
812 {
813 return "<unknown>";
814 }
815
816 pomProperties.load( is );
817
818 return pomProperties.getProperty( "version", "<unknown>" );
819 }
820 catch ( IOException e )
821 {
822 return "<unknown>";
823 }
824 finally
825 {
826 IOUtil.close( is );
827 }
828 }
829
830
831
832
833 static class ReflectionValueExtractor
834 {
835 @SuppressWarnings( "rawtypes" )
836 private static final Class[] CLASS_ARGS = new Class[0];
837
838 private static final Object[] OBJECT_ARGS = new Object[0];
839
840
841
842
843
844 @SuppressWarnings( "rawtypes" )
845 private static final Map<Class, ClassMap> CLASS_MAPS = new WeakHashMap<Class, ClassMap>();
846
847 private ReflectionValueExtractor()
848 {
849 }
850
851 public static Object evaluate( String expression, Object root )
852 throws Exception
853 {
854 return evaluate( expression, root, true );
855 }
856
857
858 public static Object evaluate( String expression, Object root, boolean trimRootToken )
859 throws Exception
860 {
861
862 if ( trimRootToken )
863 {
864 expression = expression.substring( expression.indexOf( '.' ) + 1 );
865 }
866
867 Object value = root;
868
869
870
871
872
873
874 StringTokenizer parser = new StringTokenizer( expression, "." );
875
876 while ( parser.hasMoreTokens() )
877 {
878 String token = parser.nextToken();
879 if ( value == null )
880 {
881 return null;
882 }
883
884 StringTokenizer parser2 = new StringTokenizer( token, "[]" );
885 int index = -1;
886 if ( parser2.countTokens() > 1 )
887 {
888 token = parser2.nextToken();
889 try
890 {
891 index = Integer.valueOf( parser2.nextToken() ).intValue();
892 }
893 catch ( NumberFormatException e )
894 {
895
896 }
897 }
898
899 final ClassMap classMap = getClassMap( value.getClass() );
900
901 final String methodBase = StringUtils.capitalizeFirstLetter( token );
902
903 String methodName = "get" + methodBase;
904
905 Method method = classMap.findMethod( methodName, CLASS_ARGS );
906
907 if ( method == null )
908 {
909
910 methodName = "is" + methodBase;
911
912 method = classMap.findMethod( methodName, CLASS_ARGS );
913 }
914
915 if ( method == null )
916 {
917 return null;
918 }
919
920 value = method.invoke( value, OBJECT_ARGS );
921 if ( value == null )
922 {
923 return null;
924 }
925 if ( Collection.class.isAssignableFrom( value.getClass() ) )
926 {
927 ClassMap classMap2 = getClassMap( value.getClass() );
928
929 Method method2 = classMap2.findMethod( "toArray", CLASS_ARGS );
930
931 value = method2.invoke( value, OBJECT_ARGS );
932 }
933 if ( value.getClass().isArray() )
934 {
935 value = ( (Object[]) value )[index];
936 }
937 }
938
939 return value;
940 }
941
942 private static ClassMap getClassMap( Class<? extends Object> clazz )
943 {
944 ClassMap classMap = CLASS_MAPS.get( clazz );
945
946 if ( classMap == null )
947 {
948 classMap = new ClassMap( clazz );
949
950 CLASS_MAPS.put( clazz, classMap );
951 }
952
953 return classMap;
954 }
955 }
956 }