diff --git a/OpenXmlFormats/OOXMLFactory.cs b/OpenXmlFormats/OOXMLFactory.cs index 74753d409..c2cc7120b 100644 --- a/OpenXmlFormats/OOXMLFactory.cs +++ b/OpenXmlFormats/OOXMLFactory.cs @@ -6,7 +6,7 @@ namespace NPOI.OpenXmlFormats { - public class OOXMLFactory + public class OOXMLFactory where T : new() { XmlSerializer serializerObj = null; public OOXMLFactory() @@ -22,7 +22,7 @@ public T Parse(Stream stream) } public T Create() { - return (T)Activator.CreateInstance(typeof(T)); + return new T(); } } } diff --git a/OpenXmlFormats/Wordprocessing/Table.cs b/OpenXmlFormats/Wordprocessing/Table.cs index 5080e6e6e..3ce6e7d79 100644 --- a/OpenXmlFormats/Wordprocessing/Table.cs +++ b/OpenXmlFormats/Wordprocessing/Table.cs @@ -5784,7 +5784,7 @@ public CT_Row Copy() ctRow.textIdField = this.textIdField?.ToArray(); ctRow.trPrField = this.trPrField?.Copy(); ctRow.tblPrExField = this.tblPrExField?.Copy(); - ctRow.itemsElementNameField = this.itemsElementNameField?.Copy(); + ctRow.itemsElementNameField = this.itemsElementNameField?.ReflectionlessDeepCopy(); ctRow.itemsField = this.itemsField?.Copy(); return ctRow; } diff --git a/main/HPSF/MutableSection.cs b/main/HPSF/MutableSection.cs index 23a74417f..07b897553 100644 --- a/main/HPSF/MutableSection.cs +++ b/main/HPSF/MutableSection.cs @@ -557,7 +557,7 @@ public override Property[] Properties /// public void EnsureProperties() { - properties = (Property[])preprops.ToArray(typeof(Property)); + properties = preprops.ToArray(); } diff --git a/main/HSSF/EventUserModel/EventWorkbookBuilder.cs b/main/HSSF/EventUserModel/EventWorkbookBuilder.cs index f6269c0d7..ad3027007 100644 --- a/main/HSSF/EventUserModel/EventWorkbookBuilder.cs +++ b/main/HSSF/EventUserModel/EventWorkbookBuilder.cs @@ -23,6 +23,7 @@ namespace NPOI.HSSF.EventUserModel using NPOI.HSSF.Record; using NPOI.HSSF.UserModel; using System.Collections.Generic; + using NPOI.Util; /// /// When working with the EventUserModel, if you want to @@ -134,9 +135,7 @@ public SheetRecordCollectingListener(IHSSFListener childListener) /// public BoundSheetRecord[] GetBoundSheetRecords() { - return (BoundSheetRecord[])boundSheetRecords.ToArray( - typeof(BoundSheetRecord) - ); + return boundSheetRecords.ToArray(); } /// /// Gets the extern sheet records. @@ -144,9 +143,7 @@ public BoundSheetRecord[] GetBoundSheetRecords() /// public ExternSheetRecord[] GetExternSheetRecords() { - return (ExternSheetRecord[])externSheetRecords.ToArray( - typeof(ExternSheetRecord) - ); + return externSheetRecords.ToArray(); } /// /// Gets the SST record. diff --git a/main/HSSF/Model/LinkTable.cs b/main/HSSF/Model/LinkTable.cs index 065c021d4..2e75d559f 100644 --- a/main/HSSF/Model/LinkTable.cs +++ b/main/HSSF/Model/LinkTable.cs @@ -135,7 +135,7 @@ public ExternalBookBlock(RecordStream rs) { temp.Add(rs.GetNext()); } - _externalNameRecords = (ExternalNameRecord[])temp.ToArray(typeof(ExternalNameRecord)); + _externalNameRecords = temp.ToArray(); temp.Clear(); @@ -143,7 +143,7 @@ public ExternalBookBlock(RecordStream rs) { temp.Add(new CRNBlock(rs)); } - _crnBlocks = (CRNBlock[])temp.ToArray(typeof(CRNBlock)); + _crnBlocks = temp.ToArray(); } /** @@ -235,7 +235,7 @@ public LinkTable(List inputList, int startIndex, WorkbookRecordList work } //_externalBookBlocks = new ExternalBookBlock[temp.Count]; - _externalBookBlocks = (ExternalBookBlock[])temp.ToArray(typeof(ExternalBookBlock)); + _externalBookBlocks = temp.ToArray(); temp.Clear(); if (_externalBookBlocks.Length > 0) diff --git a/main/HSSF/Model/RowBlocksReader.cs b/main/HSSF/Model/RowBlocksReader.cs index acb5dd859..5f0482d03 100644 --- a/main/HSSF/Model/RowBlocksReader.cs +++ b/main/HSSF/Model/RowBlocksReader.cs @@ -24,6 +24,7 @@ namespace NPOI.HSSF.Model using NPOI.HSSF.Record; using NPOI.HSSF.Record.Aggregates; using System.Collections; + using NPOI.Util; /** * Segregates the 'Row Blocks' section of a single sheet into plain row/cell records and @@ -92,20 +93,19 @@ public RowBlocksReader(RecordStream rs) dest.Add(rec); prevRec = rec; } - SharedFormulaRecord[] sharedFormulaRecs = new SharedFormulaRecord[shFrmRecords.Count]; - List arrayRecs = new List(arrayRecords.Count); - List tableRecs = new List(tableRecords.Count); - sharedFormulaRecs = (SharedFormulaRecord[])shFrmRecords.ToArray(typeof(SharedFormulaRecord)); + SharedFormulaRecord[] sharedFormulaRecs; + List arrayRecs; + List tableRecs; + sharedFormulaRecs = shFrmRecords.ToArray(); - CellReference[] firstCells = new CellReference[firstCellRefs.Count]; - firstCells=firstCellRefs.ToArray(); - arrayRecs = new List((ArrayRecord[])arrayRecords.ToArray(typeof(ArrayRecord))); - tableRecs = new List((TableRecord[])tableRecords.ToArray(typeof(TableRecord))); + CellReference[] firstCells; + firstCells = firstCellRefs.ToArray(); + arrayRecs = new List(arrayRecords.ToArray()); + tableRecs = new List(tableRecords.ToArray()); _plainRecords = plainRecords; _sfm = SharedValueManager.Create(sharedFormulaRecs,firstCells, arrayRecs, tableRecs); - _mergedCellsRecords = new MergeCellsRecord[mergeCellRecords.Count]; - _mergedCellsRecords = (MergeCellsRecord[])mergeCellRecords.ToArray(typeof(MergeCellsRecord)); + _mergedCellsRecords = mergeCellRecords.ToArray(); } /** diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 38f00fbca..445d17b04 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -23,7 +23,6 @@ namespace NPOI.HSSF.UserModel using System.Collections.Generic; using System.Globalization; using System.IO; - using System.Reflection; using System.Security.Cryptography; using System.Text; using NPOI.DDF; diff --git a/main/SS/Formula/CellEvaluationFrame.cs b/main/SS/Formula/CellEvaluationFrame.cs index a4eaaf39e..cc9024686 100644 --- a/main/SS/Formula/CellEvaluationFrame.cs +++ b/main/SS/Formula/CellEvaluationFrame.cs @@ -22,6 +22,7 @@ namespace NPOI.SS.Formula using System.Collections; using System.Text; using NPOI.SS.Formula.Eval; + using NPOI.Util; /** * Stores details about the current evaluation of a cell.
@@ -69,7 +70,7 @@ private CellCacheEntry[] GetSensitiveInputCells() return CellCacheEntry.EMPTY_ARRAY; } CellCacheEntry[] result = new CellCacheEntry[nItems]; - result = (CellCacheEntry[])_sensitiveInputCells.ToArray(typeof(CellCacheEntry)); + result = _sensitiveInputCells.ToArray(); return result; } public void AddUsedBlankCell(int bookIndex, int sheetIndex, int rowIndex, int columnIndex) diff --git a/main/SS/Formula/CollaboratingWorkbooksEnvironment.cs b/main/SS/Formula/CollaboratingWorkbooksEnvironment.cs index 3410f8adb..57726d648 100644 --- a/main/SS/Formula/CollaboratingWorkbooksEnvironment.cs +++ b/main/SS/Formula/CollaboratingWorkbooksEnvironment.cs @@ -22,6 +22,7 @@ namespace NPOI.SS.Formula using System.Collections; using System.Collections.Generic; using NPOI.SS.UserModel; + using NPOI.Util; [Serializable] public class WorkbookNotFoundException : Exception @@ -169,7 +170,7 @@ private void UnhookOldEnvironments(WorkbookEvaluator[] evaluators) oldEnvs.Add(evaluators[i].GetEnvironment()); } CollaboratingWorkbooksEnvironment[] oldCWEs = new CollaboratingWorkbooksEnvironment[oldEnvs.Count]; - oldCWEs = (CollaboratingWorkbooksEnvironment[])oldEnvs.ToArray(typeof(CollaboratingWorkbooksEnvironment)); + oldCWEs = oldEnvs.ToArray(); for (int i = 0; i < oldCWEs.Length; i++) { oldCWEs[i].Unhook(); diff --git a/main/SS/Formula/EvaluationConditionalFormatRule.cs b/main/SS/Formula/EvaluationConditionalFormatRule.cs index 2da873de2..ab59452a8 100644 --- a/main/SS/Formula/EvaluationConditionalFormatRule.cs +++ b/main/SS/Formula/EvaluationConditionalFormatRule.cs @@ -1,5 +1,4 @@ -using EnumsNET; -using NPOI.SS.Formula.Eval; +using NPOI.SS.Formula.Eval; using NPOI.SS.Formula.Functions; using NPOI.SS.UserModel; using NPOI.SS.Util; diff --git a/main/SS/Formula/FormulaParser.cs b/main/SS/Formula/FormulaParser.cs index 238354994..9fde2ad6e 100644 --- a/main/SS/Formula/FormulaParser.cs +++ b/main/SS/Formula/FormulaParser.cs @@ -1805,7 +1805,7 @@ private ParseNode[] Arguments() throw expected("',' or ')'"); } } - ParseNode[] result = (ParseNode[])temp.ToArray(typeof(ParseNode)); + ParseNode[] result = temp.ToArray(); return result; } diff --git a/main/SS/Formula/OperationEvaluatorFactory.cs b/main/SS/Formula/OperationEvaluatorFactory.cs index 41df31acf..898a114b9 100644 --- a/main/SS/Formula/OperationEvaluatorFactory.cs +++ b/main/SS/Formula/OperationEvaluatorFactory.cs @@ -20,7 +20,6 @@ namespace NPOI.SS.Formula using System; using System.Collections; - using System.Reflection; using NPOI.SS.Formula.Eval; using NPOI.SS.Formula.Functions; @@ -69,13 +68,15 @@ private static Hashtable InitialiseInstancesMap() private static void Add(Hashtable m, OperationPtg ptgKey, Functions.Function instance) { + // REMOVE-REFLECTION: Reflection here is only to ensure singleton mode. Safe to remove since we are sure. + // make sure ptg has single private constructor because map lookups assume singleton keys - ConstructorInfo[] cc = ptgKey.GetType().GetConstructors(); + /*ConstructorInfo[] cc = ptgKey.GetType().GetConstructors(); if (cc.Length > 1 || (cc.Length > 0 && !cc[0].IsPrivate)) { throw new Exception("Failed to verify instance (" + ptgKey.GetType().Name + ") is a singleton."); - } + }*/ m[ptgKey] = instance; } diff --git a/main/SS/Formula/PTG/AttrPtg.cs b/main/SS/Formula/PTG/AttrPtg.cs index e08da45f7..5d6a79736 100644 --- a/main/SS/Formula/PTG/AttrPtg.cs +++ b/main/SS/Formula/PTG/AttrPtg.cs @@ -337,7 +337,7 @@ public override String ToFormulaString() return "UNKNOWN ATTRIBUTE"; } - public override Object Clone() + /*public override Object Clone() { int[] jt; if (_jumpTable == null) @@ -349,6 +349,6 @@ public override Object Clone() jt = (int[])_jumpTable.Clone(); } return new AttrPtg(field_1_options, field_2_data, jt, _chooseFuncOffset); - } + }*/ } } diff --git a/main/SS/Formula/PTG/OperandPtg.cs b/main/SS/Formula/PTG/OperandPtg.cs index 05f54252c..6084e7da0 100644 --- a/main/SS/Formula/PTG/OperandPtg.cs +++ b/main/SS/Formula/PTG/OperandPtg.cs @@ -37,7 +37,8 @@ public OperandPtg Copy() { try { - return (OperandPtg)Clone(); + // REMOVE-REFLECTION: After careful inspection, MemberwiseClone() should be enough for all built-in OpreandPtgs. + return (OperandPtg)MemberwiseClone(); } catch (NotSupportedException e) { diff --git a/main/SS/Formula/PTG/Ptg.cs b/main/SS/Formula/PTG/Ptg.cs index db7def91e..b59678830 100644 --- a/main/SS/Formula/PTG/Ptg.cs +++ b/main/SS/Formula/PTG/Ptg.cs @@ -44,7 +44,7 @@ namespace NPOI.SS.Formula.PTG * @author Jason Height (jheight at chariot dot net dot au) */ [Serializable] - public abstract class Ptg : ICloneable + public abstract class Ptg //: ICloneable { public static Ptg[] EMPTY_PTG_ARRAY = { }; @@ -187,7 +187,7 @@ private static Ptg[] ToPtgArray(ArrayList l) return EMPTY_PTG_ARRAY; } - Ptg[] result = (Ptg[])l.ToArray(typeof(Ptg)); + Ptg[] result = l.ToArray(); return result; } /** @@ -208,10 +208,10 @@ private static Ptg[] ToPtgArray(ArrayList l) // } // return (Ptg)Clone(); //} - public virtual object Clone() - { - return this.Copy(); - } + // private virtual object Clone() + //{ + //return this.Copy(); + //} /** * This method will return the same result as {@link #getEncodedSizeWithoutArrayData(Ptg[])} @@ -358,14 +358,14 @@ public char RVAType } } - #region ICloneable Members + /*#region ICloneable Members object ICloneable.Clone() { throw new NotImplementedException(); } - #endregion + #endregion*/ public static bool DoesFormulaReferToDeletedCell(Ptg[] ptgs) { diff --git a/main/SS/Formula/PTG/UnknownPtg.cs b/main/SS/Formula/PTG/UnknownPtg.cs index e18fc2941..16eda6a08 100644 --- a/main/SS/Formula/PTG/UnknownPtg.cs +++ b/main/SS/Formula/PTG/UnknownPtg.cs @@ -64,9 +64,9 @@ public override byte DefaultOperandClass get { return Ptg.CLASS_VALUE; } } - public override Object Clone() + /*public override Object Clone() { return new UnknownPtg(); - } + }*/ } } \ No newline at end of file diff --git a/main/SS/Util/AreaReference.cs b/main/SS/Util/AreaReference.cs index e45b2ac82..90cd8193d 100644 --- a/main/SS/Util/AreaReference.cs +++ b/main/SS/Util/AreaReference.cs @@ -21,6 +21,7 @@ namespace NPOI.SS.Util using System; using System.Text; using System.Collections; + using NPOI.Util; public class AreaReference { @@ -315,7 +316,7 @@ public static AreaReference[] GenerateContiguous(String reference) new AreaReference(t) ); } - return (AreaReference[])refs.ToArray(typeof(AreaReference)); + return refs.ToArray(); } /** @@ -373,7 +374,7 @@ public CellReference[] GetAllReferencedCells() refs.Add(ref1); } } - return (CellReference[])refs.ToArray(typeof(CellReference)); + return refs.ToArray(); } /** diff --git a/main/SS/Util/CellRangeAddressList.cs b/main/SS/Util/CellRangeAddressList.cs index 6f6f0f1dc..6ef754b91 100644 --- a/main/SS/Util/CellRangeAddressList.cs +++ b/main/SS/Util/CellRangeAddressList.cs @@ -146,7 +146,7 @@ public CellRangeAddress[] CellRangeAddresses get { CellRangeAddress[] result = - (CellRangeAddress[])_list.ToArray(typeof(CellRangeAddress)); + _list.ToArray(); return result; } } diff --git a/main/SS/Util/CellRangeUtil.cs b/main/SS/Util/CellRangeUtil.cs index ffa6d4a08..b005f64ae 100644 --- a/main/SS/Util/CellRangeUtil.cs +++ b/main/SS/Util/CellRangeUtil.cs @@ -21,6 +21,7 @@ namespace NPOI.SS.Util using System.Collections; using NPOI.SS.Util; using System.Collections.Generic; + using NPOI.Util; /** * Utility class that builds on {@link CellRangeAddress} @@ -297,7 +298,7 @@ private static CellRangeAddress[] MergeRanges(CellRangeAddress range1, CellRange private static CellRangeAddress[] ToArray(ArrayList temp) { CellRangeAddress[] result = new CellRangeAddress[temp.Count]; - result = (CellRangeAddress[])temp.ToArray(typeof(CellRangeAddress)); + result = temp.ToArray(); return result; } diff --git a/main/SS/Util/SSCellRange.cs b/main/SS/Util/SSCellRange.cs index 5c7286fae..d1cd15e8e 100644 --- a/main/SS/Util/SSCellRange.cs +++ b/main/SS/Util/SSCellRange.cs @@ -54,8 +54,7 @@ public static SSCellRange Create(int firstRow, int firstColumn, int height, i throw new ArgumentException("Array size mismatch."); } - K[] flattenedArray = (K[])Array.CreateInstance(cellClass, nItems); - flattenedArray=flattenedList.ToArray(); + K[] flattenedArray = flattenedList.ToArray(); return new SSCellRange(firstRow, firstColumn, height, width, flattenedArray); } @@ -155,12 +154,10 @@ public K[] FlattenedCells public K[][] Cells { get { - Type itemCls = _flattenedArray.GetType(); - K[][] result = (K[][])Array.CreateInstance(itemCls, _height); - itemCls = itemCls.GetElementType(); + K[][] result = new K[_height][]; for (int r = _height - 1; r >= 0; r--) { - K[] row = (K[])Array.CreateInstance(itemCls, _width); + K[] row = new K[_width]; int flatIndex = _width * r; Array.Copy(_flattenedArray, flatIndex, row, 0, _width); } diff --git a/main/Util/CollectionExtensions.cs b/main/Util/CollectionExtensions.cs new file mode 100644 index 000000000..281bd9b23 --- /dev/null +++ b/main/Util/CollectionExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPOI.Util +{ + /// + /// This class provides helper methods that reduce dynamic code / reflection use, for better AOT performance. + /// + internal static class CollectionExtensions + { + public static T[] ToArray(this ArrayList arrayList) + { + if (arrayList.Count == 0) + return Array.Empty(); + + var array = new T[arrayList.Count]; + arrayList.CopyTo(array); + + return array; + } + } +} diff --git a/main/Util/HexRead.cs b/main/Util/HexRead.cs index d057c1d5b..81213b8ac 100644 --- a/main/Util/HexRead.cs +++ b/main/Util/HexRead.cs @@ -168,7 +168,7 @@ public static byte[] ReadData( Stream stream, int eofChar ) } } } - byte[] polished = (byte[]) bytes.ToArray(typeof(byte) ); + byte[] polished = bytes.ToArray(); //byte[] rval = new byte[polished.Length]; //for ( int j = 0; j < polished.Length; j++ ) //{ diff --git a/main/Util/ObjectExtensions.cs b/main/Util/ObjectExtensions.cs index b732237de..1ddf8488a 100644 --- a/main/Util/ObjectExtensions.cs +++ b/main/Util/ObjectExtensions.cs @@ -2,10 +2,21 @@ using System.Reflection; using System; using NPOI.Util.ArrayExtensions; +using System.Diagnostics.CodeAnalysis; + +// REMOVE-REFLECTION: The DeepCopy function is difficult to remove. +// I tested derived classes of Ptg and removes Ptg's IClonable interface and refactored OperandPtg's deepcopy logic. +// However, XWPFTableRow.CloneRow relies on deepcopy and I would recommend `rd.xml` to let AOT engine keep metadata. +// Or turn to some sort of source generators, generating deepcopy code for all CT_* classes. namespace NPOI.Util { - public static class ObjectExtensions + public static partial class ObjectExtensions + { + public static List ReflectionlessDeepCopy(this List list) where T : unmanaged + => new List(list); + } + public static partial class ObjectExtensions { private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); @@ -15,24 +26,32 @@ public static bool IsPrimitive(this Type type) return (type.IsValueType & type.IsPrimitive); } - public static Object Copy(this Object originalObject) + public static T Copy< +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] +# endif + T>(this T originalObject) where T : class { return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer())); } - private static Object InternalCopy(Object originalObject, IDictionary visited) + private static T InternalCopy< +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] +# endif + T>(T originalObject, Dictionary visited) where T : class { if (originalObject == null) return null; - var typeToReflect = originalObject.GetType(); + var typeToReflect = typeof(T); if (IsPrimitive(typeToReflect)) return originalObject; - if (visited.ContainsKey(originalObject)) return visited[originalObject]; + if (visited.ContainsKey(originalObject)) return (T)visited[originalObject]; if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null; - var cloneObject = CloneMethod.Invoke(originalObject, null); - if (typeToReflect.IsArray) + var cloneObject = (T)CloneMethod.Invoke(originalObject, null); + if (originalObject is Array) { var arrayType = typeToReflect.GetElementType(); if (IsPrimitive(arrayType) == false) { - Array clonedArray = (Array)cloneObject; + Array clonedArray = (Array)(object)cloneObject; clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices)); } @@ -43,7 +62,11 @@ private static Object InternalCopy(Object originalObject, IDictionary visited, object cloneObject, Type typeToReflect) + private static void RecursiveCopyBaseTypePrivateFields(object originalObject, Dictionary visited, object cloneObject, +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] +#endif + Type typeToReflect) { if (typeToReflect.BaseType != null) { @@ -52,7 +75,12 @@ private static void RecursiveCopyBaseTypePrivateFields(object originalObject, ID } } - private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) + private static void CopyFields(object originalObject, Dictionary visited, object cloneObject, +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] +#endif + Type typeToReflect, + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) { foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags)) { @@ -63,10 +91,6 @@ private static void CopyFields(object originalObject, IDictionary(this T original) - { - return (T)Copy((Object)original); - } } public class ReferenceEqualityComparer : EqualityComparer diff --git a/ooxml/POIXMLDocument.cs b/ooxml/POIXMLDocument.cs index c8a069c64..f5285da34 100644 --- a/ooxml/POIXMLDocument.cs +++ b/ooxml/POIXMLDocument.cs @@ -215,7 +215,16 @@ public void Write(Stream stream) if (!this.GetProperties().CustomProperties.Contains("Generator")) this.GetProperties().CustomProperties.AddProperty("Generator", "NPOI"); if (!this.GetProperties().CustomProperties.Contains("Generator Version")) - this.GetProperties().CustomProperties.AddProperty("Generator Version", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); + // REMOVE-REFLECTION: Reflection here is used to extract assembly version. + // This will work on AOT. + try + { + this.GetProperties().CustomProperties.AddProperty("Generator Version", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); + } + catch + { + // Silent fail for native AOT. + } //force all children to commit their Changes into the underlying OOXML Package List context = new List(); OnSave(context); diff --git a/ooxml/Util/CollectionExtensions.cs b/ooxml/Util/CollectionExtensions.cs new file mode 100644 index 000000000..ef6a51d53 --- /dev/null +++ b/ooxml/Util/CollectionExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPOI.OOXML.Util +{ + /// + /// This class provides helper methods that reduce dynamic code / reflection use, for better AOT performance. + /// + internal static class CollectionExtensions + { + public static T[] ToArray(this ArrayList arrayList) + { + if (arrayList.Count == 0) + return Array.Empty(); + + var array = new T[arrayList.Count]; + arrayList.CopyTo(array); + + return array; + } + } +} diff --git a/ooxml/Util/EnumConverter.cs b/ooxml/Util/EnumConverter.cs index 940e1c668..ad0dff692 100644 --- a/ooxml/Util/EnumConverter.cs +++ b/ooxml/Util/EnumConverter.cs @@ -3,7 +3,6 @@ using System.Text; using NPOI.XWPF.UserModel; using NPOI.OpenXmlFormats.Wordprocessing; -using System.Reflection; namespace NPOI.Util { diff --git a/ooxml/XSSF/UserModel/XSSFTable.cs b/ooxml/XSSF/UserModel/XSSFTable.cs index 3a1e350cf..3fdcac59b 100644 --- a/ooxml/XSSF/UserModel/XSSFTable.cs +++ b/ooxml/XSSF/UserModel/XSSFTable.cs @@ -31,6 +31,7 @@ limitations under the License. using NPOI.SS; using System.Linq; using NPOI.OOXML.XSSF.UserModel; +using NPOI.OOXML.Util; namespace NPOI.XSSF.UserModel { @@ -174,10 +175,8 @@ public String GetCommonXpath() if (!commonTokens.GetValue(i).Equals(tokens[i])) { ArrayList subCommonTokens = Arrays.AsList(commonTokens).GetRange(0, i); - commonTokens = subCommonTokens.ToArray(typeof(string)); + commonTokens = subCommonTokens.ToArray(); break; - - } } } diff --git a/ooxml/XSSF/Util/XmlEnumParser.cs b/ooxml/XSSF/Util/XmlEnumParser.cs index 2e575ad34..f43afea02 100644 --- a/ooxml/XSSF/Util/XmlEnumParser.cs +++ b/ooxml/XSSF/Util/XmlEnumParser.cs @@ -1,24 +1,31 @@ using NPOI.OpenXmlFormats.Spreadsheet; +using NPOI.Util; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text; using System.Xml.Serialization; +// REMOVE-REFLECTION: Reflection used is unremovable but the class is obsolete and not used elsewhere. +// AOT should automatically trim this class if the end user is not using it. + namespace NPOI.XSSF.Util { [Obsolete] - public class XmlEnumParser + public class XmlEnumParser< +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] +#endif + TReturn> where TReturn : struct, Enum { private static Dictionary values; static XmlEnumParser() { Type type = typeof(TReturn); - MemberInfo[] members = type.GetMembers(BindingFlags.Public | BindingFlags.Static); - string[] names = Enum.GetNames(type); + MemberInfo[] members = type.GetFields(BindingFlags.Public | BindingFlags.Static); values = new Dictionary(); - Array array = type.GetEnumValues(); foreach (var member in members) { object[] cas = member.GetCustomAttributes(typeof(XmlEnumAttribute), false);