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 <title> tags
144 * see the javadoc documentation for the property of the same name
145 *
146 * @param windowTitle the <title> 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 }