// 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.job;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.Callable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
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.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
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.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.EngineMetaInterface;
import org.pentaho.di.core.NotePadMeta;
import org.pentaho.di.core.Props;
import org.pentaho.di.core.Result;
import org.pentaho.di.core.ResultFile;
import org.pentaho.di.core.RowMetaAndData;
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.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.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.HasLogChannelInterface;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.logging.LogParentProvidedInterface;
import org.pentaho.di.core.logging.LoggingObjectType;
import org.pentaho.di.core.logging.SimpleLoggingObject;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.Job;
import org.pentaho.di.job.JobAdapter;
import org.pentaho.di.job.JobEntryListener;
import org.pentaho.di.job.JobEntryResult;
import org.pentaho.di.job.JobExecutionConfiguration;
import org.pentaho.di.job.JobHopMeta;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.job.JobPainter;
import org.pentaho.di.job.entries.abort.JobEntryAbort;
import org.pentaho.di.job.entries.job.JobEntryJob;
import org.pentaho.di.job.entries.trans.JobEntryTrans;
import org.pentaho.di.job.entry.JobEntryCopy;
import org.pentaho.di.job.entry.JobEntryInterface;
import org.pentaho.di.repository.KettleRepositoryLostException;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.repository.RepositoryDirectoryInterface;
import org.pentaho.di.repository.RepositoryObjectType;
import org.pentaho.di.repository.RepositoryOperation;
import org.pentaho.di.shared.SharedObjects;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransPainter;
import org.pentaho.di.ui.core.ConstUI;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.dialog.EnterTextDialog;
import org.pentaho.di.ui.core.dialog.ErrorDialog;
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.job.dialog.JobDialog;
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.SwtScrollBar;
import org.pentaho.di.ui.spoon.TabItemInterface;
import org.pentaho.di.ui.spoon.TabMapEntry;
import org.pentaho.di.ui.spoon.TabMapEntry.ObjectType;
import org.pentaho.di.ui.spoon.XulSpoonResourceBundle;
import org.pentaho.di.ui.spoon.XulSpoonSettingsManager;
import org.pentaho.di.ui.spoon.dialog.NotePadDialog;
import org.pentaho.di.ui.spoon.trans.DelayListener;
import org.pentaho.di.ui.spoon.trans.DelayTimer;
import org.pentaho.di.ui.spoon.trans.TransGraph;
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;
/**
* Handles the display of Jobs in Spoon, in a graphical form.
*
* @author Matt Created on 17-may-2003
*
*/
public class JobGraph extends AbstractGraph implements XulEventHandler, Redrawable, TabItemInterface,
LogParentProvidedInterface, MouseListener, MouseMoveListener, MouseTrackListener, MouseWheelListener,
KeyListener {
private static Class<?> PKG = JobGraph.class; // for i18n purposes, needed by Translator2!!
private static final String XUL_FILE_JOB_GRAPH = "ui/job-graph.xul";
public static final String START_TEXT = BaseMessages.getString( PKG, "JobLog.Button.Start" );
public static final String STOP_TEXT = BaseMessages.getString( PKG, "JobLog.Button.Stop" );
private static final String STRING_PARALLEL_WARNING_PARAMETER = "ParallelJobEntriesWarning";
private static final int HOP_SEL_MARGIN = 9;
protected Shell shell;
protected LogChannelInterface log;
protected JobMeta jobMeta;
public Job job;
protected PropsUI props;
protected int iconsize;
protected int linewidth;
protected Point lastclick;
protected List<JobEntryCopy> selectedEntries;
protected JobEntryCopy selectedEntry;
protected Point[] previousLocations;
private List<NotePadMeta> selectedNotes;
protected NotePadMeta selectedNote;
protected Point previous_note_location;
protected Point lastMove;
protected JobHopMeta hop_candidate;
protected Point drop_candidate;
protected Spoon spoon;
// public boolean shift, control;
protected boolean split_hop;
protected int lastButton;
protected JobHopMeta last_hop_split;
protected org.pentaho.di.core.gui.Rectangle selectionRegion;
protected static final double theta = Math.toRadians( 10 ); // arrowhead sharpness
protected static final int size = 30; // arrowhead length
protected int shadowsize;
protected Map<String, XulMenupopup> menuMap = new HashMap<String, XulMenupopup>();
protected int currentMouseX = 0;
protected int currentMouseY = 0;
protected JobEntryCopy jobEntry;
protected NotePadMeta ni = null;
protected JobHopMeta currentHop;
// private Text filenameLabel;
private SashForm sashForm;
public Composite extraViewComposite;
public CTabFolder extraViewTabFolder;
private XulToolbar toolbar;
public JobLogDelegate jobLogDelegate;
public JobHistoryDelegate jobHistoryDelegate;
public JobGridDelegate jobGridDelegate;
public JobMetricsDelegate jobMetricsDelegate;
private Composite mainComposite;
private List<RefreshListener> refreshListeners;
private Label closeButton;
private Label minMaxButton;
private CheckBoxToolTip helpTip;
private List<AreaOwner> areaOwners;
private List<JobEntryCopy> mouseOverEntries;
/** A map that keeps track of which log line was written by which job entry */
private Map<JobEntryCopy, String> entryLogMap;
private Map<JobEntryCopy, DelayTimer> delayTimers;
private JobEntryCopy startHopEntry;
private Point endHopLocation;
private JobEntryCopy endHopEntry;
private JobEntryCopy noInputEntry;
private DefaultToolTip toolTip;
private Point[] previous_step_locations;
private Point[] previous_note_locations;
private JobEntryCopy currentEntry;
public JobGraph( Composite par, final Spoon spoon, final JobMeta jobMeta ) {
super( par, SWT.NONE );
shell = par.getShell();
this.log = spoon.getLog();
this.spoon = spoon;
this.jobMeta = jobMeta;
spoon.selectionFilter.setText( "" );
this.props = PropsUI.getInstance();
this.areaOwners = new ArrayList<AreaOwner>();
this.mouseOverEntries = new ArrayList<JobEntryCopy>();
this.delayTimers = new HashMap<JobEntryCopy, DelayTimer>();
jobLogDelegate = new JobLogDelegate( spoon, this );
jobHistoryDelegate = new JobHistoryDelegate( spoon, this );
jobGridDelegate = new JobGridDelegate( spoon, this );
jobMetricsDelegate = new JobMetricsDelegate( spoon, this );
refreshListeners = new ArrayList<RefreshListener>();
try {
KettleXulLoader loader = new KettleXulLoader();
loader.setIconsSize( 16, 16 );
loader.setSettingsManager( XulSpoonSettingsManager.getInstance() );
ResourceBundle bundle = new XulSpoonResourceBundle( JobGraph.class );
XulDomContainer container = loader.loadXul( XUL_FILE_JOB_GRAPH, bundle );
container.addEventHandler( this );
SpoonPluginManager.getInstance().applyPluginsForContainer( "job-graph", xulDomContainer );
setXulDomContainer( container );
} catch ( XulException e1 ) {
log.logError( toString(), Const.getStackTracker( 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();
// 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 {
Document doc = xulDomContainer.getDocumentRoot();
menuMap.put( "job-graph-hop", (XulMenupopup) doc.getElementById( "job-graph-hop" ) );
menuMap.put( "job-graph-note", (XulMenupopup) doc.getElementById( "job-graph-note" ) );
menuMap.put( "job-graph-background", (XulMenupopup) doc.getElementById( "job-graph-background" ) );
menuMap.put( "job-graph-entry", (XulMenupopup) doc.getElementById( "job-graph-entry" ) );
} catch ( Throwable t ) {
log.logError( Const.getStackTracker( t ) );
new ErrorDialog(
shell, BaseMessages.getString( PKG, "JobGraph.Exception.ErrorReadingXULFile.Title" ), BaseMessages
.getString( PKG, "JobGraph.Exception.ErrorReadingXULFile.Message", Spoon.XUL_FILE_MENUS ),
new Exception( t ) );
}
toolTip = new DefaultToolTip( canvas, ToolTip.NO_RECREATE, true );
toolTip.setRespectMonitorBounds( true );
toolTip.setRespectDisplayBounds( true );
toolTip.setPopupDelay( 350 );
toolTip.setShift( new org.eclipse.swt.graphics.Point( ConstUI.TOOLTIP_OFFSET, ConstUI.TOOLTIP_OFFSET ) );
helpTip = new CheckBoxToolTip( canvas );
helpTip.addCheckBoxToolTipListener( new CheckBoxToolTipListener() {
public void checkBoxSelected( boolean enabled ) {
spoon.props.setShowingHelpToolTips( enabled );
}
} );
newProps();
selectionRegion = null;
hop_candidate = null;
last_hop_split = null;
selectedEntries = null;
selectedNote = null;
hori = canvas.getHorizontalBar();
vert = canvas.getVerticalBar();
hori.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent e ) {
redraw();
}
} );
vert.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent e ) {
redraw();
}
} );
hori.setThumb( 100 );
vert.setThumb( 100 );
hori.setVisible( true );
vert.setVisible( true );
setVisible( true );
canvas.addPaintListener( new PaintListener() {
public void paintControl( PaintEvent e ) {
JobGraph.this.paintControl( e );
}
} );
selectedEntries = null;
lastclick = null;
canvas.addMouseListener( this );
canvas.addMouseMoveListener( this );
canvas.addMouseTrackListener( this );
canvas.addMouseWheelListener( 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() {
public void dragEnter( DropTargetEvent event ) {
drop_candidate = PropsUI.calculateGridPosition( getRealPosition( canvas, event.x, event.y ) );
redraw();
}
public void dragLeave( DropTargetEvent event ) {
drop_candidate = null;
redraw();
}
public void dragOperationChanged( DropTargetEvent event ) {
}
public void dragOver( DropTargetEvent event ) {
drop_candidate = PropsUI.calculateGridPosition( getRealPosition( canvas, event.x, event.y ) );
redraw();
}
public void drop( DropTargetEvent event ) {
// no data to copy, indicate failure in event.detail
if ( event.data == null ) {
event.detail = DND.DROP_NONE;
return;
}
Point p = getRealPosition( canvas, event.x, event.y );
try {
DragAndDropContainer container = (DragAndDropContainer) event.data;
String entry = container.getData();
switch ( container.getType() ) {
case DragAndDropContainer.TYPE_BASE_JOB_ENTRY: // Create a new Job Entry on the canvas
JobEntryCopy jge = spoon.newJobEntry( jobMeta, entry, false );
if ( jge != null ) {
PropsUI.setLocation( jge, p.x, p.y );
jge.setDrawn();
redraw();
// See if we want to draw a tool tip explaining how to create new hops...
//
if ( jobMeta.nrJobEntries() > 1
&& jobMeta.nrJobEntries() < 5 && spoon.props.isShowingHelpToolTips() ) {
showHelpTip(
p.x, p.y, BaseMessages.getString( PKG, "JobGraph.HelpToolTip.CreatingHops.Title" ),
BaseMessages.getString( PKG, "JobGraph.HelpToolTip.CreatingHops.Message" ) );
}
}
break;
case DragAndDropContainer.TYPE_JOB_ENTRY: // Drag existing one onto the canvas
jge = jobMeta.findJobEntry( entry, 0, true );
if ( jge != null ) {
// Create duplicate of existing entry
// There can be only 1 start!
if ( jge.isStart() && jge.isDrawn() ) {
showOnlyStartOnceMessage( shell );
return;
}
boolean jge_changed = false;
// For undo :
JobEntryCopy before = (JobEntryCopy) jge.clone_deep();
JobEntryCopy newjge = jge;
if ( jge.isDrawn() ) {
newjge = (JobEntryCopy) jge.clone();
if ( newjge != null ) {
// newjge.setEntry(jge.getEntry());
if ( log.isDebug() ) {
log.logDebug( "entry aft = " + ( (Object) jge.getEntry() ).toString() );
}
newjge.setNr( jobMeta.findUnusedNr( newjge.getName() ) );
jobMeta.addJobEntry( newjge );
spoon.addUndoNew( jobMeta, new JobEntryCopy[] { newjge }, new int[] { jobMeta
.indexOfJobEntry( newjge ) } );
} else {
if ( log.isDebug() ) {
log.logDebug( "jge is not cloned!" );
}
}
} else {
if ( log.isDebug() ) {
log.logDebug( jge.toString() + " is not drawn" );
}
jge_changed = true;
}
PropsUI.setLocation( newjge, p.x, p.y );
newjge.setDrawn();
if ( jge_changed ) {
spoon.addUndoChange(
jobMeta, new JobEntryCopy[] { before }, new JobEntryCopy[] { newjge }, new int[] { jobMeta
.indexOfJobEntry( newjge ) } );
}
redraw();
spoon.refreshTree();
log.logBasic( "DropTargetEvent", "DROP "
+ newjge.toString() + "!, type=" + newjge.getEntry().getPluginId() );
} else {
log.logError( "Unknown job entry dropped onto the canvas." );
}
break;
default:
break;
}
} catch ( Exception e ) {
new ErrorDialog(
shell, BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorDroppingObject.Message" ), BaseMessages
.getString( PKG, "JobGraph.Dialog.ErrorDroppingObject.Title" ), e );
}
}
public void dropAccept( DropTargetEvent event ) {
drop_candidate = null;
}
} );
canvas.addKeyListener( this );
setBackground( GUIResource.getInstance().getColorBackground() );
setControlStates();
// Add a timer to set correct the state of the run/stop buttons every 2 seconds...
//
final Timer timer = new Timer( "JobGraph.setControlStates Timer: " + getMeta().getName() );
TimerTask timerTask = new TimerTask() {
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() {
public void widgetDisposed( DisposeEvent arg0 ) {
timer.cancel();
}
} );
}
protected void hideToolTips() {
toolTip.hide();
helpTip.hide();
}
public void mouseDoubleClick( MouseEvent e ) {
clearSettings();
Point real = screen2real( e.x, e.y );
// Hide the tooltip!
hideToolTips();
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.JobGraphMouseDoubleClick.id,
new JobGraphExtension( this, e, real ) );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling JobGraphMouseDoubleClick extension point", ex );
}
JobEntryCopy jobentry = jobMeta.getJobEntryCopy( real.x, real.y, iconsize );
if ( jobentry != null ) {
if ( e.button == 1 ) {
editEntry( jobentry );
} else {
// open tab in Spoon
launchStuff( jobentry );
}
} else {
// Check if point lies on one of the many hop-lines...
JobHopMeta online = findJobHop( real.x, real.y );
if ( online == null ) {
NotePadMeta ni = jobMeta.getNote( real.x, real.y );
if ( ni != null ) {
editNote( ni );
} else {
// Clicked on the background...
//
editJobProperties();
}
}
}
}
public void mouseDown( MouseEvent e ) {
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.JobGraphMouseDown.id,
new JobGraphExtension( this, e, real ) );
} catch ( Exception ex ) {
LogChannel.GENERAL.logError( "Error calling JobGraphMouseDown 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 JOB_ENTRY_MINI_ICON_OUTPUT:
// Click on the output icon means: start of drag
// Action: We show the input icons on the other steps...
//
selectedEntry = null;
startHopEntry = (JobEntryCopy) areaOwner.getOwner();
// stopEntryMouseOverDelayTimer(startHopEntry);
break;
case JOB_ENTRY_MINI_ICON_INPUT:
// Click on the input icon means: start to a new hop
// In this case, we set the end hop step...
//
selectedEntry = null;
startHopEntry = null;
endHopEntry = (JobEntryCopy) areaOwner.getOwner();
// stopEntryMouseOverDelayTimer(endHopEntry);
break;
case JOB_ENTRY_MINI_ICON_EDIT:
clearSettings();
currentEntry = (JobEntryCopy) areaOwner.getOwner();
stopEntryMouseOverDelayTimer( currentEntry );
editEntry( currentEntry );
break;
case JOB_ENTRY_MINI_ICON_CONTEXT:
clearSettings();
JobEntryCopy jobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
setMenu( jobEntryCopy.getLocation().x, jobEntryCopy.getLocation().y );
break;
case JOB_ENTRY_ICON:
jobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
currentEntry = jobEntryCopy;
if ( hop_candidate != null ) {
addCandidateAsHop();
} else if ( e.button == 2 || ( e.button == 1 && shift ) ) {
// SHIFT CLICK is start of drag to create a new hop
//
startHopEntry = jobEntryCopy;
} else {
selectedEntries = jobMeta.getSelectedEntries();
selectedEntry = jobEntryCopy;
//
// 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 = jobMeta.getSelectedLocations();
Point p = jobEntryCopy.getLocation();
iconoffset = new Point( real.x - p.x, real.y - p.y );
}
redraw();
break;
case NOTE:
ni = (NotePadMeta) areaOwner.getOwner();
selectedNotes = jobMeta.getSelectedNotes();
selectedNote = ni;
Point loc = ni.getLocation();
previous_note_locations = jobMeta.getSelectedNoteLocations();
noteoffset = new Point( real.x - loc.x, real.y - loc.y );
redraw();
break;
// If you click on an evaluating icon, change the evaluation...
//
case JOB_HOP_ICON:
JobHopMeta hop = (JobHopMeta) areaOwner.getOwner();
if ( hop.getFromEntry().evaluates() ) {
if ( hop.isUnconditional() ) {
hop.setUnconditional( false );
hop.setEvaluation( true );
} else {
if ( hop.getEvaluation() ) {
hop.setEvaluation( false );
} else {
hop.setUnconditional( true );
}
}
spoon.setShellText();
redraw();
}
break;
default:
break;
}
} else {
// A hop? --> enable/disable
//
JobHopMeta hop = findJobHop( real.x, real.y );
if ( hop != null ) {
JobHopMeta before = (JobHopMeta) hop.clone();
hop.setEnabled( !hop.isEnabled() );
JobHopMeta after = (JobHopMeta) hop.clone();
spoon.addUndoChange(
jobMeta, new JobHopMeta[] { before }, new JobHopMeta[] { after }, new int[] { jobMeta
.indexOfJobHop( hop ) } );
spoon.setShellText();
redraw();
} else {
// No area-owner means: background:
//
startHopEntry = null;
if ( !control ) {
selectionRegion = new org.pentaho.di.core.gui.Rectangle( real.x, real.y, 0, 0 );
}
redraw();
}
}
}
}
public void mouseUp( MouseEvent e ) {
boolean control = ( e.stateMask & SWT.MOD1 ) != 0;
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 );
// Quick new hop option? (drag from one step to another)
//
if ( hop_candidate != null ) {
addCandidateAsHop();
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;
jobMeta.unselectAll();
selectInRect( jobMeta, selectionRegion );
selectionRegion = null;
stopEntryMouseOverDelayTimers();
redraw();
} else {
// Clicked on an icon?
//
if ( selectedEntry != null && startHopEntry == 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 ) {
selectedEntry.flipSelected();
} else {
// Otherwise, select only the icon clicked on!
jobMeta.unselectAll();
selectedEntry.setSelected( true );
}
} else {
// Find out which Steps & Notes are selected
selectedEntries = jobMeta.getSelectedEntries();
selectedNotes = jobMeta.getSelectedNotes();
// We moved around some items: store undo info...
//
boolean also = false;
if ( selectedNotes != null && selectedNotes.size() > 0 && previous_note_locations != null ) {
int[] indexes = jobMeta.getNoteIndexes( selectedNotes );
addUndoPosition(
selectedNotes.toArray( new NotePadMeta[selectedNotes.size()] ), indexes,
previous_note_locations, jobMeta.getSelectedNoteLocations(), also );
also = selectedEntries != null && selectedEntries.size() > 0;
}
if ( selectedEntries != null && selectedEntries.size() > 0 && previous_step_locations != null ) {
int[] indexes = jobMeta.getEntryIndexes( selectedEntries );
addUndoPosition(
selectedEntries.toArray( new JobEntryCopy[selectedEntries.size()] ), indexes,
previous_step_locations, jobMeta.getSelectedLocations(), also );
}
}
}
// OK, we moved the step, did we move it across a hop?
// If so, ask to split the hop!
if ( split_hop ) {
JobHopMeta hi = findHop( icon.x + iconsize / 2, icon.y + iconsize / 2, selectedEntry );
if ( hi != null ) {
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.getFromEntry()
// B ==> hi.getToEntry();
// C ==> selected_step
//
if ( jobMeta.findJobHop( selectedEntry, hi.getFromEntry() ) == null
&& jobMeta.findJobHop( hi.getToEntry(), selectedEntry ) == null ) {
if ( jobMeta.findJobHop( hi.getFromEntry(), selectedEntry, true ) == null ) {
JobHopMeta newhop1 = new JobHopMeta( hi.getFromEntry(), selectedEntry );
if ( hi.getFromEntry().getEntry().isUnconditional() ) {
newhop1.setUnconditional();
}
jobMeta.addJobHop( newhop1 );
spoon.addUndoNew( jobMeta, new JobHopMeta[] { newhop1, }, new int[] { jobMeta
.indexOfJobHop( newhop1 ), }, true );
}
if ( jobMeta.findJobHop( selectedEntry, hi.getToEntry(), true ) == null ) {
JobHopMeta newhop2 = new JobHopMeta( selectedEntry, hi.getToEntry() );
if ( selectedEntry.getEntry().isUnconditional() ) {
newhop2.setUnconditional();
}
jobMeta.addJobHop( newhop2 );
spoon.addUndoNew( jobMeta, new JobHopMeta[] { newhop2, }, new int[] { jobMeta
.indexOfJobHop( newhop2 ), }, true );
}
int idx = jobMeta.indexOfJobHop( hi );
spoon.addUndoDelete( jobMeta, new JobHopMeta[] { hi }, new int[] { idx }, true );
jobMeta.removeJobHop( idx );
spoon.refreshTree();
}
// else: Silently discard this hop-split attempt.
}
}
split_hop = false;
}
selectedEntries = null;
selectedNotes = null;
selectedEntry = null;
selectedNote = null;
startHopEntry = 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!
jobMeta.unselectAll();
selectedNote.setSelected( true );
}
} else {
// Find out which Steps & Notes are selected
selectedEntries = jobMeta.getSelectedEntries();
selectedNotes = jobMeta.getSelectedNotes();
// We moved around some items: store undo info...
boolean also = false;
if ( selectedNotes != null && selectedNotes.size() > 0 && previous_note_locations != null ) {
int[] indexes = jobMeta.getNoteIndexes( selectedNotes );
addUndoPosition(
selectedNotes.toArray( new NotePadMeta[selectedNotes.size()] ), indexes,
previous_note_locations, jobMeta.getSelectedNoteLocations(), also );
also = selectedEntries != null && selectedEntries.size() > 0;
}
if ( selectedEntries != null && selectedEntries.size() > 0 && previous_step_locations != null ) {
int[] indexes = jobMeta.getEntryIndexes( selectedEntries );
addUndoPosition(
selectedEntries.toArray( new JobEntryCopy[selectedEntries.size()] ), indexes,
previous_step_locations, jobMeta.getSelectedLocations(), also );
}
}
}
selectedNotes = null;
selectedEntries = null;
selectedEntry = null;
selectedNote = null;
startHopEntry = null;
endHopLocation = null;
} else {
AreaOwner areaOwner = getVisibleAreaOwner( real.x, real.y );
if ( areaOwner == null && selectionRegion == null ) {
// Hit absolutely nothing: clear the settings
//
clearSettings();
}
}
}
}
}
lastButton = 0;
}
public void mouseMove( MouseEvent e ) {
boolean shift = ( e.stateMask & SWT.SHIFT ) != 0;
noInputEntry = 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 ) {
JobEntryCopy jobEntryCopy = null;
switch ( areaOwner.getAreaType() ) {
case JOB_ENTRY_ICON:
jobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
resetDelayTimer( jobEntryCopy );
break;
case MINI_ICONS_BALLOON: // Give the timer a bit more time
jobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
resetDelayTimer( jobEntryCopy );
break;
default:
break;
}
}
//
// 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 ( selectedEntry != null && !selectedEntry.isSelected() ) {
jobMeta.unselectAll();
selectedEntry.setSelected( true );
selectedEntries = new ArrayList<JobEntryCopy>();
selectedEntries.add( selectedEntry );
previous_step_locations = new Point[] { selectedEntry.getLocation() };
redraw();
} else if ( selectedNote != null && !selectedNote.isSelected() ) {
jobMeta.unselectAll();
selectedNote.setSelected( true );
selectedNotes = new ArrayList<NotePadMeta>();
selectedNotes.add( selectedNote );
previous_note_locations = new Point[] { selectedNote.getLocation() };
redraw();
} else if ( selectionRegion != null && startHopEntry == null ) {
// Did we select a region...?
//
selectionRegion.width = real.x - selectionRegion.x;
selectionRegion.height = real.y - selectionRegion.y;
redraw();
} else if ( selectedEntry != null && lastButton == 1 && !shift && startHopEntry == null ) {
// Move around steps & notes
//
//
// 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 - selectedEntry.getLocation().x;
int dy = icon.y - selectedEntry.getLocation().y;
// See if we have a hop-split candidate
//
JobHopMeta hi = findHop( icon.x + iconsize / 2, icon.y + iconsize / 2, selectedEntry );
if ( hi != null ) {
// OK, we want to split the hop in 2
//
if ( !hi.getFromEntry().equals( selectedEntry ) && !hi.getToEntry().equals( selectedEntry ) ) {
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 = jobMeta.getSelectedNotes();
selectedEntries = jobMeta.getSelectedEntries();
// Adjust location of selected steps...
if ( selectedEntries != null ) {
for ( int i = 0; i < selectedEntries.size(); i++ ) {
JobEntryCopy jobEntryCopy = selectedEntries.get( i );
PropsUI.setLocation( jobEntryCopy, jobEntryCopy.getLocation().x + dx, jobEntryCopy.getLocation().y + dy );
stopEntryMouseOverDelayTimer( jobEntryCopy );
}
}
// 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 ( ( startHopEntry != null && endHopEntry == null )
|| ( endHopEntry != null && startHopEntry == null ) ) {
// Are we creating a new hop with the middle button or pressing SHIFT?
//
JobEntryCopy jobEntryCopy = jobMeta.getJobEntryCopy( real.x, real.y, iconsize );
endHopLocation = new Point( real.x, real.y );
if ( jobEntryCopy != null
&& ( ( startHopEntry != null && !startHopEntry.equals( jobEntryCopy ) ) || ( endHopEntry != null && !endHopEntry
.equals( jobEntryCopy ) ) ) ) {
if ( hop_candidate == null ) {
// See if the step accepts input. If not, we can't create a new hop...
//
if ( startHopEntry != null ) {
if ( !jobEntryCopy.isStart() ) {
hop_candidate = new JobHopMeta( startHopEntry, jobEntryCopy );
endHopLocation = null;
} else {
noInputEntry = jobEntryCopy;
toolTip.setImage( null );
toolTip.setText( "The start entry can only be used at the start of a Job" );
toolTip.show( new org.eclipse.swt.graphics.Point( real.x, real.y ) );
}
} else if ( endHopEntry != null ) {
hop_candidate = new JobHopMeta( jobEntryCopy, endHopEntry );
endHopLocation = null;
}
}
} else {
if ( hop_candidate != null ) {
hop_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 = jobMeta.getSelectedNotes();
selectedEntries = jobMeta.getSelectedEntries();
// Adjust location of selected steps...
if ( selectedEntries != null ) {
for ( int i = 0; i < selectedEntries.size(); i++ ) {
JobEntryCopy jobEntryCopy = selectedEntries.get( i );
PropsUI.setLocation( jobEntryCopy, jobEntryCopy.getLocation().x + dx, jobEntryCopy.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();
}
}
}
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 JOB_ENTRY_ICON:
JobEntryCopy jobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
if ( !jobEntryCopy.isMissing() && !mouseOverEntries.contains( jobEntryCopy ) ) {
addEntryMouseOverDelayTimer( jobEntryCopy );
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 );
}
}
}
public void mouseEnter( MouseEvent event ) {
}
public void mouseExit( MouseEvent event ) {
}
public void mouseScrolled( MouseEvent e ) {
/*
* if (e.count == 3) { // scroll up zoomIn(); } else if (e.count == -3) { // scroll down zoomOut(); }
*/
}
private void addCandidateAsHop() {
if ( hop_candidate != null ) {
if ( !hop_candidate.getFromEntry().evaluates() && hop_candidate.getFromEntry().isUnconditional() ) {
hop_candidate.setUnconditional();
} else {
hop_candidate.setConditional();
int nr = jobMeta.findNrNextJobEntries( hop_candidate.getFromEntry() );
// If there is one green link: make this one red! (or
// vice-versa)
if ( nr == 1 ) {
JobEntryCopy jge = jobMeta.findNextJobEntry( hop_candidate.getFromEntry(), 0 );
JobHopMeta other = jobMeta.findJobHop( hop_candidate.getFromEntry(), jge );
if ( other != null ) {
hop_candidate.setEvaluation( !other.getEvaluation() );
}
}
}
if ( checkIfHopAlreadyExists( jobMeta, hop_candidate ) ) {
boolean cancel = false;
jobMeta.addJobHop( hop_candidate );
if ( jobMeta.hasLoop( hop_candidate.getFromEntry() ) || jobMeta.hasLoop( hop_candidate.getToEntry() ) ) {
MessageBox mb = new MessageBox( spoon.getShell(), SWT.OK | SWT.CANCEL | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.HopCausesLoop.Message" ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.HopCausesLoop.Title" ) );
int choice = mb.open();
if ( choice == SWT.CANCEL ) {
jobMeta.removeJobHop( hop_candidate );
cancel = true;
}
}
if ( !cancel ) {
spoon.addUndoNew( jobMeta, new JobHopMeta[] { hop_candidate }, new int[] { jobMeta
.indexOfJobHop( hop_candidate ) } );
}
spoon.refreshTree();
clearSettings();
redraw();
}
}
}
public boolean checkIfHopAlreadyExists( JobMeta jobMeta, JobHopMeta newHop ) {
boolean ok = true;
if ( jobMeta.findJobHop( newHop.getFromEntry(), newHop.getToEntry(), true ) != null ) {
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_ERROR );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.HopExists.Message" ) ); // "This hop already exists!"
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.HopExists.Title" ) ); // Error!
mb.open();
ok = false;
}
return ok;
}
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;
}
private synchronized void addEntryMouseOverDelayTimer( final JobEntryCopy jobEntryCopy ) {
// Don't add the same mouse over delay timer twice...
//
if ( mouseOverEntries.contains( jobEntryCopy ) ) {
return;
}
mouseOverEntries.add( jobEntryCopy );
DelayTimer delayTimer = new DelayTimer( 500, new DelayListener() {
public void expired() {
mouseOverEntries.remove( jobEntryCopy );
delayTimers.remove( jobEntryCopy );
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 && areaOwner.getAreaType() != null ) {
AreaType areaType = areaOwner.getAreaType();
if ( areaType == AreaType.JOB_ENTRY_ICON || areaType.belongsToJobContextMenu() ) {
JobEntryCopy selectedJobEntryCopy = (JobEntryCopy) areaOwner.getOwner();
return selectedJobEntryCopy == jobEntryCopy;
}
}
}
return false;
}
} );
new Thread( delayTimer ).start();
delayTimers.put( jobEntryCopy, delayTimer );
}
private void stopEntryMouseOverDelayTimer( final JobEntryCopy jobEntryCopy ) {
DelayTimer delayTimer = delayTimers.get( jobEntryCopy );
if ( delayTimer != null ) {
delayTimer.stop();
}
}
private void stopEntryMouseOverDelayTimers() {
for ( DelayTimer timer : delayTimers.values() ) {
timer.stop();
}
}
private void resetDelayTimer( JobEntryCopy jobEntryCopy ) {
DelayTimer delayTimer = delayTimers.get( jobEntryCopy );
if ( delayTimer != null ) {
delayTimer.reset();
}
}
protected void asyncRedraw() {
spoon.getDisplay().asyncExec( new Runnable() {
public void run() {
if ( !isDisposed() ) {
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 ) {
runJob();
}
} );
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 ) {
runOptionsJob();
}
} );
menu.setLocation( shell.getDisplay().map( mainComposite.getParent(), null, mainComposite.getLocation() ) );
menu.setVisible( true );
} else {
runJob();
}
}
} );
// Hack alert : more XUL limitations...
//
ToolItem sep = new ToolItem( swtToolbar, SWT.SEPARATOR );
zoomLabel = new Combo( swtToolbar, SWT.DROP_DOWN );
zoomLabel.setItems( TransPainter.magnificationDescriptions );
zoomLabel.addSelectionListener( new SelectionAdapter() {
public void widgetSelected( SelectionEvent arg0 ) {
readMagnification();
}
} );
zoomLabel.addKeyListener( new KeyAdapter() {
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( Const.getStackTracker( t ) );
new ErrorDialog( shell,
BaseMessages.getString( PKG, "Spoon.Exception.ErrorReadingXULFile.Title" ),
BaseMessages.getString( PKG, "Spoon.Exception.ErrorReadingXULFile.Message", XUL_FILE_JOB_GRAPH ),
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();
}
public void keyPressed( KeyEvent e ) {
// Delete
if ( e.keyCode == SWT.DEL ) {
List<JobEntryCopy> copies = jobMeta.getSelectedEntries();
if ( copies != null && copies.size() > 0 ) {
delSelected();
}
}
if ( e.keyCode == SWT.F1 ) {
spoon.browseVersionHistory();
}
// 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 );
}
// 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();
}
}
public void keyReleased( KeyEvent e ) {
}
public void selectInRect( JobMeta jobMeta, org.pentaho.di.core.gui.Rectangle rect ) {
int i;
for ( i = 0; i < jobMeta.nrJobEntries(); i++ ) {
JobEntryCopy je = jobMeta.getJobEntry( i );
Point p = je.getLocation();
if ( ( ( p.x >= rect.x && p.x <= rect.x + rect.width ) || ( p.x >= rect.x + rect.width && p.x <= rect.x ) )
&& ( ( p.y >= rect.y && p.y <= rect.y + rect.height ) || ( p.y >= rect.y + rect.height && p.y <= rect.y ) ) ) {
je.setSelected( true );
}
}
for ( i = 0; i < jobMeta.nrNotes(); i++ ) {
NotePadMeta ni = jobMeta.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 );
}
}
}
public boolean setFocus() {
xulDomContainer.addEventHandler( this );
return canvas.setFocus();
}
/**
* Method gets called, when the user wants to change a job entries name and he indeed entered a different name then
* the old one. Make sure that no other job entry matches this name and rename in case of uniqueness.
*
* @param jobEntry
* @param newName
*/
public void renameJobEntry( JobEntryCopy jobEntry, String newName ) {
JobEntryCopy[] jobs = jobMeta.getAllJobGraphEntries( newName );
if ( jobs != null && jobs.length > 0 ) {
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_INFORMATION );
mb.setMessage( BaseMessages.getString( PKG, "Spoon.Dialog.JobEntryNameExists.Message", newName ) );
mb.setText( BaseMessages.getString( PKG, "Spoon.Dialog.JobEntryNameExists.Title" ) );
mb.open();
} else {
jobEntry.setName( newName );
jobEntry.setChanged();
spoon.refreshTree(); // to reflect the new name
spoon.refreshGraph();
}
}
public static void showOnlyStartOnceMessage( Shell shell ) {
MessageBox mb = new MessageBox( shell, SWT.YES | SWT.ICON_ERROR );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.OnlyUseStartOnce.Message" ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.OnlyUseStartOnce.Title" ) );
mb.open();
}
public void delSelected() {
delSelected( getJobEntry() );
}
public void delSelected( JobEntryCopy clickedEntry ) {
List<JobEntryCopy> copies = jobMeta.getSelectedEntries();
int nrsels = copies.size();
if ( nrsels == 0 ) {
if ( clickedEntry != null ) {
spoon.deleteJobEntryCopies( jobMeta, clickedEntry );
}
return;
}
JobEntryCopy[] jobEntries = copies.toArray( new JobEntryCopy[copies.size()] );
spoon.deleteJobEntryCopies( jobMeta, jobEntries );
}
public void clearSettings() {
selectedEntry = null;
selectedNote = null;
selectedEntries = null;
selectedNotes = null;
selectionRegion = null;
hop_candidate = null;
last_hop_split = null;
lastButton = 0;
startHopEntry = null;
endHopEntry = null;
iconoffset = null;
for ( int i = 0; i < jobMeta.nrJobHops(); i++ ) {
jobMeta.getJobHop( i ).setSplit( false );
}
stopEntryMouseOverDelayTimers();
}
public Point getRealPosition( Composite canvas, int x, int y ) {
Point p = new Point( 0, 0 );
Composite follow = canvas;
while ( follow != null ) {
Point xy = new Point( follow.getLocation().x, follow.getLocation().y );
p.x += xy.x;
p.y += xy.y;
follow = follow.getParent();
}
p.x = x - p.x - 8;
p.y = y - p.y - 48;
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
*/
private JobHopMeta findJobHop( 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 JobHopMeta findHop( int x, int y, JobEntryCopy exclude ) {
int i;
JobHopMeta online = null;
for ( i = 0; i < jobMeta.nrJobHops(); i++ ) {
JobHopMeta hi = jobMeta.getJobHop( i );
JobEntryCopy fs = hi.getFromEntry();
JobEntryCopy ts = hi.getToEntry();
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;
}
protected int[] getLine( JobEntryCopy fs, JobEntryCopy ts ) {
if ( fs == null || ts == null ) {
return null;
}
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 };
}
private void showHelpTip( int x, int y, String tipTitle, String tipMessage ) {
helpTip.setTitle( tipTitle );
helpTip.setMessage( tipMessage );
helpTip.setCheckBoxMessage( BaseMessages.getString(
PKG, "JobGraph.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 );
}
public void setJobEntry( JobEntryCopy jobEntry ) {
this.jobEntry = jobEntry;
}
public JobEntryCopy getJobEntry() {
return jobEntry;
}
public void openTransformation() {
JobEntryCopy jobEntryCopy = getJobEntry();
final JobEntryInterface entry = jobEntryCopy.getEntry();
openTransformation( (JobEntryTrans) entry, jobEntryCopy );
}
public void openJob() {
JobEntryCopy jobEntryCopy = getJobEntry();
final JobEntryInterface entry = jobEntryCopy.getEntry();
openJob( (JobEntryJob) entry, jobEntryCopy );
}
public void newHopClick() {
selectedEntries = null;
newHop();
}
public void editEntryClick() {
selectedEntries = null;
editEntry( getJobEntry() );
}
public void editEntryDescription() {
String title = BaseMessages.getString( PKG, "JobGraph.Dialog.EditDescription.Title" );
String message = BaseMessages.getString( PKG, "JobGraph.Dialog.EditDescription.Message" );
EnterTextDialog dd = new EnterTextDialog( shell, title, message, getJobEntry().getDescription() );
String des = dd.open();
if ( des != null ) {
jobEntry.setDescription( des );
jobEntry.setChanged();
spoon.setShellText();
}
}
/**
* Go from serial to parallel to serial execution
*/
public void editEntryParallel() {
JobEntryCopy je = getJobEntry();
JobEntryCopy jeOld = (JobEntryCopy) je.clone_deep();
je.setLaunchingInParallel( !je.isLaunchingInParallel() );
JobEntryCopy jeNew = (JobEntryCopy) je.clone_deep();
spoon.addUndoChange( jobMeta, new JobEntryCopy[] { jeOld }, new JobEntryCopy[] { jeNew }, new int[] { jobMeta
.indexOfJobEntry( jeNew ) } );
jobMeta.setChanged();
if ( getJobEntry().isLaunchingInParallel() ) {
// Show a warning (optional)
//
if ( "Y".equalsIgnoreCase( spoon.props.getCustomParameter( STRING_PARALLEL_WARNING_PARAMETER, "Y" ) ) ) {
MessageDialogWithToggle md =
new MessageDialogWithToggle( shell,
BaseMessages.getString( PKG, "JobGraph.ParallelJobEntriesWarning.DialogTitle" ),
null,
BaseMessages.getString( PKG, "JobGraph.ParallelJobEntriesWarning.DialogMessage", Const.CR ) + Const.CR,
MessageDialog.WARNING,
new String[] { BaseMessages.getString( PKG, "JobGraph.ParallelJobEntriesWarning.Option1" ) },
0,
BaseMessages.getString( PKG, "JobGraph.ParallelJobEntriesWarning.Option2" ),
"N".equalsIgnoreCase( spoon.props.getCustomParameter( STRING_PARALLEL_WARNING_PARAMETER, "Y" ) ) );
MessageDialogWithToggle.setDefaultImage( GUIResource.getInstance().getImageSpoon() );
md.open();
spoon.props.setCustomParameter( STRING_PARALLEL_WARNING_PARAMETER, md.getToggleState() ? "N" : "Y" );
spoon.props.saveProps();
}
}
spoon.refreshGraph();
}
public void duplicateEntry() throws KettleException {
if ( !canDup( jobEntry ) ) {
JobGraph.showOnlyStartOnceMessage( spoon.getShell() );
}
spoon.delegates.jobs.dupeJobEntry( jobMeta, jobEntry );
}
public void copyEntry() {
if ( RepositorySecurityUI.verifyOperations(
shell, spoon.rep, RepositoryOperation.MODIFY_JOB, RepositoryOperation.EXECUTE_JOB ) ) {
return;
}
List<JobEntryCopy> entries = jobMeta.getSelectedEntries();
Iterator<JobEntryCopy> iterator = entries.iterator();
while ( iterator.hasNext() ) {
JobEntryCopy entry = iterator.next();
if ( !canDup( entry ) ) {
iterator.remove();
}
}
spoon.delegates.jobs.copyJobEntries( jobMeta, entries );
}
private boolean canDup( JobEntryCopy entry ) {
return !entry.isStart();
}
public void detachEntry() {
detach( getJobEntry() );
jobMeta.unselectAll();
}
public void hideEntry() {
getJobEntry().setDrawn( false );
// nr > 1: delete
if ( jobEntry.getNr() > 0 ) {
int ind = jobMeta.indexOfJobEntry( jobEntry );
jobMeta.removeJobEntry( ind );
spoon.addUndoDelete( jobMeta, new JobEntryCopy[] { getJobEntry() }, new int[] { ind } );
}
redraw();
}
public void deleteEntry() {
delSelected();
redraw();
}
protected synchronized void setMenu( int x, int y ) {
currentMouseX = x;
currentMouseY = y;
final JobEntryCopy jobEntry = jobMeta.getJobEntryCopy( x, y, iconsize );
setJobEntry( jobEntry );
Document doc = xulDomContainer.getDocumentRoot();
if ( jobEntry != null ) {
// We clicked on a Job Entry!
XulMenupopup menu = (XulMenupopup) doc.getElementById( "job-graph-entry" );
if ( menu != null ) {
List<JobEntryCopy> selection = jobMeta.getSelectedEntries();
doRightClickSelection( jobEntry, selection );
int sels = selection.size();
XulMenuitem item = (XulMenuitem) doc.getElementById( "job-graph-entry-newhop" );
item.setDisabled( sels < 2 );
JfaceMenupopup launchMenu = (JfaceMenupopup) doc.getElementById( "job-graph-entry-launch-popup" );
String[] referencedObjects = jobEntry.getEntry().getReferencedObjectDescriptions();
boolean[] enabledObjects = jobEntry.getEntry().isReferencedObjectEnabled();
launchMenu.setDisabled( Utils.isEmpty( referencedObjects ) );
launchMenu.removeChildren();
if ( !Utils.isEmpty( referencedObjects ) ) {
for ( int i = 0; i < referencedObjects.length; i++ ) {
final int index = i;
String referencedObject = referencedObjects[i];
Action action = new Action( referencedObject, Action.AS_DROP_DOWN_MENU ) {
public void run() {
loadReferencedObject( jobEntry, index );
}
};
JfaceMenuitem child =
new JfaceMenuitem( null, launchMenu, xulDomContainer, referencedObject, i, action );
child.setLabel( referencedObject );
child.setDisabled( !enabledObjects[i] );
}
}
item = (XulMenuitem) doc.getElementById( "job-graph-entry-align-snap" );
item.setAcceltext( "ALT-HOME" );
item.setLabel( BaseMessages.getString( PKG, "JobGraph.PopupMenu.JobEntry.AllignDistribute.SnapToGrid" ) );
item.setAccesskey( "alt-home" );
XulMenu aMenu = (XulMenu) doc.getElementById( "job-graph-entry-align" );
if ( aMenu != null ) {
aMenu.setDisabled( sels < 1 );
}
item = (XulMenuitem) doc.getElementById( "job-graph-entry-detach" );
if ( item != null ) {
item.setDisabled( !jobMeta.isEntryUsedInHops( jobEntry ) );
}
item = (XulMenuitem) doc.getElementById( "job-graph-entry-hide" );
if ( item != null ) {
item.setDisabled( !( jobEntry.isDrawn() && !jobMeta.isEntryUsedInHops( jobEntry ) ) );
}
item = (XulMenuitem) doc.getElementById( "job-graph-entry-delete" );
if ( item != null ) {
item.setDisabled( !jobEntry.isDrawn() );
}
item = (XulMenuitem) doc.getElementById( "job-graph-entry-parallel" );
if ( item != null ) {
item.setSelected( jobEntry.isLaunchingInParallel() );
}
try {
JobGraphJobEntryMenuExtension extension =
new JobGraphJobEntryMenuExtension( xulDomContainer, doc, jobMeta, jobEntry, this );
ExtensionPointHandler.callExtensionPoint(
log, KettleExtensionPoint.JobGraphJobEntrySetMenu.id, extension );
} catch ( Exception e ) {
log.logError( "Error handling menu right click on job entry through extension point", e );
}
ConstUI.displayMenu( menu, canvas );
}
} else {
// Clear the menu
final JobHopMeta hi = findJobHop( x, y );
setCurrentHop( hi );
if ( hi != null ) {
// We clicked on a HOP!
XulMenupopup menu = (XulMenupopup) doc.getElementById( "job-graph-hop" );
if ( menu != null ) {
XulMenuitem miPopEvalUncond = (XulMenuitem) doc.getElementById( "job-graph-hop-evaluation-uncond" );
XulMenuitem miPopEvalTrue = (XulMenuitem) doc.getElementById( "job-graph-hop-evaluation-true" );
XulMenuitem miPopEvalFalse = (XulMenuitem) doc.getElementById( "job-graph-hop-evaluation-false" );
XulMenuitem miDisHop = (XulMenuitem) doc.getElementById( "job-graph-hop-enabled" );
XulMenuitem miFlipHop = (XulMenuitem) doc.getElementById( "job-graph-hop-flip" );
// Set the checkboxes in the right places...
//
if ( miPopEvalUncond != null && miPopEvalTrue != null && miPopEvalFalse != null ) {
if ( hi.isUnconditional() ) {
miPopEvalUncond.setSelected( true );
miPopEvalTrue.setSelected( false );
miPopEvalFalse.setSelected( false );
} else {
if ( hi.getEvaluation() ) {
miPopEvalUncond.setSelected( false );
miPopEvalTrue.setSelected( true );
miPopEvalFalse.setSelected( false );
} else {
miPopEvalUncond.setSelected( false );
miPopEvalTrue.setSelected( false );
miPopEvalFalse.setSelected( true );
}
}
if ( !hi.getFromEntry().evaluates() ) {
miPopEvalTrue.setDisabled( true );
miPopEvalFalse.setDisabled( true );
} else {
miPopEvalTrue.setDisabled( false );
miPopEvalFalse.setDisabled( false );
}
if ( !hi.getFromEntry().isUnconditional() ) {
miPopEvalUncond.setDisabled( true );
} else {
miPopEvalUncond.setDisabled( false );
}
if ( hi.getFromEntry().isStart() || hi.getToEntry().getEntry() instanceof JobEntryAbort ) {
miFlipHop.setDisabled( true );
} else {
miFlipHop.setDisabled( false );
}
}
if ( miDisHop != null ) {
if ( hi.isEnabled() ) {
miDisHop.setLabel( BaseMessages.getString( PKG, "JobGraph.PopupMenu.Hop.Disable" ) );
} else {
miDisHop.setLabel( BaseMessages.getString( PKG, "JobGraph.PopupMenu.Hop.Enable" ) );
}
}
ConstUI.displayMenu( menu, canvas );
}
} else {
// Clicked on the background: maybe we hit a note?
final NotePadMeta ni = jobMeta.getNote( x, y );
setCurrentNote( ni );
if ( ni != null ) {
XulMenupopup menu = (XulMenupopup) doc.getElementById( "job-graph-note" );
if ( menu != null ) {
ConstUI.displayMenu( menu, canvas );
}
} else {
XulMenupopup menu = (XulMenupopup) doc.getElementById( "job-graph-background" );
if ( menu != null ) {
final String clipcontent = spoon.fromClipboard();
XulMenuitem item = (XulMenuitem) doc.getElementById( "job-graph-note-paste" );
if ( item != null ) {
item.setDisabled( clipcontent == null );
}
ConstUI.displayMenu( menu, canvas );
}
}
}
}
}
public void selectAll() {
spoon.editSelectAll();
}
public void clearSelection() {
spoon.editUnselectAll();
}
public void editJobProperties() {
editProperties( jobMeta, spoon, spoon.getRepository(), true );
}
public void pasteNote() {
final String clipcontent = spoon.fromClipboard();
Point loc = new Point( currentMouseX, currentMouseY );
spoon.pasteXML( jobMeta, clipcontent, loc );
}
public void newNote() {
String title = BaseMessages.getString( PKG, "JobGraph.Dialog.EditNote.Title" );
String message = BaseMessages.getString( PKG, "JobGraph.Dialog.EditNote.Message" );
EnterTextDialog dd = new EnterTextDialog( shell, title, message, "" );
String n = dd.open();
if ( n != null ) {
NotePadMeta npi =
new NotePadMeta( n, lastclick.x, lastclick.y, ConstUI.NOTE_MIN_SIZE, ConstUI.NOTE_MIN_SIZE );
jobMeta.addNote( npi );
spoon.addUndoNew( jobMeta, new NotePadMeta[] { npi }, new int[] { jobMeta.indexOfNote( npi ) } );
redraw();
}
}
public void setCurrentNote( NotePadMeta ni ) {
this.ni = ni;
}
public NotePadMeta getCurrentNote() {
return ni;
}
public void editNote() {
selectionRegion = null;
editNote( getCurrentNote() );
}
public void deleteNote() {
selectionRegion = null;
int idx = jobMeta.indexOfNote( getCurrentNote() );
if ( idx >= 0 ) {
jobMeta.removeNote( idx );
spoon.addUndoDelete( jobMeta, new NotePadMeta[] { getCurrentNote() }, new int[] { idx } );
}
redraw();
}
public void raiseNote() {
selectionRegion = null;
int idx = jobMeta.indexOfNote( getCurrentNote() );
if ( idx >= 0 ) {
jobMeta.raiseNote( idx );
// spoon.addUndoRaise(jobMeta, new NotePadMeta[] {getCurrentNote()}, new int[] {idx} );
}
redraw();
}
public void lowerNote() {
selectionRegion = null;
int idx = jobMeta.indexOfNote( getCurrentNote() );
if ( idx >= 0 ) {
jobMeta.lowerNote( idx );
// spoon.addUndoLower(jobMeta, new NotePadMeta[] {getCurrentNote()}, new int[] {idx} );
}
redraw();
}
public void flipHop() {
selectionRegion = null;
JobEntryCopy origFrom = currentHop.getFromEntry();
JobEntryCopy origTo = currentHop.getToEntry();
currentHop.setFromEntry( currentHop.getToEntry() );
currentHop.setToEntry( origFrom );
boolean cancel = false;
if ( jobMeta.hasLoop( currentHop.getFromEntry() ) || jobMeta.hasLoop( currentHop.getToEntry() ) ) {
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.CANCEL | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.HopFlipCausesLoop.Message" ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.HopCausesLoop.Title" ) );
int choice = mb.open();
if ( choice == SWT.CANCEL ) {
cancel = true;
currentHop.setFromEntry( origFrom );
currentHop.setToEntry( origTo );
}
}
if ( !cancel ) {
currentHop.setChanged();
}
spoon.refreshGraph();
spoon.refreshTree();
spoon.setShellText();
}
public void disableHop() {
selectionRegion = null;
boolean orig = currentHop.isEnabled();
currentHop.setEnabled( !currentHop.isEnabled() );
if ( !orig && ( jobMeta.hasLoop( currentHop.getFromEntry() ) || jobMeta.hasLoop( currentHop.getToEntry() ) ) ) {
MessageBox mb = new MessageBox( shell, SWT.CANCEL | SWT.OK | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.LoopAfterHopEnabled.Message" ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.LoopAfterHopEnabled.Title" ) );
int choice = mb.open();
if ( choice == SWT.CANCEL ) {
currentHop.setEnabled( orig );
}
}
spoon.refreshGraph();
spoon.refreshTree();
}
public void deleteHop() {
selectionRegion = null;
int idx = jobMeta.indexOfJobHop( currentHop );
jobMeta.removeJobHop( idx );
spoon.refreshTree();
spoon.refreshGraph();
}
public void setHopUnconditional() {
currentHop.setUnconditional();
spoon.refreshGraph();
}
public void setHopEvaluationTrue() {
currentHop.setConditional();
currentHop.setEvaluation( true );
spoon.refreshGraph();
}
public void setHopEvaluationFalse() {
currentHop.setConditional();
currentHop.setEvaluation( false );
spoon.refreshGraph();
}
protected void setCurrentHop( JobHopMeta hop ) {
currentHop = hop;
}
protected JobHopMeta getCurrentHop() {
return currentHop;
}
public void enableHopsBetweenSelectedEntries() {
enableHopsBetweenSelectedEntries( true );
}
public void disableHopsBetweenSelectedEntries() {
enableHopsBetweenSelectedEntries( false );
}
/**
* This method enables or disables all the hops between the selected Entries.
*
**/
public void enableHopsBetweenSelectedEntries( boolean enabled ) {
List<JobEntryCopy> list = jobMeta.getSelectedEntries();
boolean hasLoop = false;
for ( int i = 0; i < jobMeta.nrJobHops(); i++ ) {
JobHopMeta hop = jobMeta.getJobHop( i );
if ( list.contains( hop.getFromEntry() ) && list.contains( hop.getToEntry() ) ) {
JobHopMeta before = (JobHopMeta) hop.clone();
hop.setEnabled( enabled );
JobHopMeta after = (JobHopMeta) hop.clone();
spoon.addUndoChange( jobMeta, new JobHopMeta[] { before }, new JobHopMeta[] { after }, new int[] { jobMeta
.indexOfJobHop( hop ) } );
if ( jobMeta.hasLoop( hop.getFromEntry() ) || jobMeta.hasLoop( hop.getToEntry() ) ) {
hasLoop = true;
}
}
}
if ( hasLoop && enabled ) {
MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.LoopAfterHopEnabled.Message" ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.LoopAfterHopEnabled.Title" ) );
mb.open();
}
spoon.refreshGraph();
}
public void enableHopsDownstream() {
enableDisableHopsDownstream( true );
}
public void disableHopsDownstream() {
enableDisableHopsDownstream( false );
}
public void enableDisableHopsDownstream( boolean enabled ) {
if ( currentHop == null ) {
return;
}
JobHopMeta before = (JobHopMeta) currentHop.clone();
currentHop.setEnabled( enabled );
JobHopMeta after = (JobHopMeta) currentHop.clone();
spoon.addUndoChange( jobMeta, new JobHopMeta[] { before }, new JobHopMeta[] { after }, new int[] { jobMeta
.indexOfJobHop( currentHop ) } );
enableDisableNextHops( currentHop.getToEntry(), enabled, 1 );
spoon.refreshGraph();
}
private void enableDisableNextHops( JobEntryCopy from, boolean enabled, int level ) {
if ( level > 100 ) {
return; // prevent endless running with loops in jobs
}
for ( JobEntryCopy to : jobMeta.getJobCopies() ) {
JobHopMeta hop = jobMeta.findJobHop( from, to, true );
if ( hop != null ) {
JobHopMeta before = (JobHopMeta) hop.clone();
hop.setEnabled( enabled );
JobHopMeta after = (JobHopMeta) hop.clone();
spoon.addUndoChange( jobMeta, new JobHopMeta[] { before }, new JobHopMeta[] { after }, new int[] { jobMeta
.indexOfJobHop( hop ) } );
enableDisableNextHops( to, enabled, level++ );
}
}
}
protected void setToolTip( int x, int y, int screenX, int screenY ) {
if ( !spoon.getProperties().showToolTips() ) {
return;
}
canvas.setToolTipText( "-" ); // Some stupid bug in GTK+ causes a phantom tool tip to pop up, even if the tip is
// null
canvas.setToolTipText( null );
Image tipImage = null;
JobHopMeta hi = findJobHop( x, y );
// check the area owner list...
//
StringBuilder tip = new StringBuilder();
AreaOwner areaOwner = getVisibleAreaOwner( x, y );
if ( areaOwner != null && areaOwner.getAreaType() != null ) {
JobEntryCopy jobEntryCopy;
switch ( areaOwner.getAreaType() ) {
case JOB_HOP_ICON:
hi = (JobHopMeta) areaOwner.getOwner();
if ( hi.isUnconditional() ) {
tipImage = GUIResource.getInstance().getImageUnconditionalHop();
tip.append( BaseMessages.getString( PKG, "JobGraph.Hop.Tooltip.Unconditional", hi
.getFromEntry().getName(), Const.CR ) );
} else {
if ( hi.getEvaluation() ) {
tip.append( BaseMessages.getString( PKG, "JobGraph.Hop.Tooltip.EvaluatingTrue", hi
.getFromEntry().getName(), Const.CR ) );
tipImage = GUIResource.getInstance().getImageTrue();
} else {
tip.append( BaseMessages.getString( PKG, "JobGraph.Hop.Tooltip.EvaluatingFalse", hi
.getFromEntry().getName(), Const.CR ) );
tipImage = GUIResource.getInstance().getImageFalse();
}
}
break;
case JOB_HOP_PARALLEL_ICON:
hi = (JobHopMeta) areaOwner.getOwner();
tip.append( BaseMessages.getString(
PKG, "JobGraph.Hop.Tooltip.Parallel", hi.getFromEntry().getName(), Const.CR ) );
tipImage = GUIResource.getInstance().getImageParallelHop();
break;
case CUSTOM:
String message = (String) areaOwner.getOwner();
tip.append( message );
tipImage = null;
GUIResource.getInstance().getImageTransGraph();
break;
case JOB_ENTRY_MINI_ICON_INPUT:
tip.append( BaseMessages.getString( PKG, "JobGraph.EntryInputConnector.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageHopInput();
resetDelayTimer( (JobEntryCopy) areaOwner.getOwner() );
break;
case JOB_ENTRY_MINI_ICON_OUTPUT:
tip.append( BaseMessages.getString( PKG, "JobGraph.EntryOutputConnector.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageHopOutput();
resetDelayTimer( (JobEntryCopy) areaOwner.getOwner() );
break;
case JOB_ENTRY_MINI_ICON_EDIT:
tip.append( BaseMessages.getString( PKG, "JobGraph.EditStep.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageEdit();
resetDelayTimer( (JobEntryCopy) areaOwner.getOwner() );
break;
case JOB_ENTRY_MINI_ICON_CONTEXT:
tip.append( BaseMessages.getString( PKG, "JobGraph.ShowMenu.Tooltip" ) );
tipImage = GUIResource.getInstance().getImageContextMenu();
resetDelayTimer( (JobEntryCopy) areaOwner.getOwner() );
break;
case JOB_ENTRY_RESULT_FAILURE:
case JOB_ENTRY_RESULT_SUCCESS:
JobEntryResult jobEntryResult = (JobEntryResult) areaOwner.getOwner();
jobEntryCopy = (JobEntryCopy) areaOwner.getParent();
Result result = jobEntryResult.getResult();
tip.append( "'" ).append( jobEntryCopy.getName() ).append( "' " );
if ( result.getResult() ) {
tipImage = GUIResource.getInstance().getImageTrue();
tip.append( "finished successfully." );
} else {
tipImage = GUIResource.getInstance().getImageFalse();
tip.append( "failed." );
}
tip.append( Const.CR ).append( "------------------------" ).append( Const.CR ).append( Const.CR );
tip.append( "Result : " ).append( result.getResult() ).append( Const.CR );
tip.append( "Errors : " ).append( result.getNrErrors() ).append( Const.CR );
if ( result.getNrLinesRead() > 0 ) {
tip.append( "Lines read : " ).append( result.getNrLinesRead() ).append( Const.CR );
}
if ( result.getNrLinesWritten() > 0 ) {
tip.append( "Lines written : " ).append( result.getNrLinesWritten() ).append( Const.CR );
}
if ( result.getNrLinesInput() > 0 ) {
tip.append( "Lines input : " ).append( result.getNrLinesInput() ).append( Const.CR );
}
if ( result.getNrLinesOutput() > 0 ) {
tip.append( "Lines output : " ).append( result.getNrLinesOutput() ).append( Const.CR );
}
if ( result.getNrLinesUpdated() > 0 ) {
tip.append( "Lines updated : " ).append( result.getNrLinesUpdated() ).append( Const.CR );
}
if ( result.getNrLinesDeleted() > 0 ) {
tip.append( "Lines deleted : " ).append( result.getNrLinesDeleted() ).append( Const.CR );
}
if ( result.getNrLinesRejected() > 0 ) {
tip.append( "Lines rejected : " ).append( result.getNrLinesRejected() ).append( Const.CR );
}
if ( result.getResultFiles() != null && !result.getResultFiles().isEmpty() ) {
tip.append( Const.CR ).append( "Result files:" ).append( Const.CR );
if ( result.getResultFiles().size() > 10 ) {
tip.append( " (10 files of " ).append( result.getResultFiles().size() ).append( " shown" );
}
List<ResultFile> files = new ArrayList<ResultFile>( result.getResultFiles().values() );
for ( int i = 0; i < files.size(); i++ ) {
ResultFile file = files.get( i );
tip.append( " - " ).append( file.toString() ).append( Const.CR );
}
}
if ( result.getRows() != null && !result.getRows().isEmpty() ) {
tip.append( Const.CR ).append( "Result rows: " );
if ( result.getRows().size() > 10 ) {
tip.append( " (10 rows of " ).append( result.getRows().size() ).append( " shown" );
}
tip.append( Const.CR );
for ( int i = 0; i < result.getRows().size() && i < 10; i++ ) {
RowMetaAndData row = result.getRows().get( i );
tip.append( " - " ).append( row.toString() ).append( Const.CR );
}
}
break;
case JOB_ENTRY_RESULT_CHECKPOINT:
tip.append( "The job started here since this is the furthest checkpoint "
+ "that was reached last time the transformation was executed." );
tipImage = GUIResource.getInstance().getImageCheckpoint();
break;
default:
break;
}
}
if ( hi != null && tip.length() == 0 ) {
// Set the tooltip for the hop:
tip.append( BaseMessages.getString( PKG, "JobGraph.Dialog.HopInfo" ) ).append( Const.CR );
tip.append( BaseMessages.getString( PKG, "JobGraph.Dialog.HopInfo.SourceEntry" ) ).append( " " ).append(
hi.getFromEntry().getName() ).append( Const.CR );
tip.append( BaseMessages.getString( PKG, "JobGraph.Dialog.HopInfo.TargetEntry" ) ).append( " " ).append(
hi.getToEntry().getName() ).append( Const.CR );
tip.append( BaseMessages.getString( PKG, "TransGraph.Dialog.HopInfo.Status" ) ).append( " " );
tip.append( ( hi.isEnabled()
? BaseMessages.getString( PKG, "JobGraph.Dialog.HopInfo.Enable" ) : BaseMessages.getString(
PKG, "JobGraph.Dialog.HopInfo.Disable" ) ) );
if ( hi.isUnconditional() ) {
tipImage = GUIResource.getInstance().getImageUnconditionalHop();
} else {
if ( hi.getEvaluation() ) {
tipImage = GUIResource.getInstance().getImageTrue();
} else {
tipImage = GUIResource.getInstance().getImageFalse();
}
}
}
if ( tip == null || tip.length() == 0 ) {
toolTip.hide();
} else {
if ( !tip.toString().equalsIgnoreCase( getToolTipText() ) ) {
if ( tipImage != null ) {
toolTip.setImage( tipImage );
} else {
toolTip.setImage( GUIResource.getInstance().getImageSpoon() );
}
toolTip.setText( tip.toString() );
toolTip.hide();
toolTip.show( new org.eclipse.swt.graphics.Point( screenX, screenY ) );
}
}
}
public void launchStuff( JobEntryCopy jobEntryCopy ) {
String[] references = jobEntryCopy.getEntry().getReferencedObjectDescriptions();
if ( !Utils.isEmpty( references ) ) {
loadReferencedObject( jobEntryCopy, 0 );
}
/*
* if (jobEntryCopy.isJob()) { final JobEntryJob entry = (JobEntryJob) jobEntryCopy.getEntry(); if ((entry != null
* && entry.getJobObjectId() == null && !Utils.isEmpty(entry.getFilename()) && spoon.rep == null) || (entry != null
* && !Utils.isEmpty(entry.getName()) && spoon.rep != null) || (entry != null && entry.getJobObjectId()!=null &&
* spoon.rep != null) ) { openJob(entry, jobEntryCopy); } } else if (jobEntryCopy.isTransformation()) { final
* JobEntryTrans entry = (JobEntryTrans) jobEntryCopy.getEntry(); if ((entry != null && entry.getTransObjectId() ==
* null && !Utils.isEmpty(entry.getFilename()) && spoon.rep == null) || (entry != null && entry.getName() != null &&
* spoon.rep != null) || (entry != null && entry.getTransObjectId()!=null && spoon.rep != null) ) {
* openTransformation(entry, jobEntryCopy); } }
*/
}
public void launchStuff() {
if ( jobEntry != null ) {
launchStuff( jobEntry );
}
}
protected void loadReferencedObject( JobEntryCopy jobEntryCopy, int index ) {
try {
Object referencedMeta =
jobEntryCopy.getEntry().loadReferencedObject( index, spoon.rep, spoon.metaStore, jobMeta );
if ( referencedMeta == null ) {
// Compatible re-try for older plugins.
referencedMeta =
compatibleJobEntryLoadReferencedObject( jobEntryCopy.getEntry(), index, spoon.rep, jobMeta );
}
if ( referencedMeta != null && ( referencedMeta instanceof TransMeta ) ) {
TransMeta launchTransMeta = (TransMeta) referencedMeta;
// Try to see if this transformation is already loaded in another tab...
//
TabMapEntry tabEntry = spoon.delegates.tabs.findTabForTransformation( launchTransMeta );
if ( tabEntry != null ) {
// Switch to this one!
//
spoon.tabfolder.setSelected( tabEntry.getTabItem() );
return;
}
copyInternalJobVariables( jobMeta, launchTransMeta );
spoon.setParametersAsVariablesInUI( launchTransMeta, launchTransMeta );
launchTransMeta.clearChanged();
spoon.addTransGraph( launchTransMeta );
TransGraph transGraph = spoon.getActiveTransGraph();
attachActiveTrans( transGraph, launchTransMeta, jobEntryCopy );
spoon.refreshTree();
spoon.applyVariables();
}
if ( referencedMeta != null && ( referencedMeta instanceof JobMeta ) ) {
JobMeta launchJobMeta = (JobMeta) referencedMeta;
// Try to see if this job is already loaded in another tab...
//
String tabName = spoon.delegates.tabs.makeTabName( launchJobMeta, true );
TabMapEntry tabEntry = spoon.delegates.tabs.findTabMapEntry( tabName, ObjectType.JOB_GRAPH );
if ( tabEntry != null ) {
// Switch to this one!
//
spoon.tabfolder.setSelected( tabEntry.getTabItem() );
return;
}
spoon.setParametersAsVariablesInUI( launchJobMeta, launchJobMeta );
launchJobMeta.clearChanged();
spoon.addJobGraph( launchJobMeta );
JobGraph jobGraph = spoon.getActiveJobGraph();
attachActiveJob( jobGraph, launchJobMeta, jobEntryCopy );
spoon.refreshTree();
spoon.applyVariables();
}
} catch ( Exception e ) {
new ErrorDialog( shell,
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingSpoonCanNotLoadTransformation.Title" ),
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingSpoonCanNotLoadTransformation.Message" ), e );
}
}
@SuppressWarnings( "deprecation" )
private Object compatibleJobEntryLoadReferencedObject( JobEntryInterface entry, int index, Repository rep,
JobMeta jobMeta2 ) throws KettleException {
return entry.loadReferencedObject( index, spoon.rep, jobMeta );
}
protected void openTransformation( JobEntryTrans entry, JobEntryCopy jobEntryCopy ) {
try {
TransMeta launchTransMeta = null;
switch ( entry.getSpecificationMethod() ) {
case FILENAME:
// See if this file is already loaded...
//
String exactFilename = jobMeta.environmentSubstitute( entry.getFilename() );
if ( Utils.isEmpty( exactFilename ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoFilenameSpecified" ) );
}
// Open the file or create a new one!
//
if ( KettleVFS.fileExists( exactFilename ) ) {
launchTransMeta = new TransMeta( exactFilename );
} else {
launchTransMeta = new TransMeta();
}
launchTransMeta.setFilename( exactFilename );
break;
case REPOSITORY_BY_NAME:
String exactTransname = jobMeta.environmentSubstitute( entry.getTransname() );
String exactDirectory = jobMeta.environmentSubstitute( entry.getDirectory() );
if ( Utils.isEmpty( exactTransname ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoTransNameSpecified" ) );
}
if ( Utils.isEmpty( exactDirectory ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoTransDirectorySpecified" ) );
}
// Open the transformation or create a new one...
// But first we look to see if the directory does exist
RepositoryDirectoryInterface repositoryDirectoryInterface =
spoon.rep.findDirectory( jobMeta.environmentSubstitute( entry.getDirectory() ) );
if ( repositoryDirectoryInterface == null ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.DirectoryDoesNotExist", jobMeta
.environmentSubstitute( entry.getDirectory() ) ) );
}
boolean exists = spoon.rep.getTransformationID( exactTransname, repositoryDirectoryInterface ) != null;
if ( !exists ) {
launchTransMeta = new TransMeta( null, exactTransname );
} else {
launchTransMeta =
spoon.rep.loadTransformation( exactTransname, spoon.rep.findDirectory( jobMeta
.environmentSubstitute( entry.getDirectory() ) ), null, true, null ); // reads last version
}
break;
case REPOSITORY_BY_REFERENCE:
if ( entry.getTransObjectId() == null ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoTransReferenceSpecified" ) );
}
launchTransMeta = spoon.rep.loadTransformation( entry.getTransObjectId(), null );
break;
default:
break;
}
// If we didn't find a valid transformation, stop here...
//
if ( launchTransMeta == null ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoValidTransSpecified" ) );
}
launchTransMeta.setRepository( spoon.getRepository() );
launchTransMeta.setMetaStore( spoon.getMetaStore() );
// Try to see if this transformation is already loaded in another tab...
//
TabMapEntry tabEntry = spoon.delegates.tabs.findTabForTransformation( launchTransMeta );
if ( tabEntry != null ) {
// Switch to this one!
//
spoon.tabfolder.setSelected( tabEntry.getTabItem() );
return;
}
copyInternalJobVariables( jobMeta, launchTransMeta );
spoon.setParametersAsVariablesInUI( launchTransMeta, launchTransMeta );
spoon.addTransGraph( launchTransMeta );
launchTransMeta.clearChanged();
TransGraph transGraph = spoon.getActiveTransGraph();
attachActiveTrans( transGraph, launchTransMeta, jobEntryCopy );
spoon.refreshTree();
spoon.applyVariables();
} catch ( Throwable e ) {
new ErrorDialog( shell,
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingSpoonCanNotLoadTransformation.Title" ),
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingSpoonCanNotLoadTransformation.Message" ),
(Exception) e );
}
}
public void openJob( JobEntryJob entry, JobEntryCopy jobEntryCopy ) {
try {
JobMeta launchJobMeta = null;
switch ( entry.getSpecificationMethod() ) {
case FILENAME:
// See if this file is already loaded...
//
String exactFilename = jobMeta.environmentSubstitute( entry.getFilename() );
if ( Utils.isEmpty( exactFilename ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoFilenameSpecified" ) );
}
// Open the file or create a new one!
//
if ( KettleVFS.fileExists( exactFilename ) ) {
launchJobMeta = new JobMeta( jobMeta, exactFilename, spoon.rep, spoon.metaStore, null );
} else {
launchJobMeta = new JobMeta();
}
launchJobMeta.setFilename( exactFilename );
break;
case REPOSITORY_BY_NAME:
String exactJobname = jobMeta.environmentSubstitute( entry.getJobName() );
String exactDirectory = jobMeta.environmentSubstitute( entry.getDirectory() );
if ( Utils.isEmpty( exactJobname ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoJobNameSpecified" ) );
}
if ( Utils.isEmpty( exactDirectory ) ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoJobDirectorySpecified" ) );
}
// Open the job or create a new one...
//
RepositoryDirectoryInterface repDir = spoon.rep.findDirectory( entry.getDirectory() );
boolean exists = spoon.rep.exists( exactJobname, repDir, RepositoryObjectType.JOB );
if ( !exists ) {
launchJobMeta = new JobMeta();
launchJobMeta.setName( exactJobname );
launchJobMeta.setRepositoryDirectory( repDir );
} else {
// Always reads last revision
launchJobMeta = spoon.rep.loadJob( exactJobname, repDir, null, null );
}
break;
case REPOSITORY_BY_REFERENCE:
if ( entry.getJobObjectId() == null ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoJobReferenceSpecified" ) );
}
// Always reads last revision
launchJobMeta = spoon.rep.loadJob( entry.getJobObjectId(), null );
break;
default:
break;
}
// If we didn't find a valid job, stop here...
//
if ( launchJobMeta == null ) {
throw new Exception( BaseMessages.getString( PKG, "JobGraph.Exception.NoValidJobSpecified" ) );
}
launchJobMeta.setRepository( spoon.getRepository() );
launchJobMeta.setMetaStore( spoon.getMetaStore() );
// Try to see if this job is already loaded in another tab...
//
String tabName = spoon.delegates.tabs.makeTabName( launchJobMeta, true );
TabMapEntry tabEntry = spoon.delegates.tabs.findTabMapEntry( tabName, ObjectType.JOB_GRAPH );
if ( tabEntry != null ) {
// Switch to this one!
//
spoon.tabfolder.setSelected( tabEntry.getTabItem() );
return;
}
spoon.setParametersAsVariablesInUI( launchJobMeta, launchJobMeta );
spoon.addJobGraph( launchJobMeta );
launchJobMeta.clearChanged();
JobGraph jobGraph = spoon.getActiveJobGraph();
attachActiveJob( jobGraph, launchJobMeta, jobEntryCopy );
spoon.refreshTree();
spoon.applyVariables();
} catch ( Throwable e ) {
new ErrorDialog( shell,
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingChefCanNotLoadJob.Title" ),
BaseMessages.getString( PKG, "JobGraph.Dialog.ErrorLaunchingChefCanNotLoadJob.Message" ), e );
}
}
/**
* Finds the last active transformation in the running job to the opened transMeta
*
* @param transGraph
* @param jobEntryCopy
*/
private void attachActiveTrans( TransGraph transGraph, TransMeta newTrans, JobEntryCopy jobEntryCopy ) {
if ( job != null && transGraph != null ) {
Trans trans = spoon.findActiveTrans( job, jobEntryCopy );
transGraph.setTrans( trans );
if ( !transGraph.isExecutionResultsPaneVisible() ) {
transGraph.showExecutionResults();
}
transGraph.setControlStates();
}
}
/**
* Finds the last active job in the running job to the openened jobMeta
*
* @param jobGraph
* @param newJob
*/
private void attachActiveJob( JobGraph jobGraph, JobMeta newJobMeta, JobEntryCopy jobEntryCopy ) {
if ( job != null && jobGraph != null ) {
Job subJob = spoon.findActiveJob( job, jobEntryCopy );
if ( subJob != null ) {
jobGraph.setJob( subJob );
jobGraph.jobGridDelegate.setJobTracker( subJob.getJobTracker() );
if ( !jobGraph.isExecutionResultsPaneVisible() ) {
jobGraph.showExecutionResults();
}
jobGraph.setControlStates();
}
}
}
public synchronized void setJob( Job job ) {
this.job = job;
}
public static void copyInternalJobVariables( JobMeta sourceJobMeta, TransMeta targetTransMeta ) {
// Also set some internal JOB variables...
//
String[] internalVariables = Const.INTERNAL_JOB_VARIABLES;
for ( String variableName : internalVariables ) {
targetTransMeta.setVariable( variableName, sourceJobMeta.getVariable( variableName ) );
}
}
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 = getJobImage( disp, area.x, area.y, magnification );
e.gc.drawImage( img, 0, 0 );
if ( jobMeta.nrJobEntries() == 0 ) {
e.gc.setForeground( GUIResource.getInstance().getColorCrystalTextPentaho() );
e.gc.setBackground( GUIResource.getInstance().getColorBackground() );
e.gc.setFont( GUIResource.getInstance().getFontMedium() );
Image pentahoImage = GUIResource.getInstance().getImageJobCanvas();
int leftPosition = ( area.x - pentahoImage.getBounds().width ) / 2;
int topPosition = ( area.y - pentahoImage.getBounds().height ) / 2;
e.gc.drawImage( pentahoImage, leftPosition, topPosition );
}
img.dispose();
}
public Image getJobImage( 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;
JobPainter jobPainter =
new JobPainter(
gc, jobMeta, new Point( x, y ), new SwtScrollBar( hori ), new SwtScrollBar( vert ), hop_candidate,
drop_candidate, selectionRegion, areaOwners, mouseOverEntries, PropsUI.getInstance().getIconSize(),
PropsUI.getInstance().getLineWidth(), gridSize, PropsUI
.getInstance().getShadowSize(), PropsUI.getInstance().isAntiAliasingEnabled(), PropsUI
.getInstance().getNoteFont().getName(), PropsUI.getInstance().getNoteFont().getHeight() );
jobPainter.setMagnification( magnificationFactor );
jobPainter.setEntryLogMap( entryLogMap );
jobPainter.setStartHopEntry( startHopEntry );
jobPainter.setEndHopLocation( endHopLocation );
jobPainter.setEndHopEntry( endHopEntry );
jobPainter.setNoInputEntry( noInputEntry );
if ( job != null ) {
jobPainter.setJobEntryResults( job.getJobEntryResults() );
} else {
jobPainter.setJobEntryResults( new ArrayList<JobEntryResult>() );
}
List<JobEntryCopy> activeJobEntries = new ArrayList<JobEntryCopy>();
if ( job != null ) {
if ( job.getActiveJobEntryJobs().size() > 0 ) {
activeJobEntries.addAll( job.getActiveJobEntryJobs().keySet() );
}
if ( job.getActiveJobEntryTransformations().size() > 0 ) {
activeJobEntries.addAll( job.getActiveJobEntryTransformations().keySet() );
}
}
jobPainter.setActiveJobEntries( activeJobEntries );
jobPainter.drawJob();
return (Image) gc.getImage();
}
protected Point getOffset() {
Point area = getArea();
Point max = jobMeta.getMaximum();
Point thumb = getThumb( area, max );
return getOffset( thumb, area );
}
protected void newHop() {
List<JobEntryCopy> selection = jobMeta.getSelectedEntries();
if ( selection == null || selection.size() < 2 ) {
return;
}
JobEntryCopy fr = selection.get( 0 );
JobEntryCopy to = selection.get( 1 );
spoon.newJobHop( jobMeta, fr, to );
}
protected void editEntry( JobEntryCopy je ) {
spoon.editJobEntry( jobMeta, je );
}
protected void editNote( NotePadMeta ni ) {
NotePadMeta before = (NotePadMeta) ni.clone();
String title = BaseMessages.getString( PKG, "JobGraph.Dialog.EditNote.Title" );
NotePadDialog dd = new NotePadDialog( jobMeta, 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() );
spoon.addUndoChange( jobMeta, new NotePadMeta[] { before }, new NotePadMeta[] { ni }, new int[] { jobMeta
.indexOfNote( ni ) } );
ni.width = ConstUI.NOTE_MIN_SIZE;
ni.height = ConstUI.NOTE_MIN_SIZE;
spoon.refreshGraph();
}
}
protected void drawArrow( GC gc, int[] line ) {
int mx, my;
int x1 = line[0] + offset.x;
int y1 = line[1] + offset.y;
int x2 = line[2] + offset.x;
int y2 = line[3] + offset.y;
int x3;
int y3;
int x4;
int y4;
int a, b, dist;
double factor;
double angle;
// gc.setLineWidth(1);
// WuLine(gc, black, x1, y1, x2, y2);
gc.drawLine( x1, y1, x2, y2 );
// What's the distance between the 2 points?
a = Math.abs( x2 - x1 );
b = Math.abs( y2 - y1 );
dist = (int) Math.sqrt( a * a + b * b );
// determine factor (position of arrow to left side or right side 0-->100%)
if ( dist >= 2 * iconsize ) {
factor = 1.5;
} else {
factor = 1.2;
}
// in between 2 points
mx = (int) ( x1 + factor * ( x2 - x1 ) / 2 );
my = (int) ( y1 + factor * ( y2 - y1 ) / 2 );
// calculate points for arrowhead
angle = Math.atan2( y2 - y1, x2 - x1 ) + Math.PI;
x3 = (int) ( mx + Math.cos( angle - theta ) * size );
y3 = (int) ( my + Math.sin( angle - theta ) * size );
x4 = (int) ( mx + Math.cos( angle + theta ) * size );
y4 = (int) ( my + Math.sin( angle + theta ) * size );
// draw arrowhead
// gc.drawLine(mx, my, x3, y3);
// gc.drawLine(mx, my, x4, y4);
// gc.drawLine( x3, y3, x4, y4 );
Color fore = gc.getForeground();
Color back = gc.getBackground();
gc.setBackground( fore );
gc.fillPolygon( new int[] { mx, my, x3, y3, x4, y4 } );
gc.setBackground( back );
}
protected 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;
}
protected 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;
}
protected SnapAllignDistribute createSnapAllignDistribute() {
List<JobEntryCopy> elements = jobMeta.getSelectedEntries();
int[] indices = jobMeta.getEntryIndexes( elements );
return new SnapAllignDistribute( jobMeta, elements, indices, spoon, this );
}
public void snaptogrid() {
snaptogrid( ConstUI.GRID_SIZE );
}
protected 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();
}
protected void drawRect( GC gc, Rectangle rect ) {
if ( rect == null ) {
return;
}
gc.setLineStyle( SWT.LINE_DASHDOT );
gc.setLineWidth( 1 );
gc.setForeground( GUIResource.getInstance().getColorDarkGray() );
// PDI-2619: SWT on Windows doesn't cater for negative rect.width/height so handle here.
Point s = new Point( rect.x + offset.x, rect.y + offset.y );
if ( rect.width < 0 ) {
s.x = s.x + rect.width;
}
if ( rect.height < 0 ) {
s.y = s.y + rect.height;
}
gc.drawRoundRectangle( s.x, s.y, Math.abs( rect.width ), Math.abs( rect.height ), 3, 3 );
gc.setLineStyle( SWT.LINE_SOLID );
}
protected void detach( JobEntryCopy je ) {
JobHopMeta hfrom = jobMeta.findJobHopTo( je );
JobHopMeta hto = jobMeta.findJobHopFrom( je );
if ( hfrom != null && hto != null ) {
if ( jobMeta.findJobHop( hfrom.getFromEntry(), hto.getToEntry() ) == null ) {
JobHopMeta hnew = new JobHopMeta( hfrom.getFromEntry(), hto.getToEntry() );
jobMeta.addJobHop( hnew );
spoon.addUndoNew( jobMeta, new JobHopMeta[] { (JobHopMeta) hnew.clone() }, new int[] { jobMeta
.indexOfJobHop( hnew ) } );
}
}
if ( hfrom != null ) {
int fromidx = jobMeta.indexOfJobHop( hfrom );
if ( fromidx >= 0 ) {
jobMeta.removeJobHop( fromidx );
spoon.addUndoDelete( jobMeta, new JobHopMeta[] { hfrom }, new int[] { fromidx } );
}
}
if ( hto != null ) {
int toidx = jobMeta.indexOfJobHop( hto );
if ( toidx >= 0 ) {
jobMeta.removeJobHop( toidx );
spoon.addUndoDelete( jobMeta, new JobHopMeta[] { hto }, new int[] { toidx } );
}
}
spoon.refreshTree();
redraw();
}
public void newProps() {
iconsize = spoon.props.getIconSize();
linewidth = spoon.props.getLineWidth();
}
public String toString() {
if ( jobMeta == null ) {
return Spoon.APP_NAME;
} else {
return jobMeta.getName();
}
}
public EngineMetaInterface getMeta() {
return jobMeta;
}
/**
* @return the jobMeta / public JobMeta getJobMeta() { return jobMeta; }
*
* /**
* @param jobMeta
* the jobMeta to set
*/
public void setJobMeta( JobMeta jobMeta ) {
this.jobMeta = jobMeta;
}
public boolean applyChanges() throws KettleException {
return spoon.saveToFile( jobMeta );
}
public boolean canBeClosed() {
return !jobMeta.hasChanged();
}
public JobMeta getManagedObject() {
return jobMeta;
}
public boolean hasContentChanged() {
return jobMeta.hasChanged();
}
public static int showChangedWarning( Shell shell, String name ) {
MessageBox mb = new MessageBox( shell, SWT.YES | SWT.NO | SWT.CANCEL | SWT.ICON_WARNING );
mb.setMessage( BaseMessages.getString( PKG, "JobGraph.Dialog.PromptSave.Message", name ) );
mb.setText( BaseMessages.getString( PKG, "JobGraph.Dialog.PromptSave.Title" ) );
return mb.open();
}
public static boolean editProperties( JobMeta jobMeta, Spoon spoon, Repository rep, boolean allowDirectoryChange ) {
if ( jobMeta == null ) {
return false;
}
JobDialog jd = new JobDialog( spoon.getShell(), SWT.NONE, jobMeta, rep );
jd.setDirectoryChangeAllowed( allowDirectoryChange );
JobMeta ji = jd.open();
// In this case, load shared objects
//
if ( jd.isSharedObjectsFileChanged() ) {
try {
SharedObjects sharedObjects =
rep != null ? rep.readJobMetaSharedObjects( jobMeta ) : jobMeta.readSharedObjects();
spoon.sharedObjectsFileMap.put( sharedObjects.getFilename(), sharedObjects );
} catch ( Exception e ) {
new ErrorDialog( spoon.getShell(),
BaseMessages.getString( PKG, "Spoon.Dialog.ErrorReadingSharedObjects.Title" ),
BaseMessages.getString( PKG, "Spoon.Dialog.ErrorReadingSharedObjects.Message", spoon.delegates.tabs.makeTabName( jobMeta, true ) ), e );
}
}
// If we added properties, add them to the variables too, so that they appear in the CTRL-SPACE variable completion.
//
spoon.setParametersAsVariablesInUI( jobMeta, jobMeta );
if ( jd.isSharedObjectsFileChanged() || ji != null ) {
spoon.refreshTree();
spoon.delegates.tabs.renameTabs(); // cheap operation, might as will do it anyway
}
spoon.setShellText();
return ji != null;
}
/**
* @return the lastMove
*/
public Point getLastMove() {
return lastMove;
}
/**
* @param lastMove
* the lastMove to set
*/
public void setLastMove( Point lastMove ) {
this.lastMove = lastMove;
}
/**
* 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, "JobGraph.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() {
public void mouseDown( MouseEvent e ) {
disposeExtraView();
}
} );
minMaxButton = new Label( extraViewComposite, SWT.NONE );
minMaxButton.setImage( GUIResource.getInstance().getImageMaximizePanel() );
minMaxButton
.setToolTipText( BaseMessages.getString( PKG, "JobGraph.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() {
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, "JobLog.ResultsPanel.NameLabel" ) );
FormData fdResultsLabel = new FormData();
fdResultsLabel.left = new FormAttachment( 0, 0 );
fdResultsLabel.right = new FormAttachment( 100, 0 );
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, } );
}
/**
* 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( "job-show-results" );
button.setTooltiptext( BaseMessages.getString( PKG, "Spoon.Tooltip.ShowExecutionResults" ) );
ToolItem swtToolItem = (ToolItem) button.getManagedObject();
swtToolItem.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, "JobGraph.ExecutionResultsPanel.MaxButton.Tooltip" ) );
} else {
// Maximize
//
sashForm.setMaximizedControl( extraViewComposite );
minMaxButton.setImage( GUIResource.getInstance().getImageMinimizePanel() );
minMaxButton.setToolTipText( BaseMessages
.getString( PKG, "JobGraph.ExecutionResultsPanel.MinButton.Tooltip" ) );
}
}
public boolean isExecutionResultsPaneVisible() {
return extraViewComposite != null && !extraViewComposite.isDisposed();
}
public void showExecutionResults() {
if ( isExecutionResultsPaneVisible() ) {
disposeExtraView();
} else {
addAllTabs();
}
}
public void addAllTabs() {
CTabItem tabItemSelection = null;
if ( extraViewTabFolder != null && !extraViewTabFolder.isDisposed() ) {
tabItemSelection = extraViewTabFolder.getSelection();
}
jobHistoryDelegate.addJobHistory();
jobLogDelegate.addJobLog();
jobGridDelegate.addJobGrid();
jobMetricsDelegate.addJobMetrics();
if ( tabItemSelection != null ) {
extraViewTabFolder.setSelection( tabItemSelection );
} else {
extraViewTabFolder.setSelection( jobGridDelegate.getJobGridTab() );
}
XulToolbarbutton button = (XulToolbarbutton) toolbar.getElementById( "job-show-results" );
button.setTooltiptext( BaseMessages.getString( PKG, "Spoon.Tooltip.HideExecutionResults" ) );
ToolItem swtToolItem = (ToolItem) button.getManagedObject();
swtToolItem.setImage( GUIResource.getInstance().getImageHideResults() );
}
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 runJob() {
spoon.runFile();
}
public void runOptionsJob() {
spoon.runOptionsFile();
}
public void getSQL() {
spoon.getSQL();
}
public XulToolbar getToolbar() {
return toolbar;
}
public void exploreDatabase() {
spoon.exploreDatabase();
}
public void browseVersionHistory() {
try {
RepositoryRevisionBrowserDialogInterface dialog =
RepositoryExplorerDialog.getVersionBrowserDialog( shell, spoon.rep, jobMeta );
String versionLabel = dialog.open();
if ( versionLabel != null ) {
spoon.loadObjectFromRepository( jobMeta.getName(), jobMeta.getRepositoryElementType(), jobMeta
.getRepositoryDirectory(), versionLabel );
}
} catch ( Exception e ) {
new ErrorDialog(
shell, BaseMessages.getString( PKG, "JobGraph.VersionBrowserException.Title" ), BaseMessages.getString(
PKG, "JobGraph.VersionBrowserException.Message" ), e );
}
}
public synchronized void startJob( JobExecutionConfiguration executionConfiguration ) throws KettleException {
// If the job is not running, start the transformation...
//
if ( job == null || job.isFinished() && !job.isActive() ) {
// Auto save feature...
//
handleJobMetaChanges( jobMeta );
// Is the repository available & name / id set?
// Is there a filename set and no repository available?
//
if ( ( ( jobMeta.getName() != null && jobMeta.getObjectId() != null && spoon.rep != null ) || ( jobMeta
.getFilename() != null && spoon.rep == null ) )
&& !jobMeta.hasChanged() // Didn't change
) {
if ( job == null || ( job != null && !job.isActive() ) ) {
try {
// Make sure we clear the log before executing again...
//
if ( executionConfiguration.isClearingLog() ) {
jobLogDelegate.clearLog();
}
// Also make sure to clear the old log entries in the central log
// store & registry
//
if ( job != null ) {
KettleLogStore.discardLines( job.getLogChannelId(), true );
}
JobMeta runJobMeta;
if ( spoon.rep != null ) {
runJobMeta = spoon.rep.loadJob( jobMeta.getName(), jobMeta.getRepositoryDirectory(), null, null );
} else {
runJobMeta = new JobMeta( null, jobMeta.getFilename(), null, jobMeta.getMetaStore(), null );
}
String spoonObjectId = UUID.randomUUID().toString();
SimpleLoggingObject spoonLoggingObject =
new SimpleLoggingObject( "SPOON", LoggingObjectType.SPOON, null );
spoonLoggingObject.setContainerObjectId( spoonObjectId );
spoonLoggingObject.setLogLevel( executionConfiguration.getLogLevel() );
job = new Job( spoon.rep, runJobMeta, spoonLoggingObject );
job.setLogLevel( executionConfiguration.getLogLevel() );
job.shareVariablesWith( jobMeta );
job.setInteractive( true );
job.setGatheringMetrics( executionConfiguration.isGatheringMetrics() );
job.setArguments( executionConfiguration.getArgumentStrings() );
// Pass specific extension points...
//
job.getExtensionDataMap().putAll( executionConfiguration.getExtensionOptions() );
// Add job entry listeners
//
job.addJobEntryListener( createRefreshJobEntryListener() );
// If there is an alternative start job entry, pass it to the job
//
if ( !Utils.isEmpty( executionConfiguration.getStartCopyName() ) ) {
JobEntryCopy startJobEntryCopy =
runJobMeta.findJobEntry( executionConfiguration.getStartCopyName(), executionConfiguration
.getStartCopyNr(), false );
job.setStartJobEntryCopy( startJobEntryCopy );
}
// Set the named parameters
Map<String, String> paramMap = executionConfiguration.getParams();
Set<String> keys = paramMap.keySet();
for ( String key : keys ) {
job.getJobMeta().setParameterValue( key, Const.NVL( paramMap.get( key ), "" ) );
}
job.getJobMeta().activateParameters();
log.logMinimal( BaseMessages.getString( PKG, "JobLog.Log.StartingJob" ) );
job.start();
jobGridDelegate.previousNrItems = -1;
// Link to the new jobTracker!
jobGridDelegate.jobTracker = job.getJobTracker();
// Attach a listener to notify us that the transformation has
// finished.
job.addJobListener( new JobAdapter() {
public void jobFinished( Job job ) {
JobGraph.this.jobFinished();
}
} );
// Show the execution results views
//
addAllTabs();
} catch ( KettleException e ) {
new ErrorDialog(
shell, BaseMessages.getString( PKG, "JobLog.Dialog.CanNotOpenJob.Title" ), BaseMessages.getString(
PKG, "JobLog.Dialog.CanNotOpenJob.Message" ), e );
job = null;
}
} else {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "JobLog.Dialog.JobIsAlreadyRunning.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "JobLog.Dialog.JobIsAlreadyRunning.Message" ) );
m.open();
}
} else {
if ( jobMeta.hasChanged() ) {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "JobLog.Dialog.JobHasChangedSave.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "JobLog.Dialog.JobHasChangedSave.Message" ) );
m.open();
} else if ( spoon.rep != null && jobMeta.getName() == null ) {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "JobLog.Dialog.PleaseGiveThisJobAName.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "JobLog.Dialog.PleaseGiveThisJobAName.Message" ) );
m.open();
} else {
MessageBox m = new MessageBox( shell, SWT.OK | SWT.ICON_WARNING );
m.setText( BaseMessages.getString( PKG, "JobLog.Dialog.NoFilenameSaveYourJobFirst.Title" ) );
m.setMessage( BaseMessages.getString( PKG, "JobLog.Dialog.NoFilenameSaveYourJobFirst.Message" ) );
m.open();
}
}
setControlStates();
}
}
private JobEntryListener createRefreshJobEntryListener() {
return new JobEntryListener() {
public void beforeExecution( Job job, JobEntryCopy jobEntryCopy, JobEntryInterface jobEntryInterface ) {
asyncRedraw();
}
public void afterExecution( Job job, JobEntryCopy jobEntryCopy, JobEntryInterface jobEntryInterface,
Result result ) {
asyncRedraw();
}
};
}
/**
* This gets called at the very end, when everything is done.
*/
protected void jobFinished() {
// Do a final check to see if it all ended...
//
if ( job != null && job.isInitialized() && job.isFinished() ) {
for ( RefreshListener listener : refreshListeners ) {
listener.refreshNeeded();
}
jobMetricsDelegate.resetLastRefreshTime();
jobMetricsDelegate.updateGraph();
log.logMinimal( BaseMessages.getString( PKG, "JobLog.Log.JobHasEnded" ) );
}
setControlStates();
}
public synchronized void stopJob() {
if ( job != null && job.isActive() && job.isInitialized() ) {
job.stopAll();
job.waitUntilFinished( 5000 ); // wait until everything is stopped, maximum 5 seconds...
log.logMinimal( BaseMessages.getString( PKG, "JobLog.Log.JobWasStopped" ) );
}
setControlStates();
}
private boolean controlDisposed( XulToolbarbutton button ) {
if ( button.getManagedObject() instanceof Widget ) {
Widget widget = (Widget) button.getManagedObject();
return widget.isDisposed();
}
return false;
}
public void setControlStates() {
if ( isDisposed() || getDisplay().isDisposed() ) {
return;
}
getDisplay().asyncExec( new Runnable() {
public void run() {
boolean operationsNotAllowed = false;
try {
operationsNotAllowed = RepositorySecurityUI.verifyOperations( shell, spoon.rep, false,
RepositoryOperation.EXECUTE_JOB );
} catch ( KettleRepositoryLostException krle ) {
log.logError( krle.getLocalizedMessage() );
spoon.handleRepositoryLost( krle );
}
// Start/Run button...
//
boolean running = job != null && job.isActive();
XulToolbarbutton runButton = (XulToolbarbutton) toolbar.getElementById( "job-run" );
if ( runButton != null && !controlDisposed( runButton ) && !operationsNotAllowed ) {
if ( runButton.isDisabled() ^ running ) {
runButton.setDisabled( running );
}
}
// Stop button...
//
XulToolbarbutton stopButton = (XulToolbarbutton) toolbar.getElementById( "job-stop" );
if ( stopButton != null && !controlDisposed( stopButton ) ) {
if ( stopButton.isDisabled() ^ !running ) {
stopButton.setDisabled( !running );
}
}
// Replay button...
//
XulToolbarbutton replayButton = (XulToolbarbutton) toolbar.getElementById( "job-replay" );
if ( replayButton != null && !controlDisposed( replayButton ) && !operationsNotAllowed ) {
if ( replayButton.isDisabled() ^ running ) {
replayButton.setDisabled( running );
}
}
// version browser button...
//
XulToolbarbutton versionsButton = (XulToolbarbutton) toolbar.getElementById( "browse-versions" );
if ( versionsButton != null && !controlDisposed( versionsButton ) ) {
boolean hasRepository = spoon.rep != null;
boolean enabled =
hasRepository && spoon.rep.getRepositoryMeta().getRepositoryCapabilities().supportsRevisions();
if ( versionsButton.isDisabled() ^ !enabled ) {
versionsButton.setDisabled( !enabled );
}
}
}
} );
}
/**
* @return the refresh listeners
*/
public List<RefreshListener> getRefreshListeners() {
return refreshListeners;
}
/**
* @param refreshListeners
* the refresh listeners to set
*/
public void setRefreshListeners( List<RefreshListener> refreshListeners ) {
this.refreshListeners = refreshListeners;
}
/**
* @param refreshListener
* the job refresh listener to add
*/
public void addRefreshListener( RefreshListener refreshListener ) {
refreshListeners.add( refreshListener );
}
public String getName() {
return "jobgraph";
}
public XulDomContainer getXulDomContainer() {
return xulDomContainer;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.ui.xul.impl.XulEventHandler#setName(java.lang.String)
*/
public void setName( String name ) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see org.pentaho.ui.xul.impl.XulEventHandler#setXulDomContainer(org.pentaho.ui.xul.XulDomContainer)
*/
public void setXulDomContainer( XulDomContainer xulDomContainer ) {
this.xulDomContainer = xulDomContainer;
}
public boolean canHandleSave() {
return true;
}
public HasLogChannelInterface getLogChannelProvider() {
return job;
}
// 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!
jobMeta.addUndo( obj, null, pos, prev, curr, TransMeta.TYPE_UNDO_POSITION, nextAlso );
spoon.setUndoMenu( jobMeta );
}
@Override
public int showChangedWarning() throws KettleException {
return showChangedWarning( jobMeta.getName() );
}
public void replayJob() {
List<JobEntryCopy> selectedEntries = jobMeta.getSelectedEntries();
if ( selectedEntries.size() != 1 ) {
MessageBox box = new MessageBox( shell, SWT.ICON_INFORMATION | SWT.CLOSE );
box.setText( BaseMessages.getString( PKG, "JobGraph.ReplayJob.SelectOneEntryToStartFrom.Title" ) );
box.setMessage( BaseMessages.getString( PKG, "JobGraph.ReplayJob.SelectOneEntryToStartFrom.Message" ) );
box.open();
return;
}
JobEntryCopy copy = selectedEntries.get( 0 );
spoon.executeJob( jobMeta, true, false, null, false, copy.getName(), copy.getNr() );
}
public void handleJobMetaChanges( JobMeta jobMeta ) throws KettleException {
if ( jobMeta.hasChanged() ) {
if ( spoon.props.getAutoSave() ) {
if ( log.isDetailed() ) {
log.logDetailed( BaseMessages.getString( PKG, "JobLog.Log.AutoSaveFileBeforeRunning" ) );
}
spoon.saveToFile( jobMeta );
} else {
MessageDialogWithToggle md =
new MessageDialogWithToggle(
shell, BaseMessages.getString( PKG, "JobLog.Dialog.SaveChangedFile.Title" ), null, BaseMessages
.getString( PKG, "JobLog.Dialog.SaveChangedFile.Message" )
+ Const.CR
+ BaseMessages.getString( PKG, "JobLog.Dialog.SaveChangedFile.Message2" )
+ Const.CR,
MessageDialog.QUESTION,
new String[] {
BaseMessages.getString( PKG, "System.Button.Yes" ),
BaseMessages.getString( PKG, "System.Button.No" ) },
0, BaseMessages.getString( PKG, "JobLog.Dialog.SaveChangedFile.Toggle" ), spoon.props.getAutoSave() );
int answer = md.open();
if ( ( answer & 0xFF ) == 0 ) {
spoon.saveToFile( jobMeta );
}
spoon.props.setAutoSave( md.getToggleState() );
}
}
}
private JobEntryCopy lastChained = null;
public void addJobEntryToChain( String typeDesc, boolean shift ) {
JobMeta jobMeta = spoon.getActiveJob();
if ( jobMeta == null ) {
return;
}
//Is the lastChained entry still valid?
//
if ( lastChained != null && jobMeta.findJobEntry( lastChained.getName(), lastChained.getNr(), false ) == null ) {
lastChained = null;
}
// If there is exactly one selected step, pick that one as last chained.
//
List<JobEntryCopy> sel = jobMeta.getSelectedEntries();
if ( sel.size() == 1 ) {
lastChained = sel.get( 0 );
}
// Where do we add this?
Point p = null;
if ( lastChained == null ) {
p = jobMeta.getMaximum();
p.x -= 100;
} else {
p = new Point( lastChained.getLocation().x, lastChained.getLocation().y );
}
p.x += 200;
// Which is the new entry?
JobEntryCopy newEntry = spoon.newJobEntry( jobMeta, typeDesc, false );
if ( newEntry == null ) {
return;
}
newEntry.setLocation( p.x, p.y );
newEntry.setDrawn();
if ( lastChained != null ) {
spoon.newJobHop( jobMeta, lastChained, newEntry );
}
lastChained = newEntry;
spoon.refreshGraph();
spoon.refreshTree();
if ( shift ) {
editEntry( newEntry );
}
jobMeta.unselectAll();
newEntry.setSelected( true );
}
public Spoon getSpoon() {
return spoon;
}
public void setSpoon( Spoon spoon ) {
this.spoon = spoon;
}
public JobMeta getJobMeta() {
return jobMeta;
}
public Job getJob() {
return job;
}
}