1   package org.daisy.pipeline.file.calabash.impl;
2   
3   import java.io.IOException;
4   import java.net.URI;
5   import java.util.ArrayList;
6   import java.util.List;
7   import java.util.Map;
8   
9   import com.xmlcalabash.core.XProcConstants;
10  import com.xmlcalabash.core.XProcRuntime;
11  import com.xmlcalabash.io.WritablePipe;
12  import com.xmlcalabash.library.DefaultStep;
13  import com.xmlcalabash.model.RuntimeValue;
14  import com.xmlcalabash.runtime.XAtomicStep;
15  
16  import net.sf.saxon.s9api.QName;
17  import net.sf.saxon.s9api.SaxonApiException;
18  
19  import static org.daisy.common.saxon.SaxonHelper.jaxpQName;
20  import org.daisy.common.transform.TransformerException;
21  import org.daisy.common.xml.DocumentBuilder;
22  import org.daisy.common.xproc.calabash.XMLCalabashOutputValue;
23  import org.daisy.common.xproc.calabash.XProcStep;
24  import org.daisy.common.xproc.calabash.XProcStepProvider;
25  import org.daisy.common.xproc.XProcMonitor;
26  
27  import org.osgi.service.component.annotations.Component;
28  import org.osgi.service.component.annotations.Reference;
29  import org.osgi.service.component.annotations.ReferenceCardinality;
30  import org.osgi.service.component.annotations.ReferencePolicy;
31  
32  import org.xml.sax.InputSource;
33  import org.xml.sax.SAXException;
34  
35  import org.w3c.dom.Document;
36  
37  @Component(
38  	name = "px:load",
39  	service = { XProcStepProvider.class },
40  	property = { "type:String={http://www.daisy.org/ns/pipeline/xproc}load" }
41  )
42  public class LoadProvider implements XProcStepProvider {
43  
44  	@Override
45  	public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
46  		return new Load(runtime, step);
47  	}
48  
49  	private static final QName _href = new QName("href");
50  	private static final QName _content_type = new QName("content-type");
51  	private static final QName err_XD0011 = new QName("err", XProcConstants.NS_XPROC_ERROR, "XD0011");
52  
53  	public class Load extends DefaultStep implements XProcStep {
54  
55  		private WritablePipe result = null;
56  
57  		public Load(XProcRuntime runtime, XAtomicStep step) {
58  			super(runtime,step);
59  		}
60  
61  		public void setOutput(String port, WritablePipe pipe) {
62  			result = pipe;
63  		}
64  
65  		public void reset() {
66  			result.resetWriter();
67  		}
68  
69  		public void run() throws SaxonApiException {
70  			super.run();
71  			try {
72  				RuntimeValue href = getOption(_href);
73  				URI absoluteHref = href.getBaseURI().resolve(href.getString());
74  				InputSource source = new InputSource(absoluteHref.toString());
75  				String contentType = getOption(_content_type, "");
76  				boolean isXml = "".equals(contentType) || contentType.matches("[^ ]*(/|\\+)xml");
77  				Document doc = null;
78  				DocumentBuilder xmlParser = null;
79  				try {
80  					for (DocumentBuilder p : parsers) {
81  						if (xmlParser == null && p.supportsContentType("text/xml")) {
82  							xmlParser = p;
83  							if (isXml) break;
84  						}
85  						if (!isXml && p.supportsContentType(contentType)) {
86  							doc = p.parse(source);
87  							break;
88  						}
89  					}
90  					if (doc == null && xmlParser != null) {
91  						// content-type is either XML, not specified, or not recognized
92  						// in all cases we use (fallback to) the XML parser
93  						doc = xmlParser.parse(source);
94  					}
95  				} catch (SAXException|IOException e) {
96  					throw new TransformerException(
97  						jaxpQName(err_XD0011),
98  						new IllegalArgumentException("Error parsing " + absoluteHref + " (content-type: " + contentType + ")", e));
99  				}
100 				if (doc == null) {
101 					// this means no XML parser was found
102 					if (xmlParser != null) throw new RuntimeException(); // coding error
103 					throw new TransformerException(new IllegalStateException("No XML parser found"));
104 				}
105 				new XMLCalabashOutputValue(result, runtime).asNodeConsumer().accept(doc);
106 			} catch (Throwable e) {
107 				throw XProcStep.raiseError(e, step);
108 			}
109 		}
110 	}
111 
112 	private final List<DocumentBuilder> parsers = new ArrayList<>();
113 
114 	@Reference(
115 		name = "input-parser",
116 		unbind = "-",
117 		service = DocumentBuilder.class,
118 		cardinality = ReferenceCardinality.MULTIPLE,
119 		policy = ReferencePolicy.STATIC
120 	)
121 	protected void addInputParser(DocumentBuilder parser) {
122 		parsers.add(parser);
123 	}
124 }