blob: 51ad999bf517d043d1e4b1324badb95f8a740b60 [file] [log] [blame]
/*******************************************************************************
* You may amend and distribute as you like, but don't remove this header!
*
* EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
* See http://www.codeplex.com/EPPlus for details.
*
* Copyright (C) 2011 Jan Källman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* All code and executables are provided "as is" with no warranty either express or implied.
* The author accepts no liability for any damage or loss of business that this product may cause.
*
* Code change notes:
*
* Author Change Date
*******************************************************************************
* Jan Källman Added 18-MAR-2010
* Jan Källman License changed GPL-->LGPL 2011-12-16
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Globalization;
namespace OfficeOpenXml;
public class ExcelTableAddress {
public string Name { get; set; }
public string ColumnSpan { get; set; }
public bool IsAll { get; set; }
public bool IsHeader { get; set; }
public bool IsData { get; set; }
public bool IsTotals { get; set; }
public bool IsThisRow { get; set; }
}
/// <summary>
/// A range address
/// </summary>
/// <remarks>Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" </remarks>
public class ExcelAddressBase : ExcelCellBase {
protected internal int _fromRow = -1,
_toRow,
_fromCol,
_toCol;
protected internal bool _fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed;
protected internal string _wb;
protected internal string _ws;
protected internal string _address;
protected internal event EventHandler AddressChange;
internal enum eAddressCollition {
No,
Partly,
Inside,
Equal,
}
internal enum eShiftType {
Right,
Down,
EntireRow,
EntireColumn,
}
internal ExcelAddressBase() {}
/// <summary>
/// Creates an Address object
/// </summary>
/// <param name="fromRow">start row</param>
/// <param name="fromCol">start column</param>
/// <param name="toRow">End row</param>
/// <param name="toColumn">End column</param>
public ExcelAddressBase(int fromRow, int fromCol, int toRow, int toColumn) {
_fromRow = fromRow;
_toRow = toRow;
_fromCol = fromCol;
_toCol = toColumn;
Validate();
_address = GetAddress(_fromRow, _fromCol, _toRow, _toCol);
}
/// <summary>
/// Creates an Address object
/// </summary>
/// <param name="fromRow">start row</param>
/// <param name="fromCol">start column</param>
/// <param name="toRow">End row</param>
/// <param name="toColumn">End column</param>
/// <param name="fromRowFixed">start row fixed</param>
/// <param name="fromColFixed">start column fixed</param>
/// <param name="toRowFixed">End row fixed</param>
/// <param name="toColFixed">End column fixed</param>
public ExcelAddressBase(
int fromRow,
int fromCol,
int toRow,
int toColumn,
bool fromRowFixed,
bool fromColFixed,
bool toRowFixed,
bool toColFixed) {
_fromRow = fromRow;
_toRow = toRow;
_fromCol = fromCol;
_toCol = toColumn;
_fromRowFixed = fromRowFixed;
_fromColFixed = fromColFixed;
_toRowFixed = toRowFixed;
_toColFixed = toColFixed;
Validate();
_address = GetAddress(
_fromRow,
_fromCol,
_toRow,
_toCol,
_fromRowFixed,
fromColFixed,
_toRowFixed,
_toColFixed);
}
/// <summary>
/// Creates an Address object
/// </summary>
/// <remarks>Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" </remarks>
/// <param name="address">The Excel Address</param>
public ExcelAddressBase(string address) {
SetAddress(address);
}
/// <summary>
/// Creates an Address object
/// </summary>
/// <remarks>Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" </remarks>
/// <param name="address">The Excel Address</param>
/// <param name="pck">Reference to the package to find information about tables and names</param>
/// <param name="referenceAddress">The address</param>
protected ExcelAddressBase(
string address,
ExcelWorkbook workbook,
ExcelAddressBase referenceAddress) {
SetAddress(address);
SetRcFromTable(workbook, referenceAddress);
}
internal void SetRcFromTable(ExcelWorkbook workbook, ExcelAddressBase referenceAddress) {
if (string.IsNullOrEmpty(_wb) && Table != null) {
foreach (var ws in workbook.Worksheets) {
foreach (var t in ws.Tables) {
if (t.Name.Equals(Table.Name, StringComparison.InvariantCultureIgnoreCase)) {
_ws = ws.Name;
if (Table.IsAll) {
_fromRow = t.Address._fromRow;
_toRow = t.Address._toRow;
} else {
if (Table.IsThisRow) {
if (referenceAddress == null) {
_fromRow = -1;
_toRow = -1;
} else {
_fromRow = referenceAddress._fromRow;
_toRow = _fromRow;
}
} else if (Table.IsHeader && Table.IsData) {
_fromRow = t.Address._fromRow;
_toRow = t.ShowTotal ? t.Address._toRow - 1 : t.Address._toRow;
} else if (Table.IsData && Table.IsTotals) {
_fromRow = t.ShowHeader ? t.Address._fromRow + 1 : t.Address._fromRow;
_toRow = t.Address._toRow;
} else if (Table.IsHeader) {
_fromRow = t.ShowHeader ? t.Address._fromRow : -1;
_toRow = t.ShowHeader ? t.Address._fromRow : -1;
} else if (Table.IsTotals) {
_fromRow = t.ShowTotal ? t.Address._toRow : -1;
_toRow = t.ShowTotal ? t.Address._toRow : -1;
} else {
_fromRow = t.ShowHeader ? t.Address._fromRow + 1 : t.Address._fromRow;
_toRow = t.ShowTotal ? t.Address._toRow - 1 : t.Address._toRow;
}
}
if (string.IsNullOrEmpty(Table.ColumnSpan)) {
_fromCol = t.Address._fromCol;
_toCol = t.Address._toCol;
return;
}
var col = t.Address._fromCol;
var cols = Table.ColumnSpan.Split(':');
foreach (var c in t.Columns) {
if (_fromCol <= 0
&& cols[0]
.Equals(
c.Name,
StringComparison.InvariantCultureIgnoreCase)) //Issue15063 Add invariant igore case
{
_fromCol = col;
if (cols.Length == 1) {
_toCol = _fromCol;
return;
}
} else if (cols.Length > 1
&& _fromCol > 0
&& cols[1]
.Equals(
c.Name,
StringComparison.InvariantCultureIgnoreCase)) //Issue15063 Add invariant igore case
{
_toCol = col;
return;
}
col++;
}
}
}
}
}
}
/// <summary>
/// Address is an defined name
/// </summary>
/// <param name="address">the name</param>
/// <param name="isName">Should always be true</param>
internal ExcelAddressBase(string address, bool isName) {
if (isName) {
_address = address;
_fromRow = -1;
_fromCol = -1;
_toRow = -1;
_toCol = -1;
_start = null;
_end = null;
} else {
SetAddress(address);
}
}
protected internal void SetAddress(string address) {
address = address.Trim();
if (address.StartsWith("'")) {
SetWbWs(address);
} else if (address.StartsWith(
"[")) //Remove any external reference
{
SetWbWs(address);
} else {
_address = address;
}
if (_address.IndexOfAny(new[] { ',', '!', '[' }) > -1) {
//Advanced address. Including Sheet or multi or table.
ExtractAddress(_address);
} else {
//Simple address
GetRowColFromAddress(
_address,
out _fromRow,
out _fromCol,
out _toRow,
out _toCol,
out _fromRowFixed,
out _fromColFixed,
out _toRowFixed,
out _toColFixed);
_addresses = null;
_start = null;
_end = null;
}
_address = address;
Validate();
}
internal void ChangeAddress() {
if (AddressChange != null) {
AddressChange(this, new());
}
}
private void SetWbWs(string address) {
int pos = 0;
// Get Workbook, if any
if (address[pos] == '[') {
pos = address.IndexOf("]");
_wb = address.Substring(1, pos - 1);
pos++;
} else {
_wb = "";
}
// Get Worksheet
if (address[pos] == '\'') {
int startPos = pos;
pos = address.IndexOf("'", pos + 1);
while (pos < address.Length && address[pos + 1] == '\'') {
pos = address.IndexOf("'", pos + 2);
}
_ws = address.Substring(startPos + 1, pos - startPos - 1).Replace("''", "'");
pos++;
} else {
int startPos = pos;
pos = address.IndexOf("!", pos);
if (pos > -1) {
_ws = address.Substring(startPos, pos - startPos);
}
}
// Get Address
pos = address.IndexOf("!", pos);
if (pos > -1) {
_address = address.Substring(pos + 1);
} else {
_address = "";
}
}
internal void ChangeWorksheet(string wsName, string newWs) {
if (_ws == wsName) {
_ws = newWs;
}
var fullAddress = GetAddress();
if (Addresses != null) {
foreach (var a in Addresses) {
if (a._ws == wsName) {
a._ws = newWs;
fullAddress += "," + a.GetAddress();
} else {
fullAddress += "," + a._address;
}
}
}
_address = fullAddress;
}
private string GetAddress() {
var adr = "";
if (string.IsNullOrEmpty(_wb)) {
adr = "[" + _wb + "]";
}
if (string.IsNullOrEmpty(_ws)) {
adr += string.Format("'{0}'!", _ws);
}
adr += GetAddress(_fromRow, _fromCol, _toRow, _toCol);
return adr;
}
private ExcelCellAddress _start;
/// <summary>
/// Gets the row and column of the top left cell.
/// </summary>
/// <value>The start row column.</value>
public ExcelCellAddress Start {
get {
if (_start == null) {
_start = new(_fromRow, _fromCol);
}
return _start;
}
}
private ExcelCellAddress _end;
/// <summary>
/// Gets the row and column of the bottom right cell.
/// </summary>
/// <value>The end row column.</value>
public ExcelCellAddress End {
get {
if (_end == null) {
_end = new(_toRow, _toCol);
}
return _end;
}
}
private ExcelTableAddress _table;
public ExcelTableAddress Table => _table;
/// <summary>
/// The address for the range
/// </summary>
public virtual string Address => _address;
/// <summary>
/// If the address is a defined name
/// </summary>
public bool IsName => _fromRow < 0;
/// <summary>
/// Returns the address text
/// </summary>
/// <returns></returns>
public override string ToString() {
return _address;
}
private string _firstAddress;
/// <summary>
/// returns the first address if the address is a multi address.
/// A1:A2,B1:B2 returns A1:A2
/// </summary>
internal string FirstAddress {
get {
if (string.IsNullOrEmpty(_firstAddress)) {
return _address;
}
return _firstAddress;
}
}
internal string AddressSpaceSeparated => _address.Replace(',', ' '); //Conditional formatting and a few other places use space as separator for mulit addresses.
/// <summary>
/// Validate the address
/// </summary>
protected void Validate() {
if (_fromRow > _toRow || _fromCol > _toCol) {
throw new ArgumentOutOfRangeException(
"Start cell Address must be less or equal to End cell address");
}
}
internal string WorkSheet => _ws;
protected internal List<ExcelAddress> _addresses;
internal virtual List<ExcelAddress> Addresses => _addresses;
private bool ExtractAddress(string fullAddress) {
var brackPos = new Stack<int>();
var bracketParts = new List<string>();
string first = "",
second = "";
bool isText = false,
hasSheet = false;
try {
if (fullAddress == "#REF!") {
SetAddress(ref fullAddress, ref second, ref hasSheet);
return true;
}
if (fullAddress.StartsWith("!")) {
// invalid address!
return false;
}
for (int i = 0; i < fullAddress.Length; i++) {
var c = fullAddress[i];
if (c == '\'') {
if (isText && i + 1 < fullAddress.Length && fullAddress[i] == '\'') {
if (hasSheet) {
second += c;
} else {
first += c;
}
}
isText = !isText;
} else {
if (brackPos.Count > 0) {
if (c == '[' && !isText) {
brackPos.Push(i);
} else if (c == ']' && !isText) {
if (brackPos.Count > 0) {
var from = brackPos.Pop();
bracketParts.Add(fullAddress.Substring(from + 1, i - from - 1));
if (brackPos.Count == 0) {
HandleBrackets(first, second, bracketParts);
}
} else {
//Invalid address!
return false;
}
}
} else if (c == '[' && !isText) {
brackPos.Push(i);
} else if (c == '!' && !isText && !first.EndsWith("#REF") && !second.EndsWith("#REF")) {
hasSheet = true;
} else if (c == ',' && !isText) {
SetAddress(ref first, ref second, ref hasSheet);
} else {
if (hasSheet) {
second += c;
} else {
first += c;
}
}
}
}
if (Table == null) {
SetAddress(ref first, ref second, ref hasSheet);
}
return true;
} catch {
return false;
}
}
private void HandleBrackets(string first, string second, List<string> bracketParts) {
if (!string.IsNullOrEmpty(first)) {
_table = new();
Table.Name = first;
foreach (var s in bracketParts) {
if (s.IndexOf("[") < 0) {
switch (s.ToLower(CultureInfo.InvariantCulture)) {
case "#all":
_table.IsAll = true;
break;
case "#headers":
_table.IsHeader = true;
break;
case "#data":
_table.IsData = true;
break;
case "#totals":
_table.IsTotals = true;
break;
case "#this row":
_table.IsThisRow = true;
break;
default:
if (string.IsNullOrEmpty(_table.ColumnSpan)) {
_table.ColumnSpan = s;
} else {
_table.ColumnSpan += ":" + s;
}
break;
}
}
}
}
}
internal eAddressCollition Collide(ExcelAddressBase address) {
if (address.WorkSheet != WorkSheet && address.WorkSheet != null) {
return eAddressCollition.No;
}
if (address._fromRow > _toRow
|| address._fromCol > _toCol
|| _fromRow > address._toRow
|| _fromCol > address._toCol) {
return eAddressCollition.No;
}
if (address._fromRow == _fromRow
&& address._fromCol == _fromCol
&& address._toRow == _toRow
&& address._toCol == _toCol) {
return eAddressCollition.Equal;
}
if (address._fromRow >= _fromRow
&& address._toRow <= _toRow
&& address._fromCol >= _fromCol
&& address._toCol <= _toCol) {
return eAddressCollition.Inside;
}
return eAddressCollition.Partly;
}
internal ExcelAddressBase AddRow(int row, int rows, bool setFixed = false) {
if (row > _toRow) {
return this;
}
if (row <= _fromRow) {
return new(
(setFixed && _fromRowFixed ? _fromRow : _fromRow + rows),
_fromCol,
(setFixed && _toRowFixed ? _toRow : _toRow + rows),
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
return new(
_fromRow,
_fromCol,
(setFixed && _toRowFixed ? _toRow : _toRow + rows),
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
internal ExcelAddressBase DeleteRow(int row, int rows, bool setFixed = false) {
if (row
> _toRow) //After
{
return this;
}
if (row + rows
<= _fromRow) //Before
{
return new(
(setFixed && _fromRowFixed ? _fromRow : _fromRow - rows),
_fromCol,
(setFixed && _toRowFixed ? _toRow : _toRow - rows),
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
if (row <= _fromRow
&& row + rows
> _toRow) //Inside
{
return null;
} //Partly
if (row <= _fromRow) {
return new(
row,
_fromCol,
(setFixed && _toRowFixed ? _toRow : _toRow - rows),
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
return new(
_fromRow,
_fromCol,
(setFixed && _toRowFixed
? _toRow
: _toRow - rows < row
? row - 1
: _toRow - rows),
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
internal ExcelAddressBase AddColumn(int col, int cols, bool setFixed = false) {
if (col > _toCol) {
return this;
}
if (col <= _fromCol) {
return new(
_fromRow,
(setFixed && _fromColFixed ? _fromCol : _fromCol + cols),
_toRow,
(setFixed && _toColFixed ? _toCol : _toCol + cols),
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
return new(
_fromRow,
_fromCol,
_toRow,
(setFixed && _toColFixed ? _toCol : _toCol + cols),
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
internal ExcelAddressBase DeleteColumn(int col, int cols, bool setFixed = false) {
if (col
> _toCol) //After
{
return this;
}
if (col + cols
<= _fromCol) //Before
{
return new(
_fromRow,
(setFixed && _fromColFixed ? _fromCol : _fromCol - cols),
_toRow,
(setFixed && _toColFixed ? _toCol : _toCol - cols),
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
if (col <= _fromCol
&& col + cols
> _toCol) //Inside
{
return null;
} //Partly
if (col <= _fromCol) {
return new(
_fromRow,
col,
_toRow,
(setFixed && _toColFixed ? _toCol : _toCol - cols),
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
return new(
_fromRow,
_fromCol,
_toRow,
(setFixed && _toColFixed
? _toCol
: _toCol - cols < col
? col - 1
: _toCol - cols),
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
internal ExcelAddressBase Insert(
ExcelAddressBase address,
eShiftType shift /*, out ExcelAddressBase topAddress, out ExcelAddressBase leftAddress, out ExcelAddressBase rightAddress, out ExcelAddressBase bottomAddress*/) {
//Before or after, no change
//if ((_toRow > address._fromRow && _toCol > address.column) ||
// (_fromRow > address._toRow && column > address._toCol))
if (_toRow < address._fromRow
|| _toCol < address._fromCol
|| (_fromRow > address._toRow && _fromCol > address._toCol)) {
//topAddress = null;
//leftAddress = null;
//rightAddress = null;
//bottomAddress = null;
return this;
}
int rows = address.Rows;
int cols = address.Columns;
string retAddress = "";
if (shift == eShiftType.Right) {
if (address._fromRow > _fromRow) {
retAddress = GetAddress(
_fromRow,
_fromCol,
address._fromRow,
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
if (address._fromCol > _fromCol) {
retAddress = GetAddress(
_fromRow < address._fromRow ? _fromRow : address._fromRow,
_fromCol,
address._fromRow,
_toCol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
}
}
if (_toRow < address._fromRow) {
if (_fromRow < address._fromRow) {}
}
return null;
}
private void SetAddress(ref string first, ref string second, ref bool hasSheet) {
string ws,
address;
if (hasSheet) {
ws = first;
address = second;
first = "";
second = "";
} else {
address = first;
ws = "";
first = "";
}
hasSheet = false;
if (string.IsNullOrEmpty(_firstAddress)) {
if (string.IsNullOrEmpty(_ws) || !string.IsNullOrEmpty(ws)) {
_ws = ws;
}
_firstAddress = address;
GetRowColFromAddress(
address,
out _fromRow,
out _fromCol,
out _toRow,
out _toCol,
out _fromRowFixed,
out _fromColFixed,
out _toRowFixed,
out _toColFixed);
} else {
if (_addresses == null) {
_addresses = new();
}
_addresses.Add(new(_ws, address));
}
}
internal enum AddressType {
Invalid,
InternalAddress,
ExternalAddress,
InternalName,
ExternalName,
Formula,
}
internal static AddressType IsValid(string address) {
if (address == "#REF!") {
return AddressType.Invalid;
}
if (double.TryParse(
address,
NumberStyles.Any,
CultureInfo.InvariantCulture,
out _)) //A double, no valid address
{
return AddressType.Invalid;
}
if (IsFormula(address)) {
return AddressType.Formula;
}
if (SplitAddress(address, out var wb, out _, out var intAddress)) {
if (intAddress.Contains(
"[")) //Table reference
{
return string.IsNullOrEmpty(wb) ? AddressType.InternalAddress : AddressType.ExternalAddress;
}
if (intAddress.Contains(",")) {
intAddress = intAddress.Substring(0, intAddress.IndexOf(','));
}
if (IsAddress(intAddress)) {
return string.IsNullOrEmpty(wb) ? AddressType.InternalAddress : AddressType.ExternalAddress;
}
return string.IsNullOrEmpty(wb) ? AddressType.InternalName : AddressType.ExternalName;
}
return AddressType.Invalid;
}
private static bool IsAddress(string intAddress) {
if (string.IsNullOrEmpty(intAddress)) {
return false;
}
var cells = intAddress.Split(':');
int toRow,
toCol;
if (!GetRowCol(cells[0], out var fromRow, out var fromCol, false)) {
return false;
}
if (cells.Length > 1) {
if (!GetRowCol(cells[1], out toRow, out toCol, false)) {
return false;
}
} else {
toRow = fromRow;
toCol = fromCol;
}
if (fromRow <= toRow
&& fromCol <= toCol
&& fromCol > -1
&& toCol <= ExcelPackage.MaxColumns
&& fromRow > -1
&& toRow <= ExcelPackage.MaxRows) {
return true;
}
return false;
}
private static bool SplitAddress(
string address,
out string wb,
out string ws,
out string intAddress) {
wb = "";
ws = "";
intAddress = "";
var text = "";
bool isText = false;
var brackPos = -1;
for (int i = 0; i < address.Length; i++) {
if (address[i] == '\'') {
isText = !isText;
if (i > 0 && address[i - 1] == '\'') {
text += "'";
}
} else {
if (address[i] == '!' && !isText) {
if (text.Length > 0 && text[0] == '[') {
wb = text.Substring(1, text.IndexOf("]") - 1);
ws = text.Substring(text.IndexOf("]") + 1);
} else {
ws = text;
}
intAddress = address.Substring(i + 1);
return true;
}
if (address[i] == '[' && !isText) {
if (i
> 0) //Table reference return full address;
{
intAddress = address;
return true;
}
brackPos = i;
} else if (address[i] == ']' && !isText) {
if (brackPos > -1) {
wb = text;
text = "";
} else {
return false;
}
} else {
text += address[i];
}
}
}
intAddress = text;
return true;
}
private static bool IsFormula(string address) {
var isText = false;
for (int i = 0; i < address.Length; i++) {
if (address[i] == '\'') {
isText = !isText;
} else {
if (isText == false
&& address
.Substring(i, 1)
.IndexOfAny(new[] { '(', ')', '+', '-', '*', '/', '.', '=', '^', '&', '%', '\"' })
> -1) {
return true;
}
}
}
return false;
}
public int Rows => _toRow - _fromRow + 1;
public int Columns => _toCol - _fromCol + 1;
internal static String GetWorkbookPart(string address) {
int ix;
if (address[0] == '[') {
ix = address.IndexOf(']') + 1;
if (ix > 0) {
return address.Substring(1, ix - 2);
}
}
return "";
}
internal static string GetWorksheetPart(string address, string defaultWorkSheet, ref int endIx) {
if (address == "") {
return defaultWorkSheet;
}
var ix = 0;
if (address[0] == '[') {
ix = address.IndexOf(']') + 1;
}
if (ix > 0 && ix < address.Length) {
if (address[ix] == '\'') {
return GetString(address, ix, out endIx);
}
var ixEnd = address.IndexOf('!', ix);
if (ixEnd > ix) {
return address.Substring(ix, ixEnd - ix);
}
return defaultWorkSheet;
}
return defaultWorkSheet;
}
internal static void SplitAddress(
string fullAddress,
out string wb,
out string ws,
out string address,
string defaultWorksheet = "") {
wb = GetWorkbookPart(fullAddress);
int ix = 0;
ws = GetWorksheetPart(fullAddress, defaultWorksheet, ref ix);
if (ix < fullAddress.Length) {
if (fullAddress[ix] == '!') {
address = fullAddress.Substring(ix + 1);
} else {
address = fullAddress.Substring(ix);
}
} else {
address = "";
}
}
private static string GetString(string address, int ix, out int endIx) {
var strIx = address.IndexOf("''");
while (strIx > -1) {
strIx = address.IndexOf("''");
}
endIx = address.IndexOf("'");
return address.Substring(ix, endIx - ix).Replace("''", "'");
}
internal bool IsValidRowCol() {
return !(_fromRow > _toRow
|| _fromCol > _toCol
|| _fromRow < 1
|| _fromCol < 1
|| _toRow > ExcelPackage.MaxRows
|| _toCol > ExcelPackage.MaxColumns);
}
}
/// <summary>
/// Range address with the address property readonly
/// </summary>
public class ExcelAddress : ExcelAddressBase {
internal ExcelAddress() {}
public ExcelAddress(int fromRow, int fromCol, int toRow, int toColumn)
: base(fromRow, fromCol, toRow, toColumn) {
_ws = "";
}
public ExcelAddress(string address)
: base(address) {}
internal ExcelAddress(string ws, string address)
: base(address) {
if (string.IsNullOrEmpty(_ws)) {
_ws = ws;
}
}
internal ExcelAddress(string ws, string address, bool isName)
: base(address, isName) {
if (string.IsNullOrEmpty(_ws)) {
_ws = ws;
}
}
public ExcelAddress(string address, ExcelWorkbook workbook, ExcelAddressBase referenceAddress)
: base(address, workbook, referenceAddress) {}
/// <summary>
/// The address for the range
/// </summary>
/// <remarks>Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" </remarks>
public new string Address {
get {
if (string.IsNullOrEmpty(_address) && _fromRow > 0) {
_address = GetAddress(_fromRow, _fromCol, _toRow, _toCol);
}
return _address;
}
set {
SetAddress(value);
ChangeAddress();
}
}
}
public class ExcelFormulaAddress : ExcelAddressBase {
private bool _fromRowFixed,
_toRowFixed,
_fromColFixed,
_toColFixed;
public ExcelFormulaAddress(int fromRow, int fromCol, int toRow, int toColumn)
: base(fromRow, fromCol, toRow, toColumn) {
_ws = "";
}
public ExcelFormulaAddress(string address)
: base(address) {
SetFixed();
}
private void SetFixed() {
if (Address.IndexOf("[") >= 0) {
return;
}
var address = FirstAddress;
if (_fromRow == _toRow && _fromCol == _toCol) {
GetFixed(address, out _fromRowFixed, out _fromColFixed);
} else {
var cells = address.Split(':');
GetFixed(cells[0], out _fromRowFixed, out _fromColFixed);
GetFixed(cells[1], out _toRowFixed, out _toColFixed);
}
}
private void GetFixed(string address, out bool rowFixed, out bool colFixed) {
rowFixed = colFixed = false;
var ix = address.IndexOf('$');
while (ix > -1) {
ix++;
if (ix < address.Length) {
if (address[ix] >= '0' && address[ix] <= '9') {
rowFixed = true;
break;
}
colFixed = true;
}
ix = address.IndexOf('$', ix);
}
}
/// <summary>
/// The address for the range
/// </summary>
/// <remarks>Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" </remarks>
public new string Address {
get {
if (string.IsNullOrEmpty(_address) && _fromRow > 0) {
_address = GetAddress(
_fromRow,
_fromCol,
_toRow,
_toCol,
_fromRowFixed,
_toRowFixed,
_fromColFixed,
_toColFixed);
}
return _address;
}
set {
SetAddress(value);
ChangeAddress();
SetFixed();
}
}
internal new List<ExcelFormulaAddress> _addresses;
public new List<ExcelFormulaAddress> Addresses {
get {
if (_addresses == null) {
_addresses = new();
}
return _addresses;
}
}
internal string GetOffset(int row, int column) {
int fromRow = _fromRow,
fromCol = _fromCol,
toRow = _toRow,
tocol = _toCol;
var isMulti = (fromRow != toRow || fromCol != tocol);
if (!_fromRowFixed) {
fromRow += row;
}
if (!_fromColFixed) {
fromCol += column;
}
if (isMulti) {
if (!_toRowFixed) {
toRow += row;
}
if (!_toColFixed) {
tocol += column;
}
} else {
toRow = fromRow;
tocol = fromCol;
}
string a = GetAddress(
fromRow,
fromCol,
toRow,
tocol,
_fromRowFixed,
_fromColFixed,
_toRowFixed,
_toColFixed);
if (Addresses != null) {
foreach (var sa in Addresses) {
a += "," + sa.GetOffset(row, column);
}
}
return a;
}
}