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