1   package org.daisy.pipeline.braille.common.calabash.impl;
2   
3   import java.util.ArrayList;
4   import java.util.Hashtable;
5   import java.util.List;
6   import java.util.Map;
7   import java.util.NoSuchElementException;
8   
9   import javax.xml.namespace.QName;
10  
11  import com.google.common.collect.ImmutableMap;
12  import com.xmlcalabash.core.XProcException;
13  import com.xmlcalabash.core.XProcRuntime;
14  import com.xmlcalabash.io.ReadablePipe;
15  import com.xmlcalabash.io.WritablePipe;
16  import com.xmlcalabash.library.DefaultStep;
17  import com.xmlcalabash.model.RuntimeValue;
18  import com.xmlcalabash.runtime.XAtomicStep;
19  
20  import net.sf.saxon.s9api.SaxonApiException;
21  
22  import org.daisy.common.transform.TransformerException;
23  import org.daisy.common.transform.XMLTransformer;
24  import org.daisy.common.xproc.calabash.XMLCalabashInputValue;
25  import org.daisy.common.xproc.calabash.XMLCalabashOutputValue;
26  import org.daisy.common.xproc.calabash.XMLCalabashParameterInputValue;
27  import org.daisy.common.xproc.calabash.XProcStep;
28  import org.daisy.common.xproc.calabash.XProcStepProvider;
29  import org.daisy.common.xproc.XProcMonitor;
30  import org.daisy.pipeline.braille.common.Transform;
31  import org.daisy.pipeline.braille.common.TransformProvider;
32  import org.daisy.pipeline.braille.common.Query;
33  import static org.daisy.pipeline.braille.common.Query.util.query;
34  import static org.daisy.pipeline.braille.common.TransformProvider.util.dispatch;
35  import static org.daisy.pipeline.braille.common.TransformProvider.util.logSelect;
36  import org.daisy.pipeline.braille.common.XMLTransform;
37  
38  import org.osgi.service.component.annotations.Component;
39  import org.osgi.service.component.annotations.Reference;
40  import org.osgi.service.component.annotations.ReferenceCardinality;
41  import org.osgi.service.component.annotations.ReferencePolicy;
42  
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  public class PxTransformStep extends DefaultStep implements XProcStep {
47  	
48  	private final XProcMonitor monitor;
49  	private final Map<String,String> properties;
50  	private final TransformProvider<Transform> provider;
51  	private ReadablePipe source = null;
52  	private WritablePipe result = null;
53  	private final Hashtable<net.sf.saxon.s9api.QName,RuntimeValue> params = new Hashtable<>();
54  	
55  	private static final net.sf.saxon.s9api.QName _query = new net.sf.saxon.s9api.QName("query");
56  	
57  	private PxTransformStep(XProcRuntime runtime,
58  	                        XAtomicStep step,
59  	                        XProcMonitor monitor,
60  	                        Map<String,String> properties,
61  	                        TransformProvider<Transform> provider) {
62  		super(runtime, step);
63  		this.monitor = monitor;
64  		this.properties = properties;
65  		this.provider = provider;
66  	}
67  
68  	@Override
69  	public void setInput(String port, ReadablePipe pipe) {
70  		source = pipe;
71  	}
72  
73  	@Override
74  	public void setOutput(String port, WritablePipe pipe) {
75  		result = pipe;
76  	}
77  
78  	@Override
79  	public void reset() {
80  		source.resetReader();
81  		result.resetWriter();
82  	}
83  	
84  	@Override
85  	public void setParameter(net.sf.saxon.s9api.QName name, RuntimeValue value) {
86  		params.put(name, value);
87  	}
88  	
89  	@Override
90  	public void setParameter(String port, net.sf.saxon.s9api.QName name, RuntimeValue value) {
91  		setParameter(name, value);
92  	}
93  	
94  	@Override
95  	public void run() throws SaxonApiException {
96  		try {
97  			Query query = query(getOption(_query).getString());
98  			XMLTransformer xmlTransformer = null;
99  			try {
100 				for (Transform t : logSelect(query, provider, logger))
101 					if (t instanceof XProcStepProvider) {
102 						// if the transform is a XProcStepProvider, it is assumed to be declared as a p:pipeline
103 						xmlTransformer = ((XProcStepProvider)t).newStep(runtime, step, monitor, properties);
104 						break; }
105 					else if (t instanceof XMLTransform) {
106 						// fromXmlToXml() is assumed to have only a "source", a "parameters" and a "result" port
107 						// (i.e. the signature of px:transform without the "query" option)
108 						xmlTransformer = ((XMLTransform)t).fromXmlToXml();
109 						break; }}
110 			catch (NoSuchElementException e) {}
111 			if (xmlTransformer == null)
112 				throw new XProcException(step, "Could not find a Transform for query: " + query);
113 			xmlTransformer.transform(
114 				ImmutableMap.of(
115 					new QName("source"), new XMLCalabashInputValue(source),
116 					new QName("parameters"), new XMLCalabashParameterInputValue(params)),
117 				ImmutableMap.of(
118 					new QName("result"), new XMLCalabashOutputValue(result, runtime))
119 			).run();
120 		} catch (Throwable e) {
121 			if (e instanceof TransformerException && e.getCause() instanceof XProcException)
122 				// assuming xmlTransformer is cx:eval based
123 				throw (XProcException)e.getCause();
124 			else
125 				throw XProcStep.raiseError(e, step);
126 		}
127 		super.run();
128 	}
129 	
130 	@Component(
131 		name = "px:transform",
132 		service = { XProcStepProvider.class },
133 		property = { "type:String={http://www.daisy.org/ns/pipeline/xproc}transform" }
134 	)
135 	public static class StepProvider implements XProcStepProvider {
136 		
137 		@Override
138 		public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
139 			return new PxTransformStep(runtime, step, monitor, properties, provider);
140 		}
141 		
142 		@Reference(
143 			name = "XProcTransformProvider",
144 			unbind = "-",
145 			service = TransformProvider.class,
146 			cardinality = ReferenceCardinality.MULTIPLE,
147 			policy = ReferencePolicy.STATIC
148 		)
149 		@SuppressWarnings(
150 			"unchecked" // safe cast to TransformProvider<XProcTransform>
151 		)
152 		public void bindXProcTransformProvider(TransformProvider<?> provider) {
153 			providers.add((TransformProvider<Transform>)provider);
154 			logger.debug("Adding XProcTransform provider: {}", provider);
155 		}
156 		
157 		public void unbindXProcTransformProvider(TransformProvider<?> provider) {
158 			providers.remove(provider);
159 			logger.debug("Removing XProcTransform provider: {}", provider);
160 		}
161 		
162 		private List<TransformProvider<Transform>> providers = new ArrayList<>();
163 		private TransformProvider<Transform> provider = dispatch(providers).withContext(logger);
164 		
165 	}
166 	
167 	private static final Logger logger = LoggerFactory.getLogger(PxTransformStep.class);
168 	
169 }