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