1   package org.daisy.pipeline.file.calabash.impl;
2   
3   import java.io.File;
4   import java.io.FileNotFoundException;
5   import java.io.FileOutputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.net.URI;
9   import java.util.Map;
10  
11  import net.sf.saxon.s9api.QName;
12  import net.sf.saxon.s9api.SaxonApiException;
13  
14  import org.daisy.common.file.URLs;
15  import org.daisy.common.xproc.calabash.XProcStep;
16  import org.daisy.common.xproc.calabash.XProcStepProvider;
17  import org.daisy.common.xproc.XProcMonitor;
18  
19  import org.slf4j.Logger;
20  import org.slf4j.LoggerFactory;
21  
22  import com.google.common.io.ByteStreams;
23  
24  import com.xmlcalabash.core.XProcConstants;
25  import com.xmlcalabash.core.XProcException;
26  import com.xmlcalabash.core.XProcRuntime;
27  import com.xmlcalabash.io.WritablePipe;
28  import com.xmlcalabash.library.DefaultStep;
29  import com.xmlcalabash.model.RuntimeValue;
30  import com.xmlcalabash.runtime.XAtomicStep;
31  import com.xmlcalabash.util.TreeWriter;
32  
33  import org.osgi.service.component.annotations.Component;
34  
35  /**
36   * This steps allows to copy any resource from a URI to a file in the file system usign a file scheme URI 
37   */
38  @Component(
39  	name = "pxi:copy-resource",
40  	service = { XProcStepProvider.class },
41  	property = { "type:String={http://www.daisy.org/ns/pipeline/xproc/internal}copy-resource" }
42  )
43  public class CopyResourceProvider implements XProcStepProvider {
44  
45  	/** The logger. */
46  	Logger logger = LoggerFactory.getLogger(this.getClass());
47  
48  	/* (non-Javadoc)
49  	 * @see org.daisy.common.xproc.calabash.XProcStepProvider#newStep(com.xmlcalabash.core.XProcRuntime, com.xmlcalabash.runtime.XAtomicStep)
50  	 */
51  	@Override
52  	public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
53  		return new CopyResource(runtime, step);
54  	}
55  
56  	/**
57  	 * Activate (OSGI)
58  	 */
59  	public void activate() {
60  		logger.trace("Activating px:copy-resource provider");
61  	}
62  	
63  	/**
64  	 * Actual implementation of the CopyResource step
65  	 *
66  	 *
67  	 */
68  	public static class CopyResource extends DefaultStep implements XProcStep {
69  		private static final QName _href = new QName("href");
70  		private static final QName _target = new QName("target");
71  		private static final QName _fail_on_error = new QName("fail-on-error");
72  		private static final int bufsize = 8192;
73  
74  		private WritablePipe result = null;
75  
76  		/**
77  		 * Creates a new instance of CopyResource 
78  		 */
79  		public CopyResource(XProcRuntime runtime, XAtomicStep step) {
80  			super(runtime,step);
81  		}
82  
83  		public void setOutput(String port, WritablePipe pipe) {
84  			result = pipe;
85  		}
86  
87  		public void reset() {
88  			result.resetWriter();
89  		}
90  
91  		public void run() throws SaxonApiException {
92  			super.run();
93  
94  			if (runtime.getSafeMode()) {
95  				throw XProcException.dynamicError(21);
96  			}
97  
98  			boolean failOnError = getOption(_fail_on_error, true);
99  
100 			// Note that href.getBaseURI() always returns the absolute path of copy-resource.xpl
101 			RuntimeValue href = getOption(_href);
102 			URI sourceUri= URLs.resolve(href.getBaseURI(), URLs.asURI(href.getString()));
103 
104 			href = getOption(_target);
105 			URI destUri = URLs.resolve(href.getBaseURI(), URLs.asURI(href.getString()));
106 
107 			File target=this.getFile(destUri,sourceUri);
108 
109 			TreeWriter tree = new TreeWriter(runtime);
110 			tree.startDocument(step.getNode().getBaseURI());
111 			tree.addStartElement(XProcConstants.c_result);
112 			tree.startContent();
113 
114 			tree.addText(target.toURI().toASCIIString());
115 
116 			this.copy(sourceUri,target);
117 
118 			tree.addEndElement();
119 			tree.endDocument();
120 
121 			result.write(tree.getResult());
122 		}
123 
124 		File getFile(URI destUri,URI sourceUri){
125 			File target=null;
126 			if (!"file".equals(destUri.getScheme())) {
127 				throw new XProcException(step, "Only file: scheme URIs are supported by taget.");
128 			} else {
129 				target = new File(destUri.getPath());
130 			}
131 
132 			if (target.isDirectory()) {
133 				int idx=sourceUri.getPath().lastIndexOf('/');
134 				if(idx>-1){
135 					target = new File(target, sourceUri.getPath().substring(idx));
136 				}else{
137 					target = new File(target, sourceUri.getPath());
138 				}
139 				if (target.isDirectory()) {
140 					throw new XProcException(step, "Cannot copy: target is a directory: " + target.getAbsolutePath());
141 				}
142 			}
143 			return target;
144 
145 		}
146 
147 		void copy(URI sourceUri,File target){
148 
149 			try {
150 				InputStream src = sourceUri.toURL().openStream();
151 				FileOutputStream dst = new FileOutputStream(target);
152 				ByteStreams.copy(src,dst);
153 				src.close();
154 				dst.close();
155 			} catch (FileNotFoundException fnfe) {
156 				throw new XProcException(step, fnfe);
157 			} catch (IOException ioe) {
158 				throw new XProcException(step, ioe);
159 			}
160 		}
161 	}
162 
163 }
164