| using System.Collections; | 
 | using System.Collections.Generic; | 
 | using System.Linq; | 
 | using OfficeOpenXml.FormulaParsing.ExcelUtilities; | 
 | using OfficeOpenXml.FormulaParsing.LexicalAnalysis; | 
 | using OfficeOpenXml.Style.XmlAccess; | 
 | using OfficeOpenXml.Utils; | 
 |  | 
 | namespace OfficeOpenXml.FormulaParsing; | 
 |  | 
 | public class EpplusExcelDataProvider : ExcelDataProvider { | 
 |   public class RangeInfo : IRangeInfo { | 
 |     internal ExcelWorksheet _ws; | 
 |     private readonly CellsStoreEnumerator<object> _values; | 
 |     private readonly int _fromRow; | 
 |     private readonly int _toRow; | 
 |     private readonly int _fromCol; | 
 |     private readonly int _toCol; | 
 |     private int _cellCount; | 
 |     private readonly ExcelAddressBase _address; | 
 |     private readonly ICellInfo _cell; | 
 |  | 
 |     public RangeInfo(ExcelWorksheet ws, int fromRow, int fromCol, int toRow, int toCol) { | 
 |       _ws = ws; | 
 |       _fromRow = fromRow; | 
 |       _fromCol = fromCol; | 
 |       _toRow = toRow; | 
 |       _toCol = toCol; | 
 |       _address = new(_fromRow, _fromCol, _toRow, _toCol); | 
 |       _address._ws = ws.Name; | 
 |       _values = new(ws._values, _fromRow, _fromCol, _toRow, _toCol); | 
 |       _cell = new CellInfo(_ws, _values); | 
 |     } | 
 |  | 
 |     public int GetNCells() { | 
 |       return ((_toRow - _fromRow) + 1) * ((_toCol - _fromCol) + 1); | 
 |     } | 
 |  | 
 |     public bool IsEmpty { | 
 |       get { | 
 |         if (_cellCount > 0) { | 
 |           return false; | 
 |         } | 
 |         if (_values.Next()) { | 
 |           _values.Reset(); | 
 |           return false; | 
 |         } | 
 |         return true; | 
 |       } | 
 |     } | 
 |  | 
 |     public bool IsMulti { | 
 |       get { | 
 |         if (_cellCount == 0) { | 
 |           if (_values.Next() && _values.Next()) { | 
 |             _values.Reset(); | 
 |             return true; | 
 |           } | 
 |           _values.Reset(); | 
 |           return false; | 
 |         } | 
 |         if (_cellCount > 1) { | 
 |           return true; | 
 |         } | 
 |         return false; | 
 |       } | 
 |     } | 
 |  | 
 |     public ICellInfo Current => _cell; | 
 |  | 
 |     public ExcelWorksheet Worksheet => _ws; | 
 |  | 
 |     public void Dispose() {} | 
 |  | 
 |     object IEnumerator.Current => this; | 
 |  | 
 |     public bool MoveNext() { | 
 |       _cellCount++; | 
 |       return _values.MoveNext(); | 
 |     } | 
 |  | 
 |     public void Reset() { | 
 |       _values.Init(); | 
 |     } | 
 |  | 
 |     public bool NextCell() { | 
 |       _cellCount++; | 
 |       return _values.MoveNext(); | 
 |     } | 
 |  | 
 |     public IEnumerator<ICellInfo> GetEnumerator() { | 
 |       Reset(); | 
 |       return this; | 
 |     } | 
 |  | 
 |     IEnumerator IEnumerable.GetEnumerator() { | 
 |       return this; | 
 |     } | 
 |  | 
 |     public ExcelAddressBase Address => _address; | 
 |  | 
 |     public object GetValue(int row, int col) { | 
 |       return _ws.GetValue(row, col); | 
 |     } | 
 |  | 
 |     public object GetOffset(int rowOffset, int colOffset) { | 
 |       if (_values.Row < _fromRow || _values.Column < _fromCol) { | 
 |         return _ws.GetValue(_fromRow + rowOffset, _fromCol + colOffset); | 
 |       } | 
 |       return _ws.GetValue(_values.Row + rowOffset, _values.Column + colOffset); | 
 |     } | 
 |   } | 
 |  | 
 |   public class CellInfo : ICellInfo { | 
 |     private readonly ExcelWorksheet _ws; | 
 |     private readonly CellsStoreEnumerator<object> _values; | 
 |  | 
 |     internal CellInfo(ExcelWorksheet ws, CellsStoreEnumerator<object> values) { | 
 |       _ws = ws; | 
 |       _values = values; | 
 |     } | 
 |  | 
 |     public string Address => _values.CellAddress; | 
 |  | 
 |     public int Row => _values.Row; | 
 |  | 
 |     public int Column => _values.Column; | 
 |  | 
 |     public string Formula => _ws.GetFormula(_values.Row, _values.Column); | 
 |  | 
 |     public object Value => _values.Value; | 
 |  | 
 |     public double ValueDouble => ConvertUtil.GetValueDouble(_values.Value, true); | 
 |  | 
 |     public double ValueDoubleLogical => ConvertUtil.GetValueDouble(_values.Value); | 
 |  | 
 |     public bool IsHiddenRow { | 
 |       get { | 
 |         var row = _ws._values.GetValue(_values.Row, 0) as RowInternal; | 
 |         if (row != null) { | 
 |           return row.Hidden || row.Height == 0; | 
 |         } | 
 |         return false; | 
 |       } | 
 |     } | 
 |  | 
 |     public bool IsExcelError => ExcelErrorValue.Values.IsErrorValue(_values.Value); | 
 |  | 
 |     public IList<Token> Tokens => _ws._formulaTokens.GetValue(_values.Row, _values.Column); | 
 |   } | 
 |  | 
 |   public class NameInfo : INameInfo { | 
 |     public ulong Id { get; set; } | 
 |  | 
 |     public string Worksheet { get; set; } | 
 |  | 
 |     public string Name { get; set; } | 
 |  | 
 |     public string Formula { get; set; } | 
 |  | 
 |     public IList<Token> Tokens { get; internal set; } | 
 |  | 
 |     public object Value { get; set; } | 
 |   } | 
 |  | 
 |   private readonly ExcelWorkbook _workbook; | 
 |   private ExcelWorksheet _currentWorksheet; | 
 |   private Dictionary<ulong, INameInfo> _names = new(); | 
 |  | 
 |   public EpplusExcelDataProvider(ExcelWorkbook workbook) { | 
 |     _workbook = workbook; | 
 |   } | 
 |  | 
 |   public override ExcelNamedRangeCollection GetWorksheetNames(string worksheet) { | 
 |     var ws = _workbook.Worksheets[worksheet]; | 
 |     if (ws != null) { | 
 |       return ws.Names; | 
 |     } | 
 |     return null; | 
 |   } | 
 |  | 
 |   public override ExcelNamedRangeCollection GetWorkbookNameValues() { | 
 |     return _workbook.Names; | 
 |   } | 
 |  | 
 |   public override IRangeInfo GetRange( | 
 |       string worksheet, | 
 |       int fromRow, | 
 |       int fromCol, | 
 |       int toRow, | 
 |       int toCol) { | 
 |     SetCurrentWorksheet(worksheet); | 
 |     var wsName = string.IsNullOrEmpty(worksheet) ? _currentWorksheet.Name : worksheet; | 
 |     var ws = _workbook.Worksheets[wsName]; | 
 |     return new RangeInfo(ws, fromRow, fromCol, toRow, toCol); | 
 |   } | 
 |  | 
 |   public override IRangeInfo GetRange(string worksheet, int row, int column, string address) { | 
 |     var addr = new ExcelAddress(worksheet, address); | 
 |     if (addr.Table != null) { | 
 |       addr.SetRcFromTable(_workbook, new(row, column, row, column)); | 
 |     } | 
 |     //SetCurrentWorksheet(addr.WorkSheet); | 
 |     var wsName = string.IsNullOrEmpty(addr.WorkSheet) ? _currentWorksheet.Name : addr.WorkSheet; | 
 |     var ws = _workbook.Worksheets[wsName]; | 
 |     //return new CellsStoreEnumerator<object>(ws._values, addr._fromRow, addr._fromCol, addr._toRow, addr._toCol); | 
 |     return new RangeInfo(ws, addr._fromRow, addr._fromCol, addr._toRow, addr._toCol); | 
 |   } | 
 |  | 
 |   public override INameInfo GetName(string worksheet, string name) { | 
 |     ExcelNamedRange nameItem; | 
 |     ulong id; | 
 |     ExcelWorksheet ws; | 
 |     if (string.IsNullOrEmpty(worksheet)) { | 
 |       if (_workbook.Names.ContainsKey(name)) { | 
 |         nameItem = _workbook.Names[name]; | 
 |       } else { | 
 |         return null; | 
 |       } | 
 |       ws = null; | 
 |     } else { | 
 |       ws = _workbook.Worksheets[worksheet]; | 
 |       if (ws != null && ws.Names.ContainsKey(name)) { | 
 |         nameItem = ws.Names[name]; | 
 |       } else if (_workbook.Names.ContainsKey(name)) { | 
 |         nameItem = _workbook.Names[name]; | 
 |       } else { | 
 |         return null; | 
 |       } | 
 |     } | 
 |     id = ExcelCellBase.GetCellId(nameItem.LocalSheetId, nameItem.Index, 0); | 
 |  | 
 |     if (_names.ContainsKey(id)) { | 
 |       return _names[id]; | 
 |     } | 
 |     var ni = new NameInfo { | 
 |       Id = id, | 
 |       Name = name, | 
 |       Worksheet = nameItem.Worksheet == null ? nameItem._ws : nameItem.Worksheet.Name, | 
 |       Formula = nameItem.Formula, | 
 |     }; | 
 |     if (nameItem._fromRow > 0) { | 
 |       ni.Value = new RangeInfo( | 
 |           nameItem.Worksheet ?? ws, | 
 |           nameItem._fromRow, | 
 |           nameItem._fromCol, | 
 |           nameItem._toRow, | 
 |           nameItem._toCol); | 
 |     } else { | 
 |       ni.Value = nameItem.Value; | 
 |     } | 
 |     _names.Add(id, ni); | 
 |     return ni; | 
 |   } | 
 |  | 
 |   public override IEnumerable<object> GetRangeValues(string address) { | 
 |     SetCurrentWorksheet(ExcelAddressInfo.Parse(address)); | 
 |     var addr = new ExcelAddress(address); | 
 |     var wsName = string.IsNullOrEmpty(addr.WorkSheet) ? _currentWorksheet.Name : addr.WorkSheet; | 
 |     var ws = _workbook.Worksheets[wsName]; | 
 |     return (new CellsStoreEnumerator<object>( | 
 |             ws._values, | 
 |             addr._fromRow, | 
 |             addr._fromCol, | 
 |             addr._toRow, | 
 |             addr._toCol)); | 
 |   } | 
 |  | 
 |   public object GetValue(int row, int column) { | 
 |     return _currentWorksheet._values.GetValue(row, column); | 
 |   } | 
 |  | 
 |   public bool IsMerged(int row, int column) { | 
 |     //return _currentWorksheet._flags.GetFlagValue(row, column, CellFlags.Merged); | 
 |     return _currentWorksheet.MergedCells[row, column] != null; | 
 |   } | 
 |  | 
 |   public bool IsHidden(int row, int column) { | 
 |     return _currentWorksheet.Column(column).Hidden | 
 |         || _currentWorksheet.Column(column).Width == 0 | 
 |         || _currentWorksheet.Row(row).Hidden | 
 |         || _currentWorksheet.Row(column).Height == 0; | 
 |   } | 
 |  | 
 |   public override object GetCellValue(string sheetName, int row, int col) { | 
 |     SetCurrentWorksheet(sheetName); | 
 |     return _currentWorksheet._values.GetValue(row, col); | 
 |   } | 
 |  | 
 |   public override ExcelCellAddress GetDimensionEnd(string worksheet) { | 
 |     ExcelCellAddress address = null; | 
 |     try { | 
 |       address = _workbook.Worksheets[worksheet].Dimension.End; | 
 |     } catch {} | 
 |  | 
 |     return address; | 
 |   } | 
 |  | 
 |   private void SetCurrentWorksheet(ExcelAddressInfo addressInfo) { | 
 |     if (addressInfo.WorksheetIsSpecified) { | 
 |       _currentWorksheet = _workbook.Worksheets[addressInfo.Worksheet]; | 
 |     } else if (_currentWorksheet == null) { | 
 |       _currentWorksheet = _workbook.Worksheets.First(); | 
 |     } | 
 |   } | 
 |  | 
 |   private void SetCurrentWorksheet(string worksheetName) { | 
 |     if (!string.IsNullOrEmpty(worksheetName)) { | 
 |       _currentWorksheet = _workbook.Worksheets[worksheetName]; | 
 |     } else { | 
 |       _currentWorksheet = _workbook.Worksheets.First(); | 
 |     } | 
 |   } | 
 |  | 
 |   public override int ExcelMaxColumns => ExcelPackage.MaxColumns; | 
 |  | 
 |   public override int ExcelMaxRows => ExcelPackage.MaxRows; | 
 |  | 
 |   public override string GetRangeFormula(string worksheetName, int row, int column) { | 
 |     SetCurrentWorksheet(worksheetName); | 
 |     return _currentWorksheet.GetFormula(row, column); | 
 |   } | 
 |  | 
 |   public override object GetRangeValue(string worksheetName, int row, int column) { | 
 |     SetCurrentWorksheet(worksheetName); | 
 |     return _currentWorksheet.GetValue(row, column); | 
 |   } | 
 |  | 
 |   public override string GetFormat(object value, string format) { | 
 |     var styles = _workbook.Styles; | 
 |     ExcelNumberFormatXml.ExcelFormatTranslator ft = null; | 
 |     foreach (var f in styles.NumberFormats) { | 
 |       if (f.Format == format) { | 
 |         ft = f.FormatTranslator; | 
 |         break; | 
 |       } | 
 |     } | 
 |     if (ft == null) { | 
 |       ft = new(format, -1); | 
 |     } | 
 |     return ExcelRangeBase.FormatValue(value, ft, format, ft.NetFormat); | 
 |   } | 
 |  | 
 |   public override List<Token> GetRangeFormulaTokens(string worksheetName, int row, int column) { | 
 |     return _workbook.Worksheets[worksheetName]._formulaTokens.GetValue(row, column); | 
 |   } | 
 |  | 
 |   public override bool IsRowHidden(string worksheetName, int row) { | 
 |     var b = | 
 |         _workbook.Worksheets[worksheetName].Row(row).Height == 0 | 
 |             || _workbook.Worksheets[worksheetName].Row(row).Hidden; | 
 |  | 
 |     return b; | 
 |   } | 
 |  | 
 |   public override void Reset() { | 
 |     _names = new(); //Reset name cache. | 
 |   } | 
 | } |