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