Bug 55640, added reproducer, a fix for the Exception cases and some verify-tests to ensure future

changes are checked via unit tests. There might be more work pending to
make grouping fully work the same way as Excel does.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1547153 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Dominik Stadler 2013-12-02 19:14:03 +00:00
parent 7926036f2f
commit 8e89928c88
4 changed files with 454 additions and 2 deletions

View File

@ -2154,6 +2154,12 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
int level = xRow.getCTRow().getOutlineLevel(); int level = xRow.getCTRow().getOutlineLevel();
for (Iterator<Row> it = rowIterator(); it.hasNext();) { for (Iterator<Row> it = rowIterator(); it.hasNext();) {
xRow = (XSSFRow) it.next(); xRow = (XSSFRow) it.next();
// skip rows before the start of this group
if(xRow.getRowNum() < rowIndex) {
continue;
}
if (xRow.getCTRow().getOutlineLevel() >= level) { if (xRow.getCTRow().getOutlineLevel() >= level) {
xRow.getCTRow().setHidden(hidden); xRow.getCTRow().setHidden(hidden);
rowIndex++; rowIndex++;
@ -2171,8 +2177,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
return; return;
XSSFRow row = getRow(rowNumber); XSSFRow row = getRow(rowNumber);
// If it is already expanded do nothing. // If it is already expanded do nothing.
if (!row.getCTRow().isSetHidden()) if (!row.getCTRow().isSetHidden()) {
return; return;
}
// Find the start of the group. // Find the start of the group.
int startIdx = findStartOfRowOutlineGroup(rowNumber); int startIdx = findStartOfRowOutlineGroup(rowNumber);
@ -2202,7 +2209,11 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
} }
} }
// Write collapse field // Write collapse field
getRow(endIdx).getCTRow().unsetCollapsed(); CTRow ctRow = getRow(endIdx).getCTRow();
// This avoids an IndexOutOfBounds if multiple nested groups are collapsed/expanded
if(ctRow.getCollapsed()) {
ctRow.unsetCollapsed();
}
} }
/** /**

View File

@ -0,0 +1,441 @@
/* ====================================================================
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.xssf.usermodel;
import java.io.IOException;
import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.XSSFTestDataSamples;
public final class TestXSSFSheetRowGrouping extends TestCase {
private static final int ROWS_NUMBER = 200;
private static final int GROUP_SIZE = 5;
//private int o_groupsNumber = 0;
public void test55640() throws IOException {
//long startTime = System.currentTimeMillis();
Workbook wb = new XSSFWorkbook();
fillData(wb);
writeToFile(wb);
//System.out.println("Number of groups: " + o_groupsNumber);
//System.out.println("Execution time: " + (System.currentTimeMillis()-startTime) + " ms");
}
private void fillData(Workbook p_wb) {
Sheet sheet = p_wb.createSheet("sheet123");
sheet.setRowSumsBelow(false);
for (int i = 0; i < ROWS_NUMBER; i++) {
Row row = sheet.createRow(i);
Cell cell = row.createCell(0);
cell.setCellValue(i+1);
}
int i = 1;
while (i < ROWS_NUMBER) {
int end = i+(GROUP_SIZE-2);
int start = i; // natural order
// int start = end - 1; // reverse order
while (start < end) { // natural order
// while (start >= i) { // reverse order
sheet.groupRow(start, end);
//o_groupsNumber++;
boolean collapsed = isCollapsed();
//System.out.println("Set group " + start + "->" + end + " to " + collapsed);
sheet.setRowGroupCollapsed(start, collapsed);
start++; // natural order
// start--; // reverse order
}
i += GROUP_SIZE;
}
}
private boolean isCollapsed() {
return Math.random() > 0.5d;
}
private void writeToFile(Workbook p_wb) throws IOException {
// FileOutputStream fileOut = new FileOutputStream("/tmp/55640.xlsx");
// try {
// p_wb.write(fileOut);
// } finally {
// fileOut.close();
// }
assertNotNull(XSSFTestDataSamples.writeOutAndReadBack(p_wb));
}
public void test55640reduce1() throws IOException {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("sheet123");
sheet.setRowSumsBelow(false);
for (int i = 0; i < ROWS_NUMBER; i++) {
Row row = sheet.createRow(i);
Cell cell = row.createCell(0);
cell.setCellValue(i+1);
}
int i = 1;
while (i < ROWS_NUMBER) {
int end = i+(GROUP_SIZE-2);
int start = i; // natural order
while (start < end) { // natural order
sheet.groupRow(start, end);
//o_groupsNumber++;
boolean collapsed = start % 2 == 0 ? false : true;
//System.out.println("Set group " + start + "->" + end + " to " + collapsed);
sheet.setRowGroupCollapsed(start, collapsed);
start++; // natural order
}
i += GROUP_SIZE;
}
writeToFile(wb);
}
public void test55640_VerifyCases() throws IOException {
// NOTE: This is currently based on current behavior of POI, somehow
// what POI returns in the calls to collapsed/hidden is not fully matching
// the examples in the spec or I did not fully understand how POI stores the data internally...
// all expanded
verifyGroupCollapsed(
// level1, level2, level3
false, false, false,
// collapsed:
new Boolean[] { false, false, false, false, false},
// hidden:
new boolean[] { false, false, false, false, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3 }
);
// Level 1 collapsed, others expanded, should only have 4 rows, all hidden:
verifyGroupCollapsed(
// level1, level2, level3
true, false, false,
// collapsed:
new Boolean[] { false, false, false, false, false},
// hidden:
new boolean[] { true, true, true, true, true},
// outlineLevel
new int[] { 1, 2, 3, 3, 3 }
);
// Level 1 and 2 collapsed, Level 3 expanded,
verifyGroupCollapsed(
// level1, level2, level3
true, true, false,
// collapsed:
new Boolean[] { false, false, false, false, true, false},
// hidden:
new boolean[] { true, true, true, true, true, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3, 0 }
);
// Level 1 collapsed, Level 2 expanded, Level 3 collapsed
verifyGroupCollapsed(
// level1, level2, level3
true, false, true,
// collapsed:
new Boolean[] { false, false, false, false, false, true},
// hidden:
new boolean[] { true, true, true, true, true, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3, 0 }
);
// Level 2 collapsed, others expanded:
verifyGroupCollapsed(
// level1, level2, level3
false, true, false,
// collapsed:
new Boolean[] { false, false, false, false, false, false},
// hidden:
new boolean[] { false, true, true, true, true, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3, 0 }
);
// Level 3 collapsed, others expanded
verifyGroupCollapsed(
// level1, level2, level3
false, false, true,
// collapsed:
new Boolean[] { false, false, false, false, false, true},
// hidden:
new boolean[] { false, false, true, true, true, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3, 0 }
);
// All collapsed
verifyGroupCollapsed(
// level1, level2, level3
true, true, true,
// collapsed:
new Boolean[] { false, false, false, false, true, true},
// hidden:
new boolean[] { true, true, true, true, true, false},
// outlineLevel
new int[] { 1, 2, 3, 3, 3, 0 }
);
}
private void verifyGroupCollapsed(boolean level1, boolean level2, boolean level3,
Boolean[] collapsed, boolean[] hidden, int[] outlineLevel) throws IOException {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("sheet123");
for (int i = 0; i < 4; i++) {
sheet.createRow(i);
}
sheet.groupRow(0, 4);
sheet.groupRow(1, 4);
sheet.groupRow(2, 4);
sheet.setRowGroupCollapsed(0, level1);
sheet.setRowGroupCollapsed(1, level2);
sheet.setRowGroupCollapsed(2, level3);
checkWorkbookGrouping(wb, collapsed, hidden, outlineLevel);
}
public void test55640_VerifyCasesSpec() throws IOException {
// NOTE: This is currently based on current behavior of POI, somehow
// what POI returns in the calls to collapsed/hidden is not fully matching
// the examples in the spec or I did not fully understand how POI stores the data internally...
// all expanded
verifyGroupCollapsedSpec(
// level3, level2, level1
false, false, false,
// collapsed:
new Boolean[] { false, false, false, false},
// hidden:
new boolean[] { false, false, false, false},
// outlineLevel
new int[] { 3, 3, 2, 1 }
);
verifyGroupCollapsedSpec(
// level3, level2, level1
false, false, true,
// collapsed:
new Boolean[] { false, false, false, true},
// hidden:
new boolean[] { true, true, true, false},
// outlineLevel
new int[] { 3, 3, 2, 1 }
);
verifyGroupCollapsedSpec(
// level3, level2, level1
false, true, false,
// collapsed:
new Boolean[] { false, false, true, false},
// hidden:
new boolean[] { true, true, true, false},
// outlineLevel
new int[] { 3, 3, 2, 1 }
);
verifyGroupCollapsedSpec(
// level3, level2, level1
false, true, true,
// collapsed:
new Boolean[] { false, false, true, true},
// hidden:
new boolean[] { true, true, true, false},
// outlineLevel
new int[] { 3, 3, 2, 1 }
);
}
private void verifyGroupCollapsedSpec(boolean level1, boolean level2, boolean level3,
Boolean[] collapsed, boolean[] hidden, int[] outlineLevel) throws IOException {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("sheet123");
for (int i = 5; i < 9; i++) {
sheet.createRow(i);
}
sheet.groupRow(5, 6);
sheet.groupRow(5, 7);
sheet.groupRow(5, 8);
sheet.setRowGroupCollapsed(6, level1);
sheet.setRowGroupCollapsed(7, level2);
sheet.setRowGroupCollapsed(8, level3);
checkWorkbookGrouping(wb, collapsed, hidden, outlineLevel);
}
private void checkWorkbookGrouping(Workbook wb, Boolean[] collapsed, boolean[] hidden, int[] outlineLevel) throws IOException {
printWorkbook(wb);
Sheet sheet = wb.getSheetAt(0);
assertEquals(collapsed.length, hidden.length);
assertEquals(collapsed.length, outlineLevel.length);
assertEquals("Expected " + collapsed.length + " rows with collapsed state, but had " + (sheet.getLastRowNum()-sheet.getFirstRowNum()+1) + " rows ("
+ sheet.getFirstRowNum() + "-" + sheet.getLastRowNum() + ")",
collapsed.length, sheet.getLastRowNum()-sheet.getFirstRowNum()+1);
for(int i = sheet.getFirstRowNum(); i < sheet.getLastRowNum();i++) {
if(collapsed[i-sheet.getFirstRowNum()] == null) {
continue;
}
XSSFRow row = (XSSFRow) sheet.getRow(i);
assertNotNull("Could not read row " + i, row);
assertNotNull("Could not read row " + i, row.getCTRow());
assertEquals("Row: " + i + ": collapsed", collapsed[i-sheet.getFirstRowNum()].booleanValue(), row.getCTRow().getCollapsed());
assertEquals("Row: " + i + ": hidden", hidden[i-sheet.getFirstRowNum()], row.getCTRow().getHidden());
assertEquals("Row: " + i + ": level", outlineLevel[i-sheet.getFirstRowNum()], row.getCTRow().getOutlineLevel());
}
writeToFile(wb);
}
public void test55640working() throws IOException {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("sheet123");
sheet.groupRow(1, 4);
sheet.groupRow(2, 5);
sheet.groupRow(3, 6);
sheet.setRowGroupCollapsed(1, true);
sheet.setRowGroupCollapsed(2, false);
sheet.setRowGroupCollapsed(3, false);
writeToFile(wb);
}
// just used for printing out contents of spreadsheets
public void notRuntest55640printSample() {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55640.xlsx");
printWorkbook(wb);
wb = XSSFTestDataSamples.openSampleWorkbook("GroupTest.xlsx");
printWorkbook(wb);
}
private void printWorkbook(Workbook wb) {
// disable all output for now...
// Sheet sheet = wb.getSheetAt(0);
//
// for(Iterator<Row> it = sheet.rowIterator();it.hasNext();) {
// XSSFRow row = (XSSFRow) it.next();
// boolean collapsed = row.getCTRow().getCollapsed();
// boolean hidden = row.getCTRow().getHidden();
// short level = row.getCTRow().getOutlineLevel();
//
// System.out.println("Row: " + row.getRowNum() + ": Level: " + level + " Collapsed: " + collapsed + " Hidden: " + hidden);
// }
}
public void testGroupingTest() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("GroupTest.xlsx");
assertEquals(31, wb.getSheetAt(0).getLastRowNum());
// NOTE: This is currently based on current behavior of POI, somehow
// what POI returns in the calls to collapsed/hidden is not fully matching
// the examples in the spec or I did not fully understand how POI stores the data internally...
checkWorkbookGrouping(wb,
new Boolean [] {
// 0-4
false, false, false, false, false, null, null,
// 7-11
false, false, true, true, true, null, null,
// 14-18
false, false, true, false, false, null,
// 20-24
false, false, true, true, false, null, null,
// 27-31
false, false, false, true, false },
new boolean[] {
// 0-4
false, false, false, false, false, false, false,
// 7-11
true, true, true, true, false, false, false,
// 14-18
true, true, false, false, false, false,
// 20-24
true, true, true, false, false, false, false,
// 27-31
true, true, true, true, false },
// outlineLevel
new int[] {
// 0-4
3, 3, 2, 1, 0, 0, 0,
// 7-11
3, 3, 2, 1, 0, 0, 0,
// 14-18
3, 3, 2, 1, 0, 0,
// 20-24
3, 3, 2, 1, 0, 0, 0,
// 27-31
3, 3, 2, 1, 0,
}
);
/*
Row: 0: Level: 3 Collapsed: false Hidden: false
Row: 1: Level: 3 Collapsed: false Hidden: false
Row: 2: Level: 2 Collapsed: false Hidden: false
Row: 3: Level: 1 Collapsed: false Hidden: false
Row: 4: Level: 0 Collapsed: false Hidden: false
Row: 7: Level: 3 Collapsed: false Hidden: true
Row: 8: Level: 3 Collapsed: false Hidden: true
Row: 9: Level: 2 Collapsed: true Hidden: true
Row: 10: Level: 1 Collapsed: true Hidden: true
Row: 11: Level: 0 Collapsed: true Hidden: false
Row: 14: Level: 3 Collapsed: false Hidden: true
Row: 15: Level: 3 Collapsed: false Hidden: true
Row: 16: Level: 2 Collapsed: true Hidden: false
Row: 17: Level: 1 Collapsed: false Hidden: false
Row: 18: Level: 0 Collapsed: false Hidden: false
Row: 20: Level: 3 Collapsed: false Hidden: true
Row: 21: Level: 3 Collapsed: false Hidden: true
Row: 22: Level: 2 Collapsed: true Hidden: true
Row: 23: Level: 1 Collapsed: true Hidden: false
Row: 24: Level: 0 Collapsed: false Hidden: false
Row: 27: Level: 3 Collapsed: false Hidden: true
Row: 28: Level: 3 Collapsed: false Hidden: true
Row: 29: Level: 2 Collapsed: false Hidden: true
Row: 30: Level: 1 Collapsed: true Hidden: true
Row: 31: Level: 0 Collapsed: true Hidden: false
*/
}
}

Binary file not shown.

Binary file not shown.