1 package org.apache.maven.report.projectinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.security.MessageDigest;
23 import java.security.NoSuchAlgorithmException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Properties;
30 import org.apache.maven.doxia.sink.Sink;
31 import org.apache.maven.model.Contributor;
32 import org.apache.maven.model.Developer;
33 import org.apache.maven.model.Model;
34 import org.apache.maven.plugin.logging.Log;
35 import org.apache.maven.plugins.annotations.Mojo;
36 import org.apache.maven.plugins.annotations.Parameter;
37 import org.codehaus.plexus.i18n.I18N;
38 import org.codehaus.plexus.util.StringUtils;
39
40
41
42
43
44
45
46
47 @Mojo( name = "project-team" )
48 public class TeamReport
49 extends AbstractProjectInfoReport
50 {
51
52
53
54
55
56
57
58
59
60
61 @Parameter( property = "teamlist.showAvatarImages", defaultValue = "true" )
62 private boolean showAvatarImages;
63
64
65
66
67
68 @Override
69 public boolean canGenerateReport()
70 {
71 boolean result = super.canGenerateReport();
72 if ( result && skipEmptyReport )
73 {
74 result = !isEmpty( getProject().getModel().getDevelopers() )
75 || !isEmpty( getProject().getModel().getContributors() );
76 }
77
78 return result;
79 }
80
81 @Override
82 public void executeReport( Locale locale )
83 {
84
85 ProjectTeamRenderer r =
86 new ProjectTeamRenderer( getSink(), project.getModel(), getI18N( locale ), locale, getLog(), showAvatarImages );
87
88
89 r.render();
90 }
91
92
93
94
95 public String getOutputName()
96 {
97 return "team-list";
98 }
99
100 @Override
101 protected String getI18Nsection()
102 {
103 return "team";
104 }
105
106
107
108
109
110
111
112
113 private static class ProjectTeamRenderer
114 extends AbstractProjectInfoRenderer
115 {
116 private static final String PROPERTIES = "properties";
117
118 private static final String TIME_ZONE = "timeZone";
119
120 private static final String ROLES = "roles";
121
122 private static final String ORGANIZATION_URL = "organizationUrl";
123
124 private static final String ORGANIZATION = "organization";
125
126 private static final String URL = "url";
127
128 private static final String EMAIL = "email";
129
130 private static final String NAME = "name";
131
132 private static final String IMAGE = "image";
133
134 private static final String ID = "id";
135
136 private final Model model;
137
138 private final Log log;
139
140 private final boolean showAvatarImages;
141
142 ProjectTeamRenderer( Sink sink, Model model, I18N i18n, Locale locale, Log log, boolean showAvatarImages )
143 {
144 super( sink, i18n, locale );
145
146 this.model = model;
147 this.log = log;
148 this.showAvatarImages = showAvatarImages;
149 }
150
151 @Override
152 protected String getI18Nsection()
153 {
154 return "team";
155 }
156
157 @Override
158 public void renderBody()
159 {
160 startSection( getI18nString( "intro.title" ) );
161
162
163 StringBuilder javascript = new StringBuilder();
164
165
166 paragraph( getI18nString( "intro.description1" ) );
167 paragraph( getI18nString( "intro.description2" ) );
168
169
170 List<Developer> developers = model.getDevelopers();
171
172 startSection( getI18nString( "developers.title" ) );
173
174 if ( isEmpty( developers ) )
175 {
176 paragraph( getI18nString( "nodeveloper" ) );
177 }
178 else
179 {
180 paragraph( getI18nString( "developers.intro" ) );
181
182 startTable();
183
184
185 Map<String, Boolean> headersMap = checkRequiredHeaders( developers );
186 String[] requiredHeaders = getRequiredDevHeaderArray( headersMap );
187
188 tableHeader( requiredHeaders );
189
190
191 int developersRowId = 0;
192 for ( Developer developer : developers )
193 {
194 renderTeamMember( developer, developersRowId, headersMap, javascript );
195
196 developersRowId++;
197 }
198
199 endTable();
200 }
201
202 endSection();
203
204
205 List<Contributor> contributors = model.getContributors();
206
207 startSection( getI18nString( "contributors.title" ) );
208
209 if ( isEmpty( contributors ) )
210 {
211 paragraph( getI18nString( "nocontributor" ) );
212 }
213 else
214 {
215 paragraph( getI18nString( "contributors.intro" ) );
216
217 startTable();
218
219 Map<String, Boolean> headersMap = checkRequiredHeaders( contributors );
220 String[] requiredHeaders = getRequiredContrHeaderArray( headersMap );
221
222 tableHeader( requiredHeaders );
223
224
225 int contributorsRowId = 0;
226 for ( Contributor contributor : contributors )
227 {
228 renderTeamMember( contributor, contributorsRowId, headersMap, javascript );
229
230 contributorsRowId++;
231 }
232
233 endTable();
234 }
235
236 endSection();
237
238 endSection();
239 }
240
241 private void renderTeamMember( Contributor member, int rowId, Map<String, Boolean> headersMap,
242 StringBuilder javascript )
243 {
244 sink.tableRow();
245
246 if ( headersMap.get( IMAGE ) == Boolean.TRUE && showAvatarImages )
247 {
248 Properties properties = member.getProperties();
249 String picUrl = properties.getProperty( "picUrl" );
250 if ( StringUtils.isEmpty( picUrl ) )
251 {
252 picUrl = getGravatarUrl( member.getEmail() );
253 }
254 if ( StringUtils.isEmpty( picUrl ) )
255 {
256 picUrl = getSpacerGravatarUrl();
257 }
258 sink.tableCell();
259 sink.figure();
260 sink.figureGraphics( picUrl );
261 sink.figure_();
262 sink.tableCell_();
263 }
264 String type = "contributor";
265 if ( member instanceof Developer )
266 {
267 type = "developer";
268 if ( headersMap.get( ID ) == Boolean.TRUE )
269 {
270 String id = ( (Developer) member ).getId();
271 if ( id == null )
272 {
273 tableCell( null );
274 }
275 else
276 {
277 tableCell( "<a name=\"" + id + "\"></a>" + id, true );
278 }
279 }
280 }
281 if ( headersMap.get( NAME ) == Boolean.TRUE )
282 {
283 tableCell( member.getName() );
284 }
285 if ( headersMap.get( EMAIL ) == Boolean.TRUE )
286 {
287 tableCell( createLinkPatternedText( member.getEmail(), member.getEmail() ) );
288 }
289 if ( headersMap.get( URL ) == Boolean.TRUE )
290 {
291 tableCellForUrl( member.getUrl() );
292 }
293 if ( headersMap.get( ORGANIZATION ) == Boolean.TRUE )
294 {
295 tableCell( member.getOrganization() );
296 }
297 if ( headersMap.get( ORGANIZATION_URL ) == Boolean.TRUE )
298 {
299 tableCellForUrl( member.getOrganizationUrl() );
300 }
301 if ( headersMap.get( ROLES ) == Boolean.TRUE )
302 {
303 if ( member.getRoles() != null )
304 {
305
306 List<String> var = member.getRoles();
307 tableCell( StringUtils.join( var.toArray( new String[var.size()] ), ", " ) );
308 }
309 else
310 {
311 tableCell( null );
312 }
313 }
314 if ( headersMap.get( TIME_ZONE ) == Boolean.TRUE )
315 {
316 tableCell( member.getTimezone() );
317 }
318
319 if ( headersMap.get( PROPERTIES ) == Boolean.TRUE )
320 {
321 Properties props = member.getProperties();
322 if ( props != null )
323 {
324 tableCell( propertiesToString( props ) );
325 }
326 else
327 {
328 tableCell( null );
329 }
330 }
331
332 sink.tableRow_();
333 }
334
335 private static final String AVATAR_SIZE = "s=60";
336
337 private String getSpacerGravatarUrl()
338 {
339 return "http://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&f=y&" + AVATAR_SIZE;
340 }
341
342 private String getGravatarUrl( String email )
343 {
344 if ( email == null )
345 {
346 return null;
347 }
348 email = StringUtils.trim( email );
349 email = email.toLowerCase();
350 MessageDigest md;
351 try
352 {
353 md = MessageDigest.getInstance( "MD5" );
354 md.update( email.getBytes() );
355 byte byteData[] = md.digest();
356 StringBuilder sb = new StringBuilder();
357 final int lowerEightBitsOnly = 0xff;
358 for ( byte aByteData : byteData )
359 {
360 sb.append( Integer.toString( ( aByteData & lowerEightBitsOnly ) + 0x100, 16 ).substring( 1 ) );
361 }
362 return "http://www.gravatar.com/avatar/" + sb.toString() + "?d=mm&" + AVATAR_SIZE;
363 }
364 catch ( NoSuchAlgorithmException e )
365 {
366 return null;
367 }
368 }
369
370
371
372
373
374 private String[] getRequiredContrHeaderArray( Map<String, Boolean> requiredHeaders )
375 {
376 List<String> requiredArray = new ArrayList<String>();
377 String image = getI18nString( "contributors.image" );
378 String name = getI18nString( "contributors.name" );
379 String email = getI18nString( "contributors.email" );
380 String url = getI18nString( "contributors.url" );
381 String organization = getI18nString( "contributors.organization" );
382 String organizationUrl = getI18nString( "contributors.organizationurl" );
383 String roles = getI18nString( "contributors.roles" );
384 String timeZone = getI18nString( "contributors.timezone" );
385 String actualTime = getI18nString( "contributors.actualtime" );
386 String properties = getI18nString( "contributors.properties" );
387 if ( requiredHeaders.get( IMAGE ) == Boolean.TRUE && showAvatarImages )
388 {
389 requiredArray.add( image );
390 }
391 setRequiredArray( requiredHeaders, requiredArray, image, name, email, url, organization, organizationUrl,
392 roles, timeZone, actualTime, properties );
393
394 return requiredArray.toArray( new String[requiredArray.size()] );
395 }
396
397
398
399
400
401 private String[] getRequiredDevHeaderArray( Map<String, Boolean> requiredHeaders )
402 {
403 List<String> requiredArray = new ArrayList<String>();
404
405 String image = getI18nString( "developers.image" );
406 String id = getI18nString( "developers.id" );
407 String name = getI18nString( "developers.name" );
408 String email = getI18nString( "developers.email" );
409 String url = getI18nString( "developers.url" );
410 String organization = getI18nString( "developers.organization" );
411 String organizationUrl = getI18nString( "developers.organizationurl" );
412 String roles = getI18nString( "developers.roles" );
413 String timeZone = getI18nString( "developers.timezone" );
414 String actualTime = getI18nString( "developers.actualtime" );
415 String properties = getI18nString( "developers.properties" );
416
417 if ( requiredHeaders.get( IMAGE ) == Boolean.TRUE && showAvatarImages )
418 {
419 requiredArray.add( image );
420 }
421 if ( requiredHeaders.get( ID ) == Boolean.TRUE )
422 {
423 requiredArray.add( id );
424 }
425
426 setRequiredArray( requiredHeaders, requiredArray, image, name, email, url, organization, organizationUrl,
427 roles, timeZone, actualTime, properties );
428
429 return requiredArray.toArray( new String[requiredArray.size()] );
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 private void setRequiredArray( Map<String, Boolean> requiredHeaders, List<String> requiredArray, String image,
447 String name, String email, String url, String organization,
448 String organizationUrl, String roles, String timeZone, String actualTime,
449 String properties )
450 {
451 if ( requiredHeaders.get( NAME ) == Boolean.TRUE )
452 {
453 requiredArray.add( name );
454 }
455 if ( requiredHeaders.get( EMAIL ) == Boolean.TRUE )
456 {
457 requiredArray.add( email );
458 }
459 if ( requiredHeaders.get( URL ) == Boolean.TRUE )
460 {
461 requiredArray.add( url );
462 }
463 if ( requiredHeaders.get( ORGANIZATION ) == Boolean.TRUE )
464 {
465 requiredArray.add( organization );
466 }
467 if ( requiredHeaders.get( ORGANIZATION_URL ) == Boolean.TRUE )
468 {
469 requiredArray.add( organizationUrl );
470 }
471 if ( requiredHeaders.get( ROLES ) == Boolean.TRUE )
472 {
473 requiredArray.add( roles );
474 }
475 if ( requiredHeaders.get( TIME_ZONE ) == Boolean.TRUE )
476 {
477 requiredArray.add( timeZone );
478 }
479
480 if ( requiredHeaders.get( PROPERTIES ) == Boolean.TRUE )
481 {
482 requiredArray.add( properties );
483 }
484 }
485
486
487
488
489
490 private Map<String, Boolean> checkRequiredHeaders( List<? extends Contributor> units )
491 {
492 Map<String, Boolean> requiredHeaders = new HashMap<String, Boolean>();
493
494 requiredHeaders.put( IMAGE, Boolean.FALSE );
495 requiredHeaders.put( ID, Boolean.FALSE );
496 requiredHeaders.put( NAME, Boolean.FALSE );
497 requiredHeaders.put( EMAIL, Boolean.FALSE );
498 requiredHeaders.put( URL, Boolean.FALSE );
499 requiredHeaders.put( ORGANIZATION, Boolean.FALSE );
500 requiredHeaders.put( ORGANIZATION_URL, Boolean.FALSE );
501 requiredHeaders.put( ROLES, Boolean.FALSE );
502 requiredHeaders.put( TIME_ZONE, Boolean.FALSE );
503 requiredHeaders.put( PROPERTIES, Boolean.FALSE );
504
505 for ( Contributor unit : units )
506 {
507 if ( unit instanceof Developer )
508 {
509 Developer developer = (Developer) unit;
510 if ( StringUtils.isNotEmpty( developer.getId() ) )
511 {
512 requiredHeaders.put( ID, Boolean.TRUE );
513 }
514 }
515 if ( StringUtils.isNotEmpty( unit.getName() ) )
516 {
517 requiredHeaders.put( NAME, Boolean.TRUE );
518 }
519 if ( StringUtils.isNotEmpty( unit.getEmail() ) )
520 {
521 requiredHeaders.put( EMAIL, Boolean.TRUE );
522 requiredHeaders.put( IMAGE, Boolean.TRUE );
523 }
524 if ( StringUtils.isNotEmpty( unit.getUrl() ) )
525 {
526 requiredHeaders.put( URL, Boolean.TRUE );
527 }
528 if ( StringUtils.isNotEmpty( unit.getOrganization() ) )
529 {
530 requiredHeaders.put( ORGANIZATION, Boolean.TRUE );
531 }
532 if ( StringUtils.isNotEmpty( unit.getOrganizationUrl() ) )
533 {
534 requiredHeaders.put( ORGANIZATION_URL, Boolean.TRUE );
535 }
536 if ( !isEmpty( unit.getRoles() ) )
537 {
538 requiredHeaders.put( ROLES, Boolean.TRUE );
539 }
540 if ( StringUtils.isNotEmpty( unit.getTimezone() ) )
541 {
542 requiredHeaders.put( TIME_ZONE, Boolean.TRUE );
543 }
544 Properties properties = unit.getProperties();
545 boolean hasPicUrl = properties.containsKey( "picUrl" );
546 if ( hasPicUrl )
547 {
548 requiredHeaders.put( IMAGE, Boolean.TRUE );
549 }
550 boolean isJustAnImageProperty = properties.size() == 1 && hasPicUrl;
551 if ( !isJustAnImageProperty && !properties.isEmpty() )
552 {
553 requiredHeaders.put( PROPERTIES, Boolean.TRUE );
554 }
555 }
556 return requiredHeaders;
557 }
558
559
560
561
562
563
564 private void tableCellForUrl( String url )
565 {
566 sink.tableCell();
567
568 if ( StringUtils.isEmpty( url ) )
569 {
570 text( url );
571 }
572 else
573 {
574 link( url, url );
575 }
576
577 sink.tableCell_();
578 }
579
580 private boolean isEmpty( List<?> list )
581 {
582 return ( list == null ) || list.isEmpty();
583 }
584 }
585 }