/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.trans;
import java.util.List;
import java.util.Map;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.NotePadMeta;
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.BasePainter;
import org.pentaho.di.core.gui.GCInterface;
import org.pentaho.di.core.gui.Point;
import org.pentaho.di.core.gui.PrimitiveGCInterface.EColor;
import org.pentaho.di.core.gui.PrimitiveGCInterface.EFont;
import org.pentaho.di.core.gui.PrimitiveGCInterface.EImage;
import org.pentaho.di.core.gui.PrimitiveGCInterface.ELineStyle;
import org.pentaho.di.core.gui.Rectangle;
import org.pentaho.di.core.gui.ScrollBarInterface;
import org.pentaho.di.core.injection.bean.BeanInjectionInfo;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.partition.PartitionSchema;
import org.pentaho.di.trans.step.BaseStepData.StepExecutionStatus;
import org.pentaho.di.trans.step.StepIOMetaInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.trans.step.StepPartitioningMeta;
import org.pentaho.di.trans.step.StepStatus;
import org.pentaho.di.trans.step.errorhandling.StreamInterface;
import org.pentaho.di.trans.step.errorhandling.StreamInterface.StreamType;
public class TransPainter extends BasePainter<TransHopMeta, StepMeta> {
private static Class<?> PKG = TransPainter.class; // for i18n purposes, needed by Translator2!!
public static final String STRING_PARTITIONING_CURRENT_STEP = "PartitioningCurrentStep";
public static final String STRING_REMOTE_INPUT_STEPS = "RemoteInputSteps";
public static final String STRING_REMOTE_OUTPUT_STEPS = "RemoteOutputSteps";
public static final String STRING_STEP_ERROR_LOG = "StepErrorLog";
public static final String STRING_HOP_TYPE_COPY = "HopTypeCopy";
public static final String STRING_ROW_DISTRIBUTION = "RowDistribution";
private TransMeta transMeta;
private Map<StepMeta, String> stepLogMap;
private List<StepMeta> mouseOverSteps;
private StepMeta startHopStep;
private Point endHopLocation;
private StepMeta endHopStep;
private StepMeta noInputStep;
private StreamType candidateHopType;
private boolean startErrorHopStep;
private StepMeta showTargetStreamsStep;
private Trans trans;
private boolean slowStepIndicatorEnabled;
public static final String[] magnificationDescriptions =
new String[] { " 200% ", " 150% ", " 100% ", " 75% ", " 50% ", " 25% " };
public TransPainter( GCInterface gc, TransMeta transMeta, Point area, ScrollBarInterface hori,
ScrollBarInterface vert, TransHopMeta candidate, Point drop_candidate, Rectangle selrect,
List<AreaOwner> areaOwners, List<StepMeta> mouseOverSteps, int iconsize, int linewidth, int gridsize,
int shadowSize, boolean antiAliasing, String noteFontName, int noteFontHeight, Trans trans,
boolean slowStepIndicatorEnabled ) {
super(
gc, transMeta, area, hori, vert, drop_candidate, selrect, areaOwners, iconsize, linewidth, gridsize,
shadowSize, antiAliasing, noteFontName, noteFontHeight );
this.transMeta = transMeta;
this.candidate = candidate;
this.mouseOverSteps = mouseOverSteps;
this.trans = trans;
this.slowStepIndicatorEnabled = slowStepIndicatorEnabled;
stepLogMap = null;
}
public TransPainter( GCInterface gc, TransMeta transMeta, Point area, ScrollBarInterface hori,
ScrollBarInterface vert, TransHopMeta candidate, Point drop_candidate, Rectangle selrect,
List<AreaOwner> areaOwners, List<StepMeta> mouseOverSteps, int iconsize, int linewidth, int gridsize,
int shadowSize, boolean antiAliasing, String noteFontName, int noteFontHeight ) {
this(
gc, transMeta, area, hori, vert, candidate, drop_candidate, selrect, areaOwners, mouseOverSteps, iconsize,
linewidth, gridsize, shadowSize, antiAliasing, noteFontName, noteFontHeight, new Trans( transMeta ), false );
}
private static String[] getPeekTitles() {
String[] titles =
{
BaseMessages.getString( PKG, "PeekMetric.Column.Copynr" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Read" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Written" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Input" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Output" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Updated" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Rejected" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Errors" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Active" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Time" ),
BaseMessages.getString( PKG, "PeekMetric.Column.Speed" ),
BaseMessages.getString( PKG, "PeekMetric.Column.PriorityBufferSizes" ) };
return titles;
}
public void buildTransformationImage() {
Point max = transMeta.getMaximum();
Point thumb = getThumb( area, max );
offset = getOffset( thumb, area );
// First clear the image in the background color
gc.setBackground( EColor.BACKGROUND );
gc.fillRectangle( 0, 0, area.x, area.y );
// If there is a shadow, we draw the transformation first with an alpha setting
//
if ( shadowSize > 0 ) {
shadow = true;
gc.setTransform( translationX, translationY, shadowSize, magnification );
gc.setAlpha( 20 );
drawTrans( thumb );
}
// Draw the transformation onto the image
//
shadow = false;
gc.setTransform( translationX, translationY, 0, magnification );
gc.setAlpha( 255 );
drawTrans( thumb );
gc.dispose();
}
private void drawTrans( Point thumb ) {
if ( !shadow && gridSize > 1 ) {
drawGrid();
}
if ( hori != null && vert != null ) {
hori.setThumb( thumb.x );
vert.setThumb( thumb.y );
}
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransPainterStart.id, this );
} catch ( KettleException e ) {
LogChannel.GENERAL.logError( "Error in TransPainterStart extension point", e );
}
gc.setFont( EFont.NOTE );
// First the notes
for ( int i = 0; i < transMeta.nrNotes(); i++ ) {
NotePadMeta ni = transMeta.getNote( i );
drawNote( ni );
}
gc.setFont( EFont.GRAPH );
gc.setBackground( EColor.BACKGROUND );
for ( int i = 0; i < transMeta.nrTransHops(); i++ ) {
TransHopMeta hi = transMeta.getTransHop( i );
drawHop( hi );
}
EImage arrow;
if ( candidate != null ) {
drawHop( candidate, true );
} else {
if ( startHopStep != null && endHopLocation != null ) {
Point fr = startHopStep.getLocation();
Point to = endHopLocation;
if ( endHopStep == null ) {
gc.setForeground( EColor.GRAY );
arrow = EImage.ARROW_DISABLED;
} else {
gc.setForeground( EColor.BLUE );
arrow = EImage.ARROW_DEFAULT;
}
Point start = real2screen( fr.x + iconsize / 2, fr.y + iconsize / 2 );
Point end = real2screen( to.x, to.y );
drawArrow( arrow, start.x, start.y, end.x, end.y, theta, calcArrowLength(), 1.2, null, startHopStep,
endHopStep == null ? endHopLocation : endHopStep );
} else if ( endHopStep != null && endHopLocation != null ) {
Point fr = endHopLocation;
Point to = endHopStep.getLocation();
if ( startHopStep == null ) {
gc.setForeground( EColor.GRAY );
arrow = EImage.ARROW_DISABLED;
} else {
gc.setForeground( EColor.BLUE );
arrow = EImage.ARROW_DEFAULT;
}
Point start = real2screen( fr.x, fr.y );
Point end = real2screen( to.x + iconsize / 2, to.y + iconsize / 2 );
drawArrow( arrow, start.x, start.y, end.x, end.y, theta, calcArrowLength(), 1.2, null, startHopStep == null
? endHopLocation : startHopStep, endHopStep );
}
}
// Draw regular step appearance
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isDrawn() ) {
drawStep( stepMeta );
}
}
if ( slowStepIndicatorEnabled ) {
// Highlight possible bottlenecks
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isDrawn() ) {
checkDrawSlowStepIndicator( stepMeta );
}
}
}
// Draw step status indicators (running vs. done)
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isDrawn() ) {
drawStepStatusIndicator( stepMeta );
}
}
// Draw performance table for selected step(s)
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isDrawn() ) {
drawStepPerformanceTable( stepMeta );
}
}
int selectedStepsCount = 0;
for ( int i = transMeta.nrSteps() - 1; i >= 0; i-- ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isSelected() ) {
selectedStepsCount++;
}
}
TransPainterFlyoutExtension extension = null;
for ( int i = transMeta.nrSteps() - 1; i >= 0; i-- ) {
StepMeta stepMeta = transMeta.getStep( i );
if ( stepMeta.isSelected() && stepMeta.isDrawn() && selectedStepsCount == 1 ) {
extension = new TransPainterFlyoutExtension(
gc, areaOwners, transMeta, stepMeta, translationX, translationY, magnification, area, offset );
break;
}
}
if ( extension == null ) {
// pass null to notify extension that nothing is selected
extension = new TransPainterFlyoutExtension(
gc, areaOwners, transMeta, null, translationX, translationY, magnification, area, offset );
}
try {
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, KettleExtensionPoint.TransPainterFlyout.id, extension );
} catch ( Exception e ) {
LogChannel.GENERAL.logError( "Error calling extension point(s) for the transformation painter step", e );
}
// Display an icon on the indicated location signaling to the user that the step in question does not accept input
//
if ( noInputStep != null ) {
gc.setLineWidth( 2 );
gc.setForeground( EColor.RED );
Point n = noInputStep.getLocation();
gc.drawLine( n.x - 5, n.y - 5, n.x + iconsize + 10, n.y + iconsize + 10 );
gc.drawLine( n.x - 5, n.y + iconsize + 5, n.x + iconsize + 5, n.y - 5 );
}
if ( drop_candidate != null ) {
gc.setLineStyle( ELineStyle.SOLID );
gc.setForeground( EColor.BLACK );
Point screen = real2screen( drop_candidate.x, drop_candidate.y );
gc.drawRectangle( screen.x, screen.y, iconsize, iconsize );
}
try {
ExtensionPointHandler.callExtensionPoint( LogChannel.GENERAL, KettleExtensionPoint.TransPainterEnd.id, this );
} catch ( KettleException e ) {
LogChannel.GENERAL.logError( "Error in TransPainterEnd extension point", e );
}
if ( !shadow ) {
drawRect( selrect );
}
}
private void checkDrawSlowStepIndicator( StepMeta stepMeta ) {
if ( stepMeta == null ) {
return;
}
// draw optional performance indicator
if ( trans != null ) {
Point pt = stepMeta.getLocation();
if ( pt == null ) {
pt = new Point( 50, 50 );
}
Point screen = real2screen( pt.x, pt.y );
int x = screen.x;
int y = screen.y;
List<StepInterface> steps = trans.findBaseSteps( stepMeta.getName() );
for ( StepInterface step : steps ) {
if ( step.isRunning() ) {
int inputRows = step.rowsetInputSize();
int outputRows = step.rowsetOutputSize();
// if the step can't keep up with its input, mark it by drawing an animation
boolean isSlow = inputRows * 0.85 > outputRows;
if ( isSlow ) {
gc.setLineWidth( linewidth + 1 );
if ( System.currentTimeMillis() % 2000 > 1000 ) {
gc.setForeground( EColor.BACKGROUND );
gc.setLineStyle( ELineStyle.SOLID );
gc.drawRectangle( x + 1, y + 1, iconsize - 2, iconsize - 2 );
gc.setForeground( EColor.DARKGRAY );
gc.setLineStyle( ELineStyle.DOT );
gc.drawRectangle( x + 1, y + 1, iconsize - 2, iconsize - 2 );
} else {
gc.setForeground( EColor.DARKGRAY );
gc.setLineStyle( ELineStyle.SOLID );
gc.drawRectangle( x + 1, y + 1, iconsize - 2, iconsize - 2 );
gc.setForeground( EColor.BACKGROUND );
gc.setLineStyle( ELineStyle.DOT );
gc.drawRectangle( x + 1, y + 1, iconsize - 2, iconsize - 2 );
}
}
}
gc.setLineStyle( ELineStyle.SOLID );
}
}
}
private void drawStepPerformanceTable( StepMeta stepMeta ) {
if ( stepMeta == null ) {
return;
}
// draw optional performance indicator
if ( trans != null ) {
Point pt = stepMeta.getLocation();
if ( pt == null ) {
pt = new Point( 50, 50 );
}
Point screen = real2screen( pt.x, pt.y );
int x = screen.x;
int y = screen.y;
List<StepInterface> steps = trans.findBaseSteps( stepMeta.getName() );
// draw mouse over performance indicator
if ( trans.isRunning() ) {
if ( stepMeta.isSelected() ) {
// determine popup dimensions up front
int popupX = x;
int popupY = y;
int popupWidth = 0;
int popupHeight = 1;
gc.setFont( EFont.SMALL );
Point p = gc.textExtent( "0000000000" );
int colWidth = p.x + MINI_ICON_MARGIN;
int rowHeight = p.y + MINI_ICON_MARGIN;
int titleWidth = 0;
// calculate max title width to get the colum with
String[] titles = TransPainter.getPeekTitles();
for ( String title : titles ) {
Point titleExtent = gc.textExtent( title );
titleWidth = Math.max( titleExtent.x + MINI_ICON_MARGIN, titleWidth );
popupHeight += titleExtent.y + MINI_ICON_MARGIN;
}
popupWidth = titleWidth + 2 * MINI_ICON_MARGIN;
// determine total popup width
popupWidth += steps.size() * colWidth;
// determine popup position
popupX = popupX + ( iconsize - popupWidth ) / 2;
popupY = popupY - popupHeight - MINI_ICON_MARGIN;
// draw the frame
gc.setForeground( EColor.DARKGRAY );
gc.setBackground( EColor.LIGHTGRAY );
gc.setLineWidth( 1 );
gc.fillRoundRectangle( popupX, popupY, popupWidth, popupHeight, 7, 7 );
// draw the title columns
// gc.setBackground(EColor.BACKGROUND);
// gc.fillRoundRectangle(popupX, popupY, titleWidth+MINI_ICON_MARGIN, popupHeight, 7, 7);
gc.setBackground( EColor.LIGHTGRAY );
gc.drawRoundRectangle( popupX, popupY, popupWidth, popupHeight, 7, 7 );
for ( int i = 0, barY = popupY; i < titles.length; i++ ) {
// fill each line with a slightly different background color
if ( i % 2 == 1 ) {
gc.setBackground( EColor.BACKGROUND );
} else {
gc.setBackground( EColor.LIGHTGRAY );
}
gc.fillRoundRectangle( popupX + 1, barY + 1, popupWidth - 2, rowHeight, 7, 7 );
barY += rowHeight;
}
// draw the header column
int rowY = popupY + MINI_ICON_MARGIN;
int rowX = popupX + MINI_ICON_MARGIN;
gc.setForeground( EColor.BLACK );
gc.setBackground( EColor.BACKGROUND );
for ( int i = 0; i < titles.length; i++ ) {
if ( i % 2 == 1 ) {
gc.setBackground( EColor.BACKGROUND );
} else {
gc.setBackground( EColor.LIGHTGRAY );
}
gc.drawText( titles[i], rowX, rowY );
rowY += rowHeight;
}
// draw the values for each copy of the step
gc.setBackground( EColor.LIGHTGRAY );
rowX += titleWidth;
for ( StepInterface step : steps ) {
rowX += colWidth;
rowY = popupY + MINI_ICON_MARGIN;
StepStatus stepStatus = new StepStatus( step );
String[] fields = stepStatus.getPeekFields();
for ( int i = 0; i < fields.length; i++ ) {
if ( i % 2 == 1 ) {
gc.setBackground( EColor.BACKGROUND );
} else {
gc.setBackground( EColor.LIGHTGRAY );
}
drawTextRightAligned( fields[i], rowX, rowY );
rowY += rowHeight;
}
}
}
}
}
}
private void drawStepStatusIndicator( StepMeta stepMeta ) {
if ( stepMeta == null ) {
return;
}
// draw status indicator
if ( trans != null ) {
Point pt = stepMeta.getLocation();
if ( pt == null ) {
pt = new Point( 50, 50 );
}
Point screen = real2screen( pt.x, pt.y );
int x = screen.x;
int y = screen.y;
List<StepInterface> steps = trans.findBaseSteps( stepMeta.getName() );
for ( StepInterface step : steps ) {
if ( step.getStatus().equals( StepExecutionStatus.STATUS_FINISHED ) ) {
gc.drawImage( EImage.TRUE, ( x + iconsize ) - ( MINI_ICON_SIZE / 2 ) + 4, y - ( MINI_ICON_SIZE / 2 ) - 1, magnification );
}
}
}
}
private void drawTextRightAligned( String txt, int x, int y ) {
int off = gc.textExtent( txt ).x;
x -= off;
gc.drawText( txt, x, y );
}
private void drawHop( TransHopMeta hi ) {
drawHop( hi, false );
}
private void drawHop( TransHopMeta hi, boolean isCandidate ) {
StepMeta fs = hi.getFromStep();
StepMeta ts = hi.getToStep();
if ( fs != null && ts != null ) {
drawLine( fs, ts, hi, isCandidate );
}
}
private void drawStep( StepMeta stepMeta ) {
if ( stepMeta == null ) {
return;
}
int alpha = gc.getAlpha();
StepIOMetaInterface ioMeta = stepMeta.getStepMetaInterface().getStepIOMeta();
Point pt = stepMeta.getLocation();
if ( pt == null ) {
pt = new Point( 50, 50 );
}
Point screen = real2screen( pt.x, pt.y );
int x = screen.x;
int y = screen.y;
boolean stepError = false;
if ( stepLogMap != null && !stepLogMap.isEmpty() ) {
String log = stepLogMap.get( stepMeta );
if ( !Utils.isEmpty( log ) ) {
stepError = true;
}
}
// REMOTE STEPS
// First draw an extra indicator for remote input steps...
//
if ( !stepMeta.getRemoteInputSteps().isEmpty() ) {
gc.setLineWidth( 1 );
gc.setForeground( EColor.GRAY );
gc.setBackground( EColor.BACKGROUND );
gc.setFont( EFont.GRAPH );
String nrInput = Integer.toString( stepMeta.getRemoteInputSteps().size() );
Point textExtent = gc.textExtent( nrInput );
textExtent.x += 2; // add a tiny listartHopStepttle bit of a margin
textExtent.y += 2;
// Draw it an icon above the step icon.
// Draw it an icon and a half to the left
//
Point point = new Point( x - iconsize - iconsize / 2, y - iconsize );
gc.drawRectangle( point.x, point.y, textExtent.x, textExtent.y );
gc.drawText( nrInput, point.x + 1, point.y + 1 );
// Now we draw an arrow from the cube to the step...
//
gc.drawLine( point.x + textExtent.x, point.y + textExtent.y / 2, x - iconsize / 2, point.y
+ textExtent.y / 2 );
drawArrow( EImage.ARROW_DISABLED,
x - iconsize / 2, point.y + textExtent.y / 2, x + iconsize / 3, y, Math.toRadians( 15 ), 15, 1.8, null,
null, null );
// Add to the list of areas...
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.REMOTE_INPUT_STEP, point.x, point.y, textExtent.x, textExtent.y, offset, stepMeta,
STRING_REMOTE_INPUT_STEPS ) );
}
}
// Then draw an extra indicator for remote output steps...
//
if ( !stepMeta.getRemoteOutputSteps().isEmpty() ) {
gc.setLineWidth( 1 );
gc.setForeground( EColor.GRAY );
gc.setBackground( EColor.BACKGROUND );
gc.setFont( EFont.GRAPH );
String nrOutput = Integer.toString( stepMeta.getRemoteOutputSteps().size() );
Point textExtent = gc.textExtent( nrOutput );
textExtent.x += 2; // add a tiny little bit of a margin
textExtent.y += 2;
// Draw it an icon above the step icon.
// Draw it an icon and a half to the right
//
Point point = new Point( x + 2 * iconsize + iconsize / 2 - textExtent.x, y - iconsize );
gc.drawRectangle( point.x, point.y, textExtent.x, textExtent.y );
gc.drawText( nrOutput, point.x + 1, point.y + 1 );
// Now we draw an arrow from the cube to the step...
// This time, we start at the left side...
//
gc.drawLine( point.x, point.y + textExtent.y / 2, x + iconsize + iconsize / 2, point.y + textExtent.y / 2 );
drawArrow( EImage.ARROW_DISABLED, x + 2 * iconsize / 3, y, x + iconsize + iconsize / 2, point.y + textExtent.y / 2, Math
.toRadians( 15 ), 15, 1.8, null, null, null );
// Add to the list of areas...
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.REMOTE_OUTPUT_STEP, point.x, point.y, textExtent.x, textExtent.y, offset, stepMeta,
STRING_REMOTE_OUTPUT_STEPS ) );
}
}
// PARTITIONING
// If this step is partitioned, we're drawing a small symbol indicating this...
//
if ( stepMeta.isPartitioned() ) {
gc.setLineWidth( 1 );
gc.setForeground( EColor.RED );
gc.setBackground( EColor.BACKGROUND );
gc.setFont( EFont.GRAPH );
PartitionSchema partitionSchema = stepMeta.getStepPartitioningMeta().getPartitionSchema();
if ( partitionSchema != null ) {
String nrInput;
if ( partitionSchema.isDynamicallyDefined() ) {
nrInput = "Dx" + partitionSchema.getNumberOfPartitionsPerSlave();
} else {
nrInput = "Px" + Integer.toString( partitionSchema.getPartitionIDs().size() );
}
Point textExtent = gc.textExtent( nrInput );
textExtent.x += 2; // add a tiny little bit of a margin
textExtent.y += 2;
// Draw it a 2 icons above the step icon.
// Draw it an icon and a half to the left
//
Point point = new Point( x - iconsize - iconsize / 2, y - iconsize - iconsize );
gc.drawRectangle( point.x, point.y, textExtent.x, textExtent.y );
gc.drawText( nrInput, point.x + 1, point.y + 1 );
// Now we draw an arrow from the cube to the step...
//
gc.drawLine( point.x + textExtent.x, point.y + textExtent.y / 2, x - iconsize / 2, point.y
+ textExtent.y / 2 );
gc.drawLine( x - iconsize / 2, point.y + textExtent.y / 2, x + iconsize / 3, y );
// Also draw the name of the partition schema below the box
//
gc.setForeground( EColor.GRAY );
gc.drawText( Const.NVL( partitionSchema.getName(), "<no partition name>" ), point.x, point.y
+ textExtent.y + 3, true );
// Add to the list of areas...
//
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.STEP_PARTITIONING, point.x, point.y, textExtent.x, textExtent.y, offset, stepMeta,
STRING_PARTITIONING_CURRENT_STEP ) );
}
}
}
String name = stepMeta.getName();
if ( stepMeta.isSelected() ) {
gc.setLineWidth( linewidth + 2 );
} else {
gc.setLineWidth( linewidth );
}
// Add to the list of areas...
if ( !shadow ) {
areaOwners.add( new AreaOwner( AreaType.STEP_ICON, x, y, iconsize, iconsize, offset, transMeta, stepMeta ) );
}
gc.setBackground( EColor.BACKGROUND );
gc.fillRoundRectangle( x - 1, y - 1, iconsize + 1, iconsize + 1, 8, 8 );
gc.drawStepIcon( x, y, stepMeta, magnification );
if ( stepError || stepMeta.isMissing() ) {
gc.setForeground( EColor.RED );
} else {
gc.setForeground( EColor.CRYSTAL );
}
if ( stepMeta.isSelected() ) {
gc.setForeground( 0, 93, 166 );
}
gc.drawRoundRectangle( x - 1, y - 1, iconsize + 1, iconsize + 1, 8, 8 );
Point namePosition = getNamePosition( name, screen, iconsize );
if ( stepMeta.isSelected() ) {
int tmpAlpha = gc.getAlpha();
gc.setAlpha( 192 );
gc.setBackground( 216, 230, 241 );
gc.fillRoundRectangle( namePosition.x - 8, namePosition.y - 2, gc.textExtent( name ).x + 15, 25,
BasePainter.CORNER_RADIUS_5 + 15, BasePainter.CORNER_RADIUS_5 + 15 );
gc.setAlpha( tmpAlpha );
}
gc.setForeground( EColor.BLACK );
gc.setFont( EFont.GRAPH );
gc.drawText( name, namePosition.x, namePosition.y + 2, true );
boolean partitioned = false;
StepPartitioningMeta meta = stepMeta.getStepPartitioningMeta();
if ( stepMeta.isPartitioned() && meta != null ) {
partitioned = true;
}
String clusterMessage = "";
if ( stepMeta.getClusterSchema() != null ) {
clusterMessage = "C";
if ( stepMeta.getClusterSchema().isDynamic() ) {
clusterMessage += "xN";
} else {
clusterMessage += "x" + stepMeta.getClusterSchema().findNrSlaves();
}
Point textExtent = gc.textExtent( clusterMessage );
gc.setBackground( EColor.BACKGROUND );
gc.setForeground( EColor.BLACK );
gc.drawText( clusterMessage, x - textExtent.x + 1, y - textExtent.y + 1 );
}
if ( stepMeta.getCopies() != 1 && !partitioned ) {
gc.setBackground( EColor.BACKGROUND );
gc.setForeground( EColor.BLACK );
String copies = "x" + stepMeta.getCopiesString();
Point textExtent = gc.textExtent( copies );
if ( stepMeta.getClusterSchema() != null ) {
Point clusterTextExtent = gc.textExtent( clusterMessage );
gc.drawText( copies, x - textExtent.x + 1, y - textExtent.y - clusterTextExtent.y + 1, false );
areaOwners.add( new AreaOwner( AreaType.STEP_COPIES_TEXT, x - textExtent.x + 1, y - textExtent.y
- clusterTextExtent.y + 1, textExtent.x, textExtent.y, offset, transMeta, stepMeta ) );
} else {
gc.drawText( copies, x - textExtent.x + 1, y - textExtent.y + 1, false );
areaOwners.add( new AreaOwner( AreaType.STEP_COPIES_TEXT, x - textExtent.x + 1, y - textExtent.y + 1, textExtent.x,
textExtent.y, offset, transMeta, stepMeta ) );
}
}
// If there was an error during the run, the map "stepLogMap" is not empty and not null.
//
if ( stepError ) {
String log = stepLogMap.get( stepMeta );
// Show an error lines icon in the upper right corner of the step...
//
int xError = ( x + iconsize ) - ( MINI_ICON_SIZE / 2 ) + 4;
int yError = y - ( MINI_ICON_SIZE / 2 ) - 1;
Point ib = gc.getImageBounds( EImage.STEP_ERROR_RED );
gc.drawImage( EImage.STEP_ERROR_RED, xError, yError, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.STEP_ERROR_RED_ICON, pt.x + iconsize - 3, pt.y - 8, ib.x, ib.y, offset, log,
STRING_STEP_ERROR_LOG ) );
}
}
// Optionally drawn the mouse-over information
//
if ( mouseOverSteps.contains( stepMeta ) ) {
gc.setTransform( translationX, translationY, 0, BasePainter.FACTOR_1_TO_1 );
StepMetaInterface stepMetaInterface = stepMeta.getStepMetaInterface();
boolean mdiSupport =
stepMetaInterface.getStepMetaInjectionInterface() != null || BeanInjectionInfo.isInjectionSupported(
stepMetaInterface.getClass() );
EImage[] miniIcons;
if ( mdiSupport ) {
miniIcons = new EImage[] { EImage.INPUT, EImage.EDIT, EImage.CONTEXT_MENU, EImage.OUTPUT, EImage.INJECT, };
} else {
miniIcons = new EImage[] { EImage.INPUT, EImage.EDIT, EImage.CONTEXT_MENU, EImage.OUTPUT, };
}
int totalHeight = 0;
int totalIconsWidth = 0;
int totalWidth = 2 * MINI_ICON_MARGIN;
for ( EImage miniIcon : miniIcons ) {
Point bounds = gc.getImageBounds( miniIcon );
totalWidth += bounds.x + MINI_ICON_MARGIN;
totalIconsWidth += bounds.x + MINI_ICON_MARGIN;
if ( bounds.y > totalHeight ) {
totalHeight = bounds.y;
}
}
totalHeight += 2 * MINI_ICON_MARGIN;
gc.setFont( EFont.SMALL );
String trimmedName =
stepMeta.getName().length() < 30 ? stepMeta.getName() : stepMeta.getName().substring( 0, 30 );
Point nameExtent = gc.textExtent( trimmedName );
nameExtent.y += 2 * MINI_ICON_MARGIN;
nameExtent.x += 3 * MINI_ICON_MARGIN;
totalHeight += nameExtent.y;
if ( nameExtent.x > totalWidth ) {
totalWidth = nameExtent.x;
}
int areaX = translateToCurrentScale( x ) + translateToCurrentScale( iconsize ) / 2 - totalWidth / 2 + MINI_ICON_SKEW;
int areaY = translateToCurrentScale( y ) + translateToCurrentScale( iconsize ) + MINI_ICON_DISTANCE + BasePainter.CONTENT_MENU_INDENT;
gc.setForeground( EColor.CRYSTAL );
gc.setBackground( EColor.CRYSTAL );
gc.setLineWidth( 1 );
gc.fillRoundRectangle( areaX, areaY, totalWidth, totalHeight, BasePainter.CORNER_RADIUS_5, BasePainter.CORNER_RADIUS_5 );
gc.setBackground( EColor.WHITE );
gc.fillRoundRectangle( areaX, areaY + nameExtent.y, totalWidth, ( totalHeight - nameExtent.y ),
BasePainter.CORNER_RADIUS_5, BasePainter.CORNER_RADIUS_5 );
gc.fillRectangle( areaX, areaY + nameExtent.y, totalWidth, ( totalHeight - nameExtent.y ) / 2 );
gc.drawRoundRectangle( areaX, areaY, totalWidth, totalHeight, BasePainter.CORNER_RADIUS_5, BasePainter.CORNER_RADIUS_5 );
gc.setForeground( EColor.WHITE );
gc.drawText( trimmedName, areaX + ( totalWidth - nameExtent.x ) / 2 + MINI_ICON_MARGIN, areaY
+ MINI_ICON_MARGIN, true );
gc.setForeground( EColor.CRYSTAL );
gc.setBackground( EColor.CRYSTAL );
gc.setFont( EFont.GRAPH );
areaOwners.add( new AreaOwner( AreaType.MINI_ICONS_BALLOON, translateTo1To1( areaX ), translateTo1To1( areaY ),
translateTo1To1( totalWidth ), translateTo1To1( totalHeight ), offset, stepMeta, ioMeta ) );
gc.fillPolygon( new int[] {
areaX + totalWidth / 2 - MINI_ICON_TRIANGLE_BASE / 2 + 1, areaY + 2,
areaX + totalWidth / 2 + MINI_ICON_TRIANGLE_BASE / 2, areaY + 2,
areaX + totalWidth / 2 - MINI_ICON_SKEW, areaY - MINI_ICON_DISTANCE - 3, } );
gc.setBackground( EColor.WHITE );
// Put on the icons...
//
int xIcon = areaX + ( totalWidth - totalIconsWidth ) / 2 + MINI_ICON_MARGIN;
int yIcon = areaY + 5 + nameExtent.y;
for ( int i = 0; i < miniIcons.length; i++ ) {
EImage miniIcon = miniIcons[i];
Point bounds = gc.getImageBounds( miniIcon );
boolean enabled = false;
switch ( i ) {
case 0: // INPUT
enabled = ioMeta.isInputAcceptor() || ioMeta.isInputDynamic();
areaOwners.add( new AreaOwner( AreaType.STEP_INPUT_HOP_ICON, translateTo1To1( xIcon ),
translateTo1To1( yIcon ), translateTo1To1( bounds.x ), translateTo1To1( bounds.y ), offset, stepMeta,
ioMeta ) );
break;
case 1: // EDIT
enabled = true;
areaOwners.add( new AreaOwner( AreaType.STEP_EDIT_ICON, translateTo1To1( xIcon ), translateTo1To1( yIcon ),
translateTo1To1( bounds.x ), translateTo1To1( bounds.y ), offset, stepMeta, ioMeta ) );
break;
case 2: // STEP_MENU
enabled = true;
areaOwners.add( new AreaOwner( AreaType.STEP_MENU_ICON, translateTo1To1( xIcon ), translateTo1To1( yIcon ),
translateTo1To1( bounds.x ), translateTo1To1( bounds.y ), offset, stepMeta, ioMeta ) );
break;
case 3: // OUTPUT
enabled = ioMeta.isOutputProducer() || ioMeta.isOutputDynamic();
areaOwners.add( new AreaOwner( AreaType.STEP_OUTPUT_HOP_ICON, translateTo1To1( xIcon ),
translateTo1To1( yIcon ), translateTo1To1( bounds.x ), translateTo1To1( bounds.y ), offset, stepMeta,
ioMeta ) );
break;
case 4: // INJECT
enabled = mdiSupport;
StepMetaInterface mdiObject = mdiSupport ? stepMetaInterface : null;
areaOwners.add( new AreaOwner( AreaType.STEP_INJECT_ICON, translateTo1To1( xIcon ),
translateTo1To1( yIcon ), translateTo1To1( bounds.x ), translateTo1To1( bounds.y ), offset, stepMeta,
mdiObject ) );
break;
default:
break;
}
if ( enabled ) {
gc.setAlpha( 255 );
} else {
gc.setAlpha( 100 );
}
gc.drawImage( miniIcon, xIcon, yIcon, BasePainter.FACTOR_1_TO_1 );
xIcon += bounds.x + 5;
}
// OK, see if we need to show a slide-out for target streams...
//
if ( showTargetStreamsStep != null ) {
ioMeta = showTargetStreamsStep.getStepMetaInterface().getStepIOMeta();
List<StreamInterface> targetStreams = ioMeta.getTargetStreams();
int targetsWidth = 0;
int targetsHeight = 0;
for ( int i = 0; i < targetStreams.size(); i++ ) {
String description = targetStreams.get( i ).getDescription();
Point extent = gc.textExtent( description );
if ( extent.x > targetsWidth ) {
targetsWidth = extent.x;
}
targetsHeight += extent.y + MINI_ICON_MARGIN;
}
targetsWidth += MINI_ICON_MARGIN;
gc.setBackground( EColor.LIGHTGRAY );
gc.fillRoundRectangle( areaX, areaY + totalHeight + 2, targetsWidth, targetsHeight, 7, 7 );
gc.drawRoundRectangle( areaX, areaY + totalHeight + 2, targetsWidth, targetsHeight, 7, 7 );
int targetY = areaY + totalHeight + MINI_ICON_MARGIN;
for ( int i = 0; i < targetStreams.size(); i++ ) {
String description = targetStreams.get( i ).getDescription();
Point extent = gc.textExtent( description );
gc.drawText( description, areaX + MINI_ICON_MARGIN, targetY, true );
if ( i < targetStreams.size() - 1 ) {
gc.drawLine( areaX + MINI_ICON_MARGIN / 2, targetY + extent.y + 3, areaX
+ targetsWidth - MINI_ICON_MARGIN / 2, targetY + extent.y + 2 );
}
areaOwners.add( new AreaOwner(
AreaType.STEP_TARGET_HOP_ICON_OPTION, areaX, targetY, targetsWidth, extent.y + MINI_ICON_MARGIN,
offset, stepMeta, targetStreams.get( i ) ) );
targetY += extent.y + MINI_ICON_MARGIN;
}
gc.setBackground( EColor.BACKGROUND );
}
gc.setTransform( translationX, translationY, 0, magnification );
}
TransPainterExtension extension =
new TransPainterExtension(
gc, shadow, areaOwners, transMeta, stepMeta, null, x, y, 0, 0, 0, 0, offset, iconsize );
try {
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, KettleExtensionPoint.TransPainterStep.id, extension );
} catch ( Exception e ) {
LogChannel.GENERAL.logError( "Error calling extension point(s) for the transformation painter step", e );
}
// Restore the previous alpha value
//
gc.setAlpha( alpha );
}
public Point getNamePosition( String string, Point screen, int iconsize ) {
Point textsize = gc.textExtent( string );
int xpos = screen.x + ( iconsize / 2 ) - ( textsize.x / 2 );
int ypos = screen.y + iconsize + 5;
return new Point( xpos, ypos );
}
private void drawLine( StepMeta fs, StepMeta ts, TransHopMeta hi, boolean is_candidate ) {
int[] line = getLine( fs, ts );
EColor col;
ELineStyle linestyle = ELineStyle.SOLID;
int activeLinewidth = linewidth;
EImage arrow;
if ( is_candidate ) {
col = EColor.BLUE;
arrow = EImage.ARROW_CANDIDATE;
} else {
if ( hi.isEnabled() ) {
if ( fs.isSendingErrorRowsToStep( ts ) ) {
col = EColor.RED;
linestyle = ELineStyle.DASH;
activeLinewidth = linewidth + 1;
arrow = EImage.ARROW_ERROR;
} else {
col = EColor.HOP_DEFAULT;
arrow = EImage.ARROW_DEFAULT;
}
} else {
col = EColor.GRAY;
arrow = EImage.ARROW_DISABLED;
}
}
if ( hi.split ) {
activeLinewidth = linewidth + 2;
}
// Check to see if the source step is an info step for the target step.
//
StepIOMetaInterface ioMeta = ts.getStepMetaInterface().getStepIOMeta();
List<StreamInterface> infoStreams = ioMeta.getInfoStreams();
if ( !infoStreams.isEmpty() ) {
// Check this situation, the source step can't run in multiple copies!
//
for ( StreamInterface stream : infoStreams ) {
if ( fs.getName().equalsIgnoreCase( stream.getStepname() ) ) {
// This is the info step over this hop!
//
if ( fs.getCopies() > 1 ) {
// This is not a desirable situation, it will always end in error.
// As such, it's better not to give feedback on it.
// We do this by drawing an error icon over the hop...
//
col = EColor.RED;
arrow = EImage.ARROW_ERROR;
}
}
}
}
gc.setForeground( col );
gc.setLineStyle( linestyle );
gc.setLineWidth( activeLinewidth );
drawArrow( arrow, line, hi, fs, ts );
if ( hi.split ) {
gc.setLineWidth( linewidth );
}
gc.setForeground( EColor.BLACK );
gc.setBackground( EColor.BACKGROUND );
gc.setLineStyle( ELineStyle.SOLID );
}
@Override
protected void drawArrow( EImage arrow, int x1, int y1, int x2, int y2, double theta, int size, double factor,
TransHopMeta transHop, Object startObject, Object endObject ) {
int mx, my;
int a, b, dist;
double angle;
gc.drawLine( x1, y1, x2, y2 );
// in between 2 points
mx = x1 + ( x2 - x1 ) / 2;
my = y1 + ( y2 - y1 ) / 2;
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 ( factor < 0 ) {
if ( dist >= 2 * iconsize ) {
factor = 1.3;
} 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
// calculate points for arrowhead
angle = Math.atan2( y2 - y1, x2 - x1 ) + ( Math.PI / 2 );
boolean q1 = Math.toDegrees( angle ) >= 0 && Math.toDegrees( angle ) <= 90;
boolean q2 = Math.toDegrees( angle ) > 90 && Math.toDegrees( angle ) <= 180;
boolean q3 = Math.toDegrees( angle ) > 180 && Math.toDegrees( angle ) <= 270;
boolean q4 = Math.toDegrees( angle ) > 270 || Math.toDegrees( angle ) < 0;
if ( q1 || q3 ) {
gc.drawImage( arrow, mx + 1, my, magnification, angle );
} else if ( q2 || q4 ) {
gc.drawImage( arrow, mx, my, magnification, angle );
}
if ( startObject instanceof StepMeta && endObject instanceof StepMeta ) {
factor = 0.8;
StepMeta fs = (StepMeta) startObject;
StepMeta ts = (StepMeta) endObject;
// in between 2 points
mx = (int) ( x1 + factor * ( x2 - x1 ) / 2 ) - 8;
my = (int) ( y1 + factor * ( y2 - y1 ) / 2 ) - 8;
boolean errorHop = fs.isSendingErrorRowsToStep( ts ) || ( startErrorHopStep && fs.equals( startHopStep ) );
boolean targetHop =
Const.indexOfString( ts.getName(), fs.getStepMetaInterface().getStepIOMeta().getTargetStepnames() ) >= 0;
if ( targetHop ) {
StepIOMetaInterface ioMeta = fs.getStepMetaInterface().getStepIOMeta();
StreamInterface targetStream = ioMeta.findTargetStream( ts );
if ( targetStream != null ) {
EImage hopsIcon = BasePainter.getStreamIconImage( targetStream.getStreamIcon() );
Point bounds = gc.getImageBounds( hopsIcon );
gc.drawImage( hopsIcon, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.STEP_TARGET_HOP_ICON, mx, my, bounds.x, bounds.y, offset, fs, targetStream ) );
}
}
} else if ( fs.isDistributes()
&& fs.getRowDistribution() != null && !ts.getStepPartitioningMeta().isMethodMirror() && !errorHop ) {
// Draw the custom row distribution plugin icon
//
EImage eImage = fs.getRowDistribution().getDistributionImage();
if ( eImage != null ) {
Point bounds = gc.getImageBounds( eImage );
gc.drawImage( eImage, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.ROW_DISTRIBUTION_ICON, mx, my, bounds.x, bounds.y, offset, fs, STRING_ROW_DISTRIBUTION ) );
}
mx += 16;
}
} else if ( !fs.isDistributes() && !ts.getStepPartitioningMeta().isMethodMirror() && !errorHop ) {
// Draw the copy icon on the hop
//
Point bounds = gc.getImageBounds( EImage.COPY_ROWS );
gc.drawImage( EImage.COPY_ROWS, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.HOP_COPY_ICON, mx, my, bounds.x, bounds.y, offset, fs, STRING_HOP_TYPE_COPY ) );
}
mx += 16;
}
if ( errorHop ) {
Point bounds = gc.getImageBounds( EImage.COPY_ROWS );
gc.drawImage( EImage.FALSE, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner( AreaType.HOP_ERROR_ICON, mx, my, bounds.x, bounds.y, offset, fs, ts ) );
}
mx += 16;
}
StepIOMetaInterface ioMeta = ts.getStepMetaInterface().getStepIOMeta();
String[] infoStepnames = ioMeta.getInfoStepnames();
if ( ( candidateHopType == StreamType.INFO && ts.equals( endHopStep ) && fs.equals( startHopStep ) )
|| Const.indexOfString( fs.getName(), infoStepnames ) >= 0 ) {
Point bounds = gc.getImageBounds( EImage.INFO );
gc.drawImage( EImage.INFO, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner( AreaType.HOP_INFO_ICON, mx, my, bounds.x, bounds.y, offset, fs, ts ) );
}
mx += 16;
}
// Check to see if the source step is an info step for the target step.
//
if ( !Utils.isEmpty( infoStepnames ) ) {
// Check this situation, the source step can't run in multiple copies!
//
for ( String infoStep : infoStepnames ) {
if ( fs.getName().equalsIgnoreCase( infoStep ) ) {
// This is the info step over this hop!
//
if ( fs.getCopies() > 1 ) {
// This is not a desirable situation, it will always end in error.
// As such, it's better not to give feedback on it.
// We do this by drawing an error icon over the hop...
//
gc.drawImage( EImage.ERROR, mx, my, magnification );
if ( !shadow ) {
areaOwners.add( new AreaOwner(
AreaType.HOP_INFO_STEP_COPIES_ERROR, mx, my, MINI_ICON_SIZE, MINI_ICON_SIZE, offset, fs, ts ) );
}
mx += 16;
}
}
}
}
}
TransPainterExtension extension =
new TransPainterExtension(
gc, shadow, areaOwners, transMeta, null, transHop, x1, y1, x2, y2, mx, my, offset, iconsize );
try {
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, KettleExtensionPoint.TransPainterArrow.id, extension );
} catch ( Exception e ) {
LogChannel.GENERAL.logError( "Error calling extension point(s) for the transformation painter arrow", e );
}
}
/**
* @return the stepLogMap
*/
public Map<StepMeta, String> getStepLogMap() {
return stepLogMap;
}
/**
* @param stepLogMap
* the stepLogMap to set
*/
public void setStepLogMap( Map<StepMeta, String> stepLogMap ) {
this.stepLogMap = stepLogMap;
}
/**
* @param startHopStep
* the startHopStep to set
*/
public void setStartHopStep( StepMeta startHopStep ) {
this.startHopStep = startHopStep;
}
/**
* @param endHopLocation
* the endHopLocation to set
*/
public void setEndHopLocation( Point endHopLocation ) {
this.endHopLocation = endHopLocation;
}
/**
* @param noInputStep
* the noInputStep to set
*/
public void setNoInputStep( StepMeta noInputStep ) {
this.noInputStep = noInputStep;
}
/**
* @param endHopStep
* the endHopStep to set
*/
public void setEndHopStep( StepMeta endHopStep ) {
this.endHopStep = endHopStep;
}
public void setCandidateHopType( StreamType candidateHopType ) {
this.candidateHopType = candidateHopType;
}
public void setStartErrorHopStep( boolean startErrorHopStep ) {
this.startErrorHopStep = startErrorHopStep;
}
/**
* @return the showTargetStreamsStep
*/
public StepMeta getShowTargetStreamsStep() {
return showTargetStreamsStep;
}
/**
* @param showTargetStreamsStep
* the showTargetStreamsStep to set
*/
public void setShowTargetStreamsStep( StepMeta showTargetStreamsStep ) {
this.showTargetStreamsStep = showTargetStreamsStep;
}
public TransMeta getTransMeta() {
return transMeta;
}
public void setTransMeta( TransMeta transMeta ) {
this.transMeta = transMeta;
}
public List<StepMeta> getMouseOverSteps() {
return mouseOverSteps;
}
public void setMouseOverSteps( List<StepMeta> mouseOverSteps ) {
this.mouseOverSteps = mouseOverSteps;
}
public Trans getTrans() {
return trans;
}
public void setTrans( Trans trans ) {
this.trans = trans;
}
public boolean isSlowStepIndicatorEnabled() {
return slowStepIndicatorEnabled;
}
public void setSlowStepIndicatorEnabled( boolean slowStepIndicatorEnabled ) {
this.slowStepIndicatorEnabled = slowStepIndicatorEnabled;
}
public StepMeta getStartHopStep() {
return startHopStep;
}
public Point getEndHopLocation() {
return endHopLocation;
}
public StepMeta getEndHopStep() {
return endHopStep;
}
public StepMeta getNoInputStep() {
return noInputStep;
}
public StreamType getCandidateHopType() {
return candidateHopType;
}
public boolean isStartErrorHopStep() {
return startErrorHopStep;
}
}