blob: 5f7053dc164c09e51e77ea4813fd03a04a512894 [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 21-MAR-2011
* Jan Källman License changed GPL-->LGPL 2011-12-16
*******************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
namespace OfficeOpenXml.Table.PivotTable;
/// <summary>
/// Base collection class for pivottable fields
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExcelPivotTableFieldCollectionBase<T> : IEnumerable<T> {
protected ExcelPivotTable _table;
internal List<T> _list = new();
internal ExcelPivotTableFieldCollectionBase(ExcelPivotTable table) {
_table = table;
}
public IEnumerator<T> GetEnumerator() {
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return _list.GetEnumerator();
}
public int Count => _list.Count;
internal void AddInternal(T field) {
_list.Add(field);
}
internal void Clear() {
_list.Clear();
}
public T this[int index] {
get {
if (index < 0 || index >= _list.Count) {
throw (new ArgumentOutOfRangeException("Index out of range"));
}
return _list[index];
}
}
}
public class ExcelPivotTableFieldCollection
: ExcelPivotTableFieldCollectionBase<ExcelPivotTableField> {
internal ExcelPivotTableFieldCollection(ExcelPivotTable table, string topNode)
: base(table) {}
/// <summary>
/// Indexer by name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public ExcelPivotTableField this[string name] {
get {
foreach (var field in _list) {
if (field.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) {
return field;
}
}
return null;
}
}
/// <summary>
/// Returns the date group field.
/// </summary>
/// <param name="groupBy">The type of grouping</param>
/// <returns>The matching field. If none is found null is returned</returns>
public ExcelPivotTableField GetDateGroupField(eDateGroupBy groupBy) {
foreach (var fld in _list) {
if (fld.Grouping is ExcelPivotTableFieldDateGroup group && (group.GroupBy) == groupBy) {
return fld;
}
}
return null;
}
/// <summary>
/// Returns the numeric group field.
/// </summary>
/// <returns>The matching field. If none is found null is returned</returns>
public ExcelPivotTableField GetNumericGroupField() {
foreach (var fld in _list) {
if (fld.Grouping is ExcelPivotTableFieldNumericGroup) {
return fld;
}
}
return null;
}
}
/// <summary>
/// Collection class for Row and column fields in a Pivottable
/// </summary>
public class ExcelPivotTableRowColumnFieldCollection
: ExcelPivotTableFieldCollectionBase<ExcelPivotTableField> {
internal string _topNode;
internal ExcelPivotTableRowColumnFieldCollection(ExcelPivotTable table, string topNode)
: base(table) {
_topNode = topNode;
}
/// <summary>
/// Add a new row/column field
/// </summary>
/// <param name="field">The field</param>
/// <returns>The new field</returns>
public ExcelPivotTableField Add(ExcelPivotTableField field) {
SetFlag(field, true);
_list.Add(field);
return field;
}
/// <summary>
/// Insert a new row/column field
/// </summary>
/// <param name="field">The field</param>
/// <param name="index">The position to insert the field</param>
/// <returns>The new field</returns>
internal ExcelPivotTableField Insert(ExcelPivotTableField field, int index) {
SetFlag(field, true);
_list.Insert(index, field);
return field;
}
private void SetFlag(ExcelPivotTableField field, bool value) {
switch (_topNode) {
case "rowFields":
if (field.IsColumnField || field.IsPageField) {
throw (new(
"This field is a column or page field. Can't add it to the RowFields collection"));
}
field.IsRowField = value;
field.Axis = ePivotFieldAxis.Row;
break;
case "colFields":
if (field.IsRowField || field.IsPageField) {
throw (new(
"This field is a row or page field. Can't add it to the ColumnFields collection"));
}
field.IsColumnField = value;
field.Axis = ePivotFieldAxis.Column;
break;
case "pageFields":
if (field.IsColumnField || field.IsRowField) {
throw (new("Field is a column or row field. Can't add it to the PageFields collection"));
}
if (_table.Address._fromRow < 3) {
throw (new(
string.Format(
"A pivot table with page fields must be located above row 3. Currenct location is {0}",
_table.Address.Address)));
}
field.IsPageField = value;
field.Axis = ePivotFieldAxis.Page;
break;
case "dataFields":
break;
}
}
/// <summary>
/// Remove a field
/// </summary>
/// <param name="field"></param>
public void Remove(ExcelPivotTableField field) {
if (!_list.Contains(field)) {
throw new ArgumentException("Field not in collection");
}
SetFlag(field, false);
_list.Remove(field);
}
/// <summary>
/// Remove a field at a specific position
/// </summary>
/// <param name="index"></param>
public void RemoveAt(int index) {
if (index > -1 && index < _list.Count) {
throw (new IndexOutOfRangeException());
}
SetFlag(_list[index], false);
_list.RemoveAt(index);
}
}
/// <summary>
/// Collection class for data fields in a Pivottable
/// </summary>
public class ExcelPivotTableDataFieldCollection
: ExcelPivotTableFieldCollectionBase<ExcelPivotTableDataField> {
internal ExcelPivotTableDataFieldCollection(ExcelPivotTable table)
: base(table) {}
/// <summary>
/// Add a new datafield
/// </summary>
/// <param name="field">The field</param>
/// <returns>The new datafield</returns>
public ExcelPivotTableDataField Add(ExcelPivotTableField field) {
var dataFieldsNode = field.TopNode.SelectSingleNode(
"../../d:dataFields",
field.NameSpaceManager);
if (dataFieldsNode == null) {
_table.CreateNode("d:dataFields");
dataFieldsNode = field.TopNode.SelectSingleNode("../../d:dataFields", field.NameSpaceManager);
}
XmlElement node = _table.PivotTableXml.CreateElement("dataField", ExcelPackage._schemaMain);
node.SetAttribute("fld", field.Index.ToString());
dataFieldsNode.AppendChild(node);
//XmlElement node = field.AppendField(dataFieldsNode, field.Index, "dataField", "fld");
field.SetXmlNodeBool("@dataField", true, false);
var dataField = new ExcelPivotTableDataField(field.NameSpaceManager, node, field);
ValidateDupName(dataField);
_list.Add(dataField);
return dataField;
}
private void ValidateDupName(ExcelPivotTableDataField dataField) {
if (ExistsDfName(dataField.Field.Name, null)) {
var index = 2;
string name;
do {
name = dataField.Field.Name + "_" + index++;
} while (ExistsDfName(name, null));
dataField.Name = name;
}
}
internal bool ExistsDfName(string name, ExcelPivotTableDataField datafield) {
foreach (var df in _list) {
if (((!string.IsNullOrEmpty(df.Name)
&& df.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
|| (string.IsNullOrEmpty(df.Name)
&& df.Field.Name.Equals(
name,
StringComparison.InvariantCultureIgnoreCase))))
&& datafield != df) {
return true;
}
}
return false;
}
/// <summary>
/// Remove a datafield
/// </summary>
/// <param name="dataField"></param>
public void Remove(ExcelPivotTableDataField dataField) {
XmlElement node =
dataField.Field.TopNode.SelectSingleNode(
string.Format("../../d:dataFields/d:dataField[@fld={0}]", dataField.Index),
dataField.NameSpaceManager) as XmlElement;
if (node != null) {
node.ParentNode.RemoveChild(node);
}
_list.Remove(dataField);
}
}