using System;
using System.Text.RegularExpressions;
namespace FlexFramework.Excel
{
///
/// A value which represents the position of a in a
///
public struct Address : IEquatable
{
#region Equality members
bool IEquatable.Equals(Address other)
{
return Column == other.Column && Row == other.Row;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Address && Equals((Address)obj);
}
public override int GetHashCode()
{
unchecked
{
return (Column * 397) ^ Row;
}
}
#endregion
///
/// One-based column index
///
public int Column { get; private set; }
///
/// One-based row index
///
public int Row { get; private set; }
///
/// Initializes a new instance of the Address class
///
/// Excel cell address; e.g. A1
///
public Address(string address)
{
var column = Regex.Match(address, "^[A-Z]+");
var row = Regex.Match(address, "\\d+$");
if (!column.Success || !row.Success)
throw new FormatException("Invalid address: " + address);
Column = ParseColumn(column.Value);
Row = int.Parse(row.Value);
}
///
/// Initializes a new instance of the Address class
///
/// One-based column index
/// One-based row index
public Address(int column, int row)
{
Column = column;
Row = row;
}
///
/// Column name; e.g. A
///
public string ColumnName
{
get { return ParseColumn(Column); }
}
public override string ToString()
{
return string.Format("{0}{1}", ColumnName, Row);
}
public static bool operator ==(Address address, Address other)
{
return address.Row == other.Row && address.Column == other.Column;
}
public static bool operator !=(Address address, Address other)
{
return address.Row != other.Row || address.Column != other.Column;
}
public static bool operator >=(Address address, Address other)
{
return address.Row >= other.Row && address.Column >= other.Column;
}
public static bool operator <=(Address address, Address other)
{
return address.Row <= other.Row && address.Column <= other.Column;
}
public static bool operator >(Address address, Address other)
{
return address.Row > other.Row && address.Column > other.Column;
}
public static bool operator <(Address address, Address other)
{
return address.Row < other.Row && address.Column < other.Column;
}
public static Range operator +(Address from, Address to)
{
return new Range(from, to);
}
public static Address operator >>(Address address, int column)
{
return new Address(address.Column + column, address.Row);
}
public static Address operator <<(Address address, int column)
{
return new Address(address.Column - column, address.Row);
}
public static Address operator +(Address address, int row)
{
return new Address(address.Column, address.Row + row);
}
public static Address operator -(Address address, int row)
{
return new Address(address.Column, address.Row - row);
}
///
/// Parse one-based column index to excel-style
///
/// One-based column index
/// Converted column index in excel-style
public static string ParseColumn(int column)
{
if (column <= 0)
throw new ArgumentException("Column value must be greater than 0");
if (column <= 26)
return Convert.ToChar(column + 64).ToString();
var div = column / 26;
var mod = column % 26;
if (mod != 0) return ParseColumn(div) + ParseColumn(mod);
mod = 26;
div--;
return ParseColumn(div) + ParseColumn(mod);
}
///
/// Parse excel-style column to one-based index
///
/// Excel-style column
/// Converted one-based column index
///
public static int ParseColumn(string column)
{
if (!Regex.IsMatch(column, "^[A-Z]+$"))
throw new FormatException("Invalid address: " + column);
var digits = new int[column.Length];
for (var i = 0; i < column.Length; ++i)
digits[i] = Convert.ToInt32(column[i]) - 64;
var mul = 1;
var res = 0;
for (var pos = digits.Length - 1; pos >= 0; --pos)
{
res += digits[pos] * mul;
mul *= 26;
}
return res;
}
///
/// Check if the given address is valid
///
///
/// True if valid
public static bool IsValid(string address)
{
return Regex.IsMatch(address, "^[A-Z]+\\d+$");
}
}
}