Package org.apache.poi.hssf.record.formula.functions

Source Code of org.apache.poi.hssf.record.formula.functions.Sumproduct

/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You 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.apache.poi.hssf.record.formula.functions;

import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;


/**
* Implementation for the Excel function SUMPRODUCT<p>
*
* Syntax : <br/>
*  SUMPRODUCT ( array1[, array2[, array3[, ...]]])
*    <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
*      <tr><th>array1, ... arrayN&nbsp;&nbsp;</th><td>typically area references,
*      possibly cell references or scalar values</td></tr>
*    </table><br/>
*
* Let A<b>n</b><sub>(<b>i</b>,<b>j</b>)</sub> represent the element in the <b>i</b>th row <b>j</b>th column
* of the <b>n</b>th array<br/>
* Assuming each array has the same dimensions (W, H), the result is defined as:<br/>
* SUMPRODUCT = &Sigma;<sub><b>i</b>: 1..H</sub> &nbsp;
*   (&nbsp; &Sigma;<sub><b>j</b>: 1..W</sub> &nbsp;
*     (&nbsp; &Pi;<sub><b>n</b>: 1..N</sub>
*       A<b>n</b><sub>(<b>i</b>,<b>j</b>)</sub>&nbsp;
*    )&nbsp;
*  )
* </p>
* @author Josh Micich
*/
public final class Sumproduct implements Function {


  public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {

    int maxN = args.length;

    if(maxN < 1) {
      return ErrorEval.VALUE_INVALID;
    }
    Eval firstArg = args[0];
    try {
      if(firstArg instanceof NumericValueEval) {
        return evaluateSingleProduct(args);
      }
      if(firstArg instanceof RefEval) {
        return evaluateSingleProduct(args);
      }
      if(firstArg instanceof AreaEval) {
        AreaEval ae = (AreaEval) firstArg;
        if(ae.isRow() && ae.isColumn()) {
          return evaluateSingleProduct(args);
        }
        return evaluateAreaSumProduct(args);
      }
    } catch (EvaluationException e) {
      return e.getErrorEval();
    }
    throw new RuntimeException("Invalid arg type for SUMPRODUCT: ("
        + firstArg.getClass().getName() + ")");
  }

  private static Eval evaluateSingleProduct(Eval[] evalArgs) throws EvaluationException {
    int maxN = evalArgs.length;

    double term = 1D;
    for(int n=0; n<maxN; n++) {
      double val = getScalarValue(evalArgs[n]);
      term *= val;
    }
    return new NumberEval(term);
  }

  private static double getScalarValue(Eval arg) throws EvaluationException {

    Eval eval;
    if (arg instanceof RefEval) {
      RefEval re = (RefEval) arg;
      eval = re.getInnerValueEval();
    } else {
      eval = arg;
    }

    if (eval == null) {
      throw new RuntimeException("parameter may not be null");
    }
    if (eval instanceof AreaEval) {
      AreaEval ae = (AreaEval) eval;
      // an area ref can work as a scalar value if it is 1x1
      if(!ae.isColumn() || !ae.isRow()) {
        throw new EvaluationException(ErrorEval.VALUE_INVALID);
      }
      eval = ae.getRelativeValue(0, 0);
    }

    if (!(eval instanceof ValueEval)) {
      throw new RuntimeException("Unexpected value eval class ("
          + eval.getClass().getName() + ")");
    }

    return getProductTerm((ValueEval) eval, true);
  }

  private static Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvaluationException {
    int maxN = evalArgs.length;
    AreaEval[] args = new AreaEval[maxN];
    try {
      System.arraycopy(evalArgs, 0, args, 0, maxN);
    } catch (ArrayStoreException e) {
      // one of the other args was not an AreaRef
      return ErrorEval.VALUE_INVALID;
    }


    AreaEval firstArg = args[0];

    int height = firstArg.getHeight();
    int width = firstArg.getWidth(); // TODO - junit

    // first check dimensions
    if (!areasAllSameSize(args, height, width)) {
      // normally this results in #VALUE!,
      // but errors in individual cells take precedence
      for (int i = 1; i < args.length; i++) {
        throwFirstError(args[i]);
      }
      return ErrorEval.VALUE_INVALID;
    }

    double acc = 0;

    for (int rrIx=0; rrIx<height; rrIx++) {
      for (int rcIx=0; rcIx<width; rcIx++) {
        double term = 1D;
        for(int n=0; n<maxN; n++) {
          double val = getProductTerm(args[n].getRelativeValue(rrIx, rcIx), false);
          term *= val;
        }
        acc += term;
      }
    }

    return new NumberEval(acc);
  }

  private static void throwFirstError(AreaEval areaEval) throws EvaluationException {
    int height = areaEval.getHeight();
    int width = areaEval.getWidth();
    for (int rrIx=0; rrIx<height; rrIx++) {
      for (int rcIx=0; rcIx<width; rcIx++) {
        ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
        if (ve instanceof ErrorEval) {
          throw new EvaluationException((ErrorEval) ve);
        }
      }
    }
  }

  private static boolean areasAllSameSize(AreaEval[] args, int height, int width) {
    for (int i = 0; i < args.length; i++) {
      AreaEval areaEval = args[i];
      // check that height and width match
      if(areaEval.getHeight() != height) {
        return false;
      }
      if(areaEval.getWidth() != width) {
        return false;
      }
    }
    return true;
  }


  /**
   * Determines a <code>double</code> value for the specified <code>ValueEval</code>.
   * @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs.
   * @throws EvaluationException if <code>ve</code> represents an error value.
   * <p/>
   * Note - string values and empty cells are interpreted differently depending on
   * <code>isScalarProduct</code>.  For scalar products, if any term is blank or a string, the
   * error (#VALUE!) is raised.  For area (sum)products, if any term is blank or a string, the
   * result is zero.
   */
  private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvaluationException {

    if(ve instanceof BlankEval || ve == null) {
      // TODO - shouldn't BlankEval.INSTANCE be used always instead of null?
      // null seems to occur when the blank cell is part of an area ref (but not reliably)
      if(isScalarProduct) {
        throw new EvaluationException(ErrorEval.VALUE_INVALID);
      }
      return 0;
    }

    if(ve instanceof ErrorEval) {
      throw new EvaluationException((ErrorEval)ve);
    }
    if(ve instanceof StringEval) {
      if(isScalarProduct) {
        throw new EvaluationException(ErrorEval.VALUE_INVALID);
      }
      // Note for area SUMPRODUCTs, string values are interpreted as zero
      // even if they would parse as valid numeric values
      return 0;
    }
    if(ve instanceof NumericValueEval) {
      NumericValueEval nve = (NumericValueEval) ve;
      return nve.getNumberValue();
    }
    throw new RuntimeException("Unexpected value eval class ("
        + ve.getClass().getName() + ")");
  }
}
TOP

Related Classes of org.apache.poi.hssf.record.formula.functions.Sumproduct

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.