diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/SetPrintArea.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/SetPrintArea.java new file mode 100755 index 000000000..4de66e3a0 --- /dev/null +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/SetPrintArea.java @@ -0,0 +1,42 @@ +/* ==================================================================== + 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.examples; + +import java.io.FileOutputStream; + +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +public class SetPrintArea { + + + public static void main(String[]args) throws Exception { + Workbook wb = new XSSFWorkbook(); + Sheet sheet = wb.createSheet("Sheet1"); + //wb.setPrintArea(0, "$A$1:$C$2"); + //sets the print area for the first sheet + //Alternatively: + wb.setPrintArea(0, 1, 2, 0, 3); //is equivalent to using the name reference (See the JavaDocs for more details) + + // Create various cells and rows for spreadsheet. + + FileOutputStream fileOut = new FileOutputStream("printArea.xlsx"); + wb.write(fileOut); + fileOut.close(); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java index 0e9e573b4..7b4494661 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java @@ -23,66 +23,165 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; * XSSF Implementation of a Named Range */ public class XSSFName implements Name { - private XSSFWorkbook workbook; - private CTDefinedName ctName; - - protected XSSFName(XSSFWorkbook workbook) { - this.workbook = workbook; - this.ctName = CTDefinedName.Factory.newInstance(); - } - protected XSSFName(CTDefinedName name, XSSFWorkbook workbook) { - this.workbook = workbook; - this.ctName = name; - } - - public boolean isFunctionName() { - // TODO Figure out how HSSF does this, and do the same! - return ctName.getFunction(); // maybe this works - verify - } - /** - * Returns the underlying named range object - */ - protected CTDefinedName getCTName() { - return ctName; - } - - public String getNameName() { - return ctName.getName(); - } - public void setNameName(String nameName) { - ctName.setName(nameName); + /** + * The following built-in names are defined in this SpreadsheetML + * specification: + * Built-in names reserved by SpreadsheetML begin with "_xlnm.". End users shall not use + * this string for custom names in the user interface. + */ + + /** + * this defined name specifies the workbook's print area + */ + public final static String BUILTIN_PRINT_AREA = "_xlnm.Print_Area"; + + /** + * this defined name specifies the row(s) or column(s) to repeat + * at the top of each printed page. + */ + public final static String BUILTIN_PRINT_TITLE = "_xlnm.Print_Titles"; + + //Filter & Advanced Filter + + /** + * this defined name refers to a range containing the criteria values + * to be used in applying an advanced filter to a range of data + */ + public final static String BUILTIN_CRITERIA = "_xlnm.Criteria:"; + + + /** + * this defined name refers to the range containing the filtered + * output values resulting from applying an advanced filter criteria to a source + * range + */ + public final static String BUILTIN_EXTRACT = "_xlnm.Extract:"; + + /** + * can be one of the following + * a. this defined name refers to a range to which an advanced filter has been + * applied. This represents the source data range, unfiltered. + * b. This defined name refers to a range to which an AutoFilter has been + * applied + */ + public final static String BUILTIN_FILTER_DB = "_xlnm._FilterDatabase:"; + + + //Miscellaneous + + /** + * the defined name refers to a consolidation area + */ + public final static String BUILTIN_CONSOLIDATE_AREA = "_xlnm.Consolidate_Area"; + + /** + * the range specified in the defined name is from a database data source + */ + public final static String BUILTIN_DATABASE = "_xlnm.Database"; + + /** + * the defined name refers to a sheet title. + */ + public final static String BUILTIN_SHEET_TITLE = "_xlnm.Sheet_Title"; + + private XSSFWorkbook workbook; + private CTDefinedName ctName; + + protected XSSFName(XSSFWorkbook workbook) { + this.workbook = workbook; + this.ctName = CTDefinedName.Factory.newInstance(); + } + protected XSSFName(CTDefinedName name, XSSFWorkbook workbook) { + this.workbook = workbook; + this.ctName = name; + } + + public boolean isFunctionName() { + // TODO Figure out how HSSF does this, and do the same! + return ctName.getFunction(); // maybe this works - verify + } + /** + * Returns the underlying named range object + */ + protected CTDefinedName getCTName() { + return ctName; + } + + public String getNameName() { + return ctName.getName(); + } + public void setNameName(String nameName) { + ctName.setName(nameName); + } + + public String getReference() { + return ctName.getStringValue(); + } + public void setReference(String ref) { + ctName.setStringValue(ref); + } + + public void setLocalSheetId(int sheetId) { + ctName.setLocalSheetId(sheetId); + } + + public int getLocalSheetId() { + return (int)ctName.getLocalSheetId(); + } + + + public void setFunction(boolean value) { + ctName.setFunction(value); + } + + public boolean getFunction() { + return ctName.getFunction(); + } + + public void setFunctionGroupId(int functionGroupId) { + ctName.setFunctionGroupId(functionGroupId); + } + + public int getFunctionGroupId() { + return (int)ctName.getFunctionGroupId(); + } + + public String getSheetName() { + if(ctName.isSetLocalSheetId()) { + // Given as explicit sheet id + long sheetId = ctName.getLocalSheetId(); + if(sheetId >= 0) { + return workbook.getSheetName((int)sheetId); + } + } else { + // Is it embeded in the reference itself? + int excl = getReference().indexOf('!'); + if(excl > -1) { + return getReference().substring(0, excl); + } } - public String getReference() { - return ctName.getStringValue(); - } - public void setReference(String ref) { - ctName.setStringValue(ref); - } - - public String getSheetName() { - if(ctName.isSetLocalSheetId()) { - // Given as explicit sheet id - long sheetId = ctName.getLocalSheetId(); - if(sheetId >= 0) { - return workbook.getSheetName((int)sheetId); - } - } else { - // Is it embeded in the reference itself? - int excl = getReference().indexOf('!'); - if(excl > -1) { - return getReference().substring(0, excl); - } - } - - // Not given at all - return null; - } + // Not given at all + return null; + } + + public String getComment() { + return ctName.getComment(); + } + public void setComment(String comment) { + ctName.setComment(comment); + } + + + public int hashCode(){ + return ctName.toString().hashCode(); + } + + public boolean equals(Object o){ + if(!(o instanceof XSSFName)) return false; + XSSFName cf = (XSSFName)o; + return ctName.toString().equals(cf.getCTName().toString()); + } + - public String getComment() { - return ctName.getComment(); - } - public void setComment(String comment) { - ctName.setComment(comment); - } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index c12c7ab77..5bed89399 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -28,6 +28,7 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.PackageHelper; @@ -604,9 +605,16 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable + * i.e. Reference = $A$1:$B$2 + * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) + * @param reference Valid name Reference for the Print Area + */ public void setPrintArea(int sheetIndex, String reference) { - // TODO Auto-generated method stub - + XSSFName name = getSpecificBuiltinRecord(XSSFName.BUILTIN_PRINT_AREA, sheetIndex); + if (name == null) { + name = createBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex); + namedRanges.add(name); + } + name.setReference(reference); } + /** + * For the Convenience of Java Programmers maintaining pointers. + * @see #setPrintArea(int, String) + * @param sheetIndex Zero-based sheet index (0 = First Sheet) + * @param startColumn Column to begin printarea + * @param endColumn Column to end the printarea + * @param startRow Row to begin the printarea + * @param endRow Row to end the printarea + */ public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) { - // TODO Auto-generated method stub - + String reference=getReferencePrintArea(getSheetName(sheetIndex), startColumn, endColumn, startRow, endRow); + setPrintArea(sheetIndex, reference); } - public void setRepeatingRowsAndColumns(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) { - // TODO Auto-generated method stub - + /** + * Sets the repeating rows and columns for a sheet. + * This is function is included in the workbook + * because it creates/modifies name records which are stored at the + * workbook level. + *

+ * To set just repeating columns: + *

+     *  workbook.setRepeatingRowsAndColumns(0,0,1,-1,-1);
+     * 
+ * To set just repeating rows: + *
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
+     * 
+ * To remove all repeating rows and columns for a sheet. + *
+     *  workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
+     * 
+ * + * @param sheetIndex 0 based index to sheet. + * @param startColumn 0 based start of repeating columns. + * @param endColumn 0 based end of repeating columns. + * @param startRow 0 based start of repeating rows. + * @param endRow 0 based end of repeating rows. + */ + public void setRepeatingRowsAndColumns(int sheetIndex, + int startColumn, int endColumn, + int startRow, int endRow) { + //TODO } + + private String getReferencePrintArea(String sheetName, int startC, int endC, int startR, int endR) { + //windows excel example: Sheet1!$C$3:$E$4 + CellReference colRef = new CellReference(sheetName, startR, startC, true, true); + CellReference colRef2 = new CellReference(sheetName, endR, endC, true, true); + + String c = "'" + sheetName + "'!$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1]; + return c; + } + + //****************** NAME RANGE ************************* + + private CTDefinedNames getDefinedNames() { + return workbook.getDefinedNames() == null ? workbook.addNewDefinedNames() : workbook.getDefinedNames(); + } + + + public XSSFName getSpecificBuiltinRecord(String builtInCode, int sheetNumber) { + for (XSSFName name : namedRanges) { + if (name.getNameName().equalsIgnoreCase(builtInCode) && name.getLocalSheetId() == sheetNumber) { + return name; + } + } + return null; + } + + /** + * Generates a NameRecord to represent a built-in region + * @return a new NameRecord + */ + public XSSFName createBuiltInName(String builtInName, int sheetNumber) { + if (sheetNumber < 0 || sheetNumber+1 > Short.MAX_VALUE) { + throw new IllegalArgumentException("Sheet number ["+sheetNumber+"]is not valid "); + } + + CTDefinedName nameRecord=getDefinedNames().addNewDefinedName(); + nameRecord.setName(builtInName); + nameRecord.setLocalSheetId(sheetNumber); + + XSSFName name=new XSSFName(nameRecord,this); + //while(namedRanges.contains(name)) { + for(XSSFName nr : namedRanges){ + if(nr.equals(name)) + throw new RuntimeException("Builtin (" + builtInName + + ") already exists for sheet (" + sheetNumber + ")"); + } + + return name; + } + + //******************************************* + /** * We only set one sheet as selected for compatibility with HSSF. */ diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 49e47c937..e4157c7d0 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -31,6 +31,7 @@ import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.StylesSource; +import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.StylesTable; import org.openxml4j.opc.ContentTypes; @@ -150,6 +151,73 @@ public final class TestXSSFWorkbook extends TestCase { workbook.removeSheetAt(0); assertEquals(0, workbook.getNumberOfSheets()); } + + public void testPrintArea(){ + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("Test Print Area"); + String sheetName = workbook.getSheetName(0); + + // String reference = sheetName+"!$A$1:$B$1"; + // workbook.setPrintArea(0, reference); + workbook.setPrintArea(0,1,5,4,9); + + String retrievedPrintArea = workbook.getPrintArea(0); + + //assertNotNull("Print Area not defined for first sheet", retrievedPrintArea); + assertEquals("'"+sheetName+"'!$B$5:$F$10", retrievedPrintArea); +} + + public void _testRepeatingRowsAndColums() { + // First test that setting RR&C for same sheet more than once only creates a + // single Print_Titles built-in record + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet("FirstSheet"); + + // set repeating rows and columns twice for the first sheet + for (int i = 0; i < 2; i++) { + wb.setRepeatingRowsAndColumns(0, 0, 0, 0, 3); + //sheet.createFreezePane(0, 3); + } + assertEquals(1, wb.getNumberOfNames()); + XSSFName nr1 = wb.getNameAt(0); + + assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName()); + assertEquals("'FirstSheet'!$A:$A,'FirstSheet'!$1:$4", nr1.getReference()); + + // Save and re-open + XSSFWorkbook nwb = XSSFTestDataSamples.writeOutAndReadBack(wb); + + assertEquals(1, nwb.getNumberOfNames()); + nr1 = nwb.getNameAt(0); + + assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName()); + assertEquals("'FirstSheet'!$A:$A,'FirstSheet'!$1:$4", nr1.getReference()); + + // check that setting RR&C on a second sheet causes a new Print_Titles built-in + // name to be created + sheet = nwb.createSheet("SecondSheet"); + nwb.setRepeatingRowsAndColumns(1, 1, 2, 0, 0); + + assertEquals(2, nwb.getNumberOfNames()); + XSSFName nr2 = nwb.getNameAt(1); + + assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr2.getNameName()); + assertEquals("'SecondSheet'!$B:$C,'SecondSheet'!$1:$1", nr2.getReference()); + + if (false) { + // In case you fancy checking in excel, to ensure it + // won't complain about the file now + try { + File tempFile = File.createTempFile("POI-45126-", ".xlsx"); + FileOutputStream fout = new FileOutputStream(tempFile); + nwb.write(fout); + fout.close(); + System.out.println("check out " + tempFile.getAbsolutePath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } /** * Tests that we can save a new document