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

net.sf.okapi.steps.xsltransform.XSLTransformStep Maven / Gradle / Ivy

/*===========================================================================
  Copyright (C) 2009-2012 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
===========================================================================*/

package net.sf.okapi.steps.xsltransform;

import java.io.File;
import java.net.URI;
import java.util.Map;
import java.util.Properties;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource;

import net.sf.okapi.common.ConfigurationString;
import net.sf.okapi.common.Event;
import net.sf.okapi.common.IParameters;
import net.sf.okapi.common.UsingParameters;
import net.sf.okapi.common.Util;
import net.sf.okapi.common.exceptions.OkapiIOException;
import net.sf.okapi.common.pipeline.BasePipelineStep;
import net.sf.okapi.common.pipeline.annotations.StepParameterMapping;
import net.sf.okapi.common.pipeline.annotations.StepParameterType;
import net.sf.okapi.common.resource.RawDocument;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

@UsingParameters(Parameters.class)
public class XSLTransformStep extends BasePipelineStep {

	private static final String FACTORY_PROP = "javax.xml.transform.TransformerFactory";
	private static final String XPATH_PROP =  "javax.xml.xpath.XPathFactory";
	private static final String VAR_SRCLANG = "${srcLang}"; 
	private static final String VAR_TRGLANG = "${trgLang}"; 
	private static final String VAR_INPUTPATH = "${inputPath}"; 
	private static final String VAR_INPUTURI = "${inputURI}"; 
	private static final String VAR_OUTPUTPATH = "${outputPath}"; 
	private static final String VAR_INPUTPATH1 = "${inputPath1}"; 
	private static final String VAR_INPUTURI1 = "${inputURI1}"; 
	private static final String VAR_OUTPUTPATH1 = "${outputPath1}"; 
	private static final String VAR_INPUTPATH2 = "${inputPath2}"; 
	private static final String VAR_INPUTURI2 = "${inputURI2}"; 
	private static final String VAR_INPUTPATH3 = "${inputPath3}"; 
	private static final String VAR_INPUTURI3 = "${inputURI3}"; 

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private Parameters params;
	private Source xsltInput;
	private Map paramList;
	private Transformer trans;
	private javax.xml.transform.TransformerFactory fact;
	private boolean isDone;
	private URI outputURI;
	private RawDocument input1;
	private RawDocument input2;
	private RawDocument input3;
	private String originalProcessor;
	private String originalXpathProcessor;
	
	private ErrorListener errorListener;
	private String systemId;
	private EntityResolver entityResolver;
	private URIResolver uriResolver;
	
	
	public XSLTransformStep () {
		params = new Parameters();
		trans = null;
		originalProcessor = System.getProperty(FACTORY_PROP);
		originalXpathProcessor = System.getProperty(XPATH_PROP);		
	}
	
	@Override
	public void destroy () {
		// Make available to GC
		trans = null;
		xsltInput = null;
	}

	@StepParameterMapping(parameterType = StepParameterType.OUTPUT_URI)
	public void setOutputURI (URI outputURI) {
		this.outputURI = outputURI;
	}
	
	@StepParameterMapping(parameterType = StepParameterType.INPUT_RAWDOC)
	public void setInput (RawDocument input) {
		input1 = input;
	}
	
	@StepParameterMapping(parameterType = StepParameterType.SECOND_INPUT_RAWDOC)
	public void setSecondInput (RawDocument secondInput) {
		input2 = secondInput;
	}
	
	@StepParameterMapping(parameterType = StepParameterType.THIRD_INPUT_RAWDOC)
	public void setThirdInput (RawDocument thridInput) {
		input3 = thridInput;
	}
	
	public String getDescription () {
		return "Apply an XSLT template to an XML document."
			+ " Expects: raw XML document. Sends back: raw document.";
	}

	public String getName () {
		return "XSL Transformation";
	}

	@Override
	public Parameters getParameters () {
		return params;
	}
	
	@Override
	public boolean isDone () {
		return isDone;
	}

	@Override
	public void setParameters (IParameters params) {
		this.params = (Parameters)params;
	}
 
	@Override
	protected Event handleStartBatch (Event event) {
		try {
			// Create the parameters map
			ConfigurationString cfgString = new ConfigurationString(params.getParamList());
			paramList = cfgString.toMap();
			
			// Create the source for the XSLT
			xsltInput = new javax.xml.transform.stream.StreamSource(
				new File(params.getXsltPath()));
			
			// Create an instance of TransformerFactory
			if ( params.getUseCustomTransformer() ) {
				System.setProperty(FACTORY_PROP, params.getFactoryClass());
				if (!Util.isEmpty(params.getXpathClass()))
					System.setProperty(XPATH_PROP, params.getXpathClass());
			}
			fact = javax.xml.transform.TransformerFactory.newInstance();
			if (errorListener != null) {
				fact.setErrorListener(errorListener);
			}

			trans = fact.newTransformer(xsltInput);
			logger.info("Factory used: {}", fact.getClass().getCanonicalName());
			logger.info("Transformer used: {}", trans.getClass().getCanonicalName());
			isDone = true;
		}
		catch ( Throwable e ) {
			throw new OkapiIOException("Error in XSLT input.\n" + e.getMessage(), e);
		}
//		finally {
			// Make sure to reset the original property
//			if ( params.useCustomTransformer ) {
//				System.setProperty(FACTORY_PROP, originalProcessor);
//				System.setProperty(XPATH_PROP, originalXpathProcessor);
//			}
//  	}
		
		return event;
	}
	
	@Override
	protected Event handleStartBatchItem (Event event) {
		isDone = false;
		return event;
	}

