From fc6fe5f375f8adba68ce0c4ccb0d971055b5625c Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 8 Aug 2010 11:11:38 +0000 Subject: [PATCH] initial support for excel auto-filters git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@983382 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/spreadsheet/quick-guide.xml | 10 ++ src/documentation/content/xdocs/status.xml | 5 +- .../org/apache/poi/hssf/dev/BiffViewer.java | 1 + .../apache/poi/hssf/model/AbstractShape.java | 3 + .../apache/poi/hssf/model/ComboboxShape.java | 114 ++++++++++++++++++ .../poi/hssf/record/AutoFilterInfoRecord.java | 101 ++++++++++++++++ .../poi/hssf/record/FtCblsSubRecord.java | 101 ++++++++++++++++ .../poi/hssf/record/LbsDataSubRecord.java | 62 ++++++++++ .../apache/poi/hssf/record/RecordFactory.java | 3 +- .../org/apache/poi/hssf/record/SubRecord.java | 2 + .../apache/poi/hssf/record/UnknownRecord.java | 2 +- .../poi/hssf/usermodel/HSSFAutoFilter.java | 32 +++++ .../poi/hssf/usermodel/HSSFPatriarch.java | 14 +++ .../apache/poi/hssf/usermodel/HSSFSheet.java | 68 ++++++++--- .../poi/hssf/usermodel/HSSFSimpleShape.java | 2 +- .../apache/poi/ss/usermodel/AutoFilter.java | 79 ++++++++++++ .../org/apache/poi/ss/usermodel/Sheet.java | 8 ++ .../poi/xssf/usermodel/XSSFAutoFilter.java | 33 +++++ .../apache/poi/xssf/usermodel/XSSFSheet.java | 48 ++------ .../poi/xssf/usermodel/TestXSSFSheet.java | 8 ++ .../hssf/record/TestAutoFilterInfoRecord.java | 77 ++++++++++++ .../poi/hssf/record/TestFtCblsSubRecord.java | 66 ++++++++++ .../poi/hssf/usermodel/TestHSSFSheet.java | 60 +++++++-- 23 files changed, 826 insertions(+), 73 deletions(-) create mode 100755 src/java/org/apache/poi/hssf/model/ComboboxShape.java create mode 100755 src/java/org/apache/poi/hssf/record/AutoFilterInfoRecord.java create mode 100755 src/java/org/apache/poi/hssf/record/FtCblsSubRecord.java create mode 100755 src/java/org/apache/poi/hssf/usermodel/HSSFAutoFilter.java create mode 100755 src/java/org/apache/poi/ss/usermodel/AutoFilter.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFAutoFilter.java create mode 100755 src/testcases/org/apache/poi/hssf/record/TestAutoFilterInfoRecord.java create mode 100755 src/testcases/org/apache/poi/hssf/record/TestFtCblsSubRecord.java diff --git a/src/documentation/content/xdocs/spreadsheet/quick-guide.xml b/src/documentation/content/xdocs/spreadsheet/quick-guide.xml index a959090ac3..acd08d9063 100644 --- a/src/documentation/content/xdocs/spreadsheet/quick-guide.xml +++ b/src/documentation/content/xdocs/spreadsheet/quick-guide.xml @@ -70,6 +70,7 @@
  • Hyperlinks
  • Data Validation
  • Embedded Objects
  • +
  • Autofilters
  • Features @@ -1638,5 +1639,14 @@ Examples: }
    + +

    (Since POI-3.7)

    +
    Autofilters + + Workbook wb = new HSSFWorkbook(); //or new XSSFWorkbook(); + Sheet sheet = wb.createSheet(); + sheet.setAutoFilter(CellRangeAddress.valueOf("C5:F200")); + +
    diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index b58b3f41fe..7f44b2b410 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,10 +33,9 @@ - 47990 - Support for .msg attachments within a MAPIMessage .msg Improve handling and warnings when closing OPCPackage objects diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index ee6e159c4f..14818a8ab3 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -122,6 +122,7 @@ public final class BiffViewer { case AxisParentRecord.sid: return new AxisParentRecord(in); case AxisRecord.sid: return new AxisRecord(in); case AxisUsedRecord.sid: return new AxisUsedRecord(in); + case AutoFilterInfoRecord.sid: return new AutoFilterInfoRecord(in); case BOFRecord.sid: return new BOFRecord(in); case BackupRecord.sid: return new BackupRecord(in); case BarRecord.sid: return new BarRecord(in); diff --git a/src/java/org/apache/poi/hssf/model/AbstractShape.java b/src/java/org/apache/poi/hssf/model/AbstractShape.java index 19878db797..31c1d1d8af 100644 --- a/src/java/org/apache/poi/hssf/model/AbstractShape.java +++ b/src/java/org/apache/poi/hssf/model/AbstractShape.java @@ -63,6 +63,9 @@ public abstract class AbstractShape case HSSFSimpleShape.OBJECT_TYPE_RECTANGLE: shape = new SimpleFilledShape( simpleShape, shapeId ); break; + case HSSFSimpleShape.OBJECT_TYPE_COMBO_BOX: + shape = new ComboboxShape( simpleShape, shapeId ); + break; default: throw new IllegalArgumentException("Do not know how to handle this type of shape"); } diff --git a/src/java/org/apache/poi/hssf/model/ComboboxShape.java b/src/java/org/apache/poi/hssf/model/ComboboxShape.java new file mode 100755 index 0000000000..df697c79f1 --- /dev/null +++ b/src/java/org/apache/poi/hssf/model/ComboboxShape.java @@ -0,0 +1,114 @@ +/* ==================================================================== + 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.hssf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.usermodel.*; + +/** + * Represents a combobox shape. + * + * @author Yegor Kozlov + */ +public class ComboboxShape + extends AbstractShape { + private EscherContainerRecord spContainer; + private ObjRecord objRecord; + + /** + * Creates the low evel records for a combobox. + * + * @param hssfShape The highlevel shape. + * @param shapeId The shape id to use for this shape. + */ + ComboboxShape(HSSFSimpleShape hssfShape, int shapeId) { + spContainer = createSpContainer(hssfShape, shapeId); + objRecord = createObjRecord(hssfShape, shapeId); + } + + /** + * Creates the low level OBJ record for this shape. + */ + private ObjRecord createObjRecord(HSSFSimpleShape shape, int shapeId) { + ObjRecord obj = new ObjRecord(); + CommonObjectDataSubRecord c = new CommonObjectDataSubRecord(); + c.setObjectType(HSSFSimpleShape.OBJECT_TYPE_COMBO_BOX); + c.setObjectId(shapeId); + c.setLocked(true); + c.setPrintable(false); + c.setAutofill(true); + c.setAutoline(false); + + LbsDataSubRecord l = LbsDataSubRecord.newAutoFilterInstance(); + + EndSubRecord e = new EndSubRecord(); + + obj.addSubRecord(c); + obj.addSubRecord(l); + obj.addSubRecord(e); + + return obj; + } + + /** + * Generates the escher shape records for this shape. + */ + private EscherContainerRecord createSpContainer(HSSFSimpleShape shape, int shapeId) { + EscherContainerRecord spContainer = new EscherContainerRecord(); + EscherSpRecord sp = new EscherSpRecord(); + EscherOptRecord opt = new EscherOptRecord(); + EscherClientDataRecord clientData = new EscherClientDataRecord(); + + spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER); + spContainer.setOptions((short) 0x000F); + sp.setRecordId(EscherSpRecord.RECORD_ID); + sp.setOptions((short) ((EscherAggregate.ST_HOSTCONTROL << 4) | 0x2)); + + sp.setShapeId(shapeId); + sp.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE); + opt.setRecordId(EscherOptRecord.RECORD_ID); + opt.addEscherProperty(new EscherBoolProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 17039620)); + opt.addEscherProperty(new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x00080008)); + opt.addEscherProperty(new EscherBoolProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GROUPSHAPE__PRINT, 0x00020000)); + + HSSFClientAnchor userAnchor = (HSSFClientAnchor) shape.getAnchor(); + userAnchor.setAnchorType(1); + EscherRecord anchor = createAnchor(userAnchor); + clientData.setRecordId(EscherClientDataRecord.RECORD_ID); + clientData.setOptions((short) 0x0000); + + spContainer.addChildRecord(sp); + spContainer.addChildRecord(opt); + spContainer.addChildRecord(anchor); + spContainer.addChildRecord(clientData); + + return spContainer; + } + + public EscherContainerRecord getSpContainer() { + return spContainer; + } + + public ObjRecord getObjRecord() { + return objRecord; + } + +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/AutoFilterInfoRecord.java b/src/java/org/apache/poi/hssf/record/AutoFilterInfoRecord.java new file mode 100755 index 0000000000..881cf3b01a --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/AutoFilterInfoRecord.java @@ -0,0 +1,101 @@ + +/* ==================================================================== + 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.hssf.record; + +import org.apache.poi.util.LittleEndianOutput; + +/** + * The AutoFilterInfo record specifies the number of columns that have AutoFilter enabled + * and indicates the beginning of the collection of AutoFilter records. + * + * @author Yegor Kozlov + */ + +public final class AutoFilterInfoRecord + extends StandardRecord +{ + public final static short sid = 0x9D; + /** + * Number of AutoFilter drop-down arrows on the sheet + */ + private short _cEntries; // = 0; + + public AutoFilterInfoRecord() + { + } + + public AutoFilterInfoRecord(RecordInputStream in) + { + _cEntries = in.readShort(); + } + + /** + * set the number of AutoFilter drop-down arrows on the sheet + * + * @param num the number of AutoFilter drop-down arrows on the sheet + */ + + public void setNumEntries(short num) + { + _cEntries = num; + } + + /** + * get the number of AutoFilter drop-down arrows on the sheet + * + * @return the number of AutoFilter drop-down arrows on the sheet + */ + + public short getNumEntries() + { + return _cEntries; + } + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[AUTOFILTERINFO]\n"); + buffer.append(" .numEntries = ") + .append(_cEntries).append("\n"); + buffer.append("[/AUTOFILTERINFO]\n"); + return buffer.toString(); + } + + public void serialize(LittleEndianOutput out) { + out.writeShort(_cEntries); + } + + protected int getDataSize() { + return 2; + } + + public short getSid() + { + return sid; + } + + @Override + public Object clone() + { + return cloneViaReserialise(); + } + +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/FtCblsSubRecord.java b/src/java/org/apache/poi/hssf/record/FtCblsSubRecord.java new file mode 100755 index 0000000000..517add94b5 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/FtCblsSubRecord.java @@ -0,0 +1,101 @@ +/* ==================================================================== + 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.hssf.record; + +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; + + +/** + * This structure appears as part of an Obj record that represents a checkbox or radio button. + * + * @author Yegor Kozlov + */ +public final class FtCblsSubRecord extends SubRecord { + public final static short sid = 0x0C; + private static final int ENCODED_SIZE = 20; + + private byte[] reserved; + + /** + * Construct a new FtCblsSubRecord and + * fill its data with the default values + */ + public FtCblsSubRecord() + { + reserved = new byte[ENCODED_SIZE]; + } + + public FtCblsSubRecord(LittleEndianInput in, int size) { + if (size != ENCODED_SIZE) { + throw new RecordFormatException("Unexpected size (" + size + ")"); + } + //just grab the raw data + byte[] buf = new byte[size]; + in.readFully(buf); + reserved = buf; + } + + /** + * Convert this record to string. + * Used by BiffViewer and other utilities. + */ + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[FtCbls ]").append("\n"); + buffer.append(" size = ").append(getDataSize()).append("\n"); + buffer.append(" reserved = ").append(HexDump.toHex(reserved)).append("\n"); + buffer.append("[/FtCbls ]").append("\n"); + return buffer.toString(); + } + + /** + * Serialize the record data into the supplied array of bytes + * + * @param out the stream to serialize into + */ + public void serialize(LittleEndianOutput out) { + out.writeShort(sid); + out.writeShort(reserved.length); + out.write(reserved); + } + + protected int getDataSize() { + return reserved.length; + } + + /** + * @return id of this record. + */ + public short getSid() + { + return sid; + } + + public Object clone() { + FtCblsSubRecord rec = new FtCblsSubRecord(); + byte[] recdata = new byte[reserved.length]; + System.arraycopy(reserved, 0, recdata, 0, recdata.length); + rec.reserved = recdata; + return rec; + } + +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java b/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java index 57ebb20396..779523a969 100755 --- a/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java +++ b/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java @@ -150,6 +150,29 @@ public class LbsDataSubRecord extends SubRecord { } + LbsDataSubRecord(){ + + } + + /** + * + * @return a new instance of LbsDataSubRecord to construct auto-filters + * @see org.apache.poi.hssf.model.ComboboxShape#createObjRecord(org.apache.poi.hssf.usermodel.HSSFSimpleShape, int) + */ + public static LbsDataSubRecord newAutoFilterInstance(){ + LbsDataSubRecord lbs = new LbsDataSubRecord(); + lbs._cbFContinued = 0x1FEE; //autofilters seem to alway have this magic number + lbs._iSel = 0x000; + + lbs._flags = 0x0301; + lbs._dropData = new LbsDropData(); + lbs._dropData._wStyle = LbsDropData.STYLE_COMBO_SIMPLE_DROPDOWN; + + // the number of lines to be displayed in the dropdown + lbs._dropData._cLine = 8; + return lbs; + } + /** * @return true as LbsDataSubRecord is always the last sub-record */ @@ -272,6 +295,19 @@ public class LbsDataSubRecord extends SubRecord { * This structure specifies properties of the dropdown list control */ public static class LbsDropData { + /** + * Combo dropdown control + */ + public static int STYLE_COMBO_DROPDOWN = 0; + /** + * Combo Edit dropdown control + */ + public static int STYLE_COMBO_EDIT_DROPDOWN = 1; + /** + * Simple dropdown control (just the dropdown button) + */ + public static int STYLE_COMBO_SIMPLE_DROPDOWN = 2; + /** * An unsigned integer that specifies the style of this dropdown. */ @@ -298,6 +334,11 @@ public class LbsDataSubRecord extends SubRecord { */ private Byte _unused; + public LbsDropData(){ + _str = ""; + _unused = 0; + } + public LbsDropData(LittleEndianInput in){ _wStyle = in.readUShort(); _cLine = in.readUShort(); @@ -308,6 +349,27 @@ public class LbsDataSubRecord extends SubRecord { } } + /** + * Set the style of this dropdown. + * + * Possible values: + *

    + * 0 Combo dropdown control + * 1 Combo Edit dropdown control + * 2 Simple dropdown control (just the dropdown button) + * + */ + public void setStyle(int style){ + _wStyle = style; + } + + /** + * Set the number of lines to be displayed in the dropdown. + */ + public void setNumLines(int num){ + _cLine = num; + } + public void serialize(LittleEndianOutput out) { out.writeShort(_wStyle); out.writeShort(_cLine); diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index 029b7e0413..68c97bd7da 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -107,7 +107,8 @@ public final class RecordFactory { @SuppressWarnings("unchecked") private static final Class[] recordClasses = new Class[] { ArrayRecord.class, - BackupRecord.class, + AutoFilterInfoRecord.class, + BackupRecord.class, BlankRecord.class, BOFRecord.class, BookBoolRecord.class, diff --git a/src/java/org/apache/poi/hssf/record/SubRecord.java b/src/java/org/apache/poi/hssf/record/SubRecord.java index 59b01cf35d..fd54acc0a7 100644 --- a/src/java/org/apache/poi/hssf/record/SubRecord.java +++ b/src/java/org/apache/poi/hssf/record/SubRecord.java @@ -57,6 +57,8 @@ public abstract class SubRecord { return new NoteStructureSubRecord(in, secondUShort); case LbsDataSubRecord.sid: return new LbsDataSubRecord(in, secondUShort, cmoOt); + case FtCblsSubRecord.sid: + return new FtCblsSubRecord(in, secondUShort); } return new UnknownSubRecord(in, sid, secondUShort); } diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java index 20b352456d..3e04d0f8bf 100644 --- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java +++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.java @@ -139,7 +139,6 @@ public final class UnknownRecord extends StandardRecord { case SORT_0090: return "SORT"; // Sorting Options case 0x0094: return "LHRECORD"; // .WK? File Conversion Information case STANDARDWIDTH_0099: return "STANDARDWIDTH"; //Standard Column Width - case 0x009D: return "AUTOFILTERINFO"; // Drop-Down Arrow Count case SCL_00A0: return "SCL"; // Window Zoom Magnification case 0x00AE: return "SCENMAN"; // Scenario Output Data @@ -241,6 +240,7 @@ public final class UnknownRecord extends StandardRecord { case 0x101B: case 0x101D: case 0x101E: + case 0x101F: case 0x1020: case 0x1021: case 0x1022: diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFAutoFilter.java b/src/java/org/apache/poi/hssf/usermodel/HSSFAutoFilter.java new file mode 100755 index 0000000000..8baa1394f0 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFAutoFilter.java @@ -0,0 +1,32 @@ +/* ==================================================================== + 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.hssf.usermodel; +import org.apache.poi.ss.usermodel.AutoFilter; + +/** + * Represents autofiltering for the specified worksheet. + * + * @author Yegor Kozlov + */ +public final class HSSFAutoFilter implements AutoFilter { + private HSSFSheet _sheet; + + HSSFAutoFilter(HSSFSheet sheet){ + _sheet = sheet; + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index 859181d7ac..0f7cde9420 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -157,6 +157,20 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { return shape; } + /** + * YK: used to create autofilters + * + * @see org.apache.poi.hssf.usermodel.HSSFSheet#setAutoFilter(int, int, int, int) + */ + HSSFSimpleShape createComboBox(HSSFAnchor anchor) + { + HSSFSimpleShape shape = new HSSFSimpleShape(null, anchor); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_COMBO_BOX); + shape.anchor = anchor; + _shapes.add(shape); + return shape; + } + public HSSFComment createCellComment(ClientAnchor anchor) { return createComment((HSSFAnchor)anchor); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 33af66680e..283032ee4d 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -34,21 +34,13 @@ import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.model.InternalSheet; import org.apache.poi.hssf.model.InternalWorkbook; -import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.DVRecord; -import org.apache.poi.hssf.record.EscherAggregate; -import org.apache.poi.hssf.record.ExtendedFormatRecord; -import org.apache.poi.hssf.record.NoteRecord; -import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.RowRecord; -import org.apache.poi.hssf.record.SCLRecord; -import org.apache.poi.hssf.record.WSBoolRecord; -import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.record.*; import org.apache.poi.hssf.record.aggregates.DataValidityTable; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock; import org.apache.poi.hssf.record.formula.FormulaShifter; import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.util.PaneInformation; import org.apache.poi.hssf.util.Region; import org.apache.poi.ss.SpreadsheetVersion; @@ -1573,15 +1565,17 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { * @return The new patriarch. */ public HSSFPatriarch createDrawingPatriarch() { - // Create the drawing group if it doesn't already exist. - _book.createDrawingGroup(); - - _sheet.aggregateDrawingRecords(_book.getDrawingManager(), true); - EscherAggregate agg = (EscherAggregate) _sheet.findFirstRecordBySid(EscherAggregate.sid); - _patriarch = new HSSFPatriarch(this, agg); - agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when - // creating a new patriarch. - agg.setPatriarch(_patriarch); + if(_patriarch == null){ + // Create the drawing group if it doesn't already exist. + _book.createDrawingGroup(); + + _sheet.aggregateDrawingRecords(_book.getDrawingManager(), true); + EscherAggregate agg = (EscherAggregate) _sheet.findFirstRecordBySid(EscherAggregate.sid); + _patriarch = new HSSFPatriarch(this, agg); + agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when + // creating a new patriarch. + agg.setPatriarch(_patriarch); + } return _patriarch; } @@ -2000,5 +1994,39 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { return new HSSFDataValidationHelper(this); } - + public HSSFAutoFilter setAutoFilter(CellRangeAddress range) { + + + InternalWorkbook workbook = _workbook.getWorkbook(); + int sheetIndex = _workbook.getSheetIndex(this); + + NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_FILTER_DB, sheetIndex+1); + + if (name == null) { + name = workbook.createBuiltInName(NameRecord.BUILTIN_FILTER_DB, sheetIndex+1); + } + + // The built-in name must consist of a single Area3d Ptg. + Area3DPtg ptg = new Area3DPtg(range.getFirstRow(), range.getLastRow(), + range.getFirstColumn(), range.getLastColumn(), + false, false, false, false, sheetIndex); + name.setNameDefinition(new Ptg[]{ptg}); + + AutoFilterInfoRecord r = new AutoFilterInfoRecord(); + // the number of columns that have AutoFilter enabled. + int numcols = 1 + range.getLastColumn() - range.getFirstColumn(); + r.setNumEntries((short)numcols); + int idx = _sheet.findFirstRecordLocBySid(DimensionsRecord.sid); + _sheet.getRecords().add(idx, r); + + //create a combobox control for each column + HSSFPatriarch p = createDrawingPatriarch(); + for(int col = range.getFirstColumn(); col <= range.getLastColumn(); col++){ + p.createComboBox(new HSSFClientAnchor(0,0,0,0, + (short)col, range.getFirstRow(), (short)(col+1), range.getFirstRow()+1)); + } + + return new HSSFAutoFilter(this); + } + } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java index dca653ad8d..0e4df40672 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java @@ -46,7 +46,7 @@ public class HSSFSimpleShape // public final static short OBJECT_TYPE_SCROLL_BAR = 17; // public final static short OBJECT_TYPE_LIST_BOX = 18; // public final static short OBJECT_TYPE_GROUP_BOX = 19; -// public final static short OBJECT_TYPE_COMBO_BOX = 20; + public final static short OBJECT_TYPE_COMBO_BOX = 20; public final static short OBJECT_TYPE_COMMENT = 25; // public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30; diff --git a/src/java/org/apache/poi/ss/usermodel/AutoFilter.java b/src/java/org/apache/poi/ss/usermodel/AutoFilter.java new file mode 100755 index 0000000000..82c7a439d0 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/AutoFilter.java @@ -0,0 +1,79 @@ +/* ==================================================================== + 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.ss.usermodel; + +/** + * Represents autofiltering for the specified worksheet. + * + *

    + * Filtering data is a quick and easy way to find and work with a subset of data in a range of cells or table. + * For example, you can filter to see only the values that you specify, filter to see the top or bottom values, + * or filter to quickly see duplicate values. + *

    + * + * TODO YK: For now (Aug 2010) POI only supports setting a basic autofilter on a range of cells. + * In future, when we support more auto-filter functions like custom criteria, sort, etc. we will add + * corresponding methods to this interface. + */ +public interface AutoFilter { + /** + * Apply a custom filter + * + *

    + * A custom AutoFilter specifies an operator and a value. + * There can be at most two customFilters specified, and in that case the parent element + * specifies whether the two conditions are joined by 'and' or 'or'. For any cells whose + * values do not meet the specified criteria, the corresponding rows shall be hidden from + * view when the filter is applied. + *

    + * + *

    + * Example: + *

    +     *  AutoFilter filter = sheet.setAutoFilter(CellRangeAddress.valueOf("A1:F200"));
    +     *  filter.applyFilter(0, FilterOperator.GreaterThanOrEqual", "0.2");
    +     *  filter.applyFilter(1, FilterOperator.LessThanOrEqual"", "0.5");
    +     * 
    + *

    + * + * @param columnIndex 0-based column index + * @param operator the operator to apply + * @param criteria top or bottom value used in the filter criteria. + * + * TODO YK: think how to combine AutoFilter with with DataValidationConstraint, they are really close relatives + * void applyFilter(int columnIndex, FilterOperator operator, String criteria); + */ + + + /** + * Apply a filter against a list of values + * + *

    + * Example: + *

    +     *  AutoFilter filter = sheet.setAutoFilter(CellRangeAddress.valueOf("A1:F200"));
    +     *  filter.applyFilter(0, "apache", "poi", "java", "api");
    +     * 
    + *

    + * + * @param columnIndex 0-based column index + * @param values the filter values + * + * void applyFilter(int columnIndex, String ... values); + */ + +} diff --git a/src/java/org/apache/poi/ss/usermodel/Sheet.java b/src/java/org/apache/poi/ss/usermodel/Sheet.java index bad9c5c719..b4a1dbc0e6 100644 --- a/src/java/org/apache/poi/ss/usermodel/Sheet.java +++ b/src/java/org/apache/poi/ss/usermodel/Sheet.java @@ -806,4 +806,12 @@ public interface Sheet extends Iterable { * @param dataValidation The Data validation object settings */ public void addValidationData(DataValidation dataValidation); + + /** + * Enable filtering for a range of cells + * + * @param range the range of cells to filter + */ + AutoFilter setAutoFilter(CellRangeAddress range); + } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFAutoFilter.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFAutoFilter.java new file mode 100755 index 0000000000..d363ab31ab --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFAutoFilter.java @@ -0,0 +1,33 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.xssf.usermodel; +import org.apache.poi.ss.usermodel.AutoFilter; + +/** + * Represents autofiltering for the specified worksheet. + * + * @author Yegor Kozlov + */ +public final class XSSFAutoFilter implements AutoFilter { + private XSSFSheet _sheet; + + XSSFAutoFilter(XSSFSheet sheet){ + _sheet = sheet; + } + +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 26c4694055..815a351663 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -61,41 +61,7 @@ import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBreak; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidations; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTLegacyDrawing; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCells; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOutlinePr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetUpPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPane; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPrintOptions; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSelection; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetProtection; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPaneState; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; /** * High level representation of a SpreadsheetML worksheet. @@ -2918,4 +2884,16 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { dataValidations.setCount(currentCount + 1); } + + public XSSFAutoFilter setAutoFilter(CellRangeAddress range) { + CTAutoFilter af = worksheet.getAutoFilter(); + if(af == null) af = worksheet.addNewAutoFilter(); + + CellRangeAddress norm = new CellRangeAddress(range.getFirstRow(), range.getLastRow(), + range.getFirstColumn(), range.getLastColumn()); + String ref = norm.formatAsString(); + af.setRef(ref); + + return new XSSFAutoFilter(this); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index 680865ed69..a07e5c20b9 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -971,4 +971,12 @@ public final class TestXSSFSheet extends BaseTestSheet { assertEquals(3, xrow[2].getR()); } + + public void testSetAutoFilter() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + sheet.setAutoFilter(CellRangeAddress.valueOf("A1:D100")); + + assertEquals("A1:D100", sheet.getCTWorksheet().getAutoFilter().getRef()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/TestAutoFilterInfoRecord.java b/src/testcases/org/apache/poi/hssf/record/TestAutoFilterInfoRecord.java new file mode 100755 index 0000000000..e4354082c9 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestAutoFilterInfoRecord.java @@ -0,0 +1,77 @@ +/* ==================================================================== + 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.hssf.record; + +import junit.framework.TestCase; +import org.apache.poi.ddf.EscherClientDataRecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherDggRecord; +import org.apache.poi.ddf.EscherSpRecord; +import org.apache.poi.hssf.model.DrawingManager2; +import org.apache.poi.util.HexDump; +import org.apache.poi.util.HexRead; + +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; + +/** + * Tests the AutoFilterInfoRecord class. + * + * @author Yegor Kozlov + */ +public final class TestAutoFilterInfoRecord extends TestCase { + private byte[] data = new byte[] { + 0x05, 0x00 + }; + + public void testRead() { + + AutoFilterInfoRecord record = new AutoFilterInfoRecord(TestcaseRecordInputStream.create(AutoFilterInfoRecord.sid, data)); + + assertEquals(AutoFilterInfoRecord.sid, record.getSid()); + assertEquals(data.length, record.getDataSize()); + assertEquals(5, record.getNumEntries()); + record.setNumEntries((short)3); + assertEquals(3, record.getNumEntries()); + } + + public void testWrite() { + AutoFilterInfoRecord record = new AutoFilterInfoRecord(); + record.setNumEntries((short)3); + + byte [] ser = record.serialize(); + assertEquals(ser.length - 4, data.length); + record = new AutoFilterInfoRecord(TestcaseRecordInputStream.create(ser)); + assertEquals(3, record.getNumEntries()); + } + + public void testClone() + { + AutoFilterInfoRecord record = new AutoFilterInfoRecord(); + record.setNumEntries((short)3); + byte[] src = record.serialize(); + + AutoFilterInfoRecord cloned = (AutoFilterInfoRecord)record.clone(); + assertEquals(3, record.getNumEntries()); + byte[] cln = cloned.serialize(); + + assertEquals(record.getDataSize(), cloned.getDataSize()); + assertTrue(Arrays.equals(src, cln)); + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/record/TestFtCblsSubRecord.java b/src/testcases/org/apache/poi/hssf/record/TestFtCblsSubRecord.java new file mode 100755 index 0000000000..3d71a8cb32 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestFtCblsSubRecord.java @@ -0,0 +1,66 @@ +/* ==================================================================== + 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.hssf.record; + + +import junit.framework.TestCase; + +import java.util.Arrays; + +/** + * Tests the serialization and deserialization of the FtCblsSubRecord + * class works correctly. + * + * @author Yegor Kozlov + */ +public final class TestFtCblsSubRecord extends TestCase { + private byte[] data = new byte[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, + 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00 + }; + + public void testRead() { + + FtCblsSubRecord record = new FtCblsSubRecord(TestcaseRecordInputStream.create(FtCblsSubRecord.sid, data), data.length); + + assertEquals(FtCblsSubRecord.sid, record.getSid()); + assertEquals(data.length, record.getDataSize()); + } + + public void testWrite() { + FtCblsSubRecord record = new FtCblsSubRecord(); + assertEquals(FtCblsSubRecord.sid, record.getSid()); + assertEquals(data.length, record.getDataSize()); + + byte [] ser = record.serialize(); + assertEquals(ser.length - 4, data.length); + + } + + public void testClone() + { + FtCblsSubRecord record = new FtCblsSubRecord(); + byte[] src = record.serialize(); + + FtCblsSubRecord cloned = (FtCblsSubRecord)record.clone(); + byte[] cln = cloned.serialize(); + + assertEquals(record.getDataSize(), cloned.getDataSize()); + assertTrue(Arrays.equals(src, cln)); + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index c041a94ad4..19cf47e1c0 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -27,18 +27,11 @@ import org.apache.poi.ddf.EscherDgRecord; import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.DrawingManager2; -import org.apache.poi.hssf.record.DimensionsRecord; -import org.apache.poi.hssf.record.GridsetRecord; -import org.apache.poi.hssf.record.HCenterRecord; -import org.apache.poi.hssf.record.ObjectProtectRecord; -import org.apache.poi.hssf.record.PasswordRecord; -import org.apache.poi.hssf.record.ProtectRecord; -import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.SCLRecord; -import org.apache.poi.hssf.record.ScenarioProtectRecord; -import org.apache.poi.hssf.record.VCenterRecord; -import org.apache.poi.hssf.record.WSBoolRecord; -import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.hssf.model.InternalSheet; +import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock; import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector; import org.apache.poi.ss.usermodel.BaseTestSheet; @@ -291,13 +284,23 @@ public final class TestHSSFSheet extends BaseTestSheet { assertEquals(0, r6.getOutlineLevel()); } + public void testCreateDrawings() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet(); + HSSFPatriarch p1 = sheet.createDrawingPatriarch(); + HSSFPatriarch p2 = sheet.createDrawingPatriarch(); + assertSame(p1, p2); + } + public void testGetDrawings() { HSSFWorkbook wb1c = HSSFTestDataSamples.openSampleWorkbook("WithChart.xls"); HSSFWorkbook wb2c = HSSFTestDataSamples.openSampleWorkbook("WithTwoCharts.xls"); // 1 chart sheet -> data on 1st, chart on 2nd assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch()); + assertSame(wb1c.getSheetAt(0).getDrawingPatriarch(), wb1c.getSheetAt(0).getDrawingPatriarch()); assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch()); + assertSame(wb1c.getSheetAt(1).getDrawingPatriarch(), wb1c.getSheetAt(1).getDrawingPatriarch()); assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart()); assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart()); @@ -860,4 +863,37 @@ public final class TestHSSFSheet extends BaseTestSheet { s.setRightToLeft(true); assertEquals(true, s.isRightToLeft()); } + + public void testAutoFilter(){ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sh = wb.createSheet(); + InternalWorkbook iwb = wb.getWorkbook(); + InternalSheet ish = sh.getSheet(); + + assertNull( iwb.getSpecificBuiltinRecord(NameRecord.BUILTIN_FILTER_DB, 1) ); + assertNull( ish.findFirstRecordBySid(AutoFilterInfoRecord.sid) ); + + CellRangeAddress range = CellRangeAddress.valueOf("A1:B10"); + sh.setAutoFilter(range); + + NameRecord name = iwb.getSpecificBuiltinRecord(NameRecord.BUILTIN_FILTER_DB, 1); + assertNotNull( name ); + + // The built-in name for auto-filter must consist of a single Area3d Ptg. + Ptg[] ptg = name.getNameDefinition(); + assertEquals("The built-in name for auto-filter must consist of a single Area3d Ptg", 1, ptg.length); + assertTrue("The built-in name for auto-filter must consist of a single Area3d Ptg", ptg[0] instanceof Area3DPtg); + + Area3DPtg aref = (Area3DPtg)ptg[0]; + assertEquals(range.getFirstColumn(), aref.getFirstColumn()); + assertEquals(range.getFirstRow(), aref.getFirstRow()); + assertEquals(range.getLastColumn(), aref.getLastColumn()); + assertEquals(range.getLastRow(), aref.getLastRow()); + + // verify AutoFilterInfoRecord + AutoFilterInfoRecord afilter = (AutoFilterInfoRecord)ish.findFirstRecordBySid(AutoFilterInfoRecord.sid); + assertNotNull(afilter ); + assertEquals(2, afilter.getNumEntries()); //filter covers two columns + + } } -- 2.39.5