View Javadoc
1   package org.apache.maven.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 org.apache.commons.io.IOUtils;
23  import org.apache.maven.jxr.pacman.ClassType;
24  import org.apache.maven.jxr.pacman.PackageManager;
25  import org.apache.maven.jxr.pacman.PackageType;
26  import org.apache.maven.jxr.log.VelocityLogger;
27  import org.apache.maven.jxr.log.Log;
28  import org.apache.oro.text.perl.Perl5Util;
29  import org.apache.velocity.Template;
30  import org.apache.velocity.VelocityContext;
31  import org.apache.velocity.app.VelocityEngine;
32  
33  import java.io.File;
34  import java.io.FileWriter;
35  import java.util.Enumeration;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.TreeMap;
40  
41  /**
42   * This class creates the navigational pages for jxr's cross-referenced source
43   * files. The navigation is inspired by javadoc, so it should have a familiar feel.
44   *
45   * Creates the following files:
46   * <ul>
47   * <li><code>index.html</code>            main index containing the frameset</li>
48   * <li><code>overview-frame.html</code>   list of the project's packages              (top left)</li>
49   * <li><code>allclasses-frame.html</code> list of all classes in the project          (bottom left)</li>
50   * <li><code>overview-summary.html</code> top-level listing of the project's packages (main frame)</li>
51   *
52   * <li>
53   * Package specific:
54   * <ul>
55   * <li><code>package-summary.html</code> listing of all classes in this package    (main frame)</li>
56   * <li><code>package-frame.html</code>   listing of all classes in this package    (bottom left)</li>
57   * </ul>
58   * </li>
59   * </ul>
60   *
61   * @author <a href="mailto:bellingard@gmail.com">Fabrice Bellingard </a>
62   * @author <a href="mailto:brian@brainslug.org">Brian Leonard</a>
63   * @version $Id: DirectoryIndexer.java 1587311 2014-04-14 20:39:58Z hboutemy $
64   */
65  public class DirectoryIndexer
66  {
67      /*
68       * JavaCodeTransform uses this to cross-reference package references
69       * with that package's main summary page.
70       */
71      static final String INDEX = "package-summary.html";
72  
73      /*
74       * Path to the root output directory.
75       */
76      private String root;
77  
78      /*
79       * Package Manager for this project.
80       */
81      private PackageManager packageManager;
82  
83      /*
84       * see the getter/setter docs for these properties
85       */
86      private String outputEncoding;
87  
88      private String templateDir;
89  
90      private String windowTitle;
91  
92      private String docTitle;
93  
94      private String bottom;
95  
96      /**
97       * Constructor for the DirectoryIndexer object
98       *
99       * @param packageManager PackageManager for this project
100      * @param root Path of the root output directory
101      */
102     public DirectoryIndexer( PackageManager packageManager, String root )
103     {
104         this.packageManager = packageManager;
105         this.root = root;
106     }
107 
108     /**
109      * OutputEncoding is the encoding of output files.
110      *
111      * @param outputEncoding output Encoding
112      */
113     public void setOutputEncoding( String outputEncoding )
114     {
115         this.outputEncoding = outputEncoding;
116     }
117 
118     /**
119      * see setOutputEncoding(String)
120      */
121     public String getOutputEncoding()
122     {
123         return outputEncoding;
124     }
125 
126     /**
127      * TemplateDir is the location of the jelly template files used
128      * to generate the navigation pages.
129      *
130      * @param templateDir location of the template directory
131      */
132     public void setTemplateDir( String templateDir )
133     {
134         this.templateDir = templateDir;
135     }
136 
137     /**
138      * see setTemplateDir(String)
139      */
140     public String getTemplateDir()
141     {
142         return templateDir;
143     }
144 
145     /**
146      * WindowTitle is used in the output's &lt;title&gt; tags
147      * see the javadoc documentation for the property of the same name
148      *
149      * @param windowTitle the &lt;title&gt; attribute
150      */
151     public void setWindowTitle( String windowTitle )
152     {
153         this.windowTitle = windowTitle;
154     }
155 
156     /**
157      * see setWindowTitle(String)
158      *
159      * @see #setWindowTitle(String) setWindowTitle
160      */
161     public String getWindowTitle()
162     {
163         return windowTitle;
164     }
165 
166     /**
167      * DocTitle is used as a page heading for the summary files
168      * see the javadoc documentation for the property of the same name
169      *
170      * @param docTitle major page heading
171      */
172     public void setDocTitle( String docTitle )
173     {
174         this.docTitle = docTitle;
175     }
176 
177     /**
178      * see setDocTitle(String)
179      *
180      * @see #setDocTitle(String) setDocTitle
181      */
182     public String getDocTitle()
183     {
184         return docTitle;
185     }
186 
187     /**
188      * Bottom is a footer for the navigation pages, usually a copyright
189      * see the javadoc documentation for the property of the same name
190      *
191      * @param bottom page footer
192      */
193     public void setBottom( String bottom )
194     {
195         this.bottom = bottom;
196     }
197 
198     /**
199      * see setBottom(String)
200      *
201      * @see #setBottom(String) setBottom
202      */
203     public String getBottom()
204     {
205         return bottom;
206     }
207 
208     /**
209      * Does the actual indexing.
210      *
211      * @throws JxrException If something went wrong
212      */
213     public void process( Log log )
214         throws JxrException
215     {
216         Map info = getPackageInfo();
217 
218         VelocityEngine engine = new VelocityEngine();
219         setProperties( engine, log );
220         try
221         {
222             engine.init();
223         }
224         catch ( Exception e )
225         {
226             throw new JxrException( "Error initializing Velocity", e );
227         }
228 
229         VelocityContext context = new VelocityContext();
230         context.put( "outputEncoding", getOutputEncoding() );
231         context.put( "windowTitle", getWindowTitle() );
232         context.put( "docTitle", getDocTitle() );
233         context.put( "bottom", getBottom() );
234         context.put( "info", info );
235 
236         doVelocity( "index", root, context, engine );
237         doVelocity( "overview-frame", root, context, engine );
238         doVelocity( "allclasses-frame", root, context, engine );
239         doVelocity( "overview-summary", root, context, engine );
240 
241         Iterator iter = ( (Map) info.get( "allPackages" ) ).values().iterator();
242         while ( iter.hasNext() )
243         {
244             Map pkgInfo = (Map) iter.next();
245 
246             VelocityContext subContext = new VelocityContext( context );
247             subContext.put( "pkgInfo", pkgInfo );
248 
249             String outDir = root + "/" + (String) pkgInfo.get( "dir" );
250             doVelocity( "package-summary", outDir, subContext, engine );
251             doVelocity( "package-frame", outDir, subContext, engine );
252         }
253     }
254 
255     /*
256      * Set Velocity properties to find templates
257      */
258     private void setProperties( VelocityEngine engine, Log log )
259     {
260         File templateDirFile = new File( getTemplateDir() );
261         if ( templateDirFile.isAbsolute() )
262         {
263             // the property has been overridden: need to use a FileResourceLoader
264             engine.setProperty( "resource.loader", "file" );
265             engine.setProperty( "file.resource.loader.class",
266                                 "org.apache.velocity.runtime.resource.loader.FileResourceLoader" );
267             engine.setProperty( "file.resource.loader.path", templateDirFile.toString() );
268         }
269         else
270         {
271             // use of the default templates
272             engine.setProperty( "resource.loader", "classpath" );
273             engine.setProperty( "classpath.resource.loader.class",
274                                 "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader" );
275         }
276         // avoid "unable to find resource 'VM_global_library.vm' in any resource loader."
277         engine.setProperty( "velocimacro.library", "" );
278         engine.setProperty( Log.class.getName(), log );
279         engine.setProperty( "runtime.log.logsystem.class", VelocityLogger.class.getName() );
280     }
281 
282     /*
283      * Generate the HTML file according to the Velocity template
284      */
285     private void doVelocity( String templateName, String outDir, VelocityContext context, VelocityEngine engine )
286         throws JxrException
287     {
288         // output file
289         File file = new File( outDir, templateName + ".html" );
290         file.getParentFile().mkdirs();
291         FileWriter writer = null;
292 
293         try
294         {
295             writer = new FileWriter( file );
296 
297             // template file
298             StringBuffer templateFile = new StringBuffer();
299             File templateDirFile = new File( getTemplateDir() );
300             if ( !templateDirFile.isAbsolute() )
301             {
302                 // default templates
303                 templateFile.append( getTemplateDir() );
304                 templateFile.append( "/" );
305             }
306             templateFile.append( templateName );
307             templateFile.append( ".vm" );
308             Template template = engine.getTemplate( templateFile.toString() );
309 
310             // do the merge
311             template.merge( context, writer );
312             writer.flush();
313         }
314         catch ( Exception e )
315         {
316             throw new JxrException( "Error merging velocity template", e );
317         }
318         finally
319         {
320             IOUtils.closeQuietly( writer );
321         }
322     }
323 
324     /*
325      * Creates a Map of other Maps containing information about
326      * this project's packages and classes, obtained from the PackageManager.
327      *
328      * allPackages collection of Maps with package info, with the following format
329      *   {name}    package name (e.g., "org.apache.maven.jxr")
330      *   {dir}     package dir relative to the root output dir (e.g., "org/apache/maven/jxr")
331      *   {rootRef} relative link to root output dir (e.g., "../../../../") note trailing slash
332      *   {classes} collection of Maps with class info
333      *      {name}  class name (e.g., "DirectoryIndexer")
334      *      {dir}   duplicate of package {dir}
335      *
336      * allClasses collection of Maps with class info, format as above
337      *
338      */
339     private Map<String, Object> getPackageInfo()
340     {
341         TreeMap<String, Map<String, Object>> allPackages = new TreeMap<String, Map<String, Object>>();
342         TreeMap<String, Map<String, String>> allClasses = new TreeMap<String, Map<String, String>>();
343         Perl5Util perl = new Perl5Util();
344 
345         Enumeration packages = packageManager.getPackageTypes();
346         while ( packages.hasMoreElements() )
347         {
348             PackageType pkg = (PackageType) packages.nextElement();
349             String pkgName = pkg.getName();
350             String pkgDir = perl.substitute( "s/\\./\\//g", pkgName );
351             String rootRef = perl.substitute( "s/[^\\.]*(\\.|$)/..\\//g", pkgName );
352 
353             // special case for the default package
354             // javadoc doesn't deal with it, but it's easy for us
355             if ( pkgName.length() == 0 )
356             {
357                 pkgName = "(default package)";
358                 pkgDir = ".";
359                 rootRef = "./";
360             }
361 
362             TreeMap<String, Map<String, String>> pkgClasses = new TreeMap<String, Map<String, String>>();
363             Enumeration classes = pkg.getClassTypes();
364             while ( classes.hasMoreElements() )
365             {
366                 ClassType clazz = (ClassType) classes.nextElement();
367 
368                 String className = clazz.getName();
369                 Map<String, String> classInfo = new HashMap<String, String>();
370                 if ( clazz.getFilename() != null )
371                 {
372                     classInfo.put( "filename", clazz.getFilename() );
373                 }
374                 else
375                 {
376                     classInfo.put( "filename", "" );
377                 }
378                 classInfo.put( "name", className );
379                 classInfo.put( "dir", pkgDir );
380 
381                 pkgClasses.put( className, classInfo );
382                 allClasses.put( className, classInfo );
383             }
384 
385             Map<String, Object> pkgInfo = new HashMap<String, Object>();
386             pkgInfo.put( "name", pkgName );
387             pkgInfo.put( "dir", pkgDir );
388             pkgInfo.put( "classes", pkgClasses );
389             pkgInfo.put( "rootRef", rootRef );
390             allPackages.put( pkgName, pkgInfo );
391         }
392 
393         Map<String, Object> info = new HashMap<String, Object>();
394         info.put( "allPackages", allPackages );
395         info.put( "allClasses", allClasses );
396 
397         return info;
398     }
399 }