|  | /******************************************************************************* | 
|  | * 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		    Initial Release		       2009-10-01 | 
|  | * Jan Källman		    License changed GPL-->LGPL 2011-12-27 | 
|  | *******************************************************************************/ | 
|  |  | 
|  | using System; | 
|  | using System.Collections; | 
|  | using System.Collections.Generic; | 
|  | using System.Text.RegularExpressions; | 
|  | using System.Xml; | 
|  | using OfficeOpenXml.Utils; | 
|  |  | 
|  | namespace OfficeOpenXml; | 
|  |  | 
|  | /// <summary> | 
|  | /// The collection of worksheets for the workbook | 
|  | /// </summary> | 
|  | public class ExcelWorksheets : XmlHelper, IEnumerable<ExcelWorksheet> { | 
|  | private readonly ExcelPackage _pck; | 
|  | private readonly ExcelWorkbook _workbook; | 
|  | private Dictionary<int, ExcelWorksheet> _worksheets = new(); | 
|  | private readonly XmlNamespaceManager _namespaceManager; | 
|  |  | 
|  | internal ExcelWorksheets( | 
|  | ExcelPackage pck, | 
|  | ExcelWorkbook workbook, | 
|  | XmlNamespaceManager nsm, | 
|  | XmlNode topNode) | 
|  | : base(nsm, topNode) { | 
|  | _pck = pck; | 
|  | _workbook = workbook; | 
|  | _namespaceManager = nsm; | 
|  | int positionId = 1; | 
|  |  | 
|  | foreach (XmlNode sheetNode in topNode.ChildNodes) { | 
|  | if (sheetNode.NodeType == XmlNodeType.Element) { | 
|  | string name = sheetNode.Attributes["name"].Value; | 
|  | //Get the relationship id | 
|  | string relId = sheetNode.Attributes["r:id"].Value; | 
|  | int sheetId = Convert.ToInt32(sheetNode.Attributes["sheetId"].Value); | 
|  |  | 
|  | //Hidden property | 
|  | eWorkSheetHidden hidden = eWorkSheetHidden.Visible; | 
|  | XmlNode attr = sheetNode.Attributes["state"]; | 
|  | if (attr != null) { | 
|  | hidden = TranslateHidden(attr.Value); | 
|  | } | 
|  |  | 
|  | var sheetRelation = _workbook.Part.GetRelationship(relId); | 
|  | Uri uriWorksheet = UriHelper.ResolvePartUri( | 
|  | ExcelWorkbook.WorkbookUri, | 
|  | sheetRelation.TargetUri); | 
|  |  | 
|  | //add the worksheet | 
|  | if (sheetRelation.RelationshipType.EndsWith("chartsheet")) { | 
|  | _worksheets.Add( | 
|  | positionId, | 
|  | new ExcelChartsheet( | 
|  | _namespaceManager, | 
|  | pck, | 
|  | _workbook, | 
|  | uriWorksheet, | 
|  | name, | 
|  | sheetId, | 
|  | positionId, | 
|  | hidden)); | 
|  | } else { | 
|  | _worksheets.Add( | 
|  | positionId, | 
|  | new( | 
|  | _namespaceManager, | 
|  | pck, | 
|  | _workbook, | 
|  | uriWorksheet, | 
|  | name, | 
|  | sheetId, | 
|  | positionId, | 
|  | hidden)); | 
|  | } | 
|  | positionId++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private eWorkSheetHidden TranslateHidden(string value) { | 
|  | switch (value) { | 
|  | case "hidden": | 
|  | return eWorkSheetHidden.Hidden; | 
|  | case "veryHidden": | 
|  | return eWorkSheetHidden.VeryHidden; | 
|  | default: | 
|  | return eWorkSheetHidden.Visible; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns the number of worksheets in the workbook | 
|  | /// </summary> | 
|  | public int Count => (_worksheets.Count); | 
|  |  | 
|  | private const string _errDupWorksheet = | 
|  | "A worksheet with this name already exists in the workbook"; | 
|  | internal const string _worksheetContentType = | 
|  | "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; | 
|  |  | 
|  | /// <summary> | 
|  | /// Foreach support | 
|  | /// </summary> | 
|  | /// <returns>An enumerator</returns> | 
|  | public IEnumerator<ExcelWorksheet> GetEnumerator() { | 
|  | return (_worksheets.Values.GetEnumerator()); | 
|  | } | 
|  |  | 
|  | IEnumerator IEnumerable.GetEnumerator() { | 
|  | return (_worksheets.Values.GetEnumerator()); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds a new blank worksheet. | 
|  | /// </summary> | 
|  | /// <param name="name">The name of the workbook</param> | 
|  | public ExcelWorksheet Add(string name) { | 
|  | name = ValidateFixSheetName(name); | 
|  | if (GetByName(name) != null) { | 
|  | throw (new InvalidOperationException(_errDupWorksheet + " : " + name)); | 
|  | } | 
|  | GetSheetUri(ref name, out var sheetId, out var uriWorksheet, false); | 
|  |  | 
|  | // Create the new worksheet | 
|  | var rel = _pck.CreateXmlDocument( | 
|  | uriWorksheet, | 
|  | _worksheetContentType, | 
|  | ExcelPackage._schemaRelationships + "/worksheet", | 
|  | CreateNewWorksheet(false)); | 
|  |  | 
|  | // Add worksheet to the workbook | 
|  | XmlElement worksheetNode = _workbook.WorkbookXml.CreateElement( | 
|  | "sheet", | 
|  | ExcelPackage._schemaMain); | 
|  | worksheetNode.SetAttribute("name", name); | 
|  | worksheetNode.SetAttribute("sheetId", sheetId.ToString()); | 
|  | worksheetNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id); | 
|  | TopNode.AppendChild(worksheetNode); | 
|  |  | 
|  | int positionId = _worksheets.Count + 1; | 
|  |  | 
|  | ExcelWorksheet worksheet = new( | 
|  | _namespaceManager, | 
|  | _pck, | 
|  | _workbook, | 
|  | uriWorksheet, | 
|  | name, | 
|  | sheetId, | 
|  | positionId, | 
|  | eWorkSheetHidden.Visible); | 
|  |  | 
|  | _worksheets.Add(positionId, worksheet); | 
|  | return worksheet; | 
|  | } | 
|  |  | 
|  | private void GetSheetUri(ref string name, out int sheetId, out Uri uriWorksheet, bool isChart) { | 
|  | name = ValidateFixSheetName(name); | 
|  |  | 
|  | //First find maximum existing sheetID | 
|  | sheetId = 0; | 
|  | foreach (var ws in this) { | 
|  | if (ws.SheetID > sheetId) { | 
|  | sheetId = ws.SheetID; | 
|  | } | 
|  | } | 
|  | // we now have the max existing values, so add one | 
|  | sheetId++; | 
|  |  | 
|  | // add the new worksheet to the package | 
|  | if (isChart) { | 
|  | uriWorksheet = new("/xl/chartsheets/chartsheet" + sheetId + ".xml", UriKind.Relative); | 
|  | } else { | 
|  | uriWorksheet = new("/xl/worksheets/sheet" + sheetId + ".xml", UriKind.Relative); | 
|  | } | 
|  | } | 
|  |  | 
|  | internal string ValidateFixSheetName(string name) { | 
|  | //remove invalid characters | 
|  | if (ValidateName(name)) { | 
|  | if (name.IndexOf(':') > -1) { | 
|  | name = name.Replace(":", " "); | 
|  | } | 
|  | if (name.IndexOf('/') > -1) { | 
|  | name = name.Replace("/", " "); | 
|  | } | 
|  | if (name.IndexOf('\\') > -1) { | 
|  | name = name.Replace("\\", " "); | 
|  | } | 
|  | if (name.IndexOf('?') > -1) { | 
|  | name = name.Replace("?", " "); | 
|  | } | 
|  | if (name.IndexOf('[') > -1) { | 
|  | name = name.Replace("[", " "); | 
|  | } | 
|  | if (name.IndexOf(']') > -1) { | 
|  | name = name.Replace("]", " "); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (name.Trim() == "") { | 
|  | throw new ArgumentException("The worksheet can not have an empty name"); | 
|  | } | 
|  | if (name.Length > 31) { | 
|  | name = name.Substring(0, 31); //A sheet can have max 31 char's | 
|  | } | 
|  | return name; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Validate the sheetname | 
|  | /// </summary> | 
|  | /// <param name="name">The Name</param> | 
|  | /// <returns>True if valid</returns> | 
|  | private bool ValidateName(string name) { | 
|  | return Regex.IsMatch(name, @":|\?|/|\\|\[|\]"); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Creates the XML document representing a new empty worksheet | 
|  | /// </summary> | 
|  | /// <returns></returns> | 
|  | internal XmlDocument CreateNewWorksheet(bool isChart) { | 
|  | XmlDocument xmlDoc = new XmlDocument(); | 
|  | XmlElement elemWs = xmlDoc.CreateElement( | 
|  | isChart ? "chartsheet" : "worksheet", | 
|  | ExcelPackage._schemaMain); | 
|  | elemWs.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships); | 
|  | xmlDoc.AppendChild(elemWs); | 
|  |  | 
|  | if (isChart) { | 
|  | XmlElement elemSheetPr = xmlDoc.CreateElement("sheetPr", ExcelPackage._schemaMain); | 
|  | elemWs.AppendChild(elemSheetPr); | 
|  |  | 
|  | XmlElement elemSheetViews = xmlDoc.CreateElement("sheetViews", ExcelPackage._schemaMain); | 
|  | elemWs.AppendChild(elemSheetViews); | 
|  |  | 
|  | XmlElement elemSheetView = xmlDoc.CreateElement("sheetView", ExcelPackage._schemaMain); | 
|  | elemSheetView.SetAttribute("workbookViewId", "0"); | 
|  | elemSheetView.SetAttribute("zoomToFit", "1"); | 
|  |  | 
|  | elemSheetViews.AppendChild(elemSheetView); | 
|  | } else { | 
|  | XmlElement elemSheetViews = xmlDoc.CreateElement("sheetViews", ExcelPackage._schemaMain); | 
|  | elemWs.AppendChild(elemSheetViews); | 
|  |  | 
|  | XmlElement elemSheetView = xmlDoc.CreateElement("sheetView", ExcelPackage._schemaMain); | 
|  | elemSheetView.SetAttribute("workbookViewId", "0"); | 
|  | elemSheetViews.AppendChild(elemSheetView); | 
|  |  | 
|  | XmlElement elemSheetFormatPr = xmlDoc.CreateElement( | 
|  | "sheetFormatPr", | 
|  | ExcelPackage._schemaMain); | 
|  | elemSheetFormatPr.SetAttribute("defaultRowHeight", "15"); | 
|  | elemWs.AppendChild(elemSheetFormatPr); | 
|  |  | 
|  | XmlElement elemSheetData = xmlDoc.CreateElement("sheetData", ExcelPackage._schemaMain); | 
|  | elemWs.AppendChild(elemSheetData); | 
|  | } | 
|  | return xmlDoc; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns the worksheet at the specified position. | 
|  | /// </summary> | 
|  | /// <param name="positionId">The position of the worksheet. 1-base</param> | 
|  | /// <returns></returns> | 
|  | public ExcelWorksheet this[int positionId] { | 
|  | get { | 
|  | if (_worksheets.ContainsKey(positionId)) { | 
|  | return _worksheets[positionId]; | 
|  | } | 
|  | throw (new IndexOutOfRangeException("Worksheet position out of range.")); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns the worksheet matching the specified name | 
|  | /// </summary> | 
|  | /// <param name="name">The name of the worksheet</param> | 
|  | /// <returns></returns> | 
|  | public ExcelWorksheet this[string name] => GetByName(name); | 
|  |  | 
|  | internal ExcelWorksheet GetBySheetId(int localSheetId) { | 
|  | foreach (ExcelWorksheet ws in this) { | 
|  | if (ws.SheetID == localSheetId) { | 
|  | return ws; | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private ExcelWorksheet GetByName(string name) { | 
|  | if (string.IsNullOrEmpty(name)) { | 
|  | return null; | 
|  | } | 
|  | ExcelWorksheet xlWorksheet = null; | 
|  | foreach (ExcelWorksheet worksheet in _worksheets.Values) { | 
|  | if (worksheet.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) { | 
|  | xlWorksheet = worksheet; | 
|  | } | 
|  | } | 
|  | return (xlWorksheet); | 
|  | } | 
|  | } // end class Worksheets |