/*! ****************************************************************************** * * 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.step; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Monitor; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.SourceToTargetMapping; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.StepPluginType; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.laf.BasePropertyHandler; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.BaseStepMeta; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.StepMetaInterface; import org.pentaho.di.ui.core.PropsUI; import org.pentaho.di.ui.core.database.dialog.DatabaseDialog; import org.pentaho.di.ui.core.dialog.EnterMappingDialog; import org.pentaho.di.ui.core.dialog.ErrorDialog; import org.pentaho.di.ui.core.gui.GUIResource; import org.pentaho.di.ui.core.gui.WindowProperty; import org.pentaho.di.ui.core.widget.ComboVar; import org.pentaho.di.ui.spoon.XulSpoonResourceBundle; import org.pentaho.di.ui.spoon.XulSpoonSettingsManager; import org.pentaho.di.ui.util.HelpUtils; import org.pentaho.di.ui.xul.KettleXulLoader; import org.pentaho.ui.xul.XulException; import org.pentaho.ui.xul.XulSettingsManager; import org.pentaho.ui.xul.containers.XulTree; import org.pentaho.ui.xul.containers.XulTreeRow; import org.pentaho.ui.xul.swt.SwtBindingFactory; import org.pentaho.ui.xul.swt.SwtXulRunner; /** * User: nbaker Date: Jun 7, 2010 */ public abstract class BaseStepXulDialog extends BaseStepGenericXulDialog { @Override public XulSettingsManager getSettingsManager() { return XulSpoonSettingsManager.getInstance(); } @Override public ResourceBundle getResourceBundle() { return new XulSpoonResourceBundle( getClassForMessages() ); } @Override public void clear() { // Nothing to do } @Override public boolean validate() { return true; } private static Class<?> PKG = StepInterface.class; // for i18n purposes, needed by Translator2!! protected Listener lsOK, lsGet, lsPreview, lsSQL, lsCreate, lsCancel; protected Listener lsResize; protected boolean changed, backupChanged; protected PropsUI props; protected static final int BUTTON_ALIGNMENT_CENTER = 0; protected static final int BUTTON_ALIGNMENT_LEFT = 1; protected static final int BUTTON_ALIGNMENT_RIGHT = 2; protected static int buttonAlignment = BUTTON_ALIGNMENT_CENTER; protected DatabaseDialog databaseDialog; protected Shell dialogShell; static { // Get the button alignment buttonAlignment = getButtonAlignment(); } public BaseStepXulDialog( String xulFile, Shell parent, BaseStepMeta baseStepMeta, TransMeta transMeta, String stepname ) { super( xulFile, parent, baseStepMeta, transMeta, stepname ); this.backupChanged = baseStepMeta.hasChanged(); this.props = PropsUI.getInstance(); try { initializeXul(); } catch ( Exception e ) { e.printStackTrace(); log.logError( "Error initializing (" + stepname + ") step dialog", e ); throw new IllegalStateException( "Cannot load dialog due to error in initialization", e ); } } protected void initializeXul() throws XulException { initializeXul( new KettleXulLoader(), new SwtBindingFactory(), new SwtXulRunner(), parent ); dialogShell = (Shell) xulDialog.getRootObject(); } public void setShellImage( Shell shell, StepMetaInterface stepMetaInterface ) { try { String id = PluginRegistry.getInstance().getPluginId( StepPluginType.class, stepMetaInterface ); if ( getShell() != null && id != null ) { getShell().setImage( GUIResource.getInstance().getImagesSteps().get( id ).getAsBitmap( shell.getDisplay() ) ); } } catch ( Throwable e ) { // Ignore } } public void dispose() { Shell shell = (Shell) this.xulDialog.getRootObject(); if ( !shell.isDisposed() ) { WindowProperty winprop = new WindowProperty( shell ); props.setScreen( winprop ); ( (Composite) this.xulDialog.getManagedObject() ).dispose(); shell.dispose(); } } public Shell getShell() { return dialogShell; } /** * Set the shell size, based upon the previous time the geometry was saved in the Properties file. */ public void setSize() { setSize( dialogShell ); } /** * Returns the default alignment for the buttons. This is set in the LAF properties with the key * <code>Button_Position</code>. The valid values are: * <UL> * <LI><code>left</code> * <LI><code>center</code> * <LI><code>right</code> * </UL> * NOTE: if the alignment is not provided or contains an invalid value, <code>center</code> will be used as a default * * @return a constant which indicates the button alignment */ protected static int getButtonAlignment() { String buttonAlign = BasePropertyHandler.getProperty( "Button_Position", "center" ).toLowerCase(); if ( "center".equals( buttonAlign ) ) { return BUTTON_ALIGNMENT_CENTER; } else if ( "left".equals( buttonAlign ) ) { return BUTTON_ALIGNMENT_LEFT; } else { return BUTTON_ALIGNMENT_RIGHT; } } protected DatabaseDialog getDatabaseDialog( Shell shell ) { if ( databaseDialog == null ) { databaseDialog = new DatabaseDialog( shell ); } return databaseDialog; } public void storeScreenSize() { props.setScreen( new WindowProperty( dialogShell ) ); } public static void setSize( Shell shell ) { setSize( shell, -1, -1, true ); } public static void setSize( Shell shell, int minWidth, int minHeight, boolean packIt ) { PropsUI props = PropsUI.getInstance(); WindowProperty winprop = props.getScreen( shell.getText() ); if ( winprop != null ) { winprop.setShell( shell, minWidth, minHeight ); } else { if ( packIt ) { shell.pack(); } else { shell.layout(); } // OK, sometimes this produces dialogs that are waay too big. // Try to limit this a bit, m'kay? // Use the same algorithm by cheating :-) // winprop = new WindowProperty( shell ); winprop.setShell( shell, minWidth, minHeight ); // Now, as this is the first time it gets opened, try to put it in the middle of the screen... Rectangle shellBounds = shell.getBounds(); Monitor monitor = shell.getDisplay().getPrimaryMonitor(); if ( shell.getParent() != null ) { monitor = shell.getParent().getMonitor(); } Rectangle monitorClientArea = monitor.getClientArea(); int middleX = monitorClientArea.x + ( monitorClientArea.width - shellBounds.width ) / 2; int middleY = monitorClientArea.y + ( monitorClientArea.height - shellBounds.height ) / 2; shell.setLocation( middleX, middleY ); } } /** * Gets unused fields from previous steps and inserts them as rows into a table view. * * @param row * the input fields * @param tableView * the table view to modify * @param keyColumn * the column in the table view to match with the names of the fields, checks for existance if >0 * @param nameColumn * the column numbers in which the name should end up in * @param dataTypeColumn * the target column numbers in which the data type should end up in * @param lengthColumn * the length column where the length should end up in (if >0) * @param precisionColumn * the length column where the precision should end up in (if >0) * @param listener * A listener that you can use to do custom modifications to the inserted table item, based on a value from * the provided row */ public static final void getFieldsFromPrevious( RowMetaInterface row, XulTree tableView, int keyColumn, int[] nameColumn, int[] dataTypeColumn, int lengthColumn, int precisionColumn, TableItemInsertListener listener ) { if ( row == null || row.size() == 0 ) { return; // nothing to do } Table table = ( (TableViewer) tableView.getManagedObject() ).getTable(); // get a list of all the non-empty keys (names) // List<String> keys = new ArrayList<String>(); for ( int i = 0; i < table.getItemCount(); i++ ) { TableItem tableItem = table.getItem( i ); String key = tableItem.getText( keyColumn ); if ( !Utils.isEmpty( key ) && keys.indexOf( key ) < 0 ) { keys.add( key ); } } int choice = 0; if ( keys.size() > 0 ) { // Ask what we should do with the existing data in the step. // Shell shell = ( (TableViewer) tableView.getManagedObject() ).getTable().getShell(); MessageDialog md = new MessageDialog( shell, BaseMessages.getString( PKG, "BaseStepDialog.GetFieldsChoice.Title" ), // "Warning!" null, BaseMessages.getString( PKG, "BaseStepDialog.GetFieldsChoice.Message", "" + keys.size(), "" + row.size() ), MessageDialog.WARNING, new String[] { BaseMessages.getString( PKG, "BaseStepDialog.AddNew" ), BaseMessages.getString( PKG, "BaseStepDialog.Add" ), BaseMessages.getString( PKG, "BaseStepDialog.ClearAndAdd" ), BaseMessages.getString( PKG, "BaseStepDialog.Cancel" ), }, 0 ); MessageDialog.setDefaultImage( GUIResource.getInstance().getImageSpoon() ); int idx = md.open(); choice = idx & 0xFF; } if ( choice == 3 || choice == 255 ) { return; // Cancel clicked } if ( choice == 2 ) { tableView.getRootChildren().removeAll(); } for ( int i = 0; i < row.size(); i++ ) { ValueMetaInterface v = row.getValueMeta( i ); boolean add = true; if ( choice == 0 ) { // hang on, see if it's not yet in the table view if ( keys.indexOf( v.getName() ) >= 0 ) { add = false; } } if ( add ) { XulTreeRow tRow = tableView.getRootChildren().addNewRow(); for ( int c = 0; c < nameColumn.length; c++ ) { tRow.addCellText( nameColumn[c], Const.NVL( v.getName(), "" ) ); } if ( dataTypeColumn != null ) { for ( int c = 0; c < dataTypeColumn.length; c++ ) { tRow.addCellText( dataTypeColumn[c], v.getTypeDesc() ); } } if ( lengthColumn > 0 ) { if ( v.getLength() >= 0 ) { tRow.addCellText( lengthColumn, Integer.toString( v.getLength() ) ); } } if ( precisionColumn > 0 ) { if ( v.getPrecision() >= 0 ) { tRow.addCellText( precisionColumn, Integer.toString( v.getPrecision() ) ); } } if ( listener != null ) { if ( !listener.tableItemInserted( table.getItem( tRow.getParent().getParent().getChildNodes().indexOf( tRow.getParent() ) ), v ) ) { tRow.getParent().getParent().removeChild( tRow.getParent() ); } } } } // tableView.removeEmptyRows(); // tableView.setRowNums(); // tableView.optWidth(true); } /** * Gets fields from previous steps and populate a ComboVar. * * @param comboVar * the comboVar to populate * @param TransMeta * the source transformation * @param StepMeta * the source step */ public static final void getFieldsFromPrevious( ComboVar comboVar, TransMeta transMeta, StepMeta stepMeta ) { String selectedField = null; int indexField = -1; try { RowMetaInterface r = transMeta.getPrevStepFields( stepMeta ); selectedField = comboVar.getText(); comboVar.removeAll(); if ( r != null && !r.isEmpty() ) { r.getFieldNames(); comboVar.setItems( r.getFieldNames() ); indexField = r.indexOfValue( selectedField ); } // Select value if possible... if ( indexField > -1 ) { comboVar.select( indexField ); } else { if ( selectedField != null ) { comboVar.setText( selectedField ); } } } catch ( KettleException ke ) { new ErrorDialog( comboVar.getShell(), BaseMessages.getString( PKG, "BaseStepDialog.FailedToGetFieldsPrevious.DialogTitle" ), BaseMessages.getString( PKG, "BaseStepDialog.FailedToGetFieldsPrevious.DialogMessage" ), ke ); } } /** * Create a new field mapping between source and target steps. * * @param shell * the shell of the parent window * @param sourceFields * the source fields * @param targetFields * the target fields * @param fieldMapping * the list of source to target mappings to default to (can be empty but not null) * @throws KettleException * in case something goes wrong during the field mapping */ public static final void generateFieldMapping( Shell shell, RowMetaInterface sourceFields, RowMetaInterface targetFields, java.util.List<SourceToTargetMapping> fieldMapping ) throws KettleException { // Build the mapping: let the user decide!! String[] source = sourceFields.getFieldNames(); for ( int i = 0; i < source.length; i++ ) { ValueMetaInterface v = sourceFields.getValueMeta( i ); source[i] += EnterMappingDialog.STRING_ORIGIN_SEPARATOR + v.getOrigin() + ")"; } String[] target = targetFields.getFieldNames(); EnterMappingDialog dialog = new EnterMappingDialog( shell, source, target, fieldMapping ); java.util.List<SourceToTargetMapping> newMapping = dialog.open(); if ( newMapping != null ) { fieldMapping.clear(); fieldMapping.addAll( newMapping ); } } public static void getFieldsFromPrevious( RowMetaInterface row, XulTree tableView, List<Object> fields, StepTableDataObject field, TableItemInsertXulListener listener ) { if ( row == null || row.size() == 0 ) { return; // nothing to do } // get a list of all the non-empty keys (names) // List<String> keys = new ArrayList<String>(); for ( Object entry : fields ) { keys.add( ( (StepTableDataObject) entry ).getName() ); } int choice = 0; if ( keys.size() > 0 ) { // Ask what we should do with the existing data in the step. // Shell shell = ( (TableViewer) tableView.getManagedObject() ).getTable().getShell(); MessageDialog md = new MessageDialog( shell, BaseMessages.getString( PKG, "BaseStepDialog.GetFieldsChoice.Title" ), // "Warning!" null, BaseMessages.getString( PKG, "BaseStepDialog.GetFieldsChoice.Message", "" + keys.size(), "" + row.size() ), MessageDialog.WARNING, new String[] { BaseMessages.getString( PKG, "BaseStepDialog.AddNew" ), BaseMessages.getString( PKG, "BaseStepDialog.Add" ), BaseMessages.getString( PKG, "BaseStepDialog.ClearAndAdd" ), BaseMessages.getString( PKG, "BaseStepDialog.Cancel" ), }, 0 ); MessageDialog.setDefaultImage( GUIResource.getInstance().getImageSpoon() ); int idx = md.open(); choice = idx & 0xFF; } if ( choice == 3 || choice == 255 ) { return; // Cancel clicked } if ( choice == 2 ) { fields.clear(); } for ( int i = 0; i < row.size(); i++ ) { ValueMetaInterface v = row.getValueMeta( i ); if ( choice == 0 ) { // hang on, see if it's not yet in the table view if ( keys.indexOf( v.getName() ) >= 0 ) { continue; } } if ( listener != null && !listener.tableItemInsertedFor( v ) ) { continue; } StepTableDataObject newField = field.createNew( v ); fields.add( newField ); } } public void onHelp() { HelpUtils.openHelpDialog( dialogShell, getPlugin() ); } }