<!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
<action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
<action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
* @version 1.0-pre
*/
public final class Workbook implements Model {
+ /**
+ * Excel silently truncates long sheet names to 31 chars.
+ * This constant is used to ensure uniqueness in the first 31 chars
+ */
+ private static final int MAX_SENSITIVE_SHEET_NAME_LEN = 31;
+
private static final int DEBUG = POILogger.DEBUG;
/**
/**
* sets the name for a given sheet. If the boundsheet record doesn't exist and
- * its only one more than we have, go ahead and create it. If its > 1 more than
+ * its only one more than we have, go ahead and create it. If it's > 1 more than
* we have, except
*
* @param sheetnum the sheet number (0 based)
* @param sheetname the name for the sheet
*/
- public void setSheetName(int sheetnum, String sheetname ) {
+ public void setSheetName(int sheetnum, String sheetname) {
checkSheets(sheetnum);
BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum );
sheet.setSheetname(sheetname);
}
/**
- * Determines whether a workbook contains the provided sheet name.
+ * Determines whether a workbook contains the provided sheet name. For the purpose of
+ * comparison, long names are truncated to 31 chars.
*
* @param name the name to test (case insensitive match)
* @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
* @return true if the sheet contains the name, false otherwise.
*/
- public boolean doesContainsSheetName( String name, int excludeSheetIdx )
- {
- for ( int i = 0; i < boundsheets.size(); i++ )
- {
+ public boolean doesContainsSheetName(String name, int excludeSheetIdx) {
+ String aName = name;
+ if (aName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
+ aName = aName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
+ }
+ for (int i = 0; i < boundsheets.size(); i++) {
BoundSheetRecord boundSheetRecord = getBoundSheetRec(i);
- if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname()))
+ if (excludeSheetIdx == i) {
+ continue;
+ }
+ String bName = boundSheetRecord.getSheetname();
+ if (bName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
+ bName = bName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
+ }
+ if (aName.equalsIgnoreCase(bName)) {
return true;
+ }
}
return false;
}
return (short)getOrCreateLinkTable().checkExternSheet(sheetNumber);
}
- public int getExternalSheetIndex(String workbookName, String sheetName) {
- return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
- }
+ public int getExternalSheetIndex(String workbookName, String sheetName) {
+ return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
+ }
/** gets the total number of names
throw new IllegalArgumentException("sheetName must not be null");
}
int len = sheetName.length();
- if (len < 1 || len > 31) {
- throw new IllegalArgumentException("sheetName '" + sheetName
- + "' is invalid - must be 1-30 characters long");
+ if (len < 1) {
+ throw new IllegalArgumentException("sheetName must not be empty string");
}
for (int i=0; i<len; i++) {
char ch = sheetName.charAt(i);
}
/**
- * set the sheet name.
- * Will throw IllegalArgumentException if the name is greater than 31 chars
- * or contains /\?*[]
+ * Sets the sheet name.
+ * Will throw IllegalArgumentException if the name is duplicated or contains /\?*[]
+ * Note - Excel allows sheet names up to 31 chars in length but other applications allow more.
+ * Excel does not crash with names longer than 31 chars, but silently truncates such names to
+ * 31 chars. POI enforces uniqueness on the first 31 chars.
+ *
* @param sheetIx number (0 based)
*/
- public void setSheetName(int sheetIx, String name)
- {
- if (workbook.doesContainsSheetName( name, sheetIx )) {
- throw new IllegalArgumentException( "The workbook already contains a sheet with this name" );
+ public void setSheetName(int sheetIx, String name) {
+ if (workbook.doesContainsSheetName(name, sheetIx)) {
+ throw new IllegalArgumentException("The workbook already contains a sheet with this name");
}
validateSheetIndex(sheetIx);
workbook.setSheetName(sheetIx, name);
* create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and
* returns the high level representation. Use this to create new sheets.
*
- * @param sheetname
- * sheetname to set for the sheet.
+ * @param sheetname the name for the new sheet. Note - certain length limits
+ * apply. See {@link #setSheetName(int, String)}.
+ *
* @return HSSFSheet representing the new sheet.
* @throws IllegalArgumentException
* if there is already a sheet present with a case-insensitive
* match for the specified name.
*/
-
public HSSFSheet createSheet(String sheetname)
{
if (workbook.doesContainsSheetName( sheetname, _sheets.size() ))
public void testName() {
BoundSheetRecord record = new BoundSheetRecord("1234567890223456789032345678904");
- try {
- record.setSheetname("12345678902234567890323456789042");
- throw new AssertionFailedError("Should have thrown IllegalArgumentException, but didnt");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
try {
record.setSheetname("s//*s");
throw new AssertionFailedError("Should have thrown IllegalArgumentException, but didnt");
//TODO: check shapeId in the cloned sheet
}
+
+ /**
+ * POI now (Sep 2008) allows sheet names longer than 31 chars (for other apps besides Excel).
+ * Since Excel silently truncates to 31, make sure that POI enforces uniqueness on the first
+ * 31 chars.
+ */
+ public void testLongSheetNames() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ final String SAME_PREFIX = "A123456789B123456789C123456789"; // 30 chars
+
+ wb.createSheet(SAME_PREFIX + "Dxxxx");
+ try {
+ wb.createSheet(SAME_PREFIX + "Dyyyy"); // identical up to the 32nd char
+ throw new AssertionFailedError("Expected exception not thrown");
+ } catch (IllegalArgumentException e) {
+ assertEquals("The workbook already contains a sheet of this name", e.getMessage());
+ }
+ wb.createSheet(SAME_PREFIX + "Exxxx"); // OK - differs in the 31st char
+ }
}