|
|
|
@ -197,8 +197,10 @@ var (
|
|
|
|
|
// calcContext defines the formula execution context.
|
|
|
|
|
type calcContext struct {
|
|
|
|
|
sync.Mutex
|
|
|
|
|
entry string
|
|
|
|
|
iterations map[string]uint
|
|
|
|
|
entry string
|
|
|
|
|
maxCalcIterations uint
|
|
|
|
|
iterations map[string]uint
|
|
|
|
|
iterationsCache map[string]formulaArg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cellRef defines the structure of a cell reference.
|
|
|
|
@ -774,8 +776,10 @@ func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string
|
|
|
|
|
token formulaArg
|
|
|
|
|
)
|
|
|
|
|
if token, err = f.calcCellValue(&calcContext{
|
|
|
|
|
entry: fmt.Sprintf("%s!%s", sheet, cell),
|
|
|
|
|
iterations: make(map[string]uint),
|
|
|
|
|
entry: fmt.Sprintf("%s!%s", sheet, cell),
|
|
|
|
|
maxCalcIterations: getOptions(opts...).MaxCalcIterations,
|
|
|
|
|
iterations: make(map[string]uint),
|
|
|
|
|
iterationsCache: make(map[string]formulaArg),
|
|
|
|
|
}, sheet, cell); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1527,17 +1531,6 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
|
|
|
|
|
value string
|
|
|
|
|
err error
|
|
|
|
|
)
|
|
|
|
|
ref := fmt.Sprintf("%s!%s", sheet, cell)
|
|
|
|
|
if formula, _ := f.GetCellFormula(sheet, cell); len(formula) != 0 {
|
|
|
|
|
ctx.Lock()
|
|
|
|
|
if ctx.entry != ref && ctx.iterations[ref] <= f.options.MaxCalcIterations {
|
|
|
|
|
ctx.iterations[ref]++
|
|
|
|
|
ctx.Unlock()
|
|
|
|
|
arg, _ = f.calcCellValue(ctx, sheet, cell)
|
|
|
|
|
return arg, nil
|
|
|
|
|
}
|
|
|
|
|
ctx.Unlock()
|
|
|
|
|
}
|
|
|
|
|
if value, err = f.GetCellValue(sheet, cell, Options{RawCellValue: true}); err != nil {
|
|
|
|
|
return arg, err
|
|
|
|
|
}
|
|
|
|
@ -1551,8 +1544,25 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
|
|
|
|
|
return newEmptyFormulaArg(), err
|
|
|
|
|
}
|
|
|
|
|
return arg.ToNumber(), err
|
|
|
|
|
default:
|
|
|
|
|
case CellTypeInlineString, CellTypeSharedString:
|
|
|
|
|
return arg, err
|
|
|
|
|
case CellTypeFormula:
|
|
|
|
|
ref := fmt.Sprintf("%s!%s", sheet, cell)
|
|
|
|
|
if ctx.entry != ref {
|
|
|
|
|
ctx.Lock()
|
|
|
|
|
if ctx.iterations[ref] <= ctx.maxCalcIterations {
|
|
|
|
|
ctx.iterations[ref]++
|
|
|
|
|
ctx.Unlock()
|
|
|
|
|
arg, _ = f.calcCellValue(ctx, sheet, cell)
|
|
|
|
|
ctx.iterationsCache[ref] = arg
|
|
|
|
|
return arg, nil
|
|
|
|
|
}
|
|
|
|
|
ctx.Unlock()
|
|
|
|
|
return ctx.iterationsCache[ref], nil
|
|
|
|
|
}
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
|
|
|
|
return newEmptyFormulaArg(), err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -7746,7 +7756,7 @@ func (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg {
|
|
|
|
|
}
|
|
|
|
|
var count float64
|
|
|
|
|
for _, cell := range argsList.Front().Value.(formulaArg).ToList() {
|
|
|
|
|
if cell.Value() == "" {
|
|
|
|
|
if cell.Type == ArgEmpty {
|
|
|
|
|
count++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|