| /******************************************************************************* |
| * 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; } |
| } |
| /// <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 |
| { |
| 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() |
| { |
| } |
| /// <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> |
| 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++; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /// <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("'")) |
| { |
| 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 |
| /// <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 ExcelCellAddress(_fromRow, _fromCol); |
| } |
| return _start; |
| } |
| } |
| ExcelCellAddress _end = null; |
| /// <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 ExcelCellAddress(_toRow, _toCol); |
| } |
| return _end; |
| } |
| } |
| ExcelTableAddress _table=null; |
| public ExcelTableAddress Table |
| { |
| get |
| { |
| return _table; |
| } |
| } |
| |
| /// <summary> |
| /// The address for the range |
| /// </summary> |
| public virtual string Address |
| { |
| get |
| { |
| return _address; |
| } |
| } |
| /// <summary> |
| /// If the address is a defined name |
| /// </summary> |
| public bool IsName |
| { |
| get |
| { |
| return _fromRow < 0; |
| } |
| } |
| /// <summary> |
| /// Returns the address text |
| /// </summary> |
| /// <returns></returns> |
| public override string ToString() |
| { |
| return _address; |
| } |
| 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; |
| } |
| else |
| { |
| return _firstAddress; |
| } |
| } |
| } |
| internal string AddressSpaceSeparated |
| { |
| get |
| { |
| return _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 |
| { |
| get |
| { |
| return _ws; |
| } |
| } |
| internal protected List<ExcelAddress> _addresses = null; |
| internal virtual List<ExcelAddress> Addresses |
| { |
| get |
| { |
| return _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; |
| } |
| else 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 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<ExcelAddress>(); |
| _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<address.Length) |
| { |
| if (address[ix] == '!') |
| { |
| return address.Substring(ix + 1); |
| } |
| else |
| { |
| return ""; |
| } |
| } |
| else |
| { |
| return ""; |
| } |
| |
| } |
| 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("''"); |
| var prevStrIx = ix; |
| while(strIx > -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); |
| } |
| } |
| /// <summary> |
| /// Range address with the address property readonly |
| /// </summary> |
| 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) |
| { |
| |
| } |
| /// <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); |
| 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); |
| } |
| } |
| /// <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); |
| base.ChangeAddress(); |
| SetFixed(); |
| } |
| } |
| internal new List<ExcelFormulaAddress> _addresses; |
| public new List<ExcelFormulaAddress> Addresses |
| { |
| get |
| { |
| if (_addresses == null) |
| { |
| _addresses = new List<ExcelFormulaAddress>(); |
| } |
| 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; |
| } |
| } |
| } |