	@Override
	protected Event handleRawDocument (Event event) {
		try {
			RawDocument rawDoc = (RawDocument)event.getResource();
			File outFile = null;
			trans.reset();
			fillParameters();
			
			Properties props = trans.getOutputProperties();
			for ( Object obj: props.keySet() ) {
				String key = (String)obj;
				String value = props.getProperty(key);
				value = value+"";
			}
			
			// Create the input source
			// Use the stream, so the encoding auto-detection can be done
			XMLReader reader = XMLReaderFactory.createXMLReader();
			SAXSource xmlInput = new SAXSource(reader, new InputSource(rawDoc.getStream()));
			
			if (systemId != null) {
				xmlInput.setSystemId(systemId);
			}
			
			if (entityResolver != null) {
				reader.setEntityResolver(entityResolver);
			}
			
			// FIXME: do we need to set this twice? see handleStartBatch
			if (errorListener != null) {
				fact.setErrorListener(errorListener);
			}
			
			if (uriResolver != null) {
				trans.setURIResolver(uriResolver);
			}
		
			// Create the output
			outFile = new File(outputURI);
			
			// In some cases we want outputURI to be a directory
			// in this case use the input file name to complete the path
			if (outFile.isDirectory()) {
				outFile = new File(outFile, Util.getFilename(rawDoc.getInputURI().getPath(), true));
			}
			
			Result result = new javax.xml.transform.stream.StreamResult(outFile);
			
			// Apply the template
			trans.transform(xmlInput, result);
	
			if (params.getPassOnOutput()) {
				// Create the new raw-document resource
				event.setResource(new RawDocument(outFile.toURI(), "UTF-8", 
					rawDoc.getSourceLocale(), rawDoc.getTargetLocale()));
			} else {
				outFile.delete(); // tmp output file
				// the xslt controls the output - don't pass on to subsequent steps
				return Event.createNoopEvent();
			}
		}
		catch ( TransformerException e ) {
			throw new OkapiIOException("Transformation error.\n" + e.getMessage(), e);
		} catch (SAXException e) {
			throw new OkapiIOException("Parser error.\n" + e.getMessage(), e);		}
		finally {	
			isDone = true;			
		}
		
		return event;
	}

	@Override
	protected Event handleEndBatch(Event event) {
		if ( params.getUseCustomTransformer() ) {
			if (originalProcessor == null) {
				System.clearProperty(FACTORY_PROP);
			} else {
				System.setProperty(FACTORY_PROP, originalProcessor);
			}
			
			if (originalXpathProcessor == null) {
				System.clearProperty(XPATH_PROP);
			} else {
				System.setProperty(XPATH_PROP, originalXpathProcessor);
			}
		}
		return event;
	}
	
	public void setErrorListener(ErrorListener errorListener) {
		this.errorListener = errorListener;
	}

	public void setSystemId(String systemId) {
		this.systemId = systemId;
	}

	public void setEntityResolver(EntityResolver entityResolver) {
		this.entityResolver = entityResolver;
	}

	public void setUriResolver(URIResolver uriResolver) {
		this.uriResolver = uriResolver;
	}

	private void fillParameters () {
		trans.clearParameters();
		String value = null;
		try {
			for ( String key : paramList.keySet() ) {
				// Try to find the replacement(s)
				value = paramList.get(key).replace(VAR_SRCLANG, input1.getSourceLocale().getLanguage());
				if (value.contains(VAR_TRGLANG)) {
					value = value.replace(VAR_TRGLANG, input1.getTargetLocale().getLanguage());
				}

				if (value.contains(VAR_INPUTPATH)) {
					value = value.replace(VAR_INPUTPATH, input1.getInputURI().getPath());
				}
				if (value.contains(VAR_INPUTURI)) {
					value = value.replace(VAR_INPUTURI, input1.getInputURI().toString());
				}
				if (value.contains(VAR_OUTPUTPATH)) {
					value = value.replace(VAR_OUTPUTPATH, outputURI.getPath());
				}
				
				if (value.contains(VAR_INPUTPATH1)) { // Same as VAR_INPUTPATH
					value = value.replace(VAR_INPUTPATH1, input1.getInputURI().getPath());
				}
				if (value.contains(VAR_INPUTURI1)) {
					value = value.replace(VAR_INPUTURI1, input1.getInputURI().toString());
				}
				if (value.contains(VAR_OUTPUTPATH1)) { // Same as VAR_OUTPUTPATH
					value = value.replace(VAR_OUTPUTPATH1, outputURI.getPath());
				}
				
				if (value.contains(VAR_INPUTPATH2)) {
					if ( input2 == null ) {
						value = value.replace(VAR_INPUTPATH2, "null");
					}
					else {
						value = value.replace(VAR_INPUTPATH2, input2.getInputURI().getPath());
					}
				}
				if (value.contains(VAR_INPUTURI2)) {
					if ( input2 == null ) {
						value = value.replace(VAR_INPUTURI2, "null");
					}
					else {
						value = value.replace(VAR_INPUTURI2, input2.getInputURI().toString());
					}
				}
				
				if (value.contains(VAR_INPUTPATH3)) {
					if ( input3 == null ) {
						value = value.replace(VAR_INPUTPATH3, "null");
					}
					else {
						value = value.replace(VAR_INPUTPATH3, input3.getInputURI().getPath());
					}
				}
				if (value.contains(VAR_INPUTURI3)) {
					if ( input3 == null ) {
						value = value.replace(VAR_INPUTURI3, "null");
					}
					else {
						value = value.replace(VAR_INPUTURI3, input3.getInputURI().toString());
					}
				}
				
				// Assign the variable
				trans.setParameter(key, value);
			}
		}
		catch ( Throwable e ) {
			logger.error("Error when trying to substitute variables in the parameter value '{}'", value);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy