diff --git a/main/HSSF/Extractor/OldExcelExtractor.cs b/main/HSSF/Extractor/OldExcelExtractor.cs index 62c02c491..e42e83b58 100644 --- a/main/HSSF/Extractor/OldExcelExtractor.cs +++ b/main/HSSF/Extractor/OldExcelExtractor.cs @@ -39,6 +39,7 @@ namespace NPOI.HSSF.Extractor */ public class OldExcelExtractor { + private const int FILE_PASS_RECORD_SID = 0x2f; private RecordInputStream ris; // sometimes we hold the stream here and thus need to ensure it is closed at some point @@ -247,6 +248,9 @@ public String Text switch (sid) { + case FILE_PASS_RECORD_SID: + throw new EncryptedDocumentException("Encryption not supported for Old Excel files"); + // Biff 5+ only, no sheet names in older formats case OldSheetRecord.sid: OldSheetRecord shr = new OldSheetRecord(ris); diff --git a/main/Util/StringUtil.cs b/main/Util/StringUtil.cs index 4bb30c4a8..bf882fed9 100644 --- a/main/Util/StringUtil.cs +++ b/main/Util/StringUtil.cs @@ -854,5 +854,28 @@ public static String Join(String separator, params object[] array) { return Join(array, separator); } + + /** + * Count number of occurrences of needle in haystack + * Has same signature as org.apache.commons.lang3.StringUtils#countMatches + * + * @param haystack the CharSequence to check, may be null + * @param needle the character to count the quantity of + * @return the number of occurrences, 0 if the CharSequence is null + */ + public static int CountMatches(string haystack, char needle) + { + if (haystack == null) return 0; + int count = 0; + int length = haystack.Length; + for (int i = 0; i < length; i++) + { + if (haystack[i] == needle) + { + count++; + } + } + return count; + } } } diff --git a/ooxml/POIXMLDocumentPart.cs b/ooxml/POIXMLDocumentPart.cs index 8521c3587..17970d33a 100644 --- a/ooxml/POIXMLDocumentPart.cs +++ b/ooxml/POIXMLDocumentPart.cs @@ -14,6 +14,7 @@ the License. You may obtain a copy of the License at See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + namespace NPOI { using NPOI.Util; @@ -22,7 +23,7 @@ namespace NPOI using System.Collections.Generic; using System.IO; using NPOI.OpenXml4Net.Exceptions; -using System.Xml; + using System.Xml; using NPOI.OpenXml4Net.OPC.Internal; using System.Diagnostics; @@ -35,6 +36,7 @@ namespace NPOI * * @author Yegor Kozlov */ + public class POIXMLDocumentPart { private static POILogger logger = POILogFactory.GetLogger(typeof(POIXMLDocumentPart)); @@ -50,11 +52,12 @@ public class POIXMLDocumentPart * The child document parts may only belong to one parent, but it's often referenced by other * parents too, having varying {@link PackageRelationship#getId() relationship ids} pointing to it. */ + public class RelationPart { private PackageRelationship relationship; private POIXMLDocumentPart documentPart; - + internal RelationPart(PackageRelationship relationship, POIXMLDocumentPart documentPart) { this.relationship = relationship; @@ -64,6 +67,7 @@ internal RelationPart(PackageRelationship relationship, POIXMLDocumentPart docum /** * @return the cached relationship, which uniquely identifies this child document part within the parent */ + public PackageRelationship Relationship { get @@ -75,7 +79,8 @@ public PackageRelationship Relationship /** * @return the child document part */ - public T GetDocumentPart() where T: POIXMLDocumentPart + + public T GetDocumentPart() where T : POIXMLDocumentPart { return (T)documentPart; } @@ -95,19 +100,19 @@ public POIXMLDocumentPart DocumentPart */ private int relationCounter = 0; - int IncrementRelationCounter() + private int IncrementRelationCounter() { relationCounter++; return relationCounter; } - int DecrementRelationCounter() + private int DecrementRelationCounter() { relationCounter--; return relationCounter; } - int GetRelationCounter() + private int GetRelationCounter() { return relationCounter; } @@ -115,6 +120,7 @@ int GetRelationCounter() /** * Construct POIXMLDocumentPart representing a "core document" namespace part. */ + public POIXMLDocumentPart(OPCPackage pkg) : this(pkg, PackageRelationshipTypes.CORE_DOCUMENT) { @@ -123,6 +129,7 @@ public POIXMLDocumentPart(OPCPackage pkg) /** * Construct POIXMLDocumentPart representing a custom "core document" package part. */ + public POIXMLDocumentPart(OPCPackage pkg, String coreDocumentRel) : this(GetPartFromOPCPackage(pkg, coreDocumentRel)) { @@ -134,9 +141,11 @@ public POIXMLDocumentPart(OPCPackage pkg, String coreDocumentRel) * * @see #CreateRelationship(POIXMLRelation, POIXMLFactory, int, bool) */ + public POIXMLDocumentPart() { } + /** * Creates an POIXMLDocumentPart representing the given package part and relationship. * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. @@ -146,11 +155,12 @@ public POIXMLDocumentPart() * * @since POI 3.14-Beta1 */ + public POIXMLDocumentPart(PackagePart part) : this(null, part) { - } + /** * Creates an POIXMLDocumentPart representing the given package part, relationship and parent * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. @@ -161,11 +171,13 @@ public POIXMLDocumentPart(PackagePart part) * * @since POI 3.14-Beta1 */ + public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part) { this.packagePart = part; this.parent = parent; } + /** * Creates an POIXMLDocumentPart representing the given namespace part and relationship. * Called by {@link #read(POIXMLFactory, java.util.Map)} when Reading in an exisiting file. @@ -174,9 +186,10 @@ public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part) * @param rel - the relationship of the given namespace part * @see #read(POIXMLFactory, java.util.Map) */ - [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] + + [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] public POIXMLDocumentPart(PackagePart part, PackageRelationship rel) - : this(null, part) + : this(null, part) { } @@ -189,9 +202,10 @@ public POIXMLDocumentPart(PackagePart part, PackageRelationship rel) * @param rel - the relationship of the given namespace part * @see #read(POIXMLFactory, java.util.Map) */ - [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] + + [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel) - : this(null, part) + : this(null, part) { } @@ -200,6 +214,7 @@ public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part, PackageRe * re-base the XML Document onto the core child of the * current core document */ + protected void Rebase(OPCPackage pkg) { PackageRelationshipCollection cores = @@ -213,15 +228,19 @@ protected void Rebase(OPCPackage pkg) } packagePart = packagePart.GetRelatedPart(cores.GetRelationship(0)); } - static XmlNamespaceManager nsm = null; + + private static XmlNamespaceManager nsm = null; + public static XmlNamespaceManager NamespaceManager { - get { + get + { if (nsm == null) nsm = CreateDefaultNSM(); return nsm; } } + internal static XmlNamespaceManager CreateDefaultNSM() { // Create a NamespaceManager to handle the default namespace, @@ -235,14 +254,14 @@ internal static XmlNamespaceManager CreateDefaultNSM() ns.AddNamespace("r", PackageNamespaces.SCHEMA_RELATIONSHIPS); ns.AddNamespace("c", PackageNamespaces.SCHEMA_CHART); ns.AddNamespace("vt", PackageNamespaces.SCHEMA_VT); - ns.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); + ns.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); ns.AddNamespace("wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"); ns.AddNamespace("m", "http://schemas.openxmlformats.org/officeDocument/2006/math"); ns.AddNamespace("ve", "http://schemas.openxmlformats.org/markup-compatibility/2006"); ns.AddNamespace("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); ns.AddNamespace("o", "urn:schemas-microsoft-com:office:office"); ns.AddNamespace("v", "urn:schemas-microsoft-com:vml"); - ns.AddNamespace("wne","http://schemas.microsoft.com/office/word/2006/wordml"); + ns.AddNamespace("wne", "http://schemas.microsoft.com/office/word/2006/wordml"); // extended properties (app.xml) ns.AddNamespace("xp", PackageRelationshipTypes.EXTENDED_PROPERTIES); // custom properties @@ -259,11 +278,13 @@ internal static XmlNamespaceManager CreateDefaultNSM() ns.AddNamespace("w14", "http://schemas.microsoft.com/office/word/2010/wordml"); return ns; } + /** * Provides access to the underlying PackagePart * * @return the underlying PackagePart */ + public PackagePart GetPackagePart() { return packagePart; @@ -281,7 +302,8 @@ public static XmlDocument ConvertStreamToXml(Stream xmlStream) * * @return the PackageRelationship that identifies this POIXMLDocumentPart */ - [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] + + [Obsolete("deprecated in POI 3.14, scheduled for removal in POI 3.16")] public PackageRelationship GetPackageRelationship() { if (this.parent != null) @@ -314,6 +336,7 @@ public PackageRelationship GetPackageRelationship() * * @return child relations */ + public IList GetRelations() { List l = new List(); @@ -329,6 +352,7 @@ public IList GetRelations() * * @return child relations */ + public IList RelationParts { get @@ -349,14 +373,14 @@ public IList RelationParts * The relation id to look for * @return the target part of the relation, or null, if none exists */ + public POIXMLDocumentPart GetRelationById(String id) { - if (string.IsNullOrEmpty(id)|| !relations.ContainsKey(id)) + if (string.IsNullOrEmpty(id) || !relations.ContainsKey(id)) return null; RelationPart rp = relations[id]; return (rp == null) ? null : rp.DocumentPart; - } /** @@ -371,6 +395,7 @@ public POIXMLDocumentPart GetRelationById(String id) * @return The value of the {@link PackageRelationship#GetId()} or null, if * parts are not related. */ + public String GetRelationId(POIXMLDocumentPart part) { foreach (RelationPart rp in relations.Values) @@ -394,6 +419,7 @@ public void AddRelation(String id, POIXMLDocumentPart part) PackageRelationship pr = part.GetPackagePart().GetRelationship(id); AddRelation(pr, part); } + /// /// Add a new child POIXMLDocumentPart /// @@ -403,7 +429,7 @@ public void AddRelation(String id, POIXMLDocumentPart part) /// public RelationPart AddRelation(String relId, POIXMLRelation relationshipType, POIXMLDocumentPart part) { - PackageRelationship pr = this.packagePart.FindExistingRelation(part.GetPackagePart()); + PackageRelationship pr = this.packagePart.FindExistingRelation(part.GetPackagePart()); if (pr == null) { PackagePartName ppn = part.GetPackagePart().PartName; @@ -413,6 +439,7 @@ public RelationPart AddRelation(String relId, POIXMLRelation relationshipType, P AddRelation(pr, part); return new RelationPart(pr, part); } + /// /// Add a new child POIXMLDocumentPart /// @@ -425,13 +452,13 @@ private void AddRelation(PackageRelationship pr, POIXMLDocumentPart part) else relations.Add(pr.Id, new RelationPart(pr, part)); part.IncrementRelationCounter(); - } /** * Remove the relation to the specified part in this namespace and remove the * part, if it is no longer needed. */ + protected internal void RemoveRelation(POIXMLDocumentPart part) { RemoveRelation(part, true); @@ -447,6 +474,7 @@ protected internal void RemoveRelation(POIXMLDocumentPart part) * true, if the part shall be Removed from the namespace if not * needed any longer. */ + protected internal bool RemoveRelation(POIXMLDocumentPart part, bool RemoveUnusedParts) { String id = GetRelationId(part); @@ -486,6 +514,7 @@ protected internal bool RemoveRelation(POIXMLDocumentPart part, bool RemoveUnuse * * @return the parent POIXMLDocumentPart or null for the root element. */ + public POIXMLDocumentPart GetParent() { return parent; @@ -514,9 +543,9 @@ public override String ToString() * * */ + protected internal virtual void Commit() { - } /** @@ -525,6 +554,7 @@ protected internal virtual void Commit() * * @param alreadySaved context set Containing already visited nodes */ + protected internal void OnSave(List alreadySaved) { // this usually clears out previous content in the part... @@ -541,6 +571,7 @@ protected internal void OnSave(List alreadySaved) } } } + /** * Ensure that a memory based package part does not have lingering data from previous * commit() calls. @@ -548,6 +579,7 @@ protected internal void OnSave(List alreadySaved) * Note: This is overwritten for some objects, as *PictureData seem to store the actual content * in the part directly without keeping a copy like all others therefore we need to handle them differently. */ + protected internal virtual void PrepareForCommit() { PackagePart part = this.GetPackagePart(); @@ -556,6 +588,7 @@ protected internal virtual void PrepareForCommit() part.Clear(); } } + /** * Create a new child POIXMLDocumentPart * @@ -563,6 +596,7 @@ protected internal virtual void PrepareForCommit() * @param factory the factory that will create an instance of the requested relation * @return the Created child POIXMLDocumentPart */ + public POIXMLDocumentPart CreateRelationship(POIXMLRelation descriptor, POIXMLFactory factory) { return CreateRelationship(descriptor, factory, -1, false).DocumentPart; @@ -589,16 +623,18 @@ public POIXMLDocumentPart CreateRelationship(POIXMLRelation descriptor, POIXMLFa * @param minIdx The minimum free index to assign, use -1 for any * @return The next free part number, or -1 if none available */ + protected internal int GetNextPartNumber(POIXMLRelation descriptor, int minIdx) { OPCPackage pkg = packagePart.Package; try { - if (descriptor.DefaultFileName.Equals(descriptor.GetFileName(9999))) + string name = descriptor.DefaultFileName; + if (name.Equals(descriptor.GetFileName(9999))) { // Non-index based, check if default is free - PackagePartName ppName = PackagingUriHelper.CreatePartName(descriptor.DefaultFileName); + PackagePartName ppName = PackagingUriHelper.CreatePartName(name); if (pkg.ContainPart(ppName)) { // Default name already taken, not index based, nothing free @@ -614,10 +650,12 @@ protected internal int GetNextPartNumber(POIXMLRelation descriptor, int minIdx) // Default to searching from 1, unless they asked for 0+ int idx = minIdx; if (minIdx < 0) idx = 1; - while (idx < 1000) + int maxIdx = minIdx + pkg.GetParts().Count; + while (idx < maxIdx) { - String name = descriptor.GetFileName(idx); - if (!pkg.ContainPart(PackagingUriHelper.CreatePartName(name))) + name = descriptor.GetFileName(idx); + PackagePartName ppName = PackagingUriHelper.CreatePartName(name); + if (!pkg.ContainPart(ppName)) { return idx; } @@ -641,6 +679,7 @@ protected internal int GetNextPartNumber(POIXMLRelation descriptor, int minIdx) * @param noRelation if true, then no relationship is Added. * @return the Created child POIXMLDocumentPart */ + protected RelationPart CreateRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, bool noRelation) { try @@ -688,6 +727,7 @@ public TValue PutDictionary(Dictionary dict, TKey ke dict.Add(key, value); return oldValue; } + public TValue GetDictionary(Dictionary dict, TKey key) { if (dict.ContainsKey(key)) @@ -696,6 +736,7 @@ public TValue GetDictionary(Dictionary dict, TKey ke } return default(TValue); } + /** * Iterate through the underlying PackagePart and create child POIXMLFactory instances * using the specified factory @@ -703,6 +744,7 @@ public TValue GetDictionary(Dictionary dict, TKey ke * @param factory the factory object that Creates POIXMLFactory instances * @param context context map Containing already visited noted keyed by tarGetURI */ + protected void Read(POIXMLFactory factory, Dictionary context) { PackagePart pp = GetPackagePart(); @@ -772,6 +814,7 @@ protected void Read(POIXMLFactory factory, Dictionary macros = r.ReadMacros(); Assert.IsNotNull(macros["Module20"]); StringAssert.Contains("here start of superscripting", macros["Module20"]); + r.Close(); } // This test is written as expected-to-fail and should be rewritten @@ -329,6 +330,7 @@ public void Bug59858() Dictionary macros = r.ReadMacros(); Assert.IsNotNull(macros["Sheet4"]); StringAssert.Contains("intentional constituent", macros["Sheet4"]); + r.Close(); } // This test is written as expected-to-fail and should be rewritten @@ -341,6 +343,7 @@ public void Bug60158() Dictionary macros = r.ReadMacros(); Assert.IsNotNull(macros["NewMacros"]); StringAssert.Contains("' dirty", macros["NewMacros"]); + r.Close(); } [Test] @@ -352,6 +355,7 @@ public void Bug60273() Dictionary macros = r.ReadMacros(); Assert.IsNotNull(macros["Module1"]); StringAssert.Contains("9/8/2004", macros["Module1"]); + r.Close(); } } } \ No newline at end of file diff --git a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs index 99f7c2984..b67028fb1 100644 --- a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs +++ b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs @@ -1380,9 +1380,11 @@ public void Test50319() wb.Close(); } - [Ignore("by poi")] + // Bug 58648: FormulaParser throws exception in parseSimpleFactor() when getCellFormula() + // is called on a cell and the formula contains spaces between closing parentheses ") )" + // https://bz.apache.org/bugzilla/show_bug.cgi?id=58648 [Test] - public void test58648() + public void Test58648() { IWorkbook wb = _testDataProvider.CreateWorkbook(); ICell cell = wb.CreateSheet().CreateRow(0).CreateCell(0); diff --git a/testcases/main/Util/TestStringUtil.cs b/testcases/main/Util/TestStringUtil.cs index 1506b6e34..053db9168 100644 --- a/testcases/main/Util/TestStringUtil.cs +++ b/testcases/main/Util/TestStringUtil.cs @@ -253,6 +253,26 @@ public void Join() Assert.AreEqual("abc|def|ghi", StringUtil.Join("|", "abc", "def", "ghi")); Assert.AreEqual("5|8.5|True|string", StringUtil.Join("|", 5, 8.5, true, "string")); //assumes Locale prints number decimal point as a period rather than a comma } + + [Test] + public void Count() + { + String test = "Apache POI project\n\u00a9 Copyright 2016"; + // supports search in null or empty string + Assert.AreEqual(0, StringUtil.CountMatches(null, 'A'), "null"); + Assert.AreEqual(0, StringUtil.CountMatches("", 'A'), "empty string"); + + Assert.AreEqual(2, StringUtil.CountMatches(test, 'e'), "normal"); + Assert.AreEqual(1, StringUtil.CountMatches(test, 'a'), "normal, should not find a in escaped copyright"); + + // search for non-printable characters + Assert.AreEqual(0, StringUtil.CountMatches(test, '\0'), "null character"); + Assert.AreEqual(0, StringUtil.CountMatches(test, '\r'), "CR"); + Assert.AreEqual(1, StringUtil.CountMatches(test, '\n'), "LF"); + + // search for unicode characters + Assert.AreEqual(1, StringUtil.CountMatches(test, '\u00a9'), "Unicode"); + } } } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 204b750e9..9934980b9 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -1495,9 +1495,7 @@ public void Test51963() /** * Sum across multiple workbooks * eg =SUM($Sheet1.C1:$Sheet4.C1) - * DISABLED As we can't currently Evaluate these */ - [Ignore("by poi")] [Test] public void Test48703() { diff --git a/testcases/ooxml/XWPF/Extractor/TestXWPFWordExtractor.cs b/testcases/ooxml/XWPF/Extractor/TestXWPFWordExtractor.cs index 20a7bd8d9..56adc3a90 100644 --- a/testcases/ooxml/XWPF/Extractor/TestXWPFWordExtractor.cs +++ b/testcases/ooxml/XWPF/Extractor/TestXWPFWordExtractor.cs @@ -17,6 +17,7 @@ limitations under the License. namespace TestCases.XWPF.Extractor { + using NPOI.Util; using NPOI.XWPF.Extractor; using NPOI.XWPF.UserModel; using NUnit.Framework; @@ -53,16 +54,9 @@ public void TestGetSimpleText() )); // Check number of paragraphs - int ps = 0; - char[] t = text.ToCharArray(); - for (int i = 0; i < t.Length; i++) - { - if (t[i] == '\n') - { - ps++; - } - } - Assert.AreEqual(3, ps); + // Check number of paragraphs by counting number of newlines + int numberOfParagraphs = StringUtil.CountMatches(text, '\n'); + Assert.AreEqual(3, numberOfParagraphs); } /** diff --git a/testcases/test-data/spreadsheet/60284.xls b/testcases/test-data/spreadsheet/60284.xls new file mode 100644 index 000000000..7e0f9fb83 Binary files /dev/null and b/testcases/test-data/spreadsheet/60284.xls differ