diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml index ea5830e8d..5dfbe8109 100644 --- a/src/documentation/content/xdocs/hssf/quick-guide.xml +++ b/src/documentation/content/xdocs/hssf/quick-guide.xml @@ -1,12 +1,12 @@ - +
Busy Developers' Guide to HSSF Features - +
@@ -44,6 +44,7 @@
  • Drawing Shapes.
  • Styling Shapes.
  • Shapes and Graphics2d.
  • +
  • Outlining.
  • Features @@ -902,6 +903,42 @@ using the POI logging infrastructure (disabled by default).

    + +
    + Outlining +

    + Outlines are great for grouping sections of information + together and can be added easily to columns and rows + using the POI API. Here's how: +

    + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 14 ); + sheet1.groupRow( 16, 19 ); + + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.groupColumn( (short)9, (short)12 ); + sheet1.groupColumn( (short)10, (short)11 ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + +

    + To collapse (or expand) an outline use the following calls: +

    + + sheet1.setRowGroupCollapsed( 7, true ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + +

    + The row/column you choose should contain an already + created group. It can be anywhere within the group. +

    +
    diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/Outlines.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/Outlines.java new file mode 100644 index 000000000..de6195dc8 --- /dev/null +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/Outlines.java @@ -0,0 +1,284 @@ + +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.hssf.usermodel.examples; + +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFCell; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Creates outlines. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class Outlines +{ + private Outlines(){} + + public static void main(String[] args) + throws IOException + { + createCase1( "outline1.xls" ); + System.out.println( "outline1.xls written. Two expanded groups." ); + createCase2( "outline2.xls" ); + System.out.println( "outline2.xls written. Two groups. Inner group collapsed." ); + createCase3( "outline3.xls" ); + System.out.println( "outline3.xls written. Two groups. Both collapsed." ); + createCase4( "outline4.xls" ); + System.out.println( "outline4.xls written. Two groups. Collapsed then inner group expanded." ); + createCase5( "outline5.xls" ); + System.out.println( "outline5.xls written. Two groups. Collapsed then reexpanded." ); + createCase6( "outline6.xls" ); + System.out.println( "outline6.xls written. Two groups with matching end points. Second group collapsed." ); + createCase7( "outline7.xls" ); + System.out.println( "outline7.xls written. Row outlines." ); + createCase8( "outline8.xls" ); + System.out.println( "outline8.xls written. Row outlines. Inner group collapsed." ); + createCase9( "outline9.xls" ); + System.out.println( "outline9.xls written. Row outlines. Both collapsed." ); + createCase10( "outline10.xls" ); + System.out.println( "outline10.xls written. Row outlines. Collapsed then inner group expanded." ); + createCase11( "outline11.xls" ); + System.out.println( "outline11.xls written. Row outlines. Collapsed then expanded." ); + createCase12( "outline12.xls" ); + System.out.println( "outline12.xls written. Row outlines. Two row groups with matching end points. Second group collapsed." ); + createCase13( "outline13.xls" ); + System.out.println( "outline13.xls written. Mixed bag." ); + } + + private static void createCase1( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)4, (short)7 ); + + for (int row = 0; row < 200; row++) + { + HSSFRow r = sheet1.createRow( row ); + for (int column = 0; column < 200; column++) + { + HSSFCell c = r.createCell( (short) column ); + c.setCellValue( column ); + } + } + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase2( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)2, (short)10 ); + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase3( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)2, (short)10 ); + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + sheet1.setColumnGroupCollapsed( (short)2, true ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase4( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)2, (short)10 ); + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + sheet1.setColumnGroupCollapsed( (short)2, true ); + + sheet1.setColumnGroupCollapsed( (short)4, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase5( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)2, (short)10 ); + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + sheet1.setColumnGroupCollapsed( (short)2, true ); + + sheet1.setColumnGroupCollapsed( (short)4, false ); + sheet1.setColumnGroupCollapsed( (short)3, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase6( String filename ) throws IOException{ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupColumn( (short)2, (short)10 ); + sheet1.groupColumn( (short)4, (short)10 ); + sheet1.setColumnGroupCollapsed( (short)4, true ); + sheet1.setColumnGroupCollapsed( (short)2, true ); + + sheet1.setColumnGroupCollapsed( (short)3, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase7( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 10 ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase8( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 10 ); + sheet1.setRowGroupCollapsed( 7, true ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase9( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 10 ); + sheet1.setRowGroupCollapsed( 7, true ); + sheet1.setRowGroupCollapsed( 5, true ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + + private static void createCase10( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 10 ); + sheet1.setRowGroupCollapsed( 7, true ); + sheet1.setRowGroupCollapsed( 5, true ); + sheet1.setRowGroupCollapsed( 8, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase11( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 10 ); + sheet1.setRowGroupCollapsed( 7, true ); + sheet1.setRowGroupCollapsed( 5, true ); + sheet1.setRowGroupCollapsed( 8, false ); + sheet1.setRowGroupCollapsed( 14, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase12( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 14 ); + sheet1.setRowGroupCollapsed( 7, true ); + sheet1.setRowGroupCollapsed( 5, true ); + sheet1.setRowGroupCollapsed( 6, false ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + private static void createCase13( String filename ) + throws IOException + { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("new sheet"); + + sheet1.groupRow( 5, 14 ); + sheet1.groupRow( 7, 14 ); + sheet1.groupRow( 16, 19 ); + + sheet1.groupColumn( (short)4, (short)7 ); + sheet1.groupColumn( (short)9, (short)12 ); + sheet1.groupColumn( (short)10, (short)11 ); + + FileOutputStream fileOut = new FileOutputStream(filename); + wb.write(fileOut); + fileOut.close(); + } + + +} diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 90909662b..1a44d6e57 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.*; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.util.IntList; import org.apache.poi.util.POILogFactory; @@ -60,36 +61,37 @@ public class Sheet implements Model public static final short TopMargin = 2; public static final short BottomMargin = 3; - protected ArrayList records = null; - int preoffset = 0; // offset of the sheet in a new file - int loc = 0; - protected boolean containsLabels = false; - protected int dimsloc = 0; - protected DimensionsRecord dims; - protected DefaultColWidthRecord defaultcolwidth = null; - protected DefaultRowHeightRecord defaultrowheight = null; - protected GridsetRecord gridset = null; - protected PrintSetupRecord printSetup = null; - protected HeaderRecord header = null; - protected FooterRecord footer = null; - protected PrintGridlinesRecord printGridlines = null; - protected WindowTwoRecord windowTwo = null; - protected MergeCellsRecord merged = null; - protected Margin margins[] = null; - protected List mergedRecords = new ArrayList(); - protected int numMergedRegions = 0; - protected SelectionRecord selection = null; private static POILogger log = POILogFactory.getLogger(Sheet.class); - private ArrayList columnSizes = null; // holds column info - protected ValueRecordsAggregate cells = null; - protected RowRecordsAggregate rows = null; - private Iterator valueRecIterator = null; - private Iterator rowRecIterator = null; - protected int eofLoc = 0; - protected ProtectRecord protect = null; - protected PageBreakRecord rowBreaks = null; - protected PageBreakRecord colBreaks = null; - + + protected ArrayList records = null; + int preoffset = 0; // offset of the sheet in a new file + int loc = 0; + protected boolean containsLabels = false; + protected int dimsloc = 0; + protected DimensionsRecord dims; + protected DefaultColWidthRecord defaultcolwidth = null; + protected DefaultRowHeightRecord defaultrowheight = null; + protected GridsetRecord gridset = null; + protected PrintSetupRecord printSetup = null; + protected HeaderRecord header = null; + protected FooterRecord footer = null; + protected PrintGridlinesRecord printGridlines = null; + protected WindowTwoRecord windowTwo = null; + protected MergeCellsRecord merged = null; + protected Margin[] margins = null; + protected List mergedRecords = new ArrayList(); + protected int numMergedRegions = 0; + protected SelectionRecord selection = null; + protected ColumnInfoRecordsAggregate columns = null; + protected ValueRecordsAggregate cells = null; + protected RowRecordsAggregate rows = null; + private Iterator valueRecIterator = null; + private Iterator rowRecIterator = null; + protected int eofLoc = 0; + protected ProtectRecord protect = null; + protected PageBreakRecord rowBreaks = null; + protected PageBreakRecord colBreaks = null; + public static final byte PANE_LOWER_RIGHT = (byte)0; public static final byte PANE_UPPER_RIGHT = (byte)1; @@ -123,9 +125,10 @@ public class Sheet implements Model */ public static Sheet createSheet(List recs, int sheetnum, int offset) { - log.logFormatted(POILogger.DEBUG, - "Sheet createSheet (existing file) with %", - new Integer(recs.size())); + if (log.check( POILogger.DEBUG )) + log.logFormatted(POILogger.DEBUG, + "Sheet createSheet (existing file) with %", + new Integer(recs.size())); Sheet retval = new Sheet(); ArrayList records = new ArrayList(recs.size() / 5); boolean isfirstcell = true; @@ -138,18 +141,21 @@ public class Sheet implements Model if (rec.getSid() == LabelRecord.sid) { - log.log(POILogger.DEBUG, "Hit label record."); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Hit label record."); retval.containsLabels = true; } else if (rec.getSid() == BOFRecord.sid) { bofEofNestingLevel++; - log.log(POILogger.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel); } else if (rec.getSid() == EOFRecord.sid) { --bofEofNestingLevel; - log.log(POILogger.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel); if (bofEofNestingLevel == 0) { records.add(rec); retval.eofLoc = k; @@ -158,6 +164,13 @@ public class Sheet implements Model } else if (rec.getSid() == DimensionsRecord.sid) { + // Make a columns aggregate if one hasn't ready been created. + if (retval.columns == null) + { + retval.columns = new ColumnInfoRecordsAggregate(); + records.add(retval.columns); + } + retval.dims = ( DimensionsRecord ) rec; retval.dimsloc = records.size(); } @@ -169,11 +182,16 @@ public class Sheet implements Model } else if (rec.getSid() == ColumnInfoRecord.sid) { - if (retval.columnSizes == null) + ColumnInfoRecord col = (ColumnInfoRecord)rec; + if (retval.columns != null) { - retval.columnSizes = new ArrayList(); + rec = null; //only add the aggregate once } - retval.columnSizes.add(rec); + else + { + rec = retval.columns = new ColumnInfoRecordsAggregate(); + } + retval.columns.insertColumn(col); } else if (rec.getSid() == DefaultColWidthRecord.sid) { @@ -283,7 +301,8 @@ public class Sheet implements Model // { // retval.cells = new ValueRecordsAggregate(); // } - log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited"); return retval; } @@ -342,8 +361,9 @@ public class Sheet implements Model public static Sheet createSheet(List records, int sheetnum) { - log.log(POILogger.DEBUG, - "Sheet createSheet (exisiting file) assumed offset 0"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, + "Sheet createSheet (exisiting file) assumed offset 0"); return createSheet(records, sheetnum, 0); } @@ -357,7 +377,8 @@ public class Sheet implements Model public static Sheet createSheet() { - log.log(POILogger.DEBUG, "Sheet createsheet from scratch called"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet createsheet from scratch called"); Sheet retval = new Sheet(); ArrayList records = new ArrayList(30); @@ -397,7 +418,10 @@ public class Sheet implements Model retval.defaultcolwidth = (DefaultColWidthRecord) retval.createDefaultColWidth(); records.add( retval.defaultcolwidth); - retval.dims = ( DimensionsRecord ) retval.createDimensions(); + ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate(); + records.add( columns ); + retval.columns = columns; + retval.dims = ( DimensionsRecord ) retval.createDimensions(); records.add(retval.dims); retval.dimsloc = records.size()-1; records.add(retval.windowTwo = retval.createWindowTwo()); @@ -408,9 +432,11 @@ public class Sheet implements Model retval.protect = (ProtectRecord) retval.createProtect(); records.add(retval.protect); records.add(retval.createEOF()); - + + retval.records = records; - log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit"); return retval; } @@ -549,7 +575,8 @@ public class Sheet implements Model public void convertLabelRecords(Workbook wb) { - log.log(POILogger.DEBUG, "convertLabelRecords called"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "convertLabelRecords called"); if (containsLabels) { for (int k = 0; k < records.size(); k++) @@ -573,7 +600,8 @@ public class Sheet implements Model } } } - log.log(POILogger.DEBUG, "convertLabelRecords exit"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "convertLabelRecords exit"); } /** @@ -587,14 +615,17 @@ public class Sheet implements Model { checkCells(); checkRows(); - log.log(POILogger.DEBUG, "Sheet.getNumRecords"); - log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[] + if (log.check( POILogger.DEBUG )) { - records.size(), cells.getPhysicalNumberOfCells(), - rows.getPhysicalNumberOfRows(), - records.size() + cells.getPhysicalNumberOfCells() - + rows.getPhysicalNumberOfRows() - 2 - }); + log.log(POILogger.DEBUG, "Sheet.getNumRecords"); + log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[] + { + records.size(), cells.getPhysicalNumberOfCells(), + rows.getPhysicalNumberOfRows(), + records.size() + cells.getPhysicalNumberOfCells() + + rows.getPhysicalNumberOfRows() - 2 + }); + } return records.size() + cells.getPhysicalNumberOfCells() + rows.getPhysicalNumberOfRows() - 2; } @@ -611,17 +642,21 @@ public class Sheet implements Model public void setDimensions(int firstrow, short firstcol, int lastrow, short lastcol) { - log.log(POILogger.DEBUG, "Sheet.setDimensions"); - log.log(POILogger.DEBUG, - (new StringBuffer("firstrow")).append(firstrow) - .append("firstcol").append(firstcol).append("lastrow") - .append(lastrow).append("lastcol").append(lastcol) - .toString()); + if (log.check( POILogger.DEBUG )) + { + log.log(POILogger.DEBUG, "Sheet.setDimensions"); + log.log(POILogger.DEBUG, + (new StringBuffer("firstrow")).append(firstrow) + .append("firstcol").append(firstcol).append("lastrow") + .append(lastrow).append("lastcol").append(lastcol) + .toString()); + } dims.setFirstCol(firstcol); dims.setFirstRow(firstrow); dims.setLastCol(lastcol); dims.setLastRow(lastrow); - log.log(POILogger.DEBUG, "Sheet.setDimensions exiting"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet.setDimensions exiting"); } /** @@ -643,7 +678,8 @@ public class Sheet implements Model public void setLoc(int loc) { valueRecIterator = null; - log.log(POILogger.DEBUG, "sheet.setLoc(): " + loc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "sheet.setLoc(): " + loc); this.loc = loc; } @@ -654,7 +690,8 @@ public class Sheet implements Model public int getLoc() { - log.log(POILogger.DEBUG, "sheet.getLoc():" + loc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "sheet.getLoc():" + loc); return loc; } @@ -692,7 +729,8 @@ public class Sheet implements Model public byte [] serialize() { - log.log(POILogger.DEBUG, "Sheet.serialize"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet.serialize"); // addDBCellRecords(); byte[] retval = null; @@ -721,7 +759,8 @@ public class Sheet implements Model pos += (( Record ) records.get(k)).serialize(pos, retval); // rec.length; } - log.log(POILogger.DEBUG, "Sheet.serialize returning " + retval); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet.serialize returning " + retval); return retval; } @@ -736,7 +775,8 @@ public class Sheet implements Model public int serialize(int offset, byte [] data) { - log.log(POILogger.DEBUG, "Sheet.serialize using offsets"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet.serialize using offsets"); // addDBCellRecords(); // ArrayList bytes = new ArrayList(4096); @@ -775,7 +815,8 @@ public class Sheet implements Model pos += record.serialize(pos + offset, data ); // rec.length; } - log.log(POILogger.DEBUG, "Sheet.serialize returning "); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "Sheet.serialize returning "); return pos; } @@ -789,16 +830,7 @@ public class Sheet implements Model public RowRecord createRow(int row) { - log.log(POILogger.DEBUG, "create row number " + row); - RowRecord rowrec = new RowRecord(); - - //rowrec.setRowNumber(( short ) row); - rowrec.setRowNumber(row); - rowrec.setHeight(( short ) 0xff); - rowrec.setOptimize(( short ) 0x0); - rowrec.setOptionFlags(( short ) 0x0); - rowrec.setXFIndex(( short ) 0x0); - return rowrec; + return RowRecordsAggregate.createRow( row ); } /** @@ -1032,7 +1064,8 @@ public class Sheet implements Model { checkCells(); setLoc(dimsloc); - log.log(POILogger.DEBUG, "replaceValueRecord "); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "replaceValueRecord "); cells.insertCell(newval); /* @@ -1068,7 +1101,8 @@ public class Sheet implements Model public void addRow(RowRecord row) { checkRows(); - log.log(POILogger.DEBUG, "addRow "); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "addRow "); DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc()); if (row.getRowNumber() > d.getLastRow()) @@ -1122,7 +1156,8 @@ public class Sheet implements Model * } * } */ - log.log(POILogger.DEBUG, "exit addRow"); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "exit addRow"); } /** @@ -1182,7 +1217,8 @@ public class Sheet implements Model public CellValueRecordInterface getNextValueRecord() { - log.log(POILogger.DEBUG, "getNextValue loc= " + loc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "getNextValue loc= " + loc); if (valueRecIterator == null) { valueRecIterator = cells.getIterator(); @@ -1269,7 +1305,8 @@ public class Sheet implements Model public RowRecord getNextRow() { - log.log(POILogger.DEBUG, "getNextRow loc= " + loc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "getNextRow loc= " + loc); if (rowRecIterator == null) { rowRecIterator = rows.getIterator(); @@ -1315,7 +1352,8 @@ public class Sheet implements Model //public RowRecord getRow(short rownum) public RowRecord getRow(int rownum) { - log.log(POILogger.DEBUG, "getNextRow loc= " + loc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "getNextRow loc= " + loc); return rows.getRow(rownum); /* @@ -1787,12 +1825,7 @@ public class Sheet implements Model protected Record createColInfo() { - ColumnInfoRecord retval = new ColumnInfoRecord(); - - retval.setColumnWidth(( short ) 0x8); - retval.setOptions(( short ) 6); - retval.setXFIndex(( short ) 0x0f); - return retval; + return ColumnInfoRecordsAggregate.createColInfo(); } /** @@ -1867,13 +1900,12 @@ public class Sheet implements Model { short retval = 0; ColumnInfoRecord ci = null; - int k = 0; - if (columnSizes != null) + if (columns != null) { - for (k = 0; k < columnSizes.size(); k++) + for ( Iterator iterator = columns.getIterator(); iterator.hasNext(); ) { - ci = ( ColumnInfoRecord ) columnSizes.get(k); + ci = ( ColumnInfoRecord ) iterator.next(); if ((ci.getFirstColumn() <= column) && (column <= ci.getLastColumn())) { @@ -1898,104 +1930,46 @@ public class Sheet implements Model * @param column - the column number * @param width (in units of 1/20th of a character width) */ - public void setColumnWidth(short column, short width) { - ColumnInfoRecord ci = null; - int k = 0; + setColumn( column, new Short(width), null, null, null); + } - if (columnSizes == null) + public void setColumn(short column, Short width, Integer level, Boolean hidden, Boolean collapsed) + { + if (columns == null) + columns = new ColumnInfoRecordsAggregate(); + + columns.setColumn( column, width, level, hidden, collapsed ); + } + + /** + * Creates an outline group for the specified columns. + * @param fromColumn group from this column (inclusive) + * @param toColumn group to this column (inclusive) + * @param indent if true the group will be indented by one level, + * if false indenting will be removed by one level. + */ + public void groupColumnRange(short fromColumn, short toColumn, boolean indent) + { + + // Set the level for each column + columns.groupColumnRange( fromColumn, toColumn, indent); + + // Determine the maximum overall level + int maxLevel = 0; + for ( Iterator iterator = columns.getIterator(); iterator.hasNext(); ) { - columnSizes = new ArrayList(); + ColumnInfoRecord columnInfoRecord = (ColumnInfoRecord) iterator.next(); + maxLevel = Math.max(columnInfoRecord.getOutlineLevel(), maxLevel); } - //int cioffset = getDimsLoc() - columnSizes.size(); - for (k = 0; k < columnSizes.size(); k++) - { - ci = ( ColumnInfoRecord ) columnSizes.get(k); - if ((ci.getFirstColumn() <= column) - && (column <= ci.getLastColumn())) - { - break; - } - ci = null; - } - if (ci != null) - { - if (ci.getColumnWidth() == width) - { - - // do nothing...the cell's width is equal to what we're setting it to. - } - else if ((ci.getFirstColumn() == column) - && (ci.getLastColumn() == column)) - { // if its only for this cell then - ci.setColumnWidth(width); // who cares, just change the width - } - else if ((ci.getFirstColumn() == column) - || (ci.getLastColumn() == column)) - { - - // okay so the width is different but the first or last column == the column we'return setting - // we'll just divide the info and create a new one - if (ci.getFirstColumn() == column) - { - ci.setFirstColumn(( short ) (column + 1)); - } - else - { - ci.setLastColumn(( short ) (column - 1)); - } - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - - nci.setFirstColumn(column); - nci.setLastColumn(column); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - nci.setColumnWidth(width); - columnSizes.add(k, nci); - records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci); - dimsloc++; - } - else{ - //split to 3 records - short lastcolumn = ci.getLastColumn(); - ci.setLastColumn(( short ) (column - 1)); - - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - nci.setFirstColumn(column); - nci.setLastColumn(column); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - nci.setColumnWidth(width); - columnSizes.add(k, nci); - records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci); - dimsloc++; - - nci = ( ColumnInfoRecord ) createColInfo(); - nci.setFirstColumn((short)(column+1)); - nci.setLastColumn(lastcolumn); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - nci.setColumnWidth(ci.getColumnWidth()); - columnSizes.add(k, nci); - records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci); - dimsloc++; - } - } + GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid ); + guts.setColLevelMax( (short) ( maxLevel+1 ) ); + if (maxLevel == 0) + guts.setTopColGutter( (short)0 ); else - { - - // okay so there ISN'T a column info record that cover's this column so lets create one! - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - - nci.setFirstColumn(column); - nci.setLastColumn(column); - nci.setColumnWidth(width); - columnSizes.add(k, nci); - records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci); - dimsloc++; - } + guts.setTopColGutter( (short) ( 29 + (12 * (maxLevel-1)) ) ); } /** @@ -2125,7 +2099,6 @@ public class Sheet implements Model protected Record createMergedCells() { MergeCellsRecord retval = new MergeCellsRecord(); - retval.setNumAreas(( short ) 0); return retval; } @@ -2149,7 +2122,8 @@ public class Sheet implements Model public int getDimsLoc() { - log.log(POILogger.DEBUG, "getDimsLoc dimsloc= " + dimsloc); + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "getDimsLoc dimsloc= " + dimsloc); return dimsloc; } @@ -2433,25 +2407,8 @@ public class Sheet implements Model windowTwo.setFreezePanesNoSplit(true); SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid); -// SelectionRecord sel2 = (SelectionRecord) sel.clone(); -// SelectionRecord sel3 = (SelectionRecord) sel.clone(); -// SelectionRecord sel4 = (SelectionRecord) sel.clone(); -// sel.setPane(PANE_LOWER_RIGHT); // 0 -// sel3.setPane(PANE_UPPER_RIGHT); // 1 - sel.setPane((byte)pane.getActivePane()); // 2 -// sel2.setPane(PANE_UPPER_LEFT); // 3 -// sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit)); -// sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit)); + sel.setPane((byte)pane.getActivePane()); - int selLoc = findFirstRecordLocBySid(SelectionRecord.sid); -// sel.setActiveCellCol((short)15); -// sel.setActiveCellRow((short)15); -// sel2.setActiveCellCol((short)0); -// sel2.setActiveCellRow((short)0); - -// records.add(selLoc+1,sel2); -// records.add(selLoc+2,sel3); -// records.add(selLoc+3,sel4); } /** @@ -2482,25 +2439,8 @@ public class Sheet implements Model windowTwo.setFreezePanesNoSplit(false); SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid); -// SelectionRecord sel2 = (SelectionRecord) sel.clone(); -// SelectionRecord sel3 = (SelectionRecord) sel.clone(); -// SelectionRecord sel4 = (SelectionRecord) sel.clone(); - sel.setPane(PANE_LOWER_RIGHT); // 0 -// sel3.setPane(PANE_UPPER_RIGHT); // 1 -// sel4.setPane(PANE_LOWER_LEFT); // 2 -// sel2.setPane(PANE_UPPER_LEFT); // 3 -// sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit)); -// sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit)); + sel.setPane(PANE_LOWER_RIGHT); - int selLoc = findFirstRecordLocBySid(SelectionRecord.sid); -// sel.setActiveCellCol((short)15); -// sel.setActiveCellRow((short)15); -// sel2.setActiveCellCol((short)0); -// sel2.setActiveCellRow((short)0); - -// records.add(selLoc+1,sel2); -// records.add(selLoc+2,sel3); -// records.add(selLoc+3,sel4); } public SelectionRecord getSelection() @@ -2512,27 +2452,28 @@ public class Sheet implements Model { this.selection = selection; } - /** - * creates a Protect record with protect set to false. - * @see org.apache.poi.hssf.record.ProtectRecord - * @see org.apache.poi.hssf.record.Record - * @return a ProtectRecord - */ - protected Record createProtect() - { - log.log(POILogger.DEBUG, "create protect record with protection disabled"); - ProtectRecord retval = new ProtectRecord(); + /** + * creates a Protect record with protect set to false. + * @see org.apache.poi.hssf.record.ProtectRecord + * @see org.apache.poi.hssf.record.Record + * @return a ProtectRecord + */ + protected Record createProtect() + { + if (log.check( POILogger.DEBUG )) + log.log(POILogger.DEBUG, "create protect record with protection disabled"); + ProtectRecord retval = new ProtectRecord(); - retval.setProtect(false); - // by default even when we support encryption we won't - return retval; - } + retval.setProtect(false); + // by default even when we support encryption we won't + return retval; + } - public ProtectRecord getProtect() - { - return protect; - } + public ProtectRecord getProtect() + { + return protect; + } /** * Sets whether the gridlines are shown in a viewer. @@ -2705,7 +2646,7 @@ public class Sheet implements Model /** * Sets a page break at the indicated column - * @param row + * */ public void setColumnBreak(short column, short fromRow, short toRow) { colBreaks.addBreak(column, fromRow, toRow); @@ -2713,7 +2654,7 @@ public class Sheet implements Model /** * Removes a page break at the indicated column - * @param row + * */ public void removeColumnBreak(short column) { colBreaks.removeBreak(column); @@ -2721,7 +2662,7 @@ public class Sheet implements Model /** * Queries if the specified column has a page break - * @param row + * * @return true if the specified column has a page break */ public boolean isColumnBroken(short column) { @@ -2779,4 +2720,360 @@ public class Sheet implements Model public int getNumColumnBreaks(){ return (int)colBreaks.getNumBreaks(); } + + public void setColumnGroupCollapsed( short columnNumber, boolean collapsed ) + { + if (collapsed) + { + columns.collapseColumn( columnNumber ); + } + else + { + columns.expandColumn( columnNumber ); + } + } + +// private void collapseColumn( short columnNumber ) +// { +// int idx = findColumnIdx( columnNumber, 0 ); +// if (idx == -1) +// return; +// +// // Find the start of the group. +// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( findStartOfColumnOutlineGroup( idx ) ); +// +// // Hide all the columns until the end of the group +// columnInfo = writeHidden( columnInfo, idx, true ); +// +// // Write collapse field +// setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.TRUE); +// } + +// private void expandColumn( short columnNumber ) +// { +// int idx = findColumnIdx( columnNumber, 0 ); +// if (idx == -1) +// return; +// +// // If it is already exapanded do nothing. +// if (!isColumnGroupCollapsed(idx)) +// return; +// +// // Find the start of the group. +// int startIdx = findStartOfColumnOutlineGroup( idx ); +// ColumnInfoRecord columnInfo = getColInfo( startIdx ); +// +// // Find the end of the group. +// int endIdx = findEndOfColumnOutlineGroup( idx ); +// ColumnInfoRecord endColumnInfo = getColInfo( endIdx ); +// +// // expand: +// // colapsed bit must be unset +// // hidden bit gets unset _if_ surrounding groups are expanded you can determine +// // this by looking at the hidden bit of the enclosing group. You will have +// // to look at the start and the end of the current group to determine which +// // is the enclosing group +// // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups +// if (!isColumnGroupHiddenByParent( idx )) +// { +// for (int i = startIdx; i <= endIdx; i++) +// { +// if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel()) +// getColInfo(i).setHidden( false ); +// } +// } +// +// // Write collapse field +// setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.FALSE); +// } + +// private boolean isColumnGroupCollapsed( int idx ) +// { +// int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); +// if (endOfOutlineGroupIdx >= columnSizes.size()) +// return false; +// if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) +// return false; +// else +// return getColInfo(endOfOutlineGroupIdx+1).getCollapsed(); +// } + +// private boolean isColumnGroupHiddenByParent( int idx ) +// { +// // Look out outline details of end +// int endLevel; +// boolean endHidden; +// int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); +// if (endOfOutlineGroupIdx >= columnSizes.size()) +// { +// endLevel = 0; +// endHidden = false; +// } +// else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) +// { +// endLevel = 0; +// endHidden = false; +// } +// else +// { +// endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel(); +// endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden(); +// } +// +// // Look out outline details of start +// int startLevel; +// boolean startHidden; +// int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx ); +// if (startOfOutlineGroupIdx <= 0) +// { +// startLevel = 0; +// startHidden = false; +// } +// else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn()) +// { +// startLevel = 0; +// startHidden = false; +// } +// else +// { +// startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel(); +// startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden(); +// } +// +// if (endLevel > startLevel) +// { +// return endHidden; +// } +// else +// { +// return startHidden; +// } +// } + +// private ColumnInfoRecord getColInfo(int idx) +// { +// return columns.getColInfo( idx ); +// } + +// private int findStartOfColumnOutlineGroup(int idx) +// { +// // Find the start of the group. +// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( idx ); +// int level = columnInfo.getOutlineLevel(); +// while (idx != 0) +// { +// ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) columnSizes.get( idx - 1 ); +// if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn()) +// { +// if (prevColumnInfo.getOutlineLevel() < level) +// { +// break; +// } +// idx--; +// columnInfo = prevColumnInfo; +// } +// else +// { +// break; +// } +// } +// +// return idx; +// } + +// private int findEndOfColumnOutlineGroup(int idx) +// { +// // Find the end of the group. +// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( idx ); +// int level = columnInfo.getOutlineLevel(); +// while (idx < columnSizes.size() - 1) +// { +// ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) columnSizes.get( idx + 1 ); +// if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) +// { +// if (nextColumnInfo.getOutlineLevel() < level) +// { +// break; +// } +// idx++; +// columnInfo = nextColumnInfo; +// } +// else +// { +// break; +// } +// } +// +// return idx; +// } + + public void groupRowRange(int fromRow, int toRow, boolean indent) + { + checkRows(); + for (int rowNum = fromRow; rowNum <= toRow; rowNum++) + { + RowRecord row = getRow( rowNum ); + if (row == null) + { + row = createRow( rowNum ); + addRow( row ); + } + int level = row.getOutlineLevel(); + if (indent) level++; else level--; + level = Math.max(0, level); + level = Math.min(7, level); + row.setOutlineLevel((short) ( level )); + } + + recalcRowGutter(); + } + + private void recalcRowGutter() + { + int maxLevel = 0; + Iterator iterator = rows.getIterator(); + while ( iterator.hasNext() ) + { + RowRecord rowRecord = (RowRecord) iterator.next(); + maxLevel = Math.max(rowRecord.getOutlineLevel(), maxLevel); + } + + GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid ); + guts.setRowLevelMax( (short) ( maxLevel + 1 ) ); + guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) ); + } + + public void setRowGroupCollapsed( int row, boolean collapse ) + { + if (collapse) + { + rows.collapseRow( row ); + } + else + { + rows.expandRow( row ); + } + } + + +// private void collapseRow( int rowNumber ) +// { +// +// // Find the start of the group. +// int startRow = rows.findStartOfRowOutlineGroup( rowNumber ); +// RowRecord rowRecord = (RowRecord) rows.getRow( startRow ); +// +// // Hide all the columns until the end of the group +// int lastRow = rows.writeHidden( rowRecord, startRow, true ); +// +// // Write collapse field +// if (getRow(lastRow + 1) != null) +// { +// getRow(lastRow + 1).setColapsed( true ); +// } +// else +// { +// RowRecord row = createRow( lastRow + 1); +// row.setColapsed( true ); +// rows.insertRow( row ); +// } +// } + +// private int findStartOfRowOutlineGroup(int row) +// { +// // Find the start of the group. +// RowRecord rowRecord = rows.getRow( row ); +// int level = rowRecord.getOutlineLevel(); +// int currentRow = row; +// while (rows.getRow( currentRow ) != null) +// { +// rowRecord = rows.getRow( currentRow ); +// if (rowRecord.getOutlineLevel() < level) +// return currentRow + 1; +// currentRow--; +// } +// +// return currentRow + 1; +// } + +// private int writeHidden( RowRecord rowRecord, int row, boolean hidden ) +// { +// int level = rowRecord.getOutlineLevel(); +// while (rowRecord != null && rows.getRow(row).getOutlineLevel() >= level) +// { +// rowRecord.setZeroHeight( hidden ); +// row++; +// rowRecord = rows.getRow( row ); +// } +// return row - 1; +// } + +// private int findEndOfRowOutlineGroup( int row ) +// { +// int level = getRow( row ).getOutlineLevel(); +// int currentRow; +// for (currentRow = row; currentRow < rows.getLastRowNum(); currentRow++) +// { +// if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level) +// { +// break; +// } +// } +// +// return currentRow-1; +// } + +// private boolean isRowGroupCollapsed( int row ) +// { +// int collapseRow = rows.findEndOfRowOutlineGroup( row ) + 1; +// +// if (getRow(collapseRow) == null) +// return false; +// else +// return getRow( collapseRow ).getColapsed(); +// } + + +// private boolean isRowGroupHiddenByParent( int row ) +// { +// // Look out outline details of end +// int endLevel; +// boolean endHidden; +// int endOfOutlineGroupIdx = rows.findEndOfRowOutlineGroup( row ); +// if (getRow( endOfOutlineGroupIdx + 1 ) == null) +// { +// endLevel = 0; +// endHidden = false; +// } +// else +// { +// endLevel = getRow( endOfOutlineGroupIdx + 1).getOutlineLevel(); +// endHidden = getRow( endOfOutlineGroupIdx + 1).getZeroHeight(); +// } +// +// // Look out outline details of start +// int startLevel; +// boolean startHidden; +// int startOfOutlineGroupIdx = rows.findStartOfRowOutlineGroup( row ); +// if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null) +// { +// startLevel = 0; +// startHidden = false; +// } +// else +// { +// startLevel = getRow( startOfOutlineGroupIdx - 1).getOutlineLevel(); +// startHidden = getRow( startOfOutlineGroupIdx - 1 ).getZeroHeight(); +// } +// +// if (endLevel > startLevel) +// { +// return endHidden; +// } +// else +// { +// return startHidden; +// } +// } + } diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 8ce327270..9b0a18f36 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -122,8 +122,9 @@ public class Workbook implements Model * @return Workbook object */ public static Workbook createWorkbook(List recs) { - log.log(DEBUG, "Workbook (readfile) created with reclen=", - new Integer(recs.size())); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "Workbook (readfile) created with reclen=", + new Integer(recs.size())); Workbook retval = new Workbook(); ArrayList records = new ArrayList(recs.size() / 3); @@ -132,85 +133,100 @@ public class Workbook implements Model if (rec.getSid() == EOFRecord.sid) { records.add(rec); - log.log(DEBUG, "found workbook eof record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found workbook eof record at " + k); break; } switch (rec.getSid()) { case BoundSheetRecord.sid : - log.log(DEBUG, "found boundsheet record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found boundsheet record at " + k); retval.boundsheets.add(rec); retval.records.setBspos( k ); break; case SSTRecord.sid : - log.log(DEBUG, "found sst record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found sst record at " + k); retval.sst = ( SSTRecord ) rec; break; case FontRecord.sid : - log.log(DEBUG, "found font record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found font record at " + k); retval.records.setFontpos( k ); retval.numfonts++; break; case ExtendedFormatRecord.sid : - log.log(DEBUG, "found XF record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found XF record at " + k); retval.records.setXfpos( k ); retval.numxfs++; break; case TabIdRecord.sid : - log.log(DEBUG, "found tabid record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found tabid record at " + k); retval.records.setTabpos( k ); break; case ProtectRecord.sid : - log.log(DEBUG, "found protect record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found protect record at " + k); retval.records.setProtpos( k ); break; case BackupRecord.sid : - log.log(DEBUG, "found backup record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found backup record at " + k); retval.records.setBackuppos( k ); break; case ExternSheetRecord.sid : - log.log(DEBUG, "found extern sheet record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found extern sheet record at " + k); retval.externSheet = ( ExternSheetRecord ) rec; break; case NameRecord.sid : - log.log(DEBUG, "found name record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found name record at " + k); retval.names.add(rec); -// retval.records.namepos = k; + // retval.records.namepos = k; break; case SupBookRecord.sid : - log.log(DEBUG, "found SupBook record at " + k); -// retval.records.supbookpos = k; + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found SupBook record at " + k); + // retval.records.supbookpos = k; + break; + case FormatRecord.sid : + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found format record at " + k); + retval.formats.add(rec); + retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode(); break; - case FormatRecord.sid : - log.log(DEBUG, "found format record at " + k); - retval.formats.add(rec); - retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode(); - break; case DateWindow1904Record.sid : - log.log(DEBUG, "found datewindow1904 record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found datewindow1904 record at " + k); retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1; break; case PaletteRecord.sid: - log.log(DEBUG, "found palette record at " + k); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found palette record at " + k); retval.records.setPalettepos( k ); default : } records.add(rec); } //What if we dont have any ranges and supbooks -// if (retval.records.supbookpos == 0) { -// retval.records.supbookpos = retval.records.bspos + 1; -// retval.records.namepos = retval.records.supbookpos + 1; -// } + // if (retval.records.supbookpos == 0) { + // retval.records.supbookpos = retval.records.bspos + 1; + // retval.records.namepos = retval.records.supbookpos + 1; + // } retval.records.setRecords(records); - log.log(DEBUG, "exit create workbook from existing file function"); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "exit create workbook from existing file function"); return retval; } @@ -220,7 +236,8 @@ public class Workbook implements Model */ public static Workbook createWorkbook() { - log.log( DEBUG, "creating new workbook from scratch" ); + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "creating new workbook from scratch" ); Workbook retval = new Workbook(); ArrayList records = new ArrayList( 30 ); ArrayList formats = new ArrayList( 8 ); @@ -296,7 +313,8 @@ public class Workbook implements Model records.add( retval.createEOF() ); retval.records.setRecords(records); - log.log( DEBUG, "exit create new workbook from scratch" ); + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "exit create new workbook from scratch" ); return retval; } @@ -403,8 +421,9 @@ public class Workbook implements Model */ public void setSheetBof(int sheetnum, int pos) { - log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetnum), - " at pos=", new Integer(pos)); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetnum), + " at pos=", new Integer(pos)); checkSheets(sheetnum); (( BoundSheetRecord ) boundsheets.get(sheetnum)) .setPositionOfBof(pos); @@ -535,7 +554,8 @@ public class Workbook implements Model */ public int getNumSheets() { - log.log(DEBUG, "getNumSheets=", new Integer(boundsheets.size())); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "getNumSheets=", new Integer(boundsheets.size())); return boundsheets.size(); } @@ -546,7 +566,8 @@ public class Workbook implements Model */ public int getNumExFormats() { - log.log(DEBUG, "getXF=", new Integer(numxfs)); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "getXF=", new Integer(numxfs)); return numxfs; } @@ -593,7 +614,8 @@ public class Workbook implements Model */ public int addSSTString(String string, boolean use16bits) { - log.log(DEBUG, "insert to sst string='", string, "' and use16bits= ", + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "insert to sst string='", string, "' and use16bits= ", new Boolean(use16bits)); if (sst == null) { insertSST(); @@ -626,8 +648,9 @@ public class Workbook implements Model } String retval = sst.getString(str); - log.log(DEBUG, "Returning SST for index=", new Integer(str), - " String= ", retval); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "Returning SST for index=", new Integer(str), + " String= ", retval); return retval; } @@ -639,7 +662,8 @@ public class Workbook implements Model */ public void insertSST() { - log.log(DEBUG, "creating new SST via insertSST!"); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "creating new SST via insertSST!"); sst = ( SSTRecord ) createSST(); records.add(records.size() - 1, createExtendedSST()); records.add(records.size() - 2, sst); @@ -682,7 +706,8 @@ public class Workbook implements Model public int serialize( int offset, byte[] data ) { - log.log( DEBUG, "Serializing Workbook with offsets" ); + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "Serializing Workbook with offsets" ); int pos = 0; @@ -707,7 +732,8 @@ public class Workbook implements Model pos += record.serialize( pos + offset, data ); // rec.length; } } - log.log( DEBUG, "Exiting serialize workbook" ); + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "Exiting serialize workbook" ); return pos; } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java new file mode 100644 index 000000000..6eb9b111d --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java @@ -0,0 +1,509 @@ +package org.apache.poi.hssf.record.aggregates; + +import org.apache.poi.hssf.record.ColumnInfoRecord; +import org.apache.poi.hssf.record.Record; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * @author Glen Stampoultzis + * @version $Id$ + */ +public class ColumnInfoRecordsAggregate + extends Record +{ + int size = 0; + List records = null; + + public ColumnInfoRecordsAggregate() + { + records = new ArrayList(); + } + + /** You never fill an aggregate */ + protected void fillFields(byte [] data, short size, int offset) + { + } + + /** Not required by an aggregate */ + protected void validateSid(short id) + { + } + + /** It's an aggregate... just made something up */ + public short getSid() + { + return -1012; + } + + public int getRecordSize() + { + return size; + } + + public Iterator getIterator() + { + return records.iterator(); + } + + /** + * Performs a deep clone of the record + */ + public Object clone() + { + ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate(); + for ( Iterator colIter = getIterator(); colIter.hasNext(); ) + { + //return the cloned Row Record & insert + ColumnInfoRecord col = (ColumnInfoRecord) ( (ColumnInfoRecord) colIter.next() ).clone(); + rec.insertColumn( col ); + } + return rec; + } + + /** + * Inserts a column into the aggregate (at the end of the list). + */ + public void insertColumn( ColumnInfoRecord col ) + { + size += col.getRecordSize(); + records.add( col ); + } + + /** + * Inserts a column into the aggregate (at the position specified + * by idx. + */ + public void insertColumn( int idx, ColumnInfoRecord col ) + { + size += col.getRecordSize(); + records.add( idx, col ); + } + + public int getNumColumns( ) + { + return records.size(); + } + + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public int serialize(int offset, byte [] data) + { + Iterator itr = records.iterator(); + int pos = offset; + + while (itr.hasNext()) + { + pos += (( Record ) itr.next()).serialize(pos, data); + } + return pos - offset; + } + + public int findStartOfColumnOutlineGroup(int idx) + { + // Find the start of the group. + ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx ); + int level = columnInfo.getOutlineLevel(); + while (idx != 0) + { + ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get( idx - 1 ); + if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn()) + { + if (prevColumnInfo.getOutlineLevel() < level) + { + break; + } + idx--; + columnInfo = prevColumnInfo; + } + else + { + break; + } + } + + return idx; + } + + public int findEndOfColumnOutlineGroup(int idx) + { + // Find the end of the group. + ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx ); + int level = columnInfo.getOutlineLevel(); + while (idx < records.size() - 1) + { + ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get( idx + 1 ); + if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) + { + if (nextColumnInfo.getOutlineLevel() < level) + { + break; + } + idx++; + columnInfo = nextColumnInfo; + } + else + { + break; + } + } + + return idx; + } + + public ColumnInfoRecord getColInfo(int idx) + { + return (ColumnInfoRecord) records.get( idx ); + } + + public ColumnInfoRecord writeHidden( ColumnInfoRecord columnInfo, int idx, boolean hidden ) + { + int level = columnInfo.getOutlineLevel(); + while (idx < records.size()) + { + columnInfo.setHidden( hidden ); + if (idx + 1 < records.size()) + { + ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get( idx + 1 ); + if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) + { + if (nextColumnInfo.getOutlineLevel() < level) + break; + columnInfo = nextColumnInfo; + } + else + { + break; + } + } + idx++; + } + return columnInfo; + } + + public boolean isColumnGroupCollapsed( int idx ) + { + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); + if (endOfOutlineGroupIdx >= records.size()) + return false; + if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) + return false; + else + return getColInfo(endOfOutlineGroupIdx+1).getCollapsed(); + } + + + public boolean isColumnGroupHiddenByParent( int idx ) + { + // Look out outline details of end + int endLevel; + boolean endHidden; + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); + if (endOfOutlineGroupIdx >= records.size()) + { + endLevel = 0; + endHidden = false; + } + else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) + { + endLevel = 0; + endHidden = false; + } + else + { + endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel(); + endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden(); + } + + // Look out outline details of start + int startLevel; + boolean startHidden; + int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx ); + if (startOfOutlineGroupIdx <= 0) + { + startLevel = 0; + startHidden = false; + } + else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn()) + { + startLevel = 0; + startHidden = false; + } + else + { + startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel(); + startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden(); + } + + if (endLevel > startLevel) + { + return endHidden; + } + else + { + return startHidden; + } + } + + public void collapseColumn( short columnNumber ) + { + int idx = findColumnIdx( columnNumber, 0 ); + if (idx == -1) + return; + + // Find the start of the group. + ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( findStartOfColumnOutlineGroup( idx ) ); + + // Hide all the columns until the end of the group + columnInfo = writeHidden( columnInfo, idx, true ); + + // Write collapse field + setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.TRUE); + } + + public void expandColumn( short columnNumber ) + { + int idx = findColumnIdx( columnNumber, 0 ); + if (idx == -1) + return; + + // If it is already exapanded do nothing. + if (!isColumnGroupCollapsed(idx)) + return; + + // Find the start of the group. + int startIdx = findStartOfColumnOutlineGroup( idx ); + ColumnInfoRecord columnInfo = getColInfo( startIdx ); + + // Find the end of the group. + int endIdx = findEndOfColumnOutlineGroup( idx ); + ColumnInfoRecord endColumnInfo = getColInfo( endIdx ); + + // expand: + // colapsed bit must be unset + // hidden bit gets unset _if_ surrounding groups are expanded you can determine + // this by looking at the hidden bit of the enclosing group. You will have + // to look at the start and the end of the current group to determine which + // is the enclosing group + // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups + if (!isColumnGroupHiddenByParent( idx )) + { + for (int i = startIdx; i <= endIdx; i++) + { + if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel()) + getColInfo(i).setHidden( false ); + } + } + + // Write collapse field + setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.FALSE); + } + + /** + * creates the ColumnInfo Record and sets it to a default column/width + * @see org.apache.poi.hssf.record.ColumnInfoRecord + * @return record containing a ColumnInfoRecord + */ + public static Record createColInfo() + { + ColumnInfoRecord retval = new ColumnInfoRecord(); + + retval.setColumnWidth(( short ) 2275); + // was: retval.setOptions(( short ) 6); + retval.setOptions(( short ) 2); + retval.setXFIndex(( short ) 0x0f); + return retval; + } + + + public void setColumn(short column, Short width, Integer level, Boolean hidden, Boolean collapsed) + { + ColumnInfoRecord ci = null; + int k = 0; + + for (k = 0; k < records.size(); k++) + { + ci = ( ColumnInfoRecord ) records.get(k); + if ((ci.getFirstColumn() <= column) + && (column <= ci.getLastColumn())) + { + break; + } + ci = null; + } + + if (ci != null) + { + boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue(); + boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue(); + boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue(); + boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue(); + boolean columnChanged = widthChanged || levelChanged || hiddenChanged || collapsedChanged; + if (!columnChanged) + { + // do nothing...nothing changed. + } + else if ((ci.getFirstColumn() == column) + && (ci.getLastColumn() == column)) + { // if its only for this cell then + setColumnInfoFields( ci, width, level, hidden, collapsed ); + } + else if ((ci.getFirstColumn() == column) + || (ci.getLastColumn() == column)) + { + // okay so the width is different but the first or last column == the column we'return setting + // we'll just divide the info and create a new one + if (ci.getFirstColumn() == column) + { + ci.setFirstColumn(( short ) (column + 1)); + } + else + { + ci.setLastColumn(( short ) (column - 1)); + } + ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); + + nci.setFirstColumn(column); + nci.setLastColumn(column); + nci.setOptions(ci.getOptions()); + nci.setXFIndex(ci.getXFIndex()); + setColumnInfoFields( nci, width, level, hidden, collapsed ); + + insertColumn(k, nci); + } + else + { + //split to 3 records + short lastcolumn = ci.getLastColumn(); + ci.setLastColumn(( short ) (column - 1)); + + ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); + nci.setFirstColumn(column); + nci.setLastColumn(column); + nci.setOptions(ci.getOptions()); + nci.setXFIndex(ci.getXFIndex()); + setColumnInfoFields( nci, width, level, hidden, collapsed ); + insertColumn(++k, nci); + + nci = ( ColumnInfoRecord ) createColInfo(); + nci.setFirstColumn((short)(column+1)); + nci.setLastColumn(lastcolumn); + nci.setOptions(ci.getOptions()); + nci.setXFIndex(ci.getXFIndex()); + nci.setColumnWidth(ci.getColumnWidth()); + insertColumn(++k, nci); + } + } + else + { + + // okay so there ISN'T a column info record that cover's this column so lets create one! + ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); + + nci.setFirstColumn(column); + nci.setLastColumn(column); + setColumnInfoFields( nci, width, level, hidden, collapsed ); + insertColumn(k, nci); + } + } + + /** + * Sets all non null fields into the ci parameter. + */ + private void setColumnInfoFields( ColumnInfoRecord ci, Short width, Integer level, Boolean hidden, Boolean collapsed ) + { + if (width != null) + ci.setColumnWidth(width.shortValue()); + if (level != null) + ci.setOutlineLevel( level.shortValue() ); + if (hidden != null) + ci.setHidden( hidden.booleanValue() ); + if (collapsed != null) + ci.setCollapsed( collapsed.booleanValue() ); + } + + public int findColumnIdx(int column, int fromIdx) + { + if (column < 0) + throw new IllegalArgumentException( "column parameter out of range: " + column ); + if (fromIdx < 0) + throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromIdx ); + + ColumnInfoRecord ci; + for (int k = fromIdx; k < records.size(); k++) + { + ci = ( ColumnInfoRecord ) records.get(k); + if ((ci.getFirstColumn() <= column) + && (column <= ci.getLastColumn())) + { + return k; + } + ci = null; + } + return -1; + } + + public void collapseColInfoRecords( int columnIdx ) + { + if (columnIdx == 0) + return; + ColumnInfoRecord previousCol = (ColumnInfoRecord) records.get( columnIdx - 1); + ColumnInfoRecord currentCol = (ColumnInfoRecord) records.get( columnIdx ); + boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1; + if (!adjacentColumns) + return; + + boolean columnsMatch = + previousCol.getXFIndex() == currentCol.getXFIndex() && + previousCol.getOptions() == currentCol.getOptions() && + previousCol.getColumnWidth() == currentCol.getColumnWidth(); + + if (columnsMatch) + { + previousCol.setLastColumn( currentCol.getLastColumn() ); + records.remove( columnIdx ); + } + } + + /** + * Creates an outline group for the specified columns. + * @param fromColumn group from this column (inclusive) + * @param toColumn group to this column (inclusive) + * @param indent if true the group will be indented by one level, + * if false indenting will be removed by one level. + */ + public void groupColumnRange(short fromColumn, short toColumn, boolean indent) + { + + // Set the level for each column + int fromIdx = 0; + for (int i = fromColumn; i <= toColumn; i++) + { + int level = 1; + int columnIdx = findColumnIdx( i, Math.max(0,fromIdx) ); + if (columnIdx != -1) + { + level = ((ColumnInfoRecord)records.get( columnIdx )).getOutlineLevel(); + if (indent) level++; else level--; + level = Math.max(0, level); + level = Math.min(7, level); + fromIdx = columnIdx - 1; // subtract 1 just in case this column is collapsed later. + } + setColumn((short)i, null, new Integer(level), null, null); + columnIdx = findColumnIdx( i, Math.max(0, fromIdx ) ); + collapseColInfoRecords( columnIdx ); + } + + } + + +} diff --git a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java index 39e9542ba..32f25b312 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java @@ -20,12 +20,10 @@ package org.apache.poi.hssf.record.aggregates; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; -import org.apache.poi.hssf.record.UnknownRecord; +import java.util.Iterator; import java.util.Map; import java.util.TreeMap; -import java.util.Iterator; -import java.util.List; /** * @@ -46,7 +44,6 @@ public class RowRecordsAggregate public RowRecordsAggregate() { records = new TreeMap(); - } public void insertRow(RowRecord row) @@ -121,6 +118,7 @@ public class RowRecordsAggregate return k; } */ + /** * called by the class that is responsible for writing this sucker. * Subclasses should implement this so that their data is passed back in a @@ -186,15 +184,198 @@ public class RowRecordsAggregate return records.values().iterator(); } - /** Performs a deep clone of the record*/ - public Object clone() { - RowRecordsAggregate rec = new RowRecordsAggregate(); - for (Iterator rowIter = getIterator(); rowIter.hasNext();) { - //return the cloned Row Record & insert - RowRecord row = (RowRecord)((RowRecord)rowIter.next()).clone(); - rec.insertRow(row); - } - return rec; + /** + * Performs a deep clone of the record + */ + public Object clone() + { + RowRecordsAggregate rec = new RowRecordsAggregate(); + for ( Iterator rowIter = getIterator(); rowIter.hasNext(); ) + { + //return the cloned Row Record & insert + RowRecord row = (RowRecord) ( (RowRecord) rowIter.next() ).clone(); + rec.insertRow( row ); + } + return rec; + } + + + public int findStartOfRowOutlineGroup(int row) + { + // Find the start of the group. + RowRecord rowRecord = this.getRow( row ); + int level = rowRecord.getOutlineLevel(); + int currentRow = row; + while (this.getRow( currentRow ) != null) + { + rowRecord = this.getRow( currentRow ); + if (rowRecord.getOutlineLevel() < level) + return currentRow + 1; + currentRow--; + } + + return currentRow + 1; + } + + public int findEndOfRowOutlineGroup( int row ) + { + int level = getRow( row ).getOutlineLevel(); + int currentRow; + for (currentRow = row; currentRow < this.getLastRowNum(); currentRow++) + { + if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level) + { + break; + } + } + + return currentRow-1; + } + + public int writeHidden( RowRecord rowRecord, int row, boolean hidden ) + { + int level = rowRecord.getOutlineLevel(); + while (rowRecord != null && this.getRow(row).getOutlineLevel() >= level) + { + rowRecord.setZeroHeight( hidden ); + row++; + rowRecord = this.getRow( row ); + } + return row - 1; + } + + public void collapseRow( int rowNumber ) + { + + // Find the start of the group. + int startRow = findStartOfRowOutlineGroup( rowNumber ); + RowRecord rowRecord = (RowRecord) getRow( startRow ); + + // Hide all the columns until the end of the group + int lastRow = writeHidden( rowRecord, startRow, true ); + + // Write collapse field + if (getRow(lastRow + 1) != null) + { + getRow(lastRow + 1).setColapsed( true ); + } + else + { + RowRecord row = createRow( lastRow + 1); + row.setColapsed( true ); + insertRow( row ); + } + } + + /** + * Create a row record. + * + * @param row number + * @return RowRecord created for the passed in row number + * @see org.apache.poi.hssf.record.RowRecord + */ + public static RowRecord createRow(int row) + { + RowRecord rowrec = new RowRecord(); + + //rowrec.setRowNumber(( short ) row); + rowrec.setRowNumber(row); + rowrec.setHeight(( short ) 0xff); + rowrec.setOptimize(( short ) 0x0); + rowrec.setOptionFlags(( short ) 0x100); // seems necessary for outlining + rowrec.setXFIndex(( short ) 0xf); + return rowrec; + } + + public boolean isRowGroupCollapsed( int row ) + { + int collapseRow = findEndOfRowOutlineGroup( row ) + 1; + + if (getRow(collapseRow) == null) + return false; + else + return getRow( collapseRow ).getColapsed(); + } + + public void expandRow( int rowNumber ) + { + int idx = rowNumber; + if (idx == -1) + return; + + // If it is already expanded do nothing. + if (!isRowGroupCollapsed(idx)) + return; + + // Find the start of the group. + int startIdx = findStartOfRowOutlineGroup( idx ); + RowRecord row = getRow( startIdx ); + + // Find the end of the group. + int endIdx = findEndOfRowOutlineGroup( idx ); + + // expand: + // colapsed bit must be unset + // hidden bit gets unset _if_ surrounding groups are expanded you can determine + // this by looking at the hidden bit of the enclosing group. You will have + // to look at the start and the end of the current group to determine which + // is the enclosing group + // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups + if ( !isRowGroupHiddenByParent( idx ) ) + { + for ( int i = startIdx; i <= endIdx; i++ ) + { + if ( row.getOutlineLevel() == getRow( i ).getOutlineLevel() ) + getRow( i ).setZeroHeight( false ); + else if (!isRowGroupCollapsed(i)) + getRow( i ).setZeroHeight( false ); + } + } + + // Write collapse field + getRow( endIdx + 1 ).setColapsed( false ); + } + + public boolean isRowGroupHiddenByParent( int row ) + { + // Look out outline details of end + int endLevel; + boolean endHidden; + int endOfOutlineGroupIdx = findEndOfRowOutlineGroup( row ); + if (getRow( endOfOutlineGroupIdx + 1 ) == null) + { + endLevel = 0; + endHidden = false; + } + else + { + endLevel = getRow( endOfOutlineGroupIdx + 1).getOutlineLevel(); + endHidden = getRow( endOfOutlineGroupIdx + 1).getZeroHeight(); + } + + // Look out outline details of start + int startLevel; + boolean startHidden; + int startOfOutlineGroupIdx = findStartOfRowOutlineGroup( row ); + if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null) + { + startLevel = 0; + startHidden = false; + } + else + { + startLevel = getRow( startOfOutlineGroupIdx - 1).getOutlineLevel(); + startHidden = getRow( startOfOutlineGroupIdx - 1 ).getZeroHeight(); + } + + if (endLevel > startLevel) + { + return endHidden; + } + else + { + return startHidden; + } } } diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java index 546978814..4ddac6a6a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java +++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java @@ -132,12 +132,14 @@ public class EscherGraphics public void clipRect(int x, int y, int width, int height) { - logger.log(POILogger.WARN,"clipRect not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"clipRect not supported"); } public void copyArea(int x, int y, int width, int height, int dx, int dy) { - logger.log(POILogger.WARN,"copyArea not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"copyArea not supported"); } public Graphics create() @@ -154,7 +156,8 @@ public class EscherGraphics public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - logger.log(POILogger.WARN,"drawArc not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawArc not supported"); } public boolean drawImage(Image img, @@ -163,7 +166,8 @@ public class EscherGraphics Color bgcolor, ImageObserver observer) { - logger.log(POILogger.WARN,"drawImage not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawImage not supported"); return true; } @@ -173,7 +177,8 @@ public class EscherGraphics int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { - logger.log(POILogger.WARN,"drawImage not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawImage not supported"); return true; } @@ -240,18 +245,21 @@ public class EscherGraphics public void drawPolyline(int xPoints[], int yPoints[], int nPoints) { - logger.log(POILogger.WARN,"drawPolyline not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawPolyline not supported"); } public void drawRect(int x, int y, int width, int height) { - logger.log(POILogger.WARN,"drawRect not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawRect not supported"); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - logger.log(POILogger.WARN,"drawRoundRect not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawRoundRect not supported"); } public void drawString(String str, int x, int y) @@ -317,13 +325,15 @@ public class EscherGraphics public void drawString(AttributedCharacterIterator iterator, int x, int y) { - logger.log(POILogger.WARN,"drawString not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawString not supported"); } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - logger.log(POILogger.WARN,"fillArc not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"fillArc not supported"); } public void fillOval(int x, int y, int width, int height) @@ -383,7 +393,8 @@ public class EscherGraphics public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - logger.log(POILogger.WARN,"fillRoundRect not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"fillRoundRect not supported"); } public Shape getClip() @@ -438,17 +449,20 @@ public class EscherGraphics public void setPaintMode() { - logger.log(POILogger.WARN,"setPaintMode not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"setPaintMode not supported"); } public void setXORMode(Color color) { - logger.log(POILogger.WARN,"setXORMode not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"setXORMode not supported"); } public void translate(int x, int y) { - logger.log(POILogger.WARN,"translate not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"translate not supported"); } public Color getBackground() diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java index 96e0eeace..50676754b 100644 --- a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java +++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java @@ -141,7 +141,8 @@ public class EscherGraphics2d extends Graphics2D public void draw(Shape shape) { - logger.log(POILogger.WARN,"copyArea not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"copyArea not supported"); } public void drawArc(int x, int y, int width, int height, @@ -158,19 +159,22 @@ public class EscherGraphics2d extends Graphics2D public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgColor, ImageObserver imageobserver) { - logger.log(POILogger.WARN,"drawImage() not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawImage() not supported"); return true; } public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver imageobserver) { - logger.log(POILogger.WARN,"drawImage() not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawImage() not supported"); return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, imageobserver); } public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, Color bgColor, ImageObserver imageobserver) { - logger.log(POILogger.WARN,"drawImage() not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"drawImage() not supported"); return true; } @@ -285,7 +289,8 @@ public class EscherGraphics2d extends Graphics2D public void fill(Shape shape) { - logger.log(POILogger.WARN,"fill(Shape) not supported"); + if (logger.check( POILogger.WARN )) + logger.log(POILogger.WARN,"fill(Shape) not supported"); } public void fillArc(int i, int j, int k, int l, int i1, int j1) diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index 4e5655cb6..c84df9032 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -89,6 +89,7 @@ public class HSSFRow this.book = book; this.sheet = sheet; row = new RowRecord(); + row.setOptionFlags( (short)0x100 ); // seems necessary for outlining to work. row.setHeight((short) 0xff); row.setLastCol((short) -1); row.setFirstCol((short) -1); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 1d6588dc9..c381e1f1a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -134,7 +134,8 @@ public class HSSFSheet CellValueRecordInterface cval = sheet.getNextValueRecord(); long timestart = System.currentTimeMillis(); - log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ", + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ", new Long(timestart)); HSSFRow lastrow = null; @@ -150,10 +151,12 @@ public class HSSFSheet if ( hrow != null ) { lastrow = hrow; - log.log( DEBUG, "record id = " + Integer.toHexString( ( (Record) cval ).getSid() ) ); + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "record id = " + Integer.toHexString( ( (Record) cval ).getSid() ) ); hrow.createCellFromRecord( cval ); cval = sheet.getNextValueRecord(); - log.log( DEBUG, "record took ", + if (log.check( POILogger.DEBUG )) + log.log( DEBUG, "record took ", new Long( System.currentTimeMillis() - cellstart ) ); } else @@ -161,7 +164,8 @@ public class HSSFSheet cval = null; } } - log.log(DEBUG, "total sheet cell creation took ", + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "total sheet cell creation took ", new Long(System.currentTimeMillis() - timestart)); } @@ -173,8 +177,6 @@ public class HSSFSheet * @see org.apache.poi.hssf.usermodel.HSSFRow * @see #removeRow(HSSFRow) */ - - //public HSSFRow createRow(short rownum) public HSSFRow createRow(int rownum) { HSSFRow row = new HSSFRow(book, sheet, rownum); @@ -1251,6 +1253,47 @@ public class HSSFSheet return patriarch; } + /** + * Expands or collapses a column group. + * + * @param columnNumber One of the columns in the group. + * @param collapsed true = collapse group, false = expand group. + */ + public void setColumnGroupCollapsed( short columnNumber, boolean collapsed ) + { + sheet.setColumnGroupCollapsed( columnNumber, collapsed ); + } + + /** + * Create an outline for the provided column range. + * + * @param fromColumn beginning of the column range. + * @param toColumn end of the column range. + */ + public void groupColumn(short fromColumn, short toColumn) + { + sheet.groupColumnRange( fromColumn, toColumn, true ); + } + + public void ungroupColumn( short fromColumn, short toColumn ) + { + sheet.groupColumnRange( fromColumn, toColumn, false ); + } + + public void groupRow(int fromRow, int toRow) + { + sheet.groupRowRange( fromRow, toRow, true ); + } + + public void ungroupRow(int fromRow, int toRow) + { + sheet.groupRowRange( fromRow, toRow, false ); + } + + public void setRowGroupCollapsed( int row, boolean collapse ) + { + sheet.setRowGroupCollapsed( row, collapse ); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 0700c34fc..f4f8002d9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -46,8 +46,6 @@ import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; -import org.apache.poi.util.HexDump; -import org.apache.poi.ddf.*; /** * High level representation of a workbook. This is the first object most users @@ -759,7 +757,8 @@ public class HSSFWorkbook public byte[] getBytes() { - log.log(DEBUG, "HSSFWorkbook.getBytes()"); + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "HSSFWorkbook.getBytes()"); // before getting the workbook size we must tell the sheets that // serialization is about to occur. diff --git a/src/java/org/apache/poi/util/POILogger.java b/src/java/org/apache/poi/util/POILogger.java index 12865927c..7d485c6f7 100644 --- a/src/java/org/apache/poi/util/POILogger.java +++ b/src/java/org/apache/poi/util/POILogger.java @@ -42,7 +42,6 @@ public abstract class POILogger * package scope so it cannot be instantiated outside of the util * package. You need a POILogger? Go to the POILogFactory for one * - * @param log the object that does the real work of logging */ POILogger() {} @@ -55,7 +54,6 @@ public abstract class POILogger * Check if a logger is enabled to log at the specified level * * @param level One of DEBUG, INFO, WARN, ERROR, FATAL - * @param obj1 The logger to check. */ abstract public boolean check(final int level); diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheet.java b/src/testcases/org/apache/poi/hssf/model/TestSheet.java new file mode 100644 index 000000000..b11d93f01 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/model/TestSheet.java @@ -0,0 +1,58 @@ + +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.hssf.model; + +import junit.framework.TestCase; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.DimensionsRecord; +import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; + +import java.util.List; +import java.util.ArrayList; + +/** + * Unit test for the Sheet class. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class TestSheet extends TestCase +{ + public void testCreateSheet() throws Exception + { + // Check we're adding row and cell aggregates + List records = new ArrayList(); + records.add( new BOFRecord() ); + records.add( new DimensionsRecord() ); + records.add( new EOFRecord() ); + Sheet sheet = Sheet.createSheet( records, 0, 0 ); + + int pos = 0; + assertTrue( sheet.records.get(pos++) instanceof BOFRecord ); + assertTrue( sheet.records.get(pos++) instanceof ColumnInfoRecordsAggregate ); + assertTrue( sheet.records.get(pos++) instanceof DimensionsRecord ); + assertTrue( sheet.records.get(pos++) instanceof RowRecordsAggregate ); + assertTrue( sheet.records.get(pos++) instanceof ValueRecordsAggregate ); + assertTrue( sheet.records.get(pos++) instanceof EOFRecord ); + } + +}