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