mirror of https://github.com/apache/poi.git
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:
parent
7926036f2f
commit
8e89928c88
|
@ -2154,6 +2154,12 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
int level = xRow.getCTRow().getOutlineLevel();
|
||||
for (Iterator<Row> it = rowIterator(); it.hasNext();) {
|
||||
xRow = (XSSFRow) it.next();
|
||||
|
||||
// skip rows before the start of this group
|
||||
if(xRow.getRowNum() < rowIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xRow.getCTRow().getOutlineLevel() >= level) {
|
||||
xRow.getCTRow().setHidden(hidden);
|
||||
rowIndex++;
|
||||
|
@ -2171,8 +2177,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
return;
|
||||
XSSFRow row = getRow(rowNumber);
|
||||
// If it is already expanded do nothing.
|
||||
if (!row.getCTRow().isSetHidden())
|
||||
if (!row.getCTRow().isSetHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the start of the group.
|
||||
int startIdx = findStartOfRowOutlineGroup(rowNumber);
|
||||
|
@ -2202,7 +2209,11 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
}
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
Loading…
Reference in New Issue