/* * maven-docbook-plugin - Copyright (C) 2005 OPEN input - http://www.openinput.com/ * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $Id$ */ package org.codehaus.mojo.docbook; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.net.URI; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.compiler.util.scan.InclusionScanException; import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner; import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping; import org.codehaus.plexus.util.DirectoryScanner; /** * @author jgonzalez */ public class OLinkDBUpdater { protected Log log; protected File sourceDirectory; protected File databaseDirectory; protected URI stylesheetLocation; protected Collection artifacts; /** * @param log * @param sourceDirectory * @param databaseDirectory * @param artifacts */ public OLinkDBUpdater( Log log, File sourceDirectory, File databaseDirectory, URI stylesheetLocation, Collection artifacts ) { this.log = log; this.sourceDirectory = sourceDirectory; this.databaseDirectory = databaseDirectory; this.stylesheetLocation = stylesheetLocation; this.artifacts = artifacts; } public void update() { StaleSourceScanner scanner = new StaleSourceScanner( 0, Collections.singleton( "**/*.xml" ), Collections.EMPTY_SET ); scanner.addSourceMapping( new SuffixMapping( ".xml", ".xml.db" ) ); Set staleDocbookFiles; try { staleDocbookFiles = (Set) scanner.getIncludedSources( this.sourceDirectory, this.databaseDirectory ); } catch ( InclusionScanException e ) { throw new RuntimeException( "Error scanning sources in " + sourceDirectory, e ); } if ( !staleDocbookFiles.isEmpty() ) { DirectoryScanner docbookScanner = new DirectoryScanner(); docbookScanner.setBasedir( this.sourceDirectory ); docbookScanner.setFollowSymlinks( true ); docbookScanner.setIncludes( new String[] { "**/*.xml" } ); docbookScanner.scan(); String[] docbookFiles = docbookScanner.getIncludedFiles(); this.prepareFileSystem( docbookFiles ); this.updateOLinkDatabase( staleDocbookFiles ); this.createMasterOLinkDatabase( docbookFiles ); } else { this.log.info( "olink database up to date" ); } } /** * @param docbookFiles */ protected void prepareFileSystem( String[] docbookFiles ) { log.debug( "Creating database directories for the following files - " + Arrays.asList( docbookFiles ).toString() ); // TODO: This should be a bit smarter also, shouldn't it? for ( int fileIndex = 0; fileIndex < docbookFiles.length; fileIndex++ ) { String docbookFile = docbookFiles[fileIndex]; int lastFileSeparator = docbookFile.lastIndexOf( File.separator ); if ( lastFileSeparator > 0 ) { File directory = new File( this.databaseDirectory, docbookFile.substring( 0, lastFileSeparator ) ); directory.mkdirs(); } } } /** * @param docbookFiles */ protected void updateOLinkDatabase( Set docbookFiles ) { this.log.info( "Loading olink database generation stylesheet" ); TransformerFactory tf = TransformerFactory.newInstance(); MojoURIResolver resolver = new MojoURIResolver( artifacts ); String styleSheetSourceLocation = this.stylesheetLocation.toASCIIString() + "xhtml/"; Source docbookStyleSheetSource; try { docbookStyleSheetSource = resolver.resolve( "docbook.xsl", styleSheetSourceLocation ); } catch ( TransformerException e ) { throw new RuntimeException( "Unable to resolve " + styleSheetSourceLocation + "/docbook.xsl", e ); } tf.setURIResolver( resolver ); if ( tf.getFeature( SAXSource.FEATURE ) ) { SAXTransformerFactory stf = ( (SAXTransformerFactory) tf ); } Transformer olinkDBGenerator; try { olinkDBGenerator = tf.newTransformer( docbookStyleSheetSource ); } catch ( TransformerConfigurationException e ) { throw new RuntimeException( "Unable to get a transformer instance from source " + docbookStyleSheetSource.getSystemId(), e ); } olinkDBGenerator.setParameter( "collect.xref.targets", "only" ); olinkDBGenerator.setParameter( "generate.toc", "" ); this.log.info( "Creating olink database for " + docbookFiles.size() + " Docbook stale file(s)" ); Iterator filesIterator = docbookFiles.iterator(); while ( filesIterator.hasNext() ) { File docbookFile = (File) filesIterator.next(); this.log.debug( "Processing " + this.sourceDirectory + File.separator + docbookFile ); String relativePath = docbookFile.getAbsolutePath().substring( (int) this.sourceDirectory.getAbsolutePath() .length() ); File databaseFile = new File( this.databaseDirectory, relativePath + ".db" ); Source source = new StreamSource( docbookFile ); Result result = new StreamResult( new OLinkDBUpdater.NullWriter() ); olinkDBGenerator.setParameter( "targets.filename", databaseFile.getAbsolutePath() ); try { olinkDBGenerator.transform( source, result ); } catch ( TransformerException e ) { throw new RuntimeException( "Unable to transform from source " + source.getSystemId() + " into " + result.getSystemId(), e ); } this.log.debug( "Generated " + this.databaseDirectory + File.separator + docbookFile ); } } /** * @param docbookFiles */ protected void createMasterOLinkDatabase( String[] docbookFiles ) { File file = new File( this.databaseDirectory + System.getProperty( "file.separator" ) + "olinkdb.xml" ); this.log.info( "Creating master olink database file " + file ); try { BufferedWriter masterOlinkDBFile = new BufferedWriter( new FileWriter( file ) ); // Write header masterOlinkDBFile.write( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( "<!DOCTYPE targetset SYSTEM \"" ); masterOlinkDBFile.write( this.stylesheetLocation.resolve( "common/targetdatabase.dtd" ).toString() ); masterOlinkDBFile.write( "\" >" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( "<targetset>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( " <sitemap>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( " <dir name=\"root\">" ); masterOlinkDBFile.newLine(); this.writeDirectoryTagBody( masterOlinkDBFile, 1, "", docbookFiles ); masterOlinkDBFile.write( " </dir>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( " </sitemap>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.write( "</targetset>" ); masterOlinkDBFile.newLine(); masterOlinkDBFile.close(); } catch ( IOException e ) { throw new RuntimeException( "Error creating OLink database " + file, e ); } } /** * @param writer * @param level * @param currentDirectory * @param files * @throws IOException */ protected void writeDirectoryTagBody( BufferedWriter writer, int level, String currentDirectory, String[] files ) throws IOException { int currentDirectoryLength = currentDirectory.length(); String lastRelativeDirectory = ""; List subdirectory = new LinkedList(); for ( int fileIndex = 0; fileIndex < files.length; fileIndex++ ) { String file = files[fileIndex]; String relativeFile = file.substring( currentDirectoryLength ); if ( relativeFile.indexOf( File.separator ) == -1 ) { String fileID = OLinkDBUpdater.computeFileID( file ); writer.write( OLinkDBUpdater.indenting( level ) + "<document targetdoc=\"" ); writer.write( fileID ); writer.write( "\" baseuri=\"" ); writer.write( relativeFile.substring( 0, relativeFile.lastIndexOf( "." ) ) + ".html\">" ); writer.write( "<xi:include xmlns:xi=\"http://www.w3.org/2003/XInclude\" href=\"" ); writer.write( file.replace( File.separatorChar, '/' ) + ".db\"/>" ); writer.write( "</document>" ); writer.newLine(); } else { String relativeDirectory = relativeFile.substring( 0, relativeFile.indexOf( File.separator ) ); if ( !relativeDirectory.equals( lastRelativeDirectory ) ) { if ( !subdirectory.isEmpty() ) { writer.write( OLinkDBUpdater.indenting( level ) + "<dir name=\"" + lastRelativeDirectory + "\">" ); writer.newLine(); this.writeDirectoryTagBody( writer, level + 1, currentDirectory + File.separator + lastRelativeDirectory, (String[]) subdirectory.toArray( new String[] {} ) ); writer.write( OLinkDBUpdater.indenting( level ) + "</dir>" ); writer.newLine(); } lastRelativeDirectory = relativeDirectory; subdirectory.clear(); } subdirectory.add( file ); } } if ( !subdirectory.isEmpty() ) { writer.write( OLinkDBUpdater.indenting( level ) + "<dir name=\"" + lastRelativeDirectory + "\">" ); writer.newLine(); this.writeDirectoryTagBody( writer, level + 1, currentDirectory + File.separator + lastRelativeDirectory, (String[]) subdirectory.toArray( new String[] {} ) ); writer.write( OLinkDBUpdater.indenting( level ) + "</dir>" ); writer.newLine(); } } /** * @param docbookFileName * @return */ public static String computeFileID( String docbookFileName ) { String fileID = docbookFileName; if ( docbookFileName.indexOf( File.separator ) == 0 ) { fileID = docbookFileName.substring( File.separator.length() ); } return fileID.replace( File.separatorChar, '-' ); } /** * @param level * @return */ protected static String indenting( int level ) { StringBuffer indent = new StringBuffer( " " ); for ( int currentLevel = 1; currentLevel < level; currentLevel++ ) { indent.append( " " ); } return indent.toString(); } private static class NullWriter extends Writer { /* * (non-Javadoc) * * @see java.io.Writer#write(char[], int, int) */ // @Override public void write( char[] cbuff, int off, int len ) throws IOException { // Do nothing } /* * (non-Javadoc) * * @see java.io.Writer#flush() */ // @Override public void flush() throws IOException { // Do nothing } /* * (non-Javadoc) * * @see java.io.Writer#close() */ // @Override public void close() throws IOException { // Do nothing } } }