//CHECKSTYLE:FileLength:OFF
/*! ******************************************************************************
*
* 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.ui.spoon.trans;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.window.DefaultToolTip;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.pentaho.di.core.CheckResultInterface;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.EngineMetaInterface;
import org.pentaho.di.core.NotePadMeta;
import org.pentaho.di.core.Props;
import org.pentaho.di.core.SwtUniversalImage;
import org.pentaho.di.core.dnd.DragAndDropContainer;
import org.pentaho.di.core.dnd.XMLTransfer;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettlePluginException;
import org.pentaho.di.core.exception.KettleStepException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.extension.ExtensionPointHandler;
import org.pentaho.di.core.extension.KettleExtensionPoint;
import org.pentaho.di.core.gui.AreaOwner;
import org.pentaho.di.core.gui.AreaOwner.AreaType;
import org.pentaho.di.core.gui.BasePainter;
import org.pentaho.di.core.gui.GCInterface;
import org.pentaho.di.core.gui.Point;
import org.pentaho.di.core.gui.Redrawable;
import org.pentaho.di.core.gui.SnapAllignDistribute;
import org.pentaho.di.core.logging.DefaultLogLevel;
import org.pentaho.di.core.logging.HasLogChannelInterface;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.KettleLoggingEvent;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.logging.LogMessage;
import org.pentaho.di.core.logging.LogParentProvidedInterface;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.logging.LoggingObjectType;
import org.pentaho.di.core.logging.LoggingRegistry;
import org.pentaho.di.core.logging.SimpleLoggingObject;
import org.pentaho.di.core.plugins.EnginePluginType;
import org.pentaho.di.core.plugins.PluginInterface;
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.util.Utils;
import org.pentaho.di.engine.api.Engine;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.Job;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.lineage.TransDataLineage;
import org.pentaho.di.repository.KettleRepositoryLostException;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.repository.RepositoryObjectType;
import org.pentaho.di.repository.RepositoryOperation;
import org.pentaho.di.shared.SharedObjects;
import org.pentaho.di.trans.DatabaseImpact;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransExecutionConfiguration;
import org.pentaho.di.trans.TransHopMeta;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransPainter;
import org.pentaho.di.trans.ael.adapters.TransEngineAdapter;
import org.pentaho.di.trans.debug.BreakPointListener;
import org.pentaho.di.trans.debug.StepDebugMeta;
import org.pentaho.di.trans.debug.TransDebugMeta;
import org.pentaho.di.trans.debug.TransDebugMetaWrapper;
import org.pentaho.di.trans.step.RemoteStep;
import org.pentaho.di.trans.step.RowDistributionInterface;
import org.pentaho.di.trans.step.RowDistributionPluginType;
import org.pentaho.di.trans.step.RowListener;
import org.pentaho.di.trans.step.StepErrorMeta;
import org.pentaho.di.trans.step.StepIOMetaInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaDataCombi;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.trans.step.errorhandling.Stream;
import org.pentaho.di.trans.step.errorhandling.StreamIcon;
import org.pentaho.di.trans.step.errorhandling.StreamInterface;
import org.pentaho.di.trans.step.errorhandling.StreamInterface.StreamType;
import org.pentaho.di.trans.steps.tableinput.TableInputMeta;
import org.pentaho.di.ui.core.ConstUI;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.dialog.DialogClosedListener;
import org.pentaho.di.ui.core.dialog.EnterSelectionDialog;
import org.pentaho.di.ui.core.dialog.EnterStringDialog;
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.dialog.StepFieldsDialog;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.core.widget.CheckBoxToolTip;
import org.pentaho.di.ui.core.widget.CheckBoxToolTipListener;
import org.pentaho.di.ui.repository.RepositorySecurityUI;
import org.pentaho.di.ui.repository.dialog.RepositoryExplorerDialog;
import org.pentaho.di.ui.repository.dialog.RepositoryRevisionBrowserDialogInterface;
import org.pentaho.di.ui.spoon.AbstractGraph;
import org.pentaho.di.ui.spoon.SWTGC;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.spoon.SpoonPluginManager;
import org.pentaho.di.ui.spoon.SpoonUiExtenderPluginInterface;
import org.pentaho.di.ui.spoon.SpoonUiExtenderPluginType;
import org.pentaho.di.ui.spoon.SwtScrollBar;
import org.pentaho.di.ui.spoon.TabItemInterface;
import org.pentaho.di.ui.spoon.XulSpoonResourceBundle;
import org.pentaho.di.ui.spoon.XulSpoonSettingsManager;
import org.pentaho.di.ui.spoon.dialog.EnterPreviewRowsDialog;
import org.pentaho.di.ui.spoon.dialog.NotePadDialog;
import org.pentaho.di.ui.spoon.dialog.SearchFieldsProgressDialog;
import org.pentaho.di.ui.spoon.job.JobGraph;
import org.pentaho.di.ui.trans.dialog.TransDialog;
import org.pentaho.di.ui.xul.KettleXulLoader;
import org.pentaho.ui.xul.XulDomContainer;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.components.XulMenuitem;
import org.pentaho.ui.xul.components.XulToolbarbutton;
import org.pentaho.ui.xul.containers.XulMenu;
import org.pentaho.ui.xul.containers.XulMenupopup;
import org.pentaho.ui.xul.containers.XulToolbar;
import org.pentaho.ui.xul.dom.Document;
import org.pentaho.ui.xul.impl.XulEventHandler;
import org.pentaho.ui.xul.jface.tags.JfaceMenuitem;
import org.pentaho.ui.xul.jface.tags.JfaceMenupopup;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
/**
* This class handles the display of the transformations in a graphical way using icons, arrows, etc. One transformation
* is handled per TransGraph
*
* @author Matt
* @since 17-mei-2003
*/
public class TransGraph extends AbstractGraph implements XulEventHandler, Redrawable, TabItemInterface,
LogParentProvidedInterface, MouseListener, MouseMoveListener, MouseTrackListener, MouseWheelListener, KeyListener {
private static Class<?> PKG = Spoon.class; // for i18n purposes, needed by Translator2!!
private LogChannelInterface log;
private static final int HOP_SEL_MARGIN = 9;
private static final String XUL_FILE_TRANS_TOOLBAR = "ui/trans-toolbar.xul";
public static final String LOAD_TAB = "loadTab";
public static final String PREVIEW_TRANS = "previewTrans";
public static final String START_TEXT = BaseMessages.getString( PKG, "TransLog.Button.StartTransformation" );
public static final String PAUSE_TEXT = BaseMessages.getString( PKG, "TransLog.Button.PauseTransformation" );
public static final String RESUME_TEXT = BaseMessages.getString( PKG, "TransLog.Button.ResumeTransformation" );
public static final String STOP_TEXT = BaseMessages.getString( PKG, "TransLog.Button.StopTransformation" );
public static final String TRANS_GRAPH_ENTRY_SNIFF = "trans-graph-entry-sniff";
public static final String TRANS_GRAPH_ENTRY_AGAIN = "trans-graph-entry-align";
private TransMeta transMeta;
public Trans trans;
private Shell shell;
private Composite mainComposite;
private DefaultToolTip toolTip;
private CheckBoxToolTip helpTip;
private XulToolbar toolbar;
private int iconsize;
private Point lastclick;
private Point lastMove;
private Point[] previous_step_locations;
private Point[] previous_note_locations;
private List<StepMeta> selectedSteps;
private StepMeta selectedStep;
private List<StepMeta> mouseOverSteps;
private List<NotePadMeta> selectedNotes;
private NotePadMeta selectedNote;
private TransHopMeta candidate;
private Point drop_candidate;
private Spoon spoon;
// public boolean shift, control;
private boolean split_hop;
private int lastButton;
private TransHopMeta last_hop_split;
private org.pentaho.di.core.gui.Rectangle selectionRegion;
/**
* A list of remarks on the current Transformation...
*/
private List<CheckResultInterface> remarks;
/**
* A list of impacts of the current transformation on the used databases.
*/
private List<DatabaseImpact> impact;
/**
* Indicates whether or not an impact analysis has already run.
*/
private boolean impactFinished;
private TransDebugMeta lastTransDebugMeta;
private Map<String, XulMenupopup> menuMap = new HashMap<>();
protected int currentMouseX = 0;
protected int currentMouseY = 0;
protected NotePadMeta ni = null;
protected TransHopMeta currentHop;
protected StepMeta currentStep;
private List<AreaOwner> areaOwners;
// private Text filenameLabel;
private SashForm sashForm;
public Composite extraViewComposite;
public CTabFolder extraViewTabFolder;
private boolean initialized;
private boolean running;
private boolean halted;
private boolean halting;
private boolean debug;
private boolean pausing;
public TransLogDelegate transLogDelegate;
public TransGridDelegate transGridDelegate;
public TransHistoryDelegate transHistoryDelegate;
public TransPerfDelegate transPerfDelegate;
public TransMetricsDelegate transMetricsDelegate;
public TransPreviewDelegate transPreviewDelegate;
public List<SelectedStepListener> stepListeners;
public List<StepSelectionListener> currentStepListeners = new ArrayList<>();
/**
* A map that keeps track of which log line was written by which step
*/
private Map<StepMeta, String> stepLogMap;
private StepMeta startHopStep;
private Point endHopLocation;
private boolean startErrorHopStep;
private StepMeta noInputStep;
private StepMeta endHopStep;
private StreamType candidateHopType;
private Map<StepMeta, DelayTimer> delayTimers;
private StepMeta showTargetStreamsStep;
Timer redrawTimer;
public void setCurrentNote( NotePadMeta ni ) {
this.ni = ni;
}
public NotePadMeta getCurrentNote() {
return ni;
}
public TransHopMeta getCurrentHop() {
return currentHop;
}
public void setCurrentHop( TransHopMeta currentHop ) {
this.currentHop = currentHop;
}
public StepMeta getCurrentStep() {
return currentStep;
}
public void setCurrentStep( StepMeta currentStep ) {
this.currentStep = currentStep;
}
public void addSelectedStepListener( SelectedStepListener selectedStepListener ) {
stepListeners.add( selectedStepListener );
}
public void addCurrentStepListener( StepSelectionListener stepSelectionListener ) {
currentStepListeners.add( stepSelectionListener );
}
public TransGraph( Composite parent, final Spoon spoon, final TransMeta transMeta ) {
super( parent, SWT.NONE );
this.shell = parent.getShell();
this.spoon = spoon;
this.transMeta = transMeta;
this.areaOwners = new ArrayList<>();
this.log = spoon.getLog();
spoon.selectionFilter.setText( "" );
this.mouseOverSteps = new ArrayList<>();
this.delayTimers = new HashMap<>();
transLogDelegate = new TransLogDelegate( spoon, this );
transGridDelegate = new TransGridDelegate( spoon, this );
transHistoryDelegate = new TransHistoryDelegate( spoon, this );
transPerfDelegate = new TransPerfDelegate( spoon, this );
transMetricsDelegate = new TransMetricsDelegate( spoon, this );
transPreviewDelegate = new TransPreviewDelegate( spoon, this );
stepListeners = new ArrayList<>();
try {
KettleXulLoader loader = new KettleXulLoader();
loader.setIconsSize( 16, 16 );
loader.setSettingsManager( XulSpoonSettingsManager.getInstance() );
ResourceBundle bundle = new XulSpoonResourceBundle( Spoon.class );
XulDomContainer container = loader.loadXul( XUL_FILE_TRANS_TOOLBAR, bundle );
container.addEventHandler( this );
SpoonPluginManager.getInstance().applyPluginsForContainer( "trans-graph", xulDomContainer );
setXulDomContainer( container );
} catch ( XulException e1 ) {
log.logError( "Error loading XUL resource bundle for Spoon", e1 );
}
setLayout( new FormLayout() );
setLayoutData( new GridData( GridData.FILL_BOTH ) );
// Add a tool-bar at the top of the tab
// The form-data is set on the native widget automatically
//
addToolBar();
setControlStates(); // enable / disable the icons in the toolbar too.
// The main composite contains the graph view, but if needed also
// a view with an extra tab containing log, etc.
//
mainComposite = new Composite( this, SWT.NONE );
mainComposite.setLayout( new FillLayout() );
// Nick's fix below -------
Control toolbarControl = (Control) toolbar.getManagedObject();
FormData toolbarFd = new FormData();
toolbarFd.left = new FormAttachment( 0, 0 );
toolbarFd.right = new FormAttachment( 100, 0 );
toolbarControl.setLayoutData( toolbarFd );
toolbarControl.setParent( this );
// ------------------------
FormData fdMainComposite = new FormData();
fdMainComposite.left = new FormAttachment( 0, 0 );
fdMainComposite.top = new FormAttachment( (Control) toolbar.getManagedObject(), 0 );
fdMainComposite.right = new FormAttachment( 100, 0 );
fdMainComposite.bottom = new FormAttachment( 100, 0 );
mainComposite.setLayoutData( fdMainComposite );
// To allow for a splitter later on, we will add the splitter here...
//
sashForm = new SashForm( mainComposite, SWT.VERTICAL );
// Add a canvas below it, use up all space initially
//
canvas = new Canvas( sashForm, SWT.V_SCROLL | SWT.H_SCROLL | SWT.NO_BACKGROUND | SWT.BORDER );
sashForm.setWeights( new int[] { 100, } );
try {
// first get the XML document
menuMap.put( "trans-graph-hop", (XulMenupopup) getXulDomContainer().getDocumentRoot().getElementById(
"trans-graph-hop" ) );
menuMap.put( "trans-graph-entry", (XulMenupopup) getXulDomContainer().getDocumentRoot().getElementById(
"trans-graph-entry" ) );
menuMap.put( "trans-graph-background", (XulMenupopup) getXulDomContainer().getDocumentRoot().getElementById(
"trans-graph-background" ) );
menuMap.put( "trans-graph-note", (XulMenupopup) getXulDomContainer().getDocumentRoot().getElementById(
"trans-graph-note" ) );
} catch ( Throwable t ) {
log.logError( "Error parsing XUL XML", t );
}
toolTip = new DefaultToolTip( canvas, ToolTip.NO_RECREATE, true );
toolTip.setRespectMonitorBounds( true );
toolTip.setRespectDisplayBounds( true );
toolTip.setPopupDelay( 350 );
toolTip.setHideDelay( 5000 );
toolTip.setShift( new org.eclipse.swt.graphics.Point( ConstUI.TOOLTIP_OFFSET, ConstUI.TOOLTIP_OFFSET ) );
helpTip = new CheckBoxToolTip( canvas );
helpTip.addCheckBoxToolTipListener( new CheckBoxToolTipListener() {
@Override
public void checkBoxSelected( boolean enabled ) {
spoon.props.setShowingHelpToolTips( enabled );
}
} );
iconsize = spoon.props.getIconSize();
clearSettings();
remarks = new ArrayList<>();
impact = new ArrayList<>();
impactFinished = false;
hori = canvas.getHorizontalBar();
vert = canvas.getVerticalBar();
hori.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
redraw();
}
} );
vert.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
redraw();
}
} );
hori.setThumb( 100 );
vert.setThumb( 100 );
hori.setVisible( true );
vert.setVisible( true );
setVisible( true );
newProps();
canvas.setBackground( GUIResource.getInstance().getColorBackground() );
canvas.addPaintListener( new PaintListener() {
@Override
public void paintControl( PaintEvent e ) {
if ( !spoon.isStopped() ) {
TransGraph.this.paintControl( e );
}
}
} );
selectedSteps = null;
lastclick = null;
/*
* Handle the mouse...
*/
canvas.addMouseListener( this );
canvas.addMouseMoveListener( this );
canvas.addMouseTrackListener( this );
canvas.addMouseWheelListener( this );
canvas.addKeyListener( this );
// Drag & Drop for steps
Transfer[] ttypes = new Transfer[] { XMLTransfer.getInstance() };
DropTarget ddTarget = new DropTarget( canvas, DND.DROP_MOVE );
ddTarget.setTransfer( ttypes );
ddTarget.addDropListener( new DropTargetListener() {
@Override
public void dragEnter( DropTargetEvent event ) {
clearSettings();
drop_candidate = PropsUI.calculateGridPosition( getRealPosition( canvas, event.x, event.y ) );
redraw();
}
@Override
public void dragLeave( DropTargetEvent event ) {
drop_candidate = null;
redraw();
}
@Override
public void dragOperationChanged( DropTargetEvent event ) {
}
@Override
public void dragOver( DropTargetEvent event ) {
drop_candidate = PropsUI.calculateGridPosition( getRealPosition( canvas, event.x, event.y ) );
redraw();
}
@Override
public void drop( DropTargetEvent event ) {
// no data to copy, indicate failure in event.detail
if ( event.data == null ) {
event.detail = DND.DROP_NONE;
return;
}
// System.out.println("Dropping a step!!");
// What's the real drop position?
Point p = getRealPosition( canvas, event.x, event.y );
//
// We expect a Drag and Drop container... (encased in XML)
try {
DragAndDropContainer container = (DragAndDropContainer) event.data;
StepMeta stepMeta = null;
boolean newstep = false;
switch ( container.getType() ) {
// Put an existing one on the canvas.
case DragAndDropContainer.TYPE_STEP:
// Drop hidden step onto canvas....
stepMeta = transMeta.findStep( container.getData() );
if ( stepMeta != null ) {
if ( stepMeta.isDrawn() || transMeta.isStepUsedInTransHops( stepMeta ) ) {
MessageBox mb = new MessageBox( shell, SWT.OK );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.StepIsAlreadyOnCanvas.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.StepIsAlreadyOnCanvas.Title" ) );
mb.open();
return;
}
// This step gets the drawn attribute and position set below.
} else {
// Unknown step dropped: ignore this to be safe!
return;
}
break;
// Create a new step
case DragAndDropContainer.TYPE_BASE_STEP_TYPE:
// Not an existing step: data refers to the type of step to create
String steptype = container.getData();
stepMeta = spoon.newStep( transMeta, steptype, steptype, false, true );
if ( stepMeta != null ) {
newstep = true;
} else {
return; // Cancelled pressed in dialog or unable to create step.
}
break;
// Create a new TableInput step using the selected connection...
case DragAndDropContainer.TYPE_DATABASE_CONNECTION:
newstep = true;
String connectionName = container.getData();
TableInputMeta tii = new TableInputMeta();
tii.setDatabaseMeta( transMeta.findDatabase( connectionName ) );
PluginRegistry registry = PluginRegistry.getInstance();
String stepID = registry.getPluginId( StepPluginType.class, tii );
PluginInterface stepPlugin = registry.findPluginWithId( StepPluginType.class, stepID );
String stepName = transMeta.getAlternativeStepname( stepPlugin.getName() );
stepMeta = new StepMeta( stepID, stepName, tii );
if ( spoon.editStep( transMeta, stepMeta ) != null ) {
transMeta.addStep( stepMeta );
spoon.refreshTree();
spoon.refreshGraph();
} else {
return;
}
break;
// Drag hop on the canvas: create a new Hop...
case DragAndDropContainer.TYPE_TRANS_HOP:
newHop();
return;
default:
// Nothing we can use: give an error!
MessageBox mb = new MessageBox( shell, SWT.OK );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.ItemCanNotBePlacedOnCanvas.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.ItemCanNotBePlacedOnCanvas.Title" ) );
mb.open();
return;
}
transMeta.unselectAll();
StepMeta before = null;
if ( !newstep ) {
before = (StepMeta) stepMeta.clone();
}
stepMeta.drawStep();
stepMeta.setSelected( true );
PropsUI.setLocation( stepMeta, p.x, p.y );
if ( newstep ) {
spoon.addUndoNew( transMeta, new StepMeta[] { stepMeta }, new int[] { transMeta.indexOfStep( stepMeta ) } );
} else {
spoon.addUndoChange( transMeta, new StepMeta[] { before }, new StepMeta[] { (StepMeta) stepMeta.clone() },
new int[] { transMeta.indexOfStep( stepMeta ) } );
}
canvas.forceFocus();
redraw();
// See if we want to draw a tool tip explaining how to create new hops...
//
if ( newstep && transMeta.nrSteps() > 1 && transMeta.nrSteps() < 5 && spoon.props.isShowingHelpToolTips() ) {
showHelpTip( p.x, p.y, BaseMessages.getString( PKG, "TransGraph.HelpToolTip.CreatingHops.Title" ),
BaseMessages.getString( PKG, "TransGraph.HelpToolTip.CreatingHops.Message" ) );
}
} catch ( Exception e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.Dialog.ErrorDroppingObject.Message" ),
BaseMessages.getString( PKG, "TransGraph.Dialog.ErrorDroppingObject.Title" ), e );
}
}
@Override
public void dropAccept( DropTargetEvent event ) {
}
} );
setBackground( GUIResource.getInstance().getColorBackground() );
// Add a timer to set correct the state of the run/stop buttons every 2 seconds...
//
final Timer timer = new Timer( "TransGraph.setControlStates Timer: " + getMeta().getName() );
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
try {
setControlStates();
} catch ( KettleRepositoryLostException krle ) {
if ( log.isBasic() ) {
log.logBasic( krle.getLocalizedMessage() );
}
}
}
};
timer.schedule( timerTask, 2000, 1000 );
// Make sure the timer stops when we close the tab...
//
addDisposeListener( new DisposeListener() {
@Override
public void widgetDisposed( DisposeEvent arg0 ) {
timer.cancel();
}
} );
}
@Override
public void mouseDoubleClick( MouseEvent e ) {
clearSettings();
Point real = screen2real( e.x, e.y );
// Hide the tooltip!
hideToolTips();
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransGraphMouseDoubleClick.id,
new TransGraphExtension( this, e, real ) );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransGraphMouseDoubleClick extension point", ex );
}
StepMeta stepMeta = transMeta.getStep( real.x, real.y, iconsize );
if ( stepMeta != null ) {
if ( e.button == 1 ) {
editStep( stepMeta );
} else {
editDescription( stepMeta );
}
} else {
// Check if point lies on one of the many hop-lines...
TransHopMeta online = findHop( real.x, real.y );
if ( online != null ) {
editHop( online );
} else {
NotePadMeta ni = transMeta.getNote( real.x, real.y );
if ( ni != null ) {
selectedNote = null;
editNote( ni );
} else {
// See if the double click was in one of the area's...
//
boolean hit = false;
for ( AreaOwner areaOwner : areaOwners ) {
if ( areaOwner.contains( real.x, real.y ) ) {
if ( areaOwner.getParent() instanceof StepMeta
&& areaOwner.getOwner().equals( TransPainter.STRING_PARTITIONING_CURRENT_STEP ) ) {
StepMeta step = (StepMeta) areaOwner.getParent();
spoon.editPartitioning( transMeta, step );
hit = true;
break;
}
}
}
if ( !hit ) {
settings();
}
}
}
}
}
@Override
public void mouseDown( MouseEvent e ) {
boolean alt = ( e.stateMask & SWT.ALT ) != 0;
boolean control = ( e.stateMask & SWT.MOD1 ) != 0;
boolean shift = ( e.stateMask & SWT.SHIFT ) != 0;
lastButton = e.button;
Point real = screen2real( e.x, e.y );
lastclick = new Point( real.x, real.y );
// Hide the tooltip!
hideToolTips();
// Set the pop-up menu
if ( e.button == 3 ) {
setMenu( real.x, real.y );
return;
}
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransGraphMouseDown.id,
new TransGraphExtension( this, e, real ) );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransGraphMouseDown extension point", ex );
}
// A single left or middle click on one of the area owners...
//
if ( e.button == 1 || e.button == 2 ) {
AreaOwner areaOwner = getVisibleAreaOwner( real.x, real.y );
if ( areaOwner != null && areaOwner.getAreaType() != null ) {
switch ( areaOwner.getAreaType() ) {
case STEP_OUTPUT_HOP_ICON:
// Click on the output icon means: start of drag
// Action: We show the input icons on the other steps...
//
selectedStep = null;
startHopStep = (StepMeta) areaOwner.getParent();
candidateHopType = null;
startErrorHopStep = false;
// stopStepMouseOverDelayTimer(startHopStep);
break;
case STEP_INPUT_HOP_ICON:
// Click on the input icon means: start to a new hop
// In this case, we set the end hop step...
//
selectedStep = null;
startHopStep = null;
endHopStep = (StepMeta) areaOwner.getParent();
candidateHopType = null;
startErrorHopStep = false;
// stopStepMouseOverDelayTimer(endHopStep);
break;
case HOP_ERROR_ICON:
// Click on the error icon means: Edit error handling
//
StepMeta stepMeta = (StepMeta) areaOwner.getParent();
spoon.editStepErrorHandling( transMeta, stepMeta );
break;
case STEP_TARGET_HOP_ICON_OPTION:
// Below, see showStepTargetOptions()
break;
case STEP_EDIT_ICON:
clearSettings();
currentStep = (StepMeta) areaOwner.getParent();
stopStepMouseOverDelayTimer( currentStep );
editStep();
break;
case STEP_INJECT_ICON:
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_INFORMATION );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.StepInjectionSupported.Tooltip" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.StepInjectionSupported.Title" ) );
mb.open();
break;
case STEP_MENU_ICON:
clearSettings();
stepMeta = (StepMeta) areaOwner.getParent();
setMenu( stepMeta.getLocation().x, stepMeta.getLocation().y );
break;
case STEP_ICON:
stepMeta = (StepMeta) areaOwner.getOwner();
currentStep = stepMeta;
for ( StepSelectionListener listener : currentStepListeners ) {
listener.onUpdateSelection( currentStep );
}
if ( candidate != null ) {
addCandidateAsHop( e.x, e.y );
} else {
if ( transPreviewDelegate.isActive() ) {
transPreviewDelegate.setSelectedStep( currentStep );
for ( SelectedStepListener stepListener : stepListeners ) {
if ( this.extraViewComposite != null && !this.extraViewComposite.isDisposed() ) {
stepListener.onSelect( currentStep );
}
}
transPreviewDelegate.refreshView();
}
}
// ALT-Click: edit error handling
//
if ( e.button == 1 && alt && stepMeta.supportsErrorHandling() ) {
spoon.editStepErrorHandling( transMeta, stepMeta );
return;
} else if ( e.button == 2 || ( e.button == 1 && shift ) ) {
// SHIFT CLICK is start of drag to create a new hop
//
startHopStep = stepMeta;
} else {
selectedSteps = transMeta.getSelectedSteps();
selectedStep = stepMeta;
//
// When an icon is moved that is not selected, it gets
// selected too late.
// It is not captured here, but in the mouseMoveListener...
//
previous_step_locations = transMeta.getSelectedStepLocations();
Point p = stepMeta.getLocation();
iconoffset = new Point( real.x - p.x, real.y - p.y );
}
redraw();
break;
case NOTE:
ni = (NotePadMeta) areaOwner.getOwner();
selectedNotes = transMeta.getSelectedNotes();
selectedNote = ni;
Point loc = ni.getLocation();
previous_note_locations = transMeta.getSelectedNoteLocations();
noteoffset = new Point( real.x - loc.x, real.y - loc.y );
redraw();
break;
case STEP_COPIES_TEXT:
copies( (StepMeta) areaOwner.getOwner() );
break;
case STEP_DATA_SERVICE:
editProperties( transMeta, spoon, spoon.getRepository(), true, TransDialog.Tabs.EXTRA_TAB );
break;
default:
break;
}
} else {
// A hop? --> enable/disable
//
TransHopMeta hop = findHop( real.x, real.y );
if ( hop != null ) {
TransHopMeta before = (TransHopMeta) hop.clone();
hop.setEnabled( !hop.isEnabled() );
TransHopMeta after = (TransHopMeta) hop.clone();
spoon.addUndoChange( transMeta, new TransHopMeta[] { before }, new TransHopMeta[] { after },
new int[] { transMeta.indexOfTransHop( hop ) } );
redraw();
spoon.setShellText();
} else {
// No area-owner & no hop means : background click:
//
startHopStep = null;
if ( !control ) {
selectionRegion = new org.pentaho.di.core.gui.Rectangle( real.x, real.y, 0, 0 );
}
redraw();
}
}
}
}
@Override
public void mouseUp( MouseEvent e ) {
try {
TransGraphExtension ext = new TransGraphExtension( null, e, getArea() );
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransGraphMouseUp.id, ext );
if ( ext.isPreventDefault() ) {
redraw();
clearSettings();
return;
}
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransGraphMouseUp extension point", ex );
}
boolean control = ( e.stateMask & SWT.MOD1 ) != 0;
TransHopMeta selectedHop = findHop( e.x, e.y );
updateErrorMetaForHop( selectedHop );
if ( iconoffset == null ) {
iconoffset = new Point( 0, 0 );
}
Point real = screen2real( e.x, e.y );
Point icon = new Point( real.x - iconoffset.x, real.y - iconoffset.y );
AreaOwner areaOwner = getVisibleAreaOwner( real.x, real.y );
try {
TransGraphExtension ext = new TransGraphExtension( this, e, real );
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransGraphMouseUp.id, ext );
if ( ext.isPreventDefault() ) {
redraw();
clearSettings();
return;
}
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransGraphMouseUp extension point", ex );
}
// Quick new hop option? (drag from one step to another)
//
if ( candidate != null && areaOwner != null && areaOwner.getAreaType() != null ) {
switch ( areaOwner.getAreaType() ) {
case STEP_ICON:
currentStep = (StepMeta) areaOwner.getOwner();
break;
case STEP_INPUT_HOP_ICON:
currentStep = (StepMeta) areaOwner.getParent();
break;
default:
break;
}
addCandidateAsHop( e.x, e.y );
redraw();
} else {
// Did we select a region on the screen? Mark steps in region as
// selected
//
if ( selectionRegion != null ) {
selectionRegion.width = real.x - selectionRegion.x;
selectionRegion.height = real.y - selectionRegion.y;
transMeta.unselectAll();
selectInRect( transMeta, selectionRegion );
selectionRegion = null;
stopStepMouseOverDelayTimers();
redraw();
} else {
// Clicked on an icon?
//
if ( selectedStep != null && startHopStep == null ) {
if ( e.button == 1 ) {
Point realclick = screen2real( e.x, e.y );
if ( lastclick.x == realclick.x && lastclick.y == realclick.y ) {
// Flip selection when control is pressed!
if ( control ) {
selectedStep.flipSelected();
} else {
// Otherwise, select only the icon clicked on!
transMeta.unselectAll();
selectedStep.setSelected( true );
}
} else {
// Find out which Steps & Notes are selected
selectedSteps = transMeta.getSelectedSteps();
selectedNotes = transMeta.getSelectedNotes();
// We moved around some items: store undo info...
//
boolean also = false;
if ( selectedNotes != null && selectedNotes.size() > 0 && previous_note_locations != null ) {
int[] indexes = transMeta.getNoteIndexes( selectedNotes );
addUndoPosition( selectedNotes.toArray( new NotePadMeta[ selectedNotes.size() ] ), indexes,
previous_note_locations, transMeta.getSelectedNoteLocations(), also );
also = selectedSteps != null && selectedSteps.size() > 0;
}
if ( selectedSteps != null && previous_step_locations != null ) {
int[] indexes = transMeta.getStepIndexes( selectedSteps );
addUndoPosition( selectedSteps.toArray( new StepMeta[ selectedSteps.size() ] ), indexes,
previous_step_locations, transMeta.getSelectedStepLocations(), also );
}
}
}
// OK, we moved the step, did we move it across a hop?
// If so, ask to split the hop!
if ( split_hop ) {
TransHopMeta hi = findHop( icon.x + iconsize / 2, icon.y + iconsize / 2, selectedStep );
if ( hi != null ) {
splitHop( hi );
}
split_hop = false;
}
selectedSteps = null;
selectedNotes = null;
selectedStep = null;
selectedNote = null;
startHopStep = null;
endHopLocation = null;
redraw();
spoon.setShellText();
} else {
// Notes?
//
if ( selectedNote != null ) {
if ( e.button == 1 ) {
if ( lastclick.x == e.x && lastclick.y == e.y ) {
// Flip selection when control is pressed!
if ( control ) {
selectedNote.flipSelected();
} else {
// Otherwise, select only the note clicked on!
transMeta.unselectAll();
selectedNote.setSelected( true );
}
} else {
// Find out which Steps & Notes are selected
selectedSteps = transMeta.getSelectedSteps();
selectedNotes = transMeta.getSelectedNotes();
// We moved around some items: store undo info...
boolean also = false;
if ( selectedNotes != null && selectedNotes.size() > 0 && previous_note_locations != null ) {
int[] indexes = transMeta.getNoteIndexes( selectedNotes );
addUndoPosition( selectedNotes.toArray( new NotePadMeta[ selectedNotes.size() ] ), indexes,
previous_note_locations, transMeta.getSelectedNoteLocations(), also );
also = selectedSteps != null && selectedSteps.size() > 0;
}
if ( selectedSteps != null && selectedSteps.size() > 0 && previous_step_locations != null ) {
int[] indexes = transMeta.getStepIndexes( selectedSteps );
addUndoPosition( selectedSteps.toArray( new StepMeta[ selectedSteps.size() ] ), indexes,
previous_step_locations, transMeta.getSelectedStepLocations(), also );
}
}
}
selectedNotes = null;
selectedSteps = null;
selectedStep = null;
selectedNote = null;
startHopStep = null;
endHopLocation = null;
} else {
if ( areaOwner == null && selectionRegion == null ) {
// Hit absolutely nothing: clear the settings
//
clearSettings();
}
}
}
}
}
lastButton = 0;
}
private void splitHop( TransHopMeta hi ) {
int id = 0;
if ( !spoon.props.getAutoSplit() ) {
MessageDialogWithToggle md =
new MessageDialogWithToggle( shell, BaseMessages.getString( PKG, "TransGraph.Dialog.SplitHop.Title" ), null,
BaseMessages.getString( PKG, "TransGraph.Dialog.SplitHop.Message" ) + Const.CR + hi.toString(),
MessageDialog.QUESTION, new String[] { BaseMessages.getString( PKG, "System.Button.Yes" ),
BaseMessages.getString( PKG, "System.Button.No" ) }, 0, BaseMessages.getString( PKG,
"TransGraph.Dialog.Option.SplitHop.DoNotAskAgain" ), spoon.props.getAutoSplit() );
MessageDialogWithToggle.setDefaultImage( GUIResource.getInstance().getImageSpoon() );
id = md.open();
spoon.props.setAutoSplit( md.getToggleState() );
}
if ( ( id & 0xFF ) == 0 ) { // Means: "Yes" button clicked!
// Only split A-->--B by putting C in between IF...
// C-->--A or B-->--C don't exists...
// A ==> hi.getFromStep()
// B ==> hi.getToStep();
// C ==> selected_step
//
boolean caExists = transMeta.findTransHop( selectedStep, hi.getFromStep() ) != null;
boolean bcExists = transMeta.findTransHop( hi.getToStep(), selectedStep ) != null;
if ( !caExists && !bcExists ) {
StepMeta fromStep = hi.getFromStep();
StepMeta toStep = hi.getToStep();
// In case step A targets B then we now need to target C
//
StepIOMetaInterface fromIo = fromStep.getStepMetaInterface().getStepIOMeta();
for ( StreamInterface stream : fromIo.getTargetStreams() ) {
if ( stream.getStepMeta() != null && stream.getStepMeta().equals( toStep ) ) {
// This target stream was directed to B, now we need to direct it to C
stream.setStepMeta( selectedStep );
fromStep.getStepMetaInterface().handleStreamSelection( stream );
}
}
// In case step B sources from A then we now need to source from C
//
StepIOMetaInterface toIo = toStep.getStepMetaInterface().getStepIOMeta();
for ( StreamInterface stream : toIo.getInfoStreams() ) {
if ( stream.getStepMeta() != null && stream.getStepMeta().equals( fromStep ) ) {
// This info stream was reading from B, now we need to direct it to C
stream.setStepMeta( selectedStep );
toStep.getStepMetaInterface().handleStreamSelection( stream );
}
}
// In case there is error handling on A, we want to make it point to C now
//
StepErrorMeta errorMeta = fromStep.getStepErrorMeta();
if ( fromStep.isDoingErrorHandling() && toStep.equals( errorMeta.getTargetStep() ) ) {
errorMeta.setTargetStep( selectedStep );
}
TransHopMeta newhop1 = new TransHopMeta( hi.getFromStep(), selectedStep );
if ( transMeta.findTransHop( newhop1 ) == null ) {
transMeta.addTransHop( newhop1 );
spoon.addUndoNew( transMeta, new TransHopMeta[] { newhop1, },
new int[] { transMeta.indexOfTransHop( newhop1 ), }, true );
}
TransHopMeta newhop2 = new TransHopMeta( selectedStep, hi.getToStep() );
if ( transMeta.findTransHop( newhop2 ) == null ) {
transMeta.addTransHop( newhop2 );
spoon.addUndoNew( transMeta, new TransHopMeta[] { newhop2 },
new int[] { transMeta.indexOfTransHop( newhop2 ) }, true );
}
int idx = transMeta.indexOfTransHop( hi );
spoon.addUndoDelete( transMeta, new TransHopMeta[] { hi }, new int[] { idx }, true );
transMeta.removeTransHop( idx );
spoon.refreshTree();
}
// else: Silently discard this hop-split attempt.
}
}
@Override
public void mouseMove( MouseEvent e ) {
boolean shift = ( e.stateMask & SWT.SHIFT ) != 0;
noInputStep = null;
// disable the tooltip
//
toolTip.hide();
Point real = screen2real( e.x, e.y );
// Remember the last position of the mouse for paste with keyboard
//
lastMove = real;
if ( iconoffset == null ) {
iconoffset = new Point( 0, 0 );
}
Point icon = new Point( real.x - iconoffset.x, real.y - iconoffset.y );
if ( noteoffset == null ) {
noteoffset = new Point( 0, 0 );
}
Point note = new Point( real.x - noteoffset.x, real.y - noteoffset.y );
// Moved over an area?
//
AreaOwner areaOwner = getVisibleAreaOwner( real.x, real.y );
if ( areaOwner != null && areaOwner.getAreaType() != null ) {
switch ( areaOwner.getAreaType() ) {
case STEP_ICON:
StepMeta stepMeta = (StepMeta) areaOwner.getOwner();
resetDelayTimer( stepMeta );
break;
case MINI_ICONS_BALLOON: // Give the timer a bit more time
stepMeta = (StepMeta) areaOwner.getParent();
resetDelayTimer( stepMeta );
break;
default:
break;
}
}
try {
TransGraphExtension ext = new TransGraphExtension( this, e, real );
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, KettleExtensionPoint.TransGraphMouseMoved.id, ext );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransGraphMouseMoved extension point", ex );
}
//
// First see if the icon we clicked on was selected.
// If the icon was not selected, we should un-select all other
// icons, selected and move only the one icon
//
if ( selectedStep != null && !selectedStep.isSelected() ) {
transMeta.unselectAll();
selectedStep.setSelected( true );
selectedSteps = new ArrayList<>();
selectedSteps.add( selectedStep );
previous_step_locations = new Point[] { selectedStep.getLocation() };
redraw();
} else if ( selectedNote != null && !selectedNote.isSelected() ) {
transMeta.unselectAll();
selectedNote.setSelected( true );
selectedNotes = new ArrayList<>();
selectedNotes.add( selectedNote );
previous_note_locations = new Point[] { selectedNote.getLocation() };
redraw();
} else if ( selectionRegion != null && startHopStep == null ) {
// Did we select a region...?
//
selectionRegion.width = real.x - selectionRegion.x;
selectionRegion.height = real.y - selectionRegion.y;
redraw();
} else if ( selectedStep != null && lastButton == 1 && !shift && startHopStep == null ) {
//
// One or more icons are selected and moved around...
//
// new : new position of the ICON (not the mouse pointer) dx : difference with previous position
//
int dx = icon.x - selectedStep.getLocation().x;
int dy = icon.y - selectedStep.getLocation().y;
// See if we have a hop-split candidate
//
TransHopMeta hi = findHop( icon.x + iconsize / 2, icon.y + iconsize / 2, selectedStep );
if ( hi != null ) {
// OK, we want to split the hop in 2
//
if ( !hi.getFromStep().equals( selectedStep ) && !hi.getToStep().equals( selectedStep ) ) {
split_hop = true;
last_hop_split = hi;
hi.split = true;
}
} else {
if ( last_hop_split != null ) {
last_hop_split.split = false;
last_hop_split = null;
split_hop = false;
}
}
selectedNotes = transMeta.getSelectedNotes();
selectedSteps = transMeta.getSelectedSteps();
// Adjust location of selected steps...
if ( selectedSteps != null ) {
for ( int i = 0; i < selectedSteps.size(); i++ ) {
StepMeta stepMeta = selectedSteps.get( i );
PropsUI.setLocation( stepMeta, stepMeta.getLocation().x + dx, stepMeta.getLocation().y + dy );
stopStepMouseOverDelayTimer( stepMeta );
}
}
// Adjust location of selected hops...
if ( selectedNotes != null ) {
for ( int i = 0; i < selectedNotes.size(); i++ ) {
NotePadMeta ni = selectedNotes.get( i );
PropsUI.setLocation( ni, ni.getLocation().x + dx, ni.getLocation().y + dy );
}
}
redraw();
} else if ( ( startHopStep != null && endHopStep == null ) || ( endHopStep != null && startHopStep == null ) ) {
// Are we creating a new hop with the middle button or pressing SHIFT?
//
StepMeta stepMeta = transMeta.getStep( real.x, real.y, iconsize );
endHopLocation = new Point( real.x, real.y );
if ( stepMeta != null
&& ( ( startHopStep != null && !startHopStep.equals( stepMeta ) ) || ( endHopStep != null && !endHopStep
.equals( stepMeta ) ) ) ) {
StepIOMetaInterface ioMeta = stepMeta.getStepMetaInterface().getStepIOMeta();
if ( candidate == null ) {
// See if the step accepts input. If not, we can't create a new hop...
//
if ( startHopStep != null ) {
if ( ioMeta.isInputAcceptor() ) {
candidate = new TransHopMeta( startHopStep, stepMeta );
endHopLocation = null;
} else {
noInputStep = stepMeta;
toolTip.setImage( null );
toolTip.setText( "This step does not accept any input from other steps" );
toolTip.show( new org.eclipse.swt.graphics.Point( real.x, real.y ) );
}
} else if ( endHopStep != null ) {
if ( ioMeta.isOutputProducer() ) {
candidate = new TransHopMeta( stepMeta, endHopStep );
endHopLocation = null;
} else {
noInputStep = stepMeta;
toolTip.setImage( null );
toolTip
.setText( "This step doesn't pass any output to other steps. (except perhaps for targetted output)" );
toolTip.show( new org.eclipse.swt.graphics.Point( real.x, real.y ) );
}
}
}
} else {
if ( candidate != null ) {
candidate = null;
redraw();
}
}
redraw();
}
// Move around notes & steps
//
if ( selectedNote != null ) {
if ( lastButton == 1 && !shift ) {
/*
* One or more notes are selected and moved around...
*
* new : new position of the note (not the mouse pointer) dx : difference with previous position
*/
int dx = note.x - selectedNote.getLocation().x;
int dy = note.y - selectedNote.getLocation().y;
selectedNotes = transMeta.getSelectedNotes();
selectedSteps = transMeta.getSelectedSteps();
// Adjust location of selected steps...
if ( selectedSteps != null ) {
for ( int i = 0; i < selectedSteps.size(); i++ ) {
StepMeta stepMeta = selectedSteps.get( i );
PropsUI.setLocation( stepMeta, stepMeta.getLocation().x + dx, stepMeta.getLocation().y + dy );
}
}
// Adjust location of selected hops...
if ( selectedNotes != null ) {
for ( int i = 0; i < selectedNotes.size(); i++ ) {
NotePadMeta ni = selectedNotes.get( i );
PropsUI.setLocation( ni, ni.getLocation().x + dx, ni.getLocation().y + dy );
}
}
redraw();
}
}
}
@Override
public void mouseHover( MouseEvent e ) {
boolean tip = true;
toolTip.hide();
Point real = screen2real( e.x, e.y );
AreaOwner areaOwner = getVisibleAreaOwner( real.x, real.y );
if ( areaOwner != null && areaOwner.getAreaType() != null ) {
switch ( areaOwner.getAreaType() ) {
case STEP_ICON:
StepMeta stepMeta = (StepMeta) areaOwner.getOwner();
if ( !stepMeta.isMissing() && !mouseOverSteps.contains( stepMeta ) ) {
addStepMouseOverDelayTimer( stepMeta );
redraw();
tip = false;
}
break;
default:
break;
}
}
if ( tip ) {
// Show a tool tip upon mouse-over of an object on the canvas
//
if ( !helpTip.isVisible() ) {
setToolTip( real.x, real.y, e.x, e.y );
}
}
}
@Override
public void mouseScrolled( MouseEvent e ) {
/*
* if (e.count == 3) { // scroll up zoomIn(); } else if (e.count == -3) { // scroll down zoomOut(); } }
*/
}
private void addCandidateAsHop( int mouseX, int mouseY ) {
boolean forward = startHopStep != null;
StepMeta fromStep = candidate.getFromStep();
StepMeta toStep = candidate.getToStep();
// See what the options are.
// - Does the source step has multiple stream options?
// - Does the target step have multiple input stream options?
//
List<StreamInterface> streams = new ArrayList<>();
StepIOMetaInterface fromIoMeta = fromStep.getStepMetaInterface().getStepIOMeta();
List<StreamInterface> targetStreams = fromIoMeta.getTargetStreams();
if ( forward ) {
streams.addAll( targetStreams );
}
StepIOMetaInterface toIoMeta = toStep.getStepMetaInterface().getStepIOMeta();
List<StreamInterface> infoStreams = toIoMeta.getInfoStreams();
if ( !forward ) {
streams.addAll( infoStreams );
}
if ( forward ) {
if ( fromIoMeta.isOutputProducer() && toStep.equals( currentStep ) ) {
streams.add( new Stream( StreamType.OUTPUT, fromStep, BaseMessages
.getString( PKG, "Spoon.Hop.MainOutputOfStep" ), StreamIcon.OUTPUT, null ) );
}
if ( fromStep.supportsErrorHandling() && toStep.equals( currentStep ) ) {
streams.add( new Stream( StreamType.ERROR, fromStep, BaseMessages.getString( PKG,
"Spoon.Hop.ErrorHandlingOfStep" ), StreamIcon.ERROR, null ) );
}
} else {
if ( toIoMeta.isInputAcceptor() && fromStep.equals( currentStep ) ) {
streams.add( new Stream( StreamType.INPUT, toStep, BaseMessages.getString( PKG, "Spoon.Hop.MainInputOfStep" ),
StreamIcon.INPUT, null ) );
}
if ( fromStep.supportsErrorHandling() && fromStep.equals( currentStep ) ) {
streams.add( new Stream( StreamType.ERROR, fromStep, BaseMessages.getString( PKG,
"Spoon.Hop.ErrorHandlingOfStep" ), StreamIcon.ERROR, null ) );
}
}
// Targets can be dynamically added to this step...
//
if ( forward ) {
streams.addAll( fromStep.getStepMetaInterface().getOptionalStreams() );
} else {
streams.addAll( toStep.getStepMetaInterface().getOptionalStreams() );
}
// Show a list of options on the canvas...
//
if ( streams.size() > 1 ) {
// Show a pop-up menu with all the possible options...
//
Menu menu = new Menu( canvas );
for ( final StreamInterface stream : streams ) {
MenuItem item = new MenuItem( menu, SWT.NONE );
item.setText( Const.NVL( stream.getDescription(), "" ) );
item.setImage( getImageFor( stream ) );
item.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
addHop( stream );
}
} );
}
menu.setLocation( canvas.toDisplay( mouseX, mouseY ) );
menu.setVisible( true );
return;
}
if ( streams.size() == 1 ) {
addHop( streams.get( 0 ) );
} else {
return;
}
/*
*
* if (transMeta.findTransHop(candidate) == null) { spoon.newHop(transMeta, candidate); } if (startErrorHopStep) {
* addErrorHop(); } if (startTargetHopStream != null) { // Auto-configure the target in the source step... //
* startTargetHopStream.setStepMeta(candidate.getToStep());
* startTargetHopStream.setStepname(candidate.getToStep().getName()); startTargetHopStream = null; }
*/
candidate = null;
selectedSteps = null;
startHopStep = null;
endHopLocation = null;
startErrorHopStep = false;
// redraw();
}
private Image getImageFor( StreamInterface stream ) {
Display disp = shell.getDisplay();
SwtUniversalImage swtImage = SWTGC.getNativeImage( BasePainter.getStreamIconImage( stream.getStreamIcon() ) );
return swtImage.getAsBitmapForSize( disp, ConstUI.SMALL_ICON_SIZE, ConstUI.SMALL_ICON_SIZE );
}
protected void addHop( StreamInterface stream ) {
switch ( stream.getStreamType() ) {
case ERROR:
addErrorHop();
candidate.setErrorHop( true );
spoon.newHop( transMeta, candidate );
break;
case INPUT:
spoon.newHop( transMeta, candidate );
break;
case OUTPUT:
StepErrorMeta stepErrorMeta = candidate.getFromStep().getStepErrorMeta();
if ( stepErrorMeta != null && stepErrorMeta.getTargetStep() != null ) {
if ( stepErrorMeta.getTargetStep().equals( candidate.getToStep() ) ) {
candidate.getFromStep().setStepErrorMeta( null );
}
}
spoon.newHop( transMeta, candidate );
break;
case INFO:
stream.setStepMeta( candidate.getFromStep() );
candidate.getToStep().getStepMetaInterface().handleStreamSelection( stream );
spoon.newHop( transMeta, candidate );
break;
case TARGET:
// We connect a target of the source step to an output step...
//
stream.setStepMeta( candidate.getToStep() );
candidate.getFromStep().getStepMetaInterface().handleStreamSelection( stream );
spoon.newHop( transMeta, candidate );
break;
default:
break;
}
clearSettings();
}
private void addErrorHop() {
// Automatically configure the step error handling too!
//
if ( candidate == null || candidate.getFromStep() == null ) {
return;
}
StepErrorMeta errorMeta = candidate.getFromStep().getStepErrorMeta();
if ( errorMeta == null ) {
errorMeta = new StepErrorMeta( transMeta, candidate.getFromStep() );
}
errorMeta.setEnabled( true );
errorMeta.setTargetStep( candidate.getToStep() );
candidate.getFromStep().setStepErrorMeta( errorMeta );
}
private void resetDelayTimer( StepMeta stepMeta ) {
DelayTimer delayTimer = delayTimers.get( stepMeta );
if ( delayTimer != null ) {
delayTimer.reset();
}
}
/*
* private void showStepTargetOptions(final StepMeta stepMeta, StepIOMetaInterface ioMeta, int x, int y) {
*
* if (!Utils.isEmpty(ioMeta.getTargetStepnames())) { final Menu menu = new Menu(canvas); for (final StreamInterface
* stream : ioMeta.getTargetStreams()) { MenuItem menuItem = new MenuItem(menu, SWT.NONE);
* menuItem.setText(stream.getDescription()); menuItem.addSelectionListener(new SelectionAdapter() {
*
* @Override public void widgetSelected(SelectionEvent arg0) { // Click on the target icon means: create a new target
* hop // if (startHopStep==null) { startHopStep = stepMeta; } menu.setVisible(false); menu.dispose(); redraw(); } });
* } menu.setLocation(x, y); menu.setVisible(true); resetDelayTimer(stepMeta);
*
* //showTargetStreamsStep = stepMeta; } }
*/
@Override
public void mouseEnter( MouseEvent arg0 ) {
}
@Override
public void mouseExit( MouseEvent arg0 ) {
}
private synchronized void addStepMouseOverDelayTimer( final StepMeta stepMeta ) {
// Don't add the same mouse over delay timer twice...
//
if ( mouseOverSteps.contains( stepMeta ) ) {
return;
}
mouseOverSteps.add( stepMeta );
DelayTimer delayTimer = new DelayTimer( 500, new DelayListener() {
@Override
public void expired() {
mouseOverSteps.remove( stepMeta );
delayTimers.remove( stepMeta );
showTargetStreamsStep = null;
asyncRedraw();
}
}, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Point cursor = getLastMove();
if ( cursor != null ) {
AreaOwner areaOwner = getVisibleAreaOwner( cursor.x, cursor.y );
if ( areaOwner != null ) {
AreaType areaType = areaOwner.getAreaType();
if ( areaType == AreaType.STEP_ICON ) {
StepMeta selectedStepMeta = (StepMeta) areaOwner.getOwner();
return selectedStepMeta == stepMeta;
} else if ( areaType != null && areaType.belongsToTransContextMenu() ) {
StepMeta selectedStepMeta = (StepMeta) areaOwner.getParent();
return selectedStepMeta == stepMeta;
} else if ( areaOwner.getExtensionAreaType() != null ) {
return true;
}
}
}
return false;
}
} );
new Thread( delayTimer ).start();
delayTimers.put( stepMeta, delayTimer );
}
private void stopStepMouseOverDelayTimer( final StepMeta stepMeta ) {
DelayTimer delayTimer = delayTimers.get( stepMeta );
if ( delayTimer != null ) {
delayTimer.stop();
}
}
private void stopStepMouseOverDelayTimers() {
for ( DelayTimer timer : delayTimers.values() ) {
timer.stop();
}
}
protected void asyncRedraw() {
spoon.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
if ( !TransGraph.this.isDisposed() ) {
TransGraph.this.redraw();
}
}
} );
}
private void addToolBar() {
try {
toolbar = (XulToolbar) getXulDomContainer().getDocumentRoot().getElementById( "nav-toolbar" );
ToolBar swtToolbar = (ToolBar) toolbar.getManagedObject();
swtToolbar.setBackground( GUIResource.getInstance().getColorDemoGray() );
swtToolbar.pack();
// Added 1/11/2016 to implement dropdown option for "Run"
ToolItem runItem = new ToolItem( swtToolbar, SWT.DROP_DOWN, 0 );
runItem.setImage( GUIResource.getInstance().getImage( "ui/images/run.svg" ) );
runItem.setToolTipText( BaseMessages.getString( PKG, "Spoon.Tooltip.RunTranformation" ) );
runItem.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
if ( e.detail == SWT.DROP_DOWN ) {
Menu menu = new Menu( shell, SWT.POP_UP );
MenuItem item1 = new MenuItem( menu, SWT.PUSH );
item1.setText( BaseMessages.getString( PKG, "Spoon.Run.Run" ) );
item1.setAccelerator( SWT.F9 );
item1.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e1 ) {
runTransformation();
}
} );
MenuItem item2 = new MenuItem( menu, SWT.PUSH );
item2.setText( BaseMessages.getString( PKG, "Spoon.Run.RunOptions" ) );
item2.setAccelerator( SWT.F8 );
item2.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e2 ) {
runOptionsTransformation();
}
} );
menu.setLocation( shell.getDisplay().map( mainComposite.getParent(), null, mainComposite.getLocation() ) );
menu.setVisible( true );
} else {
runTransformation();
}
}
} );
// Hack alert : more XUL limitations...
// TODO: no longer a limitation use toolbaritem
//
ToolItem sep = new ToolItem( swtToolbar, SWT.SEPARATOR );
zoomLabel = new Combo( swtToolbar, SWT.DROP_DOWN );
zoomLabel.setItems( TransPainter.magnificationDescriptions );
zoomLabel.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent arg0 ) {
readMagnification();
}
} );
zoomLabel.addKeyListener( new KeyAdapter() {
@Override
public void keyPressed( KeyEvent event ) {
if ( event.character == SWT.CR ) {
readMagnification();
}
}
} );
setZoomLabel();
zoomLabel.pack();
sep.setWidth( 80 );
sep.setControl( zoomLabel );
swtToolbar.pack();
} catch ( Throwable t ) {
log.logError( "Error loading the navigation toolbar for Spoon", t );
new ErrorDialog( shell, BaseMessages.getString( PKG, "Spoon.Exception.ErrorReadingXULFile.Title" ), BaseMessages
.getString( PKG, "Spoon.Exception.ErrorReadingXULFile.Message", XUL_FILE_TRANS_TOOLBAR ), new Exception( t ) );
}
}
/**
* Allows for magnifying to any percentage entered by the user...
*/
private void readMagnification() {
String possibleText = zoomLabel.getText();
possibleText = possibleText.replace( "%", "" );
float possibleFloatMagnification;
try {
possibleFloatMagnification = Float.parseFloat( possibleText ) / 100;
magnification = possibleFloatMagnification;
if ( zoomLabel.getText().indexOf( '%' ) < 0 ) {
zoomLabel.setText( zoomLabel.getText().concat( "%" ) );
}
} catch ( Exception e ) {
MessageBox mb = new MessageBox( shell, SWT.YES | SWT.ICON_ERROR );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.InvalidZoomMeasurement.Message", zoomLabel
.getText() ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.InvalidZoomMeasurement.Title" ) );
mb.open();
}
redraw();
}
protected void hideToolTips() {
toolTip.hide();
helpTip.hide();
}
private void showHelpTip( int x, int y, String tipTitle, String tipMessage ) {
helpTip.setTitle( tipTitle );
helpTip.setMessage( tipMessage.replaceAll( "\n", Const.CR ) );
helpTip
.setCheckBoxMessage( BaseMessages.getString( PKG, "TransGraph.HelpToolTip.DoNotShowAnyMoreCheckBox.Message" ) );
// helpTip.hide();
// int iconSize = spoon.props.getIconSize();
org.eclipse.swt.graphics.Point location = new org.eclipse.swt.graphics.Point( x - 5, y - 5 );
helpTip.show( location );
}
/**
* Select all the steps in a certain (screen) rectangle
*
* @param rect The selection area as a rectangle
*/
public void selectInRect( TransMeta transMeta, org.pentaho.di.core.gui.Rectangle rect ) {
if ( rect.height < 0 || rect.width < 0 ) {
org.pentaho.di.core.gui.Rectangle rectified =
new org.pentaho.di.core.gui.Rectangle( rect.x, rect.y, rect.width, rect.height );
// Only for people not dragging from left top to right bottom
if ( rectified.height < 0 ) {
rectified.y = rectified.y + rectified.height;
rectified.height = -rectified.height;
}
if ( rectified.width < 0 ) {
rectified.x = rectified.x + rectified.width;
rectified.width = -rectified.width;
}
rect = rectified;
}
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
Point a = stepMeta.getLocation();
if ( rect.contains( a.x, a.y ) ) {
stepMeta.setSelected( true );
}
}
for ( int i = 0; i < transMeta.nrNotes(); i++ ) {
NotePadMeta ni = transMeta.getNote( i );
Point a = ni.getLocation();
Point b = new Point( a.x + ni.width, a.y + ni.height );
if ( rect.contains( a.x, a.y ) && rect.contains( b.x, b.y ) ) {
ni.setSelected( true );
}
}
}
@Override
public void keyPressed( KeyEvent e ) {
if ( e.keyCode == SWT.ESC ) {
clearSettings();
redraw();
}
if ( e.keyCode == SWT.DEL ) {
List<StepMeta> stepMeta = transMeta.getSelectedSteps();
if ( stepMeta != null && stepMeta.size() > 0 ) {
delSelected( null );
}
}
if ( e.keyCode == SWT.F1 ) {
spoon.browseVersionHistory();
}
if ( e.keyCode == SWT.F2 ) {
spoon.editKettlePropertiesFile();
}
// CTRL-UP : allignTop();
if ( e.keyCode == SWT.ARROW_UP && ( e.stateMask & SWT.MOD1 ) != 0 ) {
alligntop();
}
// CTRL-DOWN : allignBottom();
if ( e.keyCode == SWT.ARROW_DOWN && ( e.stateMask & SWT.MOD1 ) != 0 ) {
allignbottom();
}
// CTRL-LEFT : allignleft();
if ( e.keyCode == SWT.ARROW_LEFT && ( e.stateMask & SWT.MOD1 ) != 0 ) {
allignleft();
}
// CTRL-RIGHT : allignRight();
if ( e.keyCode == SWT.ARROW_RIGHT && ( e.stateMask & SWT.MOD1 ) != 0 ) {
allignright();
}
// ALT-RIGHT : distributeHorizontal();
if ( e.keyCode == SWT.ARROW_RIGHT && ( e.stateMask & SWT.ALT ) != 0 ) {
distributehorizontal();
}
// ALT-UP : distributeVertical();
if ( e.keyCode == SWT.ARROW_UP && ( e.stateMask & SWT.ALT ) != 0 ) {
distributevertical();
}
// ALT-HOME : snap to grid
if ( e.keyCode == SWT.HOME && ( e.stateMask & SWT.ALT ) != 0 ) {
snaptogrid( ConstUI.GRID_SIZE );
}
if ( e.character == 'E' && ( e.stateMask & SWT.CTRL ) != 0 ) {
checkErrorVisuals();
}
// CTRL-W or CTRL-F4 : close tab
if ( ( e.keyCode == 'w' && ( e.stateMask & SWT.MOD1 ) != 0 )
|| ( e.keyCode == SWT.F4 && ( e.stateMask & SWT.MOD1 ) != 0 ) ) {
spoon.tabCloseSelected();
}
// Auto-layout
if ( e.character == 'A' ) {
autoLayout();
}
// SPACE : over a step: show output fields...
if ( e.character == ' ' && lastMove != null ) {
Point real = lastMove;
// Hide the tooltip!
hideToolTips();
// Set the pop-up menu
StepMeta stepMeta = transMeta.getStep( real.x, real.y, iconsize );
if ( stepMeta != null ) {
// OK, we found a step, show the output fields...
inputOutputFields( stepMeta, false );
}
}
}
@Override
public void keyReleased( KeyEvent e ) {
}
@Override
public boolean setFocus() {
return ( canvas != null ) ? canvas.setFocus() : false;
}
public void renameStep( StepMeta stepMeta, String stepname ) {
String newname = stepname;
StepMeta smeta = transMeta.findStep( newname, stepMeta );
int nr = 2;
while ( smeta != null ) {
newname = stepname + " " + nr;
smeta = transMeta.findStep( newname );
nr++;
}
if ( nr > 2 ) {
stepname = newname;
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_INFORMATION );
mb.setMessage( BaseMessages.getString( PKG, "Spoon.Dialog.StepnameExists.Message", stepname ) );
mb.setText( BaseMessages.getString( PKG, "Spoon.Dialog.StepnameExists.Title" ) );
mb.open();
}
stepMeta.setName( stepname );
stepMeta.setChanged();
spoon.refreshTree(); // to reflect the new name
spoon.refreshGraph();
}
public void clearSettings() {
selectedStep = null;
noInputStep = null;
selectedNote = null;
selectedSteps = null;
selectionRegion = null;
candidate = null;
last_hop_split = null;
lastButton = 0;
iconoffset = null;
startHopStep = null;
endHopStep = null;
endHopLocation = null;
mouseOverSteps.clear();
for ( int i = 0; i < transMeta.nrTransHops(); i++ ) {
// CHECKSTYLE:Indentation:OFF
transMeta.getTransHop( i ).split = false;
}
stopStepMouseOverDelayTimers();
}
public String[] getDropStrings( String str, String sep ) {
StringTokenizer strtok = new StringTokenizer( str, sep );
String[] retval = new String[ strtok.countTokens() ];
int i = 0;
while ( strtok.hasMoreElements() ) {
retval[ i ] = strtok.nextToken();
i++;
}
return retval;
}
public Point getRealPosition( Composite canvas, int x, int y ) {
Point p = new Point( 0, 0 );
Composite follow = canvas;
while ( follow != null ) {
org.eclipse.swt.graphics.Point loc = follow.getLocation();
Point xy = new Point( loc.x, loc.y );
p.x += xy.x;
p.y += xy.y;
follow = follow.getParent();
}
int offsetX = -16;
int offsetY = -64;
if ( Const.isOSX() ) {
offsetX = -2;
offsetY = -24;
}
p.x = x - p.x + offsetX;
p.y = y - p.y + offsetY;
return screen2real( p.x, p.y );
}
/**
* See if location (x,y) is on a line between two steps: the hop!
*
* @param x
* @param y
* @return the transformation hop on the specified location, otherwise: null
*/
protected TransHopMeta findHop( int x, int y ) {
return findHop( x, y, null );
}
/**
* See if location (x,y) is on a line between two steps: the hop!
*
* @param x
* @param y
* @param exclude the step to exclude from the hops (from or to location). Specify null if no step is to be excluded.
* @return the transformation hop on the specified location, otherwise: null
*/
private TransHopMeta findHop( int x, int y, StepMeta exclude ) {
int i;
TransHopMeta online = null;
for ( i = 0; i < transMeta.nrTransHops(); i++ ) {
TransHopMeta hi = transMeta.getTransHop( i );
StepMeta fs = hi.getFromStep();
StepMeta ts = hi.getToStep();
if ( fs == null || ts == null ) {
return null;
}
// If either the "from" or "to" step is excluded, skip this hop.
//
if ( exclude != null && ( exclude.equals( fs ) || exclude.equals( ts ) ) ) {
continue;
}
int[] line = getLine( fs, ts );
if ( pointOnLine( x, y, line ) ) {
online = hi;
}
}
return online;
}
private int[] getLine( StepMeta fs, StepMeta ts ) {
Point from = fs.getLocation();
Point to = ts.getLocation();
offset = getOffset();
int x1 = from.x + iconsize / 2;
int y1 = from.y + iconsize / 2;
int x2 = to.x + iconsize / 2;
int y2 = to.y + iconsize / 2;
return new int[] { x1, y1, x2, y2 };
}
public void hideStep() {
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta sti = transMeta.getStep( i );
if ( sti.isDrawn() && sti.isSelected() ) {
sti.hideStep();
spoon.refreshTree();
}
}
getCurrentStep().hideStep();
spoon.refreshTree();
redraw();
}
public void checkSelectedSteps() {
spoon.checkTrans( transMeta, true );
}
public void detachStep() {
detach( getCurrentStep() );
selectedSteps = null;
}
public void generateMappingToThisStep() {
spoon.generateFieldMapping( transMeta, getCurrentStep() );
}
public void partitioning() {
spoon.editPartitioning( transMeta, getCurrentStep() );
}
public void clustering() {
List<StepMeta> selected = transMeta.getSelectedSteps();
if ( selected != null && selected.size() > 0 ) {
spoon.editClustering( transMeta, transMeta.getSelectedSteps() );
} else {
spoon.editClustering( transMeta, getCurrentStep() );
}
}
public void errorHandling() {
spoon.editStepErrorHandling( transMeta, getCurrentStep() );
}
public void newHopChoice() {
selectedSteps = null;
newHop();
}
public void editStep() {
selectedSteps = null;
editStep( getCurrentStep() );
}
public void editDescription() {
editDescription( getCurrentStep() );
}
public void setDistributes() {
getCurrentStep().setDistributes( true );
getCurrentStep().setRowDistribution( null );
spoon.refreshGraph();
spoon.refreshTree();
}
public void setCustomRowDistribution() {
// TODO: ask user which row distribution is needed...
//
RowDistributionInterface rowDistribution = askUserForCustomDistributionMethod();
getCurrentStep().setDistributes( true );
getCurrentStep().setRowDistribution( rowDistribution );
spoon.refreshGraph();
spoon.refreshTree();
}
public RowDistributionInterface askUserForCustomDistributionMethod() {
List<PluginInterface> plugins = PluginRegistry.getInstance().getPlugins( RowDistributionPluginType.class );
if ( Utils.isEmpty( plugins ) ) {
return null;
}
List<String> choices = new ArrayList<>();
for ( PluginInterface plugin : plugins ) {
choices.add( plugin.getName() + " : " + plugin.getDescription() );
}
EnterSelectionDialog dialog =
new EnterSelectionDialog( shell, choices.toArray( new String[ choices.size() ] ), "Select distribution method",
"Please select the row distribution method:" );
if ( dialog.open() != null ) {
PluginInterface plugin = plugins.get( dialog.getSelectionNr() );
try {
return (RowDistributionInterface) PluginRegistry.getInstance().loadClass( plugin );
} catch ( Exception e ) {
new ErrorDialog( shell, "Error", "Error loading row distribution plugin class", e );
return null;
}
} else {
return null;
}
}
public void setCopies() {
getCurrentStep().setDistributes( false );
spoon.refreshGraph();
spoon.refreshTree();
}
public void copies() {
copies( getCurrentStep() );
}
public void copies( StepMeta stepMeta ) {
final boolean multipleOK = checkNumberOfCopies( transMeta, stepMeta );
selectedSteps = null;
String tt = BaseMessages.getString( PKG, "TransGraph.Dialog.NrOfCopiesOfStep.Title" );
String mt = BaseMessages.getString( PKG, "TransGraph.Dialog.NrOfCopiesOfStep.Message" );
EnterStringDialog nd = new EnterStringDialog( shell, stepMeta.getCopiesString(), tt, mt, true, transMeta );
String cop = nd.open();
if ( !Utils.isEmpty( cop ) ) {
int copies = Const.toInt( transMeta.environmentSubstitute( cop ), -1 );
if ( copies > 1 && !multipleOK ) {
cop = "1";
MessageBox mb = new MessageBox( shell, SWT.YES | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.MultipleCopiesAreNotAllowedHere.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.MultipleCopiesAreNotAllowedHere.Title" ) );
mb.open();
}
String cps = stepMeta.getCopiesString();
if ( ( cps != null && !cps.equals( cop ) ) || ( cps == null && cop != null ) ) {
stepMeta.setChanged();
}
stepMeta.setCopiesString( cop );
spoon.refreshGraph();
}
}
public void dupeStep() {
try {
List<StepMeta> steps = transMeta.getSelectedSteps();
if ( steps.size() <= 1 ) {
spoon.dupeStep( transMeta, getCurrentStep() );
} else {
for ( StepMeta stepMeta : steps ) {
spoon.dupeStep( transMeta, stepMeta );
}
}
} catch ( Exception ex ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.Dialog.ErrorDuplicatingStep.Title" ),
BaseMessages.getString( PKG, "TransGraph.Dialog.ErrorDuplicatingStep.Message" ), ex );
}
}
public void copyStep() {
spoon.copySelected( transMeta, transMeta.getSelectedSteps(), transMeta.getSelectedNotes() );
}
public void delSelected() {
delSelected( getCurrentStep() );
}
public void fieldsBefore() {
selectedSteps = null;
inputOutputFields( getCurrentStep(), true );
}
public void fieldsAfter() {
selectedSteps = null;
inputOutputFields( getCurrentStep(), false );
}
public void fieldsLineage() {
TransDataLineage tdl = new TransDataLineage( transMeta );
try {
tdl.calculateLineage();
} catch ( Exception e ) {
new ErrorDialog( shell, "Lineage error", "Unexpected lineage calculation error", e );
}
}
public void editHop() {
selectionRegion = null;
editHop( getCurrentHop() );
}
public void flipHopDirection() {
selectionRegion = null;
TransHopMeta hi = getCurrentHop();
hi.flip();
if ( transMeta.hasLoop( hi.getFromStep() ) ) {
spoon.refreshGraph();
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_ERROR );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.LoopsAreNotAllowed.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.LoopsAreNotAllowed.Title" ) );
mb.open();
hi.flip();
spoon.refreshGraph();
} else {
hi.setChanged();
spoon.refreshGraph();
spoon.refreshTree();
spoon.setShellText();
}
}
public void enableHop() {
selectionRegion = null;
TransHopMeta hi = getCurrentHop();
TransHopMeta before = (TransHopMeta) hi.clone();
hi.setEnabled( !hi.isEnabled() );
if ( transMeta.hasLoop( hi.getToStep() ) ) {
hi.setEnabled( !hi.isEnabled() );
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_ERROR );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.LoopAfterHopEnabled.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.LoopAfterHopEnabled.Title" ) );
mb.open();
} else {
TransHopMeta after = (TransHopMeta) hi.clone();
spoon.addUndoChange( transMeta, new TransHopMeta[] { before }, new TransHopMeta[] { after },
new int[] { transMeta.indexOfTransHop( hi ) } );
spoon.refreshGraph();
spoon.refreshTree();
}
updateErrorMetaForHop( hi );
}
public void deleteHop() {
selectionRegion = null;
TransHopMeta hi = getCurrentHop();
spoon.delHop( transMeta, hi );
}
private void updateErrorMetaForHop( TransHopMeta hop ) {
if ( hop != null && hop.isErrorHop() ) {
StepErrorMeta errorMeta = hop.getFromStep().getStepErrorMeta();
if ( errorMeta != null ) {
errorMeta.setEnabled( hop.isEnabled() );
}
}
}
public void enableHopsBetweenSelectedSteps() {
enableHopsBetweenSelectedSteps( true );
}
public void disableHopsBetweenSelectedSteps() {
enableHopsBetweenSelectedSteps( false );
}
/**
* This method enables or disables all the hops between the selected steps.
**/
public void enableHopsBetweenSelectedSteps( boolean enabled ) {
List<StepMeta> list = transMeta.getSelectedSteps();
for ( int i = 0; i < transMeta.nrTransHops(); i++ ) {
TransHopMeta hop = transMeta.getTransHop( i );
if ( list.contains( hop.getFromStep() ) && list.contains( hop.getToStep() ) ) {
TransHopMeta before = (TransHopMeta) hop.clone();
hop.setEnabled( enabled );
TransHopMeta after = (TransHopMeta) hop.clone();
spoon.addUndoChange( transMeta, new TransHopMeta[] { before }, new TransHopMeta[] { after },
new int[] { transMeta.indexOfTransHop( hop ) } );
}
}
spoon.refreshGraph();
}
public void enableHopsDownstream() {
enableDisableHopsDownstream( true );
}
public void disableHopsDownstream() {
enableDisableHopsDownstream( false );
}
public void enableDisableHopsDownstream( boolean enabled ) {
if ( currentHop == null ) {
return;
}
TransHopMeta before = (TransHopMeta) currentHop.clone();
currentHop.setEnabled( enabled );
TransHopMeta after = (TransHopMeta) currentHop.clone();
spoon.addUndoChange( transMeta, new TransHopMeta[] { before }, new TransHopMeta[] { after }, new int[] { transMeta
.indexOfTransHop( currentHop ) } );
enableDisableNextHops( currentHop.getToStep(), enabled );
spoon.refreshGraph();
}
private void enableDisableNextHops( StepMeta from, boolean enabled ) {
for ( StepMeta to : transMeta.getSteps() ) {
TransHopMeta hop = transMeta.findTransHop( from, to, true );
if ( hop != null ) {
TransHopMeta before = (TransHopMeta) hop.clone();
hop.setEnabled( enabled );
TransHopMeta after = (TransHopMeta) hop.clone();
spoon.addUndoChange( transMeta, new TransHopMeta[] { before }, new TransHopMeta[] { after },
new int[] { transMeta.indexOfTransHop( hop ) } );
enableDisableNextHops( to, enabled );
}
}
}
public void editNote() {
selectionRegion = null;
editNote( getCurrentNote() );
}
public void deleteNote() {
selectionRegion = null;
int idx = transMeta.indexOfNote( ni );
if ( idx >= 0 ) {
transMeta.removeNote( idx );
spoon.addUndoDelete( transMeta, new NotePadMeta[] { (NotePadMeta) ni.clone() }, new int[] { idx } );
redraw();
}
}
public void raiseNote() {
selectionRegion = null;
int idx = transMeta.indexOfNote( getCurrentNote() );
if ( idx >= 0 ) {
transMeta.raiseNote( idx );
// TBD: spoon.addUndoRaise(transMeta, new NotePadMeta[] {getCurrentNote()}, new int[] {idx} );
}
redraw();
}
public void lowerNote() {
selectionRegion = null;
int idx = transMeta.indexOfNote( getCurrentNote() );
if ( idx >= 0 ) {
transMeta.lowerNote( idx );
// TBD: spoon.addUndoLower(transMeta, new NotePadMeta[] {getCurrentNote()}, new int[] {idx} );
}
redraw();
}
public void newNote() {
selectionRegion = null;
String title = BaseMessages.getString( PKG, "TransGraph.Dialog.NoteEditor.Title" );
NotePadDialog dd = new NotePadDialog( transMeta, shell, title );
NotePadMeta n = dd.open();
if ( n != null ) {
NotePadMeta npi =
new NotePadMeta( n.getNote(), lastclick.x, lastclick.y, ConstUI.NOTE_MIN_SIZE, ConstUI.NOTE_MIN_SIZE, n
.getFontName(), n.getFontSize(), n.isFontBold(), n.isFontItalic(), n.getFontColorRed(), n
.getFontColorGreen(), n.getFontColorBlue(), n.getBackGroundColorRed(), n.getBackGroundColorGreen(), n
.getBackGroundColorBlue(), n.getBorderColorRed(), n.getBorderColorGreen(), n.getBorderColorBlue(), n
.isDrawShadow() );
transMeta.addNote( npi );
spoon.addUndoNew( transMeta, new NotePadMeta[] { npi }, new int[] { transMeta.indexOfNote( npi ) } );
redraw();
}
}
public void paste() {
final String clipcontent = spoon.fromClipboard();
Point loc = new Point( currentMouseX, currentMouseY );
spoon.pasteXML( transMeta, clipcontent, loc );
}
public void settings() {
editProperties( transMeta, spoon, spoon.getRepository(), true );
}
public void newStep( String description ) {
StepMeta stepMeta = spoon.newStep( transMeta, description, description, false, true );
PropsUI.setLocation( stepMeta, currentMouseX, currentMouseY );
stepMeta.setDraw( true );
redraw();
}
/**
* This sets the popup-menu on the background of the canvas based on the xy coordinate of the mouse. This method is
* called after a mouse-click.
*
* @param x X-coordinate on screen
* @param y Y-coordinate on screen
*/
private synchronized void setMenu( int x, int y ) {
try {
currentMouseX = x;
currentMouseY = y;
final StepMeta stepMeta = transMeta.getStep( x, y, iconsize );
if ( stepMeta != null ) { // We clicked on a Step!
setCurrentStep( stepMeta );
XulMenupopup menu = menuMap.get( "trans-graph-entry" );
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransStepRightClick.id,
new StepMenuExtension( this, menu ) );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling TransStepRightClick extension point", ex );
}
if ( menu != null ) {
List<StepMeta> selection = transMeta.getSelectedSteps();
doRightClickSelection( stepMeta, selection );
int sels = selection.size();
Document doc = getXulDomContainer().getDocumentRoot();
// TODO: cache the next line (seems fast enough)?
//
List<PluginInterface> rowDistributionPlugins =
PluginRegistry.getInstance().getPlugins( RowDistributionPluginType.class );
JfaceMenupopup customRowDistMenu =
(JfaceMenupopup) doc.getElementById( "trans-graph-entry-data-movement-popup" );
customRowDistMenu.setDisabled( false );
customRowDistMenu.removeChildren();
// Add the default round robin plugin...
//
Action action = new Action( "RoundRobinRowDistribution", Action.AS_CHECK_BOX ) {
@Override
public void run() {
stepMeta.setRowDistribution( null ); // default
stepMeta.setDistributes( true );
}
};
boolean selected = stepMeta.isDistributes() && stepMeta.getRowDistribution() == null;
action.setChecked( selected );
JfaceMenuitem child =
new JfaceMenuitem( null, customRowDistMenu, xulDomContainer, "Round Robin row distribution", 0, action );
child.setLabel( BaseMessages.getString( PKG, "TransGraph.PopupMenu.RoundRobin" ) );
child.setDisabled( false );
child.setSelected( selected );
for ( int p = 0; p < rowDistributionPlugins.size(); p++ ) {
final PluginInterface rowDistributionPlugin = rowDistributionPlugins.get( p );
selected =
stepMeta.isDistributes() && stepMeta.getRowDistribution() != null
&& stepMeta.getRowDistribution().getCode().equals( rowDistributionPlugin.getIds()[ 0 ] );
action = new Action( rowDistributionPlugin.getIds()[ 0 ], Action.AS_CHECK_BOX ) {
@Override
public void run() {
try {
stepMeta.setRowDistribution( (RowDistributionInterface) PluginRegistry.getInstance().loadClass(
rowDistributionPlugin ) );
} catch ( Exception e ) {
LogChannel.GENERAL.logError( "Error loading row distribution plugin class: ", e );
}
}
};
action.setChecked( selected );
child =
new JfaceMenuitem( null, customRowDistMenu, xulDomContainer, rowDistributionPlugin.getName(), p + 1,
action );
child.setLabel( rowDistributionPlugin.getName() );
child.setDisabled( false );
child.setSelected( selected );
}
// Add the default copy rows plugin...
//
action = new Action( "CopyRowsDistribution", Action.AS_CHECK_BOX ) {
@Override
public void run() {
stepMeta.setDistributes( false );
}
};
selected = !stepMeta.isDistributes();
action.setChecked( selected );
child = new JfaceMenuitem( null, customRowDistMenu, xulDomContainer, "Copy rows distribution", 0, action );
child.setLabel( BaseMessages.getString( PKG, "TransGraph.PopupMenu.CopyData" ) );
child.setDisabled( false );
child.setSelected( selected );
JfaceMenupopup launchMenu = (JfaceMenupopup) doc.getElementById( "trans-graph-entry-launch-popup" );
String[] referencedObjects = stepMeta.getStepMetaInterface().getReferencedObjectDescriptions();
boolean[] enabledObjects = stepMeta.getStepMetaInterface().isReferencedObjectEnabled();
launchMenu.setDisabled( Utils.isEmpty( referencedObjects ) );
launchMenu.removeChildren();
int childIndex = 0;
// First see if we need to add a special "active" entry (running transformation)
//
StepMetaInterface stepMetaInterface = stepMeta.getStepMetaInterface();
String activeReferencedObjectDescription = stepMetaInterface.getActiveReferencedObjectDescription();
if ( getActiveSubtransformation( this, stepMeta ) != null && activeReferencedObjectDescription != null ) {
action = new Action( activeReferencedObjectDescription, Action.AS_DROP_DOWN_MENU ) {
@Override
public void run() {
openMapping( stepMeta, -1 ); // negative by convention
}
};
child =
new JfaceMenuitem( null, launchMenu, xulDomContainer, activeReferencedObjectDescription, childIndex++,
action );
child.setLabel( activeReferencedObjectDescription );
child.setDisabled( false );
}
if ( !Utils.isEmpty( referencedObjects ) ) {
for ( int i = 0; i < referencedObjects.length; i++ ) {
final int index = i;
String referencedObject = referencedObjects[ i ];
action = new Action( referencedObject, Action.AS_DROP_DOWN_MENU ) {
@Override
public void run() {
openMapping( stepMeta, index );
}
};
child = new JfaceMenuitem( null, launchMenu, xulDomContainer, referencedObject, childIndex++, action );
child.setLabel( referencedObject );
child.setDisabled( !enabledObjects[ i ] );
}
}
initializeXulMenu( doc, selection, stepMeta );
ConstUI.displayMenu( menu, canvas );
}
} else {
final TransHopMeta hi = findHop( x, y );
if ( hi != null ) { // We clicked on a HOP!
XulMenupopup menu = menuMap.get( "trans-graph-hop" );
if ( menu != null ) {
setCurrentHop( hi );
XulMenuitem item =
(XulMenuitem) getXulDomContainer().getDocumentRoot().getElementById( "trans-graph-hop-enabled" );
if ( item != null ) {
if ( hi.isEnabled() ) {
item.setLabel( BaseMessages.getString( PKG, "TransGraph.PopupMenu.DisableHop" ) );
} else {
item.setLabel( BaseMessages.getString( PKG, "TransGraph.PopupMenu.EnableHop" ) );
}
}
ConstUI.displayMenu( menu, canvas );
}
} else {
// Clicked on the background: maybe we hit a note?
final NotePadMeta ni = transMeta.getNote( x, y );
setCurrentNote( ni );
if ( ni != null ) {
XulMenupopup menu = menuMap.get( "trans-graph-note" );
if ( menu != null ) {
ConstUI.displayMenu( menu, canvas );
}
} else {
XulMenupopup menu = menuMap.get( "trans-graph-background" );
if ( menu != null ) {
final String clipcontent = spoon.fromClipboard();
XulMenuitem item =
(XulMenuitem) getXulDomContainer().getDocumentRoot().getElementById( "trans-graph-background-paste" );
if ( item != null ) {
item.setDisabled( clipcontent == null );
}
ConstUI.displayMenu( menu, canvas );
}
}
}
}
} catch ( Throwable t ) {
// TODO: fix this: log somehow, is IGNORED for now.
t.printStackTrace();
}
}
public void selectAll() {
spoon.editSelectAll();
}
public void clearSelection() {
spoon.editUnselectAll();
}
protected void initializeXulMenu( Document doc, List<StepMeta> selection, StepMeta stepMeta ) throws KettleException {
XulMenuitem item = (XulMenuitem) doc.getElementById( "trans-graph-entry-newhop" );
int sels = selection.size();
item.setDisabled( sels != 2 );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-align-snap" );
item.setAcceltext( "ALT-HOME" );
item.setLabel( BaseMessages.getString( PKG, "TransGraph.PopupMenu.SnapToGrid" ) );
item.setAccesskey( "alt-home" );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-open-mapping" );
XulMenu men = (XulMenu) doc.getElementById( TRANS_GRAPH_ENTRY_SNIFF );
men.setDisabled( trans == null || trans.isRunning() == false );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-sniff-input" );
item.setDisabled( trans == null || trans.isRunning() == false );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-sniff-output" );
item.setDisabled( trans == null || trans.isRunning() == false );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-sniff-error" );
item.setDisabled( !( stepMeta.supportsErrorHandling() && stepMeta.getStepErrorMeta() != null
&& stepMeta.getStepErrorMeta().getTargetStep() != null && trans != null && trans.isRunning() ) );
XulMenu aMenu = (XulMenu) doc.getElementById( TRANS_GRAPH_ENTRY_AGAIN );
if ( aMenu != null ) {
aMenu.setDisabled( sels < 2 );
}
// item = (XulMenuitem) doc.getElementById("trans-graph-entry-data-movement-distribute");
// item.setSelected(stepMeta.isDistributes());
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-partitioning" );
item.setDisabled( spoon.getPartitionSchemasNames( transMeta ).isEmpty() );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-data-movement-copy" );
item.setSelected( !stepMeta.isDistributes() );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-hide" );
item.setDisabled( !( stepMeta.isDrawn() && !transMeta.isAnySelectedStepUsedInTransHops() ) );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-detach" );
item.setDisabled( !transMeta.isStepUsedInTransHops( stepMeta ) );
item = (XulMenuitem) doc.getElementById( "trans-graph-entry-errors" );
item.setDisabled( !stepMeta.supportsErrorHandling() );
}
private boolean checkNumberOfCopies( TransMeta transMeta, StepMeta stepMeta ) {
boolean enabled = true;
List<StepMeta> prevSteps = transMeta.findPreviousSteps( stepMeta );
for ( StepMeta prevStep : prevSteps ) {
// See what the target steps are.
// If one of the target steps is our original step, we can't start multiple copies
//
String[] targetSteps = prevStep.getStepMetaInterface().getStepIOMeta().getTargetStepnames();
if ( targetSteps != null ) {
for ( int t = 0; t < targetSteps.length && enabled; t++ ) {
if ( !Utils.isEmpty( targetSteps[ t ] ) && targetSteps[ t ].equalsIgnoreCase( stepMeta.getName() ) ) {
enabled = false;
}
}
}
}
return enabled;
}
private AreaOwner setToolTip( int x, int y, int screenX, int screenY ) {
AreaOwner subject = null;
if ( !spoon.getProperties().showToolTips() ) {
return subject;
}
canvas.setToolTipText( null );
String newTip = null;
Image tipImage = null;
final TransHopMeta hi = findHop( x, y );
// check the area owner list...
//
StringBuilder tip = new StringBuilder();
AreaOwner areaOwner = getVisibleAreaOwner( x, y );
if ( areaOwner != null && areaOwner.getAreaType() != null ) {
switch ( areaOwner.getAreaType() ) {
case REMOTE_INPUT_STEP:
StepMeta step = (StepMeta) areaOwner.getParent();
tip.append( "Remote input steps:" ).append( Const.CR ).append( "-----------------------" ).append( Const.CR );
for ( RemoteStep remoteStep : step.getRemoteInputSteps() ) {
tip.append( remoteStep.toString() ).append( Const.CR );
}
break;
case REMOTE_OUTPUT_STEP:
step = (StepMeta) areaOwner.getParent();
tip.append( "Remote output steps:" ).append( Const.CR ).append( "-----------------------" )
.append( Const.CR );
for ( RemoteStep remoteStep : step.getRemoteOutputSteps() ) {
tip.append( remoteStep.toString() ).append( Const.CR );
}
break;
case STEP_PARTITIONING:
step = (StepMeta) areaOwner.getParent();
tip.append( "Step partitioning:" ).append( Const.CR ).append( "-----------------------" ).append( Const.CR );
tip.append( step.getStepPartitioningMeta().toString() ).append( Const.CR );
if ( step.getTargetStepPartitioningMeta() != null ) {
tip.append( Const.CR ).append( Const.CR ).append(
"TARGET: " + step.getTargetStepPartitioningMeta().toString() ).append( Const.CR );
}
break;
case STEP_ERROR_ICON:
String log = (String) areaOwner.getParent();
tip.append( log );
tipImage = GUIResource.getInstance().getImageStepError();
break;
case STEP_ERROR_RED_ICON:
String redLog = (String) areaOwner.getParent();
tip.append( redLog );
tipImage = GUIResource.getInstance().getImageRedStepError();
break;
case HOP_COPY_ICON:
step = (StepMeta) areaOwner.getParent();
tip.append( BaseMessages.getString( PKG, "TransGraph.Hop.Tooltip.HopTypeCopy", step.getName(), Const.CR ) );
tipImage = GUIResource.getInstance().getImageCopyHop();
break;
case ROW_DISTRIBUTION_ICON:
step = (StepMeta) areaOwner.getParent();
tip.append( BaseMessages.getString( PKG, "TransGraph.Hop.Tooltip.RowDistribution", step.getName(), step
.getRowDistribution() == null ? "" : step.getRowDistribution().getDescription() ) );
tip.append( Const.CR );
tipImage = GUIResource.getInstance().getImageBalance();
break;
case HOP_INFO_ICON:
StepMeta from = (StepMeta) areaOwner.getParent();
StepMeta to = (StepMeta) areaOwner.getOwner();
tip.append( BaseMessages.getString( PKG, "TransGraph.Hop.Tooltip.HopTypeInfo", to.getName(), from.getName(),
Const.CR ) );
tipImage = GUIResource.getInstance().getImageInfoHop();
break;
case HOP_ERROR_ICON:
from = (StepMeta) areaOwner.getParent();
to = (StepMeta) areaOwner.getOwner();
areaOwner.getOwner();
tip.append( BaseMessages.getString( PKG, "TransGraph.Hop.Tooltip.HopTypeError", from.getName(), to.getName(),
Const.CR ) );
tipImage = GUIResource.getInstance().getImageErrorHop();
break;
case HOP_INFO_STEP_COPIES_ERROR:
from = (StepMeta) areaOwner.getParent();
to = (StepMeta) areaOwner.getOwner();
tip.append( BaseMessages.getString( PKG, "TransGraph.Hop.Tooltip.InfoStepCopies", from.getName(), to
.getName(), Const.CR ) );
tipImage = GUIResource.getInstance().getImageStepError();
break;
case STEP_INPUT_HOP_ICON:
// StepMeta subjectStep = (StepMeta) (areaOwner.getParent());
tip.append( BaseMessages.getString( PKG, "TransGraph.StepInputConnector.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageHopInput();
break;
case STEP_OUTPUT_HOP_ICON:
// subjectStep = (StepMeta) (areaOwner.getParent());
tip.append( BaseMessages.getString( PKG, "TransGraph.StepOutputConnector.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageHopOutput();
break;
case STEP_INFO_HOP_ICON:
// subjectStep = (StepMeta) (areaOwner.getParent());
// StreamInterface stream = (StreamInterface) areaOwner.getOwner();
StepIOMetaInterface ioMeta = (StepIOMetaInterface) areaOwner.getOwner();
tip.append( BaseMessages.getString( PKG, "TransGraph.StepInfoConnector.Tooltip" ) + Const.CR
+ ioMeta.toString() );
tipImage = GUIResource.getInstance().getImageHopOutput();
break;
case STEP_TARGET_HOP_ICON:
StreamInterface stream = (StreamInterface) areaOwner.getOwner();
tip.append( stream.getDescription() );
tipImage = GUIResource.getInstance().getImageHopOutput();
break;
case STEP_ERROR_HOP_ICON:
StepMeta stepMeta = (StepMeta) areaOwner.getParent();
if ( stepMeta.supportsErrorHandling() ) {
tip.append( BaseMessages.getString( PKG, "TransGraph.StepSupportsErrorHandling.Tooltip" ) );
} else {
tip.append( BaseMessages.getString( PKG, "TransGraph.StepDoesNotSupportsErrorHandling.Tooltip" ) );
}
tipImage = GUIResource.getInstance().getImageHopOutput();
break;
case STEP_EDIT_ICON:
stepMeta = (StepMeta) ( areaOwner.getParent() );
tip.append( BaseMessages.getString( PKG, "TransGraph.EditStep.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageEdit();
break;
case STEP_INJECT_ICON:
stepMeta = (StepMeta) ( areaOwner.getParent() );
Object injection = areaOwner.getOwner();
if ( injection != null ) {
tip.append( BaseMessages.getString( PKG, "TransGraph.StepInjectionSupported.Tooltip" ) );
} else {
tip.append( BaseMessages.getString( PKG, "TransGraph.StepInjectionNotSupported.Tooltip" ) );
}
tipImage = GUIResource.getInstance().getImageInject();
break;
case STEP_MENU_ICON:
tip.append( BaseMessages.getString( PKG, "TransGraph.ShowMenu.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageContextMenu();
break;
default:
break;
}
}
if ( hi != null ) { // We clicked on a HOP!
// Set the tooltip for the hop:
tip.append( Const.CR ).append( BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo" ) ).append(
newTip = hi.toString() ).append( Const.CR );
}
if ( tip.length() == 0 ) {
newTip = null;
} else {
newTip = tip.toString();
}
if ( newTip == null ) {
toolTip.hide();
if ( hi != null ) { // We clicked on a HOP!
// Set the tooltip for the hop:
newTip =
BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo" )
+ Const.CR
+ BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo.SourceStep" )
+ " "
+ hi.getFromStep().getName()
+ Const.CR
+ BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo.TargetStep" )
+ " "
+ hi.getToStep().getName()
+ Const.CR
+ BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo.Status" )
+ " "
+ ( hi.isEnabled() ? BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo.Enable" ) : BaseMessages
.getString( PKG, "TransGraph.Dialog.HopInfo.Disable" ) );
toolTip.setText( newTip );
if ( hi.isEnabled() ) {
toolTip.setImage( GUIResource.getInstance().getImageHop() );
} else {
toolTip.setImage( GUIResource.getInstance().getImageDisabledHop() );
}
toolTip.show( new org.eclipse.swt.graphics.Point( screenX, screenY ) );
} else {
newTip = null;
}
} else if ( !newTip.equalsIgnoreCase( getToolTipText() ) ) {
Image tooltipImage = null;
if ( tipImage != null ) {
tooltipImage = tipImage;
} else {
tooltipImage = GUIResource.getInstance().getImageSpoon();
}
showTooltip( newTip, tooltipImage, screenX, screenY );
}
if ( areaOwner != null && areaOwner.getExtensionAreaType() != null ) {
try {
TransPainterFlyoutTooltipExtension extension =
new TransPainterFlyoutTooltipExtension( areaOwner, this, new Point( screenX, screenY ) );
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, KettleExtensionPoint.TransPainterFlyoutTooltip.id, extension );
} catch ( Exception e ) {
LogChannel.GENERAL.logError( "Error calling extension point(s) for the transformation painter step", e );
}
}
return subject;
}
public void showTooltip( String label, Image image, int screenX, int screenY ) {
toolTip.setImage( image );
toolTip.setText( label );
toolTip.hide();
toolTip.show( new org.eclipse.swt.graphics.Point( screenX, screenY ) );
}
public AreaOwner getVisibleAreaOwner( int x, int y ) {
for ( int i = areaOwners.size() - 1; i >= 0; i-- ) {
AreaOwner areaOwner = areaOwners.get( i );
if ( areaOwner.contains( x, y ) ) {
return areaOwner;
}
}
return null;
}
public void delSelected( StepMeta stMeta ) {
List<StepMeta> selection = transMeta.getSelectedSteps();
if ( selection.size() == 0 ) {
spoon.delStep( transMeta, stMeta );
return;
}
if ( currentStep != null && selection.contains( currentStep ) ) {
currentStep = null;
transPreviewDelegate.setSelectedStep( currentStep );
transPreviewDelegate.refreshView();
for ( StepSelectionListener listener : currentStepListeners ) {
listener.onUpdateSelection( currentStep );
}
}
StepMeta[] steps = selection.toArray( new StepMeta[ selection.size() ] );
spoon.delSteps( transMeta, steps );
}
public void editDescription( StepMeta stepMeta ) {
String title = BaseMessages.getString( PKG, "TransGraph.Dialog.StepDescription.Title" );
String message = BaseMessages.getString( PKG, "TransGraph.Dialog.StepDescription.Message" );
EnterTextDialog dd = new EnterTextDialog( shell, title, message, stepMeta.getDescription() );
String d = dd.open();
if ( d != null ) {
stepMeta.setDescription( d );
stepMeta.setChanged();
spoon.setShellText();
}
}
/**
* Display the input- or outputfields for a step.
*
* @param stepMeta The step (it's metadata) to query
* @param before set to true if you want to have the fields going INTO the step, false if you want to see all the
* fields that exit the step.
*/
private void inputOutputFields( StepMeta stepMeta, boolean before ) {
spoon.refreshGraph();
transMeta.setRepository( spoon.rep );
SearchFieldsProgressDialog op = new SearchFieldsProgressDialog( transMeta, stepMeta, before );
try {
final ProgressMonitorDialog pmd = new ProgressMonitorDialog( shell );
// Run something in the background to cancel active database queries, forecably if needed!
Runnable run = new Runnable() {
@Override
public void run() {
IProgressMonitor monitor = pmd.getProgressMonitor();
while ( pmd.getShell() == null || ( !pmd.getShell().isDisposed() && !monitor.isCanceled() ) ) {
try {
Thread.sleep( 250 );
} catch ( InterruptedException e ) {
// Ignore
}
}
if ( monitor.isCanceled() ) { // Disconnect and see what happens!
try {
transMeta.cancelQueries();
} catch ( Exception e ) {
// Ignore
}
}
}
};
// Dump the cancel looker in the background!
new Thread( run ).start();
pmd.run( true, true, op );
} catch ( InvocationTargetException e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.Dialog.GettingFields.Title" ), BaseMessages
.getString( PKG, "TransGraph.Dialog.GettingFields.Message" ), e );
} catch ( InterruptedException e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.Dialog.GettingFields.Title" ), BaseMessages
.getString( PKG, "TransGraph.Dialog.GettingFields.Message" ), e );
}
RowMetaInterface fields = op.getFields();
if ( fields != null && fields.size() > 0 ) {
StepFieldsDialog sfd = new StepFieldsDialog( shell, transMeta, SWT.NONE, stepMeta.getName(), fields );
String sn = (String) sfd.open();
if ( sn != null ) {
StepMeta esi = transMeta.findStep( sn );
if ( esi != null ) {
editStep( esi );
}
}
} else {
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_INFORMATION );
mb.setMessage( BaseMessages.getString( PKG, "TransGraph.Dialog.CouldntFindFields.Message" ) );
mb.setText( BaseMessages.getString( PKG, "TransGraph.Dialog.CouldntFindFields.Title" ) );
mb.open();
}
}
public void paintControl( PaintEvent e ) {
Point area = getArea();
if ( area.x == 0 || area.y == 0 ) {
return; // nothing to do!
}
Display disp = shell.getDisplay();
Image img = getTransformationImage( disp, area.x, area.y, magnification );
e.gc.drawImage( img, 0, 0 );
if ( transMeta.nrSteps() == 0 ) {
e.gc.setForeground( GUIResource.getInstance().getColorCrystalTextPentaho() );
e.gc.setFont( GUIResource.getInstance().getFontMedium() );
Image pentahoImage = GUIResource.getInstance().getImageTransCanvas();
int leftPosition = ( area.x - pentahoImage.getBounds().width ) / 2;
int topPosition = ( area.y - pentahoImage.getBounds().height ) / 2;
e.gc.drawImage( pentahoImage, leftPosition, topPosition );
}
img.dispose();
// spoon.setShellText();
}
public Image getTransformationImage( Device device, int x, int y, float magnificationFactor ) {
GCInterface gc = new SWTGC( device, new Point( x, y ), iconsize );
int gridSize =
PropsUI.getInstance().isShowCanvasGridEnabled() ? PropsUI.getInstance().getCanvasGridSize() : 1;
TransPainter transPainter =
new TransPainter( gc, transMeta, new Point( x, y ), new SwtScrollBar( hori ), new SwtScrollBar( vert ),
candidate, drop_candidate, selectionRegion, areaOwners, mouseOverSteps,
PropsUI.getInstance().getIconSize(), PropsUI.getInstance().getLineWidth(), gridSize,
PropsUI.getInstance().getShadowSize(), PropsUI.getInstance()
.isAntiAliasingEnabled(), PropsUI.getInstance().getNoteFont().getName(), PropsUI.getInstance()
.getNoteFont().getHeight(), trans, PropsUI.getInstance().isIndicateSlowTransStepsEnabled() );
transPainter.setMagnification( magnificationFactor );
transPainter.setStepLogMap( stepLogMap );
transPainter.setStartHopStep( startHopStep );
transPainter.setEndHopLocation( endHopLocation );
transPainter.setNoInputStep( noInputStep );
transPainter.setEndHopStep( endHopStep );
transPainter.setCandidateHopType( candidateHopType );
transPainter.setStartErrorHopStep( startErrorHopStep );
transPainter.setShowTargetStreamsStep( showTargetStreamsStep );
transPainter.buildTransformationImage();
Image img = (Image) gc.getImage();
gc.dispose();
return img;
}
@Override
protected Point getOffset() {
Point area = getArea();
Point max = transMeta.getMaximum();
Point thumb = getThumb( area, max );
return getOffset( thumb, area );
}
private void editStep( StepMeta stepMeta ) {
spoon.editStep( transMeta, stepMeta );
}
private void editNote( NotePadMeta ni ) {
NotePadMeta before = (NotePadMeta) ni.clone();
String title = BaseMessages.getString( PKG, "TransGraph.Dialog.EditNote.Title" );
NotePadDialog dd = new NotePadDialog( transMeta, shell, title, ni );
NotePadMeta n = dd.open();
if ( n != null ) {
ni.setChanged();
ni.setNote( n.getNote() );
ni.setFontName( n.getFontName() );
ni.setFontSize( n.getFontSize() );
ni.setFontBold( n.isFontBold() );
ni.setFontItalic( n.isFontItalic() );
// font color
ni.setFontColorRed( n.getFontColorRed() );
ni.setFontColorGreen( n.getFontColorGreen() );
ni.setFontColorBlue( n.getFontColorBlue() );
// background color
ni.setBackGroundColorRed( n.getBackGroundColorRed() );
ni.setBackGroundColorGreen( n.getBackGroundColorGreen() );
ni.setBackGroundColorBlue( n.getBackGroundColorBlue() );
// border color
ni.setBorderColorRed( n.getBorderColorRed() );
ni.setBorderColorGreen( n.getBorderColorGreen() );
ni.setBorderColorBlue( n.getBorderColorBlue() );
ni.setDrawShadow( n.isDrawShadow() );
ni.width = ConstUI.NOTE_MIN_SIZE;
ni.height = ConstUI.NOTE_MIN_SIZE;
NotePadMeta after = (NotePadMeta) ni.clone();
spoon.addUndoChange( transMeta, new NotePadMeta[] { before }, new NotePadMeta[] { after }, new int[] { transMeta
.indexOfNote( ni ) } );
spoon.refreshGraph();
}
}
private void editHop( TransHopMeta transHopMeta ) {
String name = transHopMeta.toString();
if ( log.isDebug() ) {
log.logDebug( BaseMessages.getString( PKG, "TransGraph.Logging.EditingHop" ) + name );
}
spoon.editHop( transMeta, transHopMeta );
}
private void newHop() {
List<StepMeta> selection = transMeta.getSelectedSteps();
if ( selection.size() == 2 ) {
StepMeta fr = selection.get( 0 );
StepMeta to = selection.get( 1 );
spoon.newHop( transMeta, fr, to );
}
}
private boolean pointOnLine( int x, int y, int[] line ) {
int dx, dy;
int pm = HOP_SEL_MARGIN / 2;
boolean retval = false;
for ( dx = -pm; dx <= pm && !retval; dx++ ) {
for ( dy = -pm; dy <= pm && !retval; dy++ ) {
retval = pointOnThinLine( x + dx, y + dy, line );
}
}
return retval;
}
private boolean pointOnThinLine( int x, int y, int[] line ) {
int x1 = line[ 0 ];
int y1 = line[ 1 ];
int x2 = line[ 2 ];
int y2 = line[ 3 ];
// Not in the square formed by these 2 points: ignore!
// CHECKSTYLE:LineLength:OFF
if ( !( ( ( x >= x1 && x <= x2 ) || ( x >= x2 && x <= x1 ) ) && ( ( y >= y1 && y <= y2 ) || ( y >= y2
&& y <= y1 ) ) ) ) {
return false;
}
double angle_line = Math.atan2( y2 - y1, x2 - x1 ) + Math.PI;
double angle_point = Math.atan2( y - y1, x - x1 ) + Math.PI;
// Same angle, or close enough?
if ( angle_point >= angle_line - 0.01 && angle_point <= angle_line + 0.01 ) {
return true;
}
return false;
}
private SnapAllignDistribute createSnapAllignDistribute() {
List<StepMeta> selection = transMeta.getSelectedSteps();
int[] indices = transMeta.getStepIndexes( selection );
return new SnapAllignDistribute( transMeta, selection, indices, spoon, this );
}
public void snaptogrid() {
snaptogrid( ConstUI.GRID_SIZE );
}
private void snaptogrid( int size ) {
createSnapAllignDistribute().snaptogrid( size );
}
public void allignleft() {
createSnapAllignDistribute().allignleft();
}
public void allignright() {
createSnapAllignDistribute().allignright();
}
public void alligntop() {
createSnapAllignDistribute().alligntop();
}
public void allignbottom() {
createSnapAllignDistribute().allignbottom();
}
public void distributehorizontal() {
createSnapAllignDistribute().distributehorizontal();
}
public void distributevertical() {
createSnapAllignDistribute().distributevertical();
}
private void detach( StepMeta stepMeta ) {
for ( int i = transMeta.nrTransHops() - 1; i >= 0; i-- ) {
TransHopMeta hop = transMeta.getTransHop( i );
if ( stepMeta.equals( hop.getFromStep() ) || stepMeta.equals( hop.getToStep() ) ) {
// Step is connected with a hop, remove this hop.
//
spoon.addUndoNew( transMeta, new TransHopMeta[] { hop }, new int[] { i } );
transMeta.removeTransHop( i );
}
}
/*
* TransHopMeta hfrom = transMeta.findTransHopTo(stepMeta); TransHopMeta hto = transMeta.findTransHopFrom(stepMeta);
*
* if (hfrom != null && hto != null) { if (transMeta.findTransHop(hfrom.getFromStep(), hto.getToStep()) == null) {
* TransHopMeta hnew = new TransHopMeta(hfrom.getFromStep(), hto.getToStep()); transMeta.addTransHop(hnew);
* spoon.addUndoNew(transMeta, new TransHopMeta[] { hnew }, new int[] { transMeta.indexOfTransHop(hnew) });
* spoon.refreshTree(); } } if (hfrom != null) { int fromidx = transMeta.indexOfTransHop(hfrom); if (fromidx >= 0) {
* transMeta.removeTransHop(fromidx); spoon.refreshTree(); } } if (hto != null) { int toidx =
* transMeta.indexOfTransHop(hto); if (toidx >= 0) { transMeta.removeTransHop(toidx); spoon.refreshTree(); } }
*/
spoon.refreshTree();
redraw();
}
// Preview the selected steps...
public void preview() {
spoon.previewTransformation();
}
public void newProps() {
iconsize = spoon.props.getIconSize();
}
@Override
public EngineMetaInterface getMeta() {
return transMeta;
}
/**
* @param transMeta the transMeta to set
* @return the transMeta / public TransMeta getTransMeta() { return transMeta; }
* <p/>
* /**
*/
public void setTransMeta( TransMeta transMeta ) {
this.transMeta = transMeta;
}
// Change of step, connection, hop or note...
public void addUndoPosition( Object[] obj, int[] pos, Point[] prev, Point[] curr ) {
addUndoPosition( obj, pos, prev, curr, false );
}
// Change of step, connection, hop or note...
public void addUndoPosition( Object[] obj, int[] pos, Point[] prev, Point[] curr, boolean nextAlso ) {
// It's better to store the indexes of the objects, not the objects itself!
transMeta.addUndo( obj, null, pos, prev, curr, TransMeta.TYPE_UNDO_POSITION, nextAlso );
spoon.setUndoMenu( transMeta );
}
@Override
public boolean applyChanges() throws KettleException {
return spoon.saveToFile( transMeta );
}
@Override
public boolean canBeClosed() {
return !transMeta.hasChanged();
}
@Override
public TransMeta getManagedObject() {
return transMeta;
}
@Override
public boolean hasContentChanged() {
return transMeta.hasChanged();
}
public List<CheckResultInterface> getRemarks() {
return remarks;
}
public void setRemarks( List<CheckResultInterface> remarks ) {
this.remarks = remarks;
}
public List<DatabaseImpact> getImpact() {
return impact;
}
public void setImpact( List<DatabaseImpact> impact ) {
this.impact = impact;
}
public boolean isImpactFinished() {
return impactFinished;
}
public void setImpactFinished( boolean impactHasRun ) {
this.impactFinished = impactHasRun;
}
/**
* @return the lastMove
*/
public Point getLastMove() {
return lastMove;
}
public static boolean editProperties( TransMeta transMeta, Spoon spoon, Repository rep,
boolean allowDirectoryChange ) {
return editProperties( transMeta, spoon, rep, allowDirectoryChange, null );
}
public static boolean editProperties( TransMeta transMeta, Spoon spoon, Repository rep, boolean allowDirectoryChange,
TransDialog.Tabs currentTab ) {
if ( transMeta == null ) {
return false;
}
TransDialog tid = new TransDialog( spoon.getShell(), SWT.NONE, transMeta, rep, currentTab );
tid.setDirectoryChangeAllowed( allowDirectoryChange );
TransMeta ti = tid.open();
// Load shared objects
//
if ( tid.isSharedObjectsFileChanged() ) {
try {
SharedObjects sharedObjects =
rep != null ? rep.readTransSharedObjects( transMeta ) : transMeta.readSharedObjects();
spoon.sharedObjectsFileMap.put( sharedObjects.getFilename(), sharedObjects );
} catch ( KettleException e ) {
// CHECKSTYLE:LineLength:OFF
new ErrorDialog( spoon.getShell(),
BaseMessages.getString( PKG, "Spoon.Dialog.ErrorReadingSharedObjects.Title" ), BaseMessages.getString( PKG,
"Spoon.Dialog.ErrorReadingSharedObjects.Message", spoon.makeTabName( transMeta, true ) ), e );
}
// If we added properties, add them to the variables too, so that they appear in the CTRL-SPACE variable
// completion.
//
spoon.setParametersAsVariablesInUI( transMeta, transMeta );
spoon.refreshTree();
spoon.delegates.tabs.renameTabs(); // cheap operation, might as will do it anyway
}
spoon.setShellText();
return ti != null;
}
public void openFile() {
spoon.openFile();
}
public void saveFile() throws KettleException {
spoon.saveFile();
}
public void saveFileAs() throws KettleException {
spoon.saveFileAs();
}
public void saveXMLFileToVfs() {
spoon.saveXMLFileToVfs();
}
public void printFile() {
spoon.printFile();
}
public void runTransformation() {
spoon.runFile();
}
public void runOptionsTransformation() {
spoon.runOptionsFile();
}
public void pauseTransformation() {
pauseResume();
}
public void stopTransformation() {
stop();
}
public void previewFile() {
spoon.previewFile();
}
public void debugFile() {
spoon.debugFile();
}
public void transReplay() {
spoon.replayTransformation();
}
public void checkTrans() {
spoon.checkTrans();
}
public void analyseImpact() {
spoon.analyseImpact();
}
public void getSQL() {
spoon.getSQL();
}
public void exploreDatabase() {
spoon.exploreDatabase();
}
public boolean isExecutionResultsPaneVisible() {
return extraViewComposite != null && !extraViewComposite.isDisposed();
}
public void showExecutionResults() {
if ( isExecutionResultsPaneVisible() ) {
disposeExtraView();
} else {
addAllTabs();
}
}
public void browseVersionHistory() {
try {
if ( spoon.rep.exists( transMeta.getName(), transMeta.getRepositoryDirectory(),
RepositoryObjectType.TRANSFORMATION ) ) {
RepositoryRevisionBrowserDialogInterface dialog =
RepositoryExplorerDialog.getVersionBrowserDialog( shell, spoon.rep, transMeta );
String versionLabel = dialog.open();
if ( versionLabel != null ) {
spoon.loadObjectFromRepository( transMeta.getName(), transMeta.getRepositoryElementType(), transMeta
.getRepositoryDirectory(), versionLabel );
}
} else {
MessageBox box = new MessageBox( shell, SWT.CLOSE | SWT.ICON_ERROR );
box.setText( "Sorry" );
box.setMessage( "Can't find this transformation in the repository" );
box.open();
}
} catch ( Exception e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.VersionBrowserException.Title" ), BaseMessages
.getString( PKG, "TransGraph.VersionBrowserException.Message" ), e );
}
}
/**
* If the extra tab view at the bottom is empty, we close it.
*/
public void checkEmptyExtraView() {
if ( extraViewTabFolder.getItemCount() == 0 ) {
disposeExtraView();
}
}
private void disposeExtraView() {
extraViewComposite.dispose();
sashForm.layout();
sashForm.setWeights( new int[] { 100, } );
XulToolbarbutton button = (XulToolbarbutton) toolbar.getElementById( "trans-show-results" );
button.setTooltiptext( BaseMessages.getString( PKG, "Spoon.Tooltip.ShowExecutionResults" ) );
ToolItem toolItem = (ToolItem) button.getManagedObject();
toolItem.setImage( GUIResource.getInstance().getImageShowResults() );
}
private void minMaxExtraView() {
// What is the state?
//
boolean maximized = sashForm.getMaximizedControl() != null;
if ( maximized ) {
// Minimize
//
sashForm.setMaximizedControl( null );
minMaxButton.setImage( GUIResource.getInstance().getImageMaximizePanel() );
minMaxButton
.setToolTipText( BaseMessages.getString( PKG, "TransGraph.ExecutionResultsPanel.MaxButton.Tooltip" ) );
} else {
// Maximize
//
sashForm.setMaximizedControl( extraViewComposite );
minMaxButton.setImage( GUIResource.getInstance().getImageMinimizePanel() );
minMaxButton
.setToolTipText( BaseMessages.getString( PKG, "TransGraph.ExecutionResultsPanel.MinButton.Tooltip" ) );
}
}
/**
* @return the toolbar
*/
public XulToolbar getToolbar() {
return toolbar;
}
/**
* @param toolbar the toolbar to set
*/
public void setToolbar( XulToolbar toolbar ) {
this.toolbar = toolbar;
}
private Label closeButton;
private Label minMaxButton;
/**
* Add an extra view to the main composite SashForm
*/
public void addExtraView() {
extraViewComposite = new Composite( sashForm, SWT.NONE );
FormLayout extraCompositeFormLayout = new FormLayout();
extraCompositeFormLayout.marginWidth = 2;
extraCompositeFormLayout.marginHeight = 2;
extraViewComposite.setLayout( extraCompositeFormLayout );
// Put a close and max button to the upper right corner...
//
closeButton = new Label( extraViewComposite, SWT.NONE );
closeButton.setImage( GUIResource.getInstance().getImageClosePanel() );
closeButton.setToolTipText( BaseMessages.getString( PKG, "TransGraph.ExecutionResultsPanel.CloseButton.Tooltip" ) );
FormData fdClose = new FormData();
fdClose.right = new FormAttachment( 100, 0 );
fdClose.top = new FormAttachment( 0, 0 );
closeButton.setLayoutData( fdClose );
closeButton.addMouseListener( new MouseAdapter() {
@Override
public void mouseDown( MouseEvent e ) {
disposeExtraView();
}
} );
minMaxButton = new Label( extraViewComposite, SWT.NONE );
minMaxButton.setImage( GUIResource.getInstance().getImageMaximizePanel() );
minMaxButton.setToolTipText( BaseMessages.getString( PKG, "TransGraph.ExecutionResultsPanel.MaxButton.Tooltip" ) );
FormData fdMinMax = new FormData();
fdMinMax.right = new FormAttachment( closeButton, -Const.MARGIN );
fdMinMax.top = new FormAttachment( 0, 0 );
minMaxButton.setLayoutData( fdMinMax );
minMaxButton.addMouseListener( new MouseAdapter() {
@Override
public void mouseDown( MouseEvent e ) {
minMaxExtraView();
}
} );
// Add a label at the top: Results
//
Label wResultsLabel = new Label( extraViewComposite, SWT.LEFT );
wResultsLabel.setFont( GUIResource.getInstance().getFontMediumBold() );
wResultsLabel.setBackground( GUIResource.getInstance().getColorWhite() );
wResultsLabel.setText( BaseMessages.getString( PKG, "TransLog.ResultsPanel.NameLabel" ) );
FormData fdResultsLabel = new FormData();
fdResultsLabel.left = new FormAttachment( 0, 0 );
fdResultsLabel.right = new FormAttachment( minMaxButton, -Const.MARGIN );
fdResultsLabel.top = new FormAttachment( 0, 0 );
wResultsLabel.setLayoutData( fdResultsLabel );
// Add a tab folder ...
//
extraViewTabFolder = new CTabFolder( extraViewComposite, SWT.MULTI );
spoon.props.setLook( extraViewTabFolder, Props.WIDGET_STYLE_TAB );
extraViewTabFolder.addMouseListener( new MouseAdapter() {
@Override
public void mouseDoubleClick( MouseEvent arg0 ) {
if ( sashForm.getMaximizedControl() == null ) {
sashForm.setMaximizedControl( extraViewComposite );
} else {
sashForm.setMaximizedControl( null );
}
}
} );
FormData fdTabFolder = new FormData();
fdTabFolder.left = new FormAttachment( 0, 0 );
fdTabFolder.right = new FormAttachment( 100, 0 );
fdTabFolder.top = new FormAttachment( wResultsLabel, Const.MARGIN );
fdTabFolder.bottom = new FormAttachment( 100, 0 );
extraViewTabFolder.setLayoutData( fdTabFolder );
sashForm.setWeights( new int[] { 60, 40, } );
}
public void checkErrors() {
if ( trans != null ) {
if ( !trans.isFinished() ) {
if ( trans.getErrors() != 0 ) {
trans.killAll();
}
}
}
}
public synchronized void start( TransExecutionConfiguration executionConfiguration ) throws KettleException {
// Auto save feature...
handleTransMetaChanges( transMeta );
if ( ( ( transMeta.getName() != null && transMeta.getObjectId() != null && spoon.rep != null ) || // Repository
// available &
// name / id set
( transMeta.getFilename() != null && spoon.rep == null ) // No repository & filename set
)
&& !transMeta.hasChanged() // Didn't change
) {
if ( trans == null || ( trans != null && !running ) ) {
try {
// Set the requested logging level..
//
DefaultLogLevel.setLogLevel( executionConfiguration.getLogLevel() );
transMeta.injectVariables( executionConfiguration.getVariables() );
// Set the named parameters
Map<String, String> paramMap = executionConfiguration.getParams();
Set<String> keys = paramMap.keySet();
for ( String key : keys ) {
transMeta.setParameterValue( key, Const.NVL( paramMap.get( key ), "" ) );
}
transMeta.activateParameters();
// Do we need to clear the log before running?
//
if ( executionConfiguration.isClearingLog() ) {
transLogDelegate.clearLog();
}
// Also make sure to clear the log entries in the central log store & registry
//
if ( trans != null ) {
KettleLogStore.discardLines( trans.getLogChannelId(), true );
}
// Important: even though transMeta is passed to the Trans constructor, it is not the same object as is in
// memory
// To be able to completely test this, we need to run it as we would normally do in pan
//
trans = createTrans();
trans.setRepository( spoon.getRepository() );
trans.setMetaStore( spoon.getMetaStore() );
String spoonLogObjectId = UUID.randomUUID().toString();
SimpleLoggingObject spoonLoggingObject = new SimpleLoggingObject( "SPOON", LoggingObjectType.SPOON, null );
spoonLoggingObject.setContainerObjectId( spoonLogObjectId );
spoonLoggingObject.setLogLevel( executionConfiguration.getLogLevel() );
trans.setParent( spoonLoggingObject );
trans.setLogLevel( executionConfiguration.getLogLevel() );
trans.setReplayDate( executionConfiguration.getReplayDate() );
trans.setRepository( executionConfiguration.getRepository() );
trans.setMonitored( true );
log.logBasic( BaseMessages.getString( PKG, "TransLog.Log.TransformationOpened" ) );
} catch ( KettleException e ) {
trans = null;
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransLog.Dialog.ErrorOpeningTransformation.Title" ),
BaseMessages.getString( PKG, "TransLog.Dialog.ErrorOpeningTransformation.Message" ), e );
}
if ( trans != null ) {
Map<String, String> arguments = executionConfiguration.getArguments();
final String[] args;
if ( arguments != null ) {
args = convertArguments( arguments );
} else {
args = null;
}
log.logMinimal( BaseMessages.getString( PKG, "TransLog.Log.LaunchingTransformation" )
+ trans.getTransMeta().getName() + "]..." );
trans.setSafeModeEnabled( executionConfiguration.isSafeModeEnabled() );
trans.setGatheringMetrics( executionConfiguration.isGatheringMetrics() );
// Launch the step preparation in a different thread.
// That way Spoon doesn't block anymore and that way we can follow the progress of the initialization
//
final Thread parentThread = Thread.currentThread();
shell.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
addAllTabs();
prepareTrans( parentThread, args );
}
} );
log.logMinimal( BaseMessages.getString( PKG, "TransLog.Log.StartedExecutionOfTransformation" ) );
setControlStates();
}
} else {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "TransLog.Dialog.DoNoStartTransformationTwice.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "TransLog.Dialog.DoNoStartTransformationTwice.Message" ) );
m.open();
}
} else {
if ( transMeta.hasChanged() ) {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "TransLog.Dialog.SaveTransformationBeforeRunning.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "TransLog.Dialog.SaveTransformationBeforeRunning.Message" ) );
m.open();
} else if ( spoon.rep != null && transMeta.getName() == null ) {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "TransLog.Dialog.GiveTransformationANameBeforeRunning.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "TransLog.Dialog.GiveTransformationANameBeforeRunning.Message" ) );
m.open();
} else {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "TransLog.Dialog.SaveTransformationBeforeRunning2.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "TransLog.Dialog.SaveTransformationBeforeRunning2.Message" ) );
m.open();
}
}
}
public void addAllTabs() {
CTabItem tabItemSelection = null;
if ( extraViewTabFolder != null && !extraViewTabFolder.isDisposed() ) {
tabItemSelection = extraViewTabFolder.getSelection();
}
transHistoryDelegate.addTransHistory();
transLogDelegate.addTransLog();
transGridDelegate.addTransGrid();
transPerfDelegate.addTransPerf();
transMetricsDelegate.addTransMetrics();
transPreviewDelegate.addTransPreview();
List<SpoonUiExtenderPluginInterface> relevantExtenders =
SpoonUiExtenderPluginType.getInstance().getRelevantExtenders( TransGraph.class, LOAD_TAB );
for ( SpoonUiExtenderPluginInterface relevantExtender : relevantExtenders ) {
relevantExtender.uiEvent( this, LOAD_TAB );
}
if ( tabItemSelection != null ) {
extraViewTabFolder.setSelection( tabItemSelection );
} else {
extraViewTabFolder.setSelection( transGridDelegate.getTransGridTab() );
}
XulToolbarbutton button = (XulToolbarbutton) toolbar.getElementById( "trans-show-results" );
button.setTooltiptext( BaseMessages.getString( PKG, "Spoon.Tooltip.HideExecutionResults" ) );
ToolItem toolItem = (ToolItem) button.getManagedObject();
toolItem.setImage( GUIResource.getInstance().getImageHideResults() );
}
public synchronized void debug( TransExecutionConfiguration executionConfiguration, TransDebugMeta transDebugMeta ) {
if ( !running ) {
try {
this.lastTransDebugMeta = transDebugMeta;
log.setLogLevel( executionConfiguration.getLogLevel() );
if ( log.isDetailed() ) {
log.logDetailed( BaseMessages.getString( PKG, "TransLog.Log.DoPreview" ) );
}
String[] args = null;
Map<String, String> arguments = executionConfiguration.getArguments();
if ( arguments != null ) {
args = convertArguments( arguments );
}
transMeta.injectVariables( executionConfiguration.getVariables() );
// Set the named parameters
Map<String, String> paramMap = executionConfiguration.getParams();
Set<String> keys = paramMap.keySet();
for ( String key : keys ) {
transMeta.setParameterValue( key, Const.NVL( paramMap.get( key ), "" ) );
}
transMeta.activateParameters();
// Do we need to clear the log before running?
//
if ( executionConfiguration.isClearingLog() ) {
transLogDelegate.clearLog();
}
// Do we have a previous execution to clean up in the logging registry?
//
if ( trans != null ) {
KettleLogStore.discardLines( trans.getLogChannelId(), false );
LoggingRegistry.getInstance().removeIncludingChildren( trans.getLogChannelId() );
}
// Create a new transformation to execution
//
trans = new Trans( transMeta );
trans.setSafeModeEnabled( executionConfiguration.isSafeModeEnabled() );
trans.setPreview( true );
trans.setGatheringMetrics( executionConfiguration.isGatheringMetrics() );
trans.setMetaStore( spoon.getMetaStore() );
trans.prepareExecution( args );
trans.setRepository( spoon.rep );
List<SpoonUiExtenderPluginInterface> relevantExtenders =
SpoonUiExtenderPluginType.getInstance().getRelevantExtenders( TransDebugMetaWrapper.class, PREVIEW_TRANS );
TransDebugMetaWrapper transDebugMetaWrapper = new TransDebugMetaWrapper( trans, transDebugMeta );
for ( SpoonUiExtenderPluginInterface relevantExtender : relevantExtenders ) {
relevantExtender.uiEvent( transDebugMetaWrapper, PREVIEW_TRANS );
}
// Add the row listeners to the allocated threads
//
transDebugMeta.addRowListenersToTransformation( trans );
// What method should we call back when a break-point is hit?
transDebugMeta.addBreakPointListers( new BreakPointListener() {
@Override
public void breakPointHit( TransDebugMeta transDebugMeta, StepDebugMeta stepDebugMeta,
RowMetaInterface rowBufferMeta, List<Object[]> rowBuffer ) {
showPreview( transDebugMeta, stepDebugMeta, rowBufferMeta, rowBuffer );
}
} );
// Do we capture data?
//
if ( transPreviewDelegate.isActive() ) {
transPreviewDelegate.capturePreviewData( trans, transMeta.getSteps() );
}
// Start the threads for the steps...
//
startThreads();
debug = true;
// Show the execution results view...
//
shell.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
addAllTabs();
}
} );
} catch ( Exception e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransLog.Dialog.UnexpectedErrorDuringPreview.Title" ),
BaseMessages.getString( PKG, "TransLog.Dialog.UnexpectedErrorDuringPreview.Message" ), e );
}
} else {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "TransLog.Dialog.DoNoPreviewWhileRunning.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "TransLog.Dialog.DoNoPreviewWhileRunning.Message" ) );
m.open();
}
checkErrorVisuals();
}
public synchronized void showPreview( final TransDebugMeta transDebugMeta, final StepDebugMeta stepDebugMeta,
final RowMetaInterface rowBufferMeta, final List<Object[]> rowBuffer ) {
shell.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
if ( isDisposed() ) {
return;
}
spoon.enableMenus();
// The transformation is now paused, indicate this in the log dialog...
//
pausing = true;
setControlStates();
checkErrorVisuals();
PreviewRowsDialog previewRowsDialog =
new PreviewRowsDialog(
shell, transMeta, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.APPLICATION_MODAL | SWT.SHEET,
stepDebugMeta.getStepMeta().getName(), rowBufferMeta, rowBuffer );
previewRowsDialog.setProposingToGetMoreRows( true );
previewRowsDialog.setProposingToStop( true );
previewRowsDialog.open();
if ( previewRowsDialog.isAskingForMoreRows() ) {
// clear the row buffer.
// That way if you click resume, you get the next N rows for the step :-)
//
rowBuffer.clear();
// Resume running: find more rows...
//
pauseResume();
}
if ( previewRowsDialog.isAskingToStop() ) {
// Stop running
//
stop();
}
}
} );
}
private String[] convertArguments( Map<String, String> arguments ) {
String[] argumentNames = arguments.keySet().toArray( new String[ arguments.size() ] );
Arrays.sort( argumentNames );
String[] args = new String[ argumentNames.length ];
for ( int i = 0; i < args.length; i++ ) {
String argumentName = argumentNames[ i ];
args[ i ] = arguments.get( argumentName );
}
return args;
}
public void stop() {
if ( running && !halting ) {
halting = true;
trans.stopAll();
log.logMinimal( BaseMessages.getString( PKG, "TransLog.Log.ProcessingOfTransformationStopped" ) );
running = false;
initialized = false;
halted = false;
halting = false;
setControlStates();
transMeta.setInternalKettleVariables(); // set the original vars back as they may be changed by a mapping
}
}
public synchronized void pauseResume() {
if ( running ) {
// Get the pause toolbar item
//
if ( !pausing ) {
pausing = true;
trans.pauseRunning();
setControlStates();
} else {
pausing = false;
trans.resumeRunning();
setControlStates();
}
}
}
private boolean controlDisposed( XulToolbarbutton button ) {
if ( button.getManagedObject() instanceof Widget ) {
Widget widget = (Widget) button.getManagedObject();
return widget.isDisposed();
}
return false;
}
@Override
public synchronized void setControlStates() {
if ( isDisposed() || getDisplay().isDisposed() ) {
return;
}
if ( ( (Control) toolbar.getManagedObject() ).isDisposed() ) {
return;
}
getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
boolean operationsNotAllowed = false;
try {
operationsNotAllowed =
RepositorySecurityUI.verifyOperations( shell, spoon.rep, false,
RepositoryOperation.EXECUTE_TRANSFORMATION );
} catch ( KettleRepositoryLostException krle ) {
log.logError( krle.getLocalizedMessage() );
spoon.handleRepositoryLost( krle );
}
// Start/Run button...
//
XulToolbarbutton runButton = (XulToolbarbutton) toolbar.getElementById( "trans-run" );
if ( runButton != null && !controlDisposed( runButton ) && !operationsNotAllowed ) {
if ( runButton.isDisabled() ^ running ) {
runButton.setDisabled( running );
}
}
// Pause button...
//
XulToolbarbutton pauseButton = (XulToolbarbutton) toolbar.getElementById( "trans-pause" );
if ( pauseButton != null && !controlDisposed( pauseButton ) ) {
if ( pauseButton.isDisabled() ^ !running ) {
pauseButton.setDisabled( !running );
pauseButton.setLabel( pausing ? RESUME_TEXT : PAUSE_TEXT );
pauseButton.setTooltiptext( pausing ? BaseMessages.getString( PKG, "Spoon.Tooltip.ResumeTranformation" )
: BaseMessages.getString( PKG, "Spoon.Tooltip.PauseTranformation" ) );
}
}
// Stop button...
//
XulToolbarbutton stopButton = (XulToolbarbutton) toolbar.getElementById( "trans-stop" );
if ( stopButton != null && !controlDisposed( stopButton ) ) {
if ( stopButton.isDisabled() ^ !running ) {
stopButton.setDisabled( !running );
}
}
// Debug button...
//
XulToolbarbutton debugButton = (XulToolbarbutton) toolbar.getElementById( "trans-debug" );
if ( debugButton != null && !controlDisposed( debugButton ) && !operationsNotAllowed ) {
if ( debugButton.isDisabled() ^ running ) {
debugButton.setDisabled( running );
}
}
// Preview button...
//
XulToolbarbutton previewButton = (XulToolbarbutton) toolbar.getElementById( "trans-preview" );
if ( previewButton != null && !controlDisposed( previewButton ) && !operationsNotAllowed ) {
if ( previewButton.isDisabled() ^ running ) {
previewButton.setDisabled( running );
}
}
}
} );
}
private synchronized void prepareTrans( final Thread parentThread, final String[] args ) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
trans.prepareExecution( args );
// Do we capture data?
//
if ( transPreviewDelegate.isActive() ) {
transPreviewDelegate.capturePreviewData( trans, transMeta.getSteps() );
}
initialized = true;
} catch ( KettleException e ) {
log.logError( trans.getName() + ": preparing transformation execution failed", e );
checkErrorVisuals();
}
halted = trans.hasHaltedSteps();
if ( trans.isReadyToStart() ) {
checkStartThreads(); // After init, launch the threads.
} else {
initialized = false;
running = false;
checkErrorVisuals();
}
}
};
Thread thread = new Thread( runnable );
thread.start();
}
private void checkStartThreads() {
if ( initialized && !running && trans != null ) {
startThreads();
}
}
private synchronized void startThreads() {
running = true;
try {
// Add a listener to the transformation.
// If the transformation is done, we want to do the end processing, etc.
//
trans.addTransListener( new org.pentaho.di.trans.TransAdapter() {
@Override
public void transFinished( Trans trans ) {
checkTransEnded();
checkErrorVisuals();
stopRedrawTimer();
transMetricsDelegate.resetLastRefreshTime();
transMetricsDelegate.updateGraph();
}
} );
trans.startThreads();
startRedrawTimer();
setControlStates();
} catch ( KettleException e ) {
log.logError( "Error starting step threads", e );
checkErrorVisuals();
stopRedrawTimer();
}
// See if we have to fire off the performance graph updater etc.
//
getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
if ( transPerfDelegate.getTransPerfTab() != null ) {
// If there is a tab open, try to the correct content on there now
//
transPerfDelegate.setupContent();
transPerfDelegate.layoutPerfComposite();
}
}
} );
}
private void startRedrawTimer() {
redrawTimer = new Timer( "TransGraph: redraw timer" );
TimerTask timtask = new TimerTask() {
@Override
public void run() {
if ( !spoon.getDisplay().isDisposed() ) {
spoon.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
if ( !TransGraph.this.canvas.isDisposed() ) {
TransGraph.this.canvas.redraw();
}
}
} );
}
}
};
redrawTimer.schedule( timtask, 0L, ConstUI.INTERVAL_MS_TRANS_CANVAS_REFRESH );
}
protected void stopRedrawTimer() {
if ( redrawTimer != null ) {
redrawTimer.cancel();
redrawTimer.purge();
redrawTimer = null;
}
}
private void checkTransEnded() {
if ( trans != null ) {
if ( trans.isFinished() && ( running || halted ) ) {
log.logMinimal( BaseMessages.getString( PKG, "TransLog.Log.TransformationHasFinished" ) );
running = false;
initialized = false;
halted = false;
halting = false;
setControlStates();
// OK, also see if we had a debugging session going on.
// If so and we didn't hit a breakpoint yet, display the show
// preview dialog...
//
if ( debug && lastTransDebugMeta != null && lastTransDebugMeta.getTotalNumberOfHits() == 0 ) {
debug = false;
showLastPreviewResults();
}
debug = false;
checkErrorVisuals();
shell.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
spoon.fireMenuControlers();
redraw();
}
} );
}
}
}
private void checkErrorVisuals() {
if ( trans.getErrors() > 0 ) {
// Get the logging text and filter it out. Store it in the stepLogMap...
//
stepLogMap = new HashMap<>();
shell.getDisplay().syncExec( new Runnable() {
@Override
public void run() {
for ( StepMetaDataCombi combi : trans.getSteps() ) {
if ( combi.step.getErrors() > 0 ) {
String channelId = combi.step.getLogChannel().getLogChannelId();
List<KettleLoggingEvent> eventList =
KettleLogStore.getLogBufferFromTo( channelId, false, 0, KettleLogStore.getLastBufferLineNr() );
StringBuilder logText = new StringBuilder();
for ( KettleLoggingEvent event : eventList ) {
Object message = event.getMessage();
if ( message instanceof LogMessage ) {
LogMessage logMessage = (LogMessage) message;
if ( logMessage.isError() ) {
logText.append( logMessage.getMessage() ).append( Const.CR );
}
}
}
stepLogMap.put( combi.stepMeta, logText.toString() );
}
}
}
} );
} else {
stepLogMap = null;
}
// Redraw the canvas to show the error icons etc.
//
shell.getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
redraw();
}
} );
}
public synchronized void showLastPreviewResults() {
if ( lastTransDebugMeta == null || lastTransDebugMeta.getStepDebugMetaMap().isEmpty() ) {
return;
}
final List<String> stepnames = new ArrayList<>();
final List<RowMetaInterface> rowMetas = new ArrayList<>();
final List<List<Object[]>> rowBuffers = new ArrayList<>();
// Assemble the buffers etc in the old style...
//
for ( StepMeta stepMeta : lastTransDebugMeta.getStepDebugMetaMap().keySet() ) {
StepDebugMeta stepDebugMeta = lastTransDebugMeta.getStepDebugMetaMap().get( stepMeta );
stepnames.add( stepMeta.getName() );
rowMetas.add( stepDebugMeta.getRowBufferMeta() );
rowBuffers.add( stepDebugMeta.getRowBuffer() );
}
getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
EnterPreviewRowsDialog dialog = new EnterPreviewRowsDialog( shell, SWT.NONE, stepnames, rowMetas, rowBuffers );
dialog.open();
}
} );
}
/**
* XUL dummy menu entry
*/
public void openMapping() {
}
/**
* Open the transformation mentioned in the mapping...
*/
public void openMapping( StepMeta stepMeta, int index ) {
try {
Object referencedMeta = null;
Trans subTrans = getActiveSubtransformation( this, stepMeta );
if ( subTrans != null
&& ( stepMeta.getStepMetaInterface().getActiveReferencedObjectDescription() == null || index < 0 ) ) {
TransMeta subTransMeta = subTrans.getTransMeta();
referencedMeta = subTransMeta;
Object[] objectArray = new Object[ 4 ];
objectArray[ 0 ] = stepMeta;
objectArray[ 1 ] = subTransMeta;
ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.OpenMapping.id, objectArray );
} else {
StepMetaInterface meta = stepMeta.getStepMetaInterface();
if ( !Utils.isEmpty( meta.getReferencedObjectDescriptions() ) ) {
referencedMeta = meta.loadReferencedObject( index, spoon.rep, spoon.metaStore, transMeta );
}
}
if ( referencedMeta == null ) {
return;
}
if ( referencedMeta instanceof TransMeta ) {
TransMeta mappingMeta = (TransMeta) referencedMeta;
mappingMeta.clearChanged();
spoon.addTransGraph( mappingMeta );
TransGraph subTransGraph = spoon.getActiveTransGraph();
attachActiveTrans( subTransGraph, this.currentStep );
}
if ( referencedMeta instanceof JobMeta ) {
JobMeta jobMeta = (JobMeta) referencedMeta;
jobMeta.clearChanged();
spoon.addJobGraph( jobMeta );
JobGraph jobGraph = spoon.getActiveJobGraph();
attachActiveJob( jobGraph, this.currentStep );
}
} catch ( Exception e ) {
new ErrorDialog( shell, BaseMessages.getString( PKG, "TransGraph.Exception.UnableToLoadMapping.Title" ),
BaseMessages.getString( PKG, "TransGraph.Exception.UnableToLoadMapping.Message" ), e );
}
}
/**
* Finds the last active transformation in the running job to the opened transMeta
*
* @param transGraph
* @param stepMeta
*/
private void attachActiveTrans( TransGraph transGraph, StepMeta stepMeta ) {
if ( trans != null && transGraph != null ) {
Trans subTransformation = trans.getActiveSubTransformation( stepMeta.getName() );
transGraph.setTrans( subTransformation );
if ( !transGraph.isExecutionResultsPaneVisible() ) {
transGraph.showExecutionResults();
}
transGraph.setControlStates();
}
}
/**
* Finds the last active transformation in the running job to the opened transMeta
*
* @param transGraph
* @param stepMeta
*/
private Trans getActiveSubtransformation( TransGraph transGraph, StepMeta stepMeta ) {
if ( trans != null && transGraph != null ) {
return trans.getActiveSubTransformation( stepMeta.getName() );
}
return null;
}
/**
* Finds the last active job in the running transformation to the opened jobMeta
*
* @param jobGraph
* @param stepMeta
*/
private void attachActiveJob( JobGraph jobGraph, StepMeta stepMeta ) {
if ( trans != null && jobGraph != null ) {
Job subJob = trans.getActiveSubjobs().get( stepMeta.getName() );
jobGraph.setJob( subJob );
if ( !jobGraph.isExecutionResultsPaneVisible() ) {
jobGraph.showExecutionResults();
}
jobGraph.setControlStates();
}
}
/**
* @return the running
*/
public boolean isRunning() {
return running;
}
/**
* @param running the running to set
*/
public void setRunning( boolean running ) {
this.running = running;
}
/**
* @return the lastTransDebugMeta
*/
public TransDebugMeta getLastTransDebugMeta() {
return lastTransDebugMeta;
}
/**
* @return the halting
*/
public boolean isHalting() {
return halting;
}
/**
* @param halting the halting to set
*/
public void setHalting( boolean halting ) {
this.halting = halting;
}
/**
* @return the stepLogMap
*/
public Map<StepMeta, String> getStepLogMap() {
return stepLogMap;
}
/**
* @param stepLogMap the stepLogMap to set
*/
public void setStepLogMap( Map<StepMeta, String> stepLogMap ) {
this.stepLogMap = stepLogMap;
}
public void dumpLoggingRegistry() {
LoggingRegistry registry = LoggingRegistry.getInstance();
Map<String, LoggingObjectInterface> loggingMap = registry.getMap();
for ( LoggingObjectInterface loggingObject : loggingMap.values() ) {
System.out.println( loggingObject.getLogChannelId() + " - " + loggingObject.getObjectName() + " - "
+ loggingObject.getObjectType() );
}
}
@Override
public HasLogChannelInterface getLogChannelProvider() {
return trans;
}
public synchronized void setTrans( Trans trans ) {
this.trans = trans;
if ( trans != null ) {
pausing = trans.isPaused();
initialized = trans.isInitializing();
running = trans.isRunning();
halted = trans.isStopped();
if ( running ) {
trans.addTransListener( new org.pentaho.di.trans.TransAdapter() {
@Override
public void transFinished( Trans trans ) {
checkTransEnded();
checkErrorVisuals();
}
} );
}
}
}
public void sniffInput() {
sniff( true, false, false );
}
public void sniffOutput() {
sniff( false, true, false );
}
public void sniffError() {
sniff( false, false, true );
}
public void sniff( final boolean input, final boolean output, final boolean error ) {
StepMeta stepMeta = getCurrentStep();
if ( stepMeta == null || trans == null ) {
return;
}
final StepInterface runThread = trans.findRunThread( stepMeta.getName() );
if ( runThread != null ) {
List<Object[]> rows = new ArrayList<>();
final PreviewRowsDialog dialog = new PreviewRowsDialog( shell, trans, SWT.NONE, stepMeta.getName(), null, rows );
dialog.setDynamic( true );
// Add a row listener that sends the rows over to the dialog...
//
final RowListener rowListener = new RowListener() {
@Override
public void rowReadEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
if ( input ) {
try {
dialog.addDataRow( rowMeta, rowMeta.cloneRow( row ) );
} catch ( KettleValueException e ) {
throw new KettleStepException( e );
}
}
}
@Override
public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
if ( output ) {
try {
dialog.addDataRow( rowMeta, rowMeta.cloneRow( row ) );
} catch ( KettleValueException e ) {
throw new KettleStepException( e );
}
}
}
@Override
public void errorRowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
if ( error ) {
try {
dialog.addDataRow( rowMeta, rowMeta.cloneRow( row ) );
} catch ( KettleValueException e ) {
throw new KettleStepException( e );
}
}
}
};
// When the dialog is closed, make sure to remove the listener!
//
dialog.addDialogClosedListener( new DialogClosedListener() {
@Override
public void dialogClosed() {
runThread.removeRowListener( rowListener );
}
} );
// Open the dialog in a separate thread to make sure it doesn't block
//
getDisplay().asyncExec( new Runnable() {
@Override
public void run() {
dialog.open();
}
} );
runThread.addRowListener( rowListener );
}
}
@Override
public String getName() {
return "transgraph";
}
/*
* (non-Javadoc)
*
* @see org.pentaho.ui.xul.impl.XulEventHandler#getXulDomContainer()
*/
@Override
public XulDomContainer getXulDomContainer() {
return xulDomContainer;
}
@Override
public void setName( String arg0 ) {
}
/*
* (non-Javadoc)
*
* @see org.pentaho.ui.xul.impl.XulEventHandler#setXulDomContainer(org.pentaho.ui.xul.XulDomContainer)
*/
@Override
public void setXulDomContainer( XulDomContainer xulDomContainer ) {
this.xulDomContainer = xulDomContainer;
}
@Override
public boolean canHandleSave() {
return true;
}
@Override
public int showChangedWarning() throws KettleException {
return showChangedWarning( transMeta.getName() );
}
private class StepVelocity {
public StepVelocity( double dx, double dy ) {
this.dx = dx;
this.dy = dy;
}
public double dx, dy;
}
private class StepLocation {
public StepLocation( double x, double y ) {
this.x = x;
this.y = y;
}
public double x, y;
public void add( StepLocation loc ) {
x += loc.x;
y += loc.y;
}
}
private class Force {
public Force( double fx, double fy ) {
this.fx = fx;
this.fy = fy;
}
public double fx, fy;
public void add( Force force ) {
fx += force.fx;
fy += force.fy;
}
}
private static double dampningConstant = 0.5;
// private static double springConstant = 1.0;
private static double timeStep = 1.0;
private static double nodeMass = 1.0;
/**
* Perform an automatic layout of a transformation based on "Force-based algorithms". Source:
* http://en.wikipedia.org/wiki/Force-based_algorithms_(graph_drawing)
* <p/>
* set up initial node velocities to (0,0) set up initial node positions randomly // make sure no 2 nodes are in
* exactly the same position loop total_kinetic_energy := 0 // running sum of total kinetic energy over all particles
* for each node net-force := (0, 0) // running sum of total force on this particular node
* <p/>
* for each other node net-force := net-force + Coulomb_repulsion( this_node, other_node ) next node
* <p/>
* for each spring connected to this node net-force := net-force + Hooke_attraction( this_node, spring ) next spring
* <p/>
* // without damping, it moves forever this_node.velocity := (this_node.velocity + timestep * net-force) * damping
* this_node.position := this_node.position + timestep * this_node.velocity total_kinetic_energy :=
* total_kinetic_energy + this_node.mass * (this_node.velocity)^2 next node until total_kinetic_energy is less than
* some small number // the simulation has stopped moving
*/
public void autoLayout() {
// Initialize...
//
Map<StepMeta, StepVelocity> speeds = new HashMap<>();
Map<StepMeta, StepLocation> locations = new HashMap<>();
for ( StepMeta stepMeta : transMeta.getSteps() ) {
speeds.put( stepMeta, new StepVelocity( 0, 0 ) );
StepLocation location = new StepLocation( stepMeta.getLocation().x, stepMeta.getLocation().y );
locations.put( stepMeta, location );
}
StepLocation center = calculateCenter( locations );
// Layout loop!
//
double totalKineticEngergy = 0;
do {
totalKineticEngergy = 0;
for ( StepMeta stepMeta : transMeta.getSteps() ) {
Force netForce = new Force( 0, 0 );
StepVelocity velocity = speeds.get( stepMeta );
StepLocation location = locations.get( stepMeta );
for ( StepMeta otherStep : transMeta.getSteps() ) {
if ( !stepMeta.equals( otherStep ) ) {
netForce.add( getCoulombRepulsion( stepMeta, otherStep, locations ) );
}
}
for ( int i = 0; i < transMeta.nrTransHops(); i++ ) {
TransHopMeta hopMeta = transMeta.getTransHop( i );
if ( hopMeta.getFromStep().equals( stepMeta ) || hopMeta.getToStep().equals( stepMeta ) ) {
netForce.add( getHookeAttraction( hopMeta, locations ) );
}
}
adjustVelocity( velocity, netForce );
adjustLocation( location, velocity );
totalKineticEngergy += nodeMass * ( velocity.dx * velocity.dx + velocity.dy * velocity.dy );
}
StepLocation newCenter = calculateCenter( locations );
StepLocation diff = new StepLocation( center.x - newCenter.x, center.y - newCenter.y );
for ( StepMeta stepMeta : transMeta.getSteps() ) {
StepLocation location = locations.get( stepMeta );
location.x += diff.x;
location.y += diff.y;
stepMeta.setLocation( (int) Math.round( location.x ), (int) Math.round( location.y ) );
}
// redraw...
//
redraw();
} while ( totalKineticEngergy < 0.01 );
}
private StepLocation calculateCenter( Map<StepMeta, StepLocation> locations ) {
StepLocation center = new StepLocation( 0, 0 );
for ( StepLocation location : locations.values() ) {
center.add( location );
}
center.x /= locations.size();
center.y /= locations.size();
return center;
}
/**
* http://en.wikipedia.org/wiki/Coulomb's_law
*
* @param step1
* @param step2
* @param locations
* @return
*/
private Force getCoulombRepulsion( StepMeta step1, StepMeta step2, Map<StepMeta, StepLocation> locations ) {
double q1 = 4.0;
double q2 = 4.0;
double Ke = -3.0;
StepLocation loc1 = locations.get( step1 );
StepLocation loc2 = locations.get( step2 );
double fx = Ke * q1 * q2 / Math.abs( loc1.x - loc2.x );
double fy = Ke * q1 * q2 / Math.abs( loc1.y - loc2.y );
return new Force( fx, fy );
}
/**
* The longer the hop, the higher the force
*
* @param hopMeta
* @param locations
* @return
*/
private Force getHookeAttraction( TransHopMeta hopMeta, Map<StepMeta, StepLocation> locations ) {
StepLocation loc1 = locations.get( hopMeta.getFromStep() );
StepLocation loc2 = locations.get( hopMeta.getToStep() );
double springConstant = 0.01;
double fx = springConstant * Math.abs( loc1.x - loc2.x );
double fy = springConstant * Math.abs( loc1.y - loc2.y );
return new Force( fx * fx, fy * fy );
}
private void adjustVelocity( StepVelocity velocity, Force netForce ) {
velocity.dx = ( velocity.dx + timeStep * netForce.fx ) * dampningConstant;
velocity.dy = ( velocity.dy + timeStep * netForce.fy ) * dampningConstant;
}
private void adjustLocation( StepLocation location, StepVelocity velocity ) {
location.x = location.x + nodeMass * velocity.dx * velocity.dx;
location.y = location.y + nodeMass * velocity.dy * velocity.dy;
}
public void handleTransMetaChanges( TransMeta transMeta ) throws KettleException {
if ( transMeta.hasChanged() ) {
if ( spoon.props.getAutoSave() ) {
spoon.saveToFile( transMeta );
} else {
MessageDialogWithToggle md =
new MessageDialogWithToggle( shell, BaseMessages.getString( PKG, "TransLog.Dialog.FileHasChanged.Title" ),
null, BaseMessages.getString( PKG, "TransLog.Dialog.FileHasChanged1.Message" ) + Const.CR
+ BaseMessages.getString( PKG, "TransLog.Dialog.FileHasChanged2.Message" ) + Const.CR,
MessageDialog.QUESTION, new String[] { BaseMessages.getString( PKG, "System.Button.Yes" ),
BaseMessages.getString( PKG, "System.Button.No" ) }, 0, BaseMessages.getString( PKG,
"TransLog.Dialog.Option.AutoSaveTransformation" ), spoon.props.getAutoSave() );
MessageDialogWithToggle.setDefaultImage( GUIResource.getInstance().getImageSpoon() );
int answer = md.open();
if ( ( answer & 0xFF ) == 0 ) {
spoon.saveToFile( transMeta );
}
spoon.props.setAutoSave( md.getToggleState() );
}
}
}
private StepMeta lastChained = null;
public void addStepToChain( PluginInterface stepPlugin, boolean shift ) {
TransMeta transMeta = spoon.getActiveTransformation();
if ( transMeta == null ) {
return;
}
// Is the lastChained entry still valid?
//
if ( lastChained != null && transMeta.findStep( lastChained.getName() ) == null ) {
lastChained = null;
}
// If there is exactly one selected step, pick that one as last chained.
//
List<StepMeta> sel = transMeta.getSelectedSteps();
if ( sel.size() == 1 ) {
lastChained = sel.get( 0 );
}
// Where do we add this?
Point p = null;
if ( lastChained == null ) {
p = transMeta.getMaximum();
p.x -= 100;
} else {
p = new Point( lastChained.getLocation().x, lastChained.getLocation().y );
}
p.x += 200;
// Which is the new step?
StepMeta newStep = spoon.newStep( transMeta, stepPlugin.getName(), stepPlugin.getName(), false, true );
if ( newStep == null ) {
return;
}
newStep.setLocation( p.x, p.y );
newStep.setDraw( true );
if ( lastChained != null ) {
TransHopMeta hop = new TransHopMeta( lastChained, newStep );
spoon.newHop( transMeta, hop );
}
lastChained = newStep;
spoon.refreshGraph();
spoon.refreshTree();
if ( shift ) {
editStep( newStep );
}
transMeta.unselectAll();
newStep.setSelected( true );
}
public Spoon getSpoon() {
return spoon;
}
public void setSpoon( Spoon spoon ) {
this.spoon = spoon;
}
public TransMeta getTransMeta() {
return transMeta;
}
public Trans getTrans() {
return trans;
}
/**
* Creates the appropriate trans. Either
* 1) A {@link TransEngineAdapter} wrapping an {@link Engine}
* if an alternate execution engine has been selected
* 2) A legacy {@link Trans} otherwise.
*/
private Trans createTrans() throws KettleException {
if ( Utils.isEmpty( transMeta.getVariable( "engine" ) ) ) {
log.logBasic( "Using legacy execution engine" );
return createLegacyTrans();
}
return PluginRegistry.getInstance().getPlugins( EnginePluginType.class ).stream()
.filter( useThisEngine() )
.findFirst()
.map( plugin -> (Engine) loadPlugin( plugin ) )
.map( engine -> {
log.logBasic( "Using execution engine " + engine.getClass().getCanonicalName() );
return (Trans) new TransEngineAdapter( engine, transMeta );
} )
.orElseThrow( () -> new KettleException( "Unable to find engine [" + transMeta.getVariable( "engine" ) + "]" ) );
}
/**
* Uses a trans variable called "engine" to determine which engine to use.
* Will be replaced when UI engine selection is available.
*
* @return
*/
private Predicate<PluginInterface> useThisEngine() {
return plugin -> Arrays.stream( plugin.getIds() )
.filter( id -> id.equals( ( transMeta.getVariable( "engine" ) ) ) )
.findAny()
.isPresent();
}
private Trans createLegacyTrans() {
try {
return new Trans( transMeta, spoon.rep, transMeta.getName(),
transMeta.getRepositoryDirectory().getPath(),
transMeta.getFilename() );
} catch ( KettleException e ) {
throw new RuntimeException( e );
}
}
private Object loadPlugin( PluginInterface plugin ) {
try {
return PluginRegistry.getInstance().loadClass( plugin );
} catch ( KettlePluginException e ) {
throw new RuntimeException( e );
}
}
}