private DataProviderMultiplier createDataProviderMultiplier(DataVariableMultiplication dataVariableMultiplication, int dataProviderOffset) {
      DataVariableMultiplicationFactor multiplier = dataVariableMultiplication.getMultiplier();
      DataVariableMultiplicationFactor multiplicand = dataVariableMultiplication.getMultiplicand();

      if (multiplier instanceof DataVariableMultiplication) {
        // recursively dive into the multiplication depth-first
        // if you combined a with b with c with d, the multiplication is represented as
        // (((a * b) * c) * d), where each character can represent multiple data providers
        // this path handles all multiplications except (a * b) which is handled in the else path
        // through this depth-first recursion ultimately (a * b) is handled first,
        // then (ab * c), then (abc * d).
        //
        // here we first build the data provider multiplier for the multiplier
        // then we collect the data provider infos and data providers for the multiplicand
        // and then create the data provider multiplier for them
        DataProviderMultiplier multiplierProvider = createDataProviderMultiplier((DataVariableMultiplication) multiplier, dataProviderOffset);
        List<DataProviderInfo> multiplicandProviderInfos = new ArrayList<>();
        List<Object> multiplicandProviders = new ArrayList<>();
        collectDataProviders(dataProviderOffset + multiplierProvider.getProcessedDataProviders(), multiplicand, multiplicandProviderInfos, multiplicandProviders);

        return new DataProviderMultiplier(supervisor, context,
          Arrays.asList(dataVariableMultiplication.getDataVariables()),
          multiplierProvider.multiplierProviderInfos.subList(0, 1),
          multiplicandProviderInfos, multiplierProvider,
          multiplicandProviders.toArray(new Object[0]));
      } else {
        // this path handles the innermost multiplication (a * b)
        //
        // it collects the data provider infos and data providers for a and b
        // and then creates a data provider multiplier for them
        List<DataProviderInfo> multiplierProviderInfos = new ArrayList<>();
        List<DataProviderInfo> multiplicandProviderInfos = new ArrayList<>();
        List<Object> multiplierProviders = new ArrayList<>();
        List<Object> multiplicandProviders = new ArrayList<>();
        collectDataProviders(dataProviderOffset, multiplier, multiplierProviderInfos, multiplierProviders);
        collectDataProviders(dataProviderOffset + multiplierProviderInfos.size(), multiplicand, multiplicandProviderInfos, multiplicandProviders);

        return new DataProviderMultiplier(supervisor, context,
          Arrays.asList(dataVariableMultiplication.getDataVariables()),
          multiplierProviderInfos, multiplicandProviderInfos,
          multiplierProviders.toArray(new Object[0]),
          multiplicandProviders.toArray(new Object[0]));
      }
    }
Creates a multiplier that is backed by data providers and on-the-fly multiplies them as they are processed.

@param dataVariableMultiplication the multiplication for which to create the multiplier
@param dataProviderOffset the index of the first data provider for the given multiplication
@return the data provider multiplier
spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java