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