/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package com.espertech.esper.epl.join.assemble; import com.espertech.esper.client.EventBean; import java.util.List; import java.util.Arrays; /** * Helper class to compute the cartesian product of the events from two streams. */ public class CartesianUtil { /** * Form the 2-ary cartesian product between zero or more events from 2 streams. * @param streamOne is the events from stream one * @param subStreamNumsOne is the list of substream numbers to stream one to include in the product * @param streamTwo is the events from stream two * @param subStreamNumsTwo is the list of substream numbers to stream two to include in the product * @param resultList is where the result of the cartesian product is added to */ protected static void computeCartesian(List<EventBean[]> streamOne, int[] subStreamNumsOne, List<EventBean[]> streamTwo, int[] subStreamNumsTwo, List<EventBean[]> resultList) { if ((streamTwo == null) || (streamTwo.isEmpty())) { if ((streamOne == null) || (streamOne.isEmpty())) { return; } resultList.addAll(streamOne); return; } if ((streamOne == null) || (streamOne.isEmpty())) { resultList.addAll(streamTwo); return; } int streamOneSize = streamOne.size(); int streamTwoSize = streamTwo.size(); if (streamOneSize == 1) { // Yes we are re-using the results of stream two, same row reference copyToEach(subStreamNumsOne, streamOne.get(0), streamTwo); resultList.addAll(streamTwo); return; } if (streamTwoSize == 1) { // Yes we are re-using the results of stream one, same row reference copyToEach(subStreamNumsTwo, streamTwo.get(0), streamOne); resultList.addAll(streamOne); return; } // we have more then 1 rows each child stream // Exchange streams if one is smaller then two // Since if one has 100 rows the other has 2 then we can re-use the 100 event rows. if (streamTwoSize > streamOneSize) { List<EventBean[]> holdRows = streamOne; int holdSize = streamOneSize; streamOne = streamTwo; streamOneSize = streamTwoSize; streamTwo = holdRows; streamTwoSize = holdSize; subStreamNumsTwo = subStreamNumsOne; } // allocate resultList of join int cartesianTotalRows = streamOneSize * streamTwoSize; int numColumns = streamOne.get(0).length; EventBean[][] results = new EventBean[cartesianTotalRows][]; // Allocate and pre-populate copies of stream 1 int streamOneCount = 0; for (EventBean[] row : streamOne) { // first use all events in stream 1 results[streamOneCount] = row; // then allocate copies for each in stream 2 for (int i = 1; i < streamTwoSize; i++) { EventBean[] dupRow = new EventBean[numColumns]; System.arraycopy(row, 0, dupRow, 0, numColumns); int index = streamOneSize * i + streamOneCount; results[index] = dupRow; } streamOneCount++; } // Copy stream 2 rows into rows of stream 1 int streamTwoCount = 0; for (EventBean[] row : streamTwo) { for (int i = 0; i < streamOneSize; i++) { int index = streamTwoCount * streamOneSize + i; copy(subStreamNumsTwo, row, results[index]); } streamTwoCount++; } // Add results resultList.addAll(Arrays.asList(results)); } private static void copyToEach(int[] subStreamNums, EventBean[] sourceRow, List<EventBean[]> destRows) { for (EventBean[] destRow : destRows) { copy(subStreamNums, sourceRow, destRow); } } private static void copy(int[] subStreamsFrom, EventBean[] from, EventBean[] to) { for (int index : subStreamsFrom) { to[index] = from[index]; } } }