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