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
This commit is contained in:
Dominik Stadler 2016-11-25 08:55:52 +00:00
parent 90ce8cb096
commit e2b22e51f7
4 changed files with 126 additions and 19 deletions

View File

@ -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');
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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<? extends DataValidation> 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);
}
}
}