/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * 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 org.pentaho.di.trans.steps.addxml; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.RowDataUtil; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.xml.XMLParserFactoryProducer; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.BaseStep; import org.pentaho.di.trans.step.StepDataInterface; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.StepMetaInterface; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Converts input rows to one or more XML files. * * @author Matt * @since 14-jan-2006 */ public class AddXML extends BaseStep implements StepInterface { private static Class<?> PKG = AddXML.class; // for i18n purposes, needed by Translator2!! private AddXMLMeta meta; private AddXMLData data; private DOMImplementation domImplentation; private Transformer serializer; public AddXML( StepMeta stepMeta, StepDataInterface sdi, int copyNr, TransMeta tm, Trans trans ) { super( stepMeta, sdi, copyNr, tm, trans ); } public boolean processRow( StepMetaInterface smi, StepDataInterface sdi ) throws KettleException { meta = (AddXMLMeta) smi; data = (AddXMLData) sdi; Object[] r = getRow(); // This also waits for a row to be finished. if ( r == null ) { // no more input to be expected... setOutputDone(); return false; } if ( first ) { first = false; data.outputRowMeta = getInputRowMeta().clone(); meta.getFields( data.outputRowMeta, getStepname(), null, null, this, repository, metaStore ); // Cache the field name indexes // data.fieldIndexes = new int[meta.getOutputFields().length]; for ( int i = 0; i < data.fieldIndexes.length; i++ ) { String fieldsName = meta.getOutputFields()[i].getFieldName(); data.fieldIndexes[i] = getInputRowMeta().indexOfValue( fieldsName ); if ( data.fieldIndexes[i] < 0 ) { throw new KettleException( BaseMessages.getString( PKG, "AddXML.Exception.FieldNotFound", fieldsName ) ); } } } Document xmldoc = getDomImplentation().createDocument( null, meta.getRootNode(), null ); Element root = xmldoc.getDocumentElement(); for ( int i = 0; i < meta.getOutputFields().length; i++ ) { XMLField outputField = meta.getOutputFields()[i]; String fieldname = outputField.getFieldName(); ValueMetaInterface v = getInputRowMeta().getValueMeta( data.fieldIndexes[i] ); Object valueData = r[data.fieldIndexes[i]]; if ( !meta.isOmitNullValues() || !v.isNull( valueData ) ) { String value = formatField( v, valueData, outputField ); String element = outputField.getElementName(); if ( element == null || element.length() == 0 ) { element = fieldname; } if ( element == null || element.length() == 0 ) { throw new KettleException( "XML does not allow empty strings for element names." ); } if ( outputField.isAttribute() ) { String attributeParentName = outputField.getAttributeParentName(); Element node; if ( attributeParentName == null || attributeParentName.length() == 0 ) { node = root; } else { NodeList nodelist = root.getElementsByTagName( attributeParentName ); if ( nodelist.getLength() > 0 ) { node = (Element) nodelist.item( 0 ); } else { node = root; } } node.setAttribute( element, value ); } else { /* encode as subnode */ if ( !element.equals( meta.getRootNode() ) ) { Element e = xmldoc.createElement( element ); Node n = xmldoc.createTextNode( value ); e.appendChild( n ); root.appendChild( e ); } else { Node n = xmldoc.createTextNode( value ); root.appendChild( n ); } } } } StringWriter sw = new StringWriter(); DOMSource domSource = new DOMSource( xmldoc ); try { this.getSerializer().transform( domSource, new StreamResult( sw ) ); } catch ( TransformerException e ) { throw new KettleException( e ); } catch ( Exception e ) { throw new KettleException( e ); } Object[] outputRowData = RowDataUtil.addValueData( r, getInputRowMeta().size(), sw.toString() ); putRow( data.outputRowMeta, outputRowData ); return true; } private String formatField( ValueMetaInterface valueMeta, Object valueData, XMLField field ) throws KettleValueException { String retval = ""; if ( field == null ) { return ""; } if ( valueMeta == null || valueMeta.isNull( valueData ) ) { String defaultNullValue = field.getNullString(); return Utils.isEmpty( defaultNullValue ) ? "" : defaultNullValue; } if ( valueMeta.isNumeric() ) { // Formatting if ( !Utils.isEmpty( field.getFormat() ) ) { data.df.applyPattern( field.getFormat() ); } else { data.df.applyPattern( data.defaultDecimalFormat.toPattern() ); } // Decimal if ( !Utils.isEmpty( field.getDecimalSymbol() ) ) { data.dfs.setDecimalSeparator( field.getDecimalSymbol().charAt( 0 ) ); } else { data.dfs.setDecimalSeparator( data.defaultDecimalFormatSymbols.getDecimalSeparator() ); } // Grouping if ( !Utils.isEmpty( field.getGroupingSymbol() ) ) { data.dfs.setGroupingSeparator( field.getGroupingSymbol().charAt( 0 ) ); } else { data.dfs.setGroupingSeparator( data.defaultDecimalFormatSymbols.getGroupingSeparator() ); } // Currency symbol if ( !Utils.isEmpty( field.getCurrencySymbol() ) ) { data.dfs.setCurrencySymbol( field.getCurrencySymbol() ); } else { data.dfs.setCurrencySymbol( data.defaultDecimalFormatSymbols.getCurrencySymbol() ); } data.df.setDecimalFormatSymbols( data.dfs ); if ( valueMeta.isBigNumber() ) { retval = data.df.format( valueMeta.getBigNumber( valueData ) ); } else if ( valueMeta.isNumber() ) { retval = data.df.format( valueMeta.getNumber( valueData ) ); } else { // Integer retval = data.df.format( valueMeta.getInteger( valueData ) ); } } else if ( valueMeta.isDate() ) { if ( field != null && !Utils.isEmpty( field.getFormat() ) && valueMeta.getDate( valueData ) != null ) { if ( !Utils.isEmpty( field.getFormat() ) ) { data.daf.applyPattern( field.getFormat() ); } else { data.daf.applyPattern( data.defaultDateFormat.toLocalizedPattern() ); } data.daf.setDateFormatSymbols( data.dafs ); retval = data.daf.format( valueMeta.getDate( valueData ) ); } else { if ( valueMeta.isNull( valueData ) ) { if ( field != null && !Utils.isEmpty( field.getNullString() ) ) { retval = field.getNullString(); } } else { retval = valueMeta.getString( valueData ); } } } else if ( valueMeta.isString() ) { retval = valueMeta.getString( valueData ); } else if ( valueMeta.isBinary() ) { if ( valueMeta.isNull( valueData ) ) { if ( !Utils.isEmpty( field.getNullString() ) ) { retval = field.getNullString(); } else { retval = Const.NULL_BINARY; } } else { try { retval = new String( valueMeta.getBinary( valueData ), "UTF-8" ); } catch ( UnsupportedEncodingException e ) { // chances are small we'll get here. UTF-8 is // mandatory. retval = Const.NULL_BINARY; } } } else { // Boolean retval = valueMeta.getString( valueData ); } return retval; } public boolean init( StepMetaInterface smi, StepDataInterface sdi ) { meta = (AddXMLMeta) smi; data = (AddXMLData) sdi; if ( !super.init( smi, sdi ) ) { return false; } try { setSerializer( TransformerFactory.newInstance().newTransformer() ); setDomImplentation( XMLParserFactoryProducer.createSecureDocBuilderFactory().newDocumentBuilder().getDOMImplementation() ); if ( meta.getEncoding() != null ) { getSerializer().setOutputProperty( OutputKeys.ENCODING, meta.getEncoding() ); } if ( meta.isOmitXMLheader() ) { getSerializer().setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); } } catch ( TransformerConfigurationException e ) { return false; } catch ( ParserConfigurationException e ) { return false; } return true; } public void dispose( StepMetaInterface smi, StepDataInterface sdi ) { meta = (AddXMLMeta) smi; data = (AddXMLData) sdi; super.dispose( smi, sdi ); } private void setDomImplentation( DOMImplementation domImplentation ) { this.domImplentation = domImplentation; } private DOMImplementation getDomImplentation() { return domImplentation; } private void setSerializer( Transformer serializer ) { this.serializer = serializer; } private Transformer getSerializer() { return serializer; } }