View Javadoc

1   /*
2    *  jDTAUS Container Mojo
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.container.mojo;
22  
23  import java.io.File;
24  import java.util.Collection;
25  import java.util.HashSet;
26  import java.util.LinkedList;
27  import java.util.Locale;
28  import java.util.Set;
29  import org.apache.maven.doxia.siterenderer.Renderer;
30  import org.apache.maven.doxia.tools.SiteTool;
31  import org.apache.maven.project.MavenProject;
32  import org.apache.maven.reporting.AbstractMavenReport;
33  import org.apache.maven.reporting.MavenReport;
34  import org.apache.maven.reporting.MavenReportException;
35  import org.jdtaus.core.container.ContainerError;
36  import org.jdtaus.core.container.ContextError;
37  import org.jdtaus.core.container.Dependencies;
38  import org.jdtaus.core.container.Dependency;
39  import org.jdtaus.core.container.Implementation;
40  import org.jdtaus.core.container.Message;
41  import org.jdtaus.core.container.Messages;
42  import org.jdtaus.core.container.MissingModuleException;
43  import org.jdtaus.core.container.Model;
44  import org.jdtaus.core.container.ModelError;
45  import org.jdtaus.core.container.ModelFactory;
46  import org.jdtaus.core.container.Module;
47  import org.jdtaus.core.container.Properties;
48  import org.jdtaus.core.container.Property;
49  import org.jdtaus.core.container.Specification;
50  
51  /**
52   * Generates a report for the artifact's container metadata.
53   *
54   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
55   * @version $JDTAUS: ContainerReport.java 8743 2012-10-07 03:06:20Z schulte $
56   * @goal report
57   * @requiresDependencyResolution runtime
58   */
59  public class ContainerReport extends AbstractMavenReport
60  {
61      //--Configuration-----------------------------------------------------------
62  
63      /**
64       * The output directory for the report. Note that this parameter is only
65       * evaluated if the goal is run directly from the command line. If the goal
66       * is run indirectly as part of a site generation, the output directory
67       * configured in Maven Site Plugin is used instead.
68       *
69       * @parameter default-value="${project.reporting.outputDirectory}"
70       * @required
71       */
72      private File outputDirectory;
73  
74      /**
75       * Build output directory.
76       * @parameter expression="${project.build.directory}/container"
77       */
78      private File buildOutputDirectory;
79  
80      /**
81       * Name of the module to generate a report for.
82       *
83       * @parameter default-value="${project.name}"
84       * @optional
85       */
86      private String moduleName;
87  
88      /**
89       * Location of the javadoc documentation to link to. Keep this in sync with
90       * the javadoc plugin.
91       *
92       * @parameter default-value="apidocs"
93       * @optional
94       */
95      private String javadocDirectory;
96  
97      /**
98       * {@code true} to generate links to javadoc documentation. {@code false}
99       * to not generate links to javadoc documentation.
100      *
101      * @parameter default-value="true"
102      * @optional
103      */
104     private boolean linkJavadoc;
105 
106     /**
107      * Generates the site report.
108      *
109      * @component
110      * @required
111      * @readonly
112      */
113     private Renderer siteRenderer;
114 
115     /**
116      * @parameter expression="${project}"
117      * @required
118      * @readonly
119      */
120     private MavenProject project;
121 
122     /**
123      * SiteTool.
124      *
125      * @component role="org.apache.maven.doxia.tools.SiteTool"
126      * @required
127      * @readonly
128      */
129     private SiteTool siteTool;
130 
131     /**
132      * Gets the Site Tool implementation.
133      *
134      * @return Site Tool implementation.
135      */
136     protected final SiteTool getSiteTool()
137     {
138         return this.siteTool;
139     }
140 
141     //-----------------------------------------------------------Configuration--
142     //--AbstractMavenReport-----------------------------------------------------
143 
144     public String getOutputName()
145     {
146         return "container-report";
147     }
148 
149     public String getCategoryName()
150     {
151         return MavenReport.CATEGORY_PROJECT_REPORTS;
152     }
153 
154     public String getName( final Locale locale )
155     {
156         return ContainerReportBundle.getInstance().
157             getReportNameMessage( locale );
158 
159     }
160 
161     public String getDescription( final Locale locale )
162     {
163         return ContainerReportBundle.getInstance().
164             getReportDescriptionMessage( locale );
165 
166     }
167 
168     public void setReportOutputDirectory( final File reportOutputDirectory )
169     {
170         super.setReportOutputDirectory( reportOutputDirectory );
171         this.outputDirectory = reportOutputDirectory;
172     }
173 
174     public boolean canGenerateReport()
175     {
176         boolean canGenerate = super.canGenerateReport();
177 
178         if ( canGenerate )
179         {
180             final File modelFile = new File( this.buildOutputDirectory,
181                                              "container-report.xml" );
182 
183             canGenerate = modelFile.exists();
184         }
185 
186         return canGenerate;
187     }
188 
189     protected Renderer getSiteRenderer()
190     {
191         return this.siteRenderer;
192     }
193 
194     protected String getOutputDirectory()
195     {
196         return this.outputDirectory.getAbsolutePath();
197     }
198 
199     protected MavenProject getProject()
200     {
201         return this.project;
202     }
203 
204     protected void executeReport( final Locale locale )
205         throws MavenReportException
206     {
207         final ClassLoader mavenLoader =
208                           Thread.currentThread().getContextClassLoader();
209 
210         try
211         {
212             final File modelFile = new File( this.buildOutputDirectory,
213                                              "container-report.xml" );
214 
215             final ResourceLoader resourceLoader = new ResourceLoader(
216                 this.getClass().getClassLoader() );
217 
218             resourceLoader.addResource( "META-INF/jdtaus/module.xml",
219                                         modelFile.toURI().toURL() );
220 
221             Thread.currentThread().setContextClassLoader( resourceLoader );
222             AbstractContainerMojo.enableThreadContextClassLoader();
223 
224             this.getSink().head();
225             this.getSink().title();
226             this.getSink().text( this.getName( locale ) );
227             this.getSink().title_();
228             this.getSink().head_();
229 
230             this.getSink().body();
231 
232             this.getSink().section1();
233             this.getSink().sectionTitle1();
234             this.getSink().text(
235                 ContainerReportBundle.getInstance().
236                 getOverviewTitleMessage( locale ) );
237 
238             this.getSink().sectionTitle1_();
239 
240             this.getSink().paragraph();
241             this.getSink().text( this.getDescription( locale ) );
242             this.getSink().paragraph_();
243 
244             final Model model = ModelFactory.newModel();
245             if ( this.moduleName != null )
246             {
247                 final Module module = model.getModules().
248                     getModule( this.moduleName );
249 
250                 this.generateModuleReport( module, locale );
251 
252                 this.generateUnresolvedDependenciesReport(
253                     this.getUnresolvedDependencies( model ), locale );
254 
255                 // End the overview section.
256                 this.getSink().section1_();
257 
258                 if ( module.getImplementations().size() > 0 ||
259                      module.getSpecifications().size() > 0 )
260                 {
261                     this.getSink().section1();
262                     this.getSink().sectionTitle1();
263                     this.getSink().text(
264                         ContainerReportBundle.getInstance().
265                         getDetailsTitleMessage( locale ) );
266 
267                     this.getSink().sectionTitle1_();
268 
269                     this.generateDetails( module, locale );
270 
271                     this.getSink().section1_();
272                 }
273             }
274             else
275             {
276                 this.generateUnresolvedDependenciesReport(
277                     this.getUnresolvedDependencies( model ), locale );
278 
279                 // End the overview section.
280                 this.getSink().section1_();
281             }
282 
283             this.getSink().body_();
284         }
285         catch ( final ContextError e )
286         {
287             final MavenReportException mre =
288                 new MavenReportException( e.getMessage() );
289 
290             mre.initCause( e );
291             throw mre;
292         }
293         catch ( final ContainerError e )
294         {
295             final MavenReportException mre =
296                 new MavenReportException( e.getMessage() );
297 
298             mre.initCause( e );
299             throw mre;
300         }
301         catch ( final ModelError e )
302         {
303             final MavenReportException mre =
304                 new MavenReportException( e.getMessage() );
305 
306             mre.initCause( e );
307             throw mre;
308         }
309         catch ( final MissingModuleException e )
310         {
311             this.getSink().paragraph();
312             this.getSink().text( ContainerReportBundle.getInstance().
313                 getNotAModuleMessage( locale ) );
314 
315             this.getSink().paragraph_();
316             // End the overview section.
317             this.getSink().section1_();
318             this.getSink().body_();
319         }
320         catch ( final Exception e )
321         {
322             throw new MavenReportException( e.getMessage(), e );
323         }
324         finally
325         {
326             AbstractContainerMojo.disableThreadContextClassLoader();
327             Thread.currentThread().setContextClassLoader( mavenLoader );
328         }
329     }
330 
331     //-----------------------------------------------------AbstractMavenReport--
332     //--ContainerReport---------------------------------------------------------
333 
334     /** Creates a new {@code ContainerReport} instance. */
335     public ContainerReport()
336     {
337         super();
338     }
339 
340     private void generateModuleReport( final Module model,
341                                        final Locale locale )
342     {
343         this.getSink().section2();
344         this.getSink().sectionTitle2();
345         this.getSink().text(
346             ContainerReportBundle.getInstance().
347             getModuleTitleMessage( locale ) );
348 
349         this.getSink().sectionTitle2_();
350 
351         this.getSink().paragraph();
352         this.getSink().text(
353             ContainerReportBundle.getInstance().
354             getModuleDescriptionMessage( locale ) );
355 
356         this.getSink().table();
357         this.getSink().tableRow();
358         this.getSink().tableHeaderCell();
359         this.getSink().text(
360             ContainerReportBundle.getInstance().
361             getModuleNameHeaderMessage( locale ) );
362 
363         this.getSink().tableHeaderCell_();
364 
365         this.getSink().tableCell();
366         this.getSink().monospaced();
367         this.getSink().text( model.getName() );
368         this.getSink().monospaced_();
369         this.getSink().tableCell_();
370 
371         this.getSink().tableRow_();
372 
373         if ( model.getDocumentation().getLocales().length > 0 )
374         {
375             this.getSink().tableRow();
376 
377             this.getSink().tableHeaderCell();
378             this.getSink().text(
379                 ContainerReportBundle.getInstance().
380                 getModuleDescriptionHeaderMessage( locale ) );
381 
382             this.getSink().tableHeaderCell_();
383 
384             this.getSink().tableCell();
385             this.getSink().monospaced();
386             this.getSink().text( model.getDocumentation().getValue( locale ) );
387             this.getSink().monospaced_();
388             this.getSink().tableCell_();
389             this.getSink().tableRow_();
390         }
391 
392         this.getSink().tableRow();
393 
394         this.getSink().tableHeaderCell();
395         this.getSink().text(
396             ContainerReportBundle.getInstance().
397             getModuleVersionHeaderMessage( locale ) );
398 
399         this.getSink().tableHeaderCell_();
400 
401         this.getSink().tableCell();
402         this.getSink().monospaced();
403         this.getSink().text( model.getVersion() );
404         this.getSink().monospaced_();
405         this.getSink().tableCell_();
406 
407         this.getSink().tableRow_();
408         this.getSink().table_();
409 
410         this.getSink().paragraph_();
411         this.getSink().section2_();
412 
413         this.getSink().section2();
414         this.getSink().sectionTitle2();
415         this.getSink().text(
416             ContainerReportBundle.getInstance().
417             getModuleSpecTitleMessage( locale ) );
418 
419         this.getSink().sectionTitle2_();
420 
421         this.getSink().paragraph();
422 
423         if ( model.getSpecifications().size() > 0 )
424         {
425             this.getSink().text(
426                 ContainerReportBundle.getInstance().
427                 getModuleSpecificationDescriptionMessage( locale ) );
428 
429             this.getSink().table();
430             this.getSink().tableRow();
431 
432             this.getSink().tableHeaderCell();
433             this.getSink().text(
434                 ContainerReportBundle.getInstance().
435                 getModuleSpecIdentifierHeaderMessage( locale ) );
436 
437             this.getSink().tableHeaderCell_();
438 
439             this.getSink().tableHeaderCell();
440             this.getSink().text(
441                 ContainerReportBundle.getInstance().
442                 getModuleSpecVendorHeaderMessage( locale ) );
443 
444             this.getSink().tableHeaderCell_();
445 
446             this.getSink().tableHeaderCell();
447             this.getSink().text(
448                 ContainerReportBundle.getInstance().
449                 getModuleSpecVersionHeaderMessage( locale ) );
450 
451             this.getSink().tableHeaderCell_();
452 
453             this.getSink().tableHeaderCell();
454             this.getSink().text(
455                 ContainerReportBundle.getInstance().
456                 getModuleSpecMultiplicityHeaderMessage( locale ) );
457 
458             this.getSink().tableHeaderCell_();
459 
460             this.getSink().tableHeaderCell();
461             this.getSink().text(
462                 ContainerReportBundle.getInstance().
463                 getModuleSpecScopeHeaderMessage( locale ) );
464 
465             this.getSink().tableHeaderCell_();
466 
467             this.getSink().tableRow_();
468 
469             for ( int s = model.getSpecifications().size() - 1; s >= 0; s-- )
470             {
471                 final Specification spec = model.getSpecifications().
472                     getSpecification( s );
473 
474                 this.getSink().tableRow();
475 
476                 this.getSink().tableCell();
477                 this.getSink().link( "#specification_" + spec.getIdentifier() );
478                 this.getSink().monospaced();
479                 this.getSink().text( spec.getIdentifier() );
480                 this.getSink().monospaced_();
481                 this.getSink().link_();
482                 this.getSink().tableCell_();
483 
484                 this.getSink().tableCell();
485                 this.getSink().monospaced();
486                 this.getSink().text( spec.getVendor() );
487                 this.getSink().monospaced_();
488                 this.getSink().tableCell_();
489 
490                 this.getSink().tableCell();
491                 this.getSink().monospaced();
492                 this.getSink().text( spec.getVersion() );
493                 this.getSink().monospaced_();
494                 this.getSink().tableCell_();
495 
496                 this.getSink().tableCell();
497                 this.getSink().monospaced();
498                 this.getSink().text(
499                     ContainerReportBundle.getInstance().getMultiplicityMessage(
500                     locale, new Integer( spec.getMultiplicity() ) ) );
501 
502                 this.getSink().monospaced_();
503                 this.getSink().tableCell_();
504 
505                 this.getSink().tableCell();
506                 this.getSink().monospaced();
507                 this.getSink().text(
508                     ContainerReportBundle.getInstance().
509                     getScopeMessage( locale, new Integer( spec.getScope() ) ) );
510 
511                 this.getSink().monospaced_();
512                 this.getSink().tableCell_();
513 
514                 this.getSink().tableRow_();
515             }
516 
517             this.getSink().table_();
518         }
519         else
520         {
521             this.getSink().text(
522                 ContainerReportBundle.getInstance().
523                 getNoSpecificationsMessage( Locale.getDefault() ) );
524 
525         }
526 
527         this.getSink().paragraph_();
528         this.getSink().section2_();
529 
530         this.getSink().section2();
531         this.getSink().sectionTitle2();
532         this.getSink().text(
533             ContainerReportBundle.getInstance().
534             getModuleImplTitleMessage( locale ) );
535 
536         this.getSink().sectionTitle2_();
537 
538         this.getSink().paragraph();
539 
540         if ( model.getImplementations().size() > 0 )
541         {
542             this.getSink().text(
543                 ContainerReportBundle.getInstance().
544                 getModuleImplementationDescriptionMessage( locale ) );
545 
546             this.getSink().table();
547             this.getSink().tableRow();
548 
549             this.getSink().tableHeaderCell();
550             this.getSink().text(
551                 ContainerReportBundle.getInstance().
552                 getModuleImplIdentifierHeaderMessage( locale ) );
553 
554             this.getSink().tableHeaderCell_();
555 
556             this.getSink().tableHeaderCell();
557             this.getSink().text(
558                 ContainerReportBundle.getInstance().
559                 getModuleImplNameHeaderMessage( locale ) );
560 
561             this.getSink().tableHeaderCell_();
562 
563             this.getSink().tableHeaderCell();
564             this.getSink().text(
565                 ContainerReportBundle.getInstance().
566                 getModuleImplVersionHeaderMessage( locale ) );
567 
568             this.getSink().tableHeaderCell_();
569 
570             this.getSink().tableHeaderCell();
571             this.getSink().text(
572                 ContainerReportBundle.getInstance().
573                 getModuleImplFinalHeaderMessage( locale ) );
574 
575             this.getSink().tableHeaderCell_();
576 
577             this.getSink().tableHeaderCell();
578             this.getSink().text(
579                 ContainerReportBundle.getInstance().
580                 getModuleImplSpecificationsHeaderMessage( locale ) );
581 
582             this.getSink().tableHeaderCell_();
583 
584             this.getSink().tableRow_();
585 
586             for ( int i = model.getImplementations().size() - 1; i >= 0; i-- )
587             {
588                 final Implementation impl = model.getImplementations().
589                     getImplementation( i );
590 
591                 this.getSink().tableRow();
592 
593                 this.getSink().tableCell();
594                 this.getSink().link( "#implementation_" + impl.getIdentifier() );
595                 this.getSink().monospaced();
596                 this.getSink().text( impl.getIdentifier() );
597                 this.getSink().monospaced_();
598                 this.getSink().link_();
599                 this.getSink().tableCell_();
600 
601                 this.getSink().tableCell();
602                 this.getSink().monospaced();
603                 this.getSink().text( impl.getName() );
604                 this.getSink().monospaced_();
605                 this.getSink().tableCell_();
606 
607                 this.getSink().tableCell();
608                 this.getSink().monospaced();
609                 this.getSink().text( impl.getVersion() );
610                 this.getSink().monospaced_();
611                 this.getSink().tableCell_();
612 
613                 this.getSink().tableCell();
614                 this.getSink().monospaced();
615                 this.getSink().text( Boolean.toString( impl.isFinal() ) );
616                 this.getSink().monospaced_();
617                 this.getSink().tableCell_();
618 
619                 this.getSink().tableCell();
620 
621                 for ( int is = impl.getImplementedSpecifications().size() - 1;
622                       is >= 0; is-- )
623                 {
624                     final Specification spec =
625                                         impl.getImplementedSpecifications().
626                         getSpecification( is );
627 
628                     this.getSink().monospaced();
629                     this.getSink().text( spec.getIdentifier() );
630                     this.getSink().nonBreakingSpace();
631                     this.getSink().text( spec.getVersion() );
632                     this.getSink().lineBreak();
633                     this.getSink().monospaced_();
634                 }
635 
636                 this.getSink().tableCell_();
637 
638                 this.getSink().tableRow_();
639             }
640 
641             this.getSink().table_();
642         }
643         else
644         {
645             this.getSink().text(
646                 ContainerReportBundle.getInstance().
647                 getNoImplementationsMessage( locale ) );
648 
649         }
650 
651         this.getSink().paragraph_();
652         this.getSink().section2_();
653     }
654 
655     private void generateUnresolvedDependenciesReport(
656         final Dependencies unresolved, final Locale locale )
657     {
658         this.getSink().section2();
659         this.getSink().sectionTitle2();
660         this.getSink().text(
661             ContainerReportBundle.getInstance().
662             getUnresolvedDependenciesTitleMessage( locale ) );
663 
664         this.getSink().sectionTitle2_();
665 
666         this.getSink().paragraph();
667 
668         if ( unresolved.size() > 0 )
669         {
670             this.getSink().text(
671                 ContainerReportBundle.getInstance().
672                 getUnresolvedDependenciesDescriptionMessage( locale ) );
673 
674             this.getSink().table();
675             this.getSink().tableRow();
676             this.getSink().tableHeaderCell();
677             this.getSink().text(
678                 ContainerReportBundle.getInstance().
679                 getSpecificationIdentifierTitleMessage( locale ) );
680 
681             this.getSink().tableHeaderCell_();
682 
683             this.getSink().tableHeaderCell();
684             this.getSink().text(
685                 ContainerReportBundle.getInstance().
686                 getModuleNameTitleMessage( locale ) );
687 
688             this.getSink().tableHeaderCell_();
689             this.getSink().tableHeaderCell();
690             this.getSink().text(
691                 ContainerReportBundle.getInstance().
692                 getMinimumVersionTitleMessage( locale ) );
693 
694             this.getSink().tableHeaderCell_();
695             this.getSink().tableRow_();
696 
697             final Set rendered = new HashSet();
698 
699             for ( int i = unresolved.size() - 1; i >= 0; i-- )
700             {
701                 final Dependency dep = unresolved.getDependency( i );
702 
703                 if ( rendered.contains( dep.getSpecification().
704                     getIdentifier() ) )
705                 {
706                     continue;
707                 }
708 
709                 rendered.add( dep.getSpecification().getIdentifier() );
710 
711                 this.getSink().tableRow();
712 
713                 this.getSink().tableCell();
714                 this.getSink().monospaced();
715                 this.getSink().text( dep.getSpecification().getIdentifier() );
716                 if ( dep.getSpecification().getDocumentation().
717                     getLocales().length > 0 )
718                 {
719                     this.getSink().paragraph();
720                     this.getSink().text( dep.getSpecification().
721                         getDocumentation().
722                         getValue( locale ) );
723 
724                     this.getSink().paragraph_();
725                 }
726 
727                 this.getSink().monospaced_();
728                 this.getSink().tableCell_();
729 
730                 this.getSink().tableCell();
731                 this.getSink().monospaced();
732                 this.getSink().text( dep.getSpecification().getModuleName() );
733                 this.getSink().monospaced_();
734                 this.getSink().tableCell_();
735 
736                 this.getSink().tableCell();
737                 this.getSink().monospaced();
738                 this.getSink().text( dep.getSpecification().getVersion() );
739                 this.getSink().monospaced_();
740                 this.getSink().tableCell_();
741 
742                 this.getSink().tableRow_();
743             }
744 
745             this.getSink().table_();
746         }
747         else
748         {
749             this.getSink().text(
750                 ContainerReportBundle.getInstance().
751                 getNoUnresolvedDependenciesMessage( locale ) );
752 
753         }
754 
755         this.getSink().paragraph_();
756         this.getSink().section2_();
757     }
758 
759     private void generateDetails( final Module model,
760                                   final Locale locale )
761     {
762         for ( int i = model.getSpecifications().size() - 1;
763               i >= 0; i-- )
764         {
765             final Specification spec = model.getSpecifications().
766                 getSpecification( i );
767 
768             this.getSink().section2();
769             this.getSink().anchor( "specification_" + spec.getIdentifier() );
770             this.getSink().sectionTitle2();
771             this.getSink().text(
772                 ContainerReportBundle.getInstance().
773                 getSpecificationDetailTitleMessage(
774                 locale, spec.getIdentifier() ) );
775 
776             this.getSink().sectionTitle2_();
777             this.getSink().anchor_();
778 
779             if ( spec.getDocumentation().getLocales().length > 0 )
780             {
781                 this.getSink().paragraph();
782                 this.getSink().text( spec.getDocumentation().
783                     getValue( locale ) );
784 
785                 this.getSink().paragraph_();
786             }
787 
788             this.getSink().paragraph();
789             this.generatePropertiesTable(
790                 spec.getProperties(), false, false,
791                 ContainerReportBundle.getInstance().
792                 getSpecificationPropertiesTableDescriptionMessage( locale ),
793                 ContainerReportBundle.getInstance().
794                 getNoSpecificationPropertiesMessage( locale ),
795                 locale );
796 
797             this.getSink().paragraph_();
798 
799             if ( this.linkJavadoc )
800             {
801                 this.getSink().paragraph();
802                 this.getSink().link(
803                     this.javadocDirectory + '/' +
804                     spec.getIdentifier().replaceAll( "\\.", "/" ) + ".html" );
805 
806                 this.getSink().text( ContainerReportBundle.getInstance().
807                     getSeeJavadocLinkMessage( locale ) );
808 
809                 this.getSink().link_();
810                 this.getSink().paragraph_();
811             }
812 
813             this.getSink().section2_();
814         }
815 
816         for ( int i = model.getImplementations().size() - 1;
817               i >= 0; i-- )
818         {
819             final Implementation impl = model.getImplementations().
820                 getImplementation( i );
821 
822             this.getSink().section2();
823             this.getSink().anchor( "implementation_" + impl.getIdentifier() );
824             this.getSink().sectionTitle2();
825             this.getSink().text(
826                 ContainerReportBundle.getInstance().
827                 getImplementationDetailTitleMessage(
828                 locale, impl.getIdentifier() ) );
829 
830             this.getSink().sectionTitle2_();
831             this.getSink().anchor_();
832 
833             if ( impl.getDocumentation().getLocales().length > 0 )
834             {
835                 this.getSink().paragraph();
836                 this.getSink().text( impl.getDocumentation().
837                     getValue( locale ) );
838 
839                 this.getSink().paragraph_();
840             }
841 
842             this.getSink().paragraph();
843             this.generatePropertiesTable(
844                 impl.getProperties(), true, true,
845                 ContainerReportBundle.getInstance().
846                 getImplementationPropertiesTableDescriptionMessage( locale ),
847                 ContainerReportBundle.getInstance().
848                 getNoImplementationPropertiesMessage( locale ),
849                 locale );
850 
851             this.getSink().paragraph_();
852 
853             this.getSink().paragraph();
854 
855             if ( impl.getDependencies().size() > 0 )
856             {
857                 this.getSink().text(
858                     ContainerReportBundle.getInstance().
859                     getDependenciesTableDescriptionMessage( locale ) );
860 
861                 this.getSink().table();
862                 this.getSink().tableRow();
863 
864                 this.getSink().tableHeaderCell();
865                 this.getSink().text(
866                     ContainerReportBundle.getInstance().
867                     getDependencyNameHeaderMessage( locale ) );
868 
869                 this.getSink().tableHeaderCell_();
870 
871                 this.getSink().tableHeaderCell();
872                 this.getSink().text(
873                     ContainerReportBundle.getInstance().
874                     getDependencySpecificationHeaderMessage( locale ) );
875 
876                 this.getSink().tableHeaderCell_();
877 
878                 this.getSink().tableHeaderCell();
879                 this.getSink().text(
880                     ContainerReportBundle.getInstance().
881                     getDependencyImplementationHeaderMessage( locale ) );
882 
883                 this.getSink().tableHeaderCell_();
884 
885                 this.getSink().tableHeaderCell();
886                 this.getSink().text(
887                     ContainerReportBundle.getInstance().
888                     getDependencyRequiredVersionHeaderMessage( locale ) );
889 
890                 this.getSink().tableHeaderCell_();
891 
892                 this.getSink().tableRow_();
893 
894                 for ( int d = impl.getDependencies().size() - 1; d >= 0; d-- )
895                 {
896                     final Dependency dr = impl.getDependencies().
897                         getDependency( d );
898 
899                     this.getSink().tableRow();
900 
901                     this.getSink().tableCell();
902                     this.getSink().monospaced();
903                     this.getSink().text( dr.getName() );
904                     this.getSink().monospaced_();
905                     this.getSink().tableCell_();
906 
907                     this.getSink().tableCell();
908                     this.getSink().monospaced();
909                     this.getSink().text( dr.getSpecification().getIdentifier() );
910                     this.getSink().monospaced_();
911                     this.getSink().tableCell_();
912 
913                     this.getSink().tableCell();
914                     this.getSink().monospaced();
915 
916                     if ( dr.getImplementation() != null )
917                     {
918                         this.getSink().text( dr.getImplementation().getName() );
919                     }
920                     else
921                     {
922                         this.getSink().text(
923                             ContainerReportBundle.getInstance().
924                             getAnyAvailableMessage( locale ) );
925 
926                     }
927 
928                     this.getSink().monospaced_();
929                     this.getSink().tableCell_();
930 
931                     this.getSink().tableCell();
932                     this.getSink().monospaced();
933                     this.getSink().text( dr.getSpecification().getVersion() );
934                     this.getSink().monospaced_();
935                     this.getSink().tableCell_();
936 
937                     this.getSink().tableRow_();
938                 }
939 
940                 this.getSink().table_();
941             }
942             else
943             {
944                 this.getSink().text(
945                     ContainerReportBundle.getInstance().
946                     getNoDependenciesMessage( locale ) );
947 
948             }
949 
950             this.getSink().paragraph_();
951 
952             this.getSink().paragraph();
953             this.generateMessagesTable( impl.getMessages(),
954                                         ContainerReportBundle.getInstance().
955                 getMessagesTableDescriptionMessage(
956                 locale ),
957                                         ContainerReportBundle.getInstance().
958                 getNoMessagesMessage( locale ),
959                                         locale );
960 
961             this.getSink().paragraph_();
962             if ( this.linkJavadoc )
963             {
964                 this.getSink().paragraph();
965                 this.getSink().link(
966                     this.javadocDirectory + '/' +
967                     impl.getIdentifier().replaceAll( "\\.", "/" ) + ".html" );
968 
969                 this.getSink().text( ContainerReportBundle.getInstance().
970                     getSeeJavadocLinkMessage( locale ) );
971 
972                 this.getSink().link_();
973                 this.getSink().paragraph_();
974             }
975 
976             this.getSink().section2_();
977         }
978 
979     }
980 
981     /**
982      * Generates a table showing property details.
983      *
984      * @param properties the properties to generate the table for.
985      * @param valueColumn {@code true} to include a column showing a property's
986      * value; {@code false} to not include a column for the property's value.
987      * @param apiColumn {@code true} to include a column showing a property's
988      * api flag; {@code false} to not include a column for the property's api
989      * flag.
990      * @param tableDescription presentation description of the table.
991      * @param emptyDescription presentation description of the table when empty.
992      * @param locale the locale of the table.
993      */
994     private void generatePropertiesTable( final Properties properties,
995                                           final boolean valueColumn,
996                                           final boolean apiColumn,
997                                           final String tableDescription,
998                                           final String emptyDescription,
999                                           final Locale locale )
1000     {
1001         if ( properties.size() > 0 )
1002         {
1003             this.getSink().text( tableDescription );
1004 
1005             this.getSink().table();
1006             this.getSink().tableRow();
1007 
1008             this.getSink().tableHeaderCell();
1009             this.getSink().text(
1010                 ContainerReportBundle.getInstance().
1011                 getPropertyNameHeaderMessage( locale ) );
1012 
1013             this.getSink().tableHeaderCell_();
1014 
1015             this.getSink().tableHeaderCell();
1016             this.getSink().text(
1017                 ContainerReportBundle.getInstance().
1018                 getPropertyTypeHeaderMessage( locale ) );
1019 
1020             this.getSink().tableHeaderCell_();
1021 
1022             if ( apiColumn )
1023             {
1024                 this.getSink().tableHeaderCell();
1025                 this.getSink().text(
1026                     ContainerReportBundle.getInstance().
1027                     getPropertyApiFlagHeaderMessage( locale ) );
1028 
1029                 this.getSink().tableHeaderCell_();
1030             }
1031 
1032             if ( valueColumn )
1033             {
1034                 this.getSink().tableHeaderCell();
1035                 this.getSink().text(
1036                     ContainerReportBundle.getInstance().
1037                     getPropertyValueHeaderMessage( locale ) );
1038 
1039                 this.getSink().tableHeaderCell_();
1040             }
1041 
1042             this.getSink().tableRow_();
1043 
1044             for ( int p = properties.size() - 1; p >= 0; p-- )
1045             {
1046                 final Property pr = properties.getProperty( p );
1047 
1048                 this.getSink().tableRow();
1049 
1050                 this.getSink().tableCell();
1051                 this.getSink().monospaced();
1052                 this.getSink().text( pr.getName() );
1053                 if ( pr.getDocumentation().getLocales().length > 0 )
1054                 {
1055                     this.getSink().paragraph();
1056                     this.getSink().text( pr.getDocumentation().
1057                         getValue( locale ) );
1058 
1059                     this.getSink().paragraph_();
1060                 }
1061                 this.getSink().monospaced_();
1062                 this.getSink().tableCell_();
1063 
1064                 this.getSink().tableCell();
1065                 this.getSink().monospaced();
1066                 this.getSink().text( pr.getType().toString() );
1067                 this.getSink().monospaced_();
1068                 this.getSink().tableCell_();
1069 
1070                 if ( apiColumn )
1071                 {
1072                     this.getSink().tableCell();
1073                     this.getSink().monospaced();
1074                     this.getSink().text(
1075                         ContainerReportBundle.getInstance().
1076                         getYesNoMessage( locale,
1077                                          new Integer( pr.isApi() ? 1 : 0 ) ) );
1078 
1079                     this.getSink().monospaced_();
1080                     this.getSink().tableCell_();
1081                 }
1082 
1083                 if ( valueColumn )
1084                 {
1085                     this.getSink().tableCell();
1086                     this.getSink().monospaced();
1087                     this.getSink().text( pr.getValue() != null
1088                                          ? pr.getValue().toString()
1089                                          : "" );
1090                     this.getSink().monospaced_();
1091                     this.getSink().tableCell_();
1092                 }
1093 
1094                 this.getSink().tableRow_();
1095             }
1096 
1097             this.getSink().table_();
1098         }
1099         else
1100         {
1101             this.getSink().text( emptyDescription );
1102         }
1103     }
1104 
1105     /**
1106      * Generates a table showing implementation messages.
1107      *
1108      * @param messages the messages to generate the table for.
1109      * @param tableDescription presentation description of the table.
1110      * @param emptyDescription presentation description of the table when empty.
1111      * @param locale the locale of the table.
1112      */
1113     private void generateMessagesTable( final Messages messages,
1114                                         final String tableDescription,
1115                                         final String emptyDescription,
1116                                         final Locale locale )
1117     {
1118         if ( messages.size() > 0 )
1119         {
1120             this.getSink().text( tableDescription );
1121 
1122             this.getSink().table();
1123             this.getSink().tableRow();
1124 
1125             this.getSink().tableHeaderCell();
1126             this.getSink().text(
1127                 ContainerReportBundle.getInstance().
1128                 getMessageNameHeaderMessage( locale ) );
1129 
1130             this.getSink().tableHeaderCell_();
1131 
1132             this.getSink().tableHeaderCell();
1133             this.getSink().text(
1134                 ContainerReportBundle.getInstance().
1135                 getMessageTemplatesHeaderMessage( locale ) );
1136 
1137             this.getSink().tableHeaderCell_();
1138             this.getSink().tableRow_();
1139 
1140             for ( int m = messages.size() - 1; m >= 0; m-- )
1141             {
1142                 final Message msg = messages.getMessage( m );
1143 
1144                 this.getSink().tableRow();
1145 
1146                 this.getSink().tableCell();
1147                 this.getSink().text( msg.getName() );
1148                 if ( msg.getDocumentation().getLocales().length > 0 )
1149                 {
1150                     this.getSink().paragraph();
1151                     this.getSink().monospaced();
1152                     this.getSink().text( msg.getDocumentation().
1153                         getValue( locale ) );
1154 
1155                     this.getSink().monospaced_();
1156                     this.getSink().paragraph_();
1157                 }
1158                 this.getSink().tableCell_();
1159 
1160                 this.getSink().tableCell();
1161                 final Locale[] msgLocales = msg.getTemplate().getLocales();
1162                 for ( int l = msgLocales.length - 1; l >= 0; l-- )
1163                 {
1164                     final String template = msg.getTemplate().
1165                         getValue( msgLocales[l] );
1166 
1167                     this.getSink().paragraph();
1168                     this.getSink().text(
1169                         msgLocales[l].getDisplayLanguage( msgLocales[l] ) +
1170                         ':' );
1171 
1172                     this.getSink().lineBreak();
1173                     this.getSink().verbatim( false );
1174                     this.getSink().text( template );
1175                     this.getSink().verbatim_();
1176                     this.getSink().paragraph_();
1177                 }
1178 
1179                 this.getSink().tableCell_();
1180                 this.getSink().tableRow_();
1181             }
1182 
1183             this.getSink().table_();
1184         }
1185         else
1186         {
1187             this.getSink().text( emptyDescription );
1188         }
1189     }
1190 
1191     /**
1192      * Gets all unresolved dependencies from a given model.
1193      *
1194      * @param model the model to get unresolved dependencies from.
1195      *
1196      * @throws NullPointerException if {@code model} is {@code null}.
1197      */
1198     private Dependencies getUnresolvedDependencies( final Model model )
1199     {
1200         final String unresolvedName = "Unresolved";
1201         final Collection deps = new LinkedList();
1202 
1203         for ( int m = model.getModules().size() - 1; m >= 0; m-- )
1204         {
1205             final Module mod = model.getModules().getModule( m );
1206 
1207             for ( int i = mod.getImplementations().size() - 1; i >= 0; i-- )
1208             {
1209                 final Implementation impl = mod.getImplementations().
1210                     getImplementation( i );
1211 
1212                 for ( int d = impl.getDependencies().size() - 1; d >= 0; d-- )
1213                 {
1214                     Dependency dep = (Dependency) impl.getDependencies().
1215                         getDependency( d ).clone();
1216 
1217                     if ( dep.getImplementation() == null &&
1218                          dep.getSpecification().getMultiplicity() ==
1219                          Specification.MULTIPLICITY_ONE &&
1220                          dep.getSpecification().getImplementations().
1221                         size() != 1 )
1222                     {
1223                         dep = (Dependency) dep.clone();
1224                         dep.setName(
1225                             unresolvedName + "-" + m + "-" + i + "-" + d );
1226 
1227                         deps.add( dep );
1228                     }
1229                 }
1230             }
1231         }
1232 
1233         final Dependencies dependencies = new Dependencies();
1234         dependencies.setDependencies(
1235             (Dependency[]) deps.toArray( new Dependency[ deps.size() ] ) );
1236 
1237         return dependencies;
1238     }
1239 
1240     //---------------------------------------------------------ContainerReport--
1241 }