/*! ******************************************************************************
*
* 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.ui.trans.steps.csvinput;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.provider.local.LocalFile;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
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.KettleStepException;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.LoggingRegistry;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaFactory;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransPreviewFactory;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.RowAdapter;
import org.pentaho.di.trans.step.StepDialogInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.steps.csvinput.CsvInput;
import org.pentaho.di.trans.steps.csvinput.CsvInputMeta;
import org.pentaho.di.trans.steps.textfileinput.EncodingType;
import org.pentaho.di.trans.steps.textfileinput.TextFileInput;
import org.pentaho.di.trans.steps.textfileinput.TextFileInputField;
import org.pentaho.di.trans.steps.textfileinput.TextFileInputMeta;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.dialog.EnterNumberDialog;
import org.pentaho.di.ui.core.dialog.EnterTextDialog;
import org.pentaho.di.ui.core.dialog.ErrorDialog;
import org.pentaho.di.ui.core.dialog.PreviewRowsDialog;
import org.pentaho.di.ui.core.widget.ColumnInfo;
import org.pentaho.di.ui.core.widget.ComboValuesSelectionListener;
import org.pentaho.di.ui.core.widget.ComboVar;
import org.pentaho.di.ui.core.widget.TableView;
import org.pentaho.di.ui.core.widget.TextVar;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.spoon.trans.TransGraph;
import org.pentaho.di.ui.trans.dialog.TransPreviewProgressDialog;
import org.pentaho.di.ui.trans.step.BaseStepDialog;
import org.pentaho.di.ui.trans.steps.textfileinput.TextFileCSVImportProgressDialog;
public class CsvInputDialog extends BaseStepDialog implements StepDialogInterface {
private static Class<?> PKG = CsvInput.class; // for i18n purposes, needed by Translator2!!
private CsvInputMeta inputMeta;
private TextVar wFilename;
private CCombo wFilenameField;
private Button wbbFilename; // Browse for a file
private Button wIncludeFilename;
private TextVar wRowNumField;
private Button wbDelimiter;
private TextVar wDelimiter;
private TextVar wEnclosure;
private TextVar wBufferSize;
private Button wLazyConversion;
private Button wHeaderPresent;
private FormData fdAddResult;
private FormData fdlAddResult;
private TableView wFields;
private Label wlAddResult;
private Button wAddResult;
private boolean isReceivingInput;
private Button wRunningInParallel;
private Button wNewlinePossible;
private ComboVar wEncoding;
private boolean gotEncodings = false;
private Label wlRunningInParallel;
private boolean initializing;
private AtomicBoolean previewBusy;
public CsvInputDialog( Shell parent, Object in, TransMeta tr, String sname ) {
super( parent, (BaseStepMeta) in, tr, sname );
inputMeta = (CsvInputMeta) in;
}
public String open() {
Shell parent = getParent();
Display display = parent.getDisplay();
shell = new Shell( parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MIN | SWT.MAX );
props.setLook( shell );
setShellImage( shell, inputMeta );
ModifyListener lsMod = new ModifyListener() {
public void modifyText( ModifyEvent e ) {
inputMeta.setChanged();
}
};
changed = inputMeta.hasChanged();
ModifyListener lsContent = new ModifyListener() {
@Override
public void modifyText( ModifyEvent arg0 ) {
// asyncUpdatePreview();
}
};
initializing = true;
previewBusy = new AtomicBoolean( false );
FormLayout formLayout = new FormLayout();
formLayout.marginWidth = Const.FORM_MARGIN;
formLayout.marginHeight = Const.FORM_MARGIN;
shell.setLayout( formLayout );
shell.setText( BaseMessages.getString( PKG, "CsvInputDialog.Shell.Title" ) );
int middle = props.getMiddlePct();
int margin = Const.MARGIN;
// Step name line
//
wlStepname = new Label( shell, SWT.RIGHT );
wlStepname.setText( BaseMessages.getString( PKG, "CsvInputDialog.Stepname.Label" ) );
props.setLook( wlStepname );
fdlStepname = new FormData();
fdlStepname.left = new FormAttachment( 0, 0 );
fdlStepname.right = new FormAttachment( middle, -margin );
fdlStepname.top = new FormAttachment( 0, margin );
wlStepname.setLayoutData( fdlStepname );
wStepname = new Text( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wStepname );
wStepname.addModifyListener( lsMod );
fdStepname = new FormData();
fdStepname.left = new FormAttachment( middle, 0 );
fdStepname.top = new FormAttachment( 0, margin );
fdStepname.right = new FormAttachment( 100, 0 );
wStepname.setLayoutData( fdStepname );
Control lastControl = wStepname;
// See if the step receives input. If so, we don't ask for the filename, but
// for the filename field.
//
isReceivingInput = transMeta.findNrPrevSteps( stepMeta ) > 0;
if ( isReceivingInput ) {
RowMetaInterface previousFields;
try {
previousFields = transMeta.getPrevStepFields( stepMeta );
} catch ( KettleStepException e ) {
new ErrorDialog( shell,
BaseMessages.getString( PKG, "CsvInputDialog.ErrorDialog.UnableToGetInputFields.Title" ),
BaseMessages.getString( PKG, "CsvInputDialog.ErrorDialog.UnableToGetInputFields.Message" ), e );
previousFields = new RowMeta();
}
// The filename field ...
//
Label wlFilename = new Label( shell, SWT.RIGHT );
wlFilename.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "FILENAME_FIELD" ) ) );
props.setLook( wlFilename );
FormData fdlFilename = new FormData();
fdlFilename.top = new FormAttachment( lastControl, margin );
fdlFilename.left = new FormAttachment( 0, 0 );
fdlFilename.right = new FormAttachment( middle, -margin );
wlFilename.setLayoutData( fdlFilename );
wFilenameField = new CCombo( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
wFilenameField.setItems( previousFields.getFieldNames() );
props.setLook( wFilenameField );
wFilenameField.addModifyListener( lsMod );
FormData fdFilename = new FormData();
fdFilename.top = new FormAttachment( lastControl, margin );
fdFilename.left = new FormAttachment( middle, 0 );
fdFilename.right = new FormAttachment( 100, 0 );
wFilenameField.setLayoutData( fdFilename );
lastControl = wFilenameField;
// Checkbox to include the filename in the output...
//
Label wlIncludeFilename = new Label( shell, SWT.RIGHT );
wlIncludeFilename.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "INCLUDE_FILENAME" ) ) );
props.setLook( wlIncludeFilename );
FormData fdlIncludeFilename = new FormData();
fdlIncludeFilename.top = new FormAttachment( lastControl, margin );
fdlIncludeFilename.left = new FormAttachment( 0, 0 );
fdlIncludeFilename.right = new FormAttachment( middle, -margin );
wlIncludeFilename.setLayoutData( fdlIncludeFilename );
wIncludeFilename = new Button( shell, SWT.CHECK );
props.setLook( wIncludeFilename );
wFilenameField.addModifyListener( lsMod );
FormData fdIncludeFilename = new FormData();
fdIncludeFilename.top = new FormAttachment( lastControl, margin );
fdIncludeFilename.left = new FormAttachment( middle, 0 );
fdIncludeFilename.right = new FormAttachment( 100, 0 );
wIncludeFilename.setLayoutData( fdIncludeFilename );
lastControl = wIncludeFilename;
} else {
// Filename...
//
// The filename browse button
//
wbbFilename = new Button( shell, SWT.PUSH | SWT.CENTER );
props.setLook( wbbFilename );
wbbFilename.setText( BaseMessages.getString( PKG, "System.Button.Browse" ) );
wbbFilename.setToolTipText( BaseMessages.getString( PKG, "System.Tooltip.BrowseForFileOrDirAndAdd" ) );
FormData fdbFilename = new FormData();
fdbFilename.top = new FormAttachment( lastControl, margin );
fdbFilename.right = new FormAttachment( 100, 0 );
wbbFilename.setLayoutData( fdbFilename );
// The field itself...
//
Label wlFilename = new Label( shell, SWT.RIGHT );
wlFilename.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "FILENAME" ) ) );
props.setLook( wlFilename );
FormData fdlFilename = new FormData();
fdlFilename.top = new FormAttachment( lastControl, margin );
fdlFilename.left = new FormAttachment( 0, 0 );
fdlFilename.right = new FormAttachment( middle, -margin );
wlFilename.setLayoutData( fdlFilename );
wFilename = new TextVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wFilename );
wFilename.addModifyListener( lsMod );
FormData fdFilename = new FormData();
fdFilename.top = new FormAttachment( lastControl, margin );
fdFilename.left = new FormAttachment( middle, 0 );
fdFilename.right = new FormAttachment( wbbFilename, -margin );
wFilename.setLayoutData( fdFilename );
/*
* wFilename.addFocusListener(new FocusAdapter() {
*
* @Override public void focusLost(FocusEvent arg0) { asyncUpdatePreview(); } });
*/
lastControl = wFilename;
}
// delimiter
Label wlDelimiter = new Label( shell, SWT.RIGHT );
wlDelimiter.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "DELIMITER" ) ) );
props.setLook( wlDelimiter );
FormData fdlDelimiter = new FormData();
fdlDelimiter.top = new FormAttachment( lastControl, margin );
fdlDelimiter.left = new FormAttachment( 0, 0 );
fdlDelimiter.right = new FormAttachment( middle, -margin );
wlDelimiter.setLayoutData( fdlDelimiter );
wbDelimiter = new Button( shell, SWT.PUSH | SWT.CENTER );
props.setLook( wbDelimiter );
wbDelimiter.setText( BaseMessages.getString( PKG, "CsvInputDialog.Delimiter.Button" ) );
FormData fdbDelimiter = new FormData();
fdbDelimiter.top = new FormAttachment( lastControl, margin );
fdbDelimiter.right = new FormAttachment( 100, 0 );
wbDelimiter.setLayoutData( fdbDelimiter );
wDelimiter = new TextVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wDelimiter );
wDelimiter.addModifyListener( lsMod );
FormData fdDelimiter = new FormData();
fdDelimiter.top = new FormAttachment( lastControl, margin );
fdDelimiter.left = new FormAttachment( middle, 0 );
fdDelimiter.right = new FormAttachment( wbDelimiter, -margin );
wDelimiter.setLayoutData( fdDelimiter );
wDelimiter.addModifyListener( lsContent );
lastControl = wDelimiter;
// enclosure
Label wlEnclosure = new Label( shell, SWT.RIGHT );
wlEnclosure.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "ENCLOSURE" ) ) );
props.setLook( wlEnclosure );
FormData fdlEnclosure = new FormData();
fdlEnclosure.top = new FormAttachment( lastControl, margin );
fdlEnclosure.left = new FormAttachment( 0, 0 );
fdlEnclosure.right = new FormAttachment( middle, -margin );
wlEnclosure.setLayoutData( fdlEnclosure );
wEnclosure = new TextVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wEnclosure );
wEnclosure.addModifyListener( lsMod );
FormData fdEnclosure = new FormData();
fdEnclosure.top = new FormAttachment( lastControl, margin );
fdEnclosure.left = new FormAttachment( middle, 0 );
fdEnclosure.right = new FormAttachment( 100, 0 );
wEnclosure.setLayoutData( fdEnclosure );
wEnclosure.addModifyListener( lsContent );
lastControl = wEnclosure;
// bufferSize
//
Label wlBufferSize = new Label( shell, SWT.RIGHT );
wlBufferSize.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "BUFFERSIZE" ) ) );
props.setLook( wlBufferSize );
FormData fdlBufferSize = new FormData();
fdlBufferSize.top = new FormAttachment( lastControl, margin );
fdlBufferSize.left = new FormAttachment( 0, 0 );
fdlBufferSize.right = new FormAttachment( middle, -margin );
wlBufferSize.setLayoutData( fdlBufferSize );
wBufferSize = new TextVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wBufferSize );
wBufferSize.addModifyListener( lsMod );
FormData fdBufferSize = new FormData();
fdBufferSize.top = new FormAttachment( lastControl, margin );
fdBufferSize.left = new FormAttachment( middle, 0 );
fdBufferSize.right = new FormAttachment( 100, 0 );
wBufferSize.setLayoutData( fdBufferSize );
lastControl = wBufferSize;
// performingLazyConversion?
//
Label wlLazyConversion = new Label( shell, SWT.RIGHT );
wlLazyConversion.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "LAZY_CONVERSION" ) ) );
props.setLook( wlLazyConversion );
FormData fdlLazyConversion = new FormData();
fdlLazyConversion.top = new FormAttachment( lastControl, margin );
fdlLazyConversion.left = new FormAttachment( 0, 0 );
fdlLazyConversion.right = new FormAttachment( middle, -margin );
wlLazyConversion.setLayoutData( fdlLazyConversion );
wLazyConversion = new Button( shell, SWT.CHECK );
props.setLook( wLazyConversion );
FormData fdLazyConversion = new FormData();
fdLazyConversion.top = new FormAttachment( lastControl, margin );
fdLazyConversion.left = new FormAttachment( middle, 0 );
fdLazyConversion.right = new FormAttachment( 100, 0 );
wLazyConversion.setLayoutData( fdLazyConversion );
lastControl = wLazyConversion;
// header row?
//
Label wlHeaderPresent = new Label( shell, SWT.RIGHT );
wlHeaderPresent.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "HEADER_PRESENT" ) ) );
props.setLook( wlHeaderPresent );
FormData fdlHeaderPresent = new FormData();
fdlHeaderPresent.top = new FormAttachment( lastControl, margin );
fdlHeaderPresent.left = new FormAttachment( 0, 0 );
fdlHeaderPresent.right = new FormAttachment( middle, -margin );
wlHeaderPresent.setLayoutData( fdlHeaderPresent );
wHeaderPresent = new Button( shell, SWT.CHECK );
props.setLook( wHeaderPresent );
FormData fdHeaderPresent = new FormData();
fdHeaderPresent.top = new FormAttachment( lastControl, margin );
fdHeaderPresent.left = new FormAttachment( middle, 0 );
fdHeaderPresent.right = new FormAttachment( 100, 0 );
wHeaderPresent.setLayoutData( fdHeaderPresent );
/*
* wHeaderPresent.addSelectionListener(new SelectionAdapter() {
*
* @Override public void widgetSelected(SelectionEvent arg0) { asyncUpdatePreview(); } });
*/
lastControl = wHeaderPresent;
wlAddResult = new Label( shell, SWT.RIGHT );
wlAddResult.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "ADD_FILENAME_RESULT" ) ) );
props.setLook( wlAddResult );
fdlAddResult = new FormData();
fdlAddResult.left = new FormAttachment( 0, 0 );
fdlAddResult.top = new FormAttachment( wHeaderPresent, margin );
fdlAddResult.right = new FormAttachment( middle, -margin );
wlAddResult.setLayoutData( fdlAddResult );
wAddResult = new Button( shell, SWT.CHECK );
props.setLook( wAddResult );
wAddResult.setToolTipText( BaseMessages.getString( PKG, inputMeta.getTooltip( "ADD_FILENAME_RESULT" ) ) );
fdAddResult = new FormData();
fdAddResult.left = new FormAttachment( middle, 0 );
fdAddResult.top = new FormAttachment( wHeaderPresent, margin );
wAddResult.setLayoutData( fdAddResult );
lastControl = wAddResult;
// The field itself...
//
Label wlRowNumField = new Label( shell, SWT.RIGHT );
wlRowNumField.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "ROW_NUM_FIELD" ) ) );
props.setLook( wlRowNumField );
FormData fdlRowNumField = new FormData();
fdlRowNumField.top = new FormAttachment( lastControl, margin );
fdlRowNumField.left = new FormAttachment( 0, 0 );
fdlRowNumField.right = new FormAttachment( middle, -margin );
wlRowNumField.setLayoutData( fdlRowNumField );
wRowNumField = new TextVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wRowNumField );
wRowNumField.addModifyListener( lsMod );
FormData fdRowNumField = new FormData();
fdRowNumField.top = new FormAttachment( lastControl, margin );
fdRowNumField.left = new FormAttachment( middle, 0 );
fdRowNumField.right = new FormAttachment( 100, 0 );
wRowNumField.setLayoutData( fdRowNumField );
lastControl = wRowNumField;
// running in parallel?
//
wlRunningInParallel = new Label( shell, SWT.RIGHT );
wlRunningInParallel.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "PARALLEL" ) ) );
props.setLook( wlRunningInParallel );
FormData fdlRunningInParallel = new FormData();
fdlRunningInParallel.top = new FormAttachment( lastControl, margin );
fdlRunningInParallel.left = new FormAttachment( 0, 0 );
fdlRunningInParallel.right = new FormAttachment( middle, -margin );
wlRunningInParallel.setLayoutData( fdlRunningInParallel );
wRunningInParallel = new Button( shell, SWT.CHECK );
props.setLook( wRunningInParallel );
FormData fdRunningInParallel = new FormData();
fdRunningInParallel.top = new FormAttachment( lastControl, margin );
fdRunningInParallel.left = new FormAttachment( middle, 0 );
wRunningInParallel.setLayoutData( fdRunningInParallel );
lastControl = wRunningInParallel;
// Is a new line possible in a field?
//
Label wlNewlinePossible = new Label( shell, SWT.RIGHT );
wlNewlinePossible.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "NEWLINE_POSSIBLE" ) ) );
props.setLook( wlNewlinePossible );
FormData fdlNewlinePossible = new FormData();
fdlNewlinePossible.top = new FormAttachment( lastControl, margin );
fdlNewlinePossible.left = new FormAttachment( 0, 0 );
fdlNewlinePossible.right = new FormAttachment( middle, -margin );
wlNewlinePossible.setLayoutData( fdlNewlinePossible );
wNewlinePossible = new Button( shell, SWT.CHECK );
props.setLook( wNewlinePossible );
FormData fdNewlinePossible = new FormData();
fdNewlinePossible.top = new FormAttachment( lastControl, margin );
fdNewlinePossible.left = new FormAttachment( middle, 0 );
wNewlinePossible.setLayoutData( fdNewlinePossible );
wNewlinePossible.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent event ) {
setFlags();
}
} );
wNewlinePossible.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent arg0 ) {
asyncUpdatePreview();
}
} );
lastControl = wNewlinePossible;
// Encoding
Label wlEncoding = new Label( shell, SWT.RIGHT );
wlEncoding.setText( BaseMessages.getString( PKG, inputMeta.getDescription( "ENCODING" ) ) );
props.setLook( wlEncoding );
FormData fdlEncoding = new FormData();
fdlEncoding.top = new FormAttachment( lastControl, margin );
fdlEncoding.left = new FormAttachment( 0, 0 );
fdlEncoding.right = new FormAttachment( middle, -margin );
wlEncoding.setLayoutData( fdlEncoding );
wEncoding = new ComboVar( transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
props.setLook( wEncoding );
wEncoding.addModifyListener( lsMod );
FormData fdEncoding = new FormData();
fdEncoding.top = new FormAttachment( lastControl, margin );
fdEncoding.left = new FormAttachment( middle, 0 );
fdEncoding.right = new FormAttachment( 100, 0 );
wEncoding.setLayoutData( fdEncoding );
wEncoding.addModifyListener( lsContent );
lastControl = wEncoding;
wEncoding.addFocusListener( new FocusListener() {
public void focusLost( org.eclipse.swt.events.FocusEvent e ) {
}
public void focusGained( org.eclipse.swt.events.FocusEvent e ) {
Cursor busy = new Cursor( shell.getDisplay(), SWT.CURSOR_WAIT );
shell.setCursor( busy );
setEncodings();
shell.setCursor( null );
busy.dispose();
}
} );
// Some buttons first, so that the dialog scales nicely...
//
wOK = new Button( shell, SWT.PUSH );
wOK.setText( BaseMessages.getString( PKG, "System.Button.OK" ) );
wCancel = new Button( shell, SWT.PUSH );
wCancel.setText( BaseMessages.getString( PKG, "System.Button.Cancel" ) );
wPreview = new Button( shell, SWT.PUSH );
wPreview.setText( BaseMessages.getString( PKG, "System.Button.Preview" ) );
wPreview.setEnabled( !isReceivingInput );
wGet = new Button( shell, SWT.PUSH );
wGet.setText( BaseMessages.getString( PKG, "System.Button.GetFields" ) );
wGet.setEnabled( !isReceivingInput );
setButtonPositions( new Button[] { wOK, wGet, wPreview, wCancel }, margin, null );
// Fields
ColumnInfo[] colinf =
new ColumnInfo[] {
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_NAME" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_TYPE" ) ),
ColumnInfo.COLUMN_TYPE_CCOMBO, ValueMetaFactory.getValueMetaNames(), true ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_FORMAT" ) ),
ColumnInfo.COLUMN_TYPE_FORMAT, 2 ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_LENGTH" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_PRECISION" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_CURRENCY" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_DECIMAL" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_GROUP" ) ),
ColumnInfo.COLUMN_TYPE_TEXT, false ),
new ColumnInfo(
BaseMessages.getString( PKG, inputMeta.getDescription( "FIELD_TRIM_TYPE" ) ),
ColumnInfo.COLUMN_TYPE_CCOMBO, ValueMetaString.trimTypeDesc ), };
colinf[2].setComboValuesSelectionListener( new ComboValuesSelectionListener() {
public String[] getComboValues( TableItem tableItem, int rowNr, int colNr ) {
String[] comboValues = new String[] {};
int type = ValueMetaFactory.getIdForValueMeta( tableItem.getText( colNr - 1 ) );
switch ( type ) {
case ValueMetaInterface.TYPE_DATE:
comboValues = Const.getDateFormats();
break;
case ValueMetaInterface.TYPE_INTEGER:
case ValueMetaInterface.TYPE_BIGNUMBER:
case ValueMetaInterface.TYPE_NUMBER:
comboValues = Const.getNumberFormats();
break;
default:
break;
}
return comboValues;
}
} );
wFields = new TableView( transMeta, shell, SWT.FULL_SELECTION | SWT.MULTI, colinf, 1, lsMod, props );
FormData fdFields = new FormData();
fdFields.top = new FormAttachment( lastControl, margin * 2 );
fdFields.bottom = new FormAttachment( wOK, -margin * 2 );
fdFields.left = new FormAttachment( 0, 0 );
fdFields.right = new FormAttachment( 100, 0 );
wFields.setLayoutData( fdFields );
wFields.setContentListener( lsContent );
// Add listeners
lsCancel = new Listener() {
public void handleEvent( Event e ) {
cancel();
}
};
lsOK = new Listener() {
public void handleEvent( Event e ) {
ok();
}
};
lsPreview = new Listener() {
public void handleEvent( Event e ) {
preview();
}
};
lsGet = new Listener() {
public void handleEvent( Event e ) {
getCSV();
}
};
wCancel.addListener( SWT.Selection, lsCancel );
wOK.addListener( SWT.Selection, lsOK );
wPreview.addListener( SWT.Selection, lsPreview );
wGet.addListener( SWT.Selection, lsGet );
lsDef = new SelectionAdapter() {
public void widgetDefaultSelected( SelectionEvent e ) {
ok();
}
};
wStepname.addSelectionListener( lsDef );
if ( wFilename != null ) {
wFilename.addSelectionListener( lsDef );
}
if ( wFilenameField != null ) {
wFilenameField.addSelectionListener( lsDef );
}
wDelimiter.addSelectionListener( lsDef );
wEnclosure.addSelectionListener( lsDef );
wBufferSize.addSelectionListener( lsDef );
wRowNumField.addSelectionListener( lsDef );
// Allow the insertion of tabs as separator...
wbDelimiter.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent se ) {
Text t = wDelimiter.getTextWidget();
if ( t != null ) {
t.insert( "\t" );
}
}
} );
if ( wbbFilename != null ) {
// Listen to the browse button next to the file name
wbbFilename.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent e ) {
FileDialog dialog = new FileDialog( shell, SWT.OPEN );
dialog.setFilterExtensions( new String[] { "*.txt;*.csv", "*.csv", "*.txt", "*" } );
if ( wFilename.getText() != null ) {
String fname = transMeta.environmentSubstitute( wFilename.getText() );
dialog.setFileName( fname );
}
dialog.setFilterNames( new String[] {
BaseMessages.getString( PKG, "System.FileType.CSVFiles" ) + ", "
+ BaseMessages.getString( PKG, "System.FileType.TextFiles" ),
BaseMessages.getString( PKG, "System.FileType.CSVFiles" ),
BaseMessages.getString( PKG, "System.FileType.TextFiles" ),
BaseMessages.getString( PKG, "System.FileType.AllFiles" ) } );
if ( dialog.open() != null ) {
String str = dialog.getFilterPath() + System.getProperty( "file.separator" ) + dialog.getFileName();
wFilename.setText( str );
}
}
} );
}
// Detect X or ALT-F4 or something that kills this window...
shell.addShellListener( new ShellAdapter() {
public void shellClosed( ShellEvent e ) {
cancel();
}
} );
// Set the shell size, based upon previous time...
setSize();
getData();
inputMeta.setChanged( changed );
initializing = false;
// Update the preview window.
//
// asyncUpdatePreview();
shell.open();
while ( !shell.isDisposed() ) {
if ( !display.readAndDispatch() ) {
display.sleep();
}
}
return stepname;
}
protected void setFlags() {
// In case there are newlines in fields, we can't load data in parallel
//
boolean parallelPossible = !wNewlinePossible.getSelection();
wlRunningInParallel.setEnabled( parallelPossible );
wRunningInParallel.setEnabled( parallelPossible );
if ( !parallelPossible ) {
wRunningInParallel.setSelection( false );
}
}
private void setEncodings() {
// Encoding of the text file:
if ( !gotEncodings ) {
gotEncodings = true;
wEncoding.removeAll();
List<Charset> values = new ArrayList<Charset>( Charset.availableCharsets().values() );
for ( int i = 0; i < values.size(); i++ ) {
Charset charSet = values.get( i );
wEncoding.add( charSet.displayName() );
}
// Now select the default!
String defEncoding = Const.getEnvironmentVariable( "file.encoding", "UTF-8" );
int idx = Const.indexOfString( defEncoding, wEncoding.getItems() );
if ( idx >= 0 ) {
wEncoding.select( idx );
}
}
}
public void getData() {
getData( inputMeta, true );
}
/**
* Copy information from the meta-data input to the dialog fields.
*/
public void getData( CsvInputMeta inputMeta, boolean copyStepname ) {
if ( copyStepname ) {
wStepname.setText( stepname );
}
if ( isReceivingInput ) {
wFilenameField.setText( Const.NVL( inputMeta.getFilenameField(), "" ) );
wIncludeFilename.setSelection( inputMeta.isIncludingFilename() );
} else {
wFilename.setText( Const.NVL( inputMeta.getFilename(), "" ) );
}
wDelimiter.setText( Const.NVL( inputMeta.getDelimiter(), "" ) );
wEnclosure.setText( Const.NVL( inputMeta.getEnclosure(), "" ) );
wBufferSize.setText( Const.NVL( inputMeta.getBufferSize(), "" ) );
wLazyConversion.setSelection( inputMeta.isLazyConversionActive() );
wHeaderPresent.setSelection( inputMeta.isHeaderPresent() );
wRunningInParallel.setSelection( inputMeta.isRunningInParallel() );
wNewlinePossible.setSelection( inputMeta.isNewlinePossibleInFields() );
wRowNumField.setText( Const.NVL( inputMeta.getRowNumField(), "" ) );
wAddResult.setSelection( inputMeta.isAddResultFile() );
wEncoding.setText( Const.NVL( inputMeta.getEncoding(), "" ) );
for ( int i = 0; i < inputMeta.getInputFields().length; i++ ) {
TextFileInputField field = inputMeta.getInputFields()[i];
TableItem item = new TableItem( wFields.table, SWT.NONE );
int colnr = 1;
item.setText( colnr++, Const.NVL( field.getName(), "" ) );
item.setText( colnr++, ValueMetaFactory.getValueMetaName( field.getType() ) );
item.setText( colnr++, Const.NVL( field.getFormat(), "" ) );
item.setText( colnr++, field.getLength() >= 0 ? Integer.toString( field.getLength() ) : "" );
item.setText( colnr++, field.getPrecision() >= 0 ? Integer.toString( field.getPrecision() ) : "" );
item.setText( colnr++, Const.NVL( field.getCurrencySymbol(), "" ) );
item.setText( colnr++, Const.NVL( field.getDecimalSymbol(), "" ) );
item.setText( colnr++, Const.NVL( field.getGroupSymbol(), "" ) );
item.setText( colnr++, Const.NVL( field.getTrimTypeDesc(), "" ) );
}
wFields.removeEmptyRows();
wFields.setRowNums();
wFields.optWidth( true );
setFlags();
wStepname.selectAll();
wStepname.setFocus();
}
private void cancel() {
stepname = null;
inputMeta.setChanged( changed );
dispose();
}
private void getInfo( CsvInputMeta inputMeta ) {
if ( isReceivingInput ) {
inputMeta.setFilenameField( wFilenameField.getText() );
inputMeta.setIncludingFilename( wIncludeFilename.getSelection() );
} else {
inputMeta.setFilename( wFilename.getText() );
}
inputMeta.setDelimiter( wDelimiter.getText() );
inputMeta.setEnclosure( wEnclosure.getText() );
inputMeta.setBufferSize( wBufferSize.getText() );
inputMeta.setLazyConversionActive( wLazyConversion.getSelection() );
inputMeta.setHeaderPresent( wHeaderPresent.getSelection() );
inputMeta.setRowNumField( wRowNumField.getText() );
inputMeta.setAddResultFile( wAddResult.getSelection() );
inputMeta.setRunningInParallel( wRunningInParallel.getSelection() );
inputMeta.setNewlinePossibleInFields( wNewlinePossible.getSelection() );
inputMeta.setEncoding( wEncoding.getText() );
int nrNonEmptyFields = wFields.nrNonEmpty();
inputMeta.allocate( nrNonEmptyFields );
for ( int i = 0; i < nrNonEmptyFields; i++ ) {
TableItem item = wFields.getNonEmpty( i );
//CHECKSTYLE:Indentation:OFF
inputMeta.getInputFields()[i] = new TextFileInputField();
int colnr = 1;
inputMeta.getInputFields()[i].setName( item.getText( colnr++ ) );
inputMeta.getInputFields()[i].setType( ValueMetaFactory.getIdForValueMeta( item.getText( colnr++ ) ) );
inputMeta.getInputFields()[i].setFormat( item.getText( colnr++ ) );
inputMeta.getInputFields()[i].setLength( Const.toInt( item.getText( colnr++ ), -1 ) );
inputMeta.getInputFields()[i].setPrecision( Const.toInt( item.getText( colnr++ ), -1 ) );
inputMeta.getInputFields()[i].setCurrencySymbol( item.getText( colnr++ ) );
inputMeta.getInputFields()[i].setDecimalSymbol( item.getText( colnr++ ) );
inputMeta.getInputFields()[i].setGroupSymbol( item.getText( colnr++ ) );
inputMeta.getInputFields()[i].setTrimType( ValueMetaString.getTrimTypeByDesc( item.getText( colnr++ ) ) );
}
wFields.removeEmptyRows();
wFields.setRowNums();
wFields.optWidth( true );
inputMeta.setChanged();
}
private void ok() {
if ( Utils.isEmpty( wStepname.getText() ) ) {
return;
}
getInfo( inputMeta );
stepname = wStepname.getText();
dispose();
}
// Get the data layout
private void getCSV() {
InputStream inputStream = null;
try {
CsvInputMeta meta = new CsvInputMeta();
getInfo( meta );
String filename = transMeta.environmentSubstitute( meta.getFilename() );
String delimiter = transMeta.environmentSubstitute( meta.getDelimiter() );
String enclosure = transMeta.environmentSubstitute( meta.getEnclosure() );
FileObject fileObject = KettleVFS.getFileObject( filename );
if ( !( fileObject instanceof LocalFile ) ) {
// We can only use NIO on local files at the moment, so that's what we
// limit ourselves to.
//
throw new KettleException( BaseMessages.getString( PKG, "CsvInput.Log.OnlyLocalFilesAreSupported" ) );
}
wFields.table.removeAll();
inputStream = KettleVFS.getInputStream( fileObject );
String realEncoding = transMeta.environmentSubstitute( meta.getEncoding() );
InputStreamReader reader;
if ( Utils.isEmpty( realEncoding ) ) {
reader = new InputStreamReader( inputStream );
} else {
reader = new InputStreamReader( inputStream, realEncoding );
}
EncodingType encodingType = EncodingType.guessEncodingType( reader.getEncoding() );
// Read a line of data to determine the number of rows...
//
String line =
TextFileInput.getLine(
log, reader, encodingType, TextFileInputMeta.FILE_FORMAT_UNIX, new StringBuilder( 1000 ) );
// Split the string, header or data into parts...
//
String[] fieldNames =
CsvInput.guessStringsFromLine( log, line, delimiter, enclosure, meta.getEscapeCharacter() );
if ( !meta.isHeaderPresent() ) {
// Don't use field names from the header...
// Generate field names F1 ... F10
//
DecimalFormat df = new DecimalFormat( "000" );
for ( int i = 0; i < fieldNames.length; i++ ) {
fieldNames[i] = "Field_" + df.format( i );
}
} else {
if ( !Utils.isEmpty( meta.getEnclosure() ) ) {
for ( int i = 0; i < fieldNames.length; i++ ) {
if ( fieldNames[i].startsWith( meta.getEnclosure() )
&& fieldNames[i].endsWith( meta.getEnclosure() ) && fieldNames[i].length() > 1 ) {
fieldNames[i] = fieldNames[i].substring( 1, fieldNames[i].length() - 1 );
}
}
}
}
// Trim the names to make sure...
//
for ( int i = 0; i < fieldNames.length; i++ ) {
fieldNames[i] = Const.trim( fieldNames[i] );
}
// Update the GUI
//
for ( int i = 0; i < fieldNames.length; i++ ) {
TableItem item = new TableItem( wFields.table, SWT.NONE );
item.setText( 1, fieldNames[i] );
item.setText( 2, ValueMetaFactory.getValueMetaName( ValueMetaInterface.TYPE_STRING ) );
}
wFields.removeEmptyRows();
wFields.setRowNums();
wFields.optWidth( true );
// Now we can continue reading the rows of data and we can guess the
// Sample a few lines to determine the correct type of the fields...
//
String shellText = BaseMessages.getString( PKG, "CsvInputDialog.LinesToSample.DialogTitle" );
String lineText = BaseMessages.getString( PKG, "CsvInputDialog.LinesToSample.DialogMessage" );
EnterNumberDialog end = new EnterNumberDialog( shell, 100, shellText, lineText );
int samples = end.open();
if ( samples >= 0 ) {
getInfo( meta );
TextFileCSVImportProgressDialog pd =
new TextFileCSVImportProgressDialog( shell, meta, transMeta, reader, samples, true );
String message = pd.open();
if ( message != null ) {
wFields.removeAll();
// OK, what's the result of our search?
getData( meta, false );
wFields.removeEmptyRows();
wFields.setRowNums();
wFields.optWidth( true );
EnterTextDialog etd =
new EnterTextDialog(
shell, BaseMessages.getString( PKG, "CsvInputDialog.ScanResults.DialogTitle" ), BaseMessages
.getString( PKG, "CsvInputDialog.ScanResults.DialogMessage" ), message, true );
etd.setReadOnly();
etd.open();
// asyncUpdatePreview();
}
}
} catch ( IOException e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "CsvInputDialog.IOError.DialogTitle" ), BaseMessages
.getString( PKG, "CsvInputDialog.IOError.DialogMessage" ), e );
} catch ( KettleException e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "System.Dialog.Error.Title" ), BaseMessages.getString(
PKG, "CsvInputDialog.ErrorGettingFileDesc.DialogMessage" ), e );
} finally {
try {
inputStream.close();
} catch ( Exception e ) {
// Ignore close errors
}
}
}
// Preview the data
private synchronized void preview() {
// Create the XML input step
CsvInputMeta oneMeta = new CsvInputMeta();
getInfo( oneMeta );
TransMeta previewMeta =
TransPreviewFactory.generatePreviewTransformation( transMeta, oneMeta, wStepname.getText() );
transMeta.getVariable( "Internal.Transformation.Filename.Directory" );
previewMeta.getVariable( "Internal.Transformation.Filename.Directory" );
EnterNumberDialog numberDialog =
new EnterNumberDialog( shell, props.getDefaultPreviewSize(), BaseMessages.getString(
PKG, "CsvInputDialog.PreviewSize.DialogTitle" ), BaseMessages.getString(
PKG, "CsvInputDialog.PreviewSize.DialogMessage" ) );
int previewSize = numberDialog.open();
if ( previewSize > 0 ) {
TransPreviewProgressDialog progressDialog =
new TransPreviewProgressDialog(
shell, previewMeta, new String[] { wStepname.getText() }, new int[] { previewSize } );
progressDialog.open();
Trans trans = progressDialog.getTrans();
String loggingText = progressDialog.getLoggingText();
if ( !progressDialog.isCancelled() ) {
if ( trans.getResult() != null && trans.getResult().getNrErrors() > 0 ) {
EnterTextDialog etd =
new EnterTextDialog(
shell, BaseMessages.getString( PKG, "System.Dialog.PreviewError.Title" ), BaseMessages
.getString( PKG, "System.Dialog.PreviewError.Message" ), loggingText, true );
etd.setReadOnly();
etd.open();
}
}
PreviewRowsDialog prd =
new PreviewRowsDialog(
shell, transMeta, SWT.NONE, wStepname.getText(), progressDialog.getPreviewRowsMeta( wStepname
.getText() ), progressDialog.getPreviewRows( wStepname.getText() ), loggingText );
prd.open();
}
}
/**
* Load metadata from step window
*/
protected void updatePreview() {
if ( initializing ) {
return;
}
if ( previewBusy.get() ) {
return;
}
try {
previewBusy.set( true );
CsvInputMeta meta = new CsvInputMeta();
getInfo( meta );
// Validate some basic data...
//
if ( Utils.isEmpty( meta.getFilename() ) ) {
return;
}
if ( Utils.isEmpty( meta.getInputFields() ) ) {
return;
}
String stepname = wStepname.getText();
// StepMeta stepMeta = new StepMeta(stepname, meta);
StringBuffer buffer = new StringBuffer();
final List<Object[]> rowsData = new ArrayList<Object[]>();
final RowMetaInterface rowMeta = new RowMeta();
try {
meta.getFields( rowMeta, stepname, null, null, transMeta, repository, metaStore );
TransMeta previewTransMeta = TransPreviewFactory.generatePreviewTransformation( transMeta, meta, stepname );
final Trans trans = new Trans( previewTransMeta );
trans.prepareExecution( null );
StepInterface step = trans.getRunThread( stepname, 0 );
step.addRowListener( new RowAdapter() {
@Override
public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
rowsData.add( row );
// If we have enough rows we can stop
//
if ( rowsData.size() > PropsUI.getInstance().getDefaultPreviewSize() ) {
trans.stopAll();
}
}
} );
trans.startThreads();
trans.waitUntilFinished();
if ( trans.getErrors() > 0 ) {
StringBuffer log = KettleLogStore.getAppender().getBuffer( trans.getLogChannelId(), false );
buffer.append( log );
}
KettleLogStore.discardLines( trans.getLogChannelId(), false );
LoggingRegistry.getInstance().removeIncludingChildren( trans.getLogChannelId() );
} catch ( Exception e ) {
buffer.append( Const.getStackTracker( e ) );
}
TransGraph transGraph = Spoon.getInstance().getActiveTransGraph();
if ( transGraph != null ) {
if ( !transGraph.isExecutionResultsPaneVisible() ) {
transGraph.showExecutionResults();
}
transGraph.extraViewTabFolder.setSelection( 5 );
transGraph.transPreviewDelegate.addPreviewData( stepMeta, rowMeta, rowsData, buffer );
transGraph.transPreviewDelegate.setSelectedStep( stepMeta );
transGraph.transPreviewDelegate.refreshView();
}
} finally {
previewBusy.set( false );
}
}
protected void asyncUpdatePreview() {
Runnable update = new Runnable() {
@Override
public void run() {
try {
updatePreview();
} catch ( SWTException e ) {
// Ignore widget disposed errors
}
}
};
shell.getDisplay().asyncExec( update );
}
}