| /************************************************************************ |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER |
| * |
| * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. |
| * |
| * Use is subject to license terms. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy |
| * of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also |
| * obtain a copy of the License at http://odftoolkit.org/docs/license.txt |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ************************************************************************/ |
| |
| package org.odftoolkit.odfxsltrunner; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import javax.xml.transform.ErrorListener; |
| import javax.xml.transform.Result; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.URIResolver; |
| import javax.xml.transform.sax.SAXSource; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| import org.odftoolkit.odfdom.pkg.OdfPackage; |
| import org.odftoolkit.odfdom.pkg.manifest.OdfFileEntry; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.XMLReader; |
| import org.xml.sax.helpers.XMLReaderFactory; |
| |
| /** |
| * Class for applying style sheets to ODF documents. |
| */ |
| public class ODFXSLTRunner { |
| |
| /** |
| * Input file is a plain XML file. |
| */ |
| public static final int INPUT_MODE_FILE = 0; |
| |
| /** |
| * Input file is an ODF package. The style sheet is applied to the |
| * specified sub file. |
| */ |
| public static final int INPUT_MODE_PACKAGE = 1; |
| |
| /** |
| * Output file is a plain XML or text file. |
| */ |
| public static final int OUTPUT_MODE_FILE = 0; |
| |
| /** |
| * Output is stdout. |
| */ |
| public static final int OUTPUT_MODE_STDOUT = 1; |
| |
| /** |
| * The transformation replaces the specified path within |
| * the input file. |
| */ |
| public static final int OUTPUT_MODE_REPLACE_INPUT_PACKAGE = 2; |
| |
| /** |
| * The input package is copied and the result of the transformation |
| * is stored in the specified path within the copied package. |
| */ |
| public static final int OUTPUT_MODE_COPY_INPUT_PACKAGE = 3; |
| |
| /** |
| * The result of the transformation is stored in the specified path within |
| * the output package. |
| */ |
| public static final int OUTPUT_MODE_TEMPLATE_PACKAGE = 4; |
| |
| private static final int FILE_COPY_BUFFER_SIZE = 4096; |
| |
| /** |
| * Create new instance of ODFXSLTRunner. |
| */ |
| public ODFXSLTRunner() |
| { |
| } |
| |
| /** |
| * Apply a style sheeet. |
| * |
| * @param aStyleSheet Path of the style sheet |
| * @param aParams Parameters that are passed to the XSLT processor |
| * @param aInputFile Path of the input file |
| * @param aInputMode Input mode |
| * @param aOutputFile Path of the output file |
| * @param aOutputMode Output mode |
| * @param aTransformerFactoryClassName XSLT transformer factory to use |
| * @param aExtractFileNames A list of files or directory that shell be extracted from the package |
| * @param aPathInPackage Path within the package. Default is "content.xml" |
| * @param aLogger Logger object |
| * |
| * @return true if an error occured. |
| */ |
| public boolean runXSLT( String aStyleSheet, List<XSLTParameter> aParams, |
| String aInputFile, int aInputMode, |
| String aOutputFile, int aOutputMode, |
| String aPathInPackage, |
| String aTransformerFactoryClassName, |
| List<String> aExtractFileNames, |
| Logger aLogger ) |
| { |
| return runXSLT( new File( aStyleSheet ), aParams, |
| new File( aInputFile), aInputMode, |
| aOutputFile != null ? new File(aOutputFile) : null, aOutputMode, |
| aPathInPackage, aTransformerFactoryClassName, |
| aExtractFileNames, aLogger ); |
| } |
| |
| |
| /** |
| * Apply a style sheeet. |
| * |
| * @param aStyleSheetFile Style sheet |
| * @param aParams Parameters that are passed to the XSLT processor |
| * @param aInputFile Input file |
| * @param aInputMode Input mode |
| * @param aOutputFile Output file |
| * @param aOutputMode Output mode |
| * @param aPathInPackage Path within the package. Default is "content.xml" |
| * @param aTransformerFactoryClassName XSLT transformer factory to use |
| * @param aExtractFileNames A list of files or directory that shell be extracted from the package |
| * @param aLogger Logger object |
| * |
| * @return true if an error occured. |
| */ |
| public boolean runXSLT( File aStyleSheetFile, List<XSLTParameter> aParams, |
| File aInputFile, int aInputMode, |
| File aOutputFile, int aOutputMode, |
| String aPathInPackage, |
| String aTransformerFactoryClassName, |
| List<String> aExtractFileNames, |
| Logger aLogger ) |
| { |
| boolean bError = false; |
| URIResolver aURIResolver = null; |
| |
| InputSource aInputSource = null; |
| OdfPackage aInputPkg = null; |
| String aMediaType ="text/xml"; |
| aLogger.setName( aInputFile.getAbsolutePath() ); |
| try |
| { |
| if( INPUT_MODE_FILE == aInputMode ) |
| { |
| aInputSource = new InputSource( new FileInputStream(aInputFile) ); |
| } |
| else |
| { |
| aInputPkg = OdfPackage.loadPackage( aInputFile ); |
| aLogger.setName( aInputFile.getAbsolutePath(), aPathInPackage ); |
| aInputSource = new InputSource( aInputPkg.getInputStream(aPathInPackage) ); |
| aInputSource.setSystemId( aInputFile.toURI().toString() + '/' + aPathInPackage ); |
| OdfFileEntry aFileEntry = aInputPkg.getFileEntry(aPathInPackage); |
| if( aFileEntry != null ) |
| aMediaType = aFileEntry.getMediaTypeString(); |
| aURIResolver = |
| new ODFURIResolver( aInputPkg, aInputFile.toURI().toString(), aPathInPackage, aLogger ); |
| } |
| } |
| catch( Exception e ) |
| { |
| aLogger.logFatalError(e.getMessage()); |
| return true; |
| } |
| String aInputName = aLogger.getName(); |
| |
| Result aOutputResult = null; |
| OdfPackage aOutputPkg = null; |
| OutputStream aOutputStream = null; |
| aLogger.setName( aOutputFile != null ? aOutputFile.getAbsolutePath() : "(none)" ); |
| boolean bMkOutputDirs = false; |
| try |
| { |
| switch( aOutputMode ) |
| { |
| case OUTPUT_MODE_FILE: |
| bMkOutputDirs = true; |
| aOutputResult = new StreamResult( aOutputFile ); |
| break; |
| case OUTPUT_MODE_STDOUT: |
| aOutputResult = new StreamResult( System.out ); |
| break; |
| case OUTPUT_MODE_REPLACE_INPUT_PACKAGE: |
| aOutputPkg = aInputPkg; |
| aOutputFile = aInputFile; |
| break; |
| case OUTPUT_MODE_COPY_INPUT_PACKAGE: |
| bMkOutputDirs = true; |
| aOutputPkg = aInputPkg; |
| break; |
| case OUTPUT_MODE_TEMPLATE_PACKAGE: |
| aOutputPkg = OdfPackage.loadPackage( aOutputFile ); |
| break; |
| } |
| if( aOutputResult == null ) |
| { |
| aLogger.setName( aOutputFile.getAbsolutePath(), aPathInPackage ); |
| aOutputStream = |
| aOutputPkg.insertOutputStream(aPathInPackage, aMediaType ); |
| aOutputResult = new StreamResult( aOutputStream ); |
| } |
| } |
| catch( Exception e ) |
| { |
| aLogger.logFatalError(e.getMessage()); |
| return true; |
| } |
| |
| if( bMkOutputDirs ) |
| { |
| File aOutputDir = aOutputFile.getParentFile(); |
| if( aOutputDir != null ) |
| aOutputDir.mkdirs(); |
| } |
| |
| String aOutputName = aLogger.getName(); |
| |
| aLogger.setName( aStyleSheetFile.getAbsolutePath() ); |
| aLogger.logInfo( "Applying stylesheet to '" + aInputName + "'"); |
| bError = runXSLT( aStyleSheetFile, aParams, aInputSource, aOutputResult, |
| aTransformerFactoryClassName, aURIResolver, aLogger ); |
| if( bError ) |
| return true; |
| |
| aLogger.setName( aOutputFile != null ? aOutputFile.getAbsolutePath() : "(none)" ); |
| try |
| { |
| aLogger.logInfo( "Storing transformation result to '" + aOutputName + "'"); |
| if( !bError && aOutputStream != null ) |
| aOutputStream.close(); |
| if( !bError && aOutputPkg != null ) |
| aOutputPkg.save(aOutputFile); |
| if( aOutputMode == OUTPUT_MODE_FILE && aExtractFileNames != null && aInputPkg != null ) |
| { |
| File aTargetDir = aOutputFile.getParentFile(); |
| extractFiles( aInputPkg, aTargetDir, aExtractFileNames, aLogger ); |
| } |
| } |
| catch( Exception e ) |
| { |
| aLogger.logFatalError(e.getMessage()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| private boolean runXSLT( File aStyleSheetFile, |
| List<XSLTParameter> aParams, |
| InputSource aInputInputSource, Result aOutputTarget, |
| String aTransformerFactoryClassName, |
| URIResolver aURIResolver, |
| Logger aLogger ) |
| { |
| InputStream aStyleSheetInputStream = null; |
| try |
| { |
| aStyleSheetInputStream = new FileInputStream(aStyleSheetFile); |
| } |
| catch( FileNotFoundException e ) |
| { |
| aLogger.logFatalError(e.getMessage()); |
| return true; |
| } |
| |
| InputSource aStyleSheetInputSource = new InputSource(aStyleSheetInputStream); |
| aStyleSheetInputSource.setSystemId(aStyleSheetFile.getAbsolutePath()); |
| |
| XMLReader aStyleSheetXMLReader = null; |
| XMLReader aInputXMLReader = null; |
| try |
| { |
| aStyleSheetXMLReader = XMLReaderFactory.createXMLReader(); |
| aInputXMLReader = XMLReaderFactory.createXMLReader(); |
| } |
| catch( SAXException e ) |
| { |
| aLogger.logFatalError(e.getMessage()); |
| return true; |
| } |
| |
| aStyleSheetXMLReader.setErrorHandler(new SAXErrorHandler(aLogger)); |
| aInputXMLReader.setErrorHandler(new SAXErrorHandler(aLogger)); |
| aInputXMLReader.setEntityResolver(new ODFEntityResolver(aLogger)); |
| |
| Source aStyleSheetSource = new SAXSource( aStyleSheetXMLReader, aStyleSheetInputSource ); |
| Source aInputSource = new SAXSource( aInputXMLReader, aInputInputSource ); |
| |
| if( aTransformerFactoryClassName!=null ) |
| aLogger.logInfo( "Requesting transformer factory class: " + aTransformerFactoryClassName ); |
| TransformerFactory aFactory = null; |
| if(aTransformerFactoryClassName==null) |
| { |
| aFactory = TransformerFactory.newInstance(); |
| } |
| else |
| { |
| try |
| { |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
| if (cl == null) |
| cl = ClassLoader.getSystemClassLoader(); |
| Class classInstance = cl.loadClass(aTransformerFactoryClassName); |
| aFactory = (TransformerFactory)classInstance.newInstance(); |
| } |
| catch( ClassNotFoundException ce ) |
| { |
| aLogger.logFatalError(ce.getMessage()); |
| return true; |
| } |
| catch( InstantiationException ie ) |
| { |
| aLogger.logFatalError(ie.getMessage()); |
| return true; |
| } |
| catch( IllegalAccessException ile ) |
| { |
| aLogger.logFatalError(ile.getMessage()); |
| return true; |
| } |
| } |
| ErrorListener aErrorListener = new TransformerErrorListener( aLogger ); |
| aLogger.logInfo( "Using transformer factory class: " + aFactory.getClass().getName() ); |
| aFactory.setErrorListener(aErrorListener); |
| |
| try |
| { |
| Transformer aTransformer = aFactory.newTransformer(aStyleSheetSource); |
| |
| if( aParams != null ) |
| { |
| Iterator<XSLTParameter> aIter = aParams.iterator(); |
| while( aIter.hasNext() ) |
| { |
| XSLTParameter aParam = aIter.next(); |
| aTransformer.setParameter(aParam.getName(), aParam.getValue()); |
| aLogger.logInfo("Using parameter: "+aParam.getName()+"="+aParam.getValue()); |
| } |
| } |
| aTransformer.setErrorListener(aErrorListener); |
| if( aURIResolver != null ) |
| aTransformer.setURIResolver(aURIResolver); |
| aTransformer.transform(aInputSource, aOutputTarget); |
| } |
| catch( TransformerException e ) |
| { |
| aLogger.logFatalError(e); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private boolean extractFiles( OdfPackage aInputPkg, |
| File aTargetDir, |
| List<String> aExtractFileNames, |
| Logger aLogger ) |
| { |
| Set<String> aInputPkgEntries = aInputPkg.getFilePaths(); |
| |
| Iterator<String> aInputPkgEntryIter = aInputPkgEntries.iterator(); |
| while( aInputPkgEntryIter.hasNext() ) |
| { |
| String aInputFileName = aInputPkgEntryIter.next(); |
| |
| Iterator<String> aExtractFileNameIter = aExtractFileNames.iterator(); |
| while( aExtractFileNameIter.hasNext() ) |
| { |
| String aExtractFileName = aExtractFileNameIter.next(); |
| if( !aInputFileName.endsWith("/") && |
| (aInputFileName.equals(aExtractFileName) || |
| (aExtractFileName.endsWith("/") ? aInputFileName.startsWith(aExtractFileName) |
| : aInputFileName.startsWith(aExtractFileName+"/")) ) ) |
| { |
| try |
| { |
| File aTargetFile = new File( aTargetDir, aInputFileName ); |
| File aTargetFileDir = aTargetFile.getParentFile(); |
| if( aTargetFileDir != null ) |
| aTargetFileDir.mkdirs(); |
| |
| aLogger.logInfo( "Extracting file " + aInputFileName + " to " + aTargetFile.getAbsolutePath() ); |
| InputStream aInputStream = aInputPkg.getInputStream(aInputFileName); |
| OutputStream aTargetStream = new FileOutputStream( aTargetFile ); |
| byte[] aBuffer = new byte[FILE_COPY_BUFFER_SIZE]; |
| int n = 0; |
| while ((n = aInputStream.read(aBuffer, 0, FILE_COPY_BUFFER_SIZE)) > -1) |
| { |
| aTargetStream.write(aBuffer, 0, n); |
| } |
| aTargetStream.close(); |
| aInputStream.close(); |
| } |
| catch(java.lang.Exception e) |
| { |
| aLogger.logError(e.getMessage()); |
| } |
| break; |
| } |
| } |
| } |
| return false; |
| } |
| } |