1 package org.apache.maven.doxia.docrenderer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.io.StringWriter;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.LinkedHashMap;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37
38 import org.apache.maven.doxia.Doxia;
39 import org.apache.maven.doxia.document.DocumentModel;
40 import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader;
41 import org.apache.maven.doxia.sink.Sink;
42 import org.apache.maven.doxia.parser.ParseException;
43 import org.apache.maven.doxia.parser.Parser;
44 import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
45 import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
46 import org.apache.maven.doxia.module.site.SiteModule;
47 import org.apache.maven.doxia.module.site.manager.SiteModuleManager;
48 import org.apache.maven.doxia.util.XmlValidator;
49
50 import org.apache.velocity.VelocityContext;
51 import org.apache.velocity.context.Context;
52
53 import org.codehaus.plexus.logging.AbstractLogEnabled;
54
55 import org.codehaus.plexus.util.DirectoryScanner;
56 import org.codehaus.plexus.util.FileUtils;
57 import org.codehaus.plexus.util.IOUtil;
58 import org.codehaus.plexus.util.ReaderFactory;
59 import org.codehaus.plexus.util.xml.XmlStreamReader;
60 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
61 import org.codehaus.plexus.velocity.SiteResourceLoader;
62 import org.codehaus.plexus.velocity.VelocityComponent;
63
64
65
66
67
68
69
70
71
72 public abstract class AbstractDocumentRenderer
73 extends AbstractLogEnabled
74 implements DocumentRenderer
75 {
76
77 protected SiteModuleManager siteModuleManager;
78
79
80 protected Doxia doxia;
81
82
83 private VelocityComponent velocity;
84
85
86
87
88 private String baseDir;
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 public abstract void render( Map<String, SiteModule> filesToProcess, File outputDirectory,
106 DocumentModel documentModel )
107 throws DocumentRendererException, IOException;
108
109
110
111
112
113
114 public void render( Collection<String> files, File outputDirectory, DocumentModel documentModel )
115 throws DocumentRendererException, IOException
116 {
117 render( getFilesToProcess( files ), outputDirectory, documentModel, null );
118 }
119
120
121 public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel )
122 throws DocumentRendererException, IOException
123 {
124 render( baseDirectory, outputDirectory, documentModel, null );
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138 public void render( Map<String, SiteModule> filesToProcess, File outputDirectory, DocumentModel documentModel,
139 DocumentRendererContext context )
140 throws DocumentRendererException, IOException
141 {
142
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel,
161 DocumentRendererContext context )
162 throws DocumentRendererException, IOException
163 {
164 render( getFilesToProcess( baseDirectory ), outputDirectory, documentModel, context );
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178 public void render( File baseDirectory, File outputDirectory )
179 throws DocumentRendererException, IOException
180 {
181 render( baseDirectory, outputDirectory, (DocumentModel) null );
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 public void render( File baseDirectory, File outputDirectory, File documentDescriptor )
198 throws DocumentRendererException, IOException
199 {
200 if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) )
201 {
202 getLogger().warn( "No documentDescriptor found: using default settings!" );
203
204 render( baseDirectory, outputDirectory );
205 }
206 else
207 {
208 render( getFilesToProcess( baseDirectory ), outputDirectory, readDocumentModel( documentDescriptor ), null );
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223 public void renderIndividual( Map<String, SiteModule> filesToProcess, File outputDirectory )
224 throws DocumentRendererException, IOException
225 {
226
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240 public void renderIndividual( Map<String, SiteModule> filesToProcess, File outputDirectory,
241 DocumentRendererContext context )
242 throws DocumentRendererException, IOException
243 {
244
245 }
246
247
248
249
250
251
252
253
254
255
256
257 public Map<String, SiteModule> getFilesToProcess( File baseDirectory )
258 throws IOException, DocumentRendererException
259 {
260 if ( !baseDirectory.isDirectory() )
261 {
262 getLogger().warn( "No files found to process!" );
263
264 return new HashMap<String, SiteModule>();
265 }
266
267 setBaseDir( baseDirectory.getAbsolutePath() );
268
269 Map<String, SiteModule> filesToProcess = new LinkedHashMap<String, SiteModule>();
270 Map<String, String> duplicatesFiles = new LinkedHashMap<String, String>();
271
272 Collection<SiteModule> modules = siteModuleManager.getSiteModules();
273 for ( SiteModule module : modules )
274 {
275 File moduleBasedir = new File( baseDirectory, module.getSourceDirectory() );
276
277 if ( moduleBasedir.exists() )
278 {
279
280 List<String> allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", null, false );
281
282 String lowerCaseExtension = module.getExtension().toLowerCase( Locale.ENGLISH );
283 List<String> docs = new LinkedList<String>( allFiles );
284
285 for ( Iterator<String> it = docs.iterator(); it.hasNext(); )
286 {
287 String name = it.next().trim();
288
289 if ( !name.toLowerCase( Locale.ENGLISH ).endsWith( "." + lowerCaseExtension ) )
290 {
291 it.remove();
292 }
293 }
294
295 List<String> velocityFiles = new LinkedList<String>( allFiles );
296
297 for ( Iterator<String> it = velocityFiles.iterator(); it.hasNext(); )
298 {
299 String name = it.next().trim();
300
301 if ( !name.toLowerCase( Locale.ENGLISH ).endsWith( lowerCaseExtension + ".vm" ) )
302 {
303 it.remove();
304 }
305 }
306 docs.addAll( velocityFiles );
307
308 for ( String filePath : docs )
309 {
310 filePath = filePath.trim();
311
312 if ( filePath.lastIndexOf( "." ) > 0 )
313 {
314 String key = filePath.substring( 0, filePath.lastIndexOf( "." ) );
315
316 if ( duplicatesFiles.containsKey( key ) )
317 {
318 throw new DocumentRendererException( "Files '" + module.getSourceDirectory()
319 + File.separator + filePath + "' clashes with existing '"
320 + duplicatesFiles.get( key ) + "'." );
321 }
322
323 duplicatesFiles.put( key, module.getSourceDirectory() + File.separator + filePath );
324 }
325
326 filesToProcess.put( filePath, module );
327 }
328 }
329 }
330
331 return filesToProcess;
332 }
333
334
335
336
337
338
339
340
341 public Map<String, SiteModule> getFilesToProcess( Collection<String> files )
342 {
343
344
345
346
347 Map<String, SiteModule> filesToProcess = new HashMap<String, SiteModule>();
348
349 Collection<SiteModule> modules = siteModuleManager.getSiteModules();
350 for ( SiteModule siteModule : modules )
351 {
352 String extension = "." + siteModule.getExtension();
353
354 String sourceDirectory = File.separator + siteModule.getSourceDirectory() + File.separator;
355
356 for ( String file : files )
357 {
358
359
360
361 if ( file.indexOf( sourceDirectory ) != -1 )
362 {
363 filesToProcess.put( file, siteModule );
364 }
365 else if ( file.toLowerCase( Locale.ENGLISH ).endsWith( extension ) )
366 {
367
368 if ( !filesToProcess.containsKey( file ) )
369 {
370 filesToProcess.put( file, siteModule );
371 }
372 }
373 }
374 }
375
376 return filesToProcess;
377 }
378
379
380 public DocumentModel readDocumentModel( File documentDescriptor )
381 throws DocumentRendererException, IOException
382 {
383 DocumentModel documentModel;
384
385 Reader reader = null;
386 try
387 {
388 reader = ReaderFactory.newXmlReader( documentDescriptor );
389 documentModel = new DocumentXpp3Reader().read( reader );
390 }
391 catch ( XmlPullParserException e )
392 {
393 throw new DocumentRendererException( "Error parsing document descriptor", e );
394 }
395 finally
396 {
397 IOUtil.close( reader );
398 }
399
400 return documentModel;
401 }
402
403
404
405
406
407
408 public void setBaseDir( String newDir )
409 {
410 this.baseDir = newDir;
411 }
412
413
414
415
416
417
418 public String getBaseDir()
419 {
420 return this.baseDir;
421 }
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437 protected void parse( String fullDocPath, String parserId, Sink sink )
438 throws DocumentRendererException, IOException
439 {
440 parse( fullDocPath, parserId, sink, null );
441 }
442
443
444
445
446
447
448
449
450
451
452
453 protected void parse( String fullDocPath, String parserId, Sink sink, DocumentRendererContext context )
454 throws DocumentRendererException, IOException
455 {
456 if ( getLogger().isDebugEnabled() )
457 {
458 getLogger().debug( "Parsing file " + fullDocPath );
459 }
460
461 Reader reader = null;
462 try
463 {
464 File f = new File( fullDocPath );
465
466 Parser parser = doxia.getParser( parserId );
467 switch ( parser.getType() )
468 {
469 case Parser.XML_TYPE:
470 reader = ReaderFactory.newXmlReader( f );
471
472 if ( isVelocityFile( f ) )
473 {
474 reader = getVelocityReader( f, ( (XmlStreamReader) reader ).getEncoding(), context );
475 }
476 if ( context != null && Boolean.TRUE.equals( (Boolean) context.get( "validate" ) ) )
477 {
478 reader = validate( reader, fullDocPath );
479 }
480 break;
481
482 case Parser.TXT_TYPE:
483 case Parser.UNKNOWN_TYPE:
484 default:
485 if ( isVelocityFile( f ) )
486 {
487 reader =
488 getVelocityReader( f, ( context == null ? ReaderFactory.FILE_ENCODING
489 : context.getInputEncoding() ), context );
490 }
491 else
492 {
493 if ( context == null )
494 {
495 reader = ReaderFactory.newPlatformReader( f );
496 }
497 else
498 {
499 reader = ReaderFactory.newReader( f, context.getInputEncoding() );
500 }
501 }
502 }
503
504 sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) );
505
506 doxia.parse( reader, parserId, sink );
507 }
508 catch ( ParserNotFoundException e )
509 {
510 throw new DocumentRendererException( "No parser '" + parserId
511 + "' found for " + fullDocPath + ": " + e.getMessage(), e );
512 }
513 catch ( ParseException e )
514 {
515 throw new DocumentRendererException( "Error parsing " + fullDocPath + ": " + e.getMessage(), e );
516 }
517 finally
518 {
519 IOUtil.close( reader );
520
521 sink.flush();
522 }
523 }
524
525
526
527
528
529
530
531 protected void copyResources( File outputDirectory )
532 throws IOException
533 {
534 File resourcesDirectory = new File( getBaseDir(), "resources" );
535
536 if ( !resourcesDirectory.isDirectory() )
537 {
538 return;
539 }
540
541 if ( !outputDirectory.exists() )
542 {
543 outputDirectory.mkdirs();
544 }
545
546 copyDirectory( resourcesDirectory, outputDirectory );
547 }
548
549
550
551
552
553
554
555
556 protected void copyDirectory( File source, File destination )
557 throws IOException
558 {
559 if ( source.isDirectory() && destination.isDirectory() )
560 {
561 DirectoryScanner scanner = new DirectoryScanner();
562
563 String[] includedResources = {"**/**"};
564
565 scanner.setIncludes( includedResources );
566
567 scanner.addDefaultExcludes();
568
569 scanner.setBasedir( source );
570
571 scanner.scan();
572
573 List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
574
575 for ( String name : includedFiles )
576 {
577 File sourceFile = new File( source, name );
578
579 File destinationFile = new File( destination, name );
580
581 FileUtils.copyFile( sourceFile, destinationFile );
582 }
583 }
584 }
585
586
587
588
589
590
591
592
593
594 protected String getOutputName( DocumentModel documentModel )
595 {
596 String outputName = documentModel.getOutputName();
597 if ( outputName == null )
598 {
599 getLogger().info( "No outputName is defined in the document descriptor. Using 'target'" );
600
601 documentModel.setOutputName( "target" );
602 }
603
604 outputName = outputName.trim();
605 if ( outputName.toLowerCase( Locale.ENGLISH ).endsWith( "." + getOutputExtension() ) )
606 {
607 outputName =
608 outputName.substring( 0, outputName.toLowerCase( Locale.ENGLISH )
609 .lastIndexOf( "." + getOutputExtension() ) );
610 }
611 documentModel.setOutputName( outputName );
612
613 return documentModel.getOutputName();
614 }
615
616
617
618
619
620
621
622
623
624
625 private Reader getVelocityReader( File f, String encoding, DocumentRendererContext context )
626 throws DocumentRendererException
627 {
628 if ( getLogger().isDebugEnabled() )
629 {
630 getLogger().debug( "Velocity render for " + f.getAbsolutePath() );
631 }
632
633 SiteResourceLoader.setResource( f.getAbsolutePath() );
634
635 Context velocityContext = new VelocityContext();
636
637 if ( context.getKeys() != null )
638 {
639 for ( int i = 0; i < context.getKeys().length; i++ )
640 {
641 String key = (String) context.getKeys()[i];
642
643 velocityContext.put( key, context.get( key ) );
644 }
645 }
646
647 StringWriter sw = new StringWriter();
648 try
649 {
650 velocity.getEngine().mergeTemplate( f.getAbsolutePath(), encoding, velocityContext, sw );
651 }
652 catch ( Exception e )
653 {
654 throw new DocumentRendererException( "Error whenn parsing Velocity file " + f.getAbsolutePath() + ": "
655 + e.getMessage(), e );
656 }
657
658 return new StringReader( sw.toString() );
659 }
660
661
662
663
664
665 private static boolean isVelocityFile( File f )
666 {
667 return FileUtils.getExtension( f.getAbsolutePath() ).toLowerCase( Locale.ENGLISH ).endsWith( "vm" );
668 }
669
670 private Reader validate( Reader source, String resource )
671 throws ParseException, IOException
672 {
673 getLogger().debug( "Validating: " + resource );
674
675 try
676 {
677 String content = IOUtil.toString( new BufferedReader( source ) );
678
679 new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content );
680
681 return new StringReader( content );
682 }
683 finally
684 {
685 IOUtil.close( source );
686 }
687 }
688 }