1/*
2 *  ConversionOperation.java
3 *  Copyright (C) 2005 Amin Ahmad.
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 *
19 *  Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at
20 *  www.ahmadsoft.org.
21 */
22package org.ahmadsoft.foprocessor.operations;
23
24import java.io.BufferedOutputStream;
25import java.io.File;
26import java.io.FileOutputStream;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
30import java.util.Collection;
31import java.util.logging.Logger;
32
33import javax.xml.transform.Result;
34import javax.xml.transform.Source;
35import javax.xml.transform.Transformer;
36import javax.xml.transform.TransformerFactory;
37import javax.xml.transform.sax.SAXResult;
38import javax.xml.transform.stream.StreamSource;
39
40import org.ahmadsoft.foprocessor.core.FileRenderSpecification;
41import org.ahmadsoft.io.util.InterruptableInputStream;
42import org.apache.fop.apps.FOUserAgent;
43import org.apache.fop.apps.Fop;
44import org.apache.fop.apps.FopFactory;
45import org.eclipse.core.runtime.CoreException;
46import org.eclipse.core.runtime.IPath;
47import org.eclipse.core.runtime.IProgressMonitor;
48import org.eclipse.core.runtime.NullProgressMonitor;
49
50/**
51 * An operation that executes one or more XSL-FO render specifications.
52 * @author Amin Ahmad
53 */
54public class ConversionOperation {
55
56	public interface Listener {
57		void onFinish();
58		void onError(String msg, Throwable throwable);
59	}
60
61	private Collection<FileRenderSpecification> conversions;
62	private IProgressMonitor monitor;
63	private Listener listener;
64
65	private volatile boolean interrupted = false;
66	private InterruptableInputStream iis;
67
68	public ConversionOperation(Collection<FileRenderSpecification> conversions, IProgressMonitor monitor, Listener listener) {
69		super();
70		this.conversions = conversions;
71		this.monitor = monitor;
72		this.listener = listener;
73	}
74
75
76	public void setProgressMonitor(IProgressMonitor monitor) {
77		this.monitor = monitor;
78	}
79
80	public void setInterrupted() {
81		interrupted = true;
82		if (iis != null) {
83			iis.setInterrupted(true);
84		}
85	}
86
87	// TODO log handling.
88	public void run() {
89
90        monitor.beginTask("Initializing", 3);
91
92        monitor.worked(1);
93
94        // Determine information about the files.
95        //
96        long totalByteSize = 0;
97        for (FileRenderSpecification spec: conversions) {
98            File file = spec.getInputFile().getRawLocation().toFile();
99
100            if (file.exists()) {
101                totalByteSize += file.length();
102            }
103        }
104
105        monitor.worked(2);
106        monitor.beginTask("Rendering", totalByteSize > Integer.MAX_VALUE ? Integer.MAX_VALUE: (int) totalByteSize);
107
108        Logger logger = Logger.getLogger("org.apache.fop");
109
110        // Rendition loop
111        //
112        for (FileRenderSpecification spec: conversions) {
113        	if (interrupted)
114        		break;
115
116            IPath inputPath  = spec.getInputFile().getRawLocation();
117            IPath outputPath = spec.getOutputPath();
118
119            FopFactory fopFactory = FopFactory.newInstance();
120            FOUserAgent userAgent = fopFactory.newFOUserAgent();
121            userAgent.setOutputFile(outputPath.toFile());
122            if (inputPath != null)
123            	userAgent.setBaseURL(inputPath.removeLastSegments(1).toFile().toURI().toString());
124
125            //IFile outputFile = resource.getProject().getFile(outputPath);
126
127            OutputStream out = null;
128
129            File file = outputPath.toFile();
130            try {
131                out = new BufferedOutputStream(new FileOutputStream(file));
132                Fop driver = fopFactory.newFop(spec.getMimeType(),userAgent,out);
133
134                // Setup JAXP using identity transformer
135                TransformerFactory factory = TransformerFactory.newInstance();
136                Transformer transformer = factory.newTransformer(); // identity transformer
137
138                iis = new InterruptableInputStream(new MeteredInputStream(spec.getInputFile().getContents(), monitor));
139				Source src = new StreamSource(iis);
140
141                // Resulting SAX events (the generated FO) must be piped through to FOP
142                Result res = new SAXResult(driver.getDefaultHandler());
143
144                // Start XSLT transformation and FOP processing
145                monitor.subTask(outputPath.lastSegment());
146                logger.info("Beginning render for " + outputPath.lastSegment());
147                transformer.transform(src, res);
148            } catch (Exception e) {
149            	listener.onError(null, e);
150                continue;
151            } finally {
152                try {
153                    if (out != null) out.close();
154                    if (iis != null) iis.close();
155                } catch (IOException e) {
156                	listener.onError(null, e);
157                }
158
159                try {
160                    spec.getInputFile().getParent().refreshLocal(1, new NullProgressMonitor());
161                } catch (CoreException e) {
162                	listener.onError(null, e);
163                }
164            }
165        }
166
167        monitor.done();
168        listener.onFinish();
169    }
170}
171
172class MeteredInputStream extends InputStream {
173	private InputStream delegate;
174	private long bytesRead = 0;
175	private IProgressMonitor monitor;
176
177	public MeteredInputStream(InputStream delegate, IProgressMonitor monitor) {
178		super();
179		this.delegate = delegate;
180		this.monitor = monitor;
181	}
182
183	public int available() throws IOException {
184		return delegate.available();
185	}
186
187	public void close() throws IOException {
188		delegate.close();
189	}
190
191	public boolean equals(Object obj) {
192		return delegate.equals(obj);
193	}
194
195	public int hashCode() {
196		return delegate.hashCode();
197	}
198
199	public void mark(int readlimit) {
200		delegate.mark(readlimit);
201	}
202
203	public boolean markSupported() {
204		return delegate.markSupported();
205	}
206
207	public int read() throws IOException {
208		int result = delegate.read();
209		bytesRead += result == -1 ? 0: 1;
210		monitor.worked(result == -1 ? 0: 1);
211		return result;
212	}
213
214	public int read(byte[] b, int off, int len) throws IOException {
215		int result = delegate.read(b, off, len);
216		bytesRead += result;
217		monitor.worked(result);
218		return result;
219	}
220
221	public int read(byte[] b) throws IOException {
222		int result = delegate.read(b);
223		bytesRead += result;
224		monitor.worked(result);
225		return result;
226	}
227
228	public void reset() throws IOException {
229		delegate.reset();
230	}
231
232	public long skip(long n) throws IOException {
233		return delegate.skip(n);
234	}
235
236	public String toString() {
237		return delegate.toString();
238	}
239}
240