1 package org.apache.maven.doxia.siterenderer;
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.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.LineNumberReader;
29 import java.io.OutputStream;
30 import java.io.Reader;
31 import java.io.StringReader;
32 import java.io.StringWriter;
33 import java.io.UnsupportedEncodingException;
34 import java.io.Writer;
35 import java.net.MalformedURLException;
36 import java.net.URL;
37 import java.net.URLClassLoader;
38 import java.text.DateFormat;
39 import java.text.SimpleDateFormat;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.Date;
44 import java.util.Enumeration;
45 import java.util.Iterator;
46 import java.util.LinkedHashMap;
47 import java.util.LinkedList;
48 import java.util.List;
49 import java.util.Locale;
50 import java.util.Map;
51 import java.util.Properties;
52 import java.util.zip.ZipEntry;
53 import java.util.zip.ZipException;
54 import java.util.zip.ZipFile;
55
56 import org.apache.maven.doxia.Doxia;
57 import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
58 import org.apache.maven.doxia.parser.ParseException;
59 import org.apache.maven.doxia.parser.Parser;
60 import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
61 import org.apache.maven.doxia.site.decoration.DecorationModel;
62 import org.apache.maven.doxia.parser.module.ParserModule;
63 import org.apache.maven.doxia.parser.module.ParserModuleManager;
64 import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException;
65 import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
66 import org.apache.maven.doxia.util.XmlValidator;
67 import org.apache.velocity.Template;
68 import org.apache.velocity.context.Context;
69 import org.apache.velocity.tools.ToolManager;
70 import org.codehaus.plexus.component.annotations.Component;
71 import org.codehaus.plexus.component.annotations.Requirement;
72 import org.codehaus.plexus.i18n.I18N;
73 import org.codehaus.plexus.logging.AbstractLogEnabled;
74 import org.codehaus.plexus.util.DirectoryScanner;
75 import org.codehaus.plexus.util.FileUtils;
76 import org.codehaus.plexus.util.IOUtil;
77 import org.codehaus.plexus.util.Os;
78 import org.codehaus.plexus.util.PathTool;
79 import org.codehaus.plexus.util.ReaderFactory;
80 import org.codehaus.plexus.util.StringUtils;
81 import org.codehaus.plexus.util.WriterFactory;
82 import org.codehaus.plexus.velocity.SiteResourceLoader;
83 import org.codehaus.plexus.velocity.VelocityComponent;
84
85
86
87
88
89
90
91
92
93 @Component( role = Renderer.class )
94 public class DefaultSiteRenderer
95 extends AbstractLogEnabled
96 implements Renderer
97 {
98
99
100
101
102 @Requirement( hint = "doxia-site-renderer" )
103 private VelocityComponent velocity;
104
105 @Requirement
106 private ParserModuleManager parserModuleManager;
107
108 @Requirement
109 private Doxia doxia;
110
111 @Requirement
112 private I18N i18n;
113
114 private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources";
115
116 private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm";
117
118 private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm";
119
120
121
122
123
124
125 public void render( Collection<DocumentRenderer> documents, SiteRenderingContext siteRenderingContext,
126 File outputDirectory )
127 throws RendererException, IOException
128 {
129 renderModule( documents, siteRenderingContext, outputDirectory );
130
131 for ( File siteDirectory : siteRenderingContext.getSiteDirectories() )
132 {
133 copyResources( siteRenderingContext, new File( siteDirectory, "resources" ), outputDirectory );
134 }
135 }
136
137
138 public Map<String, DocumentRenderer> locateDocumentFiles( SiteRenderingContext siteRenderingContext )
139 throws IOException, RendererException
140 {
141 Map<String, DocumentRenderer> files = new LinkedHashMap<String, DocumentRenderer>();
142 Map<String, String> moduleExcludes = siteRenderingContext.getModuleExcludes();
143
144 for ( File siteDirectory : siteRenderingContext.getSiteDirectories() )
145 {
146 if ( siteDirectory.exists() )
147 {
148 Collection<ParserModule> modules = parserModuleManager.getParserModules();
149 for ( ParserModule module : modules )
150 {
151 File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() );
152
153 if ( moduleExcludes != null && moduleExcludes.containsKey( module.getParserId() ) )
154 {
155 addModuleFiles( moduleBasedir, module, moduleExcludes.get( module.getParserId() ),
156 files );
157 }
158 else
159 {
160 addModuleFiles( moduleBasedir, module, null, files );
161 }
162 }
163 }
164 }
165
166 for ( ModuleReference module : siteRenderingContext.getModules() )
167 {
168 try
169 {
170 if ( moduleExcludes != null && moduleExcludes.containsKey( module.getParserId() ) )
171 {
172 addModuleFiles( module.getBasedir(), parserModuleManager.getParserModule( module.getParserId() ),
173 moduleExcludes.get( module.getParserId() ), files );
174 }
175 else
176 {
177 addModuleFiles( module.getBasedir(), parserModuleManager.getParserModule( module.getParserId() ), null,
178 files );
179 }
180 }
181 catch ( ParserModuleNotFoundException e )
182 {
183 throw new RendererException( "Unable to find module: " + e.getMessage(), e );
184 }
185 }
186 return files;
187 }
188
189 private void addModuleFiles( File moduleBasedir, ParserModule module, String excludes,
190 Map<String, DocumentRenderer> files )
191 throws IOException, RendererException
192 {
193 if ( moduleBasedir.exists() )
194 {
195 List<String> allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false );
196
197 String fullExtension = "." + module.getExtension();
198 List<String> docs = new LinkedList<String>( allFiles );
199
200 for ( Iterator<String> it = docs.iterator(); it.hasNext(); )
201 {
202 String name = it.next();
203
204 if ( !endsWithIgnoreCase( name, fullExtension ) )
205 {
206 it.remove();
207 }
208 }
209
210 List<String> velocityFiles = new LinkedList<String>( allFiles );
211
212 fullExtension += ".vm";
213 for ( Iterator<String> it = velocityFiles.iterator(); it.hasNext(); )
214 {
215 String name = it.next();
216
217 if ( !endsWithIgnoreCase( name, fullExtension ) )
218 {
219 it.remove();
220 }
221 }
222 docs.addAll( velocityFiles );
223
224 for ( String doc : docs )
225 {
226 RenderingContext context =
227 new RenderingContext( moduleBasedir, doc, module.getParserId(), module.getExtension() );
228
229
230 if ( doc.substring( doc.length() - 3 ).equalsIgnoreCase( ".vm" ) )
231 {
232 context.setAttribute( "velocity", "true" );
233 }
234
235 String key = context.getOutputName();
236 key = StringUtils.replace( key, "\\", "/" );
237
238 if ( files.containsKey( key ) )
239 {
240 DocumentRenderer renderer = files.get( key );
241
242 RenderingContext originalContext = renderer.getRenderingContext();
243
244 File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() );
245
246 throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc
247 + "' clashes with existing '" + originalDoc + "'." );
248 }
249
250
251
252 for ( Map.Entry<String, DocumentRenderer> entry : files.entrySet() )
253 {
254 if ( entry.getKey().equalsIgnoreCase( key ) )
255 {
256 RenderingContext originalContext = entry.getValue().getRenderingContext();
257
258 File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() );
259
260 if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
261 {
262 throw new RendererException( "File '" + module.getSourceDirectory() + File.separator
263 + doc + "' clashes with existing '" + originalDoc + "'." );
264 }
265
266 if ( getLogger().isWarnEnabled() )
267 {
268 getLogger().warn(
269 "File '" + module.getSourceDirectory() + File.separator + doc
270 + "' could clash with existing '" + originalDoc + "'." );
271 }
272 }
273 }
274
275 files.put( key, new DoxiaDocumentRenderer( context ) );
276 }
277 }
278 }
279
280 private void renderModule( Collection<DocumentRenderer> docs, SiteRenderingContext siteRenderingContext,
281 File outputDirectory )
282 throws IOException, RendererException
283 {
284 for ( DocumentRenderer docRenderer : docs )
285 {
286 RenderingContext renderingContext = docRenderer.getRenderingContext();
287
288 File outputFile = new File( outputDirectory, docRenderer.getOutputName() );
289
290 File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() );
291
292 boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() )
293 || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() );
294
295 if ( modified || docRenderer.isOverwrite() )
296 {
297 if ( !outputFile.getParentFile().exists() )
298 {
299 outputFile.getParentFile().mkdirs();
300 }
301
302 if ( getLogger().isDebugEnabled() )
303 {
304 getLogger().debug( "Generating " + outputFile );
305 }
306
307 Writer writer = null;
308 try
309 {
310 writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() );
311 docRenderer.renderDocument( writer, this, siteRenderingContext );
312 }
313 finally
314 {
315 IOUtil.close( writer );
316 }
317 }
318 else
319 {
320 if ( getLogger().isDebugEnabled() )
321 {
322 getLogger().debug( inputFile + " unchanged, not regenerating..." );
323 }
324 }
325 }
326 }
327
328
329 public void renderDocument( Writer writer, RenderingContext renderingContext, SiteRenderingContext siteContext )
330 throws RendererException, FileNotFoundException, UnsupportedEncodingException
331 {
332 SiteRendererSink sink = new SiteRendererSink( renderingContext );
333
334 File doc = new File( renderingContext.getBasedir(), renderingContext.getInputName() );
335
336 Reader reader = null;
337 try
338 {
339 String resource = doc.getAbsolutePath();
340
341 Parser parser = doxia.getParser( renderingContext.getParserId() );
342
343
344 if ( renderingContext.getAttribute( "velocity" ) != null )
345 {
346 try
347 {
348 SiteResourceLoader.setResource( resource );
349
350 Context vc = createVelocityContext( sink, siteContext );
351
352 StringWriter sw = new StringWriter();
353
354 velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw );
355
356 reader = new StringReader( sw.toString() );
357 if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() )
358 {
359 reader = validate( reader, resource );
360 }
361 }
362 catch ( Exception e )
363 {
364 if ( getLogger().isDebugEnabled() )
365 {
366 getLogger().error( "Error parsing " + resource + " as a velocity template, using as text.", e );
367 }
368 else
369 {
370 getLogger().error( "Error parsing " + resource + " as a velocity template, using as text." );
371 }
372 }
373 }
374 else
375 {
376 switch ( parser.getType() )
377 {
378 case Parser.XML_TYPE:
379 reader = ReaderFactory.newXmlReader( doc );
380 if ( siteContext.isValidate() )
381 {
382 reader = validate( reader, resource );
383 }
384 break;
385
386 case Parser.TXT_TYPE:
387 case Parser.UNKNOWN_TYPE:
388 default:
389 reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() );
390 }
391 }
392 sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) );
393
394 if ( reader == null )
395 {
396 throw new RendererException( "Error getting a parser for '" + doc + "'" );
397 }
398 doxia.parse( reader, renderingContext.getParserId(), sink );
399 }
400 catch ( ParserNotFoundException e )
401 {
402 throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e );
403 }
404 catch ( ParseException e )
405 {
406 throw new RendererException( "Error parsing '"
407 + doc + "': line [" + e.getLineNumber() + "] " + e.getMessage(), e );
408 }
409 catch ( IOException e )
410 {
411 throw new RendererException( "IOException when processing '" + doc + "'", e );
412 }
413 finally
414 {
415 sink.flush();
416
417 sink.close();
418
419 IOUtil.close( reader );
420 }
421
422 generateDocument( writer, sink, siteContext );
423 }
424
425 private Context createVelocityContext( SiteRendererSink sink, SiteRenderingContext siteRenderingContext )
426 {
427 ToolManager toolManager = new ToolManager( true );
428 Context context = toolManager.createContext();
429
430
431
432
433
434 RenderingContext renderingContext = sink.getRenderingContext();
435 context.put( "relativePath", renderingContext.getRelativePath() );
436
437
438 context.put( "authors", sink.getAuthors() );
439
440 context.put( "shortTitle", sink.getTitle() );
441
442
443 String title = "";
444 if ( siteRenderingContext.getDecoration() != null
445 && siteRenderingContext.getDecoration().getName() != null )
446 {
447 title = siteRenderingContext.getDecoration().getName();
448 }
449 else if ( siteRenderingContext.getDefaultWindowTitle() != null )
450 {
451 title = siteRenderingContext.getDefaultWindowTitle();
452 }
453
454 if ( title.length() > 0 )
455 {
456 title += " – ";
457 }
458 title += sink.getTitle();
459
460 context.put( "title", title );
461
462 context.put( "headContent", sink.getHead() );
463
464 context.put( "bodyContent", sink.getBody() );
465
466 context.put( "decoration", siteRenderingContext.getDecoration() );
467
468 SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" );
469 if ( StringUtils.isNotEmpty( sink.getDate() ) )
470 {
471 try
472 {
473
474 context.put( "dateCreation",
475 sdf.format( new SimpleDateFormat( "yyyy-MM-dd" ).parse( sink.getDate() ) ) );
476 }
477 catch ( java.text.ParseException e )
478 {
479 getLogger().debug( "Could not parse date: " + sink.getDate() + ", ignoring!", e );
480 }
481 }
482 context.put( "dateRevision", sdf.format( new Date() ) );
483
484 context.put( "currentDate", new Date() );
485
486 context.put( "publishDate", siteRenderingContext.getPublishDate() );
487
488 Locale locale = siteRenderingContext.getLocale();
489
490 DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.DEFAULT, locale );
491
492 if ( siteRenderingContext.getDecoration().getPublishDate() != null )
493 {
494 if ( StringUtils.isNotBlank( siteRenderingContext.getDecoration().getPublishDate().getFormat() ) )
495 {
496 dateFormat =
497 new SimpleDateFormat( siteRenderingContext.getDecoration().getPublishDate().getFormat(), locale );
498 }
499 }
500
501 context.put( "dateFormat", dateFormat );
502
503 String currentFileName = renderingContext.getOutputName().replace( '\\', '/' );
504 context.put( "currentFileName", currentFileName );
505
506 context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) );
507
508 context.put( "locale", locale );
509 context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) );
510
511 InputStream inputStream = null;
512 try
513 {
514 inputStream = this.getClass().getClassLoader().getResourceAsStream( "META-INF/maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" );
515 if ( inputStream == null )
516 {
517 getLogger().debug( "pom.properties for doxia-site-renderer could not be found." );
518 }
519 else
520 {
521 Properties properties = new Properties();
522 properties.load( inputStream );
523 context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) );
524 }
525 }
526 catch( IOException e )
527 {
528 getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available in the velocityContext." );
529 }
530 finally
531 {
532 IOUtil.close( inputStream );
533 }
534
535
536 Map<String, ?> templateProperties = siteRenderingContext.getTemplateProperties();
537
538 if ( templateProperties != null )
539 {
540 for ( Map.Entry<String, ?> entry : templateProperties.entrySet() )
541 {
542 context.put( entry.getKey(), entry.getValue() );
543 }
544 }
545
546
547
548
549
550 context.put( "PathTool", new PathTool() );
551
552 context.put( "FileUtils", new FileUtils() );
553
554 context.put( "StringUtils", new StringUtils() );
555
556 context.put( "i18n", i18n );
557
558 return context;
559 }
560
561
562 public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext )
563 throws RendererException
564 {
565 Context context = createVelocityContext( sink, siteRenderingContext );
566
567 writeTemplate( writer, context, siteRenderingContext );
568 }
569
570 private void writeTemplate( Writer writer, Context context, SiteRenderingContext siteContext )
571 throws RendererException
572 {
573 ClassLoader old = null;
574
575 if ( siteContext.getTemplateClassLoader() != null )
576 {
577
578
579
580
581 old = Thread.currentThread().getContextClassLoader();
582
583 Thread.currentThread().setContextClassLoader( siteContext.getTemplateClassLoader() );
584 }
585
586 try
587 {
588 processTemplate( siteContext.getTemplateName(), context, writer );
589 }
590 finally
591 {
592 IOUtil.close( writer );
593
594 if ( old != null )
595 {
596 Thread.currentThread().setContextClassLoader( old );
597 }
598 }
599 }
600
601
602
603
604 private void processTemplate( String templateName, Context context, Writer writer )
605 throws RendererException
606 {
607 Template template;
608
609 try
610 {
611 template = velocity.getEngine().getTemplate( templateName );
612 }
613 catch ( Exception e )
614 {
615 throw new RendererException( "Could not find the template '" + templateName, e );
616 }
617
618 try
619 {
620 template.merge( context, writer );
621 }
622 catch ( Exception e )
623 {
624 throw new RendererException( "Error while generating code.", e );
625 }
626 }
627
628
629 public SiteRenderingContext createContextForSkin( File skinFile, Map<String, ?> attributes,
630 DecorationModel decoration, String defaultWindowTitle,
631 Locale locale )
632 throws IOException
633 {
634 SiteRenderingContext context = new SiteRenderingContext();
635
636 ZipFile zipFile = getZipFile( skinFile );
637
638 try
639 {
640 if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null )
641 {
642 context.setTemplateName( SKIN_TEMPLATE_LOCATION );
643 context.setTemplateClassLoader( new URLClassLoader( new URL[]{skinFile.toURI().toURL()} ) );
644 }
645 else
646 {
647 context.setTemplateName( DEFAULT_TEMPLATE );
648 context.setTemplateClassLoader( getClass().getClassLoader() );
649 context.setUsingDefaultTemplate( true );
650 }
651 }
652 finally
653 {
654 closeZipFile( zipFile );
655 }
656
657 context.setTemplateProperties( attributes );
658 context.setLocale( locale );
659 context.setDecoration( decoration );
660 context.setDefaultWindowTitle( defaultWindowTitle );
661 context.setSkinJarFile( skinFile );
662
663 return context;
664 }
665
666 private static ZipFile getZipFile( File file )
667 throws IOException
668 {
669 if ( file == null )
670 {
671 throw new IOException( "Error opening ZipFile: null" );
672 }
673
674 try
675 {
676
677 return new ZipFile( file );
678 }
679 catch ( ZipException ex )
680 {
681 IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() );
682 ioe.initCause( ex );
683 throw ioe;
684 }
685 }
686
687
688 public SiteRenderingContext createContextForTemplate( File templateFile, File skinFile, Map<String, ?> attributes,
689 DecorationModel decoration, String defaultWindowTitle,
690 Locale locale )
691 throws MalformedURLException
692 {
693 SiteRenderingContext context = new SiteRenderingContext();
694
695 context.setTemplateName( templateFile.getName() );
696 context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) );
697
698 context.setTemplateProperties( attributes );
699 context.setLocale( locale );
700 context.setDecoration( decoration );
701 context.setDefaultWindowTitle( defaultWindowTitle );
702 context.setSkinJarFile( skinFile );
703
704 return context;
705 }
706
707 private static void closeZipFile( ZipFile zipFile )
708 {
709
710 try
711 {
712 zipFile.close();
713 }
714 catch ( IOException e )
715 {
716
717 }
718 }
719
720
721 public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, File outputDirectory )
722 throws IOException
723 {
724 if ( siteRenderingContext.getSkinJarFile() != null )
725 {
726 ZipFile file = getZipFile( siteRenderingContext.getSkinJarFile() );
727
728 try
729 {
730 for ( Enumeration<? extends ZipEntry> e = file.entries(); e.hasMoreElements(); )
731 {
732 ZipEntry entry = e.nextElement();
733
734 if ( !entry.getName().startsWith( "META-INF/" ) )
735 {
736 File destFile = new File( outputDirectory, entry.getName() );
737 if ( !entry.isDirectory() )
738 {
739 destFile.getParentFile().mkdirs();
740
741 copyFileFromZip( file, entry, destFile );
742 }
743 else
744 {
745 destFile.mkdirs();
746 }
747 }
748 }
749 }
750 finally
751 {
752 closeZipFile( file );
753 }
754 }
755
756 if ( siteRenderingContext.isUsingDefaultTemplate() )
757 {
758 InputStream resourceList = getClass().getClassLoader()
759 .getResourceAsStream( RESOURCE_DIR + "/resources.txt" );
760
761 if ( resourceList != null )
762 {
763 Reader r = null;
764 LineNumberReader reader = null;
765 try
766 {
767 r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 );
768 reader = new LineNumberReader( r );
769
770 String line = reader.readLine();
771
772 while ( line != null )
773 {
774 InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line );
775
776 if ( is == null )
777 {
778 throw new IOException( "The resource " + line + " doesn't exist." );
779 }
780
781 File outputFile = new File( outputDirectory, line );
782
783 if ( !outputFile.getParentFile().exists() )
784 {
785 outputFile.getParentFile().mkdirs();
786 }
787
788 OutputStream os = null;
789 try
790 {
791
792 os = new FileOutputStream( outputFile );
793 IOUtil.copy( is, os );
794 }
795 finally
796 {
797 IOUtil.close( os );
798 }
799
800 IOUtil.close( is );
801
802 line = reader.readLine();
803 }
804 }
805 finally
806 {
807 IOUtil.close( reader );
808 IOUtil.close( r );
809 }
810 }
811 }
812
813
814 if ( resourcesDirectory != null && resourcesDirectory.exists() )
815 {
816 copyDirectory( resourcesDirectory, outputDirectory );
817 }
818
819
820 File siteCssFile = new File( outputDirectory, "/css/site.css" );
821 if ( !siteCssFile.exists() )
822 {
823
824 File cssDirectory = new File( outputDirectory, "/css/" );
825 boolean created = cssDirectory.mkdirs();
826 if ( created && getLogger().isDebugEnabled() )
827 {
828 getLogger().debug(
829 "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." );
830 }
831
832
833 if ( getLogger().isDebugEnabled() )
834 {
835 getLogger().debug(
836 "The file '" + siteCssFile.getAbsolutePath() + "' does not exists. Creating an empty file." );
837 }
838 Writer writer = null;
839 try
840 {
841 writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() );
842
843 writer.write( "/* You can override this file with your own styles */" );
844 }
845 finally
846 {
847 IOUtil.close( writer );
848 }
849 }
850 }
851
852 private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile )
853 throws IOException
854 {
855 FileOutputStream fos = new FileOutputStream( destFile );
856
857 try
858 {
859 IOUtil.copy( file.getInputStream( entry ), fos );
860 }
861 finally
862 {
863 IOUtil.close( fos );
864 }
865 }
866
867
868
869
870
871
872
873
874 protected void copyDirectory( File source, File destination )
875 throws IOException
876 {
877 if ( source.exists() )
878 {
879 DirectoryScanner scanner = new DirectoryScanner();
880
881 String[] includedResources = {"**/**"};
882
883 scanner.setIncludes( includedResources );
884
885 scanner.addDefaultExcludes();
886
887 scanner.setBasedir( source );
888
889 scanner.scan();
890
891 List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
892
893 for ( String name : includedFiles )
894 {
895 File sourceFile = new File( source, name );
896
897 File destinationFile = new File( destination, name );
898
899 FileUtils.copyFile( sourceFile, destinationFile );
900 }
901 }
902 }
903
904 private Reader validate( Reader source, String resource )
905 throws ParseException, IOException
906 {
907 getLogger().debug( "Validating: " + resource );
908
909 try
910 {
911 String content = IOUtil.toString( new BufferedReader( source ) );
912
913 new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content );
914
915 return new StringReader( content );
916 }
917 finally
918 {
919 IOUtil.close( source );
920 }
921 }
922
923
924 private boolean endsWithIgnoreCase( String str, String searchStr )
925 {
926 if ( str.length() < searchStr.length() )
927 {
928 return false;
929 }
930
931 return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() );
932 }
933 }