use of org.pentaho.di.trans.step.errorhandling.StreamInterface in project pentaho-kettle by pentaho.
the class TransPainter method drawStep.
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;
}
}
//
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);
// add a tiny listartHopStepttle bit of a margin
textExtent.x += 2;
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));
}
}
//
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);
// add a tiny little bit of a margin
textExtent.x += 2;
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));
}
}
//
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);
// add a tiny little bit of a margin
textExtent.x += 2;
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);
//
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 (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));
}
}
//
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 // INPUT
0:
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 // EDIT
1:
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 // STEP_MENU
2:
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 // OUTPUT
3:
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 // INJECT
4:
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;
}
//
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);
}
use of org.pentaho.di.trans.step.errorhandling.StreamInterface in project pentaho-kettle by pentaho.
the class TransPainter method drawArrow.
@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);
// 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;
}
//
if (!Utils.isEmpty(infoStepnames)) {
//
for (String infoStep : infoStepnames) {
if (fs.getName().equalsIgnoreCase(infoStep)) {
//
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);
}
}
use of org.pentaho.di.trans.step.errorhandling.StreamInterface in project pentaho-kettle by pentaho.
the class MergeJoin method processRow.
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
meta = (MergeJoinMeta) smi;
data = (MergeJoinData) sdi;
int compare;
if (first) {
first = false;
// Find the RowSet to read from
//
List<StreamInterface> infoStreams = meta.getStepIOMeta().getInfoStreams();
data.oneRowSet = findInputRowSet(infoStreams.get(0).getStepname());
if (data.oneRowSet == null) {
throw new KettleException(BaseMessages.getString(PKG, "MergeJoin.Exception.UnableToFindSpecifiedStep", infoStreams.get(0).getStepname()));
}
data.twoRowSet = findInputRowSet(infoStreams.get(1).getStepname());
if (data.twoRowSet == null) {
throw new KettleException(BaseMessages.getString(PKG, "MergeJoin.Exception.UnableToFindSpecifiedStep", infoStreams.get(1).getStepname()));
}
data.one = getRowFrom(data.oneRowSet);
if (data.one != null) {
data.oneMeta = data.oneRowSet.getRowMeta();
} else {
data.one = null;
data.oneMeta = getTransMeta().getStepFields(infoStreams.get(0).getStepname());
}
data.two = getRowFrom(data.twoRowSet);
if (data.two != null) {
data.twoMeta = data.twoRowSet.getRowMeta();
} else {
data.two = null;
data.twoMeta = getTransMeta().getStepFields(infoStreams.get(1).getStepname());
}
// just for speed: oneMeta+twoMeta
//
data.outputRowMeta = new RowMeta();
data.outputRowMeta.mergeRowMeta(data.oneMeta.clone());
data.outputRowMeta.mergeRowMeta(data.twoMeta.clone());
if (data.one != null) {
// Find the key indexes:
data.keyNrs1 = new int[meta.getKeyFields1().length];
for (int i = 0; i < data.keyNrs1.length; i++) {
data.keyNrs1[i] = data.oneMeta.indexOfValue(meta.getKeyFields1()[i]);
if (data.keyNrs1[i] < 0) {
String message = BaseMessages.getString(PKG, "MergeJoin.Exception.UnableToFindFieldInReferenceStream", meta.getKeyFields1()[i]);
logError(message);
throw new KettleStepException(message);
}
}
}
if (data.two != null) {
// Find the key indexes:
data.keyNrs2 = new int[meta.getKeyFields2().length];
for (int i = 0; i < data.keyNrs2.length; i++) {
data.keyNrs2[i] = data.twoMeta.indexOfValue(meta.getKeyFields2()[i]);
if (data.keyNrs2[i] < 0) {
String message = BaseMessages.getString(PKG, "MergeJoin.Exception.UnableToFindFieldInReferenceStream", meta.getKeyFields2()[i]);
logError(message);
throw new KettleStepException(message);
}
}
}
// Calculate one_dummy... defaults to null
data.one_dummy = RowDataUtil.allocateRowData(data.oneMeta.size() + data.twoMeta.size());
// Calculate two_dummy... defaults to null
//
data.two_dummy = new Object[data.twoMeta.size()];
}
if (log.isRowLevel()) {
logRowlevel(BaseMessages.getString(PKG, "MergeJoin.Log.DataInfo", data.oneMeta.getString(data.one) + "") + data.twoMeta.getString(data.two));
}
/*
* We can stop processing if any of the following is true: a) Both streams are empty b) First stream is empty and
* join type is INNER or LEFT OUTER c) Second stream is empty and join type is INNER or RIGHT OUTER
*/
if ((data.one == null && data.two == null) || (data.one == null && data.one_optional == false) || (data.two == null && data.two_optional == false)) {
//
while (data.one != null && !isStopped()) {
data.one = getRowFrom(data.oneRowSet);
}
while (data.two != null && !isStopped()) {
data.two = getRowFrom(data.twoRowSet);
}
setOutputDone();
return false;
}
if (data.one == null) {
compare = -1;
} else {
if (data.two == null) {
compare = 1;
} else {
int cmp = data.oneMeta.compare(data.one, data.twoMeta, data.two, data.keyNrs1, data.keyNrs2);
compare = cmp > 0 ? 1 : cmp < 0 ? -1 : 0;
}
}
switch(compare) {
case 0:
/*
* We've got a match. This is what we do next (to handle duplicate keys correctly): Read the next record from
* both streams If any of the keys match, this means we have duplicates. We therefore Create an array of all
* rows that have the same keys Push a Cartesian product of the two arrays to output Else Just push the combined
* rowset to output
*/
data.one_next = getRowFrom(data.oneRowSet);
data.two_next = getRowFrom(data.twoRowSet);
int compare1 = (data.one_next == null) ? -1 : data.oneMeta.compare(data.one, data.one_next, data.keyNrs1, data.keyNrs1);
int compare2 = (data.two_next == null) ? -1 : data.twoMeta.compare(data.two, data.two_next, data.keyNrs2, data.keyNrs2);
if (compare1 == 0 || compare2 == 0) {
if (data.ones == null) {
data.ones = new ArrayList<Object[]>();
} else {
data.ones.clear();
}
if (data.twos == null) {
data.twos = new ArrayList<Object[]>();
} else {
data.twos.clear();
}
data.ones.add(data.one);
if (compare1 == 0) {
// First stream has duplicates
data.ones.add(data.one_next);
for (; !isStopped(); ) {
data.one_next = getRowFrom(data.oneRowSet);
if (0 != ((data.one_next == null) ? -1 : data.oneMeta.compare(data.one, data.one_next, data.keyNrs1, data.keyNrs1))) {
break;
}
data.ones.add(data.one_next);
}
if (isStopped()) {
return false;
}
}
data.twos.add(data.two);
if (compare2 == 0) {
// Second stream has duplicates
data.twos.add(data.two_next);
for (; !isStopped(); ) {
data.two_next = getRowFrom(data.twoRowSet);
if (0 != ((data.two_next == null) ? -1 : data.twoMeta.compare(data.two, data.two_next, data.keyNrs2, data.keyNrs2))) {
break;
}
data.twos.add(data.two_next);
}
if (isStopped()) {
return false;
}
}
for (Iterator<Object[]> oneIter = data.ones.iterator(); oneIter.hasNext() && !isStopped(); ) {
Object[] one = oneIter.next();
for (Iterator<Object[]> twoIter = data.twos.iterator(); twoIter.hasNext() && !isStopped(); ) {
Object[] two = twoIter.next();
Object[] oneBig = RowDataUtil.createResizedCopy(one, data.oneMeta.size() + data.twoMeta.size());
Object[] combi = RowDataUtil.addRowData(oneBig, data.oneMeta.size(), two);
putRow(data.outputRowMeta, combi);
}
// Remove the rows as we merge them to keep the overall memory footprint minimal
oneIter.remove();
}
data.twos.clear();
} else {
// No duplicates
Object[] outputRowData = RowDataUtil.addRowData(data.one, data.oneMeta.size(), data.two);
putRow(data.outputRowMeta, outputRowData);
}
data.one = data.one_next;
data.two = data.two_next;
break;
case 1:
/*
* First stream is greater than the second stream. This means: a) This key is missing in the first stream b)
* Second stream may have finished So, if full/right outer join is set and 2nd stream is not null, we push a
* record to output with only the values for the second row populated. Next, if 2nd stream is not finished, we
* get a row from it; otherwise signal that we are done
*/
if (data.one_optional == true) {
if (data.two != null) {
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one_dummy, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two);
putRow(data.outputRowMeta, outputRowData);
data.two = getRowFrom(data.twoRowSet);
} else if (data.two_optional == false) {
//
while (data.one != null && !isStopped()) {
data.one = getRowFrom(data.oneRowSet);
}
while (data.two != null && !isStopped()) {
data.two = getRowFrom(data.twoRowSet);
}
setOutputDone();
return false;
} else {
/*
* We are doing full outer join so print the 1st stream and get the next row from 1st stream
*/
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two_dummy);
putRow(data.outputRowMeta, outputRowData);
data.one = getRowFrom(data.oneRowSet);
}
} else if (data.two == null && data.two_optional == true) {
/**
* We have reached the end of stream 2 and there are records present in the first stream. Also, join is left
* or full outer. So, create a row with just the values in the first stream and push it forward
*/
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two_dummy);
putRow(data.outputRowMeta, outputRowData);
data.one = getRowFrom(data.oneRowSet);
} else if (data.two != null) {
/*
* We are doing an inner or left outer join, so throw this row away from the 2nd stream
*/
data.two = getRowFrom(data.twoRowSet);
}
break;
case -1:
/*
* Second stream is greater than the first stream. This means: a) This key is missing in the second stream b)
* First stream may have finished So, if full/left outer join is set and 1st stream is not null, we push a
* record to output with only the values for the first row populated. Next, if 1st stream is not finished, we
* get a row from it; otherwise signal that we are done
*/
if (data.two_optional == true) {
if (data.one != null) {
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two_dummy);
putRow(data.outputRowMeta, outputRowData);
data.one = getRowFrom(data.oneRowSet);
} else if (data.one_optional == false) {
//
while (data.one != null && !isStopped()) {
data.one = getRowFrom(data.oneRowSet);
}
while (data.two != null && !isStopped()) {
data.two = getRowFrom(data.twoRowSet);
}
setOutputDone();
return false;
} else {
/*
* We are doing a full outer join so print the 2nd stream and get the next row from the 2nd stream
*/
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one_dummy, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two);
putRow(data.outputRowMeta, outputRowData);
data.two = getRowFrom(data.twoRowSet);
}
} else if (data.one == null && data.one_optional == true) {
/*
* We have reached the end of stream 1 and there are records present in the second stream. Also, join is right
* or full outer. So, create a row with just the values in the 2nd stream and push it forward
*/
Object[] outputRowData = RowDataUtil.createResizedCopy(data.one_dummy, data.outputRowMeta.size());
outputRowData = RowDataUtil.addRowData(outputRowData, data.oneMeta.size(), data.two);
putRow(data.outputRowMeta, outputRowData);
data.two = getRowFrom(data.twoRowSet);
} else if (data.one != null) {
/*
* We are doing an inner or right outer join so a non-matching row in the first stream is of no use to us -
* throw it away and get the next row
*/
data.one = getRowFrom(data.oneRowSet);
}
break;
default:
logDebug("We shouldn't be here!!");
// Make sure we do not go into an infinite loop by continuing to read data
data.one = getRowFrom(data.oneRowSet);
data.two = getRowFrom(data.twoRowSet);
break;
}
if (checkFeedback(getLinesRead())) {
logBasic(BaseMessages.getString(PKG, "MergeJoin.LineNumber") + getLinesRead());
}
return true;
}
use of org.pentaho.di.trans.step.errorhandling.StreamInterface in project pentaho-kettle by pentaho.
the class MergeJoinMeta method readData.
private void readData(Node stepnode) throws KettleXMLException {
try {
Node keysNode1 = XMLHandler.getSubNode(stepnode, "keys_1");
Node keysNode2 = XMLHandler.getSubNode(stepnode, "keys_2");
int nrKeys1 = XMLHandler.countNodes(keysNode1, "key");
int nrKeys2 = XMLHandler.countNodes(keysNode2, "key");
allocate(nrKeys1, nrKeys2);
for (int i = 0; i < nrKeys1; i++) {
Node keynode = XMLHandler.getSubNodeByNr(keysNode1, "key", i);
keyFields1[i] = XMLHandler.getNodeValue(keynode);
}
for (int i = 0; i < nrKeys2; i++) {
Node keynode = XMLHandler.getSubNodeByNr(keysNode2, "key", i);
keyFields2[i] = XMLHandler.getNodeValue(keynode);
}
List<StreamInterface> infoStreams = getStepIOMeta().getInfoStreams();
infoStreams.get(0).setSubject(XMLHandler.getTagValue(stepnode, "step1"));
infoStreams.get(1).setSubject(XMLHandler.getTagValue(stepnode, "step2"));
joinType = XMLHandler.getTagValue(stepnode, "join_type");
} catch (Exception e) {
throw new KettleXMLException(BaseMessages.getString(PKG, "MergeJoinMeta.Exception.UnableToLoadStepInfo"), e);
}
}
use of org.pentaho.di.trans.step.errorhandling.StreamInterface in project pentaho-kettle by pentaho.
the class MergeJoinMeta method clone.
public Object clone() {
MergeJoinMeta retval = (MergeJoinMeta) super.clone();
int nrKeys1 = keyFields1.length;
int nrKeys2 = keyFields2.length;
retval.allocate(nrKeys1, nrKeys2);
System.arraycopy(keyFields1, 0, retval.keyFields1, 0, nrKeys1);
System.arraycopy(keyFields2, 0, retval.keyFields2, 0, nrKeys2);
StepIOMetaInterface stepIOMeta = new StepIOMeta(true, true, false, false, false, false);
List<StreamInterface> infoStreams = getStepIOMeta().getInfoStreams();
for (StreamInterface infoStream : infoStreams) {
stepIOMeta.addStream(new Stream(infoStream));
}
retval.ioMeta = stepIOMeta;
return retval;
}
Aggregations