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