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