/* * ConversionOperation.java * Copyright (C) 2005 Amin Ahmad. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at * www.ahmadsoft.org. */ package org.ahmadsoft.foprocessor.operations; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.logging.Logger; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamSource; import org.ahmadsoft.foprocessor.core.FileRenderSpecification; import org.ahmadsoft.io.util.InterruptableInputStream; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; /** * An operation that executes one or more XSL-FO render specifications. * @author Amin Ahmad */ public class ConversionOperation { public interface Listener { void onFinish(); void onError(String msg, Throwable throwable); } private Collection conversions; private IProgressMonitor monitor; private Listener listener; private volatile boolean interrupted = false; private InterruptableInputStream iis; public ConversionOperation(Collection conversions, IProgressMonitor monitor, Listener listener) { super(); this.conversions = conversions; this.monitor = monitor; this.listener = listener; } public void setProgressMonitor(IProgressMonitor monitor) { this.monitor = monitor; } public void setInterrupted() { interrupted = true; if (iis != null) { iis.setInterrupted(true); } } // TODO log handling. public void run() { monitor.beginTask("Initializing", 3); monitor.worked(1); // Determine information about the files. // long totalByteSize = 0; for (FileRenderSpecification spec: conversions) { File file = spec.getInputFile().getRawLocation().toFile(); if (file.exists()) { totalByteSize += file.length(); } } monitor.worked(2); monitor.beginTask("Rendering", totalByteSize > Integer.MAX_VALUE ? Integer.MAX_VALUE: (int) totalByteSize); Logger logger = Logger.getLogger("org.apache.fop"); // Rendition loop // for (FileRenderSpecification spec: conversions) { if (interrupted) break; IPath inputPath = spec.getInputFile().getRawLocation(); IPath outputPath = spec.getOutputPath(); FopFactory fopFactory = FopFactory.newInstance(); FOUserAgent userAgent = fopFactory.newFOUserAgent(); userAgent.setOutputFile(outputPath.toFile()); if (inputPath != null) userAgent.setBaseURL(inputPath.removeLastSegments(1).toFile().toURI().toString()); //IFile outputFile = resource.getProject().getFile(outputPath); OutputStream out = null; File file = outputPath.toFile(); try { out = new BufferedOutputStream(new FileOutputStream(file)); Fop driver = fopFactory.newFop(spec.getMimeType(),userAgent,out); // Setup JAXP using identity transformer TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); // identity transformer iis = new InterruptableInputStream(new MeteredInputStream(spec.getInputFile().getContents(), monitor)); Source src = new StreamSource(iis); // Resulting SAX events (the generated FO) must be piped through to FOP Result res = new SAXResult(driver.getDefaultHandler()); // Start XSLT transformation and FOP processing monitor.subTask(outputPath.lastSegment()); logger.info("Beginning render for " + outputPath.lastSegment()); transformer.transform(src, res); } catch (Exception e) { listener.onError(null, e); continue; } finally { try { if (out != null) out.close(); if (iis != null) iis.close(); } catch (IOException e) { listener.onError(null, e); } try { spec.getInputFile().getParent().refreshLocal(1, new NullProgressMonitor()); } catch (CoreException e) { listener.onError(null, e); } } } monitor.done(); listener.onFinish(); } } class MeteredInputStream extends InputStream { private InputStream delegate; private long bytesRead = 0; private IProgressMonitor monitor; public MeteredInputStream(InputStream delegate, IProgressMonitor monitor) { super(); this.delegate = delegate; this.monitor = monitor; } public int available() throws IOException { return delegate.available(); } public void close() throws IOException { delegate.close(); } public boolean equals(Object obj) { return delegate.equals(obj); } public int hashCode() { return delegate.hashCode(); } public void mark(int readlimit) { delegate.mark(readlimit); } public boolean markSupported() { return delegate.markSupported(); } public int read() throws IOException { int result = delegate.read(); bytesRead += result == -1 ? 0: 1; monitor.worked(result == -1 ? 0: 1); return result; } public int read(byte[] b, int off, int len) throws IOException { int result = delegate.read(b, off, len); bytesRead += result; monitor.worked(result); return result; } public int read(byte[] b) throws IOException { int result = delegate.read(b); bytesRead += result; monitor.worked(result); return result; } public void reset() throws IOException { delegate.reset(); } public long skip(long n) throws IOException { return delegate.skip(n); } public String toString() { return delegate.toString(); } }