All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.pipeline.file.calabash.impl.SetXmlDeclarationProvider Maven / Gradle / Ivy

There is a newer version: 4.3.1
Show newest version
package org.daisy.pipeline.file.calabash.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Map;

import com.xmlcalabash.core.XProcConstants;
import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
import com.xmlcalabash.io.WritablePipe;
import com.xmlcalabash.library.DefaultStep;
import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.runtime.XAtomicStep;
import com.xmlcalabash.util.TreeWriter;

import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;

import org.daisy.common.xproc.calabash.XProcStep;
import org.daisy.common.xproc.calabash.XProcStepProvider;
import org.daisy.common.xproc.XProcMonitor;

import org.slf4j.Logger;

import org.osgi.service.component.annotations.Component;

@Component(
	name = "px:set-xml-declaration",
	service = { XProcStepProvider.class },
	property = { "type:String={http://www.daisy.org/ns/pipeline/xproc}set-xml-declaration" }
)
public class SetXmlDeclarationProvider implements XProcStepProvider {

	@Override
	public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map properties) {
		return new SetXmlDeclaration(runtime, step);
	}

	public static class SetXmlDeclaration extends DefaultStep implements XProcStep {
		private static final QName _href = new QName("href");
		private static final QName _xmlDeclaration = new QName("xml-declaration");
		private static final QName _encoding = new QName("encoding");

		private WritablePipe result = null;

		public SetXmlDeclaration(XProcRuntime runtime, XAtomicStep step) {
			super(runtime,step);
		}

		public void setOutput(String port, WritablePipe pipe) {
			result = pipe;
		}

		public void reset() {
			result.resetWriter();
		}

		public void run() throws SaxonApiException {
			super.run();

			String xmlDeclaration = getOption(_xmlDeclaration, "");
			String encoding = getOption(_encoding, "utf-8");

			RuntimeValue href = getOption(_href);
			URI sourceUri = href.getBaseURI().resolve(href.getString());
			File file = new File(sourceUri.getPath());
			
			if (file.isDirectory()) {
				throw new XProcException(step, "Cannot set xml declaration of file: file is a directory: " + file.getAbsolutePath());
			}
			
			boolean ok = false;
			ok = SetXmlDeclaration.setXmlDeclaration(file, encoding, xmlDeclaration, logger);


			TreeWriter tree = new TreeWriter(runtime);
			tree.startDocument(step.getNode().getBaseURI());
			tree.addStartElement(XProcConstants.c_result);
			tree.startContent();
			
			if (ok) {
				logger.debug("SetXmlDeclaration: successfully set the XML declaration");
				tree.addText(file.toURI().toString());
			} else {
				String errorMessage = "px:set-xml-declaration failed to read from "+file+" (xml declaration: "+xmlDeclaration+", filesize: "+(file==null?'?':file.length())+")";
				logger.warn("SetXmlDeclaration: "+errorMessage);
				tree.addAttribute(new QName("error"), errorMessage);
			}

			tree.addEndElement();
			tree.endDocument();

			result.write(tree.getResult());
		}
		
		public static boolean setXmlDeclaration(File file, String encoding, String xmlDeclaration, Logger logger) {
			
			logger.debug("SetXmlDeclaration: file=["+file+"]");
			logger.debug("SetXmlDeclaration: encoding=["+encoding+"]");
			logger.debug("SetXmlDeclaration: xmlDeclaration=["+xmlDeclaration+"]");
			
			Reader reader = null;
			InputStream in = null;
			
			File tempFile = null;
			Writer writer = null;
			
			boolean success = false;
			
			try {
				in = new FileInputStream(file);
				try {
					reader = new InputStreamReader(in, encoding);
				} catch (UnsupportedEncodingException e) {
					reader = new InputStreamReader(in);
				}
				
				tempFile = File.createTempFile("setXmlDeclaration", "");
				writer = new OutputStreamWriter(new FileOutputStream(tempFile), encoding);
				
				success = setXmlDeclarationOnStream(reader, writer, xmlDeclaration, file.toString(), logger);
				try {
					writer.close();
				} catch (IOException e) {
					logger.error("SetXmlDeclaration: unable to close OutputStreamWriter", e);
				}
				
				if (success) {
				    Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
				}
				
			} catch (IOException e) {
				if (logger != null) {
					logger.error("SetXmlDeclaration: px:set-xml-declaration failed to read from "+file, e);
				}
				e.printStackTrace();

			} finally {
				if (reader != null) {
					try {
						reader.close();
					} catch (IOException e) {}
				}
				if (in != null) {
					try {
						in.close();
					} catch (IOException e) {}
				}
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {}
				}
			}
			
			return success;
		}
		
		public static boolean setXmlDeclarationOnStream(Reader reader, Writer writer, String xmlDeclaration, String name, Logger logger) throws IOException {
			int offset = -1;
			int r;
			char currentCharacter = ' ';
			char nextCharacter = ' ';
			
			if ((r = reader.read()) != -1) {
				nextCharacter = (char) r;
				offset++;
			}
			
			while ((r = reader.read()) != -1) {
				offset++;
				
				currentCharacter = nextCharacter;
				nextCharacter = (char) r;
				
				if (offset == 1 && !(currentCharacter == '<' && nextCharacter == '?')) {
					// no existing xml declaration
					logger.debug("SetXmlDeclaration: no existing xml declaration");
					
					if (!"".equals(xmlDeclaration)) {
						writer.write(xmlDeclaration);
						writer.write("\n");
					}
					writer.write(currentCharacter);
					writer.write(nextCharacter);
					break;
				}
				
				if (offset > 1 && (currentCharacter == '?' && nextCharacter == '>')) {
					// found end of existing xml declaration
					logger.debug("SetXmlDeclaration: found end of existing xml declaration");
					
					if ("".equals(xmlDeclaration) && (r = reader.read()) != -1) {
						offset++;
						currentCharacter = nextCharacter;
						nextCharacter = (char) r;
						
						if (nextCharacter != '\n') {
							writer.write(nextCharacter);
						}
						
					} else {
						writer.write(xmlDeclaration);
					}
					
					break;
				}
			}
			
			if (offset < 1) {
				writer.write(xmlDeclaration);
				writer.write("\n");
				if (offset >= 0) {
					writer.write(nextCharacter);
				}
			}
			
			// Write remaining characters from reader to writer
			logger.debug("SetXmlDeclaration: Write remaining characters from reader to writer");
			int bufferSize = 1024;
			char[] buffer = new char[bufferSize];
			int length = 0;
			do {
				length = reader.read(buffer, 0, bufferSize);
				if (length > 0) {
					writer.write(buffer, 0, length);
				}
			} while (length > 0);
			
			return true;
			
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy