/******************************************************************************* * 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; using System.Text; using System.Text.RegularExpressions; 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; } } /// /// A range address /// /// Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" public class ExcelAddressBase : ExcelCellBase { internal protected int _fromRow=-1, _toRow, _fromCol, _toCol; protected internal bool _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed; internal protected string _wb; internal protected string _ws; internal protected string _address; internal protected event EventHandler AddressChange; internal enum eAddressCollition { No, Partly, Inside, Equal } internal enum eShiftType { Right, Down, EntireRow, EntireColumn } #region "Constructors" internal ExcelAddressBase() { } /// /// Creates an Address object /// /// start row /// start column /// End row /// End column 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); } /// /// Creates an Address object /// /// start row /// start column /// End row /// End column /// start row fixed /// start column fixed /// End row fixed /// End column fixed 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 ); } /// /// Creates an Address object /// /// Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" /// The Excel Address public ExcelAddressBase(string address) { SetAddress(address); } /// /// Creates an Address object /// /// Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" /// The Excel Address /// Reference to the package to find information about tables and names /// The address public ExcelAddressBase(string address, ExcelPackage pck, ExcelAddressBase referenceAddress) { SetAddress(address); SetRCFromTable(pck, referenceAddress); } internal void SetRCFromTable(ExcelPackage pck, ExcelAddressBase referenceAddress) { if (string.IsNullOrEmpty(_wb) && Table != null) { foreach (var ws in pck.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; } else { 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++; } } } } } } } /// /// Address is an defined name /// /// the name /// Should always be true 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) { if(address.StartsWith("'")) { int pos = address.IndexOf("'", 1); while (pos < address.Length && address[pos + 1] == '\'') { pos = address.IndexOf("'", pos+2); } var wbws = address.Substring(1,pos-1).Replace("''","'"); SetWbWs(wbws); _address = address.Substring(pos + 2); } else if (address.StartsWith("[")) //Remove any external reference { SetWbWs(address); } else { _address = address; } if(_address.IndexOfAny(new char[] {',','!', '['}) > -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 EventArgs()); } } private void SetWbWs(string address) { int pos; if (address[0] == '[') { pos = address.IndexOf("]"); _wb = address.Substring(1, pos - 1); _ws = address.Substring(pos + 1); } else { _wb = ""; _ws = address; } pos = _ws.IndexOf("!"); if (pos > -1) { _address = _ws.Substring(pos + 1); _ws = _ws.Substring(0, pos); } } 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; } ExcelCellAddress _start = null; #endregion /// /// Gets the row and column of the top left cell. /// /// The start row column. public ExcelCellAddress Start { get { if (_start == null) { _start = new ExcelCellAddress(_fromRow, _fromCol); } return _start; } } ExcelCellAddress _end = null; /// /// Gets the row and column of the bottom right cell. /// /// The end row column. public ExcelCellAddress End { get { if (_end == null) { _end = new ExcelCellAddress(_toRow, _toCol); } return _end; } } ExcelTableAddress _table=null; public ExcelTableAddress Table { get { return _table; } } /// /// The address for the range /// public virtual string Address { get { return _address; } } /// /// If the address is a defined name /// public bool IsName { get { return _fromRow < 0; } } /// /// Returns the address text /// /// public override string ToString() { return _address; } string _firstAddress; /// /// returns the first address if the address is a multi address. /// A1:A2,B1:B2 returns A1:A2 /// internal string FirstAddress { get { if (string.IsNullOrEmpty(_firstAddress)) { return _address; } else { return _firstAddress; } } } internal string AddressSpaceSeparated { get { return _address.Replace(',', ' '); //Conditional formatting and a few other places use space as separator for mulit addresses. } } /// /// Validate the address /// 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 { get { return _ws; } } internal protected List _addresses = null; internal virtual List Addresses { get { return _addresses; } } private bool ExtractAddress(string fullAddress) { var brackPos=new Stack(); var bracketParts=new List(); string first="", second=""; bool isText=false, hasSheet=false; try { if (fullAddress == "#REF!") { SetAddress(ref fullAddress, ref second, ref hasSheet); return true; } 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 bracketParts) { if(!string.IsNullOrEmpty(first)) { _table = new ExcelTableAddress(); 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; } } } } } #region Address manipulation methods 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; } else if (address._fromRow == _fromRow && address._fromCol == _fromCol && address._toRow == _toRow && address._toCol == _toCol) { return eAddressCollition.Equal; } else if (address._fromRow >= _fromRow && address._toRow <= _toRow && address._fromCol >= _fromCol && address._toCol <= _toCol) { return eAddressCollition.Inside; } else return eAddressCollition.Partly; } internal ExcelAddressBase AddRow(int row, int rows, bool setFixed=false) { if (row > _toRow) { return this; } else if (row <= _fromRow) { return new ExcelAddressBase((setFixed && _fromRowFixed ? _fromRow : _fromRow + rows), _fromCol, (setFixed && _toRowFixed ? _toRow : _toRow + rows), _toCol, _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else { return new ExcelAddressBase(_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; } else if (row+rows <= _fromRow) //Before { return new ExcelAddressBase((setFixed && _fromRowFixed ? _fromRow : _fromRow - rows), _fromCol, (setFixed && _toRowFixed ? _toRow : _toRow - rows), _toCol, _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else if (row <= _fromRow && row + rows > _toRow) //Inside { return null; } else //Partly { if (row <= _fromRow) { return new ExcelAddressBase(row, _fromCol, (setFixed && _toRowFixed ? _toRow : _toRow - rows), _toCol, _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else { return new ExcelAddressBase(_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; } else if (col <= _fromCol) { return new ExcelAddressBase(_fromRow, (setFixed && _fromColFixed ? _fromCol : _fromCol + cols), _toRow, (setFixed && _toColFixed ? _toCol : _toCol + cols), _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else { return new ExcelAddressBase(_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; } else if (col + cols <= _fromCol) //Before { return new ExcelAddressBase(_fromRow, (setFixed && _fromColFixed ? _fromCol : _fromCol - cols), _toRow, (setFixed && _toColFixed ? _toCol :_toCol - cols), _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else if (col <= _fromCol && col + cols > _toCol) //Inside { return null; } else //Partly { if (col <= _fromCol) { return new ExcelAddressBase(_fromRow, col, _toRow, (setFixed && _toColFixed ? _toCol : _toCol - cols), _fromRowFixed, _fromColFixed, _toRowFixed, _toColFixed); } else { return new ExcelAddressBase(_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) { } else { } } return null; } #endregion 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 List(); _addresses.Add(new ExcelAddress(_ws, address)); } } internal enum AddressType { Invalid, InternalAddress, ExternalAddress, InternalName, ExternalName, Formula } internal static AddressType IsValid(string Address) { double d; if (Address == "#REF!") { return AddressType.Invalid; } else if(double.TryParse(Address, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) //A double, no valid address { return AddressType.Invalid; } else if (IsFormula(Address)) { return AddressType.Formula; } else { string wb, ws, intAddress; if(SplitAddress(Address, out wb, out ws, out intAddress)) { if(intAddress.Contains("[")) //Table reference { return string.IsNullOrEmpty(wb) ? AddressType.InternalAddress : AddressType.ExternalAddress; } else if(intAddress.Contains(",")) { intAddress=intAddress.Substring(0, intAddress.IndexOf(',')); } if(IsAddress(intAddress)) { return string.IsNullOrEmpty(wb) ? AddressType.InternalAddress : AddressType.ExternalAddress; } else { return string.IsNullOrEmpty(wb) ? AddressType.InternalName : AddressType.ExternalName; } } else { return AddressType.Invalid; } //if(string.IsNullOrEmpty(wb)); } //ExcelAddress a = new ExcelAddress(Address); //if (Address.IndexOf('!') > 0) //{ // string[] split = Address.Split('!'); // if (split.Length == 2) // { // ws = split[0]; // Address = split[1]; // } // else if (split.Length == 3 && split[1] == "#REF" && split[2] == "") // { // ws = split[0]; // Address = "#REF!"; // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) // { // return AddressType.ExternalAddress; // } // else // { // return AddressType.InternalAddress; // } // } // else // { // return AddressType.Invalid; // } //} //int _fromRow, column, _toRow, _toCol; //if (ExcelAddressBase.GetRowColFromAddress(Address, out _fromRow, out column, out _toRow, out _toCol)) //{ // if (_fromRow > 0 && column > 0 && _toRow <= ExcelPackage.MaxRows && _toCol <= ExcelPackage.MaxColumns) // { // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) // { // return AddressType.ExternalAddress; // } // else // { // return AddressType.InternalAddress; // } // } // else // { // return AddressType.Invalid; // } //} //else //{ // if(IsValidName(Address)) // { // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) // { // return AddressType.ExternalName; // } // else // { // return AddressType.InternalName; // } // } // else // { // return AddressType.Invalid; // } //} } private static bool IsAddress(string intAddress) { if(string.IsNullOrEmpty(intAddress)) return false; var cells = intAddress.Split(':'); int fromRow,toRow, fromCol, toCol; if(!GetRowCol(cells[0], out fromRow, out 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; } else { 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; } else { 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 char[] { '(', ')', '+', '-', '*', '/', '.', '=', '^', '&', '%', '\"' }) > -1) { return true; } } } return false; } private static bool IsValidName(string address) { if (Regex.IsMatch(address, "[^0-9./*-+,½!\"@#£%&/{}()\\[\\]=?`^~':;<>|][^/*-+,½!\"@#£%&/{}()\\[\\]=?`^~':;<>|]*")) { return true; } else { return false; } } public int Rows { get { return _toRow - _fromRow+1; } } public int Columns { get { return _toCol - _fromCol + 1; } } internal bool IsMultiCell() { return (_fromRow < _fromCol || _fromCol < _toCol); } internal static String GetWorkbookPart(string address) { var ix = 0; 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) { int ix=0; return GetWorksheetPart(address, defaultWorkSheet, ref ix); } 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); } else { var ixEnd = address.IndexOf('!',ix); if(ixEnd>ix) { return address.Substring(ix, ixEnd-ix); } else { return defaultWorkSheet; } } } else { return defaultWorkSheet; } } internal static string GetAddressPart(string address) { var ix=0; GetWorksheetPart(address, "", ref ix); if(ix -1) { prevStrIx = strIx; 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); } } /// /// Range address with the address property readonly /// public class ExcelAddress : ExcelAddressBase { internal ExcelAddress() : base() { } 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, ExcelPackage package, ExcelAddressBase referenceAddress) : base(Address, package, referenceAddress) { } /// /// The address for the range /// /// Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" public new string Address { get { if (string.IsNullOrEmpty(_address) && _fromRow>0) { _address = GetAddress(_fromRow, _fromCol, _toRow, _toCol); } return _address; } set { SetAddress(value); base.ChangeAddress(); } } } public class ExcelFormulaAddress : ExcelAddressBase { bool _fromRowFixed, _toRowFixed, _fromColFixed, _toColFixed; internal ExcelFormulaAddress() : base() { } public ExcelFormulaAddress(int fromRow, int fromCol, int toRow, int toColumn) : base(fromRow, fromCol, toRow, toColumn) { _ws = ""; } public ExcelFormulaAddress(string address) : base(address) { SetFixed(); } internal ExcelFormulaAddress(string ws, string address) : base(address) { if (string.IsNullOrEmpty(_ws)) _ws = ws; SetFixed(); } internal ExcelFormulaAddress(string ws, string address, bool isName) : base(address, isName) { if (string.IsNullOrEmpty(_ws)) _ws = ws; if(!isName) 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; } else { colFixed=true; } } ix = address.IndexOf('$', ix); } } /// /// The address for the range /// /// Examples of addresses are "A1" "B1:C2" "A:A" "1:1" "A1:E2,G3:G5" 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); base.ChangeAddress(); SetFixed(); } } internal new List _addresses; public new List Addresses { get { if (_addresses == null) { _addresses = new List(); } 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; } } }