From: Dominik Stadler Date: Fri, 25 Nov 2016 08:55:52 +0000 (+0000) Subject: Bug 59200: Check for actual Excel limits on data validation title/text X-Git-Tag: REL_3_16_BETA2~300 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=909467c2a655370b79f7043f10ac6d14e2672791;p=poi.git Bug 59200: Check for actual Excel limits on data validation title/text git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1771254 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/record/DVRecord.java b/src/java/org/apache/poi/hssf/record/DVRecord.java index 68d560afd3..04b3f84411 100644 --- a/src/java/org/apache/poi/hssf/record/DVRecord.java +++ b/src/java/org/apache/poi/hssf/record/DVRecord.java @@ -42,13 +42,13 @@ public final class DVRecord extends StandardRecord implements Cloneable { /** Option flags */ private int _option_flags; - /** Title of the prompt box */ + /** Title of the prompt box, cannot be longer than 32 chars */ private UnicodeString _promptTitle; - /** Title of the error box */ + /** Title of the error box, cannot be longer than 32 chars */ private UnicodeString _errorTitle; - /** Text of the prompt box */ + /** Text of the prompt box, cannot be longer than 255 chars */ private UnicodeString _promptText; - /** Text of the error box */ + /** Text of the error box, cannot be longer than 255 chars */ private UnicodeString _errorText; /** Not used - Excel seems to always write 0x3FE0 */ private short _not_used_1 = 0x3FE0; @@ -82,6 +82,21 @@ public final class DVRecord extends StandardRecord implements Cloneable { Ptg[] formula1, Ptg[] formula2, CellRangeAddressList regions) { + // check length-limits + if(promptTitle != null && promptTitle.length() > 32) { + throw new IllegalStateException("Prompt-title cannot be longer than 32 characters, but had: " + promptTitle); + } + if(promptText != null && promptText.length() > 255) { + throw new IllegalStateException("Prompt-text cannot be longer than 255 characters, but had: " + promptText); + } + + if(errorTitle != null && errorTitle.length() > 32) { + throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + errorTitle); + } + if(errorText != null && errorText.length() > 255) { + throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + errorText); + } + int flags = 0; flags = opt_data_type.setValue(flags, validationType); flags = opt_condition_operator.setValue(flags, operator); @@ -267,8 +282,8 @@ public final class DVRecord extends StandardRecord implements Cloneable { } Ptg[] ptgs = f.getTokens(); sb.append('\n'); - for (int i = 0; i < ptgs.length; i++) { - sb.append('\t').append(ptgs[i].toString()).append('\n'); + for (Ptg ptg : ptgs) { + sb.append('\t').append(ptg.toString()).append('\n'); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java index 6654749863..206ee11b99 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java @@ -44,7 +44,9 @@ public final class HSSFDataValidation implements DataValidation { /** * Constructor which initializes the cell range on which this object will be * applied - * @param constraint + * + * @param regions A list of regions where the constraint is validated. + * @param constraint The constraints to apply for this validation. */ public HSSFDataValidation(CellRangeAddressList regions, DataValidationConstraint constraint) { _regions = regions; @@ -109,6 +111,7 @@ public final class HSSFDataValidation implements DataValidation { * @see org.apache.poi.hssf.usermodel.DataValidation#getSuppressDropDownArrow() */ public boolean getSuppressDropDownArrow() { + //noinspection SimplifiableIfStatement if (_constraint.getValidationType()==ValidationType.LIST) { return _suppress_dropdown_arrow; } @@ -148,6 +151,13 @@ public final class HSSFDataValidation implements DataValidation { * @see org.apache.poi.hssf.usermodel.DataValidation#createPromptBox(java.lang.String, java.lang.String) */ public void createPromptBox(String title, String text) { + // check length-limits + if(title != null && title.length() > 32) { + throw new IllegalStateException("Prompt-title cannot be longer than 32 characters, but had: " + title); + } + if(text != null && text.length() > 255) { + throw new IllegalStateException("Prompt-text cannot be longer than 255 characters, but had: " + text); + } _prompt_title = title; _prompt_text = text; this.setShowPromptBox(true); @@ -171,6 +181,12 @@ public final class HSSFDataValidation implements DataValidation { * @see org.apache.poi.hssf.usermodel.DataValidation#createErrorBox(java.lang.String, java.lang.String) */ public void createErrorBox(String title, String text) { + if(title != null && title.length() > 32) { + throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + title); + } + if(text != null && text.length() > 255) { + throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + text); + } _error_title = title; _error_text = text; this.setShowErrorBox(true); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java index c204e86a8d..4c42a3c17b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java @@ -103,6 +103,13 @@ public class XSSFDataValidation implements DataValidation { * @see org.apache.poi.ss.usermodel.DataValidation#createErrorBox(java.lang.String, java.lang.String) */ public void createErrorBox(String title, String text) { + // the spec does not specify a length-limit, however Excel reports files as "corrupt" if they exceed 255 bytes for these texts... + if(title != null && title.length() > 255) { + throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + title); + } + if(text != null && text.length() > 255) { + throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + text); + } ctDdataValidation.setErrorTitle(title); ctDdataValidation.setError(text); } @@ -111,6 +118,13 @@ public class XSSFDataValidation implements DataValidation { * @see org.apache.poi.ss.usermodel.DataValidation#createPromptBox(java.lang.String, java.lang.String) */ public void createPromptBox(String title, String text) { + // the spec does not specify a length-limit, however Excel reports files as "corrupt" if they exceed 255 bytes for these texts... + if(title != null && title.length() > 255) { + throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + title); + } + if(text != null && text.length() > 255) { + throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + text); + } ctDdataValidation.setPromptTitle(title); ctDdataValidation.setPrompt(text); } @@ -237,14 +251,12 @@ public class XSSFDataValidation implements DataValidation { } private static XSSFDataValidationConstraint getConstraint(CTDataValidation ctDataValidation) { - XSSFDataValidationConstraint constraint = null; String formula1 = ctDataValidation.getFormula1(); String formula2 = ctDataValidation.getFormula2(); Enum operator = ctDataValidation.getOperator(); org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationType.Enum type = ctDataValidation.getType(); Integer validationType = XSSFDataValidation.validationTypeReverseMappings.get(type); Integer operatorType = XSSFDataValidation.operatorTypeReverseMappings.get(operator); - constraint = new XSSFDataValidationConstraint(validationType,operatorType, formula1,formula2); - return constraint; + return new XSSFDataValidationConstraint(validationType,operatorType, formula1,formula2); } } diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java index b5649ef995..309155ca2d 100644 --- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java @@ -21,6 +21,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.ITestDataProvider; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.PaneInformation; import org.apache.poi.ss.util.SheetUtil; import org.apache.poi.util.POILogFactory; @@ -36,6 +37,7 @@ import java.awt.geom.Rectangle2D; import java.io.IOException; import java.text.AttributedString; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.junit.Assert.*; @@ -46,9 +48,12 @@ import static org.junit.Assert.*; * @author Yegor Kozlov */ public abstract class BaseTestBugzillaIssues { - private static final POILogger logger = POILogFactory.getLogger(BaseTestBugzillaIssues.class); + private static final String TEST_32 = "Some text with 32 characters to "; + private static final String TEST_255 = "Some very long text that is exactly 255 characters, which are allowed here, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla....."; + private static final String TEST_256 = "Some very long text that is longer than the 255 characters allowed in HSSF here, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla bla, bla1"; + private final ITestDataProvider _testDataProvider; protected BaseTestBugzillaIssues(ITestDataProvider testDataProvider) { @@ -477,7 +482,7 @@ public abstract class BaseTestBugzillaIssues { String txt = lines[0] + "0"; AttributedString str = new AttributedString(txt); - copyAttributes(font, str, 0, txt.length()); + copyAttributes(font, str, txt.length()); // TODO: support rich text fragments /*if (rt.numFormattingRuns() > 0) { @@ -496,18 +501,18 @@ public abstract class BaseTestBugzillaIssues { private double computeCellWidthFixed(Font font, String txt) { final FontRenderContext fontRenderContext = new FontRenderContext(null, true, true); AttributedString str = new AttributedString(txt); - copyAttributes(font, str, 0, txt.length()); + copyAttributes(font, str, txt.length()); TextLayout layout = new TextLayout(str.getIterator(), fontRenderContext); return getFrameWidth(layout); } - private static void copyAttributes(Font font, AttributedString str, int startIdx, int endIdx) { - str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx); + private static void copyAttributes(Font font, AttributedString str, int endIdx) { + str.addAttribute(TextAttribute.FAMILY, font.getFontName(), 0, endIdx); str.addAttribute(TextAttribute.SIZE, (float)font.getFontHeightInPoints()); - if (font.getBoldweight() == Font.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx); - if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, startIdx, endIdx); - if (font.getUnderline() == Font.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, startIdx, endIdx); + if (font.getBold()) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, 0, endIdx); + if (font.getItalic() ) str.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, 0, endIdx); + if (font.getUnderline() == Font.U_SINGLE ) str.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 0, endIdx); } /** @@ -1020,6 +1025,7 @@ public abstract class BaseTestBugzillaIssues { wb.close(); } + @SuppressWarnings("deprecation") @Test public void bug56981() throws IOException { Workbook wb = _testDataProvider.createWorkbook(); @@ -1095,7 +1101,7 @@ public abstract class BaseTestBugzillaIssues { Font font = wb.createFont(); font.setFontName("Arial"); font.setFontHeightInPoints((short)14); - font.setBoldweight(Font.BOLDWEIGHT_BOLD); + font.setBold(true); font.setColor(IndexedColors.RED.getIndex()); str2.applyFont(font); @@ -1276,6 +1282,7 @@ public abstract class BaseTestBugzillaIssues { wb2.close(); } + @SuppressWarnings("deprecation") @Test public void bug58260() throws IOException { //Create workbook and worksheet @@ -1779,4 +1786,61 @@ public abstract class BaseTestBugzillaIssues { wb.close(); } + + @Test + public void test59200() throws IOException { + Workbook wb = _testDataProvider.createWorkbook(); + final Sheet sheet = wb.createSheet(); + + DataValidation dataValidation; + CellRangeAddressList headerCell = new CellRangeAddressList(0, 1, 0, 1); + DataValidationConstraint constraint = sheet.getDataValidationHelper().createCustomConstraint("A1<>\"\""); + + dataValidation = sheet.getDataValidationHelper().createValidation(constraint, headerCell); + + // HSSF has 32/255 limits as part of the Spec, XSSF has no limit in the spec, but Excel applies a 255 length limit! + // more than 255 fail for all + checkFailures(dataValidation, TEST_256, TEST_32, true); + checkFailures(dataValidation, TEST_32, TEST_256, true); + // more than 32 title fail for HSSFWorkbook + checkFailures(dataValidation, TEST_255, TEST_32, wb instanceof HSSFWorkbook); + // 32 length title and 255 length text wrok for both + checkFailures(dataValidation, TEST_32, TEST_255, false); + + dataValidation.setShowErrorBox(false); + sheet.addValidationData(dataValidation); + + // write out and read back in to trigger some more validation + final Workbook wbBack = _testDataProvider.writeOutAndReadBack(wb); + + final Sheet sheetBack = wbBack.getSheetAt(0); + final List dataValidations = sheetBack.getDataValidations(); + assertEquals(1, dataValidations.size()); + + /*String ext = (wb instanceof HSSFWorkbook) ? ".xls" : ".xlsx"; + OutputStream str = new FileOutputStream("C:\\temp\\59200" + ext); + try { + wb.write(str); + } finally { + str.close(); + }*/ + + wb.close(); + } + + private void checkFailures(DataValidation dataValidation, String title, String text, boolean shouldFail) { + try { + dataValidation.createPromptBox(title, text); + assertFalse("Should fail in a length-check, had " + title.length() + " and " + text.length(), shouldFail); + } catch (IllegalStateException e) { + assertTrue("Should not fail in a length-check, had " + title.length() + " and " + text.length(), shouldFail); + // expected here + } + try { + dataValidation.createErrorBox(title, text); + assertFalse("Should fail in a length-check, had " + title.length() + " and " + text.length(), shouldFail); + } catch (IllegalStateException e) { + assertTrue("Should not fail in a length-check, had " + title.length() + " and " + text.length(), shouldFail); + } + } } \ No newline at end of file