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