|  | /******************************************************************************* | 
|  | * 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); | 
|  | } | 
|  | } |