using System; using System.Collections.Generic; using System.Linq; namespace FlexFramework.Excel { /// /// Extensions /// public static class Extensions { /// /// Convert cell to target type /// /// Cell /// /// Target type /// Target instance public static object Convert(this Cell cell, Type type) { return ValueConverter.Convert(type, cell.Text); } /// /// Convert cell to target type /// /// Target type /// Cell /// Target instance public static T Convert(this Cell cell) { return (T)Convert(cell, typeof(T)); } /// /// Convert row to object instance /// /// Target type /// Row /// Generator /// Object instance public static T Convert(this Row row, IGenerator generator) where T : new() { return (T)generator.Instantiate(row); } /// /// Convert row to object instance /// /// Target type /// Row /// Mapping expression /// Object instance public static T Convert(this Row row, string expression) where T : new() { return Convert(row, new Mapper().Map(expression)); } /// /// Convert row to object instance /// /// Target type /// Row /// Mapping provider /// Object instance public static T Convert(this Row row, IEnumerable cols) where T : new() { return Convert(row, new Mapper().Map(cols)); } /// /// Convert row to object instance /// /// Row /// Target type /// Mapping expression /// Object instance public static object Convert(this Row row, Type type, string expression) { return Convert(row, new Mapper(type).Map(expression)); } /// /// Convert row to object instance /// /// Row /// Target type /// Mapping provider /// Object instance public static object Convert(this Row row, Type type, IEnumerable cols) { return Convert(row, new Mapper(type).Map(cols)); } /// /// Convert row to object instance /// /// Row /// Generator /// Object instance public static object Convert(this Row row, IGenerator generator) { return generator.Instantiate(row); } /// /// Convert table to target enumerable /// /// Target type /// Table /// Target enumerable public static IEnumerable Convert(this Table table) where T : new() { var mapper = new TableMapper(); mapper.Extract(); return Convert(table, mapper); } /// /// Convert table to target enumerable /// /// Table /// Target type /// Target enumerable public static IEnumerable Convert(this Table table, Type type) { var mapper = new TableMapper(type); mapper.Extract(); return Convert(table, mapper); } /// /// Convert table to target enumerable /// /// Target type /// Table /// Generator /// Target enumerable public static IEnumerable Convert(this Table table, ITableGenerator generator) where T : new() { return generator.Instantiate(table); } /// /// Convert table to target enumerable /// /// Target type /// Table /// One-based mapping provider row index /// Target enumerable public static IEnumerable Convert(this Table table, int row) where T : new() { if (row < 1) { throw new ArgumentException("One-based row index must be greater than 0"); } ITableGenerator generator = new TableMapper().Map(table[row - 1]).Exclude(row); return generator.Instantiate(table); } /// /// Convert table to target enumerable /// /// Target type /// Table /// Mapping expression /// One-based row indices to exclude /// Target enumerable public static IEnumerable Convert(this Table table, string expression, params int[] exclude) where T : new() { var generator = new TableMapper().Map(expression); if (exclude != null && exclude.Length > 0) generator.Exclude(exclude); return Convert(table, generator); } /// /// Convert table to target enumerable /// /// Table /// Target type /// Mapping expression /// One-based row indices to exclude /// Target enumerable public static IEnumerable Convert(this Table table, Type type, string expression, params int[] exclude) { var generator = new TableMapper(type).Map(expression); if (exclude != null && exclude.Length > 0) generator.Exclude(exclude); return Convert(table, generator); } /// /// Convert table to target enumerable /// /// Table /// Generator /// Target enumerable public static IEnumerable Convert(this Table table, ITableGenerator generator) { return generator.Instantiate(table); } /// /// Convert table to target enumerable /// /// Table /// Target type /// One-based mapping provider row index /// Target enumerable public static IEnumerable Convert(this Table table, Type type, int row) { if (row < 1) { throw new ArgumentException("One-based row index must be greater than 0"); } ITableGenerator generator = new TableMapper(type).Map(table[row - 1]).Exclude(row); return generator.Instantiate(table); } /// /// Dump row data to string /// /// Row /// Column delimiter /// Column escaping enclose /// Plain text public static string Dump(this Row row, char delimiter, char enclose) { return row.Cells.Select(c => Dump(c, delimiter, enclose)).Aggregate(string.Empty, (a, b) => string.IsNullOrEmpty(a) ? b : a + delimiter + b); } /// /// Dump row data to string using default delimiter and escaping enclose characters /// /// Row /// Plain text public static string Dump(this Row row) { return Dump(row, Document.Delimiter, Document.Enclose); } /// /// Dump cell data to string /// /// /// This will enclose delimiter and enclose characters /// /// Cell /// Column delimiter /// Column escaping enclose /// Plain text public static string Dump(this Cell cell, char delimiter, char enclose) { var value = cell.Text; if (string.IsNullOrEmpty(value)) return string.Empty; if (value.Contains(enclose) || value.Contains(delimiter)) { return enclose + value.Replace(enclose.ToString(), enclose.ToString() + enclose) + enclose; } return value; } /// /// Dump cell data to string using default delimiter and escaping enclose characters /// /// Cell /// Plain text public static string Dump(this Cell cell) { return Dump(cell, Document.Delimiter, Document.Enclose); } /// /// Dump table data to string /// /// Column delimiter /// Column escaping enclose /// Plain text public static string Dump(this Table table, char delimiter, char enclose) { return table.Select(r => Dump(r, delimiter, enclose)).Aggregate(string.Empty, (a, b) => string.IsNullOrEmpty(a) ? b : a + Environment.NewLine + b); } /// /// Dump table data to string using default delimiter and escaping enclose characters /// /// Table /// Plain text public static string Dump(this Table table) { return Dump(table, Document.Delimiter, Document.Enclose); } /// /// Select cell by address formula /// /// Row /// Address formula(e.g. A1, C20) /// Cell or null /// public static Cell Select(this Row row, string address) { if (!Address.IsValid(address)) throw new FormatException(); var ad = new Address(address); return row.FirstOrDefault(cell => cell.Address == ad); } /// /// Select cells by range formula /// /// Row /// Range formula(e.g. A1:A5, B2:C20) /// Cells or empty /// public static IEnumerable SelectRange(this Row row, string range) { if (!Range.IsValid(range)) throw new FormatException(); return row[new Range(range)]; } /// /// Select cell by address formula /// /// Table /// Address formula(e.g. A1, C20) /// Cell or null /// public static Cell Select(this Table table, string address) { if (!Address.IsValid(address)) throw new FormatException(); var ad = new Address(address); return table.SelectMany(row => row).FirstOrDefault(cell => cell.Address == ad); } /// /// Select cells by range formula /// /// Table /// Range formula(e.g. A1:A5, B2:C20) /// Cells or empty /// public static IEnumerable SelectRange(this Table table, string range) { if (!Range.IsValid(range)) throw new FormatException(); return table[new Range(range)]; } /// /// Select cell by address formula /// /// WorkBook /// Address formula(e.g. sheet1!A1, sheet2!C20) /// Cell or null /// public static Cell Select(this WorkBook book, string path) { var args = path.Split(new[] { '!' }, StringSplitOptions.RemoveEmptyEntries); if (args.Length != 2) throw new FormatException(); var sheet = book.FirstOrDefault(s => s.Name == args[0]); return sheet == null ? null : Select(sheet, args[1]); } /// /// Select cells by range formula /// /// WorkBook /// Range formula(e.g. sheet1!A1:A5, sheet2!B2:C20) /// Cells or empty /// public static IEnumerable SelectRange(this WorkBook book, string path) { var args = path.Split(new[] { '!' }, StringSplitOptions.RemoveEmptyEntries); if (args.Length != 2) throw new FormatException(); var sheet = book.FirstOrDefault(s => s.Name == args[0]); return sheet == null ? null : SelectRange(sheet, args[1]); } /// /// Recalculate cells' addresses /// /// Table public static void Recalculate(this Table table) { for (int i = 0; i < table.Count; i++) { for (int j = 0; j < table[i].Count; j++) { table[i][j].Address = new Address(j + 1, i + 1); } } } /// /// Make a non-rectangular table rectangular /// /// /// This also calls /// /// Table /// Expanded table public static Table Expand(this Table table) { var expanded = table.DeepClone(); var width = expanded.Max(row => row.Count); for (int i = 0; i < expanded.Count; i++) { var count = width - expanded[i].Count; while (count > 0) { expanded[i].Cells.Add(new Cell()); count--; } } Recalculate(expanded); return expanded; } /// /// Remove empty boundary cells from table then remove all empty lines /// /// /// This also calls /// /// Table /// Collapsed table public static Table Collapse(this Table table) { var collapsed = table.DeepClone(); for (int i = 0; i < collapsed.Count; i++) { for (int j = 0; j < collapsed[i].Count; j++) { if (!string.IsNullOrEmpty(collapsed[i][j].Text)) break; collapsed[i].Cells.RemoveAt(j--); } for (int j = collapsed[i].Count - 1; j >= 0; j--) { if (!string.IsNullOrEmpty(collapsed[i][j].Text)) break; collapsed[i].Cells.RemoveAt(j); } } for (int i = collapsed.Count - 1; i >= 0; i--) { if (collapsed[i].Count == 0) collapsed.Rows.RemoveAt(i); } Recalculate(collapsed); return collapsed; } /// /// Rotate table clockwise /// /// /// This also calls /// /// Table /// Rotated table public static Table Rotate(this Table table) { var expanded = Expand(table); if (expanded.Count == 0) { return expanded; } var height = expanded[0].Count; if (height == 0) { return expanded; } var width = expanded.Count; var rotated = table.DeepClone(); rotated.Rows.Clear(); for (int i = 0; i < height; i++) { var row = new Row(); for (int j = width - 1; j >= 0; j--) { row.Cells.Add(expanded[j][i]); } rotated.Rows.Add(row); } Recalculate(rotated); return rotated; } /// /// Check if a row is empty(with no cells or all cells' values being null or empty string) /// /// Row /// True if empty public static bool IsEmpty(this Row row) { return row.Count == 0 || row.All(col => string.IsNullOrEmpty(col.Text)); } } }