1   package org.daisy.pipeline.epub.ace.impl;
2   
3   import java.io.File;
4   import java.net.URI;
5   import java.nio.file.Files;
6   import java.util.Map;
7   import java.util.Optional;
8   
9   import javax.xml.transform.stream.StreamSource;
10  
11  import com.xmlcalabash.core.XProcRuntime;
12  import com.xmlcalabash.io.WritablePipe;
13  import com.xmlcalabash.library.DefaultStep;
14  import com.xmlcalabash.runtime.XAtomicStep;
15  
16  import net.sf.saxon.s9api.QName;
17  import net.sf.saxon.s9api.SaxonApiException;
18  
19  import org.daisy.common.shell.BinaryFinder;
20  import org.daisy.common.shell.CommandRunner;
21  import org.daisy.common.xproc.calabash.XProcStep;
22  import org.daisy.common.xproc.calabash.XProcStepProvider;
23  import org.daisy.common.xproc.XProcMonitor;
24  import static org.daisy.pipeline.file.FileUtils.cResultDocument;
25  
26  import org.osgi.service.component.annotations.Activate;
27  import org.osgi.service.component.annotations.Component;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * <p>Accessibility Checker for EPUB (Ace) step provider</p>
34   *
35   * <p>This class provides a step that validates an EPUB using a system-wide install of the Ace tool
36   * if it is installed.</p>
37   */
38  @Component(
39  	name = "pxi:ace",
40  	service = { XProcStepProvider.class },
41  	property = { "type:String={http://www.daisy.org/ns/pipeline/xproc/internal}ace" }
42  )
43  public class AceProvider implements XProcStepProvider {
44  
45  	public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
46  		return new AceStep(runtime, step);
47  	}
48  
49  	private static File aceProgram = null;
50  
51  	/**
52  	 * Retrieve the Ace executable path
53  	 *
54  	 * @throws RuntimeException if Ace is not found on the system
55  	 */
56  	@Activate
57  	public void init() {
58  		Optional<String> lpath = BinaryFinder.find("ace");
59  		if (lpath.isPresent())
60  			aceProgram = new File(lpath.get());
61  		else
62  			throw new RuntimeException("Ace was not found on your system");
63  	}
64  
65  	public static class AceStep extends DefaultStep implements XProcStep {
66  
67  		private final static Logger mLogger = LoggerFactory.getLogger(AceStep.class);
68  
69  		private static final QName _epubFile = new QName("epub");
70  		private static final QName _tempDir = new QName("temp-dir");
71  		private static final QName _lang = new QName("lang");
72  
73  		private WritablePipe htmlReport = null;
74  		private WritablePipe jsonReport = null;
75  
76  		private AceStep(XProcRuntime runtime, XAtomicStep step) {
77  			super(runtime, step);
78  		}
79  
80  		@Override
81  		public void setOutput(String port, WritablePipe pipe) {
82  			if ("html-report-uri".equals(port)) {
83  				htmlReport = pipe;
84  			} else { // json-report-uri
85  				jsonReport = pipe;
86  			}
87  		}
88  
89  		@Override
90  		public void reset() {
91  			htmlReport.resetWriter();
92  			jsonReport.resetWriter();
93  		}
94  
95  		@Override
96  		public void run() throws SaxonApiException {
97  			super.run();
98  			try {
99  				URI epubURI = new URI(getOption(_epubFile).getString());
100 				File epubFile = new File(epubURI);
101 
102 				// Note that this should normally never happen because init() would have thrown an Exception
103 				if (aceProgram == null) throw new Exception("Ace was not found on your system");
104 
105 				// Output where the Ace reports (report.html and report.json) and unzipped epub will be stored
106 				File tempDir;
107 				if (getOption(_tempDir).getString().equals("")) {
108 					tempDir = Files.createTempDirectory("ace-").toFile();
109 					tempDir.deleteOnExit();
110 				} else
111 					tempDir = new File(new URI(getOption(_tempDir).getString()));
112 
113 				String language = getOption(_lang).getString();
114 
115 				String[] cmd = new String[] {
116 					aceProgram.getAbsolutePath(),
117 					"-o", tempDir.getAbsolutePath(),
118 					"-t", tempDir.getAbsolutePath(),
119 					"-l", language.equals("") ? "en" : language,
120 					epubFile.getCanonicalPath()
121 				};
122 
123 				new CommandRunner(cmd)
124 					.consumeError(mLogger)
125 					.run();
126 
127 				File htmlReportFile = new File(tempDir.getAbsolutePath() + File.separator + "report.html");
128 				File jsonReportFile = new File(tempDir.getAbsolutePath() + File.separator + "report.json");
129 
130 				if (getOption(_tempDir).getString().equals("")) {
131 					htmlReportFile.deleteOnExit();
132 					jsonReportFile.deleteOnExit();
133 				}
134 
135 				// write the result uris in c:result documents
136 				writeCResult(htmlReport, htmlReportFile.toURI());
137 				writeCResult(jsonReport, jsonReportFile.toURI());
138 
139 			} catch (Throwable e) {
140 				throw XProcStep.raiseError(e, step);
141 			}
142 		}
143 
144 		private void writeCResult(WritablePipe port, URI uri) throws SaxonApiException {
145 			port.write(runtime.getProcessor().newDocumentBuilder().build(new StreamSource(cResultDocument(uri.toString()))));
146 		}
147 	}
148 }