View Javadoc

1   package org.apache.maven.plugin.jxr;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Calendar;
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.ResourceBundle;
32  
33  import org.apache.maven.doxia.siterenderer.Renderer;
34  import org.apache.maven.jxr.JXR;
35  import org.apache.maven.jxr.JxrException;
36  import org.apache.maven.model.Organization;
37  import org.apache.maven.model.ReportPlugin;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.reporting.AbstractMavenReport;
40  import org.apache.maven.reporting.MavenReportException;
41  import org.codehaus.plexus.util.FileUtils;
42  import org.codehaus.plexus.util.ReaderFactory;
43  import org.codehaus.plexus.util.StringUtils;
44  
45  /**
46   * Base class for the JXR reports.
47   *
48   * @author <a href="mailto:bellingard.NO-SPAM@gmail.com">Fabrice Bellingard</a>
49   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
50   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
51   * @version $Id: AbstractJxrReport.java 710182 2008-11-03 21:53:34Z hboutemy $
52   */
53  public abstract class AbstractJxrReport
54      extends AbstractMavenReport
55  {
56      /**
57       * @parameter expression="${project}"
58       * @required
59       * @readonly
60       */
61      private MavenProject project;
62  
63      /**
64       * @component
65       */
66      private Renderer siteRenderer;
67  
68      /**
69       * Output folder where the main page of the report will be generated. Note that this parameter is only relevant if
70       * the goal is run directly from the command line or from the default lifecycle. If the goal is run indirectly as
71       * part of a site generation, the output directory configured in the Maven Site Plugin will be used instead.
72       *
73       * @parameter expression="${project.reporting.outputDirectory}"
74       * @required
75       */
76      private File outputDirectory;
77  
78      /**
79       * File input encoding.
80       *
81       * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
82       */
83      private String inputEncoding;
84  
85      /**
86       * File output encoding.
87       *
88       * @parameter expression="${outputEncoding}" default-value="${project.reporting.outputEncoding}"
89       */
90      private String outputEncoding;
91  
92      /**
93       * Title of window of the Xref HTML files.
94       *
95       * @parameter expression="${project.name} ${project.version} Reference"
96       */
97      private String windowTitle;
98  
99      /**
100      * Title of main page of the Xref HTML files.
101      *
102      * @parameter expression="${project.name} ${project.version} Reference"
103      */
104     private String docTitle;
105 
106     /**
107      * String uses at the bottom of the Xref HTML files.
108      *
109      * @parameter expression="${bottom}" default-value="Copyright &#169; {inceptionYear}-{currentYear} {projectOrganizationName}. All Rights Reserved."
110      */
111     private String bottom;
112 
113     /**
114      * Directory where Velocity templates can be found to generate overviews,
115      * frames and summaries.
116      * Should not be used. If used, should be an absolute path, like <code>"${basedir}/myTemplates"</code>.
117      *
118      * @parameter default-value="templates"
119      */
120     private String templateDir;
121 
122     /**
123      * Style sheet used for the Xref HTML files.
124      * Should not be used. If used, should be an absolute path, like <code>"${basedir}/myStyles.css"</code>.
125      *
126      * @parameter default-value="stylesheet.css"
127      */
128     private String stylesheet;
129 
130     /**
131      * A list of exclude patterns to use. By default no files are excluded.
132      *
133      * @parameter expression="${excludes}"
134      * @since 2.1
135      */
136     private ArrayList excludes;
137 
138     /**
139      * A list of include patterns to use. By default all .java files are included.
140      *
141      * @parameter expression="${includes}"
142      * @since 2.1
143      */
144     private ArrayList includes;
145 
146     /**
147      * The projects in the reactor for aggregation report.
148      *
149      * @parameter expression="${reactorProjects}"
150      * @readonly
151      */
152     protected List reactorProjects;
153 
154     /**
155      * Whether to build an aggregated report at the root, or build individual reports.
156      *
157      * @parameter expression="${aggregate}" default-value="false"
158      */
159     protected boolean aggregate;
160 
161     /**
162      * Link the Javadoc from the Source XRef. Defaults to true and will link
163      * automatically if javadoc plugin is being used.
164      *
165      * @parameter expression="${linkJavadoc}" default-value="true"
166      */
167     private boolean linkJavadoc;
168 
169     /**
170      * Gets the effective reporting output files encoding.
171      *
172      * @return The effective reporting output file encoding, never <code>null</code>: defaults to
173      * <code>UTF-8</code> instead.
174      */
175     protected String getOutputEncoding()
176     {
177         return ( outputEncoding == null ) ? ReaderFactory.UTF_8 : outputEncoding;
178     }
179 
180     /**
181      * Compiles the list of directories which contain source files that will be included in the JXR report generation.
182      *
183      * @param sourceDirs the List of the source directories
184      * @return a List of the directories that will be included in the JXR report generation
185      */
186     protected List pruneSourceDirs( List sourceDirs )
187     {
188         List pruned = new ArrayList( sourceDirs.size() );
189         for ( Iterator i = sourceDirs.iterator(); i.hasNext(); )
190         {
191             String dir = (String) i.next();
192             if ( !pruned.contains( dir ) && hasSources( new File( dir ) ) )
193             {
194                 pruned.add( dir );
195             }
196         }
197         return pruned;
198     }
199 
200     /**
201      * Initialize some attributes required during the report generation
202      */
203     protected void init()
204     {
205         // wanna know if Javadoc is being generated
206         // TODO: what if it is not part of the site though, and just on the command line?
207         Collection plugin = project.getReportPlugins();
208         if ( plugin != null )
209         {
210             for ( Iterator iter = plugin.iterator(); iter.hasNext(); )
211             {
212                 ReportPlugin reportPlugin = (ReportPlugin) iter.next();
213                 if ( "maven-javadoc-plugin".equals( reportPlugin.getArtifactId() ) )
214                 {
215                     break;
216                 }
217             }
218         }
219     }
220 
221     /**
222      * Checks whether the given directory contains Java files.
223      *
224      * @param dir the source directory
225      * @return true if the folder or one of its subfolders contains at least 1 Java file
226      */
227     private boolean hasSources( File dir )
228     {
229         boolean found = false;
230         if ( dir.exists() && dir.isDirectory() )
231         {
232             File[] files = dir.listFiles();
233             for ( int i = 0; i < files.length && !found; i++ )
234             {
235                 File currentFile = files[i];
236                 if ( currentFile.isFile() && currentFile.getName().endsWith( ".java" ) )
237                 {
238                     found = true;
239                 }
240                 else if ( currentFile.isDirectory() )
241                 {
242                     boolean hasSources = hasSources( currentFile );
243                     if ( hasSources )
244                     {
245                         found = true;
246                     }
247                 }
248             }
249         }
250         return found;
251     }
252 
253     /**
254      * Creates the Xref for the Java files found in the given source directory and puts
255      * them in the given destination directory.
256      *
257      * @param locale               The user locale to use for the Xref generation
258      * @param destinationDirectory The output folder
259      * @param sourceDirs           The source directories
260      * @throws java.io.IOException
261      * @throws org.apache.maven.jxr.JxrException
262      *
263      */
264     private void createXref( Locale locale, String destinationDirectory, List sourceDirs )
265         throws IOException, JxrException
266     {
267         JXR jxr = new JXR();
268         jxr.setDest( destinationDirectory );
269         if ( StringUtils.isEmpty( inputEncoding ) )
270         {
271             String platformEncoding = System.getProperty( "file.encoding" );
272             getLog().warn( "File encoding has not been set, using platform encoding " + platformEncoding
273                            + ", i.e. build is platform dependent!" );
274         }
275         jxr.setInputEncoding( inputEncoding );
276         jxr.setLocale( locale );
277         jxr.setLog( new PluginLogAdapter( getLog() ) );
278         jxr.setOutputEncoding( getOutputEncoding() );
279         jxr.setRevision( "HEAD" );
280         jxr.setJavadocLinkDir( getJavadocLocation() );
281         // Set include/exclude patterns on the jxr instance
282         if ( excludes != null && !excludes.isEmpty() )
283         {
284             jxr.setExcludes( (String[]) excludes.toArray( new String[0] ) );
285         }
286         if ( includes != null && !includes.isEmpty() )
287         {
288             jxr.setIncludes( (String[]) includes.toArray( new String[0] ) );
289         }
290 
291         jxr.xref( sourceDirs, templateDir, windowTitle, docTitle, getBottomText( project.getInceptionYear(), project
292             .getOrganization() ) );
293 
294         // and finally copy the stylesheet
295         copyRequiredResources( destinationDirectory );
296     }
297 
298     /**
299      * Get the bottom text to be displayed at the lower part of the generated JXR reports.
300      *
301      * @param inceptionYear the year when the project was started
302      * @param organization the organization for the project
303      * @return  a String that contains the bottom text to be displayed in the lower part of the generated JXR reports
304      */
305     private String getBottomText( String inceptionYear, Organization organization )
306     {
307         int actualYear = Calendar.getInstance().get( Calendar.YEAR );
308         String year = String.valueOf( actualYear );
309 
310         String bottom = StringUtils.replace( this.bottom, "{currentYear}", year );
311 
312         if ( inceptionYear == null )
313         {
314             bottom = StringUtils.replace( bottom, "{inceptionYear}-", "" );
315         }
316         else
317         {
318             if ( inceptionYear.equals( year ) )
319             {
320                 bottom = StringUtils.replace( bottom, "{inceptionYear}-", "" );
321             }
322             else
323             {
324                 bottom = StringUtils.replace( bottom, "{inceptionYear}", inceptionYear );
325             }
326         }
327 
328         if ( organization != null && StringUtils.isNotEmpty( organization.getName() ) )
329         {
330             bottom = StringUtils.replace( bottom, "{projectOrganizationName}", organization.getName() );
331         }
332         else
333         {
334             bottom = StringUtils.replace( bottom, " {projectOrganizationName}", "" );
335         }
336 
337         return bottom;
338     }
339 
340     /**
341      * Copy some required resources (like the stylesheet) to the
342      * given directory
343      *
344      * @param dir the directory to copy the resources to
345      */
346     private void copyRequiredResources( String dir )
347     {
348         File stylesheetFile = new File( stylesheet );
349         File destStylesheetFile = new File( dir, "stylesheet.css" );
350 
351         try
352         {
353             if ( stylesheetFile.isAbsolute() )
354             {
355                 FileUtils.copyFile( stylesheetFile, destStylesheetFile );
356             }
357             else
358             {
359                 URL stylesheetUrl = this.getClass().getClassLoader().getResource( stylesheet );
360                 FileUtils.copyURLToFile( stylesheetUrl, destStylesheetFile );
361             }
362         }
363         catch ( IOException e )
364         {
365             getLog().warn( "An error occured while copying the stylesheet to the target directory", e );
366         }
367 
368     }
369 
370     /**
371      * @see org.apache.maven.reporting.AbstractMavenReport#getSiteRenderer()
372      */
373     protected Renderer getSiteRenderer()
374     {
375         return siteRenderer;
376     }
377 
378     /**
379      * @see org.apache.maven.reporting.AbstractMavenReport#getOutputDirectory()
380      */
381     protected String getOutputDirectory()
382     {
383         return outputDirectory.getAbsolutePath();
384     }
385 
386     /**
387      * @see org.apache.maven.reporting.AbstractMavenReport#getProject()
388      */
389     public MavenProject getProject()
390     {
391         return project;
392     }
393 
394     /**
395      * Returns the correct resource bundle according to the locale
396      *
397      * @param locale the locale of the user
398      * @return the bundle corresponding to the locale
399      */
400     protected ResourceBundle getBundle( Locale locale )
401     {
402         return ResourceBundle.getBundle( "jxr-report", locale, this.getClass().getClassLoader() );
403     }
404 
405     /**
406      * @param sourceDirs
407      * @return true if the report could be generated
408      */
409     protected boolean canGenerateReport( List sourceDirs )
410     {
411         boolean canGenerate = !sourceDirs.isEmpty();
412 
413         if ( aggregate && !project.isExecutionRoot() )
414         {
415             canGenerate = false;
416         }
417         return canGenerate;
418     }
419 
420     /**
421      * @see org.apache.maven.reporting.AbstractMavenReport#executeReport(java.util.Locale)
422      */
423     protected void executeReport( Locale locale )
424         throws MavenReportException
425     {
426         List sourceDirs = constructSourceDirs();
427         if ( canGenerateReport( sourceDirs ) )
428         {
429             // init some attributes -- TODO (javadoc)
430             init();
431 
432             try
433             {
434                 createXref( locale, getDestinationDirectory(), sourceDirs );
435             }
436             catch ( JxrException e )
437             {
438                 throw new MavenReportException( "Error while generating the HTML source code of the projet.", e );
439             }
440             catch ( IOException e )
441             {
442                 throw new MavenReportException( "Error while generating the HTML source code of the projet.", e );
443             }
444         }
445     }
446 
447     /**
448      * Gets the list of the source directories to be included in the JXR report generation
449      *
450      * @return a List of the source directories whose contents will be included in the JXR report generation
451      */
452     protected List constructSourceDirs()
453     {
454         List sourceDirs = new ArrayList( getSourceRoots() );
455         if ( aggregate )
456         {
457             for ( Iterator i = reactorProjects.iterator(); i.hasNext(); )
458             {
459                 MavenProject project = (MavenProject) i.next();
460 
461                 if ( "java".equals( project.getArtifact().getArtifactHandler().getLanguage() ) )
462                 {
463                     sourceDirs.addAll( getSourceRoots( project ) );
464                 }
465             }
466         }
467 
468         sourceDirs = pruneSourceDirs( sourceDirs );
469         return sourceDirs;
470     }
471 
472     /**
473      * @see org.apache.maven.reporting.AbstractMavenReport#canGenerateReport()
474      */
475     public boolean canGenerateReport()
476     {
477         return canGenerateReport( constructSourceDirs() );
478     }
479 
480     /**
481      * @see org.apache.maven.reporting.AbstractMavenReport#isExternalReport()
482      */
483     public boolean isExternalReport()
484     {
485         return true;
486     }
487 
488     /**
489      * @return a String that contains the location of the javadocs
490      */
491     private String getJavadocLocation()
492         throws IOException
493     {
494         String location = null;
495         if ( linkJavadoc )
496         {
497             // We don't need to do the whole translation thing like normal, because JXR does it internally.
498             // It probably shouldn't.
499             if ( getJavadocDir().exists() )
500             {
501                 // XRef was already generated by manual execution of a lifecycle binding
502                 location = getJavadocDir().getAbsolutePath();
503             }
504             else
505             {
506                 // Not yet generated - check if the report is on its way
507 
508                 // Special case: using the site:stage goal
509                 String stagingDirectory = System.getProperty( "stagingDirectory" );
510 
511                 if ( StringUtils.isNotEmpty( stagingDirectory ) )
512                 {
513                     String javadocDestDir = getJavadocDir().getName();
514                     boolean javadocAggregate = Boolean
515                         .valueOf( JxrReportUtil.getMavenJavadocPluginBasicOption( project, "aggregate", "false" ) )
516                         .booleanValue();
517 
518                     String structureProject = JxrReportUtil.getStructure( project, false );
519 
520                     if ( aggregate && javadocAggregate )
521                     {
522                         File outputDirectory = new File( stagingDirectory, structureProject );
523                         location = outputDirectory + "/" + javadocDestDir;
524                     }
525                     if ( !aggregate && javadocAggregate )
526                     {
527                         location = stagingDirectory + "/" + javadocDestDir;
528 
529                         String hierarchy = project.getName();
530 
531                         MavenProject parent = project.getParent();
532                         while ( parent != null )
533                         {
534                             hierarchy = parent.getName();
535                             parent = parent.getParent();
536                         }
537                         File outputDirectory = new File( stagingDirectory, hierarchy );
538                         location = outputDirectory + "/" + javadocDestDir;
539                     }
540                     if ( aggregate && !javadocAggregate )
541                     {
542                         getLog().warn(
543                                        "The JXR plugin is configured to build an aggregated report at the root, "
544                                            + "not the Javadoc plugin." );
545                     }
546                     if ( !aggregate && !javadocAggregate )
547                     {
548                         location = stagingDirectory + "/" + structureProject + "/" + javadocDestDir;
549                     }
550                 }
551                 else
552                 {
553                     location = getJavadocDir().getAbsolutePath();
554                 }
555             }
556 
557             if ( location == null )
558             {
559                 getLog().warn( "Unable to locate Javadoc to link to - DISABLED" );
560             }
561         }
562 
563         return location;
564     }
565 
566     /**
567      * Abstract method that returns the target directory where the generated JXR reports will be put.
568      *
569      * @return  a String that contains the target directory name
570      */
571     protected abstract String getDestinationDirectory();
572 
573     /**
574      * Abstract method that returns the specified source directories that will be included in the JXR report generation.
575      *
576      * @return a List of the source directories
577      */
578     protected abstract List getSourceRoots();
579 
580     /**
581      * Abstract method that returns the compile source directories of the specified project that will be included in the
582      * JXR report generation
583      *
584      * @param project the MavenProject where the JXR report plugin will be executed
585      * @return a List of the source directories
586      */
587     protected abstract List getSourceRoots( MavenProject project );
588 
589     /**
590      * Abstract method that returns the directory of the javadoc files.
591      *
592      * @return a File for the directory of the javadocs
593      */
594     protected abstract File getJavadocDir();
595 }