From 70724609b1e2894e08f60bba43f47e6faeb40fed Mon Sep 17 00:00:00 2001 From: Mark Murphy Date: Sat, 5 Nov 2016 06:12:24 +0000 Subject: [PATCH] 60337: XWPFTableRow.isRepeatHeader throws NullPointerException, setRepeatHeader does not overwrite old value Task-Url: https://bz.apache.org/bugzilla/show_bug.cgi?id=60337 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1768153 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/xwpf/model/WMLHelper.java | 33 +++++ .../poi/xwpf/usermodel/XWPFTableRow.java | 64 +++++---- .../poi/xwpf/usermodel/TestXWPFSDT.java | 14 +- .../poi/xwpf/usermodel/TestXWPFTableRow.java | 121 ++++++++++++++---- test-data/document/Bug60337.docx | Bin 0 -> 13463 bytes 5 files changed, 183 insertions(+), 49 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java create mode 100644 test-data/document/Bug60337.docx diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java b/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java new file mode 100644 index 0000000000..7c30d21b01 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java @@ -0,0 +1,33 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xwpf.model; + +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff; + +public final class WMLHelper { + + public static boolean STOnOffToBoolean (STOnOff.Enum value) { + if (value == STOnOff.TRUE || value == STOnOff.ON || value == STOnOff.X_1) { + return true; + } + return false; + } + + public static STOnOff.Enum BooleanToSTOnOff (boolean value) { + return (value ? STOnOff.TRUE : STOnOff.FALSE); + } +} diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java index eb1ec77753..3f530df765 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.poi.util.Internal; +import org.apache.poi.xwpf.model.WMLHelper; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHeight; @@ -190,57 +191,76 @@ public class XWPFTableRow { * * @return true if rows can't be split, false otherwise. */ - public boolean isCantSplitRow() { - boolean isCant = false; - CTTrPr trpr = getTrPr(); - if (trpr.sizeOfCantSplitArray() > 0) { - CTOnOff onoff = trpr.getCantSplitArray(0); - isCant = onoff.getVal().equals(STOnOff.ON); - } - return isCant; + public boolean isCantSplitRow() { + boolean isCant = false; + if (ctRow.isSetTrPr()) { + CTTrPr trpr = getTrPr(); + if (trpr.sizeOfCantSplitArray() > 0) { + CTOnOff onoff = trpr.getCantSplitArray(0); + isCant = (onoff.isSetVal() ? WMLHelper.STOnOffToBoolean(onoff.getVal()) : true); + } + } + return isCant; } /** - * This attribute controls whether to allow table rows to split across pages. + * Controls whether to allow this table row to split across pages. * The logic for this attribute is a little unusual: a true value means * DON'T allow rows to split, false means allow rows to split. * - * @param split - if true, don't allow rows to be split. If false, allow - * rows to be split. + * @param split - if true, don't allow row to be split. If false, allow + * row to be split. */ public void setCantSplitRow(boolean split) { CTTrPr trpr = getTrPr(); - CTOnOff onoff = trpr.addNewCantSplit(); - onoff.setVal(split ? STOnOff.ON : STOnOff.OFF); + CTOnOff onoff = (trpr.sizeOfCantSplitArray() > 0 ? trpr.getCantSplitArray(0) : trpr.addNewCantSplit()); + onoff.setVal(WMLHelper.BooleanToSTOnOff(split)); } /** * Return true if a table's header row should be repeated at the top of a - * table split across pages. + * table split across pages. NOTE - Word will not repeat a table row unless + * all preceding rows of the table are also repeated. This function returns + * false if the row will not be repeated even if the repeat tag is present + * for this row. * * @return true if table's header row should be repeated at the top of each * page of table, false otherwise. */ public boolean isRepeatHeader() { - boolean repeat = false; - CTTrPr trpr = getTrPr(); - if (trpr.sizeOfTblHeaderArray() > 0) { - CTOnOff rpt = trpr.getTblHeaderArray(0); - repeat = rpt.getVal().equals(STOnOff.ON); + boolean repeat = false; + for (XWPFTableRow row : table.getRows()) { + repeat = row.getRepeat(); + if (row == this || !repeat) { + break; + } } return repeat; + } + + private boolean getRepeat() { + boolean repeat = false; + if (ctRow.isSetTrPr()) { + CTTrPr trpr = getTrPr(); + if (trpr.sizeOfTblHeaderArray() > 0) { + CTOnOff rpt = trpr.getTblHeaderArray(0); + repeat = (rpt.isSetVal() ? WMLHelper.STOnOffToBoolean(rpt.getVal()) : true); + } + } + return repeat; } /** * This attribute controls whether to repeat a table's header row at the top - * of a table split across pages. + * of a table split across pages. NOTE - for a row to be repeated, all preceding + * rows in the table must also be repeated. * * @param repeat - if TRUE, repeat header row at the top of each page of table; * if FALSE, don't repeat header row. */ public void setRepeatHeader(boolean repeat) { CTTrPr trpr = getTrPr(); - CTOnOff onoff = trpr.addNewTblHeader(); - onoff.setVal(repeat ? STOnOff.ON : STOnOff.OFF); + CTOnOff onoff = (trpr.sizeOfTblHeaderArray() > 0 ? trpr.getTblHeaderArray(0) : trpr.addNewTblHeader()); + onoff.setVal(WMLHelper.BooleanToSTOnOff(repeat)); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java index 0f625fbd90..c9d12bddce 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java @@ -17,21 +17,26 @@ package org.apache.poi.xwpf.usermodel; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import junit.framework.TestCase; import org.apache.poi.xwpf.XWPFTestDataSamples; +import org.junit.Ignore; +import org.junit.Test; -public final class TestXWPFSDT extends TestCase { +public final class TestXWPFSDT { /** * Test simple tag and title extraction from SDT * * @throws Exception */ + @Test public void testTagTitle() throws Exception { XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx"); String tag = null; @@ -51,7 +56,7 @@ public final class TestXWPFSDT extends TestCase { assertEquals("title", "MyTitle", title); } - + @Test public void testGetSDTs() throws Exception { String[] contents = new String[]{ "header_rich_text", @@ -83,6 +88,7 @@ public final class TestXWPFSDT extends TestCase { /** * POI-54771 and TIKA-1317 */ + @Test public void testSDTAsCell() throws Exception { //Bug54771a.docx and Bug54771b.docx test slightly //different recursion patterns. Keep both! @@ -110,6 +116,7 @@ public final class TestXWPFSDT extends TestCase { /** * POI-55142 and Tika 1130 */ + @Test public void testNewLinesBetweenRuns() throws Exception { XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug55142.docx"); List sdts = extractAllSDTs(doc); @@ -132,6 +139,7 @@ public final class TestXWPFSDT extends TestCase { } } + @Ignore public void test60341() throws IOException { //handle sdtbody without an sdtpr XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug60341.docx"); diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java index b22f1a0cb7..57ed435320 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java @@ -17,51 +17,124 @@ package org.apache.poi.xwpf.usermodel; -import junit.framework.TestCase; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; -public class TestXWPFTableRow extends TestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - } +import java.io.IOException; - public void testCreateRow() throws Exception { - CTRow ctRow = CTRow.Factory.newInstance(); - assertNotNull(ctRow); - } +import org.apache.poi.xwpf.XWPFTestDataSamples; +import org.junit.Test; - @Override - protected void tearDown() throws Exception { - super.tearDown(); +public class TestXWPFTableRow { + + @Test + public void testCreateRow() throws IOException { + XWPFDocument doc = new XWPFDocument(); + XWPFTable table = doc.createTable(1, 1); + XWPFTableRow tr = table.createRow(); + assertNotNull(tr); + doc.close(); } - public void testSetGetCantSplitRow() { + @Test + public void testSetGetCantSplitRow() throws IOException { // create a table XWPFDocument doc = new XWPFDocument(); - CTTbl ctTable = CTTbl.Factory.newInstance(); - XWPFTable table = new XWPFTable(ctTable, doc); + XWPFTable table = doc.createTable(1, 1); // table has a single row by default; grab it XWPFTableRow tr = table.getRow(0); assertNotNull(tr); + // Assert the repeat header is false by default + boolean isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + // Repeat the header tr.setCantSplitRow(true); - boolean isCant = tr.isCantSplitRow(); - assert (isCant); + isCantSplit = tr.isCantSplitRow(); + assertTrue(isCantSplit); + + // Make the header no longer repeating + tr.setCantSplitRow(false); + isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + doc.close(); } - public void testSetGetRepeatHeader() { + @Test + public void testSetGetRepeatHeader() throws IOException { // create a table XWPFDocument doc = new XWPFDocument(); - CTTbl ctTable = CTTbl.Factory.newInstance(); - XWPFTable table = new XWPFTable(ctTable, doc); + XWPFTable table = doc.createTable(3, 1); // table has a single row by default; grab it XWPFTableRow tr = table.getRow(0); assertNotNull(tr); + + // Assert the repeat header is false by default + boolean isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + // Repeat the header + tr.setRepeatHeader(true); + isRpt = tr.isRepeatHeader(); + assertTrue(isRpt); + // Make the header no longer repeating + tr.setRepeatHeader(false); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + // If the third row is set to repeat, but not the second, + // isRepeatHeader should report false because Word will + // ignore it. + tr = table.getRow(2); tr.setRepeatHeader(true); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + doc.close(); + } + + // Test that validates the table header value can be parsed from a document + // generated in Word + @Test + public void testIsRepeatHeader() throws Exception { + XWPFDocument doc = XWPFTestDataSamples + .openSampleDocument("Bug60337.docx"); + XWPFTable table = doc.getTables().get(0); + XWPFTableRow tr = table.getRow(0); boolean isRpt = tr.isRepeatHeader(); - assert (isRpt); + assertTrue(isRpt); + + tr = table.getRow(1); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + tr = table.getRow(2); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + } + + + // Test that validates the table header value can be parsed from a document + // generated in Word + @Test + public void testIsCantSplit() throws Exception { + XWPFDocument doc = XWPFTestDataSamples + .openSampleDocument("Bug60337.docx"); + XWPFTable table = doc.getTables().get(0); + XWPFTableRow tr = table.getRow(0); + boolean isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + tr = table.getRow(1); + isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + tr = table.getRow(2); + isCantSplit = tr.isCantSplitRow(); + assertTrue(isCantSplit); } } diff --git a/test-data/document/Bug60337.docx b/test-data/document/Bug60337.docx new file mode 100644 index 0000000000000000000000000000000000000000..06d0339fc5f854d90fe6721d991c512c815da4c5 GIT binary patch literal 13463 zcmeHu1y@{Ivv%X|?(PJ4cXxLuxVyVUf`tSccZc8}JV=5=aCi4$f!mpP=FUuJzV8p* z_w-tQcCYT|S+(oz+Eu%%T16fL5)%LofCT^mWB@qIsL&8F0N@Q20Du921=kmMa&)(J zbT`)Uakg|bWb$^fC(VZhr_BX`gMR;JF@8d4RN!$eRc�ea>a-!2d|3ulZ*y-- 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 literal 0 HcmV?d00001 -- 2.39.5