/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 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.textfileoutput;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.vfs2.FileObject;
import org.pentaho.di.core.CheckResult;
import org.pentaho.di.core.CheckResultInterface;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleStepException;
import org.pentaho.di.core.exception.KettleXMLException;
import org.pentaho.di.core.injection.Injection;
import org.pentaho.di.core.injection.InjectionDeep;
import org.pentaho.di.core.injection.InjectionSupported;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.resource.ResourceDefinition;
import org.pentaho.di.resource.ResourceNamingInterface;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepInjectionMetaEntry;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.metastore.api.IMetaStore;
import org.w3c.dom.Node;
/*
* Created on 4-apr-2003
*
*/
@InjectionSupported( localizationPrefix = "TextFileOutput.Injection.", groups = { "OUTPUT_FIELDS" } )
public class TextFileOutputMeta extends BaseStepMeta implements StepMetaInterface {
private static Class<?> PKG = TextFileOutputMeta.class; // for i18n purposes, needed by Translator2!!
protected static final int FILE_COMPRESSION_TYPE_NONE = 0;
protected static final int FILE_COMPRESSION_TYPE_ZIP = 1;
protected static final String[] fileCompressionTypeCodes = new String[] { "None", "Zip" };
public static final String[] formatMapperLineTerminator = new String[] { "DOS", "UNIX", "CR", "None" };
/** The base name of the output file */
@Injection( name = "FILENAME" )
private String fileName;
/** Whether to treat this as a command to be executed and piped into */
@Injection( name = "RUN_AS_COMMAND" )
private boolean fileAsCommand;
/** Whether to push the output into the output of a servlet with the executeTrans Carte/DI-Server servlet */
@Injection( name = "PASS_TO_SERVLET" )
private boolean servletOutput;
/** Flag: create parent folder, default to true */
@Injection( name = "CREATE_PARENT_FOLDER" )
private boolean createparentfolder = true;
/** The file extention in case of a generated filename */
@Injection( name = "EXTENSION" )
private String extension;
/** The separator to choose for the CSV file */
@Injection( name = "SEPARATOR" )
private String separator;
/** The enclosure to use in case the separator is part of a field's value */
@Injection( name = "ENCLOSURE" )
private String enclosure;
/** Setting to allow the enclosure to be always surrounding a String value, even when there is no separator inside */
@Injection( name = "FORCE_ENCLOSURE" )
private boolean enclosureForced;
/**
* Setting to allow for backwards compatibility where the enclosure did not show up at all if Force Enclosure was not
* checked
*/
@Injection( name = "DISABLE_ENCLOSURE_FIX" )
private boolean disableEnclosureFix;
/** Add a header at the top of the file? */
@Injection( name = "HEADER" )
private boolean headerEnabled;
/** Add a footer at the bottom of the file? */
@Injection( name = "FOOTER" )
private boolean footerEnabled;
/** The file format: DOS or Unix */
@Injection( name = "FORMAT" )
private String fileFormat;
/** The file compression: None, Zip or Gzip */
@Injection( name = "COMPRESSION" )
private String fileCompression;
/** if this value is larger then 0, the text file is split up into parts of this number of lines */
@Injection( name = "SPLIT_EVERY" )
private int splitEvery;
/** Flag to indicate the we want to append to the end of an existing file (if it exists) */
@Injection( name = "APPEND" )
private boolean fileAppended;
/** Flag: add the stepnr in the filename */
@Injection( name = "INC_STEPNR_IN_FILENAME" )
private boolean stepNrInFilename;
/** Flag: add the partition number in the filename */
@Injection( name = "INC_PARTNR_IN_FILENAME" )
private boolean partNrInFilename;
/** Flag: add the date in the filename */
@Injection( name = "INC_DATE_IN_FILENAME" )
private boolean dateInFilename;
/** Flag: add the time in the filename */
@Injection( name = "INC_TIME_IN_FILENAME" )
private boolean timeInFilename;
/** Flag: pad fields to their specified length */
@Injection( name = "RIGHT_PAD_FIELDS" )
private boolean padded;
/** Flag: Fast dump data without field formatting */
@Injection( name = "FAST_DATA_DUMP" )
private boolean fastDump;
/* THE FIELD SPECIFICATIONS ... */
/** The output fields */
@InjectionDeep
private TextFileField[] outputFields;
/** The encoding to use for reading: null or empty string means system default encoding */
@Injection( name = "ENCODING" )
private String encoding;
/** The string to use for append to end line of the whole file: null or empty string means no line needed */
@Injection( name = "ADD_ENDING_LINE" )
private String endedLine;
/* Specification if file name is in field */
@Injection( name = "FILENAME_IN_FIELD" )
private boolean fileNameInField;
@Injection( name = "FILENAME_FIELD" )
private String fileNameField;
/** Calculated value ... */
@Injection( name = "NEW_LINE" )
private String newline;
/** Flag: add the filenames to result filenames */
@Injection( name = "ADD_TO_RESULT" )
private boolean addToResultFilenames;
/** Flag : Do not open new file when transformation start */
@Injection( name = "DO_NOT_CREATE_FILE_AT_STARTUP" )
private boolean doNotOpenNewFileInit;
@Injection( name = "SPECIFY_DATE_FORMAT" )
private boolean specifyingFormat;
@Injection( name = "DATE_FORMAT" )
private String dateTimeFormat;
public TextFileOutputMeta() {
super(); // allocate BaseStepMeta
}
/**
* @return FileAsCommand
*/
public boolean isFileAsCommand() {
return fileAsCommand;
}
/**
* @param fileAsCommand
* The fileAsCommand to set
*/
public void setFileAsCommand( boolean fileAsCommand ) {
this.fileAsCommand = fileAsCommand;
}
public boolean isServletOutput() {
return servletOutput;
}
public void setServletOutput( boolean servletOutput ) {
this.servletOutput = servletOutput;
}
/**
* @param createparentfolder
* The createparentfolder to set.
*/
public void setCreateParentFolder( boolean createparentfolder ) {
this.createparentfolder = createparentfolder;
}
/**
* @return Returns the createparentfolder.
*/
public boolean isCreateParentFolder() {
return createparentfolder;
}
/**
* @return Returns the dateInFilename.
*/
public boolean isDateInFilename() {
return dateInFilename;
}
/**
* @param dateInFilename
* The dateInFilename to set.
*/
public void setDateInFilename( boolean dateInFilename ) {
this.dateInFilename = dateInFilename;
}
/**
* @return Returns the enclosure.
*/
public String getEnclosure() {
return enclosure;
}
/**
* @param enclosure
* The enclosure to set.
*/
public void setEnclosure( String enclosure ) {
this.enclosure = enclosure;
}
/**
* @return Returns the enclosureForced.
*/
public boolean isEnclosureForced() {
return enclosureForced;
}
/**
* @param enclosureForced
* The enclosureForced to set.
*/
public void setEnclosureForced( boolean enclosureForced ) {
this.enclosureForced = enclosureForced;
}
/**
* @return Returns the enclosureFixDisabled.
*/
public boolean isEnclosureFixDisabled() {
return disableEnclosureFix;
}
/**
* @param enclosureFixDisabled
* The enclosureFixDisabled to set.
*/
public void setEnclosureFixDisabled( boolean disableEnclosureFix ) {
this.disableEnclosureFix = disableEnclosureFix;
}
/**
* @return Returns the extension.
*/
public String getExtension() {
return extension;
}
/**
* @param extension
* The extension to set.
*/
public void setExtension( String extension ) {
this.extension = extension;
}
/**
* @return Returns the add to result filesname.
*/
public boolean isAddToResultFiles() {
return addToResultFilenames;
}
/**
* @param addtoresultfilenamesin
* The addtoresultfilenames to set.
*/
public void setAddToResultFiles( boolean addtoresultfilenamesin ) {
this.addToResultFilenames = addtoresultfilenamesin;
}
/**
* @return Returns the fileAppended.
*/
public boolean isFileAppended() {
return fileAppended;
}
/**
* @param fileAppended
* The fileAppended to set.
*/
public void setFileAppended( boolean fileAppended ) {
this.fileAppended = fileAppended;
}
/**
* @return Returns the fileFormat.
*/
public String getFileFormat() {
return fileFormat;
}
/**
* @param fileFormat
* The fileFormat to set.
*/
public void setFileFormat( String fileFormat ) {
this.fileFormat = fileFormat;
}
/**
* @return Returns the fileCompression.
*/
public String getFileCompression() {
return fileCompression;
}
/**
* @param fileCompression
* The fileCompression to set.
*/
public void setFileCompression( String fileCompression ) {
this.fileCompression = fileCompression;
}
/**
* @return Returns the fileName.
*/
public String getFileName() {
return fileName;
}
/**
* @param fileName
* The fileName to set.
*/
public void setFileName( String fileName ) {
this.fileName = fileName;
}
/**
* @return Returns the footer.
*/
public boolean isFooterEnabled() {
return footerEnabled;
}
/**
* @param footer
* The footer to set.
*/
public void setFooterEnabled( boolean footer ) {
this.footerEnabled = footer;
}
/**
* @return Returns the header.
*/
public boolean isHeaderEnabled() {
return headerEnabled;
}
/**
* @param header
* The header to set.
*/
public void setHeaderEnabled( boolean header ) {
this.headerEnabled = header;
}
/**
* @return Returns the newline.
*/
public String getNewline() {
return newline;
}
/**
* @param newline
* The newline to set.
*/
public void setNewline( String newline ) {
this.newline = newline;
}
/**
* @return Returns the padded.
*/
public boolean isPadded() {
return padded;
}
/**
* @param padded
* The padded to set.
*/
public void setPadded( boolean padded ) {
this.padded = padded;
}
/**
* @return Returns the fastDump.
*/
public boolean isFastDump() {
return fastDump;
}
/**
* @param fastDump
* The fastDump to set.
*/
public void setFastDump( boolean fastDump ) {
this.fastDump = fastDump;
}
/**
* @return Returns the separator.
*/
public String getSeparator() {
return separator;
}
/**
* @param separator
* The separator to set.
*/
public void setSeparator( String separator ) {
this.separator = separator;
}
/**
* @return Returns the "do not open new file at init" flag.
*/
public boolean isDoNotOpenNewFileInit() {
return doNotOpenNewFileInit;
}
/**
* @param doNotOpenNewFileInit
* The "do not open new file at init" flag to set.
*/
public void setDoNotOpenNewFileInit( boolean doNotOpenNewFileInit ) {
this.doNotOpenNewFileInit = doNotOpenNewFileInit;
}
/**
* @return Returns the splitEvery.
*/
public int getSplitEvery() {
return splitEvery;
}
/**
* @return <tt>1</tt> if <tt>isFooterEnabled()</tt> and <tt>0</tt> otherwise
*/
public int getFooterShift() {
return isFooterEnabled() ? 1 : 0;
}
/**
* @param splitEvery
* The splitEvery to set.
*/
public void setSplitEvery( int splitEvery ) {
this.splitEvery = splitEvery;
}
/**
* @return Returns the stepNrInFilename.
*/
public boolean isStepNrInFilename() {
return stepNrInFilename;
}
/**
* @param stepNrInFilename
* The stepNrInFilename to set.
*/
public void setStepNrInFilename( boolean stepNrInFilename ) {
this.stepNrInFilename = stepNrInFilename;
}
/**
* @return Returns the partNrInFilename.
*/
public boolean isPartNrInFilename() {
return partNrInFilename;
}
/**
* @param partNrInFilename
* The partNrInFilename to set.
*/
public void setPartNrInFilename( boolean partNrInFilename ) {
this.partNrInFilename = partNrInFilename;
}
/**
* @return Returns the timeInFilename.
*/
public boolean isTimeInFilename() {
return timeInFilename;
}
/**
* @param timeInFilename
* The timeInFilename to set.
*/
public void setTimeInFilename( boolean timeInFilename ) {
this.timeInFilename = timeInFilename;
}
public boolean isSpecifyingFormat() {
return specifyingFormat;
}
public void setSpecifyingFormat( boolean specifyingFormat ) {
this.specifyingFormat = specifyingFormat;
}
public String getDateTimeFormat() {
return dateTimeFormat;
}
public void setDateTimeFormat( String dateTimeFormat ) {
this.dateTimeFormat = dateTimeFormat;
}
/**
* @return Returns the outputFields.
*/
public TextFileField[] getOutputFields() {
return outputFields;
}
/**
* @param outputFields
* The outputFields to set.
*/
public void setOutputFields( TextFileField[] outputFields ) {
this.outputFields = outputFields;
}
/**
* @return The desired encoding of output file, null or empty if the default system encoding needs to be used.
*/
public String getEncoding() {
return encoding;
}
/**
* @param encoding
* The desired encoding of output file, null or empty if the default system encoding needs to be used.
*/
public void setEncoding( String encoding ) {
this.encoding = encoding;
}
/**
* @return The desired last line in the output file, null or empty if nothing has to be added.
*/
public String getEndedLine() {
return endedLine;
}
/**
* @param endedLine
* The desired last line in the output file, null or empty if nothing has to be added.
*/
public void setEndedLine( String endedLine ) {
this.endedLine = endedLine;
}
/**
* @return Is the file name coded in a field?
*/
public boolean isFileNameInField() {
return fileNameInField;
}
/**
* @param fileNameInField
* Is the file name coded in a field?
*/
public void setFileNameInField( boolean fileNameInField ) {
this.fileNameInField = fileNameInField;
}
/**
* @return The field name that contains the output file name.
*/
public String getFileNameField() {
return fileNameField;
}
/**
* @param fileNameField
* Name of the field that contains the file name
*/
public void setFileNameField( String fileNameField ) {
this.fileNameField = fileNameField;
}
@Override
public void loadXML( Node stepnode, List<DatabaseMeta> databases, IMetaStore metaStore ) throws KettleXMLException {
readData( stepnode, metaStore );
}
public void allocate( int nrfields ) {
outputFields = new TextFileField[nrfields];
}
@Override
public Object clone() {
TextFileOutputMeta retval = (TextFileOutputMeta) super.clone();
int nrfields = outputFields.length;
retval.allocate( nrfields );
for ( int i = 0; i < nrfields; i++ ) {
retval.outputFields[i] = (TextFileField) outputFields[i].clone();
}
return retval;
}
private void readData( Node stepnode, IMetaStore metastore ) throws KettleXMLException {
try {
separator = XMLHandler.getTagValue( stepnode, "separator" );
if ( separator == null ) {
separator = "";
}
enclosure = XMLHandler.getTagValue( stepnode, "enclosure" );
if ( enclosure == null ) {
enclosure = "";
}
enclosureForced = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "enclosure_forced" ) );
String sDisableEnclosureFix = XMLHandler.getTagValue( stepnode, "enclosure_fix_disabled" );
// Default this value to true for backwards compatibility
if ( sDisableEnclosureFix == null ) {
disableEnclosureFix = true;
} else {
disableEnclosureFix = "Y".equalsIgnoreCase( sDisableEnclosureFix );
}
// Default createparentfolder to true if the tag is missing
String createParentFolderTagValue = XMLHandler.getTagValue( stepnode, "create_parent_folder" );
createparentfolder =
( createParentFolderTagValue == null ) ? true : "Y".equalsIgnoreCase( createParentFolderTagValue );
headerEnabled = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "header" ) );
footerEnabled = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "footer" ) );
fileFormat = XMLHandler.getTagValue( stepnode, "format" );
fileCompression = XMLHandler.getTagValue( stepnode, "compression" );
if ( fileCompression == null ) {
if ( "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "zipped" ) ) ) {
fileCompression = fileCompressionTypeCodes[FILE_COMPRESSION_TYPE_ZIP];
} else {
fileCompression = fileCompressionTypeCodes[FILE_COMPRESSION_TYPE_NONE];
}
}
encoding = XMLHandler.getTagValue( stepnode, "encoding" );
endedLine = XMLHandler.getTagValue( stepnode, "endedLine" );
if ( endedLine == null ) {
endedLine = "";
}
fileName = loadSource( stepnode, metastore );
fileAsCommand = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "is_command" ) );
servletOutput = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "servlet_output" ) );
doNotOpenNewFileInit =
"Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "do_not_open_new_file_init" ) );
extension = XMLHandler.getTagValue( stepnode, "file", "extention" );
fileAppended = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "append" ) );
stepNrInFilename = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "split" ) );
partNrInFilename = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "haspartno" ) );
dateInFilename = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "add_date" ) );
timeInFilename = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "add_time" ) );
specifyingFormat = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "SpecifyFormat" ) );
dateTimeFormat = XMLHandler.getTagValue( stepnode, "file", "date_time_format" );
String AddToResultFiles = XMLHandler.getTagValue( stepnode, "file", "add_to_result_filenames" );
if ( Utils.isEmpty( AddToResultFiles ) ) {
addToResultFilenames = true;
} else {
addToResultFilenames = "Y".equalsIgnoreCase( AddToResultFiles );
}
padded = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "pad" ) );
fastDump = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "file", "fast_dump" ) );
splitEvery = Const.toInt( XMLHandler.getTagValue( stepnode, "file", "splitevery" ), 0 );
newline = getNewLine( fileFormat );
fileNameInField = "Y".equalsIgnoreCase( XMLHandler.getTagValue( stepnode, "fileNameInField" ) );
fileNameField = XMLHandler.getTagValue( stepnode, "fileNameField" );
Node fields = XMLHandler.getSubNode( stepnode, "fields" );
int nrfields = XMLHandler.countNodes( fields, "field" );
allocate( nrfields );
for ( int i = 0; i < nrfields; i++ ) {
Node fnode = XMLHandler.getSubNodeByNr( fields, "field", i );
outputFields[i] = new TextFileField();
outputFields[i].setName( XMLHandler.getTagValue( fnode, "name" ) );
outputFields[i].setType( XMLHandler.getTagValue( fnode, "type" ) );
outputFields[i].setFormat( XMLHandler.getTagValue( fnode, "format" ) );
outputFields[i].setCurrencySymbol( XMLHandler.getTagValue( fnode, "currency" ) );
outputFields[i].setDecimalSymbol( XMLHandler.getTagValue( fnode, "decimal" ) );
outputFields[i].setGroupingSymbol( XMLHandler.getTagValue( fnode, "group" ) );
outputFields[i].setTrimType( ValueMetaString.getTrimTypeByCode( XMLHandler.getTagValue( fnode, "trim_type" ) ) );
outputFields[i].setNullString( XMLHandler.getTagValue( fnode, "nullif" ) );
outputFields[i].setLength( Const.toInt( XMLHandler.getTagValue( fnode, "length" ), -1 ) );
outputFields[i].setPrecision( Const.toInt( XMLHandler.getTagValue( fnode, "precision" ), -1 ) );
}
} catch ( Exception e ) {
throw new KettleXMLException( "Unable to load step info from XML", e );
}
}
public void readData( Node stepnode ) throws KettleXMLException {
readData( stepnode, null );
}
public String getNewLine( String fformat ) {
String nl = System.getProperty( "line.separator" );
if ( fformat != null ) {
if ( fformat.equalsIgnoreCase( "DOS" ) ) {
nl = "\r\n";
} else if ( fformat.equalsIgnoreCase( "UNIX" ) ) {
nl = "\n";
} else if ( fformat.equalsIgnoreCase( "CR" ) ) {
nl = "\r";
} else if ( fformat.equalsIgnoreCase( "None" ) ) {
nl = "";
}
}
return nl;
}
@Override
public void setDefault() {
createparentfolder = true; // Default createparentfolder to true
separator = ";";
enclosure = "\"";
specifyingFormat = false;
dateTimeFormat = null;
enclosureForced = false;
disableEnclosureFix = false;
headerEnabled = true;
footerEnabled = false;
fileFormat = "DOS";
fileCompression = fileCompressionTypeCodes[FILE_COMPRESSION_TYPE_NONE];
fileName = "file";
fileAsCommand = false;
servletOutput = false;
doNotOpenNewFileInit = false;
extension = "txt";
stepNrInFilename = false;
partNrInFilename = false;
dateInFilename = false;
timeInFilename = false;
padded = false;
fastDump = false;
addToResultFilenames = true;
splitEvery = 0;
newline = getNewLine( fileFormat );
int i, nrfields = 0;
allocate( nrfields );
for ( i = 0; i < nrfields; i++ ) {
outputFields[i] = new TextFileField();
outputFields[i].setName( "field" + i );
outputFields[i].setType( "Number" );
outputFields[i].setFormat( " 0,000,000.00;-0,000,000.00" );
outputFields[i].setCurrencySymbol( "" );
outputFields[i].setDecimalSymbol( "," );
outputFields[i].setGroupingSymbol( "." );
outputFields[i].setNullString( "" );
outputFields[i].setLength( -1 );
outputFields[i].setPrecision( -1 );
}
fileAppended = false;
}
public String[] getFiles( VariableSpace space ) {
int copies = 1;
int splits = 1;
int parts = 1;
if ( stepNrInFilename ) {
copies = 3;
}
if ( partNrInFilename ) {
parts = 3;
}
if ( splitEvery != 0 ) {
splits = 3;
}
int nr = copies * parts * splits;
if ( nr > 1 ) {
nr++;
}
String[] retval = new String[nr];
int i = 0;
for ( int copy = 0; copy < copies; copy++ ) {
for ( int part = 0; part < parts; part++ ) {
for ( int split = 0; split < splits; split++ ) {
retval[i] = buildFilename( space, copy, "P" + part, split, false );
i++;
}
}
}
if ( i < nr ) {
retval[i] = "...";
}
return retval;
}
public String buildFilename( VariableSpace space, int stepnr, String partnr, int splitnr, boolean ziparchive ) {
return buildFilename( fileName, extension, space, stepnr, partnr, splitnr, ziparchive, this );
}
public String buildFilename( String filename, String extension, VariableSpace space, int stepnr, String partnr,
int splitnr, boolean ziparchive, TextFileOutputMeta meta ) {
SimpleDateFormat daf = new SimpleDateFormat();
// Replace possible environment variables...
String retval = space.environmentSubstitute( filename );
String realextension = space.environmentSubstitute( extension );
if ( meta.isFileAsCommand() ) {
return retval;
}
Date now = new Date();
if ( meta.isSpecifyingFormat() && !Utils.isEmpty( meta.getDateTimeFormat() ) ) {
daf.applyPattern( meta.getDateTimeFormat() );
String dt = daf.format( now );
retval += dt;
} else {
if ( meta.isDateInFilename() ) {
daf.applyPattern( "yyyMMdd" );
String d = daf.format( now );
retval += "_" + d;
}
if ( meta.isTimeInFilename() ) {
daf.applyPattern( "HHmmss" );
String t = daf.format( now );
retval += "_" + t;
}
}
if ( meta.isStepNrInFilename() ) {
retval += "_" + stepnr;
}
if ( meta.isPartNrInFilename() ) {
retval += "_" + partnr;
}
if ( meta.getSplitEvery() > 0 ) {
retval += "_" + splitnr;
}
if ( meta.getFileCompression().equals( "Zip" ) ) {
if ( ziparchive ) {
retval += ".zip";
} else {
if ( realextension != null && realextension.length() != 0 ) {
retval += "." + realextension;
}
}
} else {
if ( realextension != null && realextension.length() != 0 ) {
retval += "." + realextension;
}
if ( meta.getFileCompression().equals( "GZip" ) ) {
retval += ".gz";
}
}
return retval;
}
@Override
public void getFields( RowMetaInterface row, String name, RowMetaInterface[] info, StepMeta nextStep,
VariableSpace space, Repository repository, IMetaStore metaStore ) throws KettleStepException {
// No values are added to the row in this type of step
// However, in case of Fixed length records,
// the field precisions and lengths are altered!
for ( int i = 0; i < outputFields.length; i++ ) {
TextFileField field = outputFields[i];
ValueMetaInterface v = row.searchValueMeta( field.getName() );
if ( v != null ) {
v.setLength( field.getLength() );
v.setPrecision( field.getPrecision() );
v.setConversionMask( field.getFormat() );
v.setDecimalSymbol( field.getDecimalSymbol() );
v.setGroupingSymbol( field.getGroupingSymbol() );
v.setCurrencySymbol( field.getCurrencySymbol() );
v.setOutputPaddingEnabled( isPadded() );
v.setTrimType( field.getTrimType() );
if ( !Utils.isEmpty( getEncoding() ) ) {
v.setStringEncoding( getEncoding() );
}
// enable output padding by default to be compatible with v2.5.x
//
v.setOutputPaddingEnabled( true );
}
}
}
@Override
@Deprecated
public void getFields( RowMetaInterface inputRowMeta, String name, RowMetaInterface[] info, StepMeta nextStep,
VariableSpace space ) throws KettleStepException {
getFields( inputRowMeta, name, info, nextStep, space, null, null );
}
@Override
public String getXML() {
StringBuilder retval = new StringBuilder( 800 );
retval.append( " " ).append( XMLHandler.addTagValue( "separator", separator ) );
retval.append( " " ).append( XMLHandler.addTagValue( "enclosure", enclosure ) );
retval.append( " " ).append( XMLHandler.addTagValue( "enclosure_forced", enclosureForced ) );
retval.append( " " ).append( XMLHandler.addTagValue( "enclosure_fix_disabled", disableEnclosureFix ) );
retval.append( " " ).append( XMLHandler.addTagValue( "header", headerEnabled ) );
retval.append( " " ).append( XMLHandler.addTagValue( "footer", footerEnabled ) );
retval.append( " " ).append( XMLHandler.addTagValue( "format", fileFormat ) );
retval.append( " " ).append( XMLHandler.addTagValue( "compression", fileCompression ) );
retval.append( " " ).append( XMLHandler.addTagValue( "encoding", encoding ) );
retval.append( " " ).append( XMLHandler.addTagValue( "endedLine", endedLine ) );
retval.append( " " + XMLHandler.addTagValue( "fileNameInField", fileNameInField ) );
retval.append( " " + XMLHandler.addTagValue( "fileNameField", fileNameField ) );
retval.append( " " + XMLHandler.addTagValue( "create_parent_folder", createparentfolder ) );
retval.append( " <file>" ).append( Const.CR );
saveSource( retval, fileName );
retval.append( " " ).append( XMLHandler.addTagValue( "is_command", fileAsCommand ) );
retval.append( " " ).append( XMLHandler.addTagValue( "servlet_output", servletOutput ) );
retval.append( " " ).append( XMLHandler.addTagValue( "do_not_open_new_file_init", doNotOpenNewFileInit ) );
retval.append( " " ).append( XMLHandler.addTagValue( "extention", extension ) );
retval.append( " " ).append( XMLHandler.addTagValue( "append", fileAppended ) );
retval.append( " " ).append( XMLHandler.addTagValue( "split", stepNrInFilename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "haspartno", partNrInFilename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "add_date", dateInFilename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "add_time", timeInFilename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "SpecifyFormat", specifyingFormat ) );
retval.append( " " ).append( XMLHandler.addTagValue( "date_time_format", dateTimeFormat ) );
retval.append( " " ).append( XMLHandler.addTagValue( "add_to_result_filenames", addToResultFilenames ) );
retval.append( " " ).append( XMLHandler.addTagValue( "pad", padded ) );
retval.append( " " ).append( XMLHandler.addTagValue( "fast_dump", fastDump ) );
retval.append( " " ).append( XMLHandler.addTagValue( "splitevery", splitEvery ) );
retval.append( " </file>" ).append( Const.CR );
retval.append( " <fields>" ).append( Const.CR );
for ( int i = 0; i < outputFields.length; i++ ) {
TextFileField field = outputFields[i];
if ( field.getName() != null && field.getName().length() != 0 ) {
retval.append( " <field>" ).append( Const.CR );
retval.append( " " ).append( XMLHandler.addTagValue( "name", field.getName() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "type", field.getTypeDesc() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "format", field.getFormat() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "currency", field.getCurrencySymbol() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "decimal", field.getDecimalSymbol() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "group", field.getGroupingSymbol() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "nullif", field.getNullString() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "trim_type", field.getTrimTypeCode() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "length", field.getLength() ) );
retval.append( " " ).append( XMLHandler.addTagValue( "precision", field.getPrecision() ) );
retval.append( " </field>" ).append( Const.CR );
}
}
retval.append( " </fields>" ).append( Const.CR );
return retval.toString();
}
@Override
public void readRep( Repository rep, IMetaStore metaStore, ObjectId id_step, List<DatabaseMeta> databases )
throws KettleException {
try {
separator = rep.getStepAttributeString( id_step, "separator" );
enclosure = rep.getStepAttributeString( id_step, "enclosure" );
enclosureForced = rep.getStepAttributeBoolean( id_step, "enclosure_forced" );
disableEnclosureFix = rep.getStepAttributeBoolean( id_step, 0, "enclosure_fix_disabled", true );
createparentfolder = rep.getStepAttributeBoolean( id_step, "create_parent_folder" );
headerEnabled = rep.getStepAttributeBoolean( id_step, "header" );
footerEnabled = rep.getStepAttributeBoolean( id_step, "footer" );
fileFormat = rep.getStepAttributeString( id_step, "format" );
fileCompression = rep.getStepAttributeString( id_step, "compression" );
fileNameInField = rep.getStepAttributeBoolean( id_step, "fileNameInField" );
fileNameField = rep.getStepAttributeString( id_step, "fileNameField" );
if ( fileCompression == null ) {
if ( rep.getStepAttributeBoolean( id_step, "zipped" ) ) {
fileCompression = fileCompressionTypeCodes[FILE_COMPRESSION_TYPE_ZIP];
} else {
fileCompression = fileCompressionTypeCodes[FILE_COMPRESSION_TYPE_NONE];
}
}
encoding = rep.getStepAttributeString( id_step, "encoding" );
fileName = loadSourceRep( rep, id_step, metaStore );
fileAsCommand = rep.getStepAttributeBoolean( id_step, "file_is_command" );
servletOutput = rep.getStepAttributeBoolean( id_step, "file_servlet_output" );
doNotOpenNewFileInit = rep.getStepAttributeBoolean( id_step, "do_not_open_new_file_init" );
extension = rep.getStepAttributeString( id_step, "file_extention" );
fileAppended = rep.getStepAttributeBoolean( id_step, "file_append" );
splitEvery = (int) rep.getStepAttributeInteger( id_step, "file_split" );
stepNrInFilename = rep.getStepAttributeBoolean( id_step, "file_add_stepnr" );
partNrInFilename = rep.getStepAttributeBoolean( id_step, "file_add_partnr" );
dateInFilename = rep.getStepAttributeBoolean( id_step, "file_add_date" );
timeInFilename = rep.getStepAttributeBoolean( id_step, "file_add_time" );
specifyingFormat = rep.getStepAttributeBoolean( id_step, "SpecifyFormat" );
dateTimeFormat = rep.getStepAttributeString( id_step, "date_time_format" );
String AddToResultFiles = rep.getStepAttributeString( id_step, "add_to_result_filenames" );
if ( Utils.isEmpty( AddToResultFiles ) ) {
addToResultFilenames = true;
} else {
addToResultFilenames = rep.getStepAttributeBoolean( id_step, "add_to_result_filenames" );
}
padded = rep.getStepAttributeBoolean( id_step, "file_pad" );
fastDump = rep.getStepAttributeBoolean( id_step, "file_fast_dump" );
newline = getNewLine( fileFormat );
int nrfields = rep.countNrStepAttributes( id_step, "field_name" );
allocate( nrfields );
for ( int i = 0; i < nrfields; i++ ) {
outputFields[i] = new TextFileField();
outputFields[i].setName( rep.getStepAttributeString( id_step, i, "field_name" ) );
outputFields[i].setType( rep.getStepAttributeString( id_step, i, "field_type" ) );
outputFields[i].setFormat( rep.getStepAttributeString( id_step, i, "field_format" ) );
outputFields[i].setCurrencySymbol( rep.getStepAttributeString( id_step, i, "field_currency" ) );
outputFields[i].setDecimalSymbol( rep.getStepAttributeString( id_step, i, "field_decimal" ) );
outputFields[i].setGroupingSymbol( rep.getStepAttributeString( id_step, i, "field_group" ) );
outputFields[i].setTrimType( ValueMetaString.getTrimTypeByCode( rep.getStepAttributeString( id_step, i,
"field_trim_type" ) ) );
outputFields[i].setNullString( rep.getStepAttributeString( id_step, i, "field_nullif" ) );
outputFields[i].setLength( (int) rep.getStepAttributeInteger( id_step, i, "field_length" ) );
outputFields[i].setPrecision( (int) rep.getStepAttributeInteger( id_step, i, "field_precision" ) );
}
endedLine = rep.getStepAttributeString( id_step, "endedLine" );
} catch ( Exception e ) {
throw new KettleException( "Unexpected error reading step information from the repository", e );
}
}
@Override
public void saveRep( Repository rep, IMetaStore metaStore, ObjectId id_transformation, ObjectId id_step )
throws KettleException {
try {
rep.saveStepAttribute( id_transformation, id_step, "separator", separator );
rep.saveStepAttribute( id_transformation, id_step, "enclosure", enclosure );
rep.saveStepAttribute( id_transformation, id_step, "enclosure_forced", enclosureForced );
rep.saveStepAttribute( id_transformation, id_step, 0, "enclosure_fix_disabled", disableEnclosureFix );
rep.saveStepAttribute( id_transformation, id_step, "header", headerEnabled );
rep.saveStepAttribute( id_transformation, id_step, "footer", footerEnabled );
rep.saveStepAttribute( id_transformation, id_step, "format", fileFormat );
rep.saveStepAttribute( id_transformation, id_step, "compression", fileCompression );
rep.saveStepAttribute( id_transformation, id_step, "encoding", encoding );
saveSourceRep( rep, id_transformation, id_step, fileName );
rep.saveStepAttribute( id_transformation, id_step, "file_is_command", fileAsCommand );
rep.saveStepAttribute( id_transformation, id_step, "file_servlet_output", servletOutput );
rep.saveStepAttribute( id_transformation, id_step, "do_not_open_new_file_init", doNotOpenNewFileInit );
rep.saveStepAttribute( id_transformation, id_step, "file_extention", extension );
rep.saveStepAttribute( id_transformation, id_step, "file_append", fileAppended );
rep.saveStepAttribute( id_transformation, id_step, "file_split", splitEvery );
rep.saveStepAttribute( id_transformation, id_step, "file_add_stepnr", stepNrInFilename );
rep.saveStepAttribute( id_transformation, id_step, "file_add_partnr", partNrInFilename );
rep.saveStepAttribute( id_transformation, id_step, "file_add_date", dateInFilename );
rep.saveStepAttribute( id_transformation, id_step, "date_time_format", dateTimeFormat );
rep.saveStepAttribute( id_transformation, id_step, "create_parent_folder", createparentfolder );
rep.saveStepAttribute( id_transformation, id_step, "SpecifyFormat", specifyingFormat );
rep.saveStepAttribute( id_transformation, id_step, "add_to_result_filenames", addToResultFilenames );
rep.saveStepAttribute( id_transformation, id_step, "file_add_time", timeInFilename );
rep.saveStepAttribute( id_transformation, id_step, "file_pad", padded );
rep.saveStepAttribute( id_transformation, id_step, "file_fast_dump", fastDump );
rep.saveStepAttribute( id_transformation, id_step, "fileNameInField", fileNameInField );
rep.saveStepAttribute( id_transformation, id_step, "fileNameField", fileNameField );
for ( int i = 0; i < outputFields.length; i++ ) {
TextFileField field = outputFields[i];
rep.saveStepAttribute( id_transformation, id_step, i, "field_name", field.getName() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_type", field.getTypeDesc() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_format", field.getFormat() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_currency", field.getCurrencySymbol() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_decimal", field.getDecimalSymbol() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_group", field.getGroupingSymbol() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_trim_type", field.getTrimTypeCode() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_nullif", field.getNullString() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_length", field.getLength() );
rep.saveStepAttribute( id_transformation, id_step, i, "field_precision", field.getPrecision() );
}
rep.saveStepAttribute( id_transformation, id_step, "endedLine", endedLine );
} catch ( Exception e ) {
throw new KettleException( "Unable to save step information to the repository for id_step=" + id_step, e );
}
}
@Override
public void check( List<CheckResultInterface> remarks, TransMeta transMeta, StepMeta stepMeta, RowMetaInterface prev,
String[] input, String[] output, RowMetaInterface info, VariableSpace space, Repository repository,
IMetaStore metaStore ) {
CheckResult cr;
// Check output fields
if ( prev != null && prev.size() > 0 ) {
cr =
new CheckResult( CheckResultInterface.TYPE_RESULT_OK, BaseMessages.getString( PKG,
"TextFileOutputMeta.CheckResult.FieldsReceived", "" + prev.size() ), stepMeta );
remarks.add( cr );
String error_message = "";
boolean error_found = false;
// Starting from selected fields in ...
for ( int i = 0; i < outputFields.length; i++ ) {
int idx = prev.indexOfValue( outputFields[i].getName() );
if ( idx < 0 ) {
error_message += "\t\t" + outputFields[i].getName() + Const.CR;
error_found = true;
}
}
if ( error_found ) {
error_message = BaseMessages.getString( PKG, "TextFileOutputMeta.CheckResult.FieldsNotFound", error_message );
cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, error_message, stepMeta );
remarks.add( cr );
} else {
cr =
new CheckResult( CheckResultInterface.TYPE_RESULT_OK, BaseMessages.getString( PKG,
"TextFileOutputMeta.CheckResult.AllFieldsFound" ), stepMeta );
remarks.add( cr );
}
}
// See if we have input streams leading to this step!
if ( input.length > 0 ) {
cr =
new CheckResult( CheckResultInterface.TYPE_RESULT_OK, BaseMessages.getString( PKG,
"TextFileOutputMeta.CheckResult.ExpectedInputOk" ), stepMeta );
remarks.add( cr );
} else {
cr =
new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, BaseMessages.getString( PKG,
"TextFileOutputMeta.CheckResult.ExpectedInputError" ), stepMeta );
remarks.add( cr );
}
cr =
new CheckResult( CheckResultInterface.TYPE_RESULT_COMMENT, BaseMessages.getString( PKG,
"TextFileOutputMeta.CheckResult.FilesNotChecked" ), stepMeta );
remarks.add( cr );
}
@Override
public StepInterface getStep( StepMeta stepMeta, StepDataInterface stepDataInterface, int cnr, TransMeta transMeta,
Trans trans ) {
return new TextFileOutput( stepMeta, stepDataInterface, cnr, transMeta, trans );
}
@Override
public StepDataInterface getStepData() {
return new TextFileOutputData();
}
/**
* Since the exported transformation that runs this will reside in a ZIP file, we can't reference files relatively. So
* what this does is turn the name of the base path into an absolute path.
*
* @param space
* the variable space to use
* @param definitions
* @param resourceNamingInterface
* @param repository
* The repository to optionally load other resources from (to be converted to XML)
* @param metaStore
* the metaStore in which non-kettle metadata could reside.
*
* @return the filename of the exported resource
*/
@Override
public String exportResources( VariableSpace space, Map<String, ResourceDefinition> definitions,
ResourceNamingInterface resourceNamingInterface, Repository repository, IMetaStore metaStore )
throws KettleException {
try {
// The object that we're modifying here is a copy of the original!
// So let's change the filename from relative to absolute by grabbing the file object...
// In case the name of the file comes from previous steps, forget about this!
//
if ( !fileNameInField ) {
if ( !Utils.isEmpty( fileName ) ) {
FileObject fileObject = KettleVFS.getFileObject( space.environmentSubstitute( fileName ), space );
fileName = resourceNamingInterface.nameResource( fileObject, space, true );
}
}
return null;
} catch ( Exception e ) {
throw new KettleException( e );
}
}
public void setFilename( String fileName ) {
this.fileName = fileName;
}
@Override
public List<StepInjectionMetaEntry> extractStepMetadataEntries() throws KettleException {
return getStepMetaInjectionInterface().extractStepMetadataEntries();
}
protected String loadSource( Node stepnode, IMetaStore metastore ) {
return XMLHandler.getTagValue( stepnode, "file", "name" );
}
protected void saveSource( StringBuilder retVal, String value ) {
retVal.append( " " ).append( XMLHandler.addTagValue( "name", fileName ) );
}
protected String loadSourceRep( Repository rep, ObjectId id_step, IMetaStore metaStore ) throws KettleException {
return rep.getStepAttributeString( id_step, "file_name" );
}
protected void saveSourceRep( Repository rep, ObjectId id_transformation, ObjectId id_step, String value )
throws KettleException {
rep.saveStepAttribute( id_transformation, id_step, "file_name", fileName );
}
/**
* {@inheritDoc}
*/
@Override
public boolean passDataToServletOutput() {
return servletOutput;
}
